EKS on Fargate で Pod のメトリクスを取得してみた(ADOT 編)
はじめに
クラウド事業本部、あきやまです。
日々業務をしている中で運用監視って大事ですよね。
今回はEKS on Fargate 環境で ADOT(AWS Distro for OpenTelemetry)を使って Fargate 上のコンテナメトリクスを CloudWatch Container Insights に送信する方法を検証してみました。
EKS on Fargate では DaemonSet が使えないため、メトリクス収集にも EC2 とは異なるアプローチが必要です。AWS が公式に推奨している StatefulSet パターン で ADOT Collector をデプロイし、Container Insights のメトリクスを収集します。
環境
| 項目 | 値 |
|---|---|
| OS | macOS Tahoe 26.3 |
| AWS CLI | v2.32.32 |
| kubectl | v1.35.2 |
| eksctl | v0.222.0 |
| EKS クラスターバージョン | 1.31 |
| リージョン | ap-northeast-1 |
| ADOT Collector イメージ | public.ecr.aws/aws-observability/aws-otel-collector:latest |
結論
EKS on Fargate では、ADOT Collector を StatefulSet としてデプロイし、Kubernetes API Server 経由で cAdvisor メトリクスをスクレイプすることで、CloudWatch Container Insights にメトリクスを送信できます。
| 項目 | 結果 |
|---|---|
| デプロイ方式 | StatefulSet(replicas: 1) |
| メトリクス取得方法 | Kubernetes API Server 経由で各ノードの /metrics/cadvisor をプロキシスクレイプ |
| 収集できるメトリクス | CPU / メモリ / ネットワーク / ストレージ(Pod・コンテナ単位) |
| 送信先 | CloudWatch Container Insights(EMF 形式) |
| 必要なリソース | Namespace / ServiceAccount(IRSA) / 公式テンプレート一式(RBAC / ConfigMap / Service / StatefulSet) |
※ Fargate では DaemonSet ベースの CloudWatch Agent が使えないため、Container Insights の Enhanced Observability(Accelerated Compute Metrics 等)は利用できません。基本的な CPU / メモリ / ネットワークメトリクスが対象です。
EKS on Fargate のメトリクス収集アーキテクチャ
EC2 vs Fargate の違い
メトリクス収集において、EC2 と Fargate ではアーキテクチャが大きく異なります。
EKS on EC2(DaemonSet 方式)
EKS on Fargate(StatefulSet ADOT 方式)
比較表
| 比較軸 | EKS on EC2 (DaemonSet) | EKS on Fargate (StatefulSet ADOT) |
|---|---|---|
| エージェント | CloudWatch Agent | ADOT Collector |
| デプロイ方式 | DaemonSet(ノードごとに1つ) | StatefulSet(クラスターに1つ) |
| メトリクス取得経路 | ノードの kubelet に直接アクセス | Kubernetes API Server 経由でプロキシ |
| Container Insights | Enhanced Observability 対応 | 基本メトリクスのみ |
| IAM 認証 | IRSA / Pod Identity | IRSA |
| 設定方法 | Helm chart / Add-on | YAML マニフェスト |
| スケーラビリティ | ノード数に自動スケール | replicas の手動調整が必要 |
環境準備
検証用の EKS クラスターを eksctl で構築します。Fargate 専用クラスターとして作成し、ADOT Collector 用の fargate-container-insights Namespace と検証ワークロード用の demo Namespace の Fargate Profile も同時に定義します。
EKS クラスターの作成
apiVersion: eksctl.io/v1alpha5
kind: ClusterConfig
metadata:
name: fargate-logging-test
region: ap-northeast-1
version: "1.31"
iam:
withOIDC: true
# Fargate 専用のためマネージドノードグループは作成しない
fargateProfiles:
# kube-system と CoreDNS 用(Fargate 専用クラスターで必須)
- name: fp-system
selectors:
- namespace: kube-system
- namespace: default
# 検証用ワークロード
- name: fp-demo
selectors:
- namespace: demo
# ADOT Collector(メトリクス収集用)
- name: fp-container-insights
selectors:
- namespace: fargate-container-insights
eksctl create cluster -f cluster-config.yaml
※ クラスター作成には 15〜20分ほどかかります。
CoreDNS の Fargate 対応
eksctl で Fargate 専用クラスターを作成した場合、CoreDNS が EC2 ノードを待ち続けて Pending のままになることがあります。以下で Fargate 上で動作するように修正します。
# CoreDNS から EC2 用のアノテーションを削除
kubectl patch deployment coredns \
-n kube-system \
--type json \
-p='[{"op": "remove", "path": "/spec/template/metadata/annotations/eks.amazonaws.com~1compute-type"}]'
# Pod を再起動して Fargate で起動させる
kubectl rollout restart deployment coredns -n kube-system
# CoreDNS が Running になることを確認
kubectl get pods -n kube-system -l k8s-app=kube-dns
検証用アプリケーションのデプロイ
メトリクス収集の対象となるワークロードを demo Namespace にデプロイします。
apiVersion: v1
kind: Namespace
metadata:
name: demo
apiVersion: v1
kind: Service
metadata:
name: web-app
namespace: demo
spec:
selector:
app: web-app
ports:
- port: 80
targetPort: 80
type: ClusterIP
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: web-app
namespace: demo
spec:
replicas: 2
selector:
matchLabels:
app: web-app
template:
metadata:
labels:
app: web-app
spec:
containers:
# コンテナ 1: nginx(メインアプリケーション)
- name: nginx
image: nginx:1.25
ports:
- containerPort: 80
resources:
requests:
cpu: "100m"
memory: "128Mi"
limits:
cpu: "200m"
memory: "256Mi"
# コンテナ 2: log-generator(サイドカー / ログ生成用)
- name: log-generator
image: busybox:1.36
command:
- /bin/sh
- -c
- |
i=0
while true; do
i=$((i+1))
echo "{\"level\":\"info\",\"container\":\"log-generator\",\"msg\":\"heartbeat #${i}\",\"ts\":\"$(date -u +%Y-%m-%dT%H:%M:%SZ)\"}"
sleep 10
done
resources:
requests:
cpu: "50m"
memory: "64Mi"
limits:
cpu: "100m"
memory: "128Mi"
kubectl apply -f namespace-demo.yaml
kubectl apply -f deployment-web-app.yaml
Pod が起動したことを確認します。
kubectl get pods -n demo -o wide
READY が 2/2、NODE が fargate- で始まることを確認します。
やってみた
環境が整ったので、ここからは ADOT Collector の構築手順です。
Step 0: Namespace の作成
ADOT Collector をデプロイする fargate-container-insights Namespace を作成します。
apiVersion: v1
kind: Namespace
metadata:
name: fargate-container-insights
kubectl apply -f namespace-fargate-insights.yaml
Namespace が作成されたことを確認します。
kubectl get namespace fargate-container-insights
Step 1: IRSA(IAM Roles for Service Accounts)の設定
ADOT Collector が CloudWatch にメトリクスを送信するために、IRSA で IAM ロールを ServiceAccount に紐づけます。
# ADOT Collector 用の ServiceAccount を作成(IAM ロールを自動作成)
eksctl create iamserviceaccount \
--name adot-collector \
--namespace fargate-container-insights \
--cluster fargate-logging-test \
--region ap-northeast-1 \
--attach-policy-arn arn:aws:iam::aws:policy/CloudWatchAgentServerPolicy \
--approve
ServiceAccount が作成されたことを確認します。
kubectl get serviceaccount adot-collector -n fargate-container-insights -o yaml
eks.amazonaws.com/role-arn アノテーションに IAM ロール ARN が設定されていれば OK です。
Step 2: 公式テンプレートのダウンロードとカスタマイズ
AWS が提供する ADOT Collector の公式テンプレートをダウンロードし、環境に合わせてカスタマイズします。
:::note info
このテンプレートには RBAC / ConfigMap / Service / StatefulSet がすべて 1 ファイルにまとまっています。kubectl apply -f 一発でデプロイできます。
:::
このテンプレートには以下のリソースがすべて含まれています。
| リソース | 役割 |
|---|---|
| ClusterRole / ClusterRoleBinding | ADOT Collector が Kubernetes API にアクセスするための RBAC 権限 |
| ConfigMap | OpenTelemetry パイプラインの設定(Receiver → Processor → Exporter) |
| Service | ADOT Collector のメトリクスエンドポイント(ポート 8888) |
| StatefulSet | ADOT Collector 本体(replicas: 1) |
# 公式テンプレートをダウンロード
curl -O https://raw.githubusercontent.com/aws-observability/aws-otel-collector/main/deployment-template/eks/otel-fargate-container-insights.yaml
クラスター名とリージョンを環境に合わせて置換します。
# クラスター名を置換
sed -i '' 's/YOUR-EKS-CLUSTER-NAME/fargate-logging-test/g' otel-fargate-container-insights.yaml
# リージョンを置換(デフォルトは us-east-1)
sed -i '' 's/us-east-1/ap-northeast-1/g' otel-fargate-container-insights.yaml
Step 3: ADOT Collector のデプロイ
kubectl apply -f otel-fargate-container-insights.yaml
テンプレートに含まれる OpenTelemetry パイプラインは、以下の 10 段階のプロセッサを経てメトリクスを加工します。
prometheus (receiver)
│ Kubernetes API Server 経由で cAdvisor メトリクスをスクレイプ
▼
metricstransform/label_1 ... ラベルのリネーム(name→container_id, compute_type→LaunchType 等)
▼
resourcedetection .......... EKS メタデータ(ClusterName 等)を自動検出
▼
metricstransform/rename .... cAdvisor メトリクスを Pod / Container レベルに分離・リネーム
▼
filter ..................... リネーム済みメトリクス(pod_*, new_container_*)のみ通過
▼
cumulativetodelta .......... 累積値をデルタ値に変換
▼
deltatorate ................ デルタ値をレートに変換(例: bytes → bytes/sec)
▼
metricsgeneration/1 ........ 派生メトリクス生成(CPU millicore 換算、メモリ使用率 等)
▼
metricsgeneration/2 ........ CPU 使用率(pod_cpu_utilization_over_pod_limit)を算出
▼
metricstransform/label_2 ... Type ラベル追加(Pod / Container)、Namespace / PodName ラベルの正規化
▼
batch ...................... 60 秒間隔でバッチ送信
▼
awsemf (exporter)
│ CloudWatch Embedded Metric Format で送信
▼
CloudWatch Container Insights
ロググループ: /aws/containerinsights/{ClusterName}/performance
CloudWatch に送信されるメトリクスとディメンションは以下の通りです。
| メトリクス | 内容 |
|---|---|
| pod_cpu_utilization_over_pod_limit | CPU 使用率(Pod リミット比) |
| pod_cpu_usage_total | CPU 使用量(Millicore) |
| pod_cpu_limit | CPU リミット(Millicore) |
| pod_memory_utilization_over_pod_limit | メモリ使用率(Pod リミット比) |
| pod_memory_working_set | メモリ Working Set |
| pod_memory_limit | メモリリミット |
| pod_network_rx_bytes | ネットワーク受信バイト |
| pod_network_tx_bytes | ネットワーク送信バイト |
| ディメンション |
|---|
| ClusterName, LaunchType |
| ClusterName, Namespace, LaunchType |
| ClusterName, Namespace, PodName, LaunchType |
なぜ Deployment ではなく StatefulSet?
ADOT Collector はクラスター全体の cAdvisor メトリクスを 1 台でスクレイプする設計です。Deployment(replicas: 1)でも動作しますが、ローリングアップデート時に一瞬 2 台になりメトリクスが重複する可能性があります。StatefulSet なら古い Pod が完全に終了してから新しい Pod が起動するため、重複収集を防げます。
Step 4: ADOT Collector の起動確認
kubectl get pods -n fargate-container-insights -o wide
期待される出力:
NAME READY STATUS RESTARTS AGE IP NODE
adot-collector-0 1/1 Running 0 2m 10.0.x.xxx fargate-ip-10-0-x-xxx...
READY が 1/1、STATUS が Running であること、NODE が fargate- で始まることを確認します。
ADOT Collector のログを確認して、スクレイプが正常に動作しているかチェックします。
kubectl logs -n fargate-container-insights adot-collector-0 --tail=20
エラーが出ていなければ、メトリクスの収集と CloudWatch への送信が開始されています。
Step 5: CloudWatch でメトリクスを確認
ロググループにメトリクスデータが送信されていることを確認します。
aws logs describe-log-streams \
--log-group-name /aws/containerinsights/fargate-logging-test/performance \
--order-by LastEventTime \
--descending \
--region ap-northeast-1 \
--query 'logStreams[*].logStreamName' \
--output json
CloudWatch コンソールから Container Insights を確認します。
- CloudWatch コンソール → Container Insights → Performance monitoring を開く
- クラスター
fargate-logging-testを選択 - Pod / Namespace 単位で CPU / メモリ / ネットワークメトリクスが表示されることを確認
CloudWatch Logs Insights でメトリクスデータを直接クエリすることもできます。
fields @timestamp, PodName, Namespace, pod_cpu_usage_total, pod_memory_working_set
| filter Namespace = "demo"
| sort @timestamp desc
| limit 20
Step 6: アプリケーション Pod のメトリクスが収集されていることを検証
demo Namespace の web-app Pod のメトリクスが収集されていることを確認します。
# demo Namespace の Pod が動作中であることを確認
kubectl get pods -n demo -o wide
注意点・制約
検証を通じて発見した制約やハマりポイントをまとめます。
| # | カテゴリ | 内容 |
|---|---|---|
| 1 | DaemonSet 不可 | Fargate では DaemonSet が使えないため、ADOT Collector を StatefulSet としてデプロイする必要があります |
| 2 | Enhanced Observability 非対応 | DaemonSet ベースの CloudWatch Agent が必要な Enhanced Observability(Accelerated Compute Metrics 等)は Fargate では利用できません |
| 3 | API Server 経由のスクレイプ | kubelet に直接アクセスできないため、Kubernetes API Server をプロキシとして経由します。API Server の負荷に注意が必要です |
| 4 | replicas の調整 | Pod 数が多い場合、ADOT Collector の replicas を増やす必要がある場合があります。ただし Prometheus receiver の重複スクレイプに注意してください |
| 5 | Fargate のリソース課金 | ADOT Collector Pod 自体も Fargate で動作するため、CPU / メモリの課金が発生します |
| 6 | IRSA が必要 | ADOT Collector は独立した Pod としてデプロイするため、IRSA を使って ServiceAccount に IAM ロールを紐づける必要があります |
| 7 | resourcedetection の eks ディテクター非対応 |
公式テンプレートのデフォルトは detectors: [env, eks] だが、Fargate では eks ディテクターが IMDS にアクセスできずクラッシュする。detectors: [env] に変更が必要 |
まとめ
本記事では、EKS on Fargate で ADOT Collector を StatefulSet としてデプロイし、CloudWatch Container Insights にメトリクスを送信する方法を検証しました。
実運用を考えるともう少し作りこみが必要ですがどなたかの参考になれば幸いです。








