猫でもわかるWeb開発・プログラミング

本業エンジニアリングマネージャー。副業Webエンジニア。Web開発のヒントや、副業、日常生活のことを書きます。

【AWS】ECS のサービスを正しく分けていなくて失敗した話

AWS ECS について

AWS ECS は、

クラスタの中にサービスがあり、
サービスの中にタスクがあり、
タスクの中にコンテナ(Dockerコンテナ)がある

箇条書きで表すとこのようになっている

  • クラスタ
    • サービス
      • タスク
        • コンテナ
    • サービス
      • タスク
        • コンテナ
        • コンテナ

僕も一応意識はしてサービスを分けていたが、この前失敗したことがあったので、その失敗談と合わせてまとめておく。

失敗例1: オートスケールで壊れる

こういうサービスの構成を組んでいたことがあった

  • クラスタ
    • サービス
      • タスク
        • Web API コンテナ
        • Cron コンテナ

特に何も考えず、一つのサービスの中に Web API コンテナと Cron コンテナ(定期処理を行うコンテナ)を一緒に突っ込んでいた。

しかし、オートスケールをしようとした時に問題が発生する。サービスに対してオートスケールの設定を追加すると、負荷に応じてタスクを増やすことができる。

  • クラスタ
    • サービス
      • タスク
        • Web API コンテナ
        • Cron コンテナ
      • タスク
        • Web API コンテナ
        • Cron コンテナ

すると、Cron コンテナも増えてしまい、日次の定期処理が2つ動いておかしなことになる。

Cron コンテナは常に1つだけ起動して、Web API コンテナは負荷に応じて増減してほしい。必要タスク数が違う場合はサービスを分けるのが正しい。

  • クラスタ
    • Web API サービス
      • タスク
        • Web API コンテナ
      • タスク
        • Web API コンテナ
    • Cron サービス
      • タスク
        • Cron コンテナ

デプロイ時にも同じ問題が発生する

オートスケールに限らず、デプロイ時にも同じ問題が発生する。サービスの定義では、デプロイの戦略も定義することができる。

  • Web API サービスは、デプロイ時に API が止まってほしくないため、新しいバージョンのタスクを追加したあとに、古いタスクを終了させたい
    • minimumHealthyPercent=100, maximumPercent=200 のような設定になる
  • Cron コンテナでは、多重起動を防ぎたいので、古いタスクを終了させてから、新しいバージョンのタスクを起動したい
    • minimumHealthyPercent=0, maximumPercent=100 になる

この観点でも、サービスを適切に分ける必要がある。

docs.aws.amazon.com

失敗例2: コンテナが死んでタスクが落ちる

これは、以前書いたこの記事と関連する失敗例である。

www.utakata.work

このようなサービスを組んでいた

  • クラスタ
    • Web API サービス
      • タスク
        • Web API コンテナ
        • Queue Worker コンテナ
      • タスク
        • Web API コンテナ
        • Queue Worker コンテナ

Queue Worker コンテナとは、Amazon SQS というキューイングサービスからメッセージを受け取って処理するサービスだ。Queue Worker は常時起動し、Amazon SQS からメッセージを受け取って処理する。

Queue Worker は複数起動していても問題ないので、失敗例1で書いたオートスケール問題は大丈夫なのだが、別の問題が発生した。

Queue Worker コンテナが処理中にコンテナごと落ちる問題が発生した。Queue Worker コンテナが落ちると、ECS によってまるっとそのタスクが終了するため、Web API コンテナもまとめて落ちてしまう。こうなると、タスクが再起動するまで API が叩けなくなったり、LB のターゲットから外れるまで一部の API リクエストが失敗したりする。

そこで、Queue Worker と Web API はサービスを分けることにした

  • クラスタ
    • Web API サービス
      • タスク
        • Web API コンテナ
      • タスク
        • Web API コンテナ
    • Worker サービス
      • Queue Worker コンテナ

今回の場合、Web API のような高いサービスレベルを要求されるものと、すぐ落ちる Queue Worker が同じサービスの中に入っていたのが良くなかったかな、と思います。

もっとシンプルに言うと、一つのサービスの中にいろいろ詰め込みすぎるのが良くないです。

まとめ

サービスはシンプルに保ち、タスクの数などの設定項目に応じて適切にサービスを分割すること。