yikegaya’s blog

仕事関連(Webエンジニア)と資産運用について書いてます

RailsのECS環境を構築した時のエラー対応振り返り

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のデバッグ

こんな感じでいろいろ問題は起きたけど以下の手段でエラー原因特定して解決してました。

  • タスクにCloudWatchのロググループを設定して確認する
  • AWS管理画面上でECS→クラスタ→サービス内のイベントを確認する
  • タスクの詳細画面を確認する
  • aws ecs execute-commandコマンドを使ってコンテナの状態を確認する