【Rails + AWS Fargate】簡単なシェルスクリプトでデプロイ前後の外形監視を行い、デプロイの安定性を高めた話

はじめに

OHEYAGOの開発をしている田渕です!
OHEYAGOのwebサーバーはFargate上で動いていますが、AWS Fargateでのデプロイを行う際に、マイグレーションやデプロイが本当に完了したタイミングがわかりにくいという問題がありました!

例えば、マイグレーション用のコンテナは立っているのにマイグレーションが終わっていない状態で、webサーバーのバージョンを更新してしまった場合などには、予期せぬ障害が発生してしまいます。

なので、デプロイ前後で外形監視を行い、マイグレーションやデプロイが完了したかどうかを、外部から見た振る舞いで確かめることによって、デプロイの安定度を高めました!

要件

ecs-cliコマンドでは、コンテナの中の挙動までは確かめることができません。

ec2インスタンス上でのデプロイであれば、デプロイスクリプト上で、マイグレーションのログを確認することもでき、異常終了した場合は、そこでデプロイを中止することも容易です。

一方でfargate上のタスクではecs-cliコマンドではコンテナが立ったことまでしか確認できません。
同一のコンテナでサーバーの起動前に毎回マイグレーションを行いたくはないため、マイグレーション用のコンテナとwebサーバー用のコンテナを分離しています。
このような運用では、スクリプトの終了コードなども拾うにも一手間かかりますし、マイグレーションが本当に終わったかを確かめることは意外に難しいです。(厳密に確かめたかったらcloudwatch logsまで見に行く必要があります)

このような問題をはっきり見える形で解決し、安心してデプロイができる体制を作る必要がありました。

発想

バージョン監視用のページを作成し、そのページから得られるバージョンの変化を監視します。(healthcheckと似たイメージ)
実際に動いているコンテナのバージョンを直接知ることができます。

Rails側の実装

module API
  class VersionsController < API::ApplicationController
    def show
      render json: {
        container_version: ENV.fetch('RELEASE_VERSION', 'UNKNOWN'),
        migration_version: ActiveRecord::SchemaMigration.last.version
      }
    end
  end
end

上記のように、コンテナのリリースバージョンとマイグレーションのバージョンを取得するだけのアクションを作成しました。
(上の実装では省略していますが、アクセス権限には気をつけてください)

マイグレーションの外形監視を行うシェルスクリプト

ecs-cli ... up

TARGET_VERSION=`find ./db/migrate -name "*.rb" | sort | tail -1 | grep -E -o "[0-9]+"`
while true; do
  CURRENT_VERSION=`curl -s https://${CURL_TARGET_HOST}/api/version | jq ".migration_version"`
  echo "current migration_version is ${CURRENT_VERSION}"
  echo "target migration_version is ${TARGET_VERSION}"

  if [ "${CURRENT_VERSION}" = "${TARGET_VERSION}" ]; then
    echo "db reached target version"
    exit 0
  fi
  sleep 5
done;

curlで取ってきたマイグレーションバージョン(DBのレコードから取得されたもの)が、最新のマイグレーションファイルのprefixと一致しているかを検証しています。
このスクリプトでexit 0が返れば、確実にDBのバージョンが書き換わっていることがわかるので、webサーバーのコンテナのアップデートに進むことができます。

デプロイの外形監視を行うシェルスクリプト

ecs-cli ... up

while true; do
  CURRENT_VERSION=`curl -s https://${CURL_TARGET_HOST}/api/version | jq -r ".container_version"`
  echo "current container_version is ${CURRENT_VERSION}"
  echo "target container_version is ${RELEASE_VERSION}"

  if [ "${CURRENT_VERSION}" = "${RELEASE_VERSION}" ]; then
    echo "container reached target version"
    exit 0
  fi
  sleep 5
done;

環境変数として設定された任意のリリースバージョンを、ecs用のdocker-compose.ymlファイルにも流し込んでいます。
このスクリプトでexit 0が返れば、直前でecs-cli upしたコンテナが実際にリリースされたことが確実にわかります。

感想

これらの工夫はサービスのリリース前には行っていましたが、おかげでデプロイ周りで変なことが起きることもなく今まで開発できています。
デプロイがコケない安心感があると、デプロイ回数をどんどん増やし、より早く顧客に価値を届けることができ、バグや障害の可能性も減らすことができます!

また、自分で書いたスクリプトでデプロイを確かめているので、デプロイに対する心理的障壁も減るという副次的効果も得られました

今後もDevOps的な改善を続けていき、より良いプロダクトを作っていきます!
これからもよろしくお願いします。