HTTP/2対応のElastic Beanstalk環境を構築する


## はじめに
### サービスの紹介
2017/10/16 にリリースした <a href="https://snapmap.jp">Snapmap（スナップマップ）</a> は、
「いつ」「なにを」「どこで」「どうやって」撮影したのかが分かるというコンセプトのサービスです。

<img src="https://qiita-image-store.s3.amazonaws.com/0/37435/ceb004c5-18d8-dd7a-89ed-9e1ea16846eb.png" width="650" />

そんな Snapmap ですが、画像共有サービスなのでサイト全体は HTTP/2 で表示するようになっています。
その効果[^1]もあり、サイトのパフォーマンスを計測するとこんな感じで結構速いです。

<img src="https://qiita-image-store.s3.amazonaws.com/0/37435/01849ee1-911c-f6ac-7f70-9dc794506c78.png" width="450" />

### Elastic Beanstalkとは
Snapmap は Elastic Beanstalk を使ってアプリケーションサーバーの環境を構築しています。
Elastic Beanstalk は AWS のサービスの一つで、デプロイ・サーバー監視・スケーリングの全てをまるっとやってくれます。

新規サービスで考慮しておくべきことの多くをショートカットできるのはとても魅力的で、インフラにコストを掛けたくなかった今回の案件にぴったりでした。

### HTTP/2にする、その前に
Elastic Beanstalk で構築したアプリケーションサーバーを HTTP/2 に対応させるには、ロードバランサーを Application Load Balancer にする必要があります[^2]。
ただ、普通に Web の画面からポチポチ作成すると Classic Load Balancerで環境を構築されてしまいます（2017/12/1 現在）。
さらに、悲しいことに**環境構築後にロードバランサーの種類は変更できません**。

なので、一番初めから EB CLI を使って作成する必要があります。
すでに構築してしまっている場合は諦めて別環境を再作成しましょう。

というわけで、自分の備忘録として今回は一から手順を書いていきたいと思います。

## 手順
### AWS CLI / EB CLIのインストール
EB CLI のインストールがまだであれば以下のコマンドでインストールできます。（AWS CLI も後で必要なのでついでにインストールします）

```bash
$ pip install --upgrade pip
$ pip install awscli --user
$ pip install blessed --user
$ pip install awsebcli --user
```

### AWSの設定
`aws configure` を利用して、AWSへのアクセス情報を設定します。

```bash
$ aws configure
AWS Access Key ID [None]: AKIA*********EXAMPLE # アクセスキーを指定
AWS Secret Access Key [None]: wJa***************************EXAMPLEKEY # シークレットアクセスキーを指定
Default region name [None]: ap-northeast-1
Default output format [None]: json
```

アクセスキーをまだ発行していなければ[こちらを参考](http://docs.aws.amazon.com/ja_jp/general/latest/gr/managing-aws-access-keys.html)に。

### Elastic Beanstalkの環境作成
準備ができたので Elastic Beanstalk の環境を作っていきます。

まず初期設定を行います。
デプロイ用ディレクトリを作成して `eb init` を実行し、リージョンとアプリケーションを選択します。

```bash
$ mkdir hogehoge
$ cd hogehoge
$ 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) us-east-2 : US East (Ohio)
14) ca-central-1 : Canada (Central)
15) eu-west-2 : EU (London)
(default is 3): 9 # 東京リージョンを選ぶ

Select an application to use
1) [ Create new Application ]
(default is 1): 1 # 新規アプリを選ぶ

Enter Application Name
(default is "hogehoge"): hogehoge # ディレクトリ名がデフォルトになっているのでアプリ名を入力する
Application hogehoge has been created.

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): 4 # プラットフォームを選ぶ

Select a platform version.
1) Ruby 2.4 (Puma)
2) Ruby 2.3 (Puma)
3) Ruby 2.2 (Puma)
4) Ruby 2.1 (Puma)
5) Ruby 2.0 (Puma)
6) Ruby 2.4 (Passenger Standalone)
7) Ruby 2.3 (Passenger Standalone)
8) Ruby 2.2 (Passenger Standalone)
9) Ruby 2.1 (Passenger Standalone)
10) Ruby 2.0 (Passenger Standalone)
11) Ruby 1.9.3
(default is 1): 1 # バージョンを選ぶ
Cannot setup CodeCommit because there is no Source Control setup, continuing with initialization
Do you want to set up SSH for your instances?
(Y/n): Y # 対象サーバーにSSHアクセスしたいならYes

Select a keypair.
1) hogehoge_bastion
2) [ Create new KeyPair ]
(default is 1): 2 # キーペアは新規作成がオススメ
```

次に環境の作成ですが、ここで注意したいのが、1 つの VPC とアベイラビリティーゾーンが異なる 2 つのサブネットが必要という点です。
可用性確保のためにそういう仕様になっているっぽいです。

つまり先に VPC とサブネットは作成しておく必要があります。
作成ができたら `eb create` を実行し、環境を作成します。

```bash
eb create xxxxxxxx \
    --elb-type application \
    --vpc.id vpc-xxxxxxxx \ # 作成したVPC
    --vpc.elbsubnets subnet-xxxxxxxx,subnet-xxxxxxxx \ # 作成したサブネット
    --vpc.ec2subnets subnet-xxxxxxxx,subnet-xxxxxxxx \ # 作成したサブネット
    --vpc.elbpublic \
    --vpc.publicip
```

これでサンプルアプリのサイトが構築されます。こんな感じ。
<img src="https://qiita-image-store.s3.amazonaws.com/0/37435/8ad5ddf2-d23c-6eae-707c-0753388e65da.png" width="650" />

うまく起動したら、環境作成はいったん完了です。

ちなみに、試行錯誤をしていく上で `Still waiting for the following 1 instances to become healthy: [i-xxxxxxxxxxxxxxxxx].` みたいなヘルスチェック待ちがうっとおしかったりするので、AWS の管理画面にて一時的にヘルスチェックを無視するようにしておくと良いです。

<img src="https://qiita-image-store.s3.amazonaws.com/0/37435/fc239ae4-9319-c83a-18bb-eb7885462772.png" width="650" />




### ebextensionsを設置
次にソースコードを HTTP/2 対応させます。

[こちらを参考](https://qiita.com/Mic-U/items/f7981cf82b4649247d55#https%E5%AF%BE%E5%BF%9C)に、ソースコードのプロジェクト直下にHTTPをHTTPSにリダイレクトさせるための .ebextensions ファイルを作ります。

```bash:.ebextensions/01_aws.config
option_settings:
  aws:elbv2:listener:443:
    DefaultProcess: default
    ListenerEnabled: true
    Protocol: HTTPS
    SSLCertificateArns: rn:aws:acm:ap-northeast-1:*************************** # 先ほど発行した証明書のARN
  aws:elbv2:listener:80:
    DefaultProcess: http
    ListenerEnabled: true
    Protocol: HTTP
  aws:elasticbeanstalk:environment:process:default:
    Port: 80
    Protocol: HTTP
    StickinessEnabled: true
    StickinessLBCookieDuration: 43200
    HealthCheckPath: /
    HealthCheckTimeout: 30
    HealthCheckInterval: 60
    MatcherHTTPCode: 200
  aws:elasticbeanstalk:environment:process:http:
    Port: 81
    Protocol: HTTP
    HealthCheckPath: /
    HealthCheckTimeout: 30
    HealthCheckInterval: 60
    MatcherHTTPCode: 301
```

```bash:.ebextensions/02_nginx.config
files:
  /etc/nginx/conf.d/redirect.conf:
    mode: "000644"
    owner: root
    group: root
    content: |
      server {
        listen 81;
        rewrite ^ https://$host$request_uri permanent;
      }
```

Elastic Beanstalk の関連ファイルを無視するよう .gitignore に以下を追加します。

```bash:.gitignore
# Elastic Beanstalk Files
.elasticbeanstalk/*
!.elasticbeanstalk/*.cfg.yml
!.elasticbeanstalk/*.global.yml
```

ソースコードの変更は以上です。

### ソースコードの取得
作成したデプロイ用ディレクトリにソースコードを配置します。

```bash
$ cd hogehoge # デプロイ用ディレクトリに移動する
$ git init
$ git remote add origin git@github.com:CoachUnited/hogehoge.git
$ rm .gitignore # 自動で作成されてしまうので消す
$ git pull origin master
```

次に環境変数を設定し、再度デプロイします。

```bash
$ eb setenv RAILS_ENV=production HOGE=fuga 〜 # 必要な環境変数をつらつらと
$ eb deploy # ソースコードをデプロイ
```

これでサイトが正しく表示されれば OK です。
次は SSL 対応をしていきます。

### 証明書の取得
[こちらを参考に](https://dev.classmethod.jp/cloud/aws/acm-verifydomain-ses/) AWS Certificate Manager を利用して、サイトドメインで利用する証明書を取得します。

ちなみに、テスト環境もSSL化したい場合はワイルドカード証明書（*.snapmap.jp）を作成すればOKですが、ワイルドカード証明書はルートドメイン（snapmap.jp）には使えないので注意してください。

（なんと、11月の時点でSESでメールを受け取る[必要がなくなったそう](https://www.wantedly.com/companies/smarby/post_articles/84819)です :tada: ）

### Route53を設定
あとは Route53 でサイトドメインの Hosted Zone から A レコードを作成して、ElasitcBeanstalk で作成された環境のドメインを割り当ててください。

<img src="https://qiita-image-store.s3.amazonaws.com/0/37435/f77fa9e8-d572-834d-1bc7-02d3f4ef7fd4.png" width="350" />

これですべて完了です。
ブラウザでアクセスして HTTP/2 になっていれば成功です。お疲れ様でした。

## まとめ
手順自体はそこそこ長いですが、実際に作業してみるととても簡単に、そして一度作成してしまえば後は AWS におまかせできます。
スモールスタートのプロジェクトには ElasticBeanstalk 超超オススメです！

[^1]: Vue.js や materializecss の利用も速度向上に大きく寄与しています。

[^2]: 画像は CloudFront で配信されているため、Supported HTTP Versions を「HTTP/2, HTTP/1.1, HTTP/1.0」にするだけで  HTTP/2 化できます。