Posted at

AWSにおけるDockerコンテナの監視

More than 1 year has passed since last update.

この記事は株式会社ネクスト(Lifull) Advent Calendar 2016の9日目です。

業務でDockerコンテナを導入してみたいと思ったので

それらを監視するために調べた色々を書き出します。

「ログ監視」「リソース監視」の観点でまとめてみました。


:computer: 環境

弊社で運営している多くのWebサービスではAWSを利用しています。

AWS上でコンテナを利用する方法はいくつかありますが

基本はElasticBeanstalk(以下、EBと略)上での複数コンテナを想定します。

(とはいえ裏側ではECSが動いているのですが)


:page_with_curl: ログ監視


Docker: logsコマンド

Dockerのコマンドで、ホストインスタンスから

コンテナ内のSTDOUTとSTDERRを取得することができます。


例:アプリケーションコンテナに対するlogsコマンド

$ docker logs a59845048cea

[2016-12-05 17:10:05 +0000] [7] [INFO] Starting gunicorn 19.6.0
[2016-12-05 17:10:05 +0000] [7] [INFO] Listening at: http://0.0.0.0:5000 (7)
[2016-12-05 17:10:05 +0000] [7] [INFO] Using worker: sync
[2016-12-05 17:10:06 +0000] [10] [INFO] Booting worker with pid: 10

デバッグ時には便利ですが、毎回インスタンスに入るわけにもいきません。

Dockerにはロギングドライバという機能があり

logsで得られる内容をいくつかの方法で外部に出力することができます。


AWS: CloudWatch Logsの活用

Dockerは前述のロギングドライバにおいて

公式でCloudwatch Logsへの出力をサポートしています。

EBでは--log-driverのようにdocker runの際に渡すパラメータを

dockerrun.aws.jsonという独自形式のjsonに定義します。

このファイルの中のコンテナ定義形式はECSと共通なので

ログの設定についてはECSのドキュメントに記載がありました。


ロギングの設定

"logConfiguration": {

"logDriver": "json-file"|"syslog"|"journald"|"gelf"|"fluentd"|"awslogs",
"options": {"string": "string"
...}

logDriverについてはawslogsを選択して問題なさそう。

optionsには、awslogs-regionawslogs-groupを指定すればよさそうです。

試しに以下の定義でnginxコンテナのログを出力してみます。

公式のnginxイメージで、内容は80番ポートを開けているだけです。


Dockerrun.aws.json

{

"AWSEBDockerrunVersion": "2",
"containerDefinitions": [
{
"name": "nginx",
"image": "nginx:1.11.5-alpine",
"essential": true,
"memory": 128,
"portMappings": [
{
"hostPort": 80,
"containerPort": 80
}
],
"logConfiguration": {
"logDriver": "awslogs",
"options": {
"awslogs-region": "ap-northeast-1",
"awslogs-group": "nginx-group"
}
}
}
]
}

指定したロググループがCloudWatch側に定義されていないと

EBのデプロイごとコケるようなので注意してください。

デプロイが成功すると、CloudWatchのコンソールやcliでログが見られます。

何度か踏んだ結果、アクセスログとエラーログの両方が出ているようです。

awslogs.png

ちなみに、STDOUTとSTDERRを表示するはずのdocker logs

これらが見られるのは公式のnginxイメージを使用したからです。

Dockerfileを見ると、ログの出力先がシンボリックリンクになっています。


Dockerfile

# forward request and error logs to docker log collector

&& ln -sf /dev/stdout /var/log/nginx/access.log \
&& ln -sf /dev/stderr /var/log/nginx/error.log

docker logsの集約についてはこの方法でなんとかなりそうです。

STDOUTとSTDERRを分けられないのは難点ですが

DockerのIssueでも少なからず議論があるので、いずれ改善されるかもしれません。

ちなみに、fluentdのロギングドライバでは分けることができるようです。

また、EBでホストインスタンスにアクセスすると

/var/log/docker-events.logなどのログが残されていることもわかります。

こちらは上記のロギングドライバで出力されない情報なので

.ebextensionsなどでホスト側にログ出力の仕組みを作る必要がありそうです。


:bar_chart: リソース監視


Docker: statsコマンド

Dockerのコマンドで、コンテナごとのリソース使用量を表示します。


例:アプリケーションとDBを起動した状態でstatsコマンド

$ docker stats

CONTAINER CPU % MEM USAGE / LIMIT MEM % NET I/O BLOCK I/O PIDS
a59845048cea 1.02% 39.42 MiB / 1.951 GiB 1.97% 1.038 kB / 648 B 32.77 kB / 0 B 5
ac7470b95cef 0.19% 353.9 MiB / 1.951 GiB 17.72% 1.296 kB / 648 B 4.149 MB / 317.8 MB 27

Linuxのtopコマンドのように使えてわかりやすいのですが

集約や保存のためにはこれでは不十分です。


AWS: CloudWatchメトリクス

EBやECSで複数コンテナを扱う場合は

ECSのコンソールでメトリクスを確認することができます。

インスタンスのクラスタ単位では以下の4つのメトリクスが見えます。

metrics.png

ECSで言うサービスの単位では、以下の2つのメトリクスのみでした。

service.png

ちなみにEBのコンソールでは

コンテナではない通常のインスタンス監視と特に変わりません。

eb.png

見慣れたCloudWatchの形式で見られるのはありがたいのですが

コンテナごとの使用リソースなど、もう少し細かい部分が気になります。


サードパーティのサービス/ソフトウェアに頼る

cAdvisorELKスタックなどのソフトを活用したメトリクス収集や

フルスタックな有料サービスの活用など、追加の選択肢は多く存在します。

自部署では監視領域で全般的にMackerelを利用しているため

コンテナのメトリクス可視化も試してみました。

Mackerelではagentを使用してメトリクス情報を送信しますが

コンテナのかたちで監視を行うmackerel-agentも公式で提供されています。

これで、一緒に起動しているコンテナとホストインスタンスの監視が可能です。

ドキュメント内ではdocker runの引数として書かれている部分を

Dockerrun.aws.jsonに落とし込んでみます。


Dockerrun.aws.json

{                                                                                     

"AWSEBDockerrunVersion": "2",
"volumes": [
{
"name": "docker-sock",
"host": {
"sourcePath": "/var/run/docker.sock"
}
},
{
"name": "lib-mackerel-agent",
"host": {
"sourcePath": "/var/lib/mackerel-agent"
}
}
],
"containerDefinitions": [
{
"nginxコンテナの設定は": "省略"
},
{
"name": "mackerel-agent",
"image": "mackerel/mackerel-agent",
"environment": [
{ "name": "apikey", "value": "Hoge8noPEnZj9HogeMEwnHogep5aMQzKMHogee7ZHoge" },
{ "name": "enable_docker_plugin", "value": true },
{ "name": "auto_retirement", "value": true }
],
"essential": true,
"memory": 128,
"mountPoints": [
{
"sourceVolume": "docker-sock",
"containerPath": "/var/run/docker.sock"
},
{
"sourceVolume": "lib-mackerel-agent",
"containerPath": "/var/lib/mackerel-agent"
}
]
}
]
}

mackerel-agentコンテナには

サービスを利用するためにapikeyなどの環境変数を渡しています。

また、メトリクスデータのやりとりのためのボリュームマウントが必須です。

上記のDockerrun.aws.jsonからコンテナを起動した結果

Mackerelのコンソール上でメトリクスが閲覧できます。

以下のグラフはホストインスタンスの使用メモリを示します。

memory.png

メモリ以外にもloadaverate、cpu、diskioなどのメトリクスが見られます。

さらに、{ "name": "enable_docker_plugin", "value": true }の設定により

起動しているコンテナのメトリクスも取得することができます。

以下のグラフは 各コンテナ の使用メモリを示しています。

docker_memory.png

勝手に起動するecs-agentを含めて、3つのコンテナのメトリクスがあります。

これに加えてBlkIO Bytes、BlkIO IOPS、CPUの4種類が閲覧可能で

それぞれのメトリクスを起点としたアラートも飛ばせるため

かなり柔軟に監視対応ができます。

CloudWatchのメトリクスだけではかゆいところに手が届かないため

Mackerelに限らず、監視専用のソリューションは使った方が良いと思います。


:end: まとめ

コンテナは通常のサーバ運用と比べて永続性がないため

ログやメトリクスを集約し、残していくことは特に重要だと思っています。

もっと監視が簡単になって、導入の障壁がなくなれば良いですね。