インスタンスを使い捨てない。WebアプリのBule/Green風デプロイ（for PHP）

こんにちは。AWS歴３年生の人です。最近、アプリケーションのデプロイやAWSのCI/CDサービスについて勉強しています。その中でAWS CodeDeploy＋EC2＋PHPのインプレースデプロイをBule/Green風にしたのでご紹介します。(あくまで”風”です)



**注意**

- 本記事は2020年8月28日時点でのバージョンで確認しました。

- 記載のコードにつきましては参考となりますので、利用時の不具合について一切の責任を負いません。

  

## デプロイタイプについて軽くおさらい

- インプレース（In-place）デプロイ

  - 稼働中のインスタンス上に、新しいアプリケーションをインストールします。デプロイの前後で同じインスタンスが利用されます。
  - デプロイ中のサービス影響を回避するために、ロードバランサから切り離して１台ずつデプロイする構成をとることも可能です。

- ブルーグリーン（Blue/Green）デプロイ

  - 稼動中とは別に新しくインスタンスを作成し、アプリケーションをインストールします。新旧インスタンスの切り替えはロードバランサでトラフィックをコントロールしサービス投入をします。
  - デプロイの前後で異なるインスタンスを利用します。旧インスタンスは破棄するか、障害時のロールバック用として待機、利用ができます。
  
  

アプリケーションデプロイの勉強はこちらの資料を参考にしました。説明図が分かりやすいです。

[AWS Black Belt Online Seminar AWS Code Series Part 2](https://d1.awsstatic.com/webinars/jp/pdf/services/20170628_AWS-BlackBelt_CodeSeries_CodeDeploy_and_CodePipeline.pdf )



## Blue/Greenではなく、In-placeで”Blue/Green風”にした経緯

Blue/Greenのメリットを知っている方は「なぜ、最初からBlue/Greenデプロイを選択しないの？」と、思うかもしれません。

Blue/Greenデプロイを実運用にのせるためには「インフラ（インスタンス）」と「アプリケーション」の２つを、適切なバージョンでリリースするので、インフラ運用チームとアプリ開発チームが一緒に運用設計を進めていくのがベターだと感じています。

しかし実際のプロジェクトでは、運用要件、性能要件、システムの難易度、体制と役割、コストなどさまざまな制約、条件があります。その中で、”必ずしも最新のアーキテクチャやテクノロジーがいつもベストな選択とはかぎらない”　ということが、気づきになりその過程で、インプレースデプロイでBlue/Green風という手法を選択しました。

また、PHPのようなスクリプト言語でSSHを利用しないデプロイの事例紹介や記事があまりなく苦労したので、どなたかの参考になれば幸いです。



## どこがBule/Green風なのか

　1. サービス稼働用とデプロイ用、２つのアプリケーションバージョンを持つ

   インスタンスの代わりにアプリケーションの実体を格納するディレクトリをblueとgreenの２つ用意します。サービス用パスはシンボリックリンクにしてblue（もしくはgreen）の実体に向けておきます。デプロイ用パスもシンボリックリンクにしてgreen（もしくはblue）の実体に向けておきます。

　2. 旧から新へきりかえる

   稼働中のシンボリック先をblueとしたとき、新しいアプリケーションはgreenにデプロイされます。その後、シンボリックリンクを切り替えることで、新しいアプリケーションを瞬時にリリースします。

　3. 一つ前のバージョンに戻すことも可能

   新しいバージョンで問題が発生したときは、再度シンボリックリンクを切りかえることでロールバックができます

   （注：緊急避難的に手動でシンボリックリンクを切りかえてロールバックもできますが、デプロイツールを使っている場合はその後のデプロイ整合性を合わせるために、手動ではなくデプロイツールを使ってロールバックすることをおすすめします）
   
   


![image1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/611580/eb52c3c4-fc1d-97d9-c41d-e22a631ec10f.png)



## AWS CodeDeploy＋インプレースの課題も併せて回避できる

SSHを使わずに済むので、デプロイツールはAWS CodeDeployのインプレースデプロイを選択したのですが、以下のような課題がでてきました。



- アプリケーションリビジョンの展開中、ファイル参照ができない時間が発生する（AWS CodeDeployの仕様）

  - 特にアプリケーションリビジョンのサイズが大きすぎる場合はその時間が長くなります
  - １台しかインスタンスが無い場合、その間はアプリケーションの実行はできません


- ロードバランサでサービスから切り離してデプロイもできるが、一時的に縮退構成になる

  - リリースの時間帯やタイミングの考慮が必要
    - 毎回深夜帯の更新運用は避けたい

  

Bule/Green風にすることで、AWS CodeDeploy＋インプレースの持つこれらの課題も回避することができます。

- インスタンス１台でもデプロイ時の切り替えによる影響は、ほぼ一瞬
- サービス中のインスタンスを切り離す必要がない
  - 全台ほぼ同時にデプロイができるので、時間も短くなります





## 試したAWS構成例

AWS CodeDeployとEC2で確認をしました。台数は１台から可能です。

![image2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/611580/e614a8f5-42f6-8d82-0242-810aa6484cde.png)



## AWS CodeDeployの設定

押さえておく設定だけ挙げておきます。

| 設定名         | 値           | 補足                                                         |
| -------------- | ------------ | ------------------------------------------------------------ |
| デプロイタイプ | インプレース |                                                              |
| デプロイ設定   | AllAtOnce    | 全インスタンス同時にデプロイします                           |
| Load balancer  | OFF          | ロードバランシングは無効にしておきます                       |
| ロールバック   | 無効にする   | AWS CodeDeployのロールバックはデプロイ中にエラーが発生したときに一つ前のリビジョンに自動で戻してくれる便利な機能ですが無効にしておきます。今回のBlue/Green風ではサービス投入（切り替え）のコントロールはスクリプトで行います。 |



## AppSpecの設定

ポイント

- filesセクションのdestinationの値をデプロイ用のパスにしておきます
- フックセクション（hooks）で実行するスクリプトを指定します

そのほかの値は参考となります。

```yaml
version: 0.0
os: linux
files:
   - source: /
     destination: /var/www_deploy/
     overwrite: true
permissions:
  - object: /var/www_deploy
    owner: hoge
    group: fuga
    mode: 744
    type:
      - file
  - object: /var/www_deploy
    owner: hoge
    group: fuga
    mode: 755
    type:
      - directory
hooks:
   AfterInstall:
     - location: hooks/AfterInstall.sh
       timeout: 180
       owner: hoge
   ValidateService:
     - location: hooks/ValidateService.sh
       timeout: 180
       owner: hoge
```



## AppSpecのフックセクション

フックセクションではデプロイの進行に応じてイベント実行するスクリプトを指定することができます。

| セクション名    | イベント説明                                                 | Blue/Green風のスクリプト内容                                 |
| --------------- | ------------------------------------------------------------ | ------------------------------------------------------------ |
| AfterInstall    | アプリケーションファイルの展開後、アプリケーションの設定やファイル権限の変更など使用します。 | AfterInstall.shでは、サービスパスとデプロイパスのシンボリックリンクの入れ替え処理を行います。 |
| ValidateService | 最後にデプロイが正常に完了したことを確認するために使用されます。 | ValidateService.shではチェック処理を行います。問題が発生した場合はシンボリックリンクを戻すなど必要な処理を実装することもできます。 |

使っているセクションだけ挙げています。その他の説明についてはこちらをご確認ください。

[AWS > ドキュメント > AWS CodeDeploy >ユーザーガイド
AWS AppSpec 「フック」セクション](https://docs.aws.amazon.com/ja_jp/codedeploy/latest/userguide/reference-appspec-file-structure-hooks.html )



## AfterInstall.sh（シンボリックリンクの切り替え）の例

最低限ですが、このようなスクリプトでリンク先のblueとgreenを切りかえることができます。

```sh
#!/bin/bash

SERVICE_PATH='/var/www'
SERVICE_DIR=`readlink ${SERVICE_PATH}`
DEPLOY_PATH='/var/www_deploy'
DEPLOY_DIR=`readlink ${DEPLOY_PATH}`

`ln -nfs ${DEPLOY_DIR} ${SERVICE_PATH}`
`ln -nfs ${SERVICE_DIR} ${DEPLOY_PATH}`
```



## 実際に試して感じたこと

- 軽微なアプリケーションの更新作業などには向いています

  - 大規模改修や投入前検証で高い品質担保を毎回求められる場合は、メンテナンス計画や他の手法も検討したほうがよいかもしれません。

- サービス切り替えやロールバックのスクリプト開発工数が増える

  - アプリケーションの配置はAWS CodeDeployがしてくれますが、その他の処理はhooksスクリプトで行います。そのため、実運用ではこれらのスクリプトの作りこみ、エラーハンドリングの実装など、品質を保持したデプロイを実現するための工数は多くなります。

- 逆に、 柔軟な処理もスクリプトに実装できる（またそのスキルが必要）

  - AWS CodeBuildも同じく、ビルド環境はAWSが用意してくれますがビルドの設計と開発は自分たちで実装する必要があります。そのため、今後はデプロイやビルドを設計して実装するスキルが大事になってくるのかな、と思いました。

- デプロイ全体の進行管理やログはAWSのサービスにお任せ

  - ログ状況やエラーはAWSコンソールで確認ができるので、運用者やデプロイのデバッグは楽になります。
  
  

以上です。最後まで読んでいただき、ありがとうございました。