Amazon Web Services ブログ
Amazon CloudWatch での Prometheus メトリクスの使用
Imaya Kumar Jagannathan、Justin Gu、Marc Chéné、および Michael Hausenblas
今週の初めに、AWS は CloudWatch Container Insights での Prometheus メトリクスモニタリングの公開ベータ版サポートを発表しました。この記事では、ユーザーがプロビジョニングする AWS クラスター上の Amazon Elastic Kubernetes Service (EKS) および Kubernetes で、コンテナ化されたワークロードに新しい Amazon CloudWatch 機能を使用する方法をご紹介します。
Prometheus は Cloud Native Compute Foundation (CNCF) の卒業プロジェクトで、アクティブで大きなプラクティショナーコミュニティを持つ人気のオープンソースモニタリングツールです。Amazon CloudWatch Container Insights は、コンテナ化されたアプリケーションからの Prometheus メトリクスの検出と収集を自動化します。CloudWatch Container Insights は、カスタム CloudWatch メトリクスを自動的に収集し、フィルタリングして、AWS App Mesh、NGINX、Java/JMX、Memcached、および HAProxy などのワークロード用のダッシュボードで視覚化された集約メトリクスを作成します。デフォルトで、事前に選択されたサービスが 60 秒ごとにスクレイピングおよび事前集約され、クラスターおよびポッドの名前などのメタデータで自動的にリッチ化されます。
AWS では、OpenMetrics との互換性があるすべての Prometheus エクスポーターをサポートして、ユーザーが 150 を超えるオープンソースのサードパーティーエクスポーターのひとつを使ってコンテナ化されたあらゆるワークロードをスクレイプできるようにすることを目指しています。
これは次のような仕組みになっています。 まず、Kubernetes クラスターで CloudWatch エージェントを実行する必要があります。Prometheus の設定、検出、およびメトリクスのプル型収集をサポートするようになったこのエージェントは、忠実度の高い Prometheus メトリクスとメタデータのすべてをリッチ化し、埋め込みメトリックフォーマット (EMF) として CloudWatch Logs に発行します。各イベントは、完全に設定可能なキュレーションされた一連のメトリクスディメンションのための CloudWatch カスタムメトリクスとしてメトリクスデータポイントを作成します。集約された Prometheus メトリクスの CloudWatch カスタムメトリクス統計としての発行は、パフォーマンス問題と障害のモニタリング、アラーム発行、およびトラブルシューティングに必要なメトリクスの数を低減させます。また、CloudWatch Logs Insights クエリ構文を使って忠実度の高い Prometheus メトリクスを分析して、コンテナ化された環境の正常性とパフォーマンスに影響を及ぼしている特定のポッドとラベルを特定することもできます。
これらを踏まえて、2 つのセットアップでの CloudWatch Container Insights Prometheus メトリクスの使用方法を説明する実践的な部分に進みましょう。まず NGINX スクレイピングのシンプルな例から初めて、次に ASP.NET Core アプリを計装することによってカスタムメトリクスを使用する方法を見ていきます。
NGINX からの追加設定不要のメトリクス
最初の例では、EKS クラスターをランタイム環境として使用し、メトリクスを EMF イベントとして CloudWatch に取り込むために CW Prometheus エージェントをデプロイします。スクレイピングターゲットであり、専用アプリがトラフィックを生成する NGINX をイングレスコントローラとして使用します。全体的なセットアップは以下のようになります。
EKS クラスターには 3 つの名前空間があります。これらは、CW Prometheus エージェントをホストする amazon-cloudwatch
、NGINX Ingress コントローラを実行する nginx-ingress-sample
、およびトラフィックジェネレータを含めたサンプルアプリをホストする nginx-sample-traffic
です。
手順を同時に進めたい場合は、EKS クラスターをプロビジョニングするための eksctl、およびアプリケーションインストールのための Helm 3 をインストールしておく必要があります。
EKS クラスターには、以下のクラスター設定を使っています (clusterconfig.yaml
として保存。リージョンは地理的に近いリージョンに変更するとよいかもしれません)。
apiVersion: eksctl.io/v1alpha5
kind: ClusterConfig
metadata:
name: cw-prom
region: eu-west-1
iam:
withOIDC: true
managedNodeGroups:
- name: defaultng
minSize: 1
maxSize: 4
desiredCapacity: 2
labels: {role: mngworker}
iam:
withAddonPolicies:
externalDNS: true
certManager: true
ebs: true
albIngress: true
cloudWatch: true
appMesh: true
cloudWatch:
clusterLogging:
enableTypes: ["*"]
この後、以下のコマンドを使って EKS クラスターをプロビジョニングできます。
eksctl create cluster -f clusterconfig.yaml
eksctl
は見えないところで CloudFormation を使用するので、そのコンソールで進行状況を確認できます。プロビジョニングは、開始から完了まで 15 分ほどかかることを見込んでください。
次に、Helm を使って、専用の Kubernetes 名前空間 nginx-ingress-sample
に NGINX Ingress コントローラをインストールします。
kubectl create namespace nginx-ingress-sample
helm repo add stable https://kubernetes-charts.storage.googleapis.com/
helm install stable/nginx-ingress --generate-name --version 1.33.5 \
--namespace nginx-ingress-sample \
--set controller.metrics.enabled=true \
--set controller.metrics.service.annotations."prometheus\.io/port"="10254" \
--set controller.metrics.service.annotations."prometheus\.io/scrape"="true"
NGINX Ingress コントローラによって管理されるロードバランサーをトラフィックジェネレータのターゲットとするには、次のようにそのパブリック IP アドレスをクエリする必要があります。
$ kubectl -n nginx-ingress-sample get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
nginx-ingress-1588245517-controller LoadBalancer 10.100.245.88 ac8cebb58959a4627a573fa5e5bd0937-2083146415.eu-west-1.elb.amazonaws.com 80:31881/TCP,443:32010/TCP 72s
nginx-ingress-1588245517-controller-metrics ClusterIP 10.100.32.79 <none> 9913/TCP 72s
nginx-ingress-1588245517-default-backend ClusterIP 10.100.75.112 <none> 80/TCP 72s
これで、nginx-sample-traffic
名前空間にサンプルアプリとトラフィックジェネレータをセットアップする準備が整いました (EXTERNAL_IP
には、前のステップで確認した独自の IP を使用するようにしてください)。
SAMPLE_TRAFFIC_NAMESPACE=nginx-sample-traffic
EXTERNAL_IP=ac8cebb58959a4627a573fa5e5bd0937-2083146415.eu-west-1.elb.amazonaws.com
curl https://cloudwatch-agent-k8s-yamls.s3.amazonaws.com/quick-start/nginx-traffic-sample.yaml | \
sed "s/{{external_ip}}/$EXTERNAL_IP/g" | \
sed "s/{{namespace}}/$SAMPLE_TRAFFIC_NAMESPACE/g" | \
kubectl apply -f -
最後に、以下を使って amazon-cloudwatch
名前空間に CW エージェントをインストールします。
kubectl create namespace amazon-cloudwatch
kubectl apply -f https://cloudwatch-agent-k8s-yamls.s3.amazonaws.com/quick-start/prometheus-eks.yaml
完成まであと一歩ですが、もうひとつやることがあります。CloudWatch にメトリクスを書き込む許可を CW エージェントに提供することです。これにはサービスアカウントの IAM ロール (IRSA) を使用します。これは、最小権限のアクセスコントロールを許可する EKS 機能で、CW エージェントを実行するポッドへの直接的な CloudWatchAgentServerPolicy
を通じて CW へのアクセスを事実上制限します。
eksctl create iamserviceaccount \
--name cwagent-prometheus \
--namespace amazon-cloudwatch \
--cluster cw-prom \
--attach-policy-arn arn:aws:iam::aws:policy/CloudWatchAgentServerPolicy \
--override-existing-serviceaccounts \
--approve
これで、セットアップを検証できるようになりました。まず、CW エージェントデプロイメントによって使用されるサービスアカウントが適切にアノテートされているかどうかをチェックします (ここでは eks.amazonaws.com/role-arn
キーのアノテーションを確認してください)。
$ kubectl -n amazon-cloudwatch get sa cwagent-prometheus -o yaml |\
grep eks.amazon
eks.amazonaws.com/role-arn (http://eks.amazonaws.com/role-arn): arn:aws:iam::148658015984:role/eksctl-cw-prom-addon-iamserviceaccount-amazo-Role1-69WKQE6D9CG3
また、kubectl -n amazon-cloudwatch get pod
で CWAgent が適切に実行されていることも検証してください。これは、
Running
状態になっているはずです。
すべてがデプロイおよび実行されたところで、以下のように CLI からメトリクスをクエリできるようになります。
aws logs start-query \
--log-group-name /aws/containerinsights/cw-prom/prometheus \
--start-time `date -v-1H +%s` \
--end-time `date +%s` \
--query-string "fields @timestamp, Service, CloudWatchMetrics.0.Metrics.0.Name as PrometheusMetricName, @message | sort @timestamp desc | limit 50 | filter CloudWatchMetrics.0.Namespace='ContainerInsights/Prometheus'"
aws logs get-query-results \
--query-id e69f2544-add0-4d14-98ff-0fadb54f27f1
上記の aws logs
コマンドの出力は、以下のようになります (最後の value
フィールドでエンコードされている Prometheus メトリクスに注意してください)。
{
"results": [
[
{
"field": "@timestamp",
"value": "2020-04-30 11:40:38.230"
},
{
"field": "Service",
"value": "nginx-ingress-1588245517-controller-metrics"
},
{
"field": "PrometheusMetricName",
"value": "nginx_ingress_controller_nginx_process_connections"
},
{
"field": "@message",
"value": "{\"CloudWatchMetrics\":[{\"Metrics\":[{\"Name\":\"nginx_ingress_controller_nginx_process_connections\"}],\"Dimensions\":[[\"ClusterName\",\"Namespace\",\"Service\"]],\"Namespace\":\"ContainerInsights/Prometheus\"}],\"ClusterName\":\"cw-prom\",\"Namespace\":\"nginx-ingress-sample\",\"Service\":\"nginx-ingress-1588245517-controller-metrics\",\"Timestamp\":\"1588246838202\",\"Version\":\"0\",\"app\":\"nginx-ingress\",\"chart\":\"nginx-ingress-1.33.5\",\"component\":\"controller\",\"container_name\":\"nginx-ingress-controller\",\"controller_class\":\"nginx\",\"controller_namespace\":\"nginx-ingress-sample\",\"controller_pod\":\"nginx-ingress-1588245517-controller-56d5d786cd-xqwc2\",\"heritage\":\"Helm\",\"instance\":\"192.168.89.24:10254\",\"job\":\"kubernetes-service-endpoints\",\"kubernetes_node\":\"ip-192-168-73-163.eu-west-1.compute.internal\",\"nginx_ingress_controller_nginx_process_connections\":1,\"pod_name\":\"nginx-ingress-1588245517-controller-56d5d786cd-xqwc2\",\"prom_metric_type\":\"gauge\",\"release\":\"nginx-ingress-1588245517\",\"state\":\"active\"}"
},
...
NGINX からの例で Prometheus メトリクスをスクレイプして追加設定なしで使用する方法を確認したところで、カスタムメトリクスを使用する方法に進みましょう。
ASP.NET Core アプリからのカスタムメトリクス
以下のセットアップでは、Prometheus クライアントライブラリを使用して ASP.NET Core アプリケーションを計装します。ここでの目標は、カスタムメトリクスを公開して、これらのメトリクスを CloudWatch に取り込むことです。これは、カスタム設定された CloudWatch Prometheus エージェントを使って実行します。
カスタムメトリクスを公開するためのアプリの計装
まず、aws-samples/amazon-cloudwatch-prometheus-metrics-sample からサンプルアプリケーションをクローンし、HomeController.cs
ファイルを検証します。
// Prometheus metrics:
private static readonly Counter HomePageHitCounter = Metrics.CreateCounter("PrometheusDemo_HomePage_Hit_Count", "Count the number of hits to Home Page");
private static readonly Gauge SiteVisitorsCounter = Metrics.CreateGauge("PrometheusDemo_SiteVisitors_Gauge", "Site Visitors Gauge");
public IActionResult Index() {
HomePageHitCounter.Inc();
SiteVisitorsCounter.Set(rn.Next(1, 15));
return View();
}
ProductsController.cs
ファイルも検証します。
// Prometheus metric:
private static readonly Counter ProductsPageHitCounter = Metrics.CreateCounter("PrometheusDemo_ProductsPage_Hit_Count", "Count the number of hits to Products Page");
public IActionResult Index(){
ProductsPageHitCounter.Inc();
return View();
}
上記のコードスニペットは、オープンソースの Prometheus クライアントライブラリを使って、各ページへの訪問者数と全体的な訪問者数を追跡するための 3 つの異なるメトリクスを計装します。
次に、ローカルでのテストとプレビューのため、Dockerfile
があるディレクトリに移動します。以下のコマンドを使ってコンテナイメージを構築し、実行します。
docker build . -t prometheusdemo
docker run -p 80:80 prometheusdemo
localhost
に移動します。ここでは、以下のような画面が表示されるはずです。[Home] および [Products] リンクを数回クリックして、トラフィックを生成します。
次に、http://localhost/metrics
に移動します。ここには、/metrics
エンドポイントを通じてアプリが公開しているすべての Prometheus メトリクスが表示されます。
Prometheus メトリクス検出のための CloudWatch エージェントの設定
リポジトリにある /kubernetes
フォルダの prometheus-eks.yaml
ファイルを開きます。YAML ファイルの cwagentconfig.json
セクションにある以下の設定は、CloudWatch エージェントがアプリケーションからスクレイプするメトリクスを示しています。
{
"source_labels": ["job"],
"label_matcher": "prometheusdemo-dotnet",
"dimensions": [["ClusterName","Namespace"]],
"metric_selectors": [
"^process_cpu_seconds_total$",
"^process_open_handles$",
"^process_virtual_memory_bytes$",
"^process_start_time_seconds$",
"^process_private_memory_bytes$",
"^process_working_set_bytes$",
"^process_num_threads$"
]
},
{
"source_labels": ["job"],
"label_matcher": "^prometheusdemo-dotnet$",
"dimensions": [["ClusterName","Namespace"]],
"metric_selectors": [
"^dotnet_total_memory_bytes$",
"^dotnet_collection_count_total$",
"^dotnet_gc_finalization_queue_length$",
"^dotnet_jit_method_seconds_total$",
"^dotnet_jit_method_total$",
"^dotnet_threadpool_adjustments_total$",
"^dotnet_threadpool_io_num_threads$",
"^dotnet_threadpool_num_threads$",
"^dotnet_gc_pinned_objects$"
]
},
{
"source_labels": ["job"],
"label_matcher": "^prometheusdemo-dotnet$",
"dimensions": [["ClusterName","Namespace","gc_heap"]],
"metric_selectors": [
"^dotnet_gc_allocated_bytes_total$"
]
},
{
"source_labels": ["job"],
"label_matcher": "prometheusdemo-dotnet",
"dimensions": [["ClusterName","Namespace","app"]],
"metric_selectors": [
"^PrometheusDemo_HomePage_Hit_Count$",
"^PrometheusDemo_SiteVisitors_Gauge$",
"^PrometheusDemo_ProductsPage_Hit_Count$"
]
}
prometheus.yaml
には、標準の Prometheus 設定を使って、Prometheus メトリクスエンドポイントの詳細を CloudWatch エージェントに指示するセクションがあります。address
ソースラベルの regex を変更して、サンプルアプリケーションがメトリクスを公開しているエンドポイントとポート番号に一致させる必要があることに注意してください。
- job_name: 'prometheusdemo-dotnet'
sample_limit: 10000
metrics_path: /metrics
kubernetes_sd_configs:
- role: pod
relabel_configs:
- source_labels: [__address__]
action: keep
regex: '.*:80$'
- action: labelmap
regex: __meta_kubernetes_pod_label_(.+)
- action: replace
source_labels:
- __meta_kubernetes_namespace
target_label: Namespace
- source_labels: [__meta_kubernetes_pod_name]
action: replace
target_label: pod_name
- action: replace
source_labels:
- __meta_kubernetes_pod_container_name
target_label: container_name
- action: replace
source_labels:
- __meta_kubernetes_pod_controller_name
target_label: pod_controller_name
- action: replace
source_labels:
- __meta_kubernetes_pod_controller_kind
target_label: pod_controller_kind
- action: replace
source_labels:
- __meta_kubernetes_pod_phase
target_label: pod_phase
アプリと CloudWatch Prometheus エージェントのデプロイメント
まず、先ほど構築した ASP.NET Core アプリの Docker コンテナイメージを、独自に選択するコンテナレジストリにプッシュします。次に、deployment.yaml
ファイルの <YOUR_CONTAINER_REPO>
を、イメージを発行したコンテナリポジトリの値に変更します。
すべてが設定されたところで、以下のコマンドを使ってサンプルアプリケーションと CloudWatch Prometheus エージェントを Kubernetes クラスターにデプロイします。
kubectl apply -f kubernetes/
この時点で、オプションとして Kubernetes クラスターで CloudWatch Container Insights を有効化できることに注意してください。これは、AWS マネジメントコンソールでのインフラストラクチャマップと自動ダッシュボードの表示を可能にします。
セットアップは、次のように検証できます (すべてのポッドが Running 状態になっているはずです)。
$ kubectl -n amazon-cloudwatch get pods
NAME READY STATUS RESTARTS AGE
cloudwatch-agent-785zq 1/1 Running 0 26h
cloudwatch-agent-tjxcj 1/1 Running 0 26h
cwagent-prometheus-75dfcd47d7-gtx58 1/1 Running 0 120m
fluentd-cloudwatch-7ttck 1/1 Running 0 26h
fluentd-cloudwatch-n2jvm 1/1 Running 0 26h
カスタムダッシュボードの作成
us-east-2
AWS リージョンで PrometheusDotNetApp
という名前のカスタムダッシュボードを作成するには、次を実行します。
dashboardjson=$(<cloudwatch_dashboard.json)
aws cloudwatch put-dashboard \
--dashboard-name PrometheusDotNetApp \
--dashboard-body $dashboardjson
別のリージョンにダッシュボードを作成したい場合は、JSON config ファイルの us-east-2
を希望する値に置き換えてください。
これで、作成した CloudWatch ダッシュボードに移動できるようになります。ダッシュボードは以下のようになるはずです。
CloudWatch Container Insights は、Kubernetes クラスターからのパフォーマンスメトリクスに基づいて自動ダッシュボードをパブリッシュします。CloudWatch Container Insights のパフォーマンスモニタリングページに移動して、Container Insights によって作成された自動ダッシュボードを表示してください。
CloudWatch Container Insights が有効化されているので、[リソース] から、コンポーネントを含めた Kubernetes クラスタートポロジを表示するマップビューにアクセスできます。
カスタムメトリクス例のシナリオはこれで終了です。では、今後について簡単に見てみましょう。
次のステップ
私たちは、Prometheus メトリクスに対する CloudWatch Container Insights サポートの公開ベータを開始できることをとてもうれしく思っています。皆さんがこの新しい機能をどのように使用し、将来何を期待しておられるかについて、ぜひお聞かせください。たとえば、PromQL サポート、または Prometheus のヒストグラムやサマリーメトリクスのネイティブサポートなどがあります。ご提案と経験の共有には、containerinsightsfeedback@amazon.com をご利用ください。AWS では、皆さんのフィードバックに基づいて機能の改善と追加を絶えず行っているため、今後も AWS でのコンテナ分野に引き続きご注目ください。