CodeDeployでELBヘルスチェックによるローリングアップデート

2017.02.01

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

渡辺です。

AWS CodeDeployでは複数サーバにアプリケーションをデプロイできます。 この時、どの程度並列にデプロイするかを決めるのがデプロイ設定です。

デプロイ設定は、AllAtOnceHalfAtATimeOneAtATimeから選択します。 AllAtOnceは全サーバを並列にデプロイします。 一方、OneAtATimeは並列にデプロイを行わず、ひとつのサーバのデプロイ完了を待って次のサーバのデプロイを行います。 HalfAtATimeは半分ずつのグループにわけ、各グループが成功した後に残りのグループをデプロイします。

OneAtATimeを使えばローリングアップデートとなるわけですが、実際にはデプロイ後にELBのヘルスチェックが通るまでタイムラグがある点に注意しなければなりません。

リリース時のサービス断を発生させない

デプロイ(リリース)時のサービス断を発生させない有名な方法は、Blue/Greenデプロイメントです。 Blue/Greenデプロイメントでは、デプロイ環境を2セット用意します。 リリース(デプロイ)時には、利用していない環境にデプロイを行い、ELBの向き先をデプロイした新しいバージョンの環境に切り替えるなどして実現します。 しかし、Blue/Greenデプロイメントは手間がかかります。

もうひとつのサービス断を発生させない手法はローリングアップデートです。 ELBなどでサーバーが冗長化されているならば、1台づつ(もしくは半分づつ)のデプロイ(リリース)を行い、サービス断を回避する手法です。 一時的に新旧のアプリケーションが混在するタイミングが出来てしまいますが、そのデメリットが小さいならば有効な手法です。

今日はCodeDeployで確実なローリングアップデートを行う方法を紹介します。

CodeDeploy

ELBのヘルスチェックが通るまでデプロイを完了としない

CodeDeployでのローリングアップデートは、デプロイ設定でHalfAtATimeまたはOneAtATimeを設定すれば良さそうです。 しかし、実際にはELBからのヘルスチェックが成功する前にデプロイが完了してしまいます。 それではエンドユーザからの視点ではサービスが一時的に利用できないことになります。 ELBのヘルスチェックが通までデプロイ完了を待機させなければなりません。

ValidateServiceフックでデプロイ完了まで待機する

通常、CodeDeployでデプロイを行う時、次のような手順で行います。

  1. ミドルウェアの停止(ApplicationStop
  2. アプリケーションのダウンロードと配置(DownloadBundle
  3. ミドルウェアの起動(ApplicationStart
  4. サービスの起動確認(ValidateService

ValidateServiceで、ELBのヘルスチェックが成功するまで待機させます。

validate.sh

次のスクリプトは、ValidateServiceのサンプルです。

#!/bin/sh

ELB_NAME="{{ elb_name }}"
INSTANCE_ID=$(curl -s http://169.254.169.254/latest/meta-data/instance-id)

while :; do
  stat=$(aws elb describe-instance-health --region ap-northeast-1 --load-balancer-name $ELB_NAME --instances $INSTANCE_ID --query InstanceStates[0].State)
  if [ `echo $stat | grep 'InService'` ] ; then
     exit 0
  fi
  sleep 5
done

実行中のインスタンスIDを取得し、aws cliでELBにおけるステータスを取得しています。

appspec.yml

ValidateServiceを指定したappspec.ymlです(抜粋)。

---
hooks:
  ApplicationStop:
    - location: hooks/stop.sh
      timeout: 30
      runas: root
  ApplicationStart:
    - location: hooks/start.sh
      timeout: 15
      runas: root
  ValidateService:
    - location: hooks/validate.sh
      timeout: 300 # 5min
      runas: root

タイムアウトはELBのヘルスチェックとアプリケーションの起動時間との兼ね合いで調整してください。

まとめ

ValidateServiceでELBのヘルスチェックでInServiceになったことを確認するまで待機すれば、サービス断は発生しません。 デプロイ時間は増えますが、確実なローリングアップデートが必要な場合は参考にしてください。