[Docker][CloudWatch] コンテナ毎のメトリクスを取得したい

AWS CloudWatch では EC2 に対していくつかのメトリクスが自動で収集されているのですが、なぜかメモリ使用率などがありません。

公式では CloudWatch Agent を使って収集するよう書かれています。

**CloudWatch エージェントにより収集されるメトリクス**
https://docs.aws.amazon.com/ja_jp/AmazonCloudWatch/latest/monitoring/metrics-collected-by-CloudWatch-agent.html


これで EC2 のメモリ使用率のメトリクスは取得できます。
しかし、やはり docker を使っているのでコンテナ毎のメトリクスが欲しいのですが、CloudWatch Agent では取得できなさそうです。

何か方法がないかと探してみると、以下のツールが見つかりました。
`--docker` オプションを付けるとコンテナ毎の CPU使用率、メモリ使用率が取れるようです。

**[github] mon-put-instance-data**
https://github.com/mlabouardy/mon-put-instance-data

ただ、残念なことにコンテナのメトリクスの Dimention が固定で EC2 インスタンスID、コンテナ名、コンテナID、イメージ名となっていて変更できません。
これだとコンテナを起動し直すたびにメトリクスが増えてしまい使い勝手がいまいちでした。。



## コンテナのモニタリング

少し脱線して。

コンテナ毎の CPU使用率やメモリ使用率をモニタリングするにはどんな方法があるか調べてみると、そもそも標準の docker cli にありました。。汗

top コマンドのようにコンソールで逐次再表示されます。

```
$ docker container stats
CONTAINER ID        NAME                CPU %               MEM USAGE / LIMIT     MEM %               NET I/O             BLOCK I/O           PIDS
a486eca37e08        dev002-app001       0.00%               2.098MiB / 3.853GiB   0.05%               4.46kB / 0B         0B / 30.7kB         2
84140062b59c        dev002-app002       0.00%               2.078MiB / 3.853GiB   0.05%               4.46kB / 0B         0B / 29.7kB         2
```


他にも、もう少しグラフィカルに表示する `ctop` というのがありました。

**ctop**
https://github.com/bcicen/ctop

![ctop](https://raw.githubusercontent.com/bcicen/ctop/master/_docs/img/grid.gif)

docker コンテナにもなっていて、ホストにツールをインストールせず docker run で実行できるようです。

```
docker run --rm -ti \
  --name=ctop \
  --volume /var/run/docker.sock:/var/run/docker.sock:ro \
  quay.io/vektorlab/ctop:latest
```


で、ctop の docker 起動コマンドを見ていて一つ気になりました。
`--volume /var/run/docker.sock:/var/run/docker.sock:ro` って何？


## Docker Engine API

`/var/run/docker.sock` を調べてみると、docker はコマンド(cli) とサーバ(engine) 間に `Docker Engine API` という REST API があるようです。

**Develop with Docker Engine API**
https://docs.docker.com/engine/api/

**Docker Engine API (v1.41)**
https://docs.docker.com/engine/api/v1.41/

以下のように、`curl` などのコマンドで簡単に操作できます。

例はコンテナ一覧の取得処理。

```
$ curl --unix-socket /var/run/docker.sock -X GET http://localhost/containers/json | jq .
[
  {
    "Id": "a486eca37e0870704fd290d8e33ba2ba4b7a09d2663929651ca91b01b2a70f8c",
    "Names": [
      "/dev002-app001"
    ],
    "Image": "nginx:latest",
    "ImageID": "sha256:f0b8a9a541369db503ff3b9d4fa6de561b300f7363920c2bff4577c6c24c5cf6",
    "Command": "/docker-entrypoint.sh nginx -g 'daemon off;'",
      :
```

docker コマンドでできる操作はすべて API があるので、`docker container stats` の情報も取得できます。

```
$ curl --unix-socket /var/run/docker.sock -X GET http://localhost/containers/a486eca37e0870704fd290d8e33ba2ba4b7a09d2663929651ca91b01b2a70f8c/stats?stream=false | jq .
{
  "read": "2021-05-29T16:32:58.667688785Z",
  "preread": "2021-05-29T16:32:57.662946911Z",
  "pids_stats": {
    "current": 2
  },
    :
  "num_procs": 0,
  "storage_stats": {},
  "cpu_stats": { ★CPU情報
    "cpu_usage": {
      "total_usage": 193324165,
      "percpu_usage": [
        193324165
      ],
      "usage_in_kernelmode": 30000000,
      "usage_in_usermode": 150000000
    },
      :
  "memory_stats": {  ★メモリ情報
    "usage": 2215936,
    "max_usage": 7733248,
    "stats": {
      "active_anon": 1470464,
         :
  "name": "/dev002-app001",
  "id": "a486eca37e0870704fd290d8e33ba2ba4b7a09d2663929651ca91b01b2a70f8c",
    :
}
```

これを使えばシェルスクリプトでも簡単なモニタリングのツール組めるのでは？


## docker コンテナの簡易モニタリングツール

ということで、シェルスクリプトによるモニタリングツールを作ってみました。

**[github] mon-docker-cloudwatch**
https://github.com/batatch/mon-docker-cloudwatch

**[dockerhub] mon-docker-cloudwatch**
https://hub.docker.com/r/batatch/mon-docker-cloudwatch

作り込まずに最低限の使い方ができることに意味があるので、本当に簡単な内容です。

```
$ sh mon-docker-cloudwatch.sh
2021/05/29 16:42:28 Docker - InstanceId:i-aaaaaaaaaaaa ContainerName:dev002-app001 : MemUtilization:0.0531709 MemUsed:2199552 MemAvairable:4136755200
2021/05/29 16:42:28 Docker - InstanceId:i-aaaaaaaaaaaa ContainerName:dev002-app001 : CPUUtilization:0 CPUUser:193324165 CPUSystem:615096770000000
2021/05/29 16:42:30 Docker - InstanceId:i-aaaaaaaaaaaa ContainerName:dev002-app002 : MemUtilization:0.0526759 MemUsed:2179072 MemAvairable:4136755200
2021/05/29 16:42:30 Docker - InstanceId:i-aaaaaaaaaaaa ContainerName:dev002-app002 : CPUUtilization:0 CPUUser:189791026 CPUSystem:615098780000000
    :
```

docker で実行することもできます。

```
$ docker run --rm -it -v /var/run/docker.sock:/var/run/docker.sock:ro batatch/mon-docker-cloudwatch:latest
2021/05/29 17:09:53 Docker - InstanceId:i-aaaaaaaaaaaa ContainerName:dev002-app001 : MemUtilization:0.0531709 MemUsed:2199552 MemAvairable:4136755200
2021/05/29 17:09:53 Docker - InstanceId:i-aaaaaaaaaaaa ContainerName:dev002-app001 : CPUUtilization:0 CPUUser:193324165 CPUSystem:616729170000000
    :
```

シェルスクリプトを実行すれば、全コンテナの CPU使用率、メモリ使用率などを収集/算出して CloudWatch のメトリクスとして送信します。
(メトリクスの内容を標準出力にもログとして表示します)

Dimention と Metrics は以下の内容で固定です。

- Dimentions
  - InstanceId
  - ContainerName

- Metrics
  - ContainerCPUUtilization (%)
  - ContainerCPUUser (Bytes)
  - ContainerCPUSystem (Bytes)
  - ContainerMemoryUtilization (%)
  - ContainerMemoryUsed
  - ContainerMemoryAvairable

メトリクスの算出方法は REST API の説明に倣ったので `docker container stats` の内容と合ってるはずです。


`Docker Engine API`、いろいろ遊べそうですね。

// EOF