Amazon Web Services ブログ
AWS Distro for OpenTelemetry を使用した CloudWatch Container Insights の EKS Fargate サポートのご紹介
この記事は Introducing Amazon CloudWatch Container Insights for Amazon EKS Fargate using AWS Distro for OpenTelemetry (記事公開日: 2022 年 2 月 17 日) を翻訳したものです。
イントロダクション
Amazon CloudWatch Container Insights は、お客様がコンテナ化されたアプリケーションやマイクロサービスからメトリクスやログを収集、集約、要約するのに役立ちます。メトリクスデータは、埋め込みメトリクスフォーマット (Embedded Metric Format, EMF) を使用したパフォーマンスログイベントとして収集されます。これらのパフォーマンスログイベントは、構造化された JSON スキーマを使用しており、カーディナリティの高いデータを大規模に取り込んで保存することが可能です。このデータから、CloudWatch はクラスター、ノード、Pod、タスク、および Service の各レベルで集約されたメトリクスを CloudWatch メトリクスとして作成します。Container Insights が収集したメトリクスは、CloudWatch の自動ダッシュボードで利用できます。
AWS Distro for OpenTelemetry (ADOT) はセキュアで、AWS がサポートする OpenTelemetry プロジェクトのディストリビューションです。ADOT を利用することで、ユーザーはアプリケーションを一度だけインストルメント (訳注: アプリケーションに計測のためのコードを追加すること) し、相互に関連があるメトリクスやトレースを、複数の監視ソリューションに送信することができます。最近の ADOT の CloudWatch Container Insights のサポートの開始 (プレビュー) により、お客様は、ADOT を使用して Amazon Elastic Compulte Cloud (Amazon EC2) で実行されている Amazon EKS および Kubernetes クラスターから、CPU、メモリ、ディスク、ネットワーク使用率などのシステムメトリクスを収集することができ、Amazon CloudWatch エージェントと同様のエクスペリエンスが提供されます。本日、ADOT Collector は、CloudWatch Container Insights の EKS Fargate サポートをプレビューで利用できるようになりました。お客様は、Amazon EKS クラスターにデプロイされ AWS Fargate で実行されている Pod の CPU やメモリ使用率などのコンテナおよび Pod のメトリクスを収集し、既存の CloudWatch Container Insights のエクスペリエンスに変更を加えることなく CloudWatch ダッシュボードで表示できるようになりました。これにより、お客様は、トラフィックへの対応やコスト削減のために、スケールアップまたはスケールダウンするかどうかを判断することもできます。
このブログ記事では、EKS Fargate ワークロードから Container Insights メトリクスの収集を可能にする ADOT Collector のパイプラインのコンポーネントのデザインについて説明します。次に、ADOT Collector を設定およびデプロイし、EKS Fargate クラスターにデプロイされたワークロードからシステムメトリクスを収集して CloudWatch に送信する方法を紹介します。
ADOT Collector を使用した Container Insights の EKS Fargate サポートのデザイン
ADOT Collector には、Receiver、Processor、Exporter という 3 つの主要なタイプのコンポーネントで構成されるパイプラインのコンセプトがあります。Receiver は、データが Collector に入る部分です。特定のフォーマットでデータを受け取り、内部フォーマットに変換し、パイプラインで定義された Processor や Exporter に渡します。Receiver はプル型とプッシュ型があります。Processor はオプションのコンポーネントで、データを受信してからエクスポートするまでの間に、データのバッチ処理、フィルタリング、変換などのタスクを実行するために使用されます。Exporter は、メトリクス、ログ、トレースの送信先を決定するために使用されます。Collector のアーキテクチャでは、このようなパイプラインの複数のインスタンスを YAML 設定によって定義することができます。以下の図は、EKS Fargate にデプロイされた ADOT Collector インスタンスのパイプラインコンポーネントを示しています。
Kubernetes クラスターのワーカーノード上の kubelet は、CPU、メモリ、ディスク、ネットワーク使用量などのリソースメトリクスを /metrics/cadvisor エンドポイントで公開しています。しかし、EKS Fargate のネットワーキングアーキテクチャでは、Pod はそのワーカーノード上の kubelet に直接アクセスすることを許可されていません。したがって、ADOT Collector は Kubernetes API サーバーを呼び出して、ワーカーノード上の kubelet への接続をプロキシし、そのノード上のワークロードの kubelet の cAdvisor メトリクスを収集します。これらのメトリクスは Prometheus 形式で利用可能です。そのため、Collector は Prometheus Receiver のインスタンスを Prometheus サーバーの代替として使用し、Kubernetes API サーバーのエンドポイントからこれらのメトリクスをスクレイピングします。Kubernetes のサービスディスカバリーを使用して、Receiver は EKS クラスター内のすべてのワーカーノードを検出することができます。したがって、クラスター内のすべてのノードからリソースメトリクスを収集するには、ADOT Collector の 1 つのインスタンスで十分です。
次に、メトリクスは、フィルタリング、リネーム、データの集約と変換などを行う一連の Processor を通過します。以下は、上で示した EKS Fargate 向けの ADOT Collector インスタンスのパイプラインで使用される Processor のリストです。
- Filter Processor: 名前に基づいてメトリクスを含めたり除外したりします。
- Metrics Transform Processor: メトリクスの名前を変更したり、ラベルをまたいだメトリクスの集約を行います。
- Cumulative to Delta Processor: Cumulative Sum (累積和) を Delta Sum (デルタ和) に変換します。
- Delta to Rate Processor: デルタ和のメトリクスをレートのメトリクスに変換します。レートは Gauge タイプとなります。
- Metrics Generation Processor: 既存のメトリクスを使用して新しいメトリクスを作成します。
パイプラインの最後のコンポーネントは AWS CloudWatch EMF Exporter で、メトリクスを埋め込みメトリクスフォーマット (EMF) に変換し、PutLogEvents API を使用して CloudWatch Logs に直接送信します。以下のリストのメトリクスが、EKS Fargate 上で実行されているワークロードのそれぞれについて、ADOT Collector によって CloudWatch に送信されます。
- 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
各メトリクスは以下のディメンションセットに関連付けられ、ContainerInsights という名前の CloudWatch 名前空間で収集されます。
- ClusterName, LaunchType
- ClusterName, Namespace, LaunchType
- ClusterName, Namespace, PodName, LaunchType
EKS Fargate への ADOT Collector のデプロイ
EKS Fargate クラスターに ADOT Collector をインストールして、ワークロードからメトリクスデータを収集する方法を詳しく見ていきましょう。以下は、ADOT Collector をインストールするための前提条件のリストです。
- Kubernetes のバージョン 1.18 以降をサポートする EKS クラスター。EKS クラスターは、ここで説明されているいずれかの方法で作成できます。
- クラスターが AWS Fargate 上に Pod を作成する場合、Fargate インフラストラクチャ上で実行されるコンポーネントは、お客様に代わって AWS API への呼び出しを行う必要があります。これは、Amazon ECR からコンテナイメージをプルするなどのアクションを実行できるようにするためです。EKS Fargate ポッド実行ロールは、これを行うための IAM アクセス許可を提供します。ここで説明されている手順に従って、EKS Fargate ポッド実行ロールを作成します。
- Fargate で実行される Pod をスケジュールする前に、起動時にどの Pod が Fargate で実行されるかを指定する Fargate プロファイルを定義する必要があります。この記事の実装では、ここで説明されている手順に従って 2 つの Fargate プロファイルを作成します。1 つ目の Fargate プロファイルは fargate-container-insights という名前で、fargate-container-insights という Namespace を指定します。2 つ目の Fargate プロファイルは applications という名前で、golang という Namespace を指定します。
- ADOT Collector には、パフォーマンスログイベントを CloudWatch に送信するために IAM アクセス許可が必要です。これは、EKS でサポートされている IAM Roles for Service Accounts (IRSA) 機能を使って、Kubernetes のサービスアカウント (Service Account) を IAM ロールに関連付けることで実現します。IAM ロールには、AWS 管理ポリシーの CloudWatchAgentServerPolicy をアタッチします。以下に示すヘルパースクリプトを使用して、CLUSTER_NAME と REGION 変数を置換した後、EKS-ADOT-ServiceAccount-Role という名前の IAM ロールを作成してアクセス許可を付与し、adot-collector という Kubernetes サービスアカウントに関連付けることができます。
#!/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
eksctl utils associate-iam-oidc-provider \
--cluster=$CLUSTER_NAME \
--approve
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 \
--approve
次に、以下のマニフェストのプレースホルダー変数 YOUR-EKS-CLUSTER-NAME と YOUR-AWS-REGION を EKS クラスター名と AWS リージョン名に置き換え、Kubernetes StatefulSet として ADOT Collector をデプロイします。
---
kind: ClusterRole
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: adotcol-admin-role
rules:
- apiGroups: [""]
resources:
- nodes
- nodes/proxy
- nodes/metrics
- services
- endpoints
- pods
- pods/proxy
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
# collector configuration section
# update `ClusterName=YOUR-EKS-CLUSTER-NAME` in the env variable OTEL_RESOURCE_ATTRIBUTES
# update `region=YOUR-AWS-REGION` in the emfexporter with the name of the AWS Region where you want to collect Container Insights metrics.
---
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:
- action: labelmap
regex: __meta_kubernetes_node_label_(.+)
# Only for Kubernetes ^1.7.3.
# See: https://github.com/prometheus/prometheus/issues/2916
- 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:
# 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}'
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"}
- include: container_cpu_usage_seconds_total
new_name: new_container_cpu_usage_seconds_total
action: insert
match_type: regexp
experimental_match_labels: {"container": "\\S", "LaunchType": "fargate"}
- include: container_spec_memory_limit_bytes
new_name: new_container_memory_limit
action: insert
match_type: regexp
experimental_match_labels: {"container": "\\S", "LaunchType": "fargate"}
- include: container_memory_cache
new_name: new_container_memory_cache
action: insert
match_type: regexp
experimental_match_labels: {"container": "\\S", "LaunchType": "fargate"}
- include: container_memory_max_usage_bytes
new_name: new_container_memory_max_usage
action: insert
match_type: regexp
experimental_match_labels: {"container": "\\S", "LaunchType": "fargate"}
- include: container_memory_usage_bytes
new_name: new_container_memory_usage
action: insert
match_type: regexp
experimental_match_labels: {"container": "\\S", "LaunchType": "fargate"}
- include: container_memory_working_set_bytes
new_name: new_container_memory_working_set
action: insert
match_type: regexp
experimental_match_labels: {"container": "\\S", "LaunchType": "fargate"}
- include: container_memory_rss
new_name: new_container_memory_rss
action: insert
match_type: regexp
experimental_match_labels: {"container": "\\S", "LaunchType": "fargate"}
- include: container_memory_swap
new_name: new_container_memory_swap
action: insert
match_type: regexp
experimental_match_labels: {"container": "\\S", "LaunchType": "fargate"}
- include: container_memory_failcnt
new_name: new_container_memory_failcnt
action: insert
match_type: regexp
experimental_match_labels: {"container": "\\S", "LaunchType": "fargate"}
- include: container_memory_failures_total
new_name: new_container_memory_hierarchical_pgfault
action: insert
match_type: regexp
experimental_match_labels: {"container": "\\S", "LaunchType": "fargate", "failure_type": "pgfault", "scope": "hierarchy"}
- include: container_memory_failures_total
new_name: new_container_memory_hierarchical_pgmajfault
action: insert
match_type: regexp
experimental_match_labels: {"container": "\\S", "LaunchType": "fargate", "failure_type": "pgmajfault", "scope": "hierarchy"}
- include: container_memory_failures_total
new_name: new_container_memory_pgfault
action: insert
match_type: regexp
experimental_match_labels: {"container": "\\S", "LaunchType": "fargate", "failure_type": "pgfault", "scope": "container"}
- include: container_memory_failures_total
new_name: new_container_memory_pgmajfault
action: insert
match_type: regexp
experimental_match_labels: {"container": "\\S", "LaunchType": "fargate", "failure_type": "pgmajfault", "scope": "container"}
- include: container_fs_limit_bytes
new_name: new_container_filesystem_capacity
action: insert
match_type: regexp
experimental_match_labels: {"container": "\\S", "LaunchType": "fargate"}
- include: container_fs_usage_bytes
new_name: new_container_filesystem_usage
action: insert
match_type: regexp
experimental_match_labels: {"container": "\\S", "LaunchType": "fargate"}
# POD LEVEL METRICS
- include: container_spec_cpu_quota
new_name: pod_cpu_limit_raw
action: insert
match_type: regexp
experimental_match_labels: {"image": "^$", "container": "^$", "pod": "\\S", "LaunchType": "fargate"}
- include: container_spec_cpu_shares
new_name: pod_cpu_request
action: insert
match_type: regexp
experimental_match_labels: {"image": "^$", "container": "^$", "pod": "\\S", "LaunchType": "fargate"}
- include: container_cpu_usage_seconds_total
new_name: pod_cpu_usage_seconds_total
action: insert
match_type: regexp
experimental_match_labels: {"image": "^$", "container": "^$", "pod": "\\S", "LaunchType": "fargate"}
- include: container_spec_memory_limit_bytes
new_name: pod_memory_limit
action: insert
match_type: regexp
experimental_match_labels: {"image": "^$", "container": "^$", "pod": "\\S", "LaunchType": "fargate"}
- include: container_memory_cache
new_name: pod_memory_cache
action: insert
match_type: regexp
experimental_match_labels: {"image": "^$", "container": "^$", "pod": "\\S", "LaunchType": "fargate"}
- include: container_memory_max_usage_bytes
new_name: pod_memory_max_usage
action: insert
match_type: regexp
experimental_match_labels: {"image": "^$", "container": "^$", "pod": "\\S", "LaunchType": "fargate"}
- include: container_memory_usage_bytes
new_name: pod_memory_usage
action: insert
match_type: regexp
experimental_match_labels: {"image": "^$", "container": "^$", "pod": "\\S", "LaunchType": "fargate"}
- include: container_memory_working_set_bytes
new_name: pod_memory_working_set
action: insert
match_type: regexp
experimental_match_labels: {"image": "^$", "container": "^$", "pod": "\\S", "LaunchType": "fargate"}
- include: container_memory_rss
new_name: pod_memory_rss
action: insert
match_type: regexp
experimental_match_labels: {"image": "^$", "container": "^$", "pod": "\\S", "LaunchType": "fargate"}
- include: container_memory_swap
new_name: pod_memory_swap
action: insert
match_type: regexp
experimental_match_labels: {"image": "^$", "container": "^$", "pod": "\\S", "LaunchType": "fargate"}
- include: container_memory_failcnt
new_name: pod_memory_failcnt
action: insert
match_type: regexp
experimental_match_labels: {"image": "^$", "container": "^$", "pod": "\\S", "LaunchType": "fargate"}
- include: container_memory_failures_total
new_name: pod_memory_hierarchical_pgfault
action: insert
match_type: regexp
experimental_match_labels: {"image": "^$", "container": "^$", "pod": "\\S", "LaunchType": "fargate", "failure_type": "pgfault", "scope": "hierarchy"}
- include: container_memory_failures_total
new_name: pod_memory_hierarchical_pgmajfault
action: insert
match_type: regexp
experimental_match_labels: {"image": "^$", "container": "^$", "pod": "\\S", "LaunchType": "fargate", "failure_type": "pgmajfault", "scope": "hierarchy"}
- include: container_memory_failures_total
new_name: pod_memory_pgfault
action: insert
match_type: regexp
experimental_match_labels: {"image": "^$", "container": "^$", "pod": "\\S", "LaunchType": "fargate", "failure_type": "pgfault", "scope": "container"}
- include: container_memory_failures_total
new_name: pod_memory_pgmajfault
action: insert
match_type: regexp
experimental_match_labels: {"image": "^$", "container": "^$", "pod": "\\S", "LaunchType": "fargate", "failure_type": "pgmajfault", "scope": "container"}
- include: container_network_receive_bytes_total
new_name: pod_network_rx_bytes
action: insert
match_type: regexp
experimental_match_labels: {"pod": "\\S", "LaunchType": "fargate"}
- include: container_network_receive_packets_dropped_total
new_name: pod_network_rx_dropped
action: insert
match_type: regexp
experimental_match_labels: {"pod": "\\S", "LaunchType": "fargate"}
- include: container_network_receive_errors_total
new_name: pod_network_rx_errors
action: insert
match_type: regexp
experimental_match_labels: {"pod": "\\S", "LaunchType": "fargate"}
- include: container_network_receive_packets_total
new_name: pod_network_rx_packets
action: insert
match_type: regexp
experimental_match_labels: {"pod": "\\S", "LaunchType": "fargate"}
- include: container_network_transmit_bytes_total
new_name: pod_network_tx_bytes
action: insert
match_type: regexp
experimental_match_labels: {"pod": "\\S", "LaunchType": "fargate"}
- include: container_network_transmit_packets_dropped_total
new_name: pod_network_tx_dropped
action: insert
match_type: regexp
experimental_match_labels: {"pod": "\\S", "LaunchType": "fargate"}
- include: container_network_transmit_errors_total
new_name: pod_network_tx_errors
action: insert
match_type: regexp
experimental_match_labels: {"pod": "\\S", "LaunchType": "fargate"}
- include: container_network_transmit_packets_total
new_name: pod_network_tx_packets
action: insert
match_type: regexp
experimental_match_labels: {"pod": "\\S", "LaunchType": "fargate"}
# filter out only renamed metrics which we care about
filter:
metrics:
include:
match_type: regexp
metric_names:
- new_container_.*
- pod_.*
# convert cumulative sum datapoints to delta
cumulativetodelta:
metrics:
- new_container_cpu_usage_seconds_total
- pod_cpu_usage_seconds_total
- pod_memory_pgfault
- pod_memory_pgmajfault
- pod_memory_hierarchical_pgfault
- pod_memory_hierarchical_pgmajfault
- pod_network_rx_bytes
- pod_network_rx_dropped
- pod_network_rx_errors
- pod_network_rx_packets
- pod_network_tx_bytes
- pod_network_tx_dropped
- pod_network_tx_errors
- pod_network_tx_packets
- new_container_memory_pgfault
- new_container_memory_pgmajfault
- new_container_memory_hierarchical_pgfault
- new_container_memory_hierarchical_pgmajfault
# convert delta to rate
deltatorate:
metrics:
- new_container_cpu_usage_seconds_total
- pod_cpu_usage_seconds_total
- pod_memory_pgfault
- pod_memory_pgmajfault
- pod_memory_hierarchical_pgfault
- pod_memory_hierarchical_pgmajfault
- pod_network_rx_bytes
- pod_network_rx_dropped
- pod_network_rx_errors
- pod_network_rx_packets
- pod_network_tx_bytes
- pod_network_tx_dropped
- pod_network_tx_errors
- pod_network_tx_packets
- new_container_memory_pgfault
- new_container_memory_pgmajfault
- new_container_memory_hierarchical_pgfault
- new_container_memory_hierarchical_pgmajfault
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
- name: pod_memory_utilization_over_pod_limit
unit: Percent
type: calculate
metric1: pod_memory_working_set
metric2: pod_memory_limit
operation: percent
- name: pod_cpu_usage_total
unit: Millicore
type: scale
metric1: pod_cpu_usage_seconds_total
operation: multiply
# core to millicore: multiply by 1000
# millicore seconds to millicore nanoseconds: multiply by 10^9
scale_by: 1000
- name: pod_cpu_limit
unit: Millicore
type: scale
metric1: pod_cpu_limit_raw
operation: divide
scale_by: 100
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
# 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
- include: .*
match_type: regexp
action: update
operations:
- action: update_label
label: namespace
new_label: Namespace
- action: update_label
label: pod
new_label: PodName
- include: ^new_container_(.*)$$
match_type: regexp
action: update
new_name: container_$$1
# add cluster name from env variable and EKS metadata
resourcedetection:
detectors: [env, eks]
batch:
timeout: 60s
# only pod level metrics in metrics format, details in https://aws-otel.github.io/docs/getting-started/container-insights/eks-fargate
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
extensions:
health_check:
service:
pipelines:
metrics:
receivers: [prometheus]
processors: [metricstransform/label_1, resourcedetection, metricstransform/rename, filter, cumulativetodelta, deltatorate, experimental_metricsgeneration/1, experimental_metricsgeneration/2, metricstransform/label_2, batch]
exporters: [awsemf]
extensions: [health_check]
# configure the service and the collector as a StatefulSet
---
apiVersion: v1
kind: Service
metadata:
name: adot-collector-service
namespace: fargate-container-insights
labels:
app: aws-adot
component: adot-collector
spec:
ports:
- name: metrics # default endpoint for querying metrics.
port: 8888
selector:
component: adot-collector
type: ClusterIP
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: adot-collector
namespace: fargate-container-insights
labels:
app: aws-adot
component: adot-collector
spec:
selector:
matchLabels:
app: aws-adot
component: adot-collector
serviceName: adot-collector-service
template:
metadata:
labels:
app: aws-adot
component: adot-collector
spec:
serviceAccountName: adot-collector
securityContext:
fsGroup: 65534
containers:
- image: amazon/aws-otel-collector:v0.15.1
name: adot-collector
imagePullPolicy: Always
command:
- "/awscollector"
- "--config=/conf/adot-collector-config.yaml"
env:
- name: OTEL_RESOURCE_ATTRIBUTES
value: "ClusterName=YOUR-EKS-CLUSTER-NAME"
resources:
limits:
cpu: 2
memory: 2Gi
requests:
cpu: 200m
memory: 400Mi
volumeMounts:
- name: adot-collector-config-volume
mountPath: /conf
volumes:
- configMap:
name: adot-collector-config
items:
- key: adot-collector-config
path: adot-collector-config.yaml
name: adot-collector-config-volume
---
以下の Deployment マニフェストを使用して、サンプルのステートレスワークロードをクラスターにデプロイします。
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: webapp
namespace: golang
spec:
replicas: 2
selector:
matchLabels:
app: webapp
role: webapp-service
template:
metadata:
labels:
app: webapp
role: webapp-service
spec:
containers:
- name: go
image: public.ecr.aws/awsvijisarathy/prometheus-webapp:latest
imagePullPolicy: Always
resources:
requests:
cpu: "256m"
memory: "512Mi"
limits:
cpu: "512m"
memory: "1024Mi"
上記のデプロイは両方とも Fargate プロファイルに関連付けられた Namespace を対象としているため、ワークロードは Fargate で実行するようにスケジュールされます。Fargate ワーカーノードがこれらのワークロードごとにプロビジョニングされ、Pod が Ready ステータスになるまでには数分かかる場合があります。kubectl get nodes -l eks.amazonaws.com/compute-type=fargate コマンドを実行すると、プレフィックス fargate で名前が付けられた Fargate ワーカーノードがリストアップされるはずです。以下のコマンドを使用して、ADOT Collector とサンプルワークロードの Pod がすべて実行されていることを確認してください。
- kubectl get pods -n fargate-container-insights
- kubectl get pods -n golang
CloudWatch Container Insights を使用した EKS Fargate のリソースメトリクスの可視化
ワークロードのパフォーマンスログイベントは、以下のように、/aws/containerinsights/CLUSTER_NAME/performance という名前のロググループに表示されます。Fargate で実行されている各 Pod ごとに、個別のログストリームが作成されます。
以下に示すのは、ログイベントの 1 つに含まれる埋め込みメトリクス形式の JSON データの代表的な例です。pod_cpu_usage_total と pod_cpu_utilization_over_pod_limit という名前のメトリクスに関連するデータであることがわかります。
以下は、同じ pod_cpu_utilization_over_pod_limit メトリクスについて CloudWatch のメトリクスダッシュボードで表示されるグラフです。
メトリクスは、クラスター、ノード、Namespace、Service、Pod の各レベルでデータを表示する事前に構築された Container Insights ダッシュボードを使用して可視化することもできます。以下は、EKS Fargateのメトリクスをクラスターレベルで表示するダッシュボードのビューです。
まとめ
このブログ記事では、CloudWatch Container Insights で EKS Fargate をサポートするための ADOT Collector のデザインの概要を紹介し、そのデプロイと EKS Fargate クラスター上のワークロードからのメトリクス収集のデモを行いました。単一の Collector インスタンスは、Kubernetes サービスディスカバリーを使用して EKS クラスター内のすべてのワーカーノードを検出し、ワーカーノード上の kubelet へのプロキシとして Kubernetes API サーバーを使用することで、ワーカーノードからメトリクスを収集することができます。EKS のお客様は、EKS Fargate クラスターにデプロイされたワークロードから、CPU、メモリ、ディスク、ネットワーク使用量などのシステムメトリクスを収集し、CloudWatch ダッシュボードで可視化することができるようになり、CloudWatch エージェントと同様のエクスペリエンスが提供されます。
翻訳はプロフェッショナルサービスの杉田が担当しました。原文はこちらです。