Rails製のサービスを動かすECS環境をterraformで構築したんだけど結構苦労したんでハマったところ振り返ってみる。
一度Goで作ったAPIをECSで動かしたことはあったんだけどその時には踏まなかった地雷も結構踏んでしんどかった。
前提
- 現状EC2で稼働しているサービス
- Nginx +pumaで稼働している
- 開発はdocker-compose上で進めている
苦労したところ、解決に時間かかったところ
- タスクがPENDINGになったときの対応
- nginxをpumaで動かすためのvolumeの設定
- Railsのassets:precomileがエラーになる
- 改行込みの環境変数の扱い
- ECSからS3へのアクセス
- 固定IPアドレスの設定
- CodeDeployとの連携
以下それぞれの詳細
具体的なコードや設定ファイルの内容は抜きにざっくりそれぞれの内容と解決策を書き残しています。
タスクがPENDINGになったときの対応
ECSのサービスに設定しているネットワークに問題があるとPENDINGになるらしい。この場合ECSの管理画面やCloudWatchにはっきりエラーが出ず原因特定が辛い。
VPC、NAT、サブネット、ロードバランサなどの状態を地道に確認して直した。
nginxをpumaで動かすためのvolumeの設定
socketファイルを通じてpumaとnginxが通信しているのでvolumeを持たせないといけない。 これは同じタスク内にRaisとnginxのコンテナを動かすことでタスク内でvolumeを共有させて解決できた。
Railsのassets:precomile
ECS タスク内のcommand実行でpumaコマンドの前にassets:precompileさせてたんだけど何もエラーが出ずに落ちていた。
で、実行時間が長かったんでタスクに割り当てるCPUとメモリ増やしたら解決。これがはっきりエラーメッセージが出ないんでしばらく気づかなかった。
改行込みの環境変数の扱い
コンテナの環境変数の指定でS3に置いたenvファイルを使うことができて今回そうしたんだけど複数行の環境変数が含まれているせいで想定通りに動作しないRailsの動作があった。
aws ecs execute-command
で確認したところ2行目以降が切り捨てられて読み込まれているらしい。
ちなみにdocker run
コマンドやdocker-compose
でもenvファイルから環境変数は読めるんだけどそれぞれ挙動が違い、docker run
の場合は実行時にエラーになって、docker-compose
の場合は正常に読み込める。
しょうがないんで改行のところを改行コード\n
にして1行にまとめたがRailsの仕様でアプリケーション側で確認すると\n
が\\n
に勝手にエスケープ される。
これはenvの書き方いろいろ書き換えてみたりもしたけど解決できないんで愚直にRaisアプリ内でgsub
メソッドで\\n
を\n
に置き換えて動かした。
固定IPアドレスを使う方法
接続先サービスにIPアドレスの制限があったのでアウトバウンドのIPを固定する必要があった。
ECSのserviceにサブネットが割り当てられていてそのサブネットのNATゲートウェイにElastic IPを使うことでそのIPを固定IPにできる。 また、ECSのタスクはパブリックサブネットに置くこともプライベートサブネットに置くこともできるけど固定IPにするにはプライベートサブネットに置かないといけない。
パブリックサブネットでタスク動かしたんでこれもしばらく気づかず。
ECSからS3へのアクセス
タスクにS3へのGetとListを許可するIAM権限を付与しないといけない。最初Getだけでうまくいくと思っていてListも必要なことにしばらく気づかず。
CodeDeployとの連携
最初ロードバランサのターゲットグループを1つだけ作って動かしたんだけど一通り動いた後にCodeDeployでデプロイしようとしたらエラーが出た。どうやらBlue/Greenデプロイモードだとターゲットグループが2つないとダメだったらしい。ロードバランサ本体やリスナーな1つで大丈夫。
あと管理画面からGUIでデプロイするときはCodeDeploy側ではなくECSのサービス→「更新」ボタンを押下して実行するんだけどCodeDeploy側の画面でデプロイする方法を探してしばらく迷子になってしまった。
ECSのデバッグ
こんな感じでいろいろ問題は起きたけど以下の手段でエラー原因特定して解決してました。