[アップデート] OTel Container Insights for Amazon EKS を利用できるようになりました

[アップデート] OTel Container Insights for Amazon EKS を利用できるようになりました

2026.04.19

アップデート概要

OpenTelemetry をベースにした EKS 向け Container Insights を利用できるようになりました(パブリックプレビュー)。

https://aws.amazon.com/about-aws/whats-new/2026/04/cloudwatch-otel-container-insights-eks/

EKS で Container Insights を利用する際、通常版、enhanced observability、OTel Container Insights の 3 バージョンが存在することになります。

項目 Container Insights(通常) Container Insights with enhanced observability OTel Container Insights
ステータス GA(2019年8月〜) GA(2023年11月〜) Public Preview(2026年4月〜)
エージェント CloudWatchエージェント or ADOT or CloudWatch Observability EKS アドオン CloudWatchエージェント or CloudWatch Observability EKS アドオン CloudWatch Observability EKS アドオン v6.0.0+
プロトコル EMF(Embedded Metric Format) EMF(Embedded Metric Format) OTLP
メトリクスレベル Cluster, Node, Pod, Namespace, Service Cluster, Node, Pod, Container, Namespace, Service ユーザー定義のものも含んだ最大150ラベル
コンテナレベルメトリクス なし あり あり
コントロールプレーン監視 なし あり あり
料金モデル カスタムメトリクス課金 + CloudWatch ログの取り込み料金 データポイント課金 (メトリクスに関する料金とログの取り込み料金がバンドルされる) Preview 期間中は無料(GA 後の料金は未定)
PromQL サポート なし なし あり(CloudWatch Query Studio)

今回追加された OTel Container Insights はあくまで Container Insights with enhanced observability を OpenTelemetry ベースで実現したものであることに注意が必要です。
EKS on Fargate では Container Insights with enhanced observability を利用できない制約がありますが、EKS on Fargate + ADOT でも Container Insights with enhanced observability 相当のビューが使えるようになったわけではありません。
あくまで CloudWatch エージェントに依存した仕組みの中で OpenTelemetry ベースのアーキテクチャになっています。

Container Insights の進化

EKS on Fargate だと Enhanced Container Insights が利用できないとか、Enhanced Container Insights だとそもそも料金体系が違うという話はありますが、機能的には Enhanced Container Insights で強化され、OTel Container Insights でさらに強化されたと考えて良さそうです。

Enhanced Container Insights(2023年11月〜)

  • コンテナレベルのメトリクスが追加され、より細かい粒度で監視可能になった
  • GPU(NVIDIA)、Trainium/Inferentia、EFA など特殊なコンピューティング環境用のメトリクスが追加された
  • API Server / etcd のコントロールプレーンメトリクスを収集可能になった
  • メトリクス料金とログ料金がバンドルされ、コストを試算しやすくなった

OTel Container Insights(2026年4月〜)

  • OTLP エンドポイントを利用した、より OpenTelemetry の標準的な仕様に即したメトリクス収集方式になった
  • CloudWatch Query Studio で PromQL クエリが利用可能になった
  • メトリクスごとに最大150のラベル(Kubernetes メタデータ、カスタムラベル、OTel セマンティック規約属性)を付与できるようになった

試してみる

Auto Mode を有効化した、バージョン 1.35 の EKS クラスターを作成して試してみます。

アドオンの追加

アドオンを追加します。

スクリーンショット 2026-04-06 21.23.21.png

Amazon CloudWatch Observability EKS アドオンを選択します。

スクリーンショット 2026-04-06 21.23.49.png

アドオンバージョンですが、ドキュメントには、v6.0.1-eksbuild.1 以降のバージョンが必要と書かれています。

To use Container Insights with OpenTelemetry metrics, install the Amazon CloudWatch Observability EKS add-on version v6.0.1-eksbuild.1 or later.
https://docs.aws.amazon.com/AmazonCloudWatch/latest/monitoring/deploy-container-insights-EKS.html

ただし、実際には v6 系以降のバージョンであれば利用できるようです。

スクリーンショット 2026-04-12 18.38.36.png

アカウントに存在しない場合は、「推奨ロールを作成」をクリックして作成します。

スクリーンショット 2026-04-12 18.40.55.png

v6.x.x になってから、下記 2 つのリソースが追加されるようになりました。

  • cloudwatch-agent (各ノードに DaemonSet として展開される)
  • cloudwatch-agent-cluster-scraper (クラスター単位のメトリクスを取得するために Deployment として展開される)

それぞれ IAM ロールを設定できますが、権限はどちらも CloudWatchAgentServerPolicy - AWS 管理ポリシー を付与すれば問題ありません。

スクリーンショット 2026-04-12 18.41.03.png

メトリクスの確認

インストールが完了すると、各種メトリクスが CloudWatch に連携されるようになります。
この辺りは Container Insights with enhanced observability と同じです。

スクリーンショット 2026-04-12 19.16.02.png

また、CloudWatch Query Studio から Prom QL でメトリクスを確認できるようになりました。

スクリーンショット 2026-04-12 20.16.46.png

インストールされたコンポーネントの確認

v 5.x.x の CloudWatch Observability EKS アドオンを利用している場合、下記のような Kubernetes リソースが追加されます。

% kubectl get pod -A
NAMESPACE           NAME                                                              READY   STATUS    RESTARTS   AGE
amazon-cloudwatch   amazon-cloudwatch-observability-controller-manager-7d7b569t5bb6   1/1     Running   0          2m31s
amazon-cloudwatch   cloudwatch-agent-cf45k                                            1/1     Running   0          2m23s
amazon-cloudwatch   cloudwatch-agent-glksl                                            1/1     Running   0          2m24s
amazon-cloudwatch   fluent-bit-l2295                                                  1/1     Running   0          2m31s
amazon-cloudwatch   fluent-bit-tbsvq                                                  1/1     Running   0          2m31s
kube-system

v6.x.x の CloudWatch Observability EKS アドオンを利用している場合、下記のような Kubernetes リソースが追加されます。

% kubectl get pod -A
NAMESPACE           NAME                                                              READY   STATUS    RESTARTS   AGE
amazon-cloudwatch   amazon-cloudwatch-observability-controller-manager-5d76f674n5mc   1/1     Running   0          4h33m
amazon-cloudwatch   cloudwatch-agent-cluster-scraper-86b9bff485-wfngh                 1/1     Running   0          4h32m
amazon-cloudwatch   cloudwatch-agent-fvkh6                                            1/1     Running   0          4h32m
amazon-cloudwatch   fluent-bit-xbv7j                                                  1/1     Running   0          4h33m
amazon-cloudwatch   kube-state-metrics-7655bb7df5-z55k4                               1/1     Running   0          4h33m
amazon-cloudwatch   node-exporter-p9h2g                                               1/1     Running   0          4h33m

v6.x.x の CloudWatch Observability EKS アドオンを利用している場合、下記 3 種類のリソースが増えていることがわかります。

  • node-exporter
  • kube-state-metrics
  • cloudwatch-agent-cluster-scraper

node-exporter や kube-state-metrics については、従来 CloudWatch エージェントが OS レベルのメトリクスを直接収集したり、API Server 経由で取得していた所を、Kubernetes エコシステム標準のメトリクス収集方式へと移行したと考えて良さそうです。

https://github.com/prometheus/node_exporter

https://github.com/kubernetes/kube-state-metrics

cloudwatch-agent-cluster-scraper は 602401143452.dkr.ecr.us-east-1.amazonaws.com/eks/observability/cloudwatch-agent:1.300066.0b1367 を利用した単なる CloudWatch エージェントでした。
クラスター単位のメトリクスを取得する際、DaemonSet として展開されている cloudwatch-agent が各ノードから重複してスクレイピングすることを防ぐため、クラスタ全体で1回だけ収集すべきものを担当しているようです。

cloudwatch-agent-cluster-scraper の Pod 情報
% kubectl describe pod cloudwatch-agent-cluster-scraper-86b9bff485-wfngh -n amazon-cloudwatch
Name:                 cloudwatch-agent-cluster-scraper-86b9bff485-wfngh
Namespace:            amazon-cloudwatch
Priority:             2000001000
Priority Class Name:  system-node-critical
Service Account:      cloudwatch-agent
Node:                 i-059a422b6465310a9/10.0.100.23
Start Time:           Sat, 18 Apr 2026 14:57:59 +0900
Labels:               app.kubernetes.io/component=amazon-cloudwatch-agent
                      app.kubernetes.io/instance=amazon-cloudwatch.cloudwatch-agent-cluster-scraper
                      app.kubernetes.io/managed-by=amazon-cloudwatch-agent-operator
                      app.kubernetes.io/name=cloudwatch-agent-cluster-scraper
                      app.kubernetes.io/part-of=amazon-cloudwatch-agent
                      app.kubernetes.io/version=1.300066.0b1367
                      pod-template-hash=86b9bff485
                      topology.kubernetes.io/region=us-east-1
                      topology.kubernetes.io/zone=us-east-1a
Annotations:          amazon-cloudwatch-agent-operator-config/sha256: a08d38b4a834ea3b4a24dc066b3f8b24000c66737bb3a515d18ab8c4575f1a51
Status:               Running
IP:                   10.0.100.23
IPs:
  IP:           10.0.100.23
Controlled By:  ReplicaSet/cloudwatch-agent-cluster-scraper-86b9bff485
Containers:
  otc-container:
    Container ID:   containerd://5208d99b8946e0f94b46ac6a167e24e9da55d6febd9f97fead7d8229735b07f9
    Image:          602401143452.dkr.ecr.us-east-1.amazonaws.com/eks/observability/cloudwatch-agent:1.300066.0b1367
    Image ID:       602401143452.dkr.ecr.us-east-1.amazonaws.com/eks/observability/cloudwatch-agent@sha256:522d36eb276e7764b5cab0c2c5e620ac0f2bb76c9fea99d4543cf2621625f824
    Port:           <none>
    Host Port:      <none>
    State:          Running
      Started:      Sat, 18 Apr 2026 14:58:01 +0900
    Ready:          True
    Restart Count:  0
    Limits:
      cpu:     500m
      memory:  512Mi
    Requests:
      cpu:     250m
      memory:  128Mi
    Environment:
      K8S_NODE_NAME:                            (v1:spec.nodeName)
      HOST_IP:                                  (v1:status.hostIP)
      HOST_NAME:                                (v1:spec.nodeName)
      K8S_NAMESPACE:                           amazon-cloudwatch (v1:metadata.namespace)
      K8S_CLUSTER_NAME:                        test-cluster
      POD_NAME:                                cloudwatch-agent-cluster-scraper-86b9bff485-wfngh (v1:metadata.name)
      AWS_STS_REGIONAL_ENDPOINTS:              regional
      AWS_DEFAULT_REGION:                      us-east-1
      AWS_REGION:                              us-east-1
      AWS_CONTAINER_CREDENTIALS_FULL_URI:      http://169.254.170.23/v1/credentials
      AWS_CONTAINER_AUTHORIZATION_TOKEN_FILE:  /var/run/secrets/pods.eks.amazonaws.com/serviceaccount/eks-pod-identity-token
    Mounts:
      /dev/disk from devdisk (ro)
      /etc/amazon-cloudwatch-observability-agent-cert from agenttls (ro)
      /etc/amazon-cloudwatch-observability-agent-client-cert from agentclienttls (ro)
      /etc/amazon-cloudwatch-observability-agent-server-cert from agentservertls (ro)
      /etc/amazon-cloudwatch-observability-agent-ta-client-cert from agenttaclienttls (ro)
      /etc/cwagentconfig from otc-internal (rw)
      /rootfs from rootfs (ro)
      /run/containerd/containerd.sock from containerdsock (rw)
      /sys from sys (ro)
      /var/lib/containers from criocontainer (ro)
      /var/lib/docker from varlibdocker (ro)
      /var/lib/kubelet/pod-resources from kubelet-podresources (rw)
      /var/log/pods from criologs (ro)
      /var/run/crio/crio.sock from criosock (ro)
      /var/run/docker.sock from dockersock (ro)
      /var/run/secrets/kubernetes.io/serviceaccount from kube-api-access-m6k2b (ro)
      /var/run/secrets/pods.eks.amazonaws.com/serviceaccount from eks-pod-identity-token (ro)
Conditions:
  Type                        Status
  PodReadyToStartContainers   True
  Initialized                 True
  Ready                       True
  ContainersReady             True
  PodScheduled                True
Volumes:
  eks-pod-identity-token:
    Type:                    Projected (a volume that contains injected data from multiple sources)
    TokenExpirationSeconds:  83157
  otc-internal:
    Type:      ConfigMap (a volume populated by a ConfigMap)
    Name:      cloudwatch-agent-cluster-scraper
    Optional:  false
  kubelet-podresources:
    Type:          HostPath (bare host directory volume)
    Path:          /var/lib/kubelet/pod-resources
    HostPathType:  Directory
  rootfs:
    Type:          HostPath (bare host directory volume)
    Path:          /
    HostPathType:
  dockersock:
    Type:          HostPath (bare host directory volume)
    Path:          /var/run/docker.sock
    HostPathType:
  varlibdocker:
    Type:          HostPath (bare host directory volume)
    Path:          /var/lib/docker
    HostPathType:
  containerdsock:
    Type:          HostPath (bare host directory volume)
    Path:          /run/containerd/containerd.sock
    HostPathType:
  criosock:
    Type:          HostPath (bare host directory volume)
    Path:          /var/run/crio/crio.sock
    HostPathType:
  criocontainer:
    Type:          HostPath (bare host directory volume)
    Path:          /var/lib/containers
    HostPathType:
  criologs:
    Type:          HostPath (bare host directory volume)
    Path:          /var/log/pods
    HostPathType:
  sys:
    Type:          HostPath (bare host directory volume)
    Path:          /sys
    HostPathType:
  devdisk:
    Type:          HostPath (bare host directory volume)
    Path:          /dev/disk/
    HostPathType:
  agenttls:
    Type:        Secret (a volume populated by a Secret)
    SecretName:  amazon-cloudwatch-observability-agent-cert
    Optional:    false
  agentclienttls:
    Type:        Secret (a volume populated by a Secret)
    SecretName:  amazon-cloudwatch-observability-agent-client-cert
    Optional:    false
  agentservertls:
    Type:        Secret (a volume populated by a Secret)
    SecretName:  amazon-cloudwatch-observability-agent-server-cert
    Optional:    false
  agenttaclienttls:
    Type:        Secret (a volume populated by a Secret)
    SecretName:  amazon-cloudwatch-observability-agent-ta-client-cert
    Optional:    false
  kube-api-access-m6k2b:
    Type:                    Projected (a volume that contains injected data from multiple sources)
    TokenExpirationSeconds:  3607
    ConfigMapName:           kube-root-ca.crt
    ConfigMapOptional:       <nil>
    DownwardAPI:             true
QoS Class:                   Burstable
Node-Selectors:              kubernetes.io/os=linux
Tolerations:                 op=Exists
Events:                      <none>

CloudWatch エージェントの設定となる ConfigMap を確認しても、kube-state-metrics や API Server からメトリクスを読み取って、CloudWatch のOTLP メトリクスエンドポイントに送信していることがわかります。

exporters:
  otlphttp/cw_k8s_ci_v0_cwotel:
    auth:
      authenticator: sigv4auth/cw_k8s_ci_v0_cwotel
    endpoint: https://monitoring.us-east-1.amazonaws.com:443
    tls:
      insecure: false
cloudwatch-agent-cluster-scraper の 設定
% kubectl describe cm cloudwatch-agent-cluster-scraper -n amazon-cloudwatch
Name:         cloudwatch-agent-cluster-scraper
Namespace:    amazon-cloudwatch
Labels:       app.kubernetes.io/component=amazon-cloudwatch-agent
              app.kubernetes.io/instance=amazon-cloudwatch.cloudwatch-agent-cluster-scraper
              app.kubernetes.io/managed-by=amazon-cloudwatch-agent-operator
              app.kubernetes.io/name=cloudwatch-agent-cluster-scraper
              app.kubernetes.io/part-of=amazon-cloudwatch-agent
              app.kubernetes.io/version=1.300066.0b1367
Annotations:  <none>

Data
====
cwagentconfig.json:
----
{"agent":{"region":"us-east-1"}}

cwagentotelconfig.yaml:
----
exporters:
  otlphttp/cw_k8s_ci_v0_cwotel:
    auth:
      authenticator: sigv4auth/cw_k8s_ci_v0_cwotel
    endpoint: https://monitoring.us-east-1.amazonaws.com:443
    tls:
      insecure: false
extensions:
  sigv4auth/cw_k8s_ci_v0_cwotel:
    region: us-east-1
    service: monitoring
processors:
  awsattributelimit/cw_k8s_ci_v0:
    max_total_attributes: 150
  batch/cw_k8s_ci_v0_cwotel:
    send_batch_max_size: 500
    send_batch_size: 500
    timeout: 10s
  filter/cw_k8s_ci_v0_apiserver_build_info:
    error_mode: ignore
    metrics:
      metric:
      - name == "kubernetes_build_info"
  filter/cw_k8s_ci_v0_scrape_metadata:
    error_mode: ignore
    metrics:
      metric:
      - IsMatch(name, "^(up|scrape_duration_seconds|scrape_samples_scraped|scrape_samples_post_metric_relabeling|scrape_series_added)$")
  groupbyattrs/cw_k8s_ci_v0_ksm:
    keys:
    - pod
    - namespace
    - uid
    - node
    - container
    - owner_name
    - owner_kind
  k8sattributes/cw_k8s_ci_v0_node:
    auth_type: serviceAccount
    extract:
      labels:
      - from: node
        key_regex: (.*)
        tag_name: k8s.node.label.$$$1
      metadata:
      - k8s.node.name
    passthrough: false
    pod_association:
    - sources:
      - from: resource_attribute
        name: k8s.node.name
  metricstarttime/cw_k8s_ci_v0: {}
  resourcedetection/cw_k8s_ci_v0:
    detectors:
    - ec2
    - eks
    ec2:
      resource_attributes:
        cloud.account.id:
          enabled: true
        cloud.availability_zone:
          enabled: true
        cloud.platform:
          enabled: true
        cloud.provider:
          enabled: true
        cloud.region:
          enabled: true
        host.id:
          enabled: false
        host.image.id:
          enabled: false
        host.name:
          enabled: false
        host.type:
          enabled: false
  transform/cw_k8s_ci_v0_apiserver_cleanup_version:
    error_mode: ignore
    metric_statements:
    - context: resource
      statements:
      - delete_key(attributes, "k8s.apiserver.version") where attributes["k8s.apiserver.version"]
        != nil
  transform/cw_k8s_ci_v0_apiserver_extract_version:
    error_mode: ignore
    metric_statements:
    - context: datapoint
      statements:
      - set(resource.attributes["k8s.apiserver.version"], attributes["git_version"])
        where attributes["git_version"] != nil and attributes["git_version"] != ""
  transform/cw_k8s_ci_v0_clear_schema_url:
    error_mode: ignore
    metric_statements:
    - context: resource
      statements:
      - set(resource.schema_url, "")
  transform/cw_k8s_ci_v0_ksm_clean_resource:
    error_mode: ignore
    metric_statements:
    - context: resource
      statements:
      - delete_key(attributes, "k8s.pod.name")
      - delete_key(attributes, "k8s.pod.uid")
      - delete_key(attributes, "k8s.namespace.name")
      - delete_key(attributes, "k8s.node.name")
      - delete_key(attributes, "k8s.container.name")
      - delete_key(attributes, "k8s.deployment.name")
      - delete_key(attributes, "k8s.replicaset.name")
      - delete_key(attributes, "k8s.workload.name")
      - delete_key(attributes, "k8s.workload.type")
      - delete_key(attributes, "host.id")
      - delete_key(attributes, "host.name")
      - delete_key(attributes, "host.type")
      - delete_key(attributes, "host.image.id")
  transform/cw_k8s_ci_v0_ksm_promote:
    error_mode: ignore
    metric_statements:
    - context: resource
      statements:
      - set(attributes["k8s.pod.name"], attributes["pod"]) where attributes["pod"]
        != nil
      - delete_key(attributes, "pod") where attributes["pod"] != nil
      - set(attributes["k8s.namespace.name"], attributes["namespace"]) where attributes["namespace"]
        != nil
      - delete_key(attributes, "namespace") where attributes["namespace"] != nil
      - set(attributes["k8s.node.name"], attributes["node"]) where attributes["node"]
        != nil
      - delete_key(attributes, "node") where attributes["node"] != nil
      - set(attributes["k8s.pod.uid"], attributes["uid"]) where attributes["uid"]
        != nil
      - delete_key(attributes, "uid") where attributes["uid"] != nil
      - set(attributes["k8s.container.name"], attributes["container"]) where attributes["container"]
        != nil
      - delete_key(attributes, "container") where attributes["container"] != nil
      - set(attributes["k8s.workload.name"], attributes["owner_name"]) where attributes["owner_name"]
        != nil
      - set(attributes["k8s.workload.type"], attributes["owner_kind"]) where attributes["owner_kind"]
        != nil
      - delete_key(attributes, "owner_name") where attributes["owner_name"] != nil
      - delete_key(attributes, "owner_kind") where attributes["owner_kind"] != nil
  transform/cw_k8s_ci_v0_promote_component:
    error_mode: ignore
    metric_statements:
    - context: datapoint
      statements:
      - set(resource.attributes["k8s.component.name"], attributes["component"])
  transform/cw_k8s_ci_v0_set_cloud_resource_id:
    error_mode: ignore
    metric_statements:
    - context: resource
      statements:
      - set(resource.attributes["cloud.resource_id"], Concat(["arn:aws:eks:", resource.attributes["cloud.region"],
        ":", resource.attributes["cloud.account.id"], ":cluster/", resource.attributes["k8s.cluster.name"]],
        "")) where resource.attributes["cloud.region"] != nil and resource.attributes["cloud.account.id"]
        != nil and resource.attributes["k8s.cluster.name"] != nil
  transform/cw_k8s_ci_v0_set_cluster_name:
    error_mode: ignore
    metric_statements:
    - context: datapoint
      statements:
      - set(resource.attributes["k8s.cluster.name"], "test-cluster")
  transform/cw_k8s_ci_v0_set_component:
    error_mode: ignore
    metric_statements:
    - context: datapoint
      statements:
      - set(attributes["component"], "apiserver")
  transform/cw_k8s_ci_v0_set_scope_apiserver:
    error_mode: ignore
    metric_statements:
    - context: scope
      statements:
      - set(scope.version, resource.attributes["k8s.apiserver.version"]) where resource.attributes["k8s.apiserver.version"]
        != nil
      - set(scope.schema_url, "")
      - set(attributes["cloudwatch.source"], "cloudwatch-agent")
      - set(attributes["cloudwatch.solution"], "k8s-otel-container-insights")
      - set(attributes["cloudwatch.pipeline"], "apiserver")
  transform/cw_k8s_ci_v0_set_scope_kube_state_metrics:
    error_mode: ignore
    metric_statements:
    - context: scope
      statements:
      - set(scope.name, "github.com/kubernetes/kube-state-metrics")
      - set(scope.version, "v2.18.0-eksbuild.1")
      - set(scope.schema_url, "")
      - set(attributes["cloudwatch.source"], "cloudwatch-agent")
      - set(attributes["cloudwatch.solution"], "k8s-otel-container-insights")
      - set(attributes["cloudwatch.pipeline"], "kube-state-metrics")
  transform/cw_k8s_ci_v0_set_unit:
    error_mode: ignore
    metric_statements:
    - context: metric
      statements:
      - set(unit, "s") where IsMatch(name, ".*_seconds(_total)?$")
      - set(unit, "ms") where IsMatch(name, ".*_milliseconds(_total)?$")
      - set(unit, "us") where IsMatch(name, ".*_microseconds(_total)?$")
      - set(unit, "ns") where IsMatch(name, ".*_nanoseconds(_total)?$")
      - set(unit, "By") where IsMatch(name, ".*_bytes(_total)?$")
      - set(unit, "KBy") where IsMatch(name, ".*_kilobytes(_total)?$")
      - set(unit, "MBy") where IsMatch(name, ".*_megabytes(_total)?$")
      - set(unit, "GBy") where IsMatch(name, ".*_gigabytes(_total)?$")
      - set(unit, "KiBy") where IsMatch(name, ".*_kibibytes(_total)?$")
      - set(unit, "MiBy") where IsMatch(name, ".*_mebibytes(_total)?$")
      - set(unit, "GiBy") where IsMatch(name, ".*_gibibytes(_total)?$")
      - set(unit, "Cel") where IsMatch(name, ".*_celsius$")
      - set(unit, "Hz") where IsMatch(name, ".*_hertz$")
      - set(unit, "1") where IsMatch(name, ".*_ratio$")
      - set(unit, "%") where IsMatch(name, ".*_percent$")
      - set(unit, "V") where IsMatch(name, ".*_volts$")
      - set(unit, "W") where IsMatch(name, ".*_watts$")
      - set(unit, "J") where IsMatch(name, ".*_joules$")
      - set(unit, "A") where IsMatch(name, ".*_amperes$")
      - set(unit, "m") where IsMatch(name, ".*_meters(_total)?$")
      - set(unit, "1") where unit == "" and IsMatch(name, ".*_total$")
receivers:
  prometheus/cw_k8s_ci_v0_apiserver:
    config:
      scrape_configs:
      - bearer_token_file: /var/run/secrets/kubernetes.io/serviceaccount/token
        job_name: kubernetes-apiserver
        kubernetes_sd_configs:
        - namespaces:
            names:
            - default
          role: endpoints
        relabel_configs:
        - action: keep
          regex: kubernetes
          source_labels:
          - __meta_kubernetes_service_name
        - action: keep
          regex: https
          source_labels:
          - __meta_kubernetes_endpoint_port_name
        - replacement: /metrics
          target_label: __metrics_path__
        scheme: https
        scrape_interval: 30s
        scrape_timeout: 10s
        tls_config:
          ca_file: /var/run/secrets/kubernetes.io/serviceaccount/ca.crt
          insecure_skip_verify: false
  prometheus/cw_k8s_ci_v0_kube_state_metrics:
    config:
      scrape_configs:
      - job_name: kube-state-metrics
        scheme: https
        scrape_interval: 30s
        scrape_timeout: 10s
        static_configs:
        - targets:
          - kube-state-metrics.amazon-cloudwatch.svc:8443
        tls_config:
          ca_file: /etc/amazon-cloudwatch-observability-agent-cert/tls-ca.crt
service:
  extensions:
  - sigv4auth/cw_k8s_ci_v0_cwotel
  pipelines:
    metrics/cw_k8s_ci_v0_apiserver:
      exporters:
      - otlphttp/cw_k8s_ci_v0_cwotel
      processors:
      - filter/cw_k8s_ci_v0_scrape_metadata
      - transform/cw_k8s_ci_v0_set_unit
      - metricstarttime/cw_k8s_ci_v0
      - transform/cw_k8s_ci_v0_apiserver_extract_version
      - filter/cw_k8s_ci_v0_apiserver_build_info
      - transform/cw_k8s_ci_v0_set_scope_apiserver
      - transform/cw_k8s_ci_v0_apiserver_cleanup_version
      - transform/cw_k8s_ci_v0_set_cluster_name
      - transform/cw_k8s_ci_v0_set_component
      - transform/cw_k8s_ci_v0_promote_component
      - resourcedetection/cw_k8s_ci_v0
      - transform/cw_k8s_ci_v0_clear_schema_url
      - transform/cw_k8s_ci_v0_set_cloud_resource_id
      - awsattributelimit/cw_k8s_ci_v0
      - batch/cw_k8s_ci_v0_cwotel
      receivers:
      - prometheus/cw_k8s_ci_v0_apiserver
    metrics/cw_k8s_ci_v0_kube_state_metrics:
      exporters:
      - otlphttp/cw_k8s_ci_v0_cwotel
      processors:
      - filter/cw_k8s_ci_v0_scrape_metadata
      - transform/cw_k8s_ci_v0_set_unit
      - metricstarttime/cw_k8s_ci_v0
      - transform/cw_k8s_ci_v0_set_scope_kube_state_metrics
      - transform/cw_k8s_ci_v0_set_cluster_name
      - transform/cw_k8s_ci_v0_ksm_clean_resource
      - groupbyattrs/cw_k8s_ci_v0_ksm
      - transform/cw_k8s_ci_v0_ksm_promote
      - k8sattributes/cw_k8s_ci_v0_node
      - resourcedetection/cw_k8s_ci_v0
      - transform/cw_k8s_ci_v0_clear_schema_url
      - transform/cw_k8s_ci_v0_set_cloud_resource_id
      - awsattributelimit/cw_k8s_ci_v0
      - batch/cw_k8s_ci_v0_cwotel
      receivers:
      - prometheus/cw_k8s_ci_v0_kube_state_metrics

BinaryData
====

Events:  <none>

取得できるメトリクスについて

OTel Container Insights で収集されるメトリクスは下表の通りです。

ソース メトリクスカテゴリリ 前提条件
cAdvisor CPU なし
cAdvisor メモリ なし
cAdvisor ネットワーク なし
cAdvisor ディスク・ファイルシステム なし
Prometheus Node Exporter CPU なし
Prometheus Node Exporter メモリ なし
Prometheus Node Exporter ディスク なし
Prometheus Node Exporter ファイルシステム なし
Prometheus Node Exporter ネットワーク なし
Prometheus Node Exporter システム なし
Prometheus Node Exporter VMStat なし
Prometheus Node Exporter Netstat・ソケット なし
NVIDIA DCGM GPU 利用率・性能 NVIDIA device plugin / container toolkit が必要
NVIDIA DCGM GPU メモリ 同上
NVIDIA DCGM GPU 電力・温度 同上
NVIDIA DCGM GPU スロットリング 同上
NVIDIA DCGM GPU エラー・信頼性 同上
NVIDIA DCGM GPU NVLink 同上
NVIDIA DCGM GPU 情報系 同上
AWS Neuron Monitor NeuronCore Neuron driver / device plugin が必要
AWS Neuron Monitor NeuronDevice 同上
AWS Neuron Monitor Neuron システム 同上
AWS Elastic Fabric Adapter EFA メトリクス EFA device plugin が必要
NVMe NVMe SMART なし
Kube State Metrics Pod / node / Deployment / DaemonSet / StatefulSet / ReplicaSet / Job / CronJob / Service / Namespace / PersistentVolume / PersistentVolumeClaim 関連 なし
Kubernetes API server API サーバーおよび etcd なし

Container Insights with OpenTelemetry metrics for Amazon EKS | Amazon CloudWatch

基本的には Container Insights with enhanced observability で取得できるメトリクスと同じです。

https://docs.aws.amazon.com/AmazonCloudWatch/latest/monitoring/Container-Insights-metrics-enhanced-EKS.html

ただし、SageMaker AI HyperPod 関連のメトリクスが消えていることには注意が必要です。

https://dev.classmethod.jp/articles/monitor-sagemaker-hyperpod-cloudwatch-addon/

CloudWatch Observability EKS アドオンからカスタム Prometheus スクレイプターゲットを追加して PromQL でクエリしてみる

CloudWatch Observability EKS アドオンでは、カスタム Prometheus スクレイプターゲットを追加することができます。

https://docs.aws.amazon.com/AmazonCloudWatch/latest/monitoring/ContainerInsights-Prometheus-Setup-configure.html

OTel Container Insights でも同様のことができると思われるので試してみます。
まず、nginx-exporter を含めた Nginx をデプロイします。

Nginx の設定
apiVersion: apps/v1
kind: Deployment
metadata:
  namespace: default
  name: nginx
  labels:
    app.kubernetes.io/name: nginx
spec:
  selector:
    matchLabels:
      app.kubernetes.io/name: nginx
  replicas: 1
  template:
    metadata:
      labels:
        app.kubernetes.io/name: nginx
      annotations:
        prometheus.io/scrape: "true"
        prometheus.io/port: "9113"
        prometheus.io/path: "/metrics"
    spec:
      containers:
        - image: nginx
          imagePullPolicy: Always
          name: nginx
          ports:
            - containerPort: 80
          volumeMounts:
            - name: nginx-config
              mountPath: /etc/nginx/conf.d
        - name: nginx-exporter
          image: nginx/nginx-prometheus-exporter:0.11.0
          args:
            - -nginx.scrape-uri=http://127.0.0.1/nginx_status
          ports:
            - containerPort: 9113
              name: metrics
      volumes:
        - name: nginx-config
          configMap:
            name: nginx-config
---
apiVersion: v1
kind: ConfigMap
metadata:
  namespace: default
  name: nginx-config
data:
  default.conf: |
    server {
        listen 80;
        server_name localhost;

        location / {
            root /usr/share/nginx/html;
            index index.html index.htm;
        }

        # nginx status endpoint for prometheus exporter
        location /nginx_status {
            stub_status on;
            access_log off;
            allow 127.0.0.1;
            deny all;
        }
    }
---
apiVersion: v1
kind: Service
metadata:
  namespace: default
  name: nginx-service
  labels:
    app.kubernetes.io/name: nginx
  annotations:
    prometheus.io/scrape: "true"
    prometheus.io/port: "9113"
    prometheus.io/path: "/metrics"
spec:
  selector:
    app.kubernetes.io/name: nginx
  ports:
    - name: http
      port: 80
      targetPort: 80
    - name: metrics
      port: 9113
      targetPort: 9113
  type: ClusterIP

続いて、CloudWatch Observability EKS アドオンで下記追加設定を行います。

agents:
  - name: cloudwatch-agent
  - name: cloudwatch-agent-cluster-scraper
    otelConfig:
      receivers:
        prometheus/kubernetes_pod:
          config:
            global:
              scrape_interval: 1m
              scrape_timeout: 10s
            scrape_configs:
              - job_name: kubernetes-pod
                sample_limit: 10000
                metrics_path: /metrics
                kubernetes_sd_configs:
                  - role: pod
                    namespaces:
                      names:
                        - default
                relabel_configs:
                  - source_labels:
                      - __meta_kubernetes_pod_annotation_prometheus_io_scrape
                    action: keep
                    regex: "true"
                  - source_labels:
                      - __meta_kubernetes_pod_ip
                      - __meta_kubernetes_pod_annotation_prometheus_io_port
                    action: replace
                    target_label: __address__
                    separator: ":"
                  - source_labels:
                      - __meta_kubernetes_pod_annotation_prometheus_io_path
                    action: replace
                    target_label: __metrics_path__
                    regex: (.+)
                  - source_labels:
                      - __meta_kubernetes_namespace
                    action: replace
                    target_label: Namespace
                  - source_labels:
                      - __meta_kubernetes_pod_name
                    action: replace
                    target_label: pod_name
                  - source_labels:
                      - __meta_kubernetes_pod_container_name
                    action: replace
                    target_label: container_name
                  - source_labels:
                      - __meta_kubernetes_pod_label_app_kubernetes_io_name
                    action: replace
                    target_label: app
      service:
        pipelines:
          metrics/kubernetes_pod:
            receivers:
              - prometheus/kubernetes_pod
            exporters:
              - otlphttp/cw_k8s_ci_v0_cwotel

今回は default 名前空間で prometheus.io/scrape: "true" ラベルを付与した Pod を全てスクレイピング対象にしています。
また、名前空間やラベルでフィルタリングしつつ、クラスター全体でスクレイピングしたいのでcloudwatch-agent-cluster-scraper を利用しています。
この際、cloudwatch-agent 側に意図せず設定が入ってしまいエラーになってしまったので、追加設定無しで下記行を追加しています。

  • name: cloudwatch-agent

既存の exporters 経由でメトリクスを転送できるよう、pipelines 設定も忘れずに行う必要があります。
マネジメントコンソールから追加設定を行い、更新します。

スクリーンショット 2026-04-18 23.02.24.png

しばらく待った後、CloudWatch Query Studio から PromQL でクエリを行うと、Nginx のメトリクスが取得できました。

スクリーンショット 2026-04-18 23.10.51.png

Amazon Managed Service for Prometheus (AMP) との比較

CloudWatch が OpenTelemetry メトリクスに対応してくると、Amazon Managed Service for Prometheus (AMP) との違いが気になります。
ざっと挙げると下記のような違いがあります。

  • OTel Container Insights は CloudWatch Observability EKS アドオンか CloudWatch エージェントが必須。
  • OTel Container Insights は標準でダッシュボードが用意されており、必要に応じて PromQL エンドポイント経由で Grafana から可視化することも可能。AMP は基本的に Grafana とセットで利用する。
  • Amazon Managed Prometheus はデータの保持期間が最大 1095 日。
  • Amazon Managed Prometheus はマネージドコレクター (EKS に Prometheus エージェントをインストールせずにスクレイピングする機能) を利用できる。
  • OTel Container Insights は 1 分間隔のメトリクスしか収集できない。Amazon Managed Prometheus だとスクレイピング間隔を自由に設定でき、より高い解像度でメトリクスを収集できる。

現状 CloudWatch で OpenTelemetry メトリクスを扱う際の料金が不明なので、大事な判断基準であるコスト観点の比較ができません。
とはいえ、Container Insights with enhanced observability の料金感とそこまで乖離しないのではないかと思っています。

1/Amazon EKS 用のオブザーバビリティが強化された Container Insights
オブザーバビリティが強化された Container Insights を使用する場合、コンテナ環境全体の観測数に基づいて課金されます。ここで、取り込まれる観測の合計数は、コンテナの設定 (コンテナコンポーネントの数を含む) によって異なります。10 個のノード、1 個の名前空間、5 個の一意のサービス名、16 個のワークロード、20 個のポッド、20 個のコンテナを含む 1 個のクラスターをモニタリングし、1 か月分のデータポイントが報告されるセットアップを想定します。
EKS コンテナコンポーネントごとに、1 分あたりに取り込まれる観測の平均数があります。すべてのクラスターは 1,720 回の観測を報告し、すべてのノードは 68 回の観測を報告し、すべての名前空間は 2 回の観測を報告し、すべてのサービスは 2 回の観測を報告し、すべてのワークロードは 7 回の観測を報告し、すべてのポッドは 138 回の観測を報告し、すべてのコンテナが平均 21 回/分の観測を報告します。
モニタリング料金は次のとおりです:
1 か月あたりの Container Insights による観測の合計数
= [(1 個のクラスター * 1,720 回の観測) + (10 個のノード * 68 回の観測) + (1 個の名前空間 * 2 回の観測) + (5 個のサービス * 2 回の観測) + (16 個のワークロード * 7 回の観測) + (20 個のポッド * 138 回の観測) + (20 個のコンテナ * 21 回の観測)] _ 60 分 _ 24 時間 _ 30 日間
= [ 1,720 + 680 + 2 + 10 + 112 + 2760 + 420] _ 43,200 分/月 = 1 か月あたり 246,412,800 回の観測
Container Insights の 1 か月あたりの請求額 = 246,412,800 * 0.21 USD/100 万回の観測 = 51.75 USD
https://aws.amazon.com/jp/cloudwatch/pricing/

それぞれの方式で試算して決めるケースもあると思いますが、スクレイピング間隔を細かく設定したい等の要件が無ければ CloudWatch を利用した方が良いケースが多くなりそうな気がします。

最後に

OTel Container Insights が利用できるようになり、より OpenTelemetry の標準的な仕様に近い形でメトリクスを収集できるようになりました。
合わせて CloudWatch が PromQL でクエリを行えるようになっており、今後の AMP との棲み分けも気になる所です。

https://dev.classmethod.jp/articles/cloudwatch-open-telemetry-metrics/

コストが高すぎる等が無ければ、既に Enhanced Container Insights を利用しているケースはもちろん、AMP + AMG を利用しているケースからの移行も考えられます。
今から GA が待ち遠しいですね!

この記事をシェアする

関連記事