ECSにおけるログの扱い方

ECSにおけるログの扱い方:


概要

ECS運用において、Dockerコンテナはデプロイの度に破棄・生成が繰り返されるため、アプリケーションを始めとする各種ログはコンテナ内に保管しておくことができない。

すべてのログはイベントストリームとして扱い、外部ストレージに送る必要がある。

本ページではECSを運用していく中でログの扱いに関する遍歴をまとめていく。

※このページは、ECS運用のノウハウからログに関する項目を抜き出して再編集したものです。


Phase 1: CloudWatch Logsの導入

ECS運用当初 (2016年10月)は右も左も分からない状況だったので、ログの永続化についてはあまり深く考えずAWSが標準で提供するCloudWatch Logs(awslogsドライバ)を利用することにした。

CloudWatchにログを転送してしまえば、後はElasticsearch Serviceと連携し、ログの可視化まで容易だったからである。



Network (3).png


しかし、CloudWatch Logsの運用にはいくつかの問題があった。

  • アプリケーションが生成する例外(スタックトレース)が行単位でストリームに流れるため、ログを追いづらい

    • バックエンドでMultiline codec pluginといったプラグインの利用を検討したが、Elasticsearch Service自体がプラグインのインストールをサポートしていなかった。またEC2上にElasticsearchを構築することも考えたが、バックエンドサービスへの依存が高く、将来的にログドライバを変更する際の障壁と考え、導入を見送った
  • 特定のキーワード・頻度に一致したエラーを検知することが難しい


Phase 2: ログクラスタの構築

前述の運用は数ヶ月で廃止し、代案として道入したのがログをFluentd経由でElasticsearchに流す方法である。

Fluentdがログ集約基盤となったことで、不正なログの検知や将来的なバックエンドサービスの切り替えが容易な構成となった。



Network (4).png



Fluentdの役割

アプリケーション例外はSentryで追跡しており、Sentryが検知できない標準エラーやアプリケーションロガーが生成するメッセージをFluentdに送信。Fluentdはログを監視し、一定期間内に閾値を超えた回数発生したメッセージをSlackに通知。すべてのログはS3とElasticsearchに並列で書き込みを行う。


ログの欠損問題

しばらくテスト稼働させていると、一部のログがElasticsearch/S3に書き込まれていないことに気付いた。

ログクラスタにはロードバランサとしてCLBを利用していたが、どうもCLBのアイドルタイムアウト直後の数リクエストでログが消失している。試しにCLBを外してアプリケーションからログクラスタにアクセスすると問題は起きなかった。



Network (1).png


ログクラスタの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" 
同様の問題をIssueで見つけたが、ECSログドライバはKeepAliveの仕組みが無いため、アイドルタイムアウトの期間中にログの送信が無いとELBがコネクションを切断する仕様らしい。AWSサポートからも同様の回答を得られた。


Phase 3: Weighted Routing

CLBの代替策として、ログクラスタにはロードバランサを使わず、Route53のWeighted Routingでリクエストを分散することにした。



Network (2).png


仕組みとしては、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が不要となり、運用コストの削減にも繋がる。



ECS-Service-Discovery-1024x865.png


Service DiscoveryはコンテナごとにENIを割り振るため、タスクのネットワークモードをawsvpcに変更する必要がある。

例えばタスク数を2にすると、Route 53上でも2つのAレコードが生成されることを確認できた。
Screen_Shot_2018-11-11_at_6_24_04.png

ECS(EC2)のPrivate IPsはこのように表示される(一番左はEC2自体のIP)。



Screen_Shot_2018-11-11_at_6_27_41.png


尚、インスタンスタイプごとに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. 
※1: Service Discoveryの導入方法はクラスメソッドの記事が分かりやすい

※2: インフラの構築にはTerraformを使っていたので、プライベートゾーンはaws_route53_zoneで作れば良いと思いきや、ECSコンソール上でDNSが見つからないと言われた。どうやらaws_service_discovery_private_dns_namespaceで作成する必要があった

※3: 試しで作ったゾーンを削除しようとしたところ、サービスディスカバリに紐付いてるから削除できないとエラーが出た (クラスタからはサービスを削除済み)。どうやら今のところゾーンの削除はdelete-namespace APIを使う必要がある様子

コメント

このブログの人気の投稿

投稿時間:2021-06-17 22:08:45 RSSフィード2021-06-17 22:00 分まとめ(2089件)

投稿時間:2021-06-20 02:06:12 RSSフィード2021-06-20 02:00 分まとめ(3871件)

投稿時間:2021-06-17 05:05:34 RSSフィード2021-06-17 05:00 分まとめ(1274件)