おはようこんにちはこんばんは
えんじにあの@tumuginです
きんようびです
さて、今日はECSなどで運用しているDockerコンテナにAWSのCloudWatch Agentを入れて任意のログファイルをCloudWatchに転送できるようにしたのでその方法を紹介したいと思います。
通常は標準出力に出したものをECSの機能などを用いてCloudWatch側に送り込むことが多いと思いますが、コンテナ内部に複数のログファイルがあってそれぞれを別のロググループに送りたい、のようなときには困ってしまいます。
また、CloudWatchのAPIを用いて転送するのもひとつの手ですが、レートリミットのことを考慮しなければならないなど様々なハマりポイントがあるので、今回はなんとかしてCloudWatch Agentを入れてあげて送る方法にしました。
大前提
- 運用しているDockerイメージをビルドするDockerfileがある
- アプリケーションから吐き出されるログファイルがある
諸々の設定を用意する
まずは amazon-cloudwatch-agent.json
から... ここにCloudWatch Agentに渡す設定を入れておきます。
{
"agent": {
"run_as_user": "app"
},
"logs": {
"logs_collected": {
"files": {
"collect_list": [
{
"file_path": "/var/www/html/storage/logs/hogelog*.log",
"log_group_name": "/hoge-log-group/hogelog",
"log_stream_name": "all",
"timezone": "UTC"
}
]
}
}
}
}
今回はアプリケーションの実行ユーザはappに降格してるので、 run_as_user
でそのユーザを指定してあげます。
これは1つハマりポイントですが、Agentそのものはrootで開始されてないとエラーになって落ちてしまうので注意です。
このファイルに転送したいログファイルを記載しておきましょう。
次に、 cloudwatch-agent-common-config.toml
を用意します。
# This common-config is used to configure items used for both ssm and cloudwatch access
## Configuration for shared credential.
## Default credential strategy will be used if it is absent here:
## Instance role is used for EC2 case by default.
## AmazonCloudWatchAgent profile is used for onPremise case by default.
# [credentials]
# shared_credential_profile = "{profile_name}"
# shared_credential_file= "{file_name}"
## Configuration for proxy.
## System-wide environment-variable will be read if it is absent here.
## i.e. HTTP_PROXY/http_proxy; HTTPS_PROXY/https_proxy; NO_PROXY/no_proxy
## Note: system-wide environment-variable is not accessible when using ssm run-command.
## Absent in both here and environment-variable means no proxy will be used.
# [proxy]
# http_proxy = "{http_url}"
# https_proxy = "{https_url}"
# no_proxy = "{domain}"
こちらについては、特に何も設定してない状態ですがファイルが無いとこれもエラーになってしまうので入れておきます。
また、リージョンについては .aws/config
に配置するファイルで設定しておきます。
[profile AmazonCloudWatchAgent]
region = ap-northeast-1
最後に、ENTRYPOINTを書き換えてCloudWatch Agentを立ち上げるためのスクリプトを配置しておきます。
#!/bin/sh
set -e
# CloudWatch Agentを起動する(rootでないと立ち上がらないので注意)
nohup /opt/aws/amazon-cloudwatch-agent/bin/start-amazon-cloudwatch-agent > /dev/null 2> /dev/null &
# workaround: ユーザ切り替え後に/dev/stderrと/dev/stdoutをappユーザから触れないのでパーミッションを変えておく
# https://superuser.com/questions/1699317/permission-denied-on-dev-stderr-after-sudo
chmod 777 /dev/stderr
chmod 777 /dev/stdout
# コマンドそのものはappユーザで実行する
sudo -u app "$@"
DockerfileにCloudWatch Agent周りの諸々を入れる
FROM debian:buster-slim
COPY --chmod=775 docker/entrypoint /usr/local/bin/entrypoint
COPY --from=amazon/cloudwatch-agent:1.247350.0b251780 /opt/aws/amazon-cloudwatch-agent /opt/aws/amazon-cloudwatch-agent
COPY cloudwatch/amazon-cloudwatch-agent.json /opt/aws/amazon-cloudwatch-agent/etc/amazon-cloudwatch-agent.json
COPY cloudwatch/cloudwatch-agent-common-config.toml /opt/aws/amazon-cloudwatch-agent/etc/common-config.toml
RUN useradd -m app
COPY --chown=app:app cloudwatch/aws-config /home/app/.aws/config
USER root
ENTRYPOINT /usr/local/bin/entrypoint
COPY命令でCloudWatch Agentのイメージから実行ファイルを取ってきて、必要な設定ファイルをイメージ内に入れてあげることで動かしています。
ENTRYPOINTだけを書き換えているので、元々実行したいCMDには影響することなく起動させることができます。