AWS CodeDeploy を使って Laravel アプリをデプロイしてみた

[Laravel Advent Calendar 2014](http://qiita.com/advent-calendar/2014/laravel) 20日目担当の @kurikazu です。よろしくお願いします。

# はじめに
先月の [AWS re:Invent 2014](https://reinvent.awsevents.com/) で、[AWS CodeDeploy](http://aws.amazon.com/jp/codedeploy/) が発表されました。AWS CodeDeploy は EC2 インスタンスに対して Amazon S3 のバケットまたは GitHub のリポジトリにあるソースをデプロイしてくれるサービスです。
詳しい説明や上記リンクや他の記事を参照頂ければと思いますが、今回は GitHub リポジトリから EC2 に Laravel アプリケーションをデプロイするための方法について簡単にまとめたいと思います。
なおGitHubには既にLaravelアプリケーションがコミットされている前提で話を進めます。

作業の順番としては以下の通りです。
1. IAM Roleの設定
2. CodeDeploy Agent のインストール
3. AppSpec の設定
4. インスタンスを CodeDeploy でデプロイできるようにする
5. デプロイ実行

# 1. IAM Roleの設定
CodeDeploy を使うためには、以下の2つの IAM ロールが必要になります。
a. EC2 インスタンスに割り当てる IAM Role
b. CodeDeploy が使う IAM Role

## a. EC2 インスタンスに割り当てる IAM Role
デプロイ対象の EC2 インスタンスには CodeDeploy Agent を入れる必要があるのですが、CodeDeploy Agent は Amazon S3 からコピーしてインストールするという方式を取ります。デプロイ対象の EC2 インスタンスには、s3:Get* と s3:List* の実行が許可されている IAM Role を指定しておいてください。
（個人的には別のインストール方法も用意して欲しいです。理由は下記参照。）

```
ロールを既存のインスタンスに割り当てることはできません。ロールは新しいインスタンスを起動するときのみ指定できます。
参照：Amazon EC2 の IAM ロール
http://docs.aws.amazon.com/ja_jp/AWSEC2/latest/UserGuide/iam-roles-for-amazon-ec2.html
```

## b. CodeDeploy が使う IAM Role
CodeDeploy から EC2 や AutoScaling などの情報を参照するための IAM Role も必要になります。

AWS の Management Colsole から IAM を選択し Role を作成します。
[ドキュメント](http://docs.aws.amazon.com/codedeploy/latest/userguide/how-to-create-service-role.html)を参考に、下記のようなJSONを設定します。

```json:Role.json
{
    "Statement": [
        {
            "Action": [
                "ec2:Describe*"
            ],
            "Effect": "Allow",
            "Resource": [
                "*"
            ]
        },
        {
            "Action": [
                "autoscaling:CompleteLifecycleAction",
                "autoscaling:DeleteLifecycleHook",
                "autoscaling:DescribeLifecycleHooks",
                "autoscaling:DescribeAutoScalingGroups",
                "autoscaling:PutLifecycleHook",
                "autoscaling:RecordLifecycleActionHeartbeat"
            ],
            "Effect": "Allow",
            "Resource": [
                "*"
            ]
        }
    ]
}
```

Trusted Entities もドキュメントを参考に下記のような設定をしてください。

```json:TrustedEntities
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "",
      "Effect": "Allow",
      "Principal": {
        "Service": [
            "codedeploy.us-east-1.amazonaws.com",
            "codedeploy.us-west-2.amazonaws.com"
        ]
      },
      "Action": "sts:AssumeRole"
    }
  ]
}
```

# 2. CodeDeploy Agent のインストール
次にデプロイ先のEC2インスタンスに  CodeDeploy Agent をインストールします。
上記 1 の a が満たされていないとここでエラーになります。

```shell-session
sudo yum update
sudo yum install aws-cli
cd /home/ec2-user
aws s3 cp s3://aws-codedeploy-us-east-1/latest/install . --region us-east-1
chmod +x ./install
sudo ./install auto
```

# 3. AppSpec の設定
CodeDeploy ではデプロイ先や事前/事後処理の定義を **appspec.yml** というファイルに指定します。
Laravel ではこのファイルをプロジェクトディレクトリの直下に置きます。

```
laravel
　├ app
　├ bootstrap
　├ public
　├ appspec.yml
　：
```

以下は /home/ec2-user/laraveltest/ 配下に、gitリポジトリ配下のファイルをすべてデプロイする例です。デプロイ前にApacheを停止、デプロイ後にstorageディレクトリの権限を777に変更し、Apacheを起動しています。

```yaml:appspec.yml
version: 0.0
os: linux
files:
- source: /
destination: /home/ec2-user/laraveltest/
hooks:
ApplicationStop:
- location: scripts/stop_server
timeout: 300
runas: root
AfterInstall:
- location: scripts/setpermission.sh
timeout: 300
runas: root
ApplicationStart:
- location: scripts/start_server
timeout: 300
runas: root
```

```shell-session:stop_server
#!/bin/bash
isExistApp = `pgrep httpd`
if [[ -n  $isExistApp ]]; then
    service httpd stop        
fi
```

```shell-session:setpermission.sh
#!/bin/bash
chmod -R 777 /home/ec2-user/laraveltest/app/storage
```

```shell-session:start_server
#!/bin/bash
service httpd start
```

デプロイの各段階（ApplicationStop, BeforeInstall, AfterInstall, ApplicationStart, ValidateService）はフックすることができます。Laravel であれば下記のような処理を挟む事でデプロイ以外の諸々の処理も自動で行うことができます。

| ライフサイクル | 処理例 |
|:-----------|:-----------|
| ApplicationStop  | Webサーバ停止など |
| BeforeInstall    | DB のバックアップ、デプロイ済ソースのクリーンなど |
| AfterInstall     | storage のパーミッション変更、DB の migrate 処理など |
| ApplicationStart | Webサーバ開始など |

WEBサーバが複数台ある場合は、各サーバに同じ処理が行われます。DB の migrate など、全サーバで実施する必要の無いものは、デプロイのグループを分けたり、シェルスクリプト内で特定のサーバの場合のみ実行されるようにすれば良いと思います。

AppSpec の詳しい説明はドキュメントを参照してください。

# 4. インスタンスを CodeDeploy でデプロイできるようにする

ここまで設定できたら、CodeDeploy に登録をします。[CodeDeploy の Console 画面](https://console.aws.amazon.com/codedeploy/)にアクセスし、Create New Application をクリックします。

![CodeDeploy1.png](https://qiita-image-store.s3.amazonaws.com/0/32146/ea608087-3978-3f6b-5f8d-be13f12b216c.png)

Application Name と Deployment Group Name には任意の名前を設定します。

![cd1.png](https://qiita-image-store.s3.amazonaws.com/0/32146/d32d86e2-2452-f421-d3a0-f833b775fa62.png)

デプロイ対象の EC2 インスタンスを指定します。

![cd2.png](https://qiita-image-store.s3.amazonaws.com/0/32146/c8d2f773-d50b-b2d0-d1a1-1ba46ab014f3.png)

デプロイ実行方法と成功/失敗の扱い方について設定します。Service Role には上記1のbで設定した Role を指定します。

![cd3.png](https://qiita-image-store.s3.amazonaws.com/0/32146/ea518012-b7ea-3571-c753-d5ff147bea18.png)

# 5. デプロイ実行

いよいよデプロイ実行です。「Deploy New Revision」をクリックすると下記の画面が表示されます。ここで「Revision Type」を「My application is stored in GitHub」に指定し、「Repository Name」に GitHub のリポジトリ名を、「Commit ID」にデプロイ対象のコミット ID を指定します。

![github.png](https://qiita-image-store.s3.amazonaws.com/0/32146/cdb6a569-9df1-4aa4-155d-fbb55e38ab50.png)

「Deploy Now」をクリックすると、デプロイが始まります。成功すると下記のような画面が表示されます。

![cd5.png](https://qiita-image-store.s3.amazonaws.com/0/32146/f7c11853-094c-3acf-1f37-43e975c6c05b.png)

エラーになった場合でも下記の画面でどこでエラーになったか確認できるのでわかりやすいです。

![cd6.png](https://qiita-image-store.s3.amazonaws.com/0/32146/36538336-46dc-5bc3-278d-e2161be41100.png)

2回目のデプロイからは、この5番の操作だけを行えばOKです。GUI で操作ができるのでとても簡単ですね（もちろん CLI での操作も可能です）。

# おわりに

簡単ではありますが、AWS CodeDeploy で Laravel プロジェクトをデプロイする方法について説明しました。現状は GitHub か S3 からのデプロイのみ対応していますが、BitBucket など他のホスティングサービスにも対応して欲しいところです。

AWS では CodeDeploy のほかに Git リポジトリのホスティングサービス [AWS CodeCommit](http://aws.amazon.com/jp/codecommit/) や継続的デリバリーとリリース自動化サービス [AWS CodePipeline](http://aws.amazon.com/jp/codepipeline/) といった、開発者を支援する機能が来年初期に続々とリリースされます。

上手に使ってたのしい Laravel Life を送りましょう！

明日は @fumiyasac@github さんの「画像アップロードのお話と僕が作っているもの」です。おたのしみに。