RailsアプリケーションをElastic Beanstalkにデプロイするまで


これは [Ruby on Railsの環境構築をElastic Beanstalkで行う](https://qiita.com/yuyasat/items/4d93b4ad4f86a6e13d50)を参考にさせていただき手を動かしてみたときの私的メモです。

Herokuのように、サーバー管理を切り離してアプリの開発に集中し、手元からお手軽にデプロイできるPaaSがAWSにないかと探してみたところ、Elastic Beanstalkを見つけました。

アプリケーションのルートディレクトリから `eb deploy` でRailsアプリをデプロイできる点や、フロントのロードバランサやNginxを自動でセットアップしてくれる点、管理画面からインスタンスタイプやインスタンス数、アップデートロジックを簡単に変更できる点が気に入りました。

また、AWSですのでDBにRDSが使える点も良いと思います。たぶんAuroraも使えるでしょうし、そうすると大切なDBであればリージョンまたいで冗長化とかもできますね。

それと、本番環境とテスト環境など、複数の実行環境それぞれに対して手元のコマンドから操作ができる点もいいですね。本番環境から丸ごとコピーして自分用のテスト環境を作ってデプロイし、テストが終わったら環境丸ごと削除する、というようなことも簡単にできます。


## 環境

* アプリケーション名: `railsapp`
* 使用するRubyのバージョン: `2.5.3`
* 使用するデータベース: `PostgreSQL`
	* ローカル: brewでPostgreSQLをインストール
	* Elastic Beanstalk：RDSのPostgreSQL

## ローカルのデータベースの用意

ローカルにPostgreSQLが入っていない場合だけ実行。手元での開発用。

```
$ brew update
$ brew install postgresql
$ initdb /usr/local/var/postgres11
$ pg_ctl -D /usr/local/var/postgres11 -l logfile start
```

## awscliをインストール
まだインストールしてない場合は[brewでAWS CLIのインストール](https://qiita.com/okhrn/items/8da6b217d3b1fce63371)あたりを参考にさせていただいてインストールしておく。

## awsebcliをインストール

```
$ brew install awsebcli
```


## Railsアプリケーション作成と設定等

Railsをインストール

```
$ gem install rails
```


アプリケーション新規作成

```
$ rails new railsapp -d postgresql
```

テスト用にScaffold作成

```
$ rails g scaffold message subject body:text
```

マイグレーション実行

```
$ rails db:migrate
```

Credentialを生成

```
$ EDITOR=vi rails credentials:edit
```

保存して閉じる。

`Gemfile` に `gem "dotenv-rails"` を追加して、 `bundle update`

```
$ bundle update
```

`config/database.yml` のうち、production環境の設定を、環境変数から読むように編集

```yaml:config/database.yml
production:
  <<: *default
  database: <%= ENV['RDS_DB_NAME'] %>
  username: <%= ENV['RDS_USERNAME'] %>
  password: <%= ENV['RDS_PASSWORD'] %>
  host: <%= ENV['RDS_HOSTNAME'] %>
  port: <%= ENV['RDS_PORT'] %>

```

gitにcommitする。

```
$ git add .
$ git commit -m "initial commit"
```


## Elastic Beanstalkの設定

今回は `US East` リージョンを選択した。
また、Rubyバージョン番号の後ろにワーカーのWebサーバーも一緒に選択するようになっている `Puma` か `Passenger Standalone` のどちらかを使えということらしい。フロントには自動的にNginxが立つようだから、後ろのワーカーに何を使うか、ということみたい。

ちなみにPumaはスレッドベース、Passenger Standaloneはプロセスベースということでいいのかな。スレッドベースの方が軽量っぽいけど、アプリの実装によってはプロセスベースの方が安全ってこともあるかもしれない。
今回は `Ruby2.5 (Puma)` を選択した。

```
$ 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): 1

Enter Application Name
(default is "railsapp"): 
Application railsapp has been created.

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

Select a platform version.
1) Ruby 2.5 (Passenger Standalone)
2) Ruby 2.5 (Puma)
3) Ruby 2.4 (Passenger Standalone)
4) Ruby 2.4 (Puma)
5) Ruby 2.3 (Passenger Standalone)
6) Ruby 2.3 (Puma)
7) Ruby 2.2 (Passenger Standalone)
8) Ruby 2.2 (Puma)
9) Ruby 2.1 (Passenger Standalone)
10) Ruby 2.1 (Puma)
11) Ruby 2.0 (Passenger Standalone)
12) Ruby 2.0 (Puma)
13) Ruby 1.9.3
(default is 1): 2
Note: Elastic Beanstalk now supports AWS CodeCommit; a fully-managed source control service. To learn more, see Docs: https://aws.amazon.com/codecommit/
Do you wish to continue with CodeCommit? (y/N) (default is n): n
Do you want to set up SSH for your instances?
(Y/n): Y

Select a keypair.
1) xxx_xxxxxxx
2) [ Create new KeyPair ]
(default is 1): 1

```

選択したRubyのバージョンと Gemfileに記述してあるバージョンが異なる場合は、合うように編集してコミットしておく。

ここで一旦アプリケーションを作る。

```
$ eb create
```

ただ、この時点ではデータベースがないためエラーが出る。


追記： なお、以下のようにして環境名とインスタンスタイプや、RDSのデータベース種別、データベースユーザー名を指定できる。
また、 `--vpc` オプションをつけると環境全体をVPCの内部に作ってくれる。
注意点として、このオプションを指定するには、あらかじめVPCを作成しておく必要がある。

デフォルトのVPCにアプリを配置する場合は、以下の付録は読み飛ばして `RDSでデータベース作成` に行ってください。

### 付録：VPC作成

1. AWSのメニューからV `VPC` を選択。
2. `Create  VPC` をクリック。
3. 以下の通り入力・選択する
	* Name tag: `railsapp-vpc` (任意の名前)
	* IPv4 CIDR block: `10.0.0.0/16` (任意のネットワークを指定)
	* IPv6 CIDR block: `No IPv6 CIDR Block` を選択
	* Tenancy: `Default` を選択
4. `Create` をクリック。
5. 左メニューから `サブネット` を選択。
6. 1つめのサブネットを作る。 `サブネットの作成` ボタンをクリック。
7. 以下の通り入力。
	* 名前タグ: `railsapp-subnet-1` (任意のサブネット名)
	* VPC: `railsapp-vpc` を選択.
	* アベイラビリティーゾーン: `us-east-1a` (VPCを作成しているリージョンから一つ選ぶ)
	* IPv4 CIDRブロック: `10.0.0.0/24` (AZごとにVPCのCIDRの範囲で指定)
6. 2つめのサブネットを作る。ふたたび `サブネットの作成` ボタンをクリック。
7. 以下の通り入力。
	* 名前タグ: `railsapp-subnet-2` (任意のサブネット名)
	* VPC: `railsapp-vpc` を選択.
	* アベイラビリティーゾーン: `us-east-1b` (VPCを作成しているリージョンから一つ選ぶ)
	* IPv4 CIDRブロック: `10.0.1.0/24` (AZごとにVPCのCIDRの範囲で指定)
8. 左メニューから `インターネットゲートウェイ` を選択。
9. `インターネットゲートウェイの作成` ボタンをクリック。
	* Nameタグ: `railsapp-gateway`
	* `作成` ボタンをクリック。
10. リストからゲートウェイ `railsapp-gateway` を選択し、アクションから `VPCにアタッチ` を選択。 
	* VPC: `railsapp-vpc` を選択
	* `アタッチ` をクリック。
11. 左メニューから `ルートテーブル` をクリック。
12. VPC IDを参考に、 `railsapp-vpc` に紐づいているルートテーブルを選択。
13. ページ下部のタブから `Routes` を選択し、 `Edit routes` をクリック。
14. `Add route` をクリックし、以下を入力
	* Destination: `0.0.0.0/0`
	* Targetから `Internet Gateway` を選択し、続けて `railsapp-gateway` を選択。
	*  `Save routes` をクリックして保存。
15. 左メニューから `セキュリティグループ` を選択
16. `Create security group` をクリックして以下のように入力.
	* Security group name: `railsapp-security-group`
	* Description: `Web Security`
	* VPC: `railsapp-vpc` を選択。
17. `Create` をクリックしてセキュリティグループを作成する。
18. リストから今作ったセキュリティグループを選択し、画面下のタブから `Inbound Rules` をクリック。
19. `Edit rules` をクリック。
20. `ルールの追加` をクリックして以下のように入力。
	* タイプ: HTTP
	* 説明: `HTTP`
21. `Save rules` をクリックしてルールを保存する。



以上でVPCの作成は終わり。


### 付録：VPCを指定してアプリケーションを作成する
```
$ eb create railsapp-dev --instance_type t2.medium --database.engine postgres --database.username railsapp --vpc
```

上のように色々オプションを指定すると、追加でいくつかの質問がされる。
VPC IDやサブネット、セキュリテグループなどはAWSのコンソール画面から、さっき作ったもののIDをコピーして答えていく。


```
Enter an RDS DB master password: (RDSに設定するパスワードを入力)
Retype password to confirm: (確認のためもう一度入力)

Enter the VPC ID: vpc-xxxxxxxxxxxxxxx (VPCのIDを入力)
Do you want to associate a public IP address? (Y/n): Y (パブリックなIPアドレスを持たせるか？)
Enter a comma-separated list of Amazon EC2 subnets: subnet-xxxxxxxxxxxxxxxxx1,subnet-xxxxxxxxxxxxxxxx2 (EC2インスタンス用サブネット。カンマ区切りで複数指定可能)
Enter a comma-separated list of Amazon ELB subnets: subnet-xxxxxxxxxxxxxxxxx1,subnet-xxxxxxxxxxxxxxxx2 (ロードバランサ用サブネット。EC2と同じでも良い。カンマ区切りで複数指定可能)
Do you want the load balancer to be public? (Select no for internal) (Y/n): Y (ロードバランサをインターネットに公開するか？)
Enter a comma-separated list of Amazon VPC security groups: sg-zzzzzzzzzzzzzz (環境に適用するセキュリティグループを指定) 
Creating application version archive "app-3e5b-181212_0000000000".
Uploading railsapp2/app-3e5b-181212_233242.zip to S3. This may take a while.
Upload Complete.

```

## RDSでデータベース作成

データベース作成はElastic Beanstalkの設定画面からもできる。

* 上の付録で、`eb create` コマンドにデータベースを指定している場合は自動で作られるので必要なし。

1. Elastic Beanstalkのトップ画面から、今回のアプリケーションをクリック。
2. 左のメニューから設定をクリック。
3. 下の方の `データベース` の `変更` をクリックし、新規でデータベースを作成する。
4. この時設定したユーザー名とパスワードを覚えておくこと。
5. また、RDSの管理画面から、エンドポイントとデータベース名をメモっておく。


## 実行環境の環境変数の設定（DB接続のパスワードとか）

Elastic Beanstalk環境変数に `config/master.key` の値を設定する。

```
$ eb setenv RAILS_MASTER_KEY=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
```

RDSの情報（データベース名、ユーザー名、パスワード、ホスト名=エンドポイント、ポート番号）をElastic Beanstalk環境変数に登録する。


```
$ eb setenv RDS_DB_NAME=ddddbbbbnnnnaaaammmmeeee RDS_USERNAME=railsapp RDS_PASSWORD=xxxx RDS_HOSTNAME=xxx.yyy.us-east-1.rds.amazonaws.com RDS_PORT=5432
```

## デプロイ

２回目からは `deploy` コマンドを使う。

```
$ eb deploy
```

以下のように成功のログができることを確認

```
Creating application version archive "app-xxxx-xxxxxxx_xxxxxx".
Uploading railsapp/app-xxxx-xxxxxxx_xxxxxx.zip to S3. This may take a while.
Upload Complete.
2018-12-11 07:50:38    INFO    Environment update is starting.      
2018-12-11 07:50:41    INFO    Deploying new version to instance(s).
2018-12-11 07:51:09    INFO    New application version was deployed to running EC2 instances.
2018-12-11 07:51:09    INFO    Environment update completed successfully.
```

動作確認は、以下のコマンドを打ち込むか、Elastic Beanstalkの管理画面からURLのリンクを開く。

```
$ eb open
```

今回は messagesコントローラを作ったので、URLの後ろに `messages` をつけてやる必要がある。

## ログの確認方法等

### ログの確認

```
$ eb logs

[2018-12-11T08:37:35.257Z] DEBUG [32461] : Storing current stage..
[2018-12-11T08:37:35.257Z] DEBUG [32461] : Stage_num does not exist. Not saving null stage. Returning..
[2018-12-11T08:37:35.257Z] DEBUG [32461] : Reading config file: /etc/elasticbeanstalk/.aws-eb-stack.properties
[2018-12-11T08:37:35.257Z] DEBUG [32461] : Retrieving metadata for key: AWS::ElasticBeanstalk::Ext||_ContainerConfigFileContent||commands..
```

### ステータス確認

```
$ eb status

Environment details for: railsapp-dev
  Application name: railsapp
  Region: us-east-1
  Deployed Version: app-xxxx-xxxxxx_xxxxx
  Environment ID: x-xxxxxxxxx
  Platform: arn:aws:elasticbeanstalk:us-east-1::platform/Puma with Ruby 2.5 running on 64bit Amazon Linux/2.8.6
  Tier: WebServer-Standard-1.0
  CNAME: railsapp-dev.us-east-1.elasticbeanstalk.com
  Updated: 2018-12-11 08:37:40.635000+00:00
  Status: Ready
  Health: Green
```

### 設定されている環境変数の確認

```
$ eb printenv

 Environment Variables:
     RDS_PORT = xxxx
     RAILS_SKIP_ASSET_COMPILATION = false
     RDS_PASSWORD = xxxxxxx
     RDS_DB_NAME = xxxxxxxxxxxxxx
     RACK_ENV = production
     RAILS_MASTER_KEY = xxxxxxxxxxxxxxxxxxxxxxxxxxxx
     RDS_USERNAME = xxxxxx
     BUNDLE_WITHOUT = test:development
     RAILS_SKIP_MIGRATIONS = false
     RDS_HOSTNAME = xxxxxxxxxxxxxx.xxxxxxxxxxxx.us-east-1.rds.amazonaws.com
```

## クラスタ数の増減設定

デフォルトでは、 `最小:1 最大:4` に設定されており、ネットワークアウトを監視して、平均6000000バイト以上のデータ転送が一定時間以上続いたらインスタンスが1つづつ増えるようになっています。
これらの設定は `Elastic Beanstalk -> (アプリ名) -> 設定 -> 容量` で変更できます。

## インスタンスタイプの変更

デフォルトでは `t2.micro` を使うように設定されています。
この設定は `Elastic Beanstalk -> (アプリ名) -> 設定 -> インスタンス` で変更できます。
設定を変更すると自動的にインスタンスが変わります。
なお、インスタンスの変更には5分くらいかかりました。変更が終了したらEC2から古いインスタンスが削除され、新しいインスタンスが立ち上がります。

## セキュリティアップデート

プラットフォーム（OS含む）のアップデートに関しては、デフォルトでは自動的に行わないようです。
この設定は、`Elastic Beanstalk -> (アプリ名) -> 設定 -> Managed updates` で変更し、毎週設定された時間にプラットフォームの更新が行われるように設定できるようです。

## DNSの設定

アプリをcreateすると、環境ごとにURL(例： `railsapp-dev.us-east-1.elasticbeanstalk.com` )が割り当てられました。このURLをDNSのCNAMEで自社のドメインで別名をつければ自社ドメインで公開することができると思われます。

bindであれば

```db.mydomain.com
www		 	IN CNAME	railsapp-dev.us-east-1.elasticbeanstalk.com.
```

のように設定すれば `www.mydomain.com` として接続できるはず。


## 環境をまるごとコピー
この設定は `Elastic Beanstalk -> (アプリ名)` で右上の `アクション` から `環境のクローンを作成` から環境をまるごとコピーできます。公開前のテスト用ステージング環境などを簡単に作ることができます。
また、環境のクローンを作る際に、 例えばRailsアプリのサーバーであればRubyのバージョンを選択できますので、実行環境のバージョンアップを行いたい場合にも新しいバージョンの実行環境を選択してコピーしたのちに、問題なければDNSの設定を切り替えるなどの方法で安全にバージョンアップが可能だと思われます。

また、環境のコピーはebコマンドから行うこともできます。

```
$ eb clone
```

上記コマンドを実行後に環境名などを対話的に聞かれますので、入力または空欄でデフォルト選択すれば、環境のコピーが始まります。


## 環境の削除
この設定は `Elastic Beanstalk -> (アプリ名)` で右上の `アクション` から `環境の終了` を選択すると、環境をまるごと削除します。アタッチされたデータベースも削除され、立ち上がっていたEC2インスタンスも削除されます。開発環境などは使用が終わったら削除しておけばお金も節約できます。

 また、環境の削除はebコマンドからも実行できます。
 
```
$ eb terminate
```
   

## 削除した環境の再構築
一旦削除した環境を手元のアプリケーションのディレクトリから再構築できます。

```
$ eb restore
```

上記のコマンドを実行すると下のように再構築する環境を選択するように促されますので、 `R` でリストアモードにし、リストアする環境の番号を入力し（例: `1` ）、Enterを押すと。

![スクリーンショット 2018-12-12 8.21.11.png](https://qiita-image-store.s3.amazonaws.com/0/13835/014d9197-15e2-09a0-a0d3-00deb570071c.png "スクリーンショット 2018-12-12 8.21.11.png")」

下のような確認画面に移行します。 `y` を入力すると再構築が始まります。

![スクリーンショット 2018-12-12 8.22.41.png](https://qiita-image-store.s3.amazonaws.com/0/13835/e807d46e-b8c7-2ca1-48d1-8d70e4e162ec.png "スクリーンショット 2018-12-12 8.22.41.png")

ログを見ていると、 `create` の時と違ってRDSも自動的に立ち上げてくれたようです。
結構待ちますのでコーヒーでも淹れましょう☕️

## 複数の環境（ステージング環境と本番環境とか）に対する操作
例えばステージング環境と本番環境がそれぞれ別にある場合、 ebコマンドに環境名を渡すことでそれぞれの環境に対して操作を行うことができます。

まず、 `eb clone` コマンドで現在の環境をまるごとコピーして本番環境を作ります。
環境名は `railsapp-production` とします。

```
$ eb clone

Enter name for Environment Clone
(default is railsapp-dev-clone): railsapp-production
Enter DNS CNAME prefix
(default is railsapp-production): 
```

これでRDSも含めて同じ環境がまるっとコピーされます。RDS管理画面に行くとPostgreSQLのデータベースが２つになっていることが確認できます。

### 環境名のリスト表示
ここで `eb list` を実行すると、現在の環境のリストが表示されます。

```
$ eb list

* railsapp-dev
railsapp-production
```

｀*｀ がついてるのは、たぶんデフォルトの環境ってことですかね。

ここで、例えば本番環境に対して手元のアプリをデプロイしたい場合は

### 環境を指定してデプロイ

```
$ eb deploy railsapp-production
```

のようにコマンド実行すると、指定した環境に対してのみデプロイが行われます。
deployに限らず、環境に対する操作は環境名を指定すれば、その環境に行われますし、指定しなければデフォルトの環境に対して行われるようです。


### 環境を指定して削除
例えば、今作った環境の削除は

```
$ eb terminate railsapp-production
```

で削除できます。


## プラットフォームの更新

特定の環境に対してプラットフォームの更新を行うことができます。例えばRubyの最新バージョンにしたいときなど、以下のコマンドから実行できます。

```
$ eb upgrade railsapp-production
```

`railsapp-production` は先ほど作った環境名ですので、ご自分の環境に合わせて変更してください。

## 感想とか

　自社でサーバー管理してると、やはり色々と手がかかりますし心臓に悪いことも多いです。例えばRailsアプリであればRubyのバージョンアップ一つとっても本番環境に適用するには結構気を使いますし、xenなどの仮想化環境を使えばいいじゃんとか言われそうだけどDomain-0のバージョンアップはどうすんのとか、色々悩ましいです。
　
そういう意味で、できるだけそういう心配事から解放されたくてPaaSを漁ってました。最初はherokuに触手を伸ばしました、特に heroku private spaces で東京リージョンを使えばheroku自体はAWS上で動いてるそうなので Webアプリはherokuで、データベースはRDSを使うなどいい感じになるかなと思ったのですが、残念ながら毎月１０万円以上払わないと試すこともできないということで断念。他にないかな〜とAWSをよく調べたところElastic Beanstalkがいい感じにPaaSじゃんってことで試しみました。

　実際に試してみると、想像以上に簡単にアプリが稼動しました。何よりプラットフォームのバージョンアップやセキュリティアップデートから解放されるのが一番素敵ですね。あと、nginxの設定とかロードバランサとか、Webアプリを公開する上で必須の作業も自動でやってくれるので非常に楽な感じを受けました。料金的にもElastic Beanstalk自体にはお金はかからず、RDSやEC2などリソース分だけ課金ですし、負荷状況に応じてインスタンス数も設定範囲内で自動で増減してくれるので、とてもよいと思います。
　
　欠点としては、オペレーションにやたらと時間がかかる点でしょうか。環境のコピーも10分ぐらい待たされたりしましたし、削除もけっこうかかります。デプロイはそこまでではないですが、もうちょっと早いと嬉しいです。
　
　ただ、全体としてはとても良くできたサービスだと思います。もう少し試してみて、問題なさげなら仕事に使ってみようかと思います。