LoginSignup
14
9

More than 5 years have passed since last update.

【Elastic Beanstalk】1つの環境のみで完全なBlue-Greenデプロイを実現する方法【PHP】

Last updated at Posted at 2017-07-12

目的

  • /var/www/html からアプリケーションのソースを読み取る
  • /var/app/current -> /var/www/html にシンボリックリンクを貼って現在のバージョンを動かしている
  • /var/app/ondeck で次のバージョンのビルドを行う
  • 最終的に /var/app/ondeck/var/app/current を上書きしたい

Elastic Beanstalk において,なんとか1つの環境だけで Blue-Green デプロイができないかな,と試行錯誤した記録です。

手順

前提

  • /var/app/ondeck で既にビルドが終わっている。
  • cpおよびmvがGNU版である。

コマンド

ln -sf /var/app/ondeck /var/www/new_html && mv -Tf /var/www/new_html /var/www/html
sleep 5 && rm -rf /var/app/current
cp -al /var/app/ondeck /var/app/current
ln -sf /var/app/current /var/www/new_html && mv -Tf /var/www/new_html /var/www/html
sleep 5 && rm -rf /var/app/ondeck

ポイント

  • ディレクトリのシンボリックリンクを上書きできるln -snfアトミックな操作ではないので,ln -sfmv -Tfを組み合わせる必要がある。
  • 新しいパスにコピーするときには,ハードリンクを再帰的に作成するcp -alを使うと,アプリケーションによるディスク使用領域が一時的に2倍に膨れ上がる無駄も避けられる。
  • 読もうとするファイルが既に削除されていることを防ぐために,1リクエストの処理にかかりそうな時間ぶんあるいはその数倍だけ,rmの前にスリープを入れておくと安全。ここでは5秒と見なす。
  • オートローディングのベースになる__DIR____FILE__シンボリックリンク解決後のパスになるため,古いバージョンから新しいバージョンを部分的に読み込んでしまう不整合は発生しない。直接 /var/www/html/src/foo/bar/Klass.php などと,シンボリックリンクを含む絶対パスをハードコーディングしている場合はアウト。

検証

macOS上で実験しているので,mvの代わりにgmvcpの代わりにgcpを使っています。

確認用シェルスクリプト
#!/bin/sh

# 準備
mkdir -p /tmp/example/current /tmp/example/ondeck
echo aaaa > /tmp/example/current/data.txt
echo bbbb > /tmp/example/ondeck/data.txt
ln -s /tmp/example/current /tmp/example/html

# 監視を開始
perl -e 'while (1) { open(my $file, "/tmp/example/html/data.txt") or die("ERROR!"); print <$file> }' &
sleep 1

# 置き換えを開始
ln -sf /tmp/example/ondeck /tmp/example/new_html && gmv -Tf /tmp/example/new_html /tmp/example/html
sleep 5 && rm -rf /tmp/example/current
gcp -al /tmp/example/ondeck /tmp/example/current
ln -sf /tmp/example/current /tmp/example/new_html && gmv -Tf /tmp/example/new_html /tmp/example/html
sleep 5 && rm -rf /tmp/example/ondeck

# 後始末
kill %%
rm -rf /tmp/example

ゼロダウンタイムになっているかどうかを確認します。

スクリーンショット 2017-07-12 14.19.23.png

できました!

Elastic Beanstalk への適用

/opt/elasticbeanstalk/hooks/appdeploy/enact/01_flip.shに以下の変更を加えます。

【変更前】 /opt/elasticbeanstalk/hooks/appdeploy/enact/01_flip.sh
#!/usr/bin/env bash
#==============================================================================
# Copyright 2014 Amazon.com, Inc. or its affiliates. All Rights Reserved.
#
# Licensed under the Amazon Software License (the "License"). You may not use
# this file except in compliance with the License. A copy of the License is
# located at
#
#       https://aws.amazon.com/asl/
#
# or in the "license" file accompanying this file. This file is distributed on
# an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, express or
# implied. See the License for the specific language governing permissions
# and limitations under the License.
#==============================================================================

set -xe

EB_APP_STAGING_DIR=$(/opt/elasticbeanstalk/bin/get-config  container -k app_staging_dir)
EB_APP_DEPLOY_DIR=$(/opt/elasticbeanstalk/bin/get-config  container -k app_deploy_dir)

if [ -d $EB_APP_DEPLOY_DIR ]; then
  mv $EB_APP_DEPLOY_DIR $EB_APP_DEPLOY_DIR.old
fi

mv $EB_APP_STAGING_DIR $EB_APP_DEPLOY_DIR

nohup rm -rf $EB_APP_DEPLOY_DIR.old >/dev/null 2>&1 &
【変更後】 /opt/elasticbeanstalk/hooks/appdeploy/enact/01_flip.sh
#!/usr/bin/env bash
#==============================================================================
# Copyright 2014 Amazon.com, Inc. or its affiliates. All Rights Reserved.
#
# Licensed under the Amazon Software License (the "License"). You may not use
# this file except in compliance with the License. A copy of the License is
# located at
#
#       https://aws.amazon.com/asl/
#
# or in the "license" file accompanying this file. This file is distributed on
# an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, express or
# implied. See the License for the specific language governing permissions
# and limitations under the License.
#==============================================================================

set -xe

EB_APP_STAGING_DIR=$(/opt/elasticbeanstalk/bin/get-config  container -k app_staging_dir)
EB_APP_DEPLOY_DIR=$(/opt/elasticbeanstalk/bin/get-config  container -k app_deploy_dir)

ln -sf $EB_APP_STAGING_DIR /var/www/new_html && mv -Tf /var/www/new_html /var/www/html
if [ -d $EB_APP_DEPLOY_DIR ]; then
  sleep 5 && mv $EB_APP_DEPLOY_DIR $EB_APP_DEPLOY_DIR.old
fi

cp -al $EB_APP_STAGING_DIR $EB_APP_DEPLOY_DIR

ln -sf $EB_APP_DEPLOY_DIR /var/www/new_html && mv -Tf /var/www/new_html /var/www/html
sleep 5 && mv $EB_APP_STAGING_DIR $EB_APP_STAGING_DIR.old

nohup rm -rf $EB_APP_STAGING_DIR.old $EB_APP_DEPLOY_DIR.old >/dev/null 2>&1 &

オートスケールで反映させるためには, .ebextensions 配下にこの定義を書く必要があります。

.ebextensions/01-customize-flip.config
files:
  "/opt/elasticbeanstalk/hooks/appdeploy/enact/01_flip.sh":
    owner: root
    group: root
    mode: "000755"
    content: |
      #!/usr/bin/env bash
      #==============================================================================
      # Copyright 2014 Amazon.com, Inc. or its affiliates. All Rights Reserved.
      #
      # Licensed under the Amazon Software License (the "License"). You may not use
      # this file except in compliance with the License. A copy of the License is
      # located at
      #
      #       https://aws.amazon.com/asl/
      #
      # or in the "license" file accompanying this file. This file is distributed on
      # an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, express or
      # implied. See the License for the specific language governing permissions
      # and limitations under the License.
      #==============================================================================

      set -xe

      EB_APP_STAGING_DIR=$(/opt/elasticbeanstalk/bin/get-config  container -k app_staging_dir)
      EB_APP_DEPLOY_DIR=$(/opt/elasticbeanstalk/bin/get-config  container -k app_deploy_dir)

      ln -sf $EB_APP_STAGING_DIR /var/www/new_html && mv -Tf /var/www/new_html /var/www/html
      if [ -d $EB_APP_DEPLOY_DIR ]; then
        sleep 5 && mv $EB_APP_DEPLOY_DIR $EB_APP_DEPLOY_DIR.old
      fi

      cp -al $EB_APP_STAGING_DIR $EB_APP_DEPLOY_DIR

      ln -sf $EB_APP_DEPLOY_DIR /var/www/new_html && mv -Tf /var/www/new_html /var/www/html
      sleep 5 && mv $EB_APP_STAGING_DIR $EB_APP_STAGING_DIR.old

      nohup rm -rf $EB_APP_STAGING_DIR.old $EB_APP_DEPLOY_DIR.old >/dev/null 2>&1 &

これをリポジトリに含めた上で,デプロイ実行中に ll /var/www/html を連打して確認してみました。

スクリーンショット 2017-07-12 15.13.34.png

うまくいきました!

注意事項

PHPのようにソースコードを設置するだけでデプロイが完了するアプリケーションに関してはゼロダウンタイムですが,Railsのようにアプリケーションサーバとして永続させるタイプのものの場合,スタートアップにかかる時間が存在し,古いファイルパスの情報を保持し続けるため,依然として

  • ロードバランサのスワップ
  • CNAMEスワップ

といった処理は必要です。また,データベースのスキーマ変更が発生する場合もこの方法では不可能です。

…とはいえども,スキーマ変更の伴わないPHPアプリケーションの変更に関しては,非常に簡単に対応できるのは美味しいところだと思います。また1つPHPの長所が増えましたね(笑

14
9
3

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
14
9