ADOT で OpenTelemetry に入門する (Metrics | EC2 on EKS)

AWS Distro for OpenTelemetry を使用して EC2 on EKS 環境のメトリクスを収集する

oginoNovember 11, 2024

この記事は OpenTelemetry に入門してみるシリーズの第一回です。今後数回の記事に分けて、OpenTelemetry に入門していきます。

第一回となる本記事では、AWS Distro for OpenTelemetry (以降 ADOT) を使用して EC2 on EKS 環境のメトリクスを収集します。

事前準備

以降の節では、ADOT Operator がクラスターで利用可能となっていることを前提としています。

今回の記事の執筆環境は、以下のように ADOT Operator を導入しています。

  1. ADOT Operator は ADOT アドオンとして導入済み (ガイド)
  2. cert-manager を導入済み (ADOT アドオンの利用に必要となるため)

ADOT アドオンが利用可能である場合は、以下の様なポッドや API リソースが確認できます。

$ kubectl get po -n opentelemetry-operator-system
NAME                                     READY   STATUS    RESTARTS   AGE
opentelemetry-operator-b7dbbdf7c-f74jx   2/2     Running   0          2m39s
$ kubectl api-resources | grep opentelemetry
instrumentations                    otelinst,otelinsts   opentelemetry.io/v1alpha1         true         Instrumentation
opampbridges                                             opentelemetry.io/v1alpha1         true         OpAMPBridge
opentelemetrycollectors             otelcol,otelcols     opentelemetry.io/v1beta1          true         OpenTelemetryCollector

留意点

執筆時点では、ADOT アドオンは EKS バージョン 1.31 をサポートしていませんでした。(Issue | #2435 [EKS] [request]: No available versions of ADOT add-on for EKS 1.31)

そのため、本記事の執筆環境の EKS はバージョン 1.30 を利用しています。過去にも、ADOT アドオンがサポートする EKS バージョンとして、利用可能な最新の EKS バージョンが含まれない期間があったようなので、組織で EKS のバージョン管理ポリシーを定めている場合は、アドオンの利用は慎重に検討しても良いかもしれません。

メトリクスを収集する

Kubernetes のメトリクスを収集する方法はいくつかあると思いますが、本記事では Container Insights EKS Infrastructure Metrics のチュートリアルを参考に AWS Container Insigths Reciever を使用して、ノードやポッドのメトリクスを収集します。

まずは、以下の内容で adot.yaml を作成します。

このマニフェストは、ADOT Collector を DaemonSet モードで起動させ、AWS Container Insigths Reciever により収集されたメトリクスを、Logging Exporter によりコンソールに出力するパイプラインを動かすものです。

apiVersion: v1
kind: Namespace
metadata:
  name: aws-otel-eks
  labels:
    name: aws-otel-eks
 
---
apiVersion: v1
kind: ServiceAccount
metadata:
  name: otel-collector
  namespace: aws-otel-eks
 
---
kind: ClusterRole
apiVersion: rbac.authorization.k8s.io/v1
metadata:
  name: otel-collector-role
rules:
  - apiGroups: [""]
    resources: ["pods", "nodes", "endpoints"]
    verbs: ["list", "watch", "get"]
  - apiGroups: ["apps"]
    resources: ["replicasets"]
    verbs: ["list", "watch", "get"]
  - apiGroups: ["batch"]
    resources: ["jobs"]
    verbs: ["list", "watch"]
  - apiGroups: [""]
    resources: ["nodes/proxy"]
    verbs: ["get"]
  - apiGroups: [""]
    resources: ["nodes/stats", "configmaps", "events"]
    verbs: ["create", "get"]
  - apiGroups: [""]
    resources: ["configmaps"]
    resourceNames: ["otel-container-insight-clusterleader"]
    verbs: ["get","update"]
  - apiGroups: ["coordination.k8s.io"]
    resources: ["leases"]
    verbs: ["create","get", "update"]
  - apiGroups: ["coordination.k8s.io"]
    resources: ["leases"]
    resourceNames: ["otel-container-insight-clusterleader"]
    verbs: ["get","update", "create"]
 
---
kind: ClusterRoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
  name: otel-collector-role-binding
subjects:
  - kind: ServiceAccount
    name: otel-collector
    namespace: aws-otel-eks
 
roleRef:
  kind: ClusterRole
  name: otel-collector-role
  apiGroup: rbac.authorization.k8s.io
 
---
apiVersion: opentelemetry.io/v1beta1
kind: OpenTelemetryCollector
metadata:
  name: otel-collector
  namespace: aws-otel-eks
spec:
  managementState: managed
  mode: daemonset
  serviceAccount: otel-collector
  # https://github.com/aws-observability/aws-otel-collector/issues/2317
  securityContext:
    runAsUser: 0
    runAsGroup: 0
  env:
    - name: K8S_NODE_NAME
      valueFrom:
        fieldRef:
          fieldPath: spec.nodeName
    - name: HOST_IP
      valueFrom:
        fieldRef:
          fieldPath: status.hostIP
    - name: HOST_NAME
      valueFrom:
        fieldRef:
          fieldPath: spec.nodeName
    - name: K8S_NAMESPACE
      valueFrom:
        fieldRef:
          fieldPath: metadata.namespace
  volumes:
    - name: rootfs
      hostPath:
        path: /
    # https://docs.aws.amazon.com/ja_jp/AmazonCloudWatch/latest/monitoring/ContainerInsights-troubleshooting.html
    - name: dockersock
      hostPath:
        path: /var/run/docker.sock
    - name: varlibdocker
      hostPath:
        path: /var/lib/docker
    - name: varlibcontainerd
      hostPath:
        path: /var/lib/containerd/
    - name: containerdsock
      hostPath:
        # path: /run/containerd/containerd.sock
        # bottlerocket does not mount containerd sock at normal place
        # https://github.com/bottlerocket-os/bottlerocket/commit/91810c85b83ff4c3660b496e243ef8b55df0973b
        path: /run/dockershim.sock
    - name: sys
      hostPath:
        path: /sys
    - name: devdisk
      hostPath:
        path: /dev/disk/
  volumeMounts:
    - name: rootfs
      mountPath: /rootfs
      readOnly: true
    # https://docs.aws.amazon.com/ja_jp/AmazonCloudWatch/latest/monitoring/ContainerInsights-troubleshooting.html
    - name: dockersock
      mountPath: /var/run/docker.sock
      readOnly: true
    - name: varlibdocker
      mountPath: /var/lib/docker
      readOnly: true
    - name: varlibcontainerd
      mountPath: /var/lib/containerd
      readOnly: true
    - name: containerdsock # NEW mount
      mountPath: /run/containerd/containerd.sock
      readOnly: true
    - name: sys
      mountPath: /sys
      readOnly: true
    - name: devdisk
      mountPath: /dev/disk
      readOnly: true
  resources:
    limits:
      cpu:  200m
      memory: 200Mi
    requests:
      cpu: 200m
      memory: 200Mi
  config:
   receivers:
     awscontainerinsightreceiver:
       collection_interval: 60s
       container_orchestrator: eks
       add_service_as_attribute: true
       prefer_full_pod_name: false
       add_full_pod_name_metric_label: false
   exporters:
     logging:
       verbosity: detailed
       sampling_initial: 100
       sampling_thereafter: 1
   processors:
     batch:
       timeout: 10s
   service:
     pipelines:
       metrics/101:
         receivers: [awscontainerinsightreceiver]
         processors: [batch]
         exporters: [logging]

次に、作成した adot.yaml を適用します。

$ kubectl apply -f adot.yaml

適用後、ポッドが起動したことが確認できます。

$ kubectl get po -n aws-otel-eks
NAME                             READY   STATUS    RESTARTS   AGE
otel-collector-collector-2rv6g   1/1     Running   0          25s
otel-collector-collector-mzttn   1/1     Running   0          24s

起動されたいずれかのポッドのログを確認することで、ADOT Collector により、メトリクスが収集されていることが確認できます。

$ kubectl logs otel-collector-collector-2rv6g -n aws-otel-eks
 
Descriptor:
     -> Name: namespace_number_of_running_pods
     -> Description:
     -> Unit: Count
     -> DataType: Gauge
 
Descriptor:
     -> Name: node_memory_rss
     -> Description:
     -> Unit: Bytes
     -> DataType: Gauge
 
Descriptor:
     -> Name: pod_memory_max_usage
     -> Description:
     -> Unit: Bytes
     -> DataType: Gauge

おわりに

本記事では、ADOT を使用して EC2 on EKS 環境のメトリクスを収集してみました。

本記事の内容では、収集したメトリクスを Logging Exporter でコンソールに出力しているだけですが、バックエンドサービスへ連携するエクスポーターへ差し替えることで、バックエンドサービスにメトリクスを連携できるようになります。

なお、本記事の構成で ADOT Collector を動作させる場合は、Fargate ノードで動作するポッドのメトリクスは収集できません。次回は Fargate ノード上のポッドのメトリクスを収集する方法について触れてみたいと思います。