xyk blog

最近は iOS 開発の記事が多めです。

EB CLI 3.x を使って Elastic Beanstalk に Rails アプリをデプロイする

環境:
Mac
eb-cli 3.0.10
ruby 2.1.5
rails 4.2.0

今回は、Elastic Beanstalk 用のコマンドラインツールである EB CLI を使って Rails アプリをデプロイしてみる。

最近出た EB CLI 3系はコマンドが2系から大幅に変更されている。
現時点(2015/2/12)では3系に対応した日本語ドキュメントはまだ用意されていないので英語の方を参照する。
http://docs.aws.amazon.com/elasticbeanstalk/latest/dg/command-reference-eb.html

コマンド一覧を見てみると Heroku に似てきた気がする。

EB CLI のインストール手順は省略。homebrew でインストールした。

1. Ruby のサンプルアプリケーションをデプロイする

まずは、Elastic Beanstalk 側で用意してあるサンプルアプリケーションをデプロイしてみる。
カレントディレクトリにソースコードを何も用意していないと自動的にこのサンプルアプリケーションがデプロイされる。

Beanstalk 環境設定 (eb init)

最初にeb initコマンドを実行し、対話形式で入力していく。
eb の初回起動時にはAWS Access Key IDAWS Secret Access Keyを入力する。
たぶん IAM でAWSElasticBeanstalkFullAccess権限を持たせたユーザのものを入力すれば大丈夫なはず(自分はAdministratorAccessのユーザを使ったが)。

続いて、アプリケーション名入力、プラットフォームの選択、SSH セットアップを行う。

  • 現時点(2015/2/12)で Beanstalk で使用できる Ruby のバージョンは 2.1.5 まで。2.2.0 は使えない。
  • EC2 インスタンスSSH でログインするためのキーペアを作成して公開鍵をアップロードしてくれる。
  • デフォルトだと~/.sshディレクトリにaws-ebaws-eb.pubという名前で作成される。
$ mkdir my-hello-app && cd my-hello-app

$ eb init

Enter Application Name
(default is "my-hello-app"):  # <= アプリケーション名の入力。未入力だとディレクトリ名となる
Application my-hello-app has been created.

Select a platform.
1) PHP
2) Node.js
3) IIS
4) Tomcat
5) Python
6) Ruby
7) Docker
8) GlassFish
9) Go
(default is 1): 6  # <= Ruby 選択

Select a platform version.
1) Ruby 2.1 (Puma)
2) Ruby 2.1 (Passenger Standalone)
3) Ruby 2.0 (Puma)
4) Ruby 2.0 (Passenger Standalone)
5) Ruby 1.9.3
(default is 1): 1  # <= Ruby 2.1.5 & Puma を選択
Do you want to set up SSH for your instances?
(y/n): y  # <= SSHのセットアップを行う

Type a keypair name.
(Default is aws-eb):  # <= 未入力
Generating public/private rsa key pair.
Enter passphrase (empty for no passphrase):  # <= 未入力
Enter same passphrase again:
Your identification has been saved in /Users/xyk/.ssh/aws-eb.
Your public key has been saved in /Users/xyk/.ssh/aws-eb.pub.

...

WARNING: Uploaded SSH public key for "aws-eb" into EC2 for region ap-northeast-1.

この時点でカレントディレクトリに.elasticbeanstalk/config.ymlが作成される。

$ cat .elasticbeanstalk/config.yml
branch-defaults:
  default:
    environment: null
global:
  application_name: my-hello-app
  default_ec2_keyname: aws-eb
  default_platform: Ruby 2.1 (Puma)
  default_region: null
  profile: null

GUIの管理画面を見てみるとアプリケーション「my-hello-app」が作成されているのが確認できる。

Beanstalk 環境作成、アプリのデプロイ (eb create)

続いてeb createを実行する。
環境名と CNAME を入力すると Beanstalk リソース群のプロビジョニングが開始される。

  • デプロイするコースコードを zip に固めて S3 に格納する(バージョン管理される)
  • ELB の作成
  • セキュリティグループ作成
  • Auto Scaling グループの作成
  • CloudWatch の Alarm 作成
  • EC2 インスタンス(t1.micro)の生成とアプリのデプロイ

といったことが行われる。これらが作成できるまでしばらく時間がかかる。

Beanstalk のアーキテクチャここを参照。

$ eb create
Enter Environment Name
(default is my-hello-app-dev): # <= 環境名の入力。未入力だと「アプリケーション名-dev」になる
Enter DNS CNAME prefix
(default is my-hello-app-dev): # <= CNAME の入力。デフォルト選択をしようとしたが既に使用済みだったのでエラーとなった。
That cname is not available. Please choose another
Enter DNS CNAME prefix
(default is my-hello-app-dev): my-hello-app-20150212 # <= 被ってない名前にする必要あり
WARNING: The current directory does not contain any source code. Elastic Beanstalk is launching the sample application instead.
Environment details for: my-hello-app-dev
  Application name: my-hello-app
  Region: ap-northeast-1
  Deployed Version: None
  Environment ID: e-pdxw4mbwye
  Platform: 64bit Amazon Linux 2014.09 v1.1.0 running Ruby 2.1 (Puma)
  Tier: WebServer-Standard-
  CNAME: my-hello-app-20150212.elasticbeanstalk.com
  Updated: 2015-02-12 10:15:36.529000+00:00
Printing Status:
INFO: createEnvironment is starting.
INFO: Using elasticbeanstalk-ap-northeast-1-123456789012 as Amazon S3 storage bucket for environment data.
INFO: Created security group named: sg-57ec5a32
INFO: Created load balancer named: awseb-e-p-AWSEBLoa-PCXS7HIVUX8D
INFO: Created security group named: awseb-e-pdxw4mbwye-stack-AWSEBSecurityGroup-IY32FV0GOCS
INFO: Created Auto Scaling launch configuration named: awseb-e-pdxw4mbwye-stack-AWSEBAutoScalingLaunchConfiguration-3YG1CAINBBRQ
INFO: Created Auto Scaling group named: awseb-e-pdxw4mbwye-stack-AWSEBAutoScalingGroup-1NJ3F09C0837P
INFO: Waiting for EC2 instances to launch. This may take a few minutes.
INFO: Created Auto Scaling group policy named: arn:aws:autoscaling:ap-northeast-1:123456789012:scalingPolicy:00f26acf-6a88-471a-8134-4f5eade5a324:autoScalingGroupName/awseb-e-pdxw4mbwye-stack-AWSEBAutoScalingGroup-1NJ3F09C0837P:policyName/awseb-e-pdxw4mbwye-stack-AWSEBAutoScalingScaleDownPolicy-1U1W1179JQYO7
INFO: Created Auto Scaling group policy named: arn:aws:autoscaling:ap-northeast-1:123456789012:scalingPolicy:f8ab2930-6e5a-4b93-ad5c-432d757b1042:autoScalingGroupName/awseb-e-pdxw4mbwye-stack-AWSEBAutoScalingGroup-1NJ3F09C0837P:policyName/awseb-e-pdxw4mbwye-stack-AWSEBAutoScalingScaleUpPolicy-JKXHVAQQK4R3
INFO: Created CloudWatch alarm named: awseb-e-pdxw4mbwye-stack-AWSEBCloudwatchAlarmLow-7MXXI4P9QOVN
INFO: Created CloudWatch alarm named: awseb-e-pdxw4mbwye-stack-AWSEBCloudwatchAlarmHigh-1K56EXRQ89PIO
INFO: Added EC2 instance 'i-7ec12766' to Auto Scaling Group 'awseb-e-pdxw4mbwye-stack-AWSEBAutoScalingGroup-1NJ3F09C0837P'.
INFO: Application available at my-hello-app-20150212.elasticbeanstalk.com.
INFO: Successfully launched environment: my-hello-app-dev

ちなみにeb createの引数オプションで--sampleを指定しても強制的にサンプルアプリケーションのデプロイとなる。

$ eb create my-hello-app-dev --sample

ここまでで Beanstalk 環境構築とサンプルアプリケーションのデプロイが完了した。非常に簡単。

サンプルアプリケーションの確認

eb openコマンドでデプロイされたサンプルアプリケーションをブラウザで開く。

$ eb open

URL は (CNAME prefix).elasticbeanstalk.com となる。
今回の場合だと
http://my-hello-app-20150212.elasticbeanstalk.com
となる。

この時点でconfig.ymlは以下のようになっている。

$ cat .elasticbeanstalk/config.yml
branch-defaults:
  default:
    environment: my-hello-app-dev
global:
  application_name: my-hello-app
  default_ec2_keyname: aws-eb
  default_platform: Ruby 2.1 (Puma)
  default_region: null
  profile: null

Beanstalk 環境の削除 (eb terminate)

eb terminateコマンドを実行すると上記で作成した Beanstalk のリソース群がすべて削除される。

$ eb terminate
The environment "my-hello-app-dev" and all associated instances will be terminated.
To confirm, type the environment name: my-hello-app-dev # <= 確認のため削除対象の環境名の入力。
INFO: terminateEnvironment is starting.
INFO: Deleted CloudWatch alarm named: awseb-e-pdxw4mbwye-stack-AWSEBCloudwatchAlarmLow-7MXXI4P9QOVN
INFO: Deleted CloudWatch alarm named: awseb-e-pdxw4mbwye-stack-AWSEBCloudwatchAlarmHigh-1K56EXRQ89PIO
INFO: Deleted Auto Scaling group policy named: arn:aws:autoscaling:ap-northeast-1:123456789012:scalingPolicy:00f26acf-6a88-471a-8134-4f5eade5a324:autoScalingGroupName/awseb-e-pdxw4mbwye-stack-AWSEBAutoScalingGroup-1NJ3F09C0837P:policyName/awseb-e-pdxw4mbwye-stack-AWSEBAutoScalingScaleDownPolicy-1U1W1179JQYO7
INFO: Deleted Auto Scaling group policy named: arn:aws:autoscaling:ap-northeast-1:123456789012:scalingPolicy:f8ab2930-6e5a-4b93-ad5c-432d757b1042:autoScalingGroupName/awseb-e-pdxw4mbwye-stack-AWSEBAutoScalingGroup-1NJ3F09C0837P:policyName/awseb-e-pdxw4mbwye-stack-AWSEBAutoScalingScaleUpPolicy-JKXHVAQQK4R3
INFO: Waiting for EC2 instances to terminate. This may take a few minutes.
INFO: Deleted Auto Scaling group named: awseb-e-pdxw4mbwye-stack-AWSEBAutoScalingGroup-1NJ3F09C0837P
INFO: Deleted Auto Scaling launch configuration named: awseb-e-pdxw4mbwye-stack-AWSEBAutoScalingLaunchConfiguration-3YG1CAINBBRQ
INFO: Deleted security group named: awseb-e-pdxw4mbwye-stack-AWSEBSecurityGroup-IY32FV0GOCS
INFO: Deleted load balancer named: awseb-e-p-AWSEBLoa-PCXS7HIVUX8D
INFO: Deleted security group named: sg-57ec5a32
INFO: Deleting SNS topic for environment my-hello-app-dev.
INFO: terminateEnvironment completed successfully.

アプリケーション枠の削除は GUI からやるしかないっぽい。


2. 自作の Rails アプリをデプロイ

次は実際に自分で作成した Rails アプリをデプロイしてみる。

Rails アプリケーション作成

プロジェクト新規作成

$ bundle exec rails new fooapp --skip-bundle
$ cd fooapp

# アプリケーションサーバとして Puma を選択するので Gemfile に追加
$ echo "gem 'puma'" >> Gemfile

# gem インストール
$ bundle install --path vendor/bundle

コントローラ作成

bin/rails g controller welcome

ビュー作成

app/views/welcome/index.html.erb

<h2>Hello World</h2>
<p>
  The time is now: <%= Time.now %>
</p>

ルート追加

config/routes.rb

Rails.application.routes.draw do
  root 'welcome#index'
end

ローカルで動作確認

$ bin/rails s

Git リポジトリを作成してソースコードをコミットする。

$ git init && git add -A && git commit -m "first commit"

Beanstalk 環境設定 (eb init)

$ eb init

Enter Application Name
(default is "fooapp"): # <= アプリケーション名の入力。未入力だとディレクトリ名となる
Application fooapp has been created.

It appears you are using Ruby. Is this correct?
(y/n): y # <= カレントディレクトリの状態から Rails アプリケーションであると認識してくれるので YES

Select a platform version.
1) Ruby 2.1 (Puma)
2) Ruby 2.1 (Passenger Standalone)
3) Ruby 2.0 (Puma)
4) Ruby 2.0 (Passenger Standalone)
5) Ruby 1.9.3
(default is 1): 1  # <= Ruby 2.1.5 & Puma を選択
Do you want to set up SSH for your instances?
(y/n): y # <= SSHのセットアップを行う

Select a keypair.
1) aws-eb
2) [ Create new KeyPair ]
(default is 2): 1  # <= キーペアは前回作成したものを使いまわす

またはeb initコマンドに引数オプションで指定してもよい。

$ eb init fooapp \
--platform "Ruby 2.1 (Puma)" \
--keyname aws-eb

.gitignore が更新されるので確認。

$ git diff

diff --git a/.gitignore b/.gitignore
index 050c9d9..964c20f 100644
--- a/.gitignore
+++ b/.gitignore
@@ -15,3 +15,8 @@
 /log/*
 !/log/.keep
 /tmp
+
+# Elastic Beanstalk Files
+.elasticbeanstalk/*
+!.elasticbeanstalk/*.cfg.yml
+!.elasticbeanstalk/*.global.yml

コミット。

$ git commit -am "updated .gitignore"

Beanstalk 環境作成、アプリのデプロイ (eb create)

$ eb create
Enter Environment Name
(default is fooapp-dev): # <= 環境名の入力。未入力だと「アプリケーション名-dev」になる
Enter DNS CNAME prefix
(default is fooapp-dev): fooapp-dev-20150212 # <= CNAME の入力
Environment details for: fooapp-dev
  Application name: fooapp
  Region: ap-northeast-1
  Deployed Version: 61ce
  Environment ID: e-xmzmibygyi
  Platform: 64bit Amazon Linux 2014.09 v1.1.0 running Ruby 2.1 (Puma)
  Tier: WebServer-Standard-
  CNAME: fooapp-dev-20150212.elasticbeanstalk.com
  Updated: 2015-02-12 12:14:39.613000+00:00
Printing Status:
INFO: createEnvironment is starting.
INFO: Using elasticbeanstalk-ap-northeast-1-123456789012 as Amazon S3 storage bucket for environment data.
INFO: Created security group named: sg-8ad462ef
INFO: Created load balancer named: awseb-e-x-AWSEBLoa-IZJ7KWMA0A30
INFO: Created security group named: awseb-e-xmzmibygyi-stack-AWSEBSecurityGroup-1D7SIRQQT32SW
INFO: Created Auto Scaling launch configuration named: awseb-e-xmzmibygyi-stack-AWSEBAutoScalingLaunchConfiguration-C6SUL1CBOYNE
INFO: Waiting for EC2 instances to launch. This may take a few minutes.
INFO: Created Auto Scaling group named: awseb-e-xmzmibygyi-stack-AWSEBAutoScalingGroup-1UK527EJKFE1G
INFO: Created Auto Scaling group policy named: arn:aws:autoscaling:ap-northeast-1:123456789012:scalingPolicy:667e6f8b-e3b7-4d1f-af92-2007c9026687:autoScalingGroupName/awseb-e-xmzmibygyi-stack-AWSEBAutoScalingGroup-1UK527EJKFE1G:policyName/awseb-e-xmzmibygyi-stack-AWSEBAutoScalingScaleDownPolicy-18233HAQI73WN
INFO: Created Auto Scaling group policy named: arn:aws:autoscaling:ap-northeast-1:123456789012:scalingPolicy:8415d59d-b349-4df6-8f84-e7f6963b0fa5:autoScalingGroupName/awseb-e-xmzmibygyi-stack-AWSEBAutoScalingGroup-1UK527EJKFE1G:policyName/awseb-e-xmzmibygyi-stack-AWSEBAutoScalingScaleUpPolicy-1I30QUM7E5BWU
INFO: Created CloudWatch alarm named: awseb-e-xmzmibygyi-stack-AWSEBCloudwatchAlarmHigh-1GM6DNUY8LMMB
INFO: Created CloudWatch alarm named: awseb-e-xmzmibygyi-stack-AWSEBCloudwatchAlarmLow-1DXV8TCCTG40H
INFO: Added EC2 instance 'i-67dc3a7f' to Auto Scaling Group 'awseb-e-xmzmibygyi-stack-AWSEBAutoScalingGroup-1UK527EJKFE1G'.
INFO: Application available at fooapp-dev-20150212.elasticbeanstalk.com.
INFO: Successfully launched environment: fooapp-dev

またはeb createコマンドに引数オプションで指定してもよい。

eb create fooapp-dev --cname fooapp-dev-20150212

ブラウザで確認。

$ eb open

ブラウザで開いてみると画面に以下のようなエラーが出た。

An unhandled lowlevel error occured. The application logs may have details.

eb logsコマンドでサーバ側のログを確認する。

$ eb logs

...

-------------------------------------
/var/log/puma/puma.log
-------------------------------------
=== puma startup: 2015-02-12 12:22:27 +0000 ===
=== puma startup: 2015-02-12 12:22:27 +0000 ===
[20244] - Worker 0 (pid: 20248) booted, phase: 0
2015-02-12 12:24:17 +0000: Rack app error: #<RuntimeError: Missing `secret_token` and `secret_key_base` for 'production' environment, set these values in `config/secrets.yml`>

...

production 環境時のsecret_key_baseを設定し忘れていたため、エラーが出ていた。
config/secrets.yml では以下のようにsecret_key_base環境変数から取得して設定している。

# config/secrets.yml
production:
  secret_key_base: <%= ENV["SECRET_KEY_BASE"] %>

Beanstalk の環境変数eb printenvコマンドで確認してみる。

$ eb printenv
 Environment Variables:
     AWS_SECRET_KEY = None
     RAILS_SKIP_ASSET_COMPILATION = false
     BUNDLE_WITHOUT = test:development
     RACK_ENV = production
     PARAM5 = None
     PARAM4 = None
     PARAM3 = None
     PARAM2 = None
     PARAM1 = None
     RAILS_SKIP_MIGRATIONS = false
     AWS_ACCESS_KEY_ID = None

これらの環境変数は Beansstalk のプラットフォームが Ruby の場合に Beansstalk 側であらかじめ用意してくれるパラメータとなる。

eb setenvコマンドで環境変数SECRET_KEY_BASEを追加する。

$ eb setenv SECRET_KEY_BASE=`bin/rake secret`
INFO: Environment update is starting.
INFO: Updating environment fooapp-dev's configuration settings.
INFO: Successfully deployed new configuration to environment.
INFO: Environment update completed successfully.

確認。

$ eb printenv
 Environment Variables:
     AWS_SECRET_KEY = None
     RAILS_SKIP_ASSET_COMPILATION = false
     SECRET_KEY_BASE = 87cea6cb25eba020d7c5b51d0b20377a29eaa6df475f5e1cef41f31eb67b2b0c7a932902afc3f5b44107439d0f5e7a02a25db33043a81a952190dbfde7fbff8c
     RACK_ENV = production
     PARAM5 = None
     PARAM4 = None
     PARAM3 = None
     PARAM2 = None
     PARAM1 = None
     BUNDLE_WITHOUT = test:development
     RAILS_SKIP_MIGRATIONS = false
     AWS_ACCESS_KEY_ID = None

再度、ブラウザで確認してみると今度は正しく表示された。

$ eb open

このeb setenvコマンドは v3 から追加されたものでこれによってコマンドから環境変数が設定できるようになった。
設定できるタイミングはeb createの実行後となる。

ちなみに v2 ではコマンドから環境変数を設定できず、GUI からやるか、以下のようなオプションファイルを用意することで環境変数を設定できたけど、このファイルは git で管理する必要があった。

# .ebextensions/01env.config
option_settings:
  - namespace: aws:elasticbeanstalk:application:environment
    option_name: SECRET_KEY_BASE
    value: xxxxxxx

という問題があってちょっと困っていたのでこれが解決できてよかった。

再デプロイ (eb deploy)

ソースコードの修正を反映するには、 git にコミット後にeb deployコマンドを実行する。

eb deploy

次回は VPC 内に Beanstalk 環境を構築したり RDS と連携する方法をやってみる。