COS上のアプリのPrometheus MetricsをCloud Monitoringに送る
Google Cloudを利用してコンテナを動かす選択肢としてGoogle Computing Engine + Container-Optimized OS (COS)の構成があると思います。
しかし、COSで動かしているアプリケーションから出力されるPrometheusメトリクスをCloud Monitoringに送信したい場合、Ops Agent(Google Cloudで推奨される、Cloud Logging, Monitoring向けのエージェント)が利用できない問題があります。
issueでも議論がされていますが、執筆時点ではまだ対応される見込みはなさそうです。
そこで、OpenTelemetryのCustom Collectorを使って代用できないかという検証を行ってみました。
ソースコードはこちらです。
注意事項
この記事ではOpenTelemetryのstability: betaのコンポーネントをふんだんに利用しています。
また、Google Cloudの推奨構成からも離れることになるので、まずはOps Agentが利用できる別のOSに乗り換えることができないか検討することをおすすめします!
構成
やりたいこととしては、下記の要件を満たすコンポーネントを作ることです。
- prometheus metrics endpointにアクセスしてメトリクスを取得(pull)する
- バッファリングしつつCloud Monitoring APIに送信(push)する
OpenTelemetry Collectorは、Custom Collector Builder(ocb)という仕組みがあり、Receiver(メトリクスの受信)、Processor(メトリクスの加工)、Exporter(メトリクスの送信)をpluggableに構成することが可能です。
ocbの利用方法については逆井さん(@k6s4i53rx)の記事がわかりやすいです。
今回については、ありがたいことにそれぞれやりたいことが実現できるコンポーネントがありそうなので、これらを使ってみることにしました。
- Receiver: Prometheus Receiver
- Exporter: Google Cloud Exporter
OpenTelemetry Collectorの作成
※すでにDockerhubにビルド済みのコンテナイメージがありますので、自前ビルドしたい方(arm使ってる方など)以外はスキップ可能です。
今回は下記のビルド設定でCollectorを作成します。OTLPさえない最小構成です。
# builder-config.yaml
dist:
name: prom-cloud-monitoring-collector
description: Prometheus collector for Cloud Monitoring
output_path: ./prom-col
otelcol_version: 0.109.0
exporters:
- gomod:
github.com/open-telemetry/opentelemetry-collector-contrib/exporter/googlecloudexporter v0.109.0
processors:
- gomod:
go.opentelemetry.io/collector/processor/batchprocessor v0.109.0
receivers:
- gomod:
github.com/open-telemetry/opentelemetry-collector-contrib/receiver/prometheusreceiver v0.109.0
ocbでCollectorのコードを生成します。コンテナ化したいのでこの時点ではビルドはしません。
$ ocb --config builder-config.yaml --skip-strict-versioning --skip-compilation
コンテナ化のため、Dockerfileを作成します。
#
# Build
#
FROM golang:1.22 AS builder
ENV CGO_ENABLED=0
ENV GOOS=linux
WORKDIR /build
COPY ./prom-col .
RUN go build -o prom-col ./...
#
# Deploy
#
FROM gcr.io/distroless/static-debian12:latest
WORKDIR /
COPY /build/prom-col /prom-col
USER nonroot
CMD [ "/prom-col" ]
コンテナイメージを作成します。push先はお好みで。
docker build . -t yourname/otel-as-prom-agent:test
ローカルで動作確認
上記までの作業も含めてローカルで動作確認できるdocker compose yamlを用意したので、下記のリポジトリをクローンしておきます。
テスト用credentialの発行
IAMで、「モニタリング指標の書き込み」ロールを付与したService Accountを作成し、credentialをkey.json
というファイル名でクローンしたディレクトリ直下に配置します。
※実際にGCEにデプロイするときはworkload identityを利用するのでこの作業は不要です
また、Cloud Monitoring APIも有効化しておいてください。
テスト用コンテナの起動
docker compose upでコンテナが起動します。テストアプリはprometheus-example-appを利用させていただきました。
localhost:8080
にアクセスするとサンプルレスポンスが返ります。
$ curl localhost:8080
Hello from example application.
localhost:8080/metrics
にアクセスすると、メトリクス情報が取得できます。
$ curl localhost:8080/metrics
# HELP http_request_duration_seconds Duration of all HTTP requests
# TYPE http_request_duration_seconds histogram
http_request_duration_seconds_bucket{code="200",handler="found",method="get",le="0.005"} 3
http_request_duration_seconds_bucket{code="200",handler="found",method="get",le="0.01"} 3
http_request_duration_seconds_bucket{code="200",handler="found",method="get",le="0.025"} 3
http_request_duration_seconds_bucket{code="200",handler="found",method="get",le="0.05"} 3
http_request_duration_seconds_bucket{code="200",handler="found",method="get",le="0.1"} 3
http_request_duration_seconds_bucket{code="200",handler="found",method="get",le="0.25"} 3
http_request_duration_seconds_bucket{code="200",handler="found",method="get",le="0.5"} 3
http_request_duration_seconds_bucket{code="200",handler="found",method="get",le="1"} 3
http_request_duration_seconds_bucket{code="200",handler="found",method="get",le="2.5"} 3
http_request_duration_seconds_bucket{code="200",handler="found",method="get",le="5"} 3
http_request_duration_seconds_bucket{code="200",handler="found",method="get",le="10"} 3
http_request_duration_seconds_bucket{code="200",handler="found",method="get",le="+Inf"} 3
http_request_duration_seconds_sum{code="200",handler="found",method="get"} 0.000103787
http_request_duration_seconds_count{code="200",handler="found",method="get"} 3
# HELP http_requests_total Count of all HTTP requests
# TYPE http_requests_total counter
http_requests_total{code="200",method="get"} 3
# HELP version Version information about this binary
# TYPE version gauge
version{version="v0.3.0"} 1
credentialがきちんと取得できていれば、Cloud MonitoringのMetrics Explorerにメトリクス情報が飛んでいるはずです。
GCE+COSでデプロイしてみる
いよいよ本番です。構成通りインスタンス内に2つのコンテナ(app, prom-col)を立てたいのですが、そのためにはcloud-initを用いた設定を行う必要があります。
cloud-init用設定ファイル(cloud-config)の作成
各コンテナの設定をsystemdのユニットファイルとして記述します。今回の例だと下記のようになります(リポジトリにもあります)。
#cloud-config
write_files:
# コンテナ間通信のためのbridgeネットワークを作成します
- path: /etc/systemd/system/create-network.service
permissions: 0644
owner: root
content: |
[Unit]
Description=Create network for containers
Before=app.service promcol.service
[Service]
Type=oneshot
ExecStart=/usr/bin/docker network create my-bridge
RemainAfterExit=true
# サンプルアプリのコンテナ起動設定
- path: /etc/systemd/system/app.service
permissions: 0644
owner: root
content: |
[Unit]
Description=Start app container
After=create-network.service
[Service]
Environment="HOME=/home/cloudservice"
ExecStart=/usr/bin/docker run --rm --network my-bridge --name app quay.io/brancz/prometheus-example-app:v0.3.0
ExecStop=/usr/bin/docker stop app
# 今回作成したcollectorのコンテナ起動設定
- path: /etc/systemd/system/promcol.service
permissions: 0644
owner: root
content: |
[Unit]
Description=Start promcol container
After=create-network.service
[Service]
Environment="HOME=/home/cloudservice"
ExecStart=/usr/bin/docker run --rm --network my-bridge -v /etc/config.yml:/etc/config.yml --name promcol ymtdzzz/otel-as-prom-agent:test ./prom-col --config=/etc/config.yml
ExecStop=/usr/bin/docker stop promcol
# collector用の設定ファイル
- path: /etc/config.yml
permissions: 0644
owner: root
content: |
receivers:
prometheus:
config:
scrape_configs:
- job_name: 'prom-col'
scrape_interval: 5s
static_configs:
- targets: ['app:8080']
exporters:
googlecloud:
log:
default_log_name: opentelemetry.io/collector-exported-log
processors:
batch:
service:
pipelines:
metrics:
receivers: [prometheus]
processors: [batch] # NOTICE: use memory_limiter in production
exporters: [googlecloud]
runcmd:
- systemctl daemon-reload
- systemctl start create-network.service
- systemctl start app.service
- systemctl start promcol.service
インスタンスの起動
上記の設定ファイルは、メタデータuser-data
の値として設定してあげることで、GCE起動時に実行してくれます。
スクリーンショットのように設定した上で起動してみましょう(コピペミス注意)。
起動できたら、適当にSSHしてコンテナが上がっていることを確認します。
docker run --rm -it --network my-bridge alpine /bin/sh
とかで適当なコンテナを立てて、リクエストを何度か送ってみます(curl app:8080
)。
Cloud Monitoringで見てみましょう。
取れてそうですね。dimensionもちゃんと反映されてそうです。
まとめ
まだstableなコンポーネントではありませんが、OpenTelemetryのocbの仕組みを使えば比較的容易にPrometheusのscraping、及び監視バックエンドへの送信が行えました。
まだotelとpromのメトリクス変換は完全では無く、かつアップデート程度ではあるもののCustom Collector自体のコードのメンテナンスも必要になりますが、そこまで複雑なメトリクスを取得しない場合はこういった方法も使えるよ という一案として、誰かの参考になれば幸いです。
Discussion