asobannのAWS ECSデプロイでやったことと引っかかったこと その2

yattom.hatenablog.com

その1でasobannをAWS ECSに手作業でインフラ構築できた ので、これを自動化したい。CloudFormationかTerraFormを使うといいらしいと聞いて、あまり考えずにCloudFormationを使ってみることにした。これも初めて。

CloudFormationにアプローチしてたくさん間違える

軌道に乗るまでに、こんなことをした。

  1. デザイナーを用いたチュートリアルで流れを把握
  2. デザイナー上で自分で1から作ろうとしてみて、なにを作ればいいのか把握し切れてないことに気づく(コンソールからECSのクラスターを作ったときは、裏側で作ったり設定したりしてくれていたものがあって、それがわかってない)
  3. アプローチについて調べていたらこちらの記事からテンプレートのサンプル にたどり着き、これを参考にすることにした
  4. 手で作ったECSのクラスターからCloudFormationテンプレートを生成しておき、参照できるようにした

3番で見つけたテンプレートのサンプルが死ぬほど便利で、助かりました。

  • どんなリソースが必要で、どうリソース同士をつなげるのか書いてある
  • テンプレートファイルが分割してあり、わかりやすいし、分割するときの書き方もわかる
  • YAMLの書き方、!Refや!Subの書き方の例が豊富にある

必要なリソース種類がわかれば、個々のリソースに必要な設定はリファレンスを見るだけなので、あまり苦労がない。

変える部分を見落とさないためにも、自分の理解のためにも、写経のように見ながら再入力しつつテンプレートを書くことに。理解は深まったけど、見落とし、ぽかミス、勘違いなどでたくさんトライ&エラーを繰り返すことになったのでありました。

  • 複数のアベイラビリティゾーンとサブネットを作って渡さないと、AutoScalingGroupでエラーになる(最低2つ、みたいな)。ここでサブネットを増やしたとき、AWS::EC2::SubnetRouteTableAssociationも合わせて増やさないと、ルーティングが狂って変な挙動になる(EC2インスタンスクラスターのインスタンスにならない、サブネットのルーティングテーブルが勝手に変化する(ように見える)、EC2インスタンスsshできたりできなかったりする)。エラーにはならなかったので気づきにくい。
  • Service Discoveryで指定したホスト名が解決できず、VPCの設定で「DNS解決」と「DNSホスト名」の両方を有効にしたら解決した。テンプレートでは AWS::EC2::VPCに対してEnableDnsHostnamesとEnableDnsSupportを両方trueにする。(DNSホスト名が必要なのは解せないけど、こうしたら動いた)。
  • Service DiscoveryのためにPrivate NamespaceリソースをCloudFormationで作成すると、HostedZoneが作られる(明示的に作らなくてよい)。サンプルにはなかったのでちょっと迷ったところ。
  • LoadBalancerからTargetGroup(アプリ)にアクセスするには、SecurityGroupの設定が必要。LB用のSGとアプリのSGが異なるなら、アプリのSGはLBのSGからのアクセスを明示的に許可する必要がある。手で作ったときはアプリとLBを同じSGにしていたので、気づかなかった。なお本来は同じSGであっても通信は許可されていなくて、これはSecurity Groupについての基礎知識っぽいのだけど、VPC作成時にデフォルトでできるSecurity Groupでは自動で許可されているので、どうやら気がつかなかったみたい。SGさんいつも厳しい。いつか仲良くなれるだろうか。
  • CloudFormationテンプレートを変更してスタックを更新するとき、Task Definitionが変わったりしてタスクが再起動することがあるが、このとき新しいタスクを起動→古いタスクを終了、という順序で動く(これはデプロイの設定で変わるような気がするんだけど、ちゃんと調べていない)。そのとき、新しいタスクを起動する分の余裕(CPUとメモリ)がコンテナインスタンス(EC2インスタンス)にないと、当然ながら起動しなくて、スタックの更新自体も先に進まなくなる(手で元のタスクを停止するとうまくいったりした気がする)。
  • よくわからなくなったら、コンソールからスタックを削除して作り直すと、きれいにうまくいく場合がある。後から考えると、同じ論理ID(テンプレート中のリソース名)で後から矛盾する設定変更をしたせいだった気がする。スタックの削除は普通自動で進み放っておけば終わる(5~10分くらいだった。コンソール上で進捗を追える)のだけど、たまに削除が進まなくなることがあった。VPCが消えないことが多かった気がする。対象リソースをコンソールから手で削除すると、スタックの削除も無事に完了した。
  • なお4番の、構築済みのClusterからCloudFormationのテンプレートを作ってくれるので、それで構築済みのものを見て参考にしようと思ったら、肝心なリソースがぜんぜん入ってなくてダメだった。リソースは手で選ぶので漏れたのかもしれないけど、再確認はしてないのでよくわからない。動いてるやつを元に作ったテンプレートを直すだけって、すごいいいアイデアだと思ったのになあ。

あとはまあ、テンプレートを更新して実際にCloudFormationで更新するのに、実行開始→作業中…→失敗!になるまでの時間が割とかかる(10分とか)のが、トライ&エラーを繰り返すのに地味に厳しかった。また失敗したときの理由や状態を調べる方法が、なかなかつかめなかった。

  • コンソール上でCloudFormationのスタックを指定してイベントを見る
  • ネストしたテンプレートは別のスタックになっているので、そのイベントを見る。削除済みとかのものを見ると新しいことがわかるときもある。
  • 作成したClusterをコンソールで開いて、Serviceを指定して、そこからイベントを見る
  • 作成したClusterをコンソールで開いて、Taskを指定して、そこからLogsを見る。反映が遅いことがあるので、ページ下の方のコンテナから詳細を開いてCloudWatchのログを表示した方がいいかも(CloudWatchを設定している必要がある)。Stoppedのタスクを見ると新しいことがわかるときもある。
  • EC2インスタンスsshして調べる。インスタンス自体のログは/var/logに出ているし、docker execしてコンテナの中を見ることもできる

f:id:yach:20200807154138p:plain


インスタンスやコンテナのことはあるていど探れたが、ネットワーク周りはどうやって調査したらいいのかまだよくわからない。また、ECSのタスクが起動しないで、必要な要件を満たすインスタンスがないよというメッセージを出すのがとても困った。いちおうここにガイドラインがあるけどあんまり役に立たない。

続く

これで環境はほぼ構築できたのだが、まだ動かない。クラスター内でアプリがMongoDBとRedisを見つけるところで、追加の作業(実装も含む)が必要になった。またMongoDBのデータがコンテナ内なので、なにかあったらデータが消えてしまう。その話はまた次回。

 

yattom.hatenablog.com