AWS CodeDeployを使ってPHPアプリケーションをデプロイしてみた


AWS CodeDeployを使って、お手製デプロイシェルやJenkinsおじさんから卒業しよう！というお話です。
今回はPHPアプリケーションをS3経由でデプロイしてみようと思います。

## AWS CodeDeploy is 何
> **Q: AWS CodeDeploy とは何ですか?**
> AWS CodeDeploy は、Amazon EC2 インスタンス、およびオンプレミスで稼働するインスタンスを含む、さまざまなインスタンスへのコードのデプロイを自動化するサービスです。AWS CodeDeploy を使用すると、お客様はアプリケーションの複雑なアップデート処理、デプロイ中のダウンタイムの回避、新規機能の迅速なリリースが容易になります。AWS CodeDeploy を使用すると、エラーを起こしやすい手動操作やインフラストラクチャのサービススケールの必要性を排除しながら、デプロイの自動化を行うことが可能となり、1 つのインスタンスへのデプロイや、数千のインスタンスへのデプロイも容易に行うことができます。

AWS CodeDeployの[よくある質問](https://aws.amazon.com/jp/codedeploy/faqs/)より引用

## メリット/デメリット

### メリット
* フルマネージド
	* AWSのフルマネージドサービスなのでdeployサーバ（Jenkins等）をたてる必要がない。
* 安い
	* EC2インスタンスへのデプロイは無料（オンプレへのデプロイ時にのみ微量な料金が発生。詳細は[コチラ](https://aws.amazon.com/jp/codedeploy/pricing/)）
	* deployサーバ（Jenkins等）のインスタンス代もかからない。
* 多様なデプロイ方法
	* 複数のデプロイ対象に対して、一台ずつ、一気に全台、半分だけ、などのデプロイ速度調節が可能
	* blue/greenデプロイもサポート
* 簡単
	* デプロイ先EC2は任意のタグで自動的に判別される
	* ローカルのデプロイ対象ディレクトリを指定してaws-cliを実行するだけで、zipで固めてS3へpushしてくれる
	* デプロイ後の処理(パーミッション変更, Apacheのgraceful 等)はymlに書いてデプロイ対象ディレクトリに置いとくだけ

などなど。

### デメリット
* 新しいやりかたを覚える手間くらい？

## Deployの大まかな流れ
![3b41bde3-3e09-ed85-157d-313c3727031d.png](https://qiita-image-store.s3.amazonaws.com/0/38574/e38fbf58-332c-6172-f79e-65c1f9beb4a3.png "3b41bde3-3e09-ed85-157d-313c3727031d.png")

### Step1: Build
builderサーバでbuildを実行した後、aws-cli経由でソースコードzipで固め、s3にpushします。

### Step2: Deploy
AWSコンソールからAWS CodeDeployを実行すると、S3にあるdeploy対象のソースコードがdeploy対象のサーバ群にdeployされ、結果が通知されます。

今回は教材として[concrete5](https://concrete5-japan.org/about/)というCMSをデプロイしてみようと思います。
※本当はconcrete5でここまでする必要はありません。

## 環境準備
### AWS CodeDeploy
##### IAM Role作成
AWS CodeDeployで利用する為のIAM Roleを作成します。

アクセス許可する管理ポリシーに `AWSCodeDeployRole` をアタッチします。
さらに、信頼されたエンティティに `codedeploy.ap-northeast-1.amazonaws.com` を追加（東京リージョンで利用する場合）します。

```yml:信頼関係
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": {
        "Service": "codedeploy.ap-northeast-1.amazonaws.com"
      },
      "Action": "sts:AssumeRole"
    }
  ]
}
```

##### AWS CodeDeploy アプリケーション作成
AWSコンソールより、AWS CodeDeploy アプリケーションを作成します。
下記の情報を入力し、新規アプリケーションを作成します。

|設定項目 |設定値 |
|-------|------|
|アプリケーション名|任意の名前|
|デプロイグループ名|任意の名前|
|デプロイタイプ|今回は `インプレースデプロイ` を選択|
|インスタンスの追加|デプロイ先インスタンスを絞り込むためのタグを指定する <br> 例：Role=webserver, Env=development |
|デプロイ設定|今回は `CodeDeployDefult.OneAtAtime` を選択|
|詳細設定|今回はデフォルトのまま|
|サービスロール|**IAM Role作成** で作ったAWS CodeDeployで利用する為のロールを選択|

### builder(EC2)
##### IAM Role作成
以下のIAMRoleを作成し、EC2インスタンス作成時に割り当ておきます。

アクセス許可する管理ポリシーに `AWSCodeDeployDeployerAccess` をアタッチします。

##### aws-cli更新
aws-cliを最新版にアップデートしてきます。

##### concrete5用ミドルウェア設定
concrete5で利用するミドルウェアをセットアップしておきます ＞ [concrete5の使用条件](https://concrete5-japan.org/about/requirement/)
詳細は割愛しますが、GitHubに Ansible playbook を置いておくので使いたい方はどうぞ ＞ [github](https://github.com/kazumax55/ansible-aws-concrete5.git)

### deploy先(EC2)
##### IAM Role作成
空のIAMRoleを作成し、EC2インスタンス作成時に割り当ておきます。

##### aws-cli更新
aws-cliを最新版にアップデートしておきます。

##### concrete5用ミドルウェア設定
concrete5で利用するミドルウェアをセットアップしておきます ＞ [concrete5の使用条件](https://concrete5-japan.org/about/requirement/)
詳細は割愛しますが、GitHubに Ansible playbook を置いておくので使いたい方はどうぞ ＞ [github](https://github.com/kazumax55/ansible-aws-concrete5.git)

##### AWS CodeDeploy Agentインストール
[AWS CodeDeploy Agentをインストール](http://docs.aws.amazon.com/ja_jp/codedeploy/latest/userguide/how-to-run-agent-install.html)します

```bash:
### インストール
$ sudo yum install -y wget
$ sudo su - ec2-user
$ wget https://aws-codedeploy-ap-northeast-1.s3.amazonaws.com/latest/install
$ chmod +x ./install
$ sudo ./install auto

### 確認
$ yum info codedeploy-agent
Loaded plugins: priorities, update-motd, upgrade-helper
Installed Packages
Name        : codedeploy-agent
Arch        : noarch
Version     : 1.0
Release     : 1.1067
Size        : 11 M
Repo        : installed
From repo   : /codedeploy-agent-1.0-1.1067.noarch.tmp-20170130-3372-owpn7q
Summary     : Provides the required files for CodeDeploy agent to run in EC2 instances
License     : Amazon.com Internal
Description : CodeDeploy instance agent is responsible for doing the actual work of deploying software
            : on an EC2 instance.

$ sudo service codedeploy-agent status
The AWS CodeDeploy agent is running as PID 2394
```

### S3
ソースコードpush先のS3バケットを作成しておきます。
各IAMロールにアクセス権を付与するバケットポリシーを作成します。

```json:バケットポリシー
{
	"Version": "2012-10-17",
	"Statement": [
		{
			"Effect": "Allow",
			"Principal": {
				"AWS": [
					"AWS CodeDeploy用ロールARN",
					"builder用IAMロールARN",
					"deploy先用IAMロールARN"
				]
			},
			"Action": "s3:ListBucket",
			"Resource": "arn:aws:s3:::deploy用S3バケット名"
		},
		{
			"Effect": "Allow",
			"Principal": {
				"AWS": [
					"AWS CodeDeploy用ロールARN",
					"builder用IAMロールARN",
					"deploy先用IAMロールARN"
				]
			},
			"Action": [
				"s3:GetObject",
				"s3:PutObject",
				"s3:DeleteObject"
			],
			"Resource": "arn:aws:s3:::deploy用S3バケット名/*"
		}
	]
}
```

# Deploy処理
## Step1:Deploy準備(build)
##### Step1-1:build処理
builder上にソースコードをcheckoutし、composer実行

```bash:
$ git clone -b master https://github.com/concrete5/concrete5.git
$ cd concrete5
$ composer install
```

##### Step1-2:AppSpecの作成
AppSpec is 何？ 
> **Q: AppSpec ファイルとは何ですか?** 
> AppSpec ファイルとは、コピーするファイルおよび実行するスクリプトを指定する設定ファイルです。AppSpec ファイルは YAML フォーマットを使用しており、リビジョンのルートディレクトリに含める必要があります。

AWS CodeDeployの[よくある質問](https://aws.amazon.com/jp/codedeploy/faqs/#concepts)より引用

要するに、Deploy処理実行時にサーバ側で処理する内容を定義するYAMLです。
そんなわけで、ソースコードのルートディレクトリ直下に、AppSpecとhooksスクリプトを追加します。

今回のAppSpecでは、

* ソースのインストール先を指定
* ソースコード配下のパーミッションを再帰的にapache:apacheに変更
* ソースインストール後にhooksスクリプト経由でApacheのgracefulを実行

のような処理を定義します。

AppSpecのマニュアルはこちら ＞ [AWS CodeDeploy AppSpec File Reference](http://docs.aws.amazon.com/ja_jp/codedeploy/latest/userguide/app-spec-ref.html)


```bash:appspec.yml
version: 0.0
os: linux
files:
  - source: /
    destination: /var/www/example.com/htdocs/
permissions:
  - object: /var/www/example.com/htdocs/
    pattern: "**"
    owner: apache
    group: apache
hooks:
  AfterInstall:
    - location: hooks/apache_graceful.sh
      timeout: 15
      runas: root
```


```bash:hooks/apache_graceful.sh
#!/bin/sh
service httpd graceful
```

hooksスクリプトにはあらかじめ実行権限を与えておくこと。
`chmod +x hooks/*`

以後、デプロイ処理内容に変更がなければこのStepは初回のみでOKです。


##### Step1-3:build結果をS3にpush
`aws deploy push` コマンドでbuild結果をzipで固めてS3にpushします。

後で以前のバージョンにデプロイし直す事を考慮すると、zipファイル名は他と被らない様にするのが望ましい（ `タイムスタンプ` や `commit id` を付与する 等）です。

```bash:pushの例(東京リージョン)
$ aws deploy push \
--region ap-northeast-1 \
--application-name CodeDeployアプリケーション名 \
--s3-location s3://S3バケット名/zipファイル名.zip \
--source ソースディレクトリ
```
pushに成功すると `eTag` が付与されます。


## Step2:Deploy処理
##### Step2-1:Deploy実行
AWSコンソールの AWS CodeDeploy 画面より、準備段階で作成したアプリケーション、デプロイグループを選択し、 `新しいリビジョンのデプロイ` を選択します。
![d0faa7d3-ec2c-5189-c65b-2797a9aa9fc2-1.png](https://qiita-image-store.s3.amazonaws.com/0/38574/50a4d910-1dfd-89ca-254e-0f41ca125b1f.png "d0faa7d3-ec2c-5189-c65b-2797a9aa9fc2-1.png")


デプロイの作成画面で下記の内容を入力し、

|設定項目 |設定値 |
|-------|------|
|アプリケーション|デプロイ対象のアプリケーション|
|デプロイグループ|デプロイ対象のデプロイグループ|
|リビジョンタイプ|`アプリケーションは Amazon S3 に格納されています` を選択|
|リビジョンの場所|Step1-3でpushした内容が選択肢にあればそれを選択 <br> なければ `s3://バケット名/folder/zipファイル名` を入力|
|デプロイメントの説明|任意入力|
|デプロイ設定|用途に合わせて選択|

デプロイボタンをポチります！

![057b554f-6c7a-2924-5cbe-3d09f02299fe.png](https://qiita-image-store.s3.amazonaws.com/0/38574/8f5180a7-0cf6-71d5-b49d-245547f14d22.png "057b554f-6c7a-2924-5cbe-3d09f02299fe.png")


プログレスバーが緑色だったらデプロイ成功です！

もしデプロイに失敗したら、deploy先インスタンスにある `/var/log/aws/codedeploy-agent/codedeploy-agent.log` の内容を確認しましょう。

##### Step2-2:確認

ブラウザからアクセスしてみると、、、
![a62d37bc-7e2c-14db-0f66-5bb41ec1b08c.png](https://qiita-image-store.s3.amazonaws.com/0/38574/6fd48922-0bb4-1714-55b0-995295b6fd83.png "a62d37bc-7e2c-14db-0f66-5bb41ec1b08c.png")


無事、concrete5のインストール画面が表示されました！

## まとめ
AWS CodeDeployは初期設定が少々複雑ですが、それを乗り越えれば大きなメリットが待っています！！

あと、PHP限定っぽいタイトルを付けていますが他の言語でもおおまかな流れは一緒なので応用が効くはずです。

今度はblue/greenデプロイを試してみようと思います。

## 参考サイト
[AWS再入門 AWS CodeDeploy 編](http://dev.classmethod.jp/cloud/aws/cm-advent-calendar-2015-aws-re-entering-codedeploy/)
[AWS CodeDeploy AppSpec File Reference](http://docs.aws.amazon.com/ja_jp/codedeploy/latest/userguide/app-spec-ref.html)
[CodeDeployでディレクトリのパーミッションを設定する](http://dev.classmethod.jp/cloud/aws/set-permissions-todirectory-at-codedeploy-appspecyml/)
[AWS CodeDeploy の AppSpec を読み解く](http://dev.classmethod.jp/cloud/aws/code-deploy-appspec/)