オンプレ環境におけるCloudWatch AgentとPrometheusの連携

# 概要

昨年、CloudWach Agent(以下CWA)にアップデートが入り、
PrometheusのExporterからメトリクスを収集し、CloudWatchにプッシュする機能が追加された。
参考: [Amazon CloudWatch での Prometheus メトリクスの使用](https://aws.amazon.com/jp/blogs/news/using-prometheus-metrics-in-amazon-cloudwatch/)

これにより、CWAの標準機能によるメトリクス収集だけでなく、既存のPrometheus Exporterからの収集も可能となり、さらにはそれをCloudWatchに集約することができるようになった。
上述の参考リンクに記載されている内容では、EKSにCWAをデプロイして、内部の様々なコンテナからメトリクス収集する方法が紹介されている 。
そうすると、何だかEKS専用にも感じてしまうかもしれないが、実はオンプレ環境などEKSが一切関係ない環境でも同じことが可能。
非常に簡単ではあるが、ひとまずEKS関係なく使える方法を紹介したい。
本記事のタイトルではオンプレ環境と書いているが、まぁつまりEKS以外の環境なら何でも可能。

最近、既存オンプレ環境で展開しているPrometheusの資産を活かしつつ、どうにかAWSと組み合わせた良いやり方ないかなーと探していた過程で本方法を検証した。
なので、どちらかというと自分の備忘録的な意味合いが強い。ごめんなさい。

# 環境

* CentOS Linux release 8.3.2011
* amazon-cloudwatch-agent 1.247346.1b249759

CWAはもっと低いバージョンでもいけるけど、ひとまず本記事で検証したバージョンはこれ

# 注意

詳細は後述するが、**CWAによりexporterから収集されたメトリクスはログ化されCloudWatch Logsに集約**される。
直接的なメトリクスとして保存されるわけではない。そのため、ログからメトリクス化する手間が必要となる。

# 手順

## CWAのインストール

以下から自環境に適したパッケージを落としてインストール

* [Download and Configure the CloudWatch Agent Using the Command Line](https://docs.aws.amazon.com/AmazonCloudWatch/latest/monitoring/download-cloudwatch-agent-commandline.html)

例えばCentOSならコレ。
https://s3.amazonaws.com/amazoncloudwatch-agent/centos/amd64/latest/amazon-cloudwatch-agent.rpm

```
$ rpm -Uvh https://s3.amazonaws.com/amazoncloudwatch-agent/centos/amd64/latest/amazon-cloudwatch-agent.rpm
```

ちなみに最初に検証したときは、rpmなどのパッケージで公開されているバージョンでは実装されていなかった。
なのでGitHubに公開されているアップストリームのソースコードからコンパイルする必要があった。
本記事を書くにあたり改めて確かめたところ、公開されているrpmでも問題なかったので、その手順で紹介。

## 検証用exporterを稼働

* node_exporterをコンテナとして起動しておく
* あとでCWAにこのexporterからメトリクス収集させてCloudWatchに集約させる

```
$ docker run -d \
  --rm \
  --net="host" \
  --pid="host" \
  -v "/:/host:ro,rslave" \
  --name node_exporter \
  quay.io/prometheus/node-exporter \
  --path.rootfs=/host
```

* curlで動作確認

```
$ curl localhost:9100/metrics -s | head
# HELP go_gc_duration_seconds A summary of the pause duration of garbage collection cycles.
# TYPE go_gc_duration_seconds summary
go_gc_duration_seconds{quantile="0"} 0
go_gc_duration_seconds{quantile="0.25"} 0
go_gc_duration_seconds{quantile="0.5"} 0
go_gc_duration_seconds{quantile="0.75"} 0
go_gc_duration_seconds{quantile="1"} 0
go_gc_duration_seconds_sum 0
go_gc_duration_seconds_count 0
# HELP go_goroutines Number of goroutines that currently exist.
```

## AWSのConfigとCredentialの準備

自環境にあわせて適宜用意。

```
$ cat ~/.aws/config
[default]
region = ap-northeast-1
[profile AmazonCloudWatchAgent]
region = ap-northeast-1
```

```
$ cat ~/.aws/credentials
[default]
aws_access_key_id = hoge
aws_secret_access_key = hogehoge
[AmazonCloudWatchAgent]
aws_access_key_id = foo
aws_secret_access_key = foobar
```

## コンフィグの準備

必要なのは、CWAのコンフィグとCWAに読み込ませるPrometheusのコンフィグ。
今回は必要最低限な構成でコンフィグを用意する。

### CWAのコンフィグ

rpmインストールすると、`/etc/amazon/amazon-cloudwatch-agent/amazon-cloudwatch-agent.d/`というディレクトリが出来ている。
その配下に`file_amazon-cloudwatch-agent.json`というコンフィグにあたるjsonファイルを配置する。
*.jsonなら何でも読み込むと思うので、名前は好きに変えて良いと思う。

* /etc/amazon/amazon-cloudwatch-agent/amazon-cloudwatch-agent.d/file_amazon-cloudwatch-agent.json
  * 最低限これだけ書いていれば動くと思う
  * `log_group_name`に指定した名前でCloudWatch Logsにロググループを作成する (今回は検証用途なのでtest)
  * `prometheus_config_path`にはPrometheusのコンフィグパスを指定する (別にどこでも良い)

```/etc/amazon/amazon-cloudwatch-agent/amazon-cloudwatch-agent.d/file_amazon-cloudwatch-agent.json
{
  "agent": {
    "run_as_user": "root"
  },
  "logs": {
    "metrics_collected": {
      "prometheus": {
        "log_group_name": "test",
        "prometheus_config_path": "/etc/prometheusconfig/prometheus.yaml"
      }
    },
    "force_flush_interval": 5
  }
}
```

### Prometheusのコンフィグ

別にCWAと組み合わせるからといって特別な何かはない。通常のPrometheusのコンフィグそのまま。
ここは詳しく解説している人たちが他にいらっしゃると思うので詳細は割愛。
ちなみに、`job_name`で指定した名前でCloudWatch上にログストリームが作成される。
今回は[事前に準備したnode_exporter](#検証用exporterを稼働)を狙い撃ちでコンフィグを用意。実環境では、ServiceDscoveryさせることになるだろう。

本記事では、`/etc/prometheusconfig/prometheus.yaml`というパスで用意しておく。


```/etc/prometheusconfig/prometheus.yaml
global:
  evaluation_interval: 1m
  scrape_interval: 1m
  scrape_timeout: 10s
scrape_configs:
- job_name: 'node_exporter'
  sample_limit: 10000
  metrics_path: /metrics
  static_configs:
  - targets: ['localhost:9100']
```

## CWAの起動

* rpmでインストールしたので、自動的にunitファイルが作成済み

```
$ systemctl list-unit-files | grep cloudwatch
amazon-cloudwatch-agent.service            disabled
```
* 起動。一応、自動起動も有効化しておきましょう。

```
$ systemctl start amazon-cloudwatch-agent
$ systemctl enable amazon-cloudwatch-agent
```

## ログ確認

journalctlでログも見れる。が、大した内容が出力されていない。
何かしらの問題があって起動できなかった場合、このログにまともな情報が出力されず、ほぼ役に立たない。

```
$ journalctl -xu amazon-cloudwatch-agent
```

CWAの実態は`/opt/aws/amazon-cloudwatch-agent`に存在している。ここにはログファイルが出力されている。こっちを見たほうが便利。
例えばIAMの権限問題とかがあっても、こちらにはばっちり出力されているのでトラブルシュートが捗る。
ちなみにログ的にもオンプレ環境であることを検出している。
こちらメタデータがとれるかどうかで判断しているようで、EKSで動かせばEKS環境であることを正しく検出してくれる。

* /opt/aws/amazon-cloudwatch-agent/logs/amazon-cloudwatch-agent.log

```
$ cat /opt/aws/amazon-cloudwatch-agent/logs/amazon-cloudwatch-agent.log
2021/01/03 02:29:34 I! 2021/01/03 02:29:34 E! ec2metadata is not available
I! Detected the instance is OnPrem
2021/01/03 02:29:34 Reading json config file path: /opt/aws/amazon-cloudwatch-agent/etc/amazon-cloudwatch-agent.json ...
/opt/aws/amazon-cloudwatch-agent/etc/amazon-cloudwatch-agent.json does not exist or cannot read. Skipping it.
2021/01/03 02:29:34 Reading json config file path: /opt/aws/amazon-cloudwatch-agent/etc/amazon-cloudwatch-agent.d/file_amazon-cloudwatch-agent.json ...
Valid Json input schema.
I! Detecting runasuser...
Got Home directory: /root
I! Set home dir Linux: /root
I! SDKRegionWithCredsMap region:  ap-northeast-1
Got Home directory: /root
No csm configuration found.
Under path : /logs/ | Info : Got hostname localhost.localdomain as log_stream_name
No metric configuration found.
Configuration validation first phase succeeded

2021/01/03 02:29:34 I! Config has been translated into TOML /opt/aws/amazon-cloudwatch-agent/etc/amazon-cloudwatch-agent.toml
2021/01/03 02:29:34 Reading json config file path: /opt/aws/amazon-cloudwatch-agent/etc/amazon-cloudwatch-agent.json ...
2021/01/03 02:29:34 Reading json config file path: /opt/aws/amazon-cloudwatch-agent/etc/amazon-cloudwatch-agent.d/file_amazon-cloudwatch-agent.json ...
2021/01/03 02:29:34 I! Detected runAsUser: root
2021/01/03 02:29:34 I! Change ownership to root:root
2021-01-03T02:29:34Z I! Starting AmazonCloudWatchAgent 1.247346.1
2021-01-03T02:29:34Z I! Loaded inputs: prometheus_scraper
2021-01-03T02:29:34Z I! Loaded aggregators:
2021-01-03T02:29:34Z I! Loaded processors:
2021-01-03T02:29:34Z I! Loaded outputs: cloudwatchlogs
2021-01-03T02:29:34Z I! Tags enabled: host=localhost.localdomain
2021-01-03T02:29:34Z I! [agent] Config: Interval:1m0s, Quiet:false, Hostname:"localhost.localdomain", Flush Interval:1s
2021-01-03T02:29:34Z I! [logagent] starting
2021-01-03T02:29:34Z I! [logagent] found plugin cloudwatchlogs is a log backend
```

## CloudWatch側の確認

正しくコンフィグが作成され、exporterからメトリクス収集し、CloudWatchへプッシュできていれば、
下記のようにロググループやストリームが作成されているはず。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/80163/be3ec4de-9901-801b-9600-6841013a1a22.png)

こんな感じでログが溜まっていく。CWAかexporterを停止させない限り、どんどん溜まっていく。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/80163/f760b8d6-37de-c826-ae0e-78b2d01846f3.png)

あとは、このログに対してメトリクスフィルターを作成する。
ここではCPU使用率のメトリクス化を行っているが、CloudWatchへのプッシュができているなら何でも可能。

参考: [Filter and Pattern Syntax](https://docs.aws.amazon.com/AmazonCloudWatch/latest/logs/FilterAndPatternSyntax.html)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/80163/d03394fd-ab9a-6d8d-a1be-8bbe9af44a43.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/80163/9af7cd79-1951-c323-0cf3-8e134136fddd.png)

# あとがき

監視とかメトリクス収集とか難しいですよね。正解がわからない。
どんな仕組みが良いのか、どんなシステムが良いのか、どんなアーキテクチャが良いのか、どれもこれも帯に短し襷に長し。
元々この分野は詳しくないので尚更わからない。
まぁ環境やサービスの特性などによってコレといったものが言いづらいんだろうし、正解はないんだろうけど。
昨今、Prometheusを使っているユーザーは多いと思う。
exporterは引き続き使いつつ、Prometheus本体をなくしてCloudWatchに代替させる・・・という方法も一案としてはアリだと思う。

先日のre:Inventにて[AWSフルマネージドなPrometheus](https://aws.amazon.com/jp/blogs/aws/join-the-preview-amazon-managed-service-for-prometheus-amp/)が発表された。非常に興味深い。
まだ試していないので詳しいメリット/デメリットはわからないが、AWS-Prometheusの連携が捗りそうなので期待。
多分、こちらをうまく使えれば、こんなCWAに収集させる方法は使わなくて良い気がする。そのうち試してみたい。