Amazon ECS を本番環境で使ってみて感じたこと・困ったこと

先日行なわれた JAWS-UG コンテナ支部 #1 にて、Amazon ECS を利用して本番環境を構築した際に感じたこと・困ったことについて発表する機会をいただきました.

当日は時間の都合と緊張から駆け足で進めてしまったので、補足 & まとめ的な記事をば.


スライドでも触れていますが、今回本番環境に持って行ったのは
- Fluentd (ウェブサーバーなどから送られてくるログの受け取り、パース処理など)
- Elasticsearch (Fluentd で受け取ったログの突っ込み先)
- Kibana (Elasticsearch に溜め込んだログを集計したりグラフ出したり)という構成のホットデータなログの収集サーバー部分です.

以下、勘違いしてる箇所/もっといい解決方法などありましたら Twitter などにてぜひご指摘くださいませ.

tl;dr

「必要最低限な機能は揃っています」というのが個人的な感想.

ECS の前にまずはローカルの開発環境を Docker で運用しておきたい

ECS がサポートしているコンテナ実装は Docker なので、まずは日々の開発を Docker コンテナで行えるように切り替えていくことでスムーズに本番環境の設計に進めると思います.

現時点の ECS で困ったこと・感じたこと

ECS がサポートしている Docker オプションはあくまでも一部

例えば ECS は -net オプションをサポートしていません. そのため、このようなサポート外のオプションを使ってローカルマシンの VM などでうまくいったものをそのまま ECS に持ち込もうとするとほぼ確実に戻りが発生します. 過去に ECS ではないツールでクラスタリングに挑戦した経験がある方など、Docker に詳しい人ほどハマるかも.

公式ドキュメントの Task Definition Parameters - Amazon EC2 Container Service などで使えるオプションを確認し、最終的な構成をイメージしてからローカルでの検証を進めると良いかもしれません.

とはいえ、ローカルで動く ECS はないので、AWS 上に構築する環境をローカルで再現するのは現時点では厳しそうです. Dynamo DB みたいにローカル環境で ECS が使えると捗りますよね. 機能についての話ではないですが、Kubernetes に代表される他のコンテナ管理ツールはローカルでも動かせるのがいいですね. (re:Invent 2015 でローカル ECS が!みたいな展開を期待…)

サービスディスカバリーどうするの

ローカル環境や CI 環境では Docker のオプションである -link を使ってコンテナ間のネットワーキングを担保することが多いですが、本番環境でコンテナのスケーリングを考えるとサービスディスカバリーについて考える必要性が出てきます. たとえば上のログ収集サーバーでいうと、「Elasticsearch や Fluentd はスケールさせたいけど、Kibana についてはそこまでスケールさせないよね」という状況とか.

ECS では、走らせるコンテナの数は ServicedesiredCount で決まります. そのため、Kibana と Elasticsearch のコンテナ数を別々に定義したい場合は Service (Task Definition) を別々に作ってあげる必要があります.

links オプション (Docker でいう -link オプション) は同一の Task Definition に定義されたコンテナ間でのみ有効なので、今回の Kibana と Elasticsearch のように別々の Task Definition に定義するコンテナ間の通信を解決するためにはサービスディスカバリーできる機構が別途必要になる、という流れです.

この問題を解決する方法として、スライドでも紹介した Amazon Compute Blog の記事 Service Discovery via Consul with Amazon ECS のような Consul を使った方法があります. Consul によるサービス監視などのメリットも享受できることから、現時点の ECS 運用を考えるとかなり理想的だと思います.

ただし今回の本番環境構築では Consul は利用せず、Internal ELB によって Kibana から Elasticsearch にアクセスできるようにする、という選択をしました. 上記記事の方法を避けた理由は「何らかの Service が動作している全インスタンスで必ず走らせるコンテナ」という概念が現時点では ECS に存在しないからです.

ワークアラウンドとして ECS インスタンスの数に合わせて consul-agent と registrator が定義された ServicedesiredCount をセットするという方法は考えられますが、そもそも consul-agent や registrator は ECS における Service という概念よりは、ecs-agent のようなエージェント/デーモンに近い概念がフィットする存在だと思うので、個人的にはこのエリアに ServiceTask Definition を絡ませるのはちょっと違うかな…と感じました.

事前に consul-agent や registrator 入りの AMI を作っておくという方法も考えられます. これは docker pull が不要なぶん、そのインスタンスで Service を動作させるまでの時間的なオーバーヘッドが抑えられるというメリットはありますが、これはこれで consul-agent や registrator のコンテナが突然死したときの対応など ECS 外で考慮しなきゃいけないことが増えるので、あまり好手ではないかなと思います. CloudFormation で EC2 インスタンス作成時にごにょごにょする(上記記事のパターン)のも同様かな…

要は ECS マネージドなデーモンコンテナ的機能が Service みたいに状態の維持を担保してくれたらいいなーという話なんですが、そんな機能が ECS に追加される日がきたら Consul を使ってサービスディスカバリーをしようかなーと思っています. (必要にかられて上に挙げた方法のどれかで Consul 的なものを使う日が先に来てしまう気もしますが)

それから、このあたりが実現するとモニタリング用コンテナだったりログ収集用コンテナだったりも安定稼働できそう、というのもデーモンコンテナ的なものを欲する理由の一つだったりします.

あと、実際に試してないのでどういう挙動になるかは不明なんですが、すべての Task Definition に consul-agent と registrator を突っ込んでおくとどうなるんでしょうか. もしかしたらうまいこと動いてくれるのかしら… でも複数の Service が同一のインスタンスで走ったらコンフリクトを起こしそうな気もするし、なんか気持ち悪いかな…