ElasticBeanstalk + Laravel + Dockerを使って15分でオートスケールする環境を構築してアプリケーションをデプロイする

[AsiaQuest Advent Calendar 2018](https://qiita.com/advent-calendar/2018/asia-quest)の12日目を担当します。

サーバーサイドエンジニアの@makopiです。

今回はElasticBeanstalk+Laravel+Dockerを使用したWebアプリケーションを作成します。

今更ながらElasticBeanstalkを使ってみたら便利だったので記事にしました。

サクッとできるのでおすすめです。

# ElasticBeanstalkとは

AWSのリソースを色々組み合わせてアプリケーション用をデプロイできる環境を簡単に構築出来るサービスです。

ElasticBeanstalk専用の管理画面上から関連するリソースの設定を変更することも出来ます。

ElasticBeanstalkの料金自体は無料で、使用したリソースに対しての課金になります。

「サーバーサイドエンジニアが短時間で環境構築したい」みたいなケースにバッチリはまると思います。

# メリット

- AWSやインフラに詳しくなくてもスケールアウトする環境が簡単に作成できる。
- 環境のコピーがボタン一つで簡単にできるのでdevを複製してstgやprodを簡単に作成できる。
- Dockerにも対応していてlocal環境とdev/stg/prod等の環境を簡単に揃えられる。
- とにかく作成が簡単

# 今回やりたい感じの図

![image.png](https://qiita-image-store.s3.amazonaws.com/0/94098/795d394f-8aef-7b30-bcf8-dc201a555f72.png)


作業の流れです。

1. Local環境のDocker上でLaravelが動くようにする
2. 作成したDockerイメージをECRにPushする
3. ElasticBeanstalkのアプリケーションを作成する
4. ElasticBeanstalkのdev環境を作成する（DockerイメージとLaravelのソースをデプロイする）

それでは作業して行きます。

# LaravelプロジェクトをDockerで立ち上げる

## laravelプロジェクトを作成

```
$ composer create-project laravel/laravel ebsample
```

## Dockerfileとdocker-compose.ymlの作成

Docker上でlaravelを立ち上げるためにDockerfileを記述します。

まずはlaravelをインストールしたディレクトリで新しいフォルダの作成をします。

```
$ mkdir stacks
$ mkdir stacks/php
$ mkdir stacks/php/conf
```

作成したディレクトリに以下のファイルを作成します。

```:stacks/php/Dockerfile
FROM php:7.2-apache

ADD ./conf/000-default.conf /etc/apache2/sites-available/000-default.conf
```

```stacks/php/conf/000-default.conf
<VirtualHost *:80>
    DocumentRoot /var/www/html/public
</VirtualHost>
```

docker-compose.ymlに関してはlaravelプロジェクトの直下に作成します。

```docker-compose.yml
version: "3"
services:
    web:
        build: ./stacks/php
        ports:
            - "8080:80"
        volumes:
            - ./:/var/www/html
```

## dockerでlaravelを動かす

```
$ docker-compose up -d
```

localhost:8080で作成したlaravelアプリケーションにアクセスできるようになりました。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/94098/f41e5065-b324-8acc-830d-60dc03c01b2f.png)

# ECRにイメージをPushする

## ECRの作成
DockerのイメージをPushするためにAWSのコンソールからリポジトリを作成します。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/94098/68c127ab-95a4-76a0-97c5-20d56cc60145.png)

今回はebsampleというリポジトリを作成しました。

## ECRにログイン

awscliを入れます。

```
$ pip3 install awscli --upgrade --user
```

パスも通しておきます。

```
$ export PATH=~/Library/Python/3.7/bin:$PATH
```

以下のコマンドでアクセスキーとシークレットキーを設定します。

```
$ aws configure
```

ecrにログインします。
以下のコマンドを実行すると、ログイン用のコマンドが出力されるので実行します。

```
aws ecr get-login --region ap-northeast-1
```

以上でecrへのログインが完了です。

## ECRに作成したDockerイメージをPush

イメージをbuildします。

```
$ docker build -t イメージID ./stacks/php/
```

pushするtagを作成します。

```
$ docker tag イメージID アカウントID.dkr.ecr.ap-northeast-1.amazonaws.com/ebsample
```

ECRにPushします。

```
$ docker push アカウントID.dkr.ecr.ap-northeast-1.amazonaws.com/ebsample
```


# ElasticBeanstalkアプリケーションを作成

## ebコマンドのインストール

まずはコマンドライン上からアプリケーションを作成できるようにebコマンドをインストールします。

```
$ pip3 install awsebcli --upgrade --user
```

これでコマンドライン上からElasticBeanstalkが叩けるようになります。

## Dockerrun.aws.jsonの作成

Dockerrun.aws.jsonはebにデプロイするために必要な設定ファイルです。
コンテナに使用するイメージやマウントするディレクトリ等を指定します。

```Dockerrun.aws.json
{
  "AWSEBDockerrunVersion": 2,
  "volumes": [
    {
      "name": "laravel-app",
      "host": {
        "sourcePath": "/var/app/current/"
      }
    },
    {
      "name": "laravel-storage",
      "host": {
        "sourcePath": "/var/app/current/storage/"
      }
    }
  ],
  "containerDefinitions": [
    {
      "name": "laravel-app",
      "image": "アカウントID.dkr.ecr.ap-northeast-1.amazonaws.com/ebsample:latest",
      "environment": [
        {
          "name": "Container",
          "value": "PHP"
        }
      ],
      "essential": true,
      "memory": 128,
      "mountPoints": [
        {
          "sourceVolume": "laravel-app",
          "containerPath": "/var/www/html/",
          "readOnly":"true"
        },
        {
          "sourceVolume": "laravel-storage",
          "containerPath": "/var/www/html/storage"
        }
      ],
      "portMappings": [
        {
          "hostPort": 80,
          "containerPort": 80
        }
      ]
    }
  ]
}

```

## .ebextensionsの作成

.ebextensionsにはコマンド等を記載したconfigファイルを置いておくとデプロイ中に実行してくれます。
今回はstorageのパーミッションを変更するconfigファイルを作成します。

ディレクトリの作成

```
$ mkdir .ebextensions
```

00permission.configの作成

```00permission.config 
container_commands:
  01chmod_storage:
    command: "chmod -R 777 ./storage"
```


## アプリケーションの作成

eb initでアプリケーションを作成します。
アクセスキーとシークレットアクセスキーを設定していない場合は聞かれるので事前に作成する必要があります。
環境はMulti-container Dockerを使用します。

```
$ eb init

Select a default region
1) us-east-1 : US East (N. Virginia)
2) us-west-1 : US West (N. California)
3) us-west-2 : US West (Oregon)
4) eu-west-1 : EU (Ireland)
5) eu-central-1 : EU (Frankfurt)
6) ap-south-1 : Asia Pacific (Mumbai)
7) ap-southeast-1 : Asia Pacific (Singapore)
8) ap-southeast-2 : Asia Pacific (Sydney)
9) ap-northeast-1 : Asia Pacific (Tokyo)
10) ap-northeast-2 : Asia Pacific (Seoul)
11) sa-east-1 : South America (Sao Paulo)
12) cn-north-1 : China (Beijing)
13) cn-northwest-1 : China (Ningxia)
14) us-east-2 : US East (Ohio)
15) ca-central-1 : Canada (Central)
16) eu-west-2 : EU (London)
17) eu-west-3 : EU (Paris)
(default is 3): 9

Enter Application Name
(default is "ebsample"): ebsample
Application eb-site has been created.

It appears you are using PHP. Is this correct?
(Y/n): n

Select a platform.
1) Node.js
2) PHP
3) Python
4) Ruby
5) Tomcat
6) IIS
7) Docker
8) Multi-container Docker
9) GlassFish
10) Go
11) Java
12) Packer
(default is 1): 8

Select a platform version.
1) Multi-container Docker 18.06.1-ce (Generic)
2) Multi-container Docker 1.6.2 (Generic)
(default is 1): 1
Do you want to set up SSH for your instances?
(Y/n): n
```


## dev環境の作成

```
$ eb create develop
```

dev環境を作成します。

これでソースコードがデプロイされます。

作成完了までは数分かかリます。

URLはElasticBeanstalkの管理画面に表示されています。

アクセスすると無事ページが表示されました。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/94098/1d5a285c-be31-5dc6-886d-60dbdd2f5b9a.png)

## eb createが失敗したら

```
WARNING: The Multi-container Docker platform requires additional ECS permissions. Add the permissions to the aws-elasticbeanstalk-ec2-role or use your own instance profile by typing "-ip {profile-name}".
```

上記のエラーでeb deployに失敗した場合はロールに権限が足りていません。

IAMの管理画面から`aws-elasticbeanstalk-ec2-role`というロールに
`AmazonEC2ContainerServiceforEC2Role`をアタッチします。

```
$ eb deploy
```

デプロイし直すとページが表示されると思います。

# まとめ

このようにわずかな時間で環境を構築出来ます。

作成したばかりの状態でも、負荷に応じて最大4台までスケールアウトしてくれます。

結構便利なので積極的に使って行きたいと思います。