ECSのcronジョブのログがCloudWatchに出ない問題 - /proc/1/fd/1リダイレクト
AWS ECSのコンテナ内で、cronを用いて定期実行しているプログラムがあるのですが、そのプログラムのログがCloudWatch Logsに出力されないことがありました。そのことについて調べたことをまとめます。
現象
runner "ExampleBatch.exec"
上記のように、バッチをcron(ruby gemwhenever
を使用)で定期実行するよう、ECSで起動しているコンテナ内で設定していました。cronではないメインプロセスのログはCloudWatch Logsに出力されるものの、ExampleBatch
内の処理で出力されるはずのログがCloudWatch Logsに出ないという状況でした。
対応方法
runner "ExampleBatch.exec" >> /proc/1/fd/1 2>&1
このように、cronに登録されたバッチ処理の標準出力・標準エラー出力をメインプロセス(/proc/1/fd/1
)にリダイレクトすると解決しました。
原因
ECS上のプログラムのログがCloudWatch Logsに出力されるまでの流れは以下です。
ECSのログドライバ(awslogs
)は、コンテナのメインプロセス(PID 1
)の標準出力・標準エラー出力(stdout
/stderr
)をdocker logs
に送ります。CloudWatch Logsはawslogs
ログドライバを介して、docker logs
の内容を収集し、出力します。
これにより、以下の理由でcronジョブ内の出力はCloudWatch Logsに流れませんでした。
- cronジョブはメインプロセス(
PID 1
)ではなく、別プロセスとして起動する。 - cronジョブの出力は、何もリダイレクトを指定しない場合、デフォルトでは
stdout
やstderr
をターミナルには表示せずMAILTO
に設定された宛先(または/var/spool/mail/{ユーザ名}
)にメールとして送信しようとする。-
docker logs
に送られない
-
ですので、cronジョブのログ出力先をメインプロセスの標準出力にすることによって、CloudWatch Logsにログを出すことができました。
UNIXの出力周りについて整理
UNIX系OSにおける出力の仕様を整理します。
標準出力(stdout)という言葉が出てきました。
標準出力(stdout)とは、プログラムがデータを出力する際のデフォルトの出力先を指します。
通常、stdout の出力先は 端末(ターミナル) ですが、リダイレクト(>
, >>
)を使用することでファイルや別のプログラムに送ることもできます。
UNIX系OS(Linux, macOSなど)では、プロセスが扱うデータの流れ (ストリーム) は3つ あります。
名前 | ファイルディスクリプタ (FD) | 説明 | デフォルトの接続先 |
---|---|---|---|
標準入力 (stdin) | 0 |
プログラムが受け取る入力 | キーボード |
標準出力 (stdout) | 1 |
プログラムの通常の出力 | ターミナル |
標準エラー出力 (stderr) | 2 |
エラーメッセージの出力 | ターミナル |
このストリームがどこに接続されるかによって、出力の行き先が変わります。
通常、シェル(Bashなど)でコマンドを実行すると、標準出力 (stdout) は自動的にターミナルに接続されるため、結果が見えます。
echo "Hello"
この場合、stdout の「接続先」はターミナルになります。
一方、
cron
は対話的なシェルではなく、バックグラウンドで動くプロセスなので、デフォルトの標準出力 (stdout
) の接続先がありません。
* * * * * echo "Hello"
cron
で echo
を実行しても、どこにも出力が接続されていないため、結果が見えません。
ですので、例えば以下のように出力をメインプロセスの標準出力に渡すことができます。
ファイルにリダイレクト
* * * * * echo "Hello" >> /proc/1/fd/1 2>&1
なお、2>&1
は、標準エラー出力(stderr, fd 2
)を標準出力(stdout, fd 1
)にリダイレクトするという意味です。こうすると、標準エラー出力も標準出力に送られます。
-
&1
の&
は、「1
をファイルとしてではなくFD
として扱う」という意味。 -
2>1
だとstderr
をファイル1
に書き込もうとするので間違い。 -
2>&1
で、「stderr
をstdout
にリダイレクトする」という意味になる。
ということになります。
Discussion