ECSのデプロイにおける404エラー問題
ECSへのデプロイ時に、ページに必要な静的ファイル(cssやjs)が404エラーになる可能性があります。
さっと書いた図を載せておきます。
ECSでは古いバージョンのタスクと新しいバージョンのタスクが同時に起動している状態が数分続きます。これは以下のような問題を引き起こします。
- ユーザーが
/
にアクセスする - ECSのサービスが新しいTask(以下TaskA)にリクエストを流し、TaskAが
/
のHTMLを返す - ユーザーが返ってきたTaskAのHTMLで必要な、
/TaskA.js
というファイルをリクエストする - ECSのサービスが古いTask(以下TaskB)にリクエストを流す
- しかしTaskBには
/TaskA.js
が存在しないため404エラーを返す
CloudFrontによるエラーのキャッシュ
CloudFrontを利用している場合、オリジンのエラーをデフォルトで5分間キャッシュするため、さらに404エラー時間が伸びてしまう可能性があります。またエラーのキャッシュを無効にしたとしても、404エラーはデプロイ中の数分間は一定確率で発生し、根本的な解決になりません。またCDNのメリットであるDos攻撃の対策も、意味を成さなくなってしまいます。
AWSの回答は「S3とCloudFrontを使って」
この問題の対処法をAWSの技術サポートに問い合わせてみたところ、「そちらの解決方法としては、静的コンテンツをS3にアップロードしてCloudFrontで配信して欲しい」という返事をもらいました。
おそらくですが、このようなデプロイの問題は本来アプリケーション側で個別に対応するべき問題だということでしょう。アプリケーションやインフラの構成によって対応するべき範囲は違いますし、どこまで許容するべきかといった所は運用方法によって変わってきます。そのため個別に各々が対応するしかないのでしょう。
ロードバランサーであれば、スティッキーセッションを利用してユーザーは一貫性のあるオリジンの参照ができますが、現在ECSでそのようなものは提供されていない模様です。
S3へ安全なデプロイを行うために
ということで静的ファイルをS3にアップロードし、ユーザーが静的ファイルを参照する際はCloudFrontからS3を見にいくようにします。
このS3へのデプロイ時のポイントとしては、S3に静的ファイルの古いバージョンと新しいバージョンを同時に存在させておく事です。
S3へ同期
awscliのs3 sync
を使うとローカルのファイルをS3のディレクトリへ同期させることができます。同期というのは同一のファイルがあれば上書き、それ以外は新規アップロード、という形です。
これによってバージョンによってファイルの名前が違う限り安全にデプロイできます。
ライフサイクルによる解決は危険性がある
S3のライフサイクル機能を使って、削除用のタグが付いたファイルを削除するという解決方法も考えたのですが、削除タイミングによる問題がありました。
まずライフサイクルの削除のタイミングは以下のように決まってます。
参考: ライフサイクル設定の要素 - Amazon Simple Storage Service
- オブジェクト作成日時からの日数(作成日時は最終更新日時、つまりメタデータのLast Modifiedのことで、そもそもS3では作成日時を保持していない)
- ルールに指定された日数をオブジェクトの作成時間に加算し、得られた日時を翌日の午前 00:00 UTC (協定世界時) に丸める
つまり日本では午前8:59に「ライフサイクルルールの経過日数を超えているファイル」に削除タグを付けたら、1分後に削除されてしまうのです。(タグの付加ではメタデータのLast Modifiedは更新されないため)
上記がライフサイクルによる課題解決ができなかった理由です。なのでシンプルに同時に3つのバージョンをデプロイさせる方法で解決しました。