ECSにおけるログの扱い方
ECSにおけるログの扱い方:
ECS運用において、Dockerコンテナはデプロイの度に破棄・生成が繰り返されるため、アプリケーションを始めとする各種ログはコンテナ内に保管しておくことができない。
すべてのログはイベントストリームとして扱い、外部ストレージに送る必要がある。
本ページではECSを運用していく中でログの扱いに関する遍歴をまとめていく。
※このページは、ECS運用のノウハウからログに関する項目を抜き出して再編集したものです。
ECS運用当初 (2016年10月)は右も左も分からない状況だったので、ログの永続化についてはあまり深く考えずAWSが標準で提供するCloudWatch Logs(
CloudWatchにログを転送してしまえば、後はElasticsearch Serviceと連携し、ログの可視化まで容易だったからである。
しかし、CloudWatch Logsの運用にはいくつかの問題があった。
前述の運用は数ヶ月で廃止し、代案として道入したのがログをFluentd経由でElasticsearchに流す方法である。
Fluentdがログ集約基盤となったことで、不正なログの検知や将来的なバックエンドサービスの切り替えが容易な構成となった。
アプリケーション例外はSentryで追跡しており、Sentryが検知できない標準エラーやアプリケーションロガーが生成するメッセージをFluentdに送信。Fluentdはログを監視し、一定期間内に閾値を超えた回数発生したメッセージをSlackに通知。すべてのログはS3とElasticsearchに並列で書き込みを行う。
しばらくテスト稼働させていると、一部のログがElasticsearch/S3に書き込まれていないことに気付いた。
ログクラスタにはロードバランサとしてCLBを利用していたが、どうもCLBのアイドルタイムアウト直後の数リクエストでログが消失している。試しにCLBを外してアプリケーションからログクラスタにアクセスすると問題は起きなかった。
ログクラスタのECSコンテナインスタンスを調査したところ、
同様の問題をIssueで見つけたが、ECSログドライバはKeepAliveの仕組みが無いため、アイドルタイムアウトの期間中にログの送信が無いとELBがコネクションを切断する仕様らしい。AWSサポートからも同様の回答を得られた。
CLBの代替策として、ログクラスタにはロードバランサを使わず、Route53のWeighted Routingでリクエストを分散することにした。
仕組みとしては、ECS(EC2)の全てのプライベートIPをRoute 53のドメインに紐づけておくイメージである。
ただし、この方式ではログクラスタのスケールイン・アウトに合わせてRoute 53のレコードを更新する必要がある。ここではオートスケールの更新をSNS経由でLambdaに検知させ、適宜レコードを更新する仕組みを取った。
2017年9月、AWSは新しいロードバランサとしてNLBを発表した。NLBはTCPトラフィックの分散に最適ということで、Weighted Routingの変わりにログクラスタにNLBを道入。結果的には非常に安定し、ログ周りで問題が起こることは無くなった。
※: あまり知られてないですが、NLBも動的ポートマッピングをサポートしており、タスク定義でホストポートに
2018年9月にはService Discoveryが東京リージョンにローンチされた。
ECSコンテナ間の通信をDNS(Route 53)で行える仕組みのため、上述のNLBが不要となり、運用コストの削減にも繋がる。
Service DiscoveryはコンテナごとにENIを割り振るため、タスクのネットワークモードを
例えばタスク数を2にすると、Route 53上でも2つのAレコードが生成されることを確認できた。
ECS(EC2)のPrivate IPsはこのように表示される(一番左はEC2自体のIP)。
尚、インスタンスタイプごとにENIの上限が決まっており、例えばt2.mediumであれば3つまでとなる。
t2.medium 1台の構成で3以上のタスクを指定すると、以下のようなエラーが発生する。
※1: Service Discoveryの導入方法はクラスメソッドの記事が分かりやすい
※2: インフラの構築にはTerraformを使っていたので、プライベートゾーンはaws_route53_zoneで作れば良いと思いきや、ECSコンソール上でDNSが見つからないと言われた。どうやらaws_service_discovery_private_dns_namespaceで作成する必要があった
※3: 試しで作ったゾーンを削除しようとしたところ、サービスディスカバリに紐付いてるから削除できないとエラーが出た (クラスタからはサービスを削除済み)。どうやら今のところゾーンの削除はdelete-namespace APIを使う必要がある様子
概要
ECS運用において、Dockerコンテナはデプロイの度に破棄・生成が繰り返されるため、アプリケーションを始めとする各種ログはコンテナ内に保管しておくことができない。すべてのログはイベントストリームとして扱い、外部ストレージに送る必要がある。
本ページではECSを運用していく中でログの扱いに関する遍歴をまとめていく。
※このページは、ECS運用のノウハウからログに関する項目を抜き出して再編集したものです。
Phase 1: CloudWatch Logsの導入
ECS運用当初 (2016年10月)は右も左も分からない状況だったので、ログの永続化についてはあまり深く考えずAWSが標準で提供するCloudWatch Logs(awslogs
ドライバ)を利用することにした。CloudWatchにログを転送してしまえば、後はElasticsearch Serviceと連携し、ログの可視化まで容易だったからである。
しかし、CloudWatch Logsの運用にはいくつかの問題があった。
- アプリケーションが生成する例外(スタックトレース)が行単位でストリームに流れるため、ログを追いづらい
- バックエンドでMultiline codec pluginといったプラグインの利用を検討したが、Elasticsearch Service自体がプラグインのインストールをサポートしていなかった。またEC2上にElasticsearchを構築することも考えたが、バックエンドサービスへの依存が高く、将来的にログドライバを変更する際の障壁と考え、導入を見送った
- 特定のキーワード・頻度に一致したエラーを検知することが難しい
Phase 2: ログクラスタの構築
前述の運用は数ヶ月で廃止し、代案として道入したのがログをFluentd経由でElasticsearchに流す方法である。Fluentdがログ集約基盤となったことで、不正なログの検知や将来的なバックエンドサービスの切り替えが容易な構成となった。
Fluentdの役割
アプリケーション例外はSentryで追跡しており、Sentryが検知できない標準エラーやアプリケーションロガーが生成するメッセージをFluentdに送信。Fluentdはログを監視し、一定期間内に閾値を超えた回数発生したメッセージをSlackに通知。すべてのログはS3とElasticsearchに並列で書き込みを行う。
ログの欠損問題
しばらくテスト稼働させていると、一部のログがElasticsearch/S3に書き込まれていないことに気付いた。ログクラスタにはロードバランサとしてCLBを利用していたが、どうもCLBのアイドルタイムアウト直後の数リクエストでログが消失している。試しにCLBを外してアプリケーションからログクラスタにアクセスすると問題は起きなかった。
ログクラスタのECSコンテナインスタンスを調査したところ、
/var/log/docker
に次のようなログが残っていた。time="2017-08-24T11:23:55.152541218Z" level=error msg="Failed to log msg \"...\" for logger fluentd: write tcp *.*.*.*:36756->*.*.*.*:24224: write: broken pipe" 3) time="2017-08-24T11:23:57.172518425Z" level=error msg="Failed to log msg \"...\" for logger fluentd: fluent#send: can't send logs, client is reconnecting"
Phase 3: Weighted Routing
CLBの代替策として、ログクラスタにはロードバランサを使わず、Route53のWeighted Routingでリクエストを分散することにした。仕組みとしては、ECS(EC2)の全てのプライベートIPをRoute 53のドメインに紐づけておくイメージである。
ただし、この方式ではログクラスタのスケールイン・アウトに合わせてRoute 53のレコードを更新する必要がある。ここではオートスケールの更新をSNS経由でLambdaに検知させ、適宜レコードを更新する仕組みを取った。
Phase 4: NLBの道入
2017年9月、AWSは新しいロードバランサとしてNLBを発表した。NLBはTCPトラフィックの分散に最適ということで、Weighted Routingの変わりにログクラスタにNLBを道入。結果的には非常に安定し、ログ周りで問題が起こることは無くなった。※: あまり知られてないですが、NLBも動的ポートマッピングをサポートしており、タスク定義でホストポートに
0
を指定することでALB同様のフォワーディングが可能です
Phase 5: Service Discoveryの道入
2018年9月にはService Discoveryが東京リージョンにローンチされた。ECSコンテナ間の通信をDNS(Route 53)で行える仕組みのため、上述のNLBが不要となり、運用コストの削減にも繋がる。
Service DiscoveryはコンテナごとにENIを割り振るため、タスクのネットワークモードを
awsvpc
に変更する必要がある。例えばタスク数を2にすると、Route 53上でも2つのAレコードが生成されることを確認できた。
ECS(EC2)のPrivate IPsはこのように表示される(一番左はEC2自体のIP)。
尚、インスタンスタイプごとにENIの上限が決まっており、例えばt2.mediumであれば3つまでとなる。
t2.medium 1台の構成で3以上のタスクを指定すると、以下のようなエラーが発生する。
service log was unable to place a task because no container instance met all of its requirements. The closest matching container-instance XXX encountered error "RESOURCE:ENI". For more information, see the Troubleshooting section.
※2: インフラの構築にはTerraformを使っていたので、プライベートゾーンはaws_route53_zoneで作れば良いと思いきや、ECSコンソール上でDNSが見つからないと言われた。どうやらaws_service_discovery_private_dns_namespaceで作成する必要があった
※3: 試しで作ったゾーンを削除しようとしたところ、サービスディスカバリに紐付いてるから削除できないとエラーが出た (クラスタからはサービスを削除済み)。どうやら今のところゾーンの削除はdelete-namespace APIを使う必要がある様子
コメント
コメントを投稿