先日、Deno のコミュニティでログ関連のモジュールのファイル出力に関して質問していた。
質問自体は解決したが、今時はリモートにログ溜める方が一般的かもという話があって、確かにそうだなと感じた。
が、今作ってるものはEC2で動作させてお安く運用したいというのもあり、Fargate使うときみたいにあまり気にせず出力をCloudWatchに吐いてくれる感じにならない。
というわけで、EC2上で動作させたアプリケーションのログをCloudWatchで取得するまでのメモしておきたい。
参考
- AWS - Amazon CloudWatch Logs とは
- enomotodev’s blog - Nginx のアクセスログとエラーログを CloudWatch に送信する
- 【systemd】StandardOutput=fileはログをファイルに出力できるけど追記されない
前提
- EC2(AmazonLinux2) のインスタンスは作ってある
設定
awslogs をインストール。
1 | sudo yum install -y awslogs |
IAM ポリシーの作成
- IAM > ポリシー > ポリシーの作成
- ポリシーの作成
- JSON
- 次のものを貼り付け
1 | { |
- タグの追加:今回はスキップ
- ポリシーの作成
- 名前:任意(今回はAwsLogsPolicy)
- 説明:任意
ポリシーを作成。
IAM ロールの作成
- IAM > ロール > ロールの作成の順に開く
- 信頼されたエンティティを選択
- 信頼されたエンティティタイプ:AWSのサービス
- ユースケース:EC2
- 許可を追加
- 作成したポリシーにチェックを入れる
- 名前、確認、および作成
- ロール名:任意(今回はAwsLogsRole)
- 説明:任意
ロールを作成。
ロールをEC2インスタンスに関連付け
- 作ってあるログを収集したいいインスタンスを選択
- アクション > セキュリティ > IAM ロールを変更
- IAM ロールを変更
- 作成したロールを割り当て
ここまでやってから気が付いたが、インスタンスに今回作ったロールを割り当てるのは、悪手な気がする。
- インスタンスにインスタンス用のロールを割り当て
- インスタンス用のロールに使いまわすポリシーを充てる
の方が良さそう。
aws logs の設定ファイルの編集
デフォルトは次の状態だった。
1 | [plugins] |
以下のように書き換えた。
1 | [plugins] |
1 | # 上の方に、パラメータの説明が書いてある |
以下を追記する。
1 | [/aws/ec2/application.log] |
awslogs の起動
1 | # 起動 |
動作確認
動作確認したいので、適当に CloudWatch にログ転送させるアプリを実行してみる
1 | import { delay } from "https://deno.land/std@0.142.0/async/mod.ts"; |
実行。標準出力をすべて application.log へリダイレクト。
1 | $ pwd |
ここでCloudWatch のロググループを見に行くと、/aws/ec2/application.log
が作成されているので、開いてみる。
ログの書き出しがされているのが確認できる。
エラーログは標準ログと別
以下のように console.error
を使うように書き換える。
1 | import { delay } from "https://deno.land/std@0.142.0/async/mod.ts"; |
この状態で、実行すると次のようになる。
1 | $ deno run app.ts >> application.log |
標準出力とエラー出力が、分かれていたなと思い出し実行コマンドを以下のように修正。
1 | $ deno run app.ts >> application.log 2>&1 |
これで、console.error() もログ出力がリダイレクトされるので、すべてCloudwatchで参照できるようになる。
この状態だと、ログファイルが永遠に肥大化を続ける気がする
というわけで、アプリケーションの外側で logrotate を使って適当にログローテーションを設定してみる。
やり方は、以前書いていた。
ログローテーションを考える
(昔書いた記事が役に立つのは実に有意義である。)
/etc/logrotate.d/app1 を作成。
ログローテーションもするのでディレクトリを掘る前提にしておく。
1 | /home/ec2-user/app/log/*.log{ |
hourly で、logrotate が動くようにしておく。
1 | cp /etc/cron.daily/logrotate /etc/cron.hourly/ |
実行コマンドも変更
1 | $ mkdir log |
別コンソールで、ログローテーションを強制実行するといい具合にローテーションが起きてるのが確認できる。
これで単一のファイルのログが永遠と大きくなるのは回避できるはず。
サービス化する
サービス化したいので、system unit ファイルを作成します。
1 | [Unit] |
サービス化するにあたって終了してほしくないので、アプリケーション側は無限ループにしておく。
(本来はサーバーアプリの起動をすることになるだろうけれども。)
1 | import { delay } from "https://deno.land/std@0.142.0/async/mod.ts"; |
と、こんな具合で用意して、次のように起動する。
1 | $ sudo systemctl daemon-reload |
ログが書き込まれない。
調べてみてこの記事にたどり着く。
こちらに倣い、/etc/systemd/system/deno-app.service を書き換える。
1 | [Unit] |
もしくは、
1 | [Unit] |
こちらもいけそうだが、AmazonLinux2 に入っていた systemd のバージョンは、219だったので現状は、修正プラン1しか使えない。
1 | $ systemctl --version |
というわけで、/etc/systemd/system/deno-app.service をプラン1に書き換える。
改めてサービスを起動しなおすと、また欲しい先にログを吐き出してくれるようになる。
ここまでやれば、まぁ一旦いいだろう。
今回は、EC2で動かしているサービスのログをCloudWatchに吐き出すのを試みてみた。
よくやる構築なのだろうけど、イチからやらないと身には付かないものです。
これができちゃうと、複雑なログの機構をアプリケーション内で抱えるのって確かに悪手にも思える。
今回はDenoでやってみたが、deno deploy で動作させることを考えると、基本は console.hogehoge でログ出しておく方がいいだろうし。
逆に(複雑な)ログの機構を抱えるなら、cliツールとかなら検討してもいいのかもしれない。
ではでは。