Edited at

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

これは Ruby on Railsの環境構築をElastic Beanstalkで行うを参考にさせていただき手を動かしてみたときの私的メモです。

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のインストールあたりを参考にさせていただいてインストールしておく。


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

保存して閉じる。

Gemfilegem "dotenv-rails" を追加して、 bundle update

$ bundle update

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


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サーバーも一緒に選択するようになっている PumaPassenger 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の範囲で指定)



  8. 2つめのサブネットを作る。ふたたび サブネットの作成 ボタンをクリック。

  9. 以下の通り入力。


    • 名前タグ: railsapp-subnet-2 (任意のサブネット名)

    • VPC: railsapp-vpc を選択.

    • アベイラビリティーゾーン: us-east-1b (VPCを作成しているリージョンから一つ選ぶ)

    • IPv4 CIDRブロック: 10.0.1.0/24 (AZごとにVPCのCIDRの範囲で指定)



  10. 左メニューから インターネットゲートウェイ を選択。


  11. インターネットゲートウェイの作成 ボタンをクリック。


    • Nameタグ: railsapp-gateway


    • 作成 ボタンをクリック。



  12. リストからゲートウェイ railsapp-gateway を選択し、アクションから VPCにアタッチ を選択。


    • VPC: railsapp-vpc を選択


    • アタッチ をクリック。



  13. 左メニューから ルートテーブル をクリック。

  14. VPC IDを参考に、 railsapp-vpc に紐づいているルートテーブルを選択。

  15. ページ下部のタブから Routes を選択し、 Edit routes をクリック。


  16. Add route をクリックし、以下を入力


    • Destination: 0.0.0.0/0

    • Targetから Internet Gateway を選択し、続けて railsapp-gateway を選択。

    • Save routes をクリックして保存。



  17. 左メニューから セキュリティグループ を選択


  18. Create security group をクリックして以下のように入力.


    • Security group name: railsapp-security-group

    • Description: Web Security

    • VPC: railsapp-vpc を選択。




  19. Create をクリックしてセキュリティグループを作成する。

  20. リストから今作ったセキュリティグループを選択し、画面下のタブから Inbound Rules をクリック。


  21. Edit rules をクリック。


  22. ルールの追加 をクリックして以下のように入力。


    • タイプ: HTTP

    • 説明: HTTP




  23. 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


デプロイ

2回目からは 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

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

スクリーンショット 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のデータベースが2つになっていることが確認できます。


環境名のリスト表示

ここで 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を使うなどいい感じになるかなと思ったのですが、残念ながら毎月10万円以上払わないと試すこともできないということで断念。他にないかな〜とAWSをよく調べたところElastic Beanstalkがいい感じにPaaSじゃんってことで試しみました。

 実際に試してみると、想像以上に簡単にアプリが稼動しました。何よりプラットフォームのバージョンアップやセキュリティアップデートから解放されるのが一番素敵ですね。あと、nginxの設定とかロードバランサとか、Webアプリを公開する上で必須の作業も自動でやってくれるので非常に楽な感じを受けました。料金的にもElastic Beanstalk自体にはお金はかからず、RDSやEC2などリソース分だけ課金ですし、負荷状況に応じてインスタンス数も設定範囲内で自動で増減してくれるので、とてもよいと思います。

 

 欠点としては、オペレーションにやたらと時間がかかる点でしょうか。環境のコピーも10分ぐらい待たされたりしましたし、削除もけっこうかかります。デプロイはそこまでではないですが、もうちょっと早いと嬉しいです。

 

 ただ、全体としてはとても良くできたサービスだと思います。もう少し試してみて、問題なさげなら仕事に使ってみようかと思います。