EKS on Fargate で ADOT を使用した Container Insights の取得
この記事のゴール
EKS on Fargate で AWS Distro for OpenTelemetry(以下、ADOT) を使用し、CloudWatch Container Insighs が使用できるようにします。
こちらのブログに詳細な方法が記録されており、こちらを元に進めていきます。
EKS on Fargate における Container Insights について
まず Contianer Insights について、簡単にまとめます。
Container Insights は、埋め込みメトリクス形式(EMF)を使用して、パフォーマンスログイベントとしてデータを収集します。
このパフォーマンスログから、EKS の場合はクラスター、ノード、ポッド、タスク、サービスのレベルで CloudWatch メトリクスとして集約されたメトリクスを作成します。Container Insights が収集するメトリクスは、CloudWatch 自動ダッシュボードで使用でき、CloudWatch コンソールの [メトリクス] セクションでも表示できます。
EKS ではさらに、 Amazon EKS 向けにオブザーバビリティが強化された Container Insights というものがあり、コンテナレベルまでの詳細なヘルス、パフォーマンス、ステータスのメトリクスだけでなく、コントロールプレーンのメトリクスも収集できます。
こちらの機能を使用するには、Amazon CloudWatch Observability EKS アドオンまたは CloudWatch エージェントを使用する必要があります。
Fargate で Container Insights を利用する場合、AWS Distro for OpenTelemetry を使用する必要があります。
「Amazon EKS 向けにオブザーバビリティが強化された Container Insights」は、Fargate ではサポートされていません。
アーキテクチャ
冒頭のブログより
Kubernetes クラスターのワーカーノード上の kubelet は、CPU、メモリ、ディスク、ネットワーク使用量などのリソースメトリクスを
/metrics/cadvisor
エンドポイントで公開しています。
しかし、EKS Fargate のネットワーキングアーキテクチャでは、Pod はそのワーカーノード上の kubelet に直接アクセスすることを許可されていません。
したがって、ADOT Collector は Kubernetes API サーバーを呼び出して、ワーカーノード上の kubelet への接続をプロキシし、そのノード上のワークロードの kubelet の cAdvisor メトリクスを収集します。
Fargate の場合、基盤となるホストは AWS 管理となるためにワーカーノード上の kubelet へアクセスすることができないようになっています。
そのため、直接 kubelet へメトリクスを収集するのではなく、Kubernetes API サーバー経由で各ワーカーノードの kubelet へアクセスして、cAdvisor メトリクスを収集するというアーキテクチャとなります。
Kubernetes のサービスディスカバリーを使用して、Receiver は EKS クラスター内のすべてのワーカーノードを検出することができます。
したがって、クラスター内のすべてのノードからリソースメトリクスを収集するには、ADOT Collector の 1 つのインスタンスで十分です。
Prometheus Receiver から他のワーカーノード(Fargate)の検出もできると記載があります。
そのため、各ワーカーノードに ADOT Collector Pod を配置する必要はないということですね。
前提条件
当方の環境は、Kubernetes バージョン 1.29 のクラスターを使用しました。
ADOT Collector をデプロイするにあたり、いくつか前提条件があります。
1. Fargate Pod 実行ロールの準備
Fargate 上で起動する Pod から AWS API を呼び出す際に必要となるロールです。
ECR からコンテナイメージをプルしたりする際は、この Pod 実行ロールが使用されます。
--fargate オプションを使用して eksctl でクラスターを作成した場合は、クラスターに Pod 実行ロールが「eksctl-<my-cluster>-FargatePodExecutionRole-<ABCDEFGHIJKL>」という形式で自動的に作成されます。
eksctl を使用して Fargate プロファイルを作成する場合でも、自動的に作成してくれます。
2. Fargate プロファイルの準備
Fargate で実行される Pod をスケジューリングするのに、Fargate プロファイルが必要となります。
今回のブログでは、ADOT Collector とサンプルアプリがデプロイされます。
それぞれ Namespace:fargate-container-insights, golang でデプロイされるようになっています。
各自の環境に合わせて修正いただければと思いますが、当該ブログにある通りにやる場合は以下の2つのプロファイルを作成します。
ProfileName : fargate-container-insights
Namespace : fargate-container-insights
ProfileName : applications
Namespace : golang
3. CWlogsへ送るため、ServiceAccount を IAM ロールに関連づける
awsemf
Exporter を使用しているため、ADOT Collector からパフォーマンスログを CloudWatch Logs へ送信します。
この時使用される IAM ロールを設定するのに、IAM Roles for Service Accounts (IRSA) 機能を使って、Kubernetes のサービスアカウント (Service Account) を IAM ロールに関連付けることで実現できます。
当該ブログ内で、ヘルパースクリプトが用意されているためこちらを使用します。
#!/bin/bash
CLUSTER_NAME=YOUR-EKS-CLUSTER-NAME
REGION=YOUR-EKS-CLUSTER-REGION
SERVICE_ACCOUNT_NAMESPACE=fargate-container-insights
SERVICE_ACCOUNT_NAME=adot-collector
SERVICE_ACCOUNT_IAM_ROLE=EKS-Fargate-ADOT-ServiceAccount-Role
SERVICE_ACCOUNT_IAM_POLICY=arn:aws:iam::aws:policy/CloudWatchAgentServerPolicy
// クラスターの IAM OIDC プロバイダーを作成
eksctl utils associate-iam-oidc-provider \
--cluster=$CLUSTER_NAME \
--approve
// ServiceAccount と IAM ロールを関連づける
eksctl create iamserviceaccount \
--cluster=$CLUSTER_NAME \
--region=$REGION \
--name=$SERVICE_ACCOUNT_NAME \
--namespace=$SERVICE_ACCOUNT_NAMESPACE \
--role-name=$SERVICE_ACCOUNT_IAM_ROLE \
--attach-policy-arn=$SERVICE_ACCOUNT_IAM_POLICY \
ADOT Collector の設定ファイル
マニフェストファイルは冒頭ブログ中盤にあるものをご確認ください。
変更したところや、何を設定しているのかを抜粋して記録します。
ClusterRole, ClusterRoleBinding
メトリクスを収集するために必要な権限が設定されています。
kind: ClusterRole
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: adotcol-admin-role
rules:
- apiGroups: [""]
resources:
...
verbs: ["get", "list", "watch"]
- nonResourceURLs: [ "/metrics/cadvisor"]
verbs: ["get", "list", "watch"]
---
kind: ClusterRoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: adotcol-admin-role-binding
subjects:
- kind: ServiceAccount
name: adot-collector
namespace: fargate-container-insights
roleRef:
kind: ClusterRole
name: adotcol-admin-role
apiGroup: rbac.authorization.k8s.io
Receiver
ADOT Collector の設定は ConfigMap で行われています。
Prometheus Receiver を使用し、Node をスクレイプ対象としています。
apiVersion: v1
kind: ConfigMap
metadata:
name: adot-collector-config
namespace: fargate-container-insights
labels:
app: aws-adot
component: adot-collector-config
data:
adot-collector-config: |
receivers:
prometheus:
config:
global:
scrape_interval: 1m
scrape_timeout: 40s
scrape_configs:
- job_name: 'kubelets-cadvisor-metrics'
sample_limit: 10000
scheme: https
kubernetes_sd_configs:
- role: node
tls_config:
ca_file: /var/run/secrets/kubernetes.io/serviceaccount/ca.crt
bearer_token_file: /var/run/secrets/kubernetes.io/serviceaccount/token
relabel_configs
セクションでは、Node の Label をメトリクスのラベルとして設定したり、スクレイプ対象のアドレス、メトリクスパスを変更しています。
relabel_configs:
- action: labelmap
regex: __meta_kubernetes_node_label_(.+)
- target_label: __address__
# Changes the address to Kube API server's default address and port
replacement: kubernetes.default.svc:443
- source_labels: [__meta_kubernetes_node_name]
regex: (.+)
target_label: __metrics_path__
# Changes the default metrics path to kubelet's proxy cadvdisor metrics endpoint
replacement: /api/v1/nodes/$${1}/proxy/metrics/cadvisor
次に metric_relabel_configs
セクションですが、ここが何をやっているのかいまいちわからずでした。
metric_relabel_configs:
# extract readable container/pod name from id field
- action: replace
source_labels: [id]
regex: '^/machine\.slice/machine-rkt\\x2d([^\\]+)\\.+/([^/]+)\.service$'
target_label: rkt_container_name
replacement: '$${2}-$${1}'
- action: replace
source_labels: [id]
regex: '^/system\.slice/(.+)\.service$'
target_label: systemd_service_name
replacement: '$${1}'
rkt
とは過去に Kubernetest のコンテナランタイムで使用されていたもののようです。
後者は systemd で実行されているサービスということでしょうか。
今回取得してきたメトリクスにはこれらに該当するメトリクスは確認できておりません。
Processor
metricstransform
ラベルのRenameを行っています。
experimental_match_labels: {"container": "\\S", "LaunchType": "fargate"}
では、container
ラベルが存在し、LaunchType=fargate
となっているメトリクスのみを変更しています。
processors:
# rename labels which apply to all metrics and are used in metricstransform/rename processor
metricstransform/label_1:
transforms:
- include: .*
match_type: regexp
action: update
operations:
- action: update_label
label: name
new_label: container_id
- action: update_label
label: kubernetes_io_hostname
new_label: NodeName
- action: update_label
label: eks_amazonaws_com_compute_type
new_label: LaunchType
# rename container and pod metrics which we care about.
# container metrics are renamed to `new_container_*` to differentiate them with unused container metrics
metricstransform/rename:
transforms:
- include: container_spec_cpu_quota
new_name: new_container_cpu_limit_raw
action: insert
match_type: regexp
experimental_match_labels: {"container": "\\S", "LaunchType": "fargate"}
- include: container_spec_cpu_shares
new_name: new_container_cpu_request
action: insert
match_type: regexp
experimental_match_labels: {"container": "\\S", "LaunchType": "fargate"}
...
filter
new_container_
,pod_
から始まるメトリクスのみをフィルタリングしています。
ここで取得したいメトリクスを絞る設定ができそうです。
# filter out only renamed metrics which we care about
filter:
metrics:
include:
match_type: regexp
metric_names:
- new_container_.*
- pod_.*
cumulativetodelta, deltatorate
cumulativetodelta
について、ブログの書き方と最新バージョンの書き方が異なるようで、少々手直しが必要でした。
この2つの processor の役割としては、以下の2つと理解しています。
- cumulativetodelta : 累積値のメトリクスをデルタ値(増分値)に変換
- deltatorate : デルタ値をレート値(単位時間あたりの値)に変換
# convert cumulative sum datapoints to delta
cumulativetodelta:
include:
match_type: strict
metrics:
- new_container_cpu_usage_seconds_total
- pod_cpu_usage_seconds_total
- pod_memory_pgfault
- pod_memory_pgmajfault
...
# convert delta to rate
deltatorate:
metrics:
- new_container_cpu_usage_seconds_total
- pod_cpu_usage_seconds_total
- pod_memory_pgfault
- pod_memory_pgmajfault
...
experimental_metricsgeneration
既存のメトリクスから計算などを行い、新しくメトリクスを作成します。
「CPU の使用率」などはメトリクスとして用意されていないため、それをこの processor で作成しています。
experimental_metricsgeneration/1:
rules:
- name: pod_network_total_bytes
unit: Bytes/Second
type: calculate
metric1: pod_network_rx_bytes
metric2: pod_network_tx_bytes
operation: add
...
experimental_metricsgeneration/2:
rules:
- name: pod_cpu_utilization_over_pod_limit
type: calculate
unit: Percent
metric1: pod_cpu_usage_total
metric2: pod_cpu_limit
operation: percent
metricstransform(2回目)
最後にメトリクスのラベル付けとして、必要なラベルを付与しています。
上記の複数 processor により作成・変更したメトリクスに対しても、最終的にラベル付けをしているようです。
# add `Type` and rename metrics and labels
metricstransform/label_2:
transforms:
- include: pod_.*
match_type: regexp
action: update
operations:
- action: add_label
new_label: Type
new_value: "Pod"
- include: new_container_.*
match_type: regexp
action: update
operations:
- action: add_label
new_label: Type
new_value: Container
...
resourcedetection
環境変数 OTEL_RESOURCE_ATTRIBUTES
と、EKSメタデータ(EKSクラスター名など)を取得しています。
今回の場合、StatefulSet の中で OTEL_RESOURCE_ATTRIBUTES
として ClusterName=YOUR-EKS-CLUSTER-NAME
を設定しています。これでクラスター名を取得しているようです。
# add cluster name from env variable and EKS metadata
resourcedetection:
detectors: [env, eks]
host で OpenTelemetry Collector 実行している場合などは host の情報も取得できる processor のようです。
Exporter
awsefm
exporter で CloudWatch へメトリクスを送っています。
exporters:
awsemf:
log_group_name: '/aws/containerinsights/{ClusterName}/performance'
log_stream_name: '{PodName}'
namespace: 'ContainerInsights'
region: YOUR-AWS-REGION
resource_to_telemetry_conversion:
enabled: true
eks_fargate_container_insights_enabled: true
parse_json_encoded_attr_values: ["kubernetes"]
dimension_rollup_option: NoDimensionRollup
metric_declarations:
- dimensions: [ [ClusterName, LaunchType], [ClusterName, Namespace, LaunchType], [ClusterName, Namespace, PodName, LaunchType]]
metric_name_selectors:
- pod_cpu_utilization_over_pod_limit
- pod_cpu_usage_total
- pod_cpu_limit
- pod_memory_utilization_over_pod_limit
- pod_memory_working_set
- pod_memory_limit
- pod_network_rx_bytes
- pod_network_tx_bytes
気になるオプションを個別に見ていきます。
eks_fargate_container_insights_enabled
について
awsemfexporter の README.md にはこのオプションを見つけられませんでしたが、config.go の方に記載がありました。
// EKSFargateContainerInsightsEnabled is an option to reformat certin metric labels so that they take the form of a high level object
// The end result will make the labels look like those coming out of ECS and be more easily injected into cloudwatch
// Note that at the moment in order to use this feature the value "kubernetes" must also be added to the ParseJSONEncodedAttributeValues array in order to be used
EKSFargateContainerInsightsEnabled boolmapstructure:"eks_fargate_container_insights_enabled"
メトリクスラベルを CloudWatch へ送るのに必要なオプションなのでしょうか?
ちょっとこの説明からだけでは挙動がイメージできません。
parse_json_encoded_attr_values: ["kubernetes"]
とセットである必要があるとのこと。
metric_declarations
について
CloudWatch へ送るメトリクス、ディメンションを指定できます。
これで監視したいメトリクスを選択したり、ディメンションをカスタマイズできます。
ADOT Collector を StatefulSet としてデプロイ
aws-otel-collector
のイメージタグを最新の v0.40.0
に変更しています。
apiVersion: v1
kind: Service
metadata:
name: adot-collector-service
namespace: fargate-container-insights
...
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: adot-collector
namespace: fargate-container-insights
labels:
app: aws-adot
component: adot-collector
spec:
...
template:
metadata:
...
spec:
serviceAccountName: adot-collector
securityContext:
fsGroup: 65534
containers:
- image: amazon/aws-otel-collector:v0.40.0
name: adot-collector
...
サンプルアプリのデプロイ
今回のブログでは、Namespace : golang
にアプリをデプロイしようとしています。
Namespace が作成されていないと思うので、一緒に作成します。
---
apiVersion: v1
kind: Namespace
metadata:
creationTimestamp: null
name: golang
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: webapp
namespace: golang
spec:
replicas: 2
...
template:
...
spec:
containers:
- name: go
image: public.ecr.aws/awsvijisarathy/prometheus-webapp:latest
...
このアプリが何を行うアプリなのかがわかりませんでした。
image 自体は公開されているものの、詳細な仕様は確認できませんでした。
まぁ取得しているメトリクスは cAdvisor から取得しているメトリクスですし、独自メトリクスを取得しようとしているわけでもないため、特に問題ないでしょう。
結果
CloudWatch のマネジメントコンソールを確認してみましょう。
ContainerInsights
というカスタムメトリクスが作成されています。
こちらを見てみると、awsemf
Exporter で指定しているディメンションが作成されています。
メトリクスを見てみると、メトリクスが取れていることがわかります。
Namespace ごと、Pod ごとのメトリクスが取得できています。
続いて、[Container Insights] > [コンテナマップ] を確認してみます。
CPU % によって、色が 緑 -> 黄 -> 赤 と変化してくれるようです。
[Container Insights] > [パフォーマンスのモニタリング] を確認してみると、ダッシュボードも用意されています。
名前空間、サービスなどでフィルタリングもできます。
終わりに
EKS on Fargate で AWS Distro for OpenTelemetry(以下、ADOT) を使用し、CloudWatch Container Insighs が使用できるようになりました。
ブログにある通りの手順で実施しましたが、以下の疑問点があります。こちらは宿題にしたいと思います。
Q: アドオンで入れられる?
EKS では ADOT アドオンが用意されており、ADOT Operator をインストールすることが可能です。
EKS on Fargate でもおそらくできるのかと思います。
注意点として、cert-manager が必要となります。
Fargate の場合、kubelet がデフォルトで 10250
ポートを使用している関係で、cert-manager webhook で使用するポートを変更する必要があります。
If you're using AWS Fargate or else if you've specifically configured cert-manager to run the host's network, be aware that kubelet listens on port 10250 by default which clashes with the default port for the cert-manager webhook.
As such, you'll need to change the webhook's port when setting up cert-manager.
For installations using Helm, you can set the webhook.securePort parameter when installing cert-manager either using a command line flag or an entry in your values.yaml file.
If you have a port clash, you could see confusing error messages regarding untrusted certs. See #3237 for more details.
Discussion