
今回は「Ruby on Rails」で作成したWebアプリケーションをAWSのサービス「Elastic Beanstalk」でデプロイするまでの手順について紹介していきたいと思います。
デプロイ方法についてはすでに色々なサイトで解説されていますが、情報が古かったりして自分の環境では上手く動かなったり、UIの変更によってわかりにくかったりする部分も多々あったので、この記事を書いている2020年現在に通用したプロセスをそのまま残していくつもりです。
人それぞれ環境は違いますから、もしかすると全く参考にならない可能性もあります。その点はご了承ください。
途中で何度もエラーが発生する事もしたので、それらの対応についても掲載しておきます。もし同じようなエラーに遭遇された場合は参考にしてみてください。
Elastic Beanstalkとは
そもそもの話、Elastic Beanstalkとは一体何なのか?
僕もAWSの学習を始めてから日が浅いため、ぶっちゃけハッキリとはわかっていないというのが実際のところですが、ざっくり言ってしまうと
「ソースコードさえ渡してくれればインフラ周りの準備は全部こっちがやってあげるよー」
といった感じのサービスだと実際に使ってみる中で感じました。
「PaaS(パース)」と言えば良いのでしょうか。お馴染みのHerokuみたいな感じですね。ただ、個人的にはElastic Beanstalkの方が全然難しかったです。何せ情報量が少ない…。
通常、AWSを使ってアプリをデプロイしようと思ったら「VPCを作って、サブネットを作って、セキュリティグループを作って、EC2を設置して、NginxやUnicornの設定をして~」なんてプログラミング初学者にとってはまさに意味不明な手順を踏まなければいけませんが、Elastic Beanstalkではそういったものを省略して全て自動で用意してくれます。
参照記事:世界一丁寧なAWS解説。EC2を利用して、RailsアプリをAWSにあげるまで
(こちらの記事を読めばいかに面倒かわかるはず。)
そのため、一からインフラの事を学びたいという方には向かないかもしれませんが、なるべく手短にサクッとアプリをデプロイしたい人にはぜひ使ってみて欲しいです。
ちなみに、Beanstalkというのは「豆の木」といった意味があるんだとか。読み方は「ビーンスターク」。僕はずっと「ビーンズトーク(豆の会話って一体何ぞ…?)」だと思っていたので、つい恥をかくところでした。
ビーンスターク【beanstalk】
豆の茎。また、英国民話「ジャックと豆の木」で主人公が手に入れた、一晩で大木のように成長する豆の木のこと。
出典:デジタル大辞泉(小学館)
前提環境
自分の場合は下記のような環境で作業を行いました。
- Ruby: 2.6.3
- Rails: 5.2.4
- masOS: High Sierra(バージョン 10.13.6)
デプロイするアプリはこちら(https://github.com/kazama1209/ramen-quest)
※僕が就活用に作ったポートフォリオです。ただ単に使い方の勉強をしたい場合、上のリポジトリからクローンしてこれから説明する通りに手を動かせば全く同じ結果になると思います。
実際の手順
ではさっそく手を動かしていきましょう。
今回、自分がElastic Beanstalkへのデプロイに挑戦する上で参考にした記事は次の通り。
- Ruby on Railsの環境構築をElastic Beanstalkで行う
- Elastic BeanstalkでAWS上にHTTPS対応のRailsアプリケーションを構築する
- https://qiita.com/DeployCat/items/f9f0c03080d9cf6c3cc6
大まかな流れとしてはこれらの指示に従いつつ、残念ながら自身の環境で上手くいかない部分については独自の対処を行いました。
なお、上の記事内で既に詳しく解説していただいている部分についてはわざわざ深く掘り下げるつもりは無いので、こちらの記事と同時に開きながら読み進めるとよりスムーズにいくと思います。
IAMユーザーの作成
作業を行うためのIAMユーザーを作成し、アクセスキーとシークレットアクセスキーを忘れないようにメモしておきます。(.csvファイルをダウンロードして保管しておくとより安心)
EB CLI をインストール
自身のターミナルでElastic Beanstalkを操作するために、「EB CLI」というツールをインストールします。
インストール
$ brew install awsebcli ==> Downloading https://homebrew.bintray.com/bottles/aws-elasticbeanstalk-3.9.0.sierra.bottle.tar.gz ######################################################################## 100.0% ==> Pouring aws-elasticbeanstalk-3.9.0.sierra.bottle.tar.gz ==> Caveats Bash completion has been installed to: /usr/local/etc/bash_completion.d ==> Summary /usr/local/Cellar/aws-elasticbeanstalk/3.9.0: 1,540 files, 31M
$ eb --version EB CLI 3.16.0 (Python 3.8.1)
$ aws configure AWS Access Key ID [None]: ********* AWS Secret Access Key [None]: ********************** Default region name [None]: ap-northeast-1 Default output format [None]: json
ちゃんと設定できるか心配な方は次のコマンドを入力する事で確認できます。
$ cat ~/.aws/config $ cat ~/.aws/credentials
デプロイ用アプリケーションのセットアップ
EB CLIのインストールが済んだら、次は実際にデプロイするアプリケーションのリポジトリ内へ入り、セットアップを行っていきます。対話形式で進んでいくので、聞かれた事に対し自分で回答を入力していきましょう。
※人によって聞かれる項目や選択肢が異なる可能性もあります。
$ cd 自分のアプリ
$ 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) 18) eu-north-1 : EU (Stockholm) 19) ap-east-1 : Asia Pacific (Hong Kong) 20) me-south-1 : Middle East (Bahrain) (default is 3): 9 ※今回は東京リージョンを選択。 Enter Application Name (default is "リポジトリ名"): ※任意の名前を入力。何もせずエンターを押すとリポジトリ名で作成される。 Application リポジトリ名 has been created. It appears you are using Ruby. Is this correct? (Y/n): Y ※Rubyを使用しているかどうか聞かれているのでYes。 Select a platform version. 1) Ruby 2.6 (Passenger Standalone) 2) Ruby 2.6 (Puma) 3) Ruby 2.5 (Passenger Standalone) 4) Ruby 2.5 (Puma) 5) Ruby 2.4 (Passenger Standalone) 6) Ruby 2.4 (Puma) 7) Ruby 2.3 (Passenger Standalone) 8) Ruby 2.3 (Puma) 9) Ruby 2.2 (Passenger Standalone) 10) Ruby 2.2 (Puma) 11) Ruby 2.1 (Passenger Standalone) 12) Ruby 2.1 (Puma) 13) Ruby 2.0 (Passenger Standalone) 14) Ruby 2.0 (Puma) 15) Ruby 1.9.3 (default is 1): 2 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) hoge 2) foo 3) bar 4) [ Create new KeyPair ] (default is 4): 1 ※過去に自分が作ったキーペアの中から選ぶか、新たに作成する。
$cat .elasticbeanstalk/config.yml branch-defaults: master: environment: null group_suffix: null global: application_name: リポジトリ名 branch: null default_ec2_keyname: キー名 default_platform: Ruby 2.6 (Puma) default_region: ap-northeast-1 include_git_submodules: true instance_profile: null platform_name: null platform_version: null profile: null repository: null sc: git workspace_type: Application
先ほどの「eb init」後、ルートディレクトリに「.elasticbeanstalk/config.yml」というファイルが生成されているので中身を確認しておきましょう。それぞれが何を意味しているのか自分もぶっちゃけ良くわかりませんが、特に問題無かったのでそのまま進みます。
EC2の作成
次に、AWS上にアプリをデプロイするための環境である「EC2」を作成していきます。EC2と聞くと苦手意識を持たれる方もいるかもしれませんが、ここでは特に難しい操作も無く「eb create」というコマンドを叩けばOK。
$ eb create Enter Environment Name (default is ramen-quest-dev): ※特に希望が無ければ空のままエンターキー。 Enter DNS CNAME prefix (default is ramen-quest): こちらも上と同様。 Select a load balancer type 1) classic 2) application 3) network (default is 2): 1 Would you like to enable Spot Fleet requests for this environment? (y/N): N Creating application version archive "app-9c74-200106_043901". Uploading: [##################################################] 100% Done... ... (中略) ...
するとこんな感じでアプリケーション環境の構築が始まるので、完了するまでしばらく待ちます。
2020-01-05 19:41:43 INFO Command execution completed on all instances. Summary: [Successful: 0, Failed: 1]. 2020-01-05 19:42:47 ERROR Create environment operation is complete, but with errors. For more information, see troubleshooting documentation. ERROR: ServiceError - Create environment operation is complete, but with errors. For more information, see troubleshooting documentation.
数分後、画面を見てみるとまさかのエラー。処理自体は終わったものの、どこかで不具合が生じているようです。
Elastic Beanstalkのダッシュボードを確認してみると、明らかにエラーだとわかる赤色に染まっていました。
$ eb logs
慌てずにまずはログを確認。
+ '[' -d /var/app/ondeck/vendor/cache ']' + bundle install /opt/rubies/ruby-2.6.5/lib/ruby/site_ruby/2.6.0/rubygems.rb:284:in `find_spec_for_exe': Could not find 'bundler' (2.0.2) required by your /var/app/ondeck/Gemfile.lock. (Gem::GemNotFoundException) To update to the latest version installed on your system, run `bundle update --bundler`. To install the missing version, run `gem install bundler:2.0.2` from /opt/rubies/ruby-2.6.5/lib/ruby/site_ruby/2.6.0/rubygems.rb:303:in `activate_bin_path' from /opt/rubies/ruby-2.6.5/bin/bundle:23:in `<main>' (Executor::NonZeroExitStatus)
Elastic Beanstalkはソースコードをアップした際、自動で「bundle install」を実行してくれるそうなのですが、どうやらエラー文を読んでみると「bundler:2.0.2」が無いと怒られている様子。
ググってみた結果、「.ebextensions」という隠れディレクトリをルートに作成し、「.conifg」を拡張子とするYMLもしくはJSON形式のファイルを置く事で、デプロイ前後に実行するコマンドやファイルを追加するための設定を行う事ができるそうです。
参照記事
- 設定ファイル (.ebextensions) による高度な環境のカスタマイズ
- Elastic Beanstalk + Railsで、can't find gem bundlerが発生する問題
- ElasticBeanstalk でのトラブルシューティング
- elastic beanstalkの設定ファイルを活用する
files: "/opt/elasticbeanstalk/hooks/appdeploy/pre/09_yarn.sh" : mode: "000755" owner: root group: root content: | #!/usr/bin/env bash set -xe EB_SCRIPT_DIR=$(/opt/elasticbeanstalk/bin/get-config container -k script_dir) EB_APP_STAGING_DIR=$(/opt/elasticbeanstalk/bin/get-config container -k app_staging_dir) EB_APP_USER=$(/opt/elasticbeanstalk/bin/get-config container -k app_user) EB_SUPPORT_DIR=$(/opt/elasticbeanstalk/bin/get-config container -k support_dir) . $EB_SUPPORT_DIR/envvars . $EB_SCRIPT_DIR/use-app-ruby.sh # Install nodejs echo "install nodejs" curl --silent --location https://rpm.nodesource.com/setup_6.x | sudo bash - yum -y install nodejs echo "install yarn" # install yarn wget https://dl.yarnpkg.com/rpm/yarn.repo -O /etc/yum.repos.d/yarn.repo; yum -y install yarn; # yarn install cd $EB_APP_STAGING_DIR yarn install --ignore-engines # mkdir /home/webapp mkdir -p /home/webapp chown webapp:webapp /home/webapp chmod 700 /home/webapp
option_settings: aws:elasticbeanstalk:environment: LoadBalancerType: application aws:autoscaling:launchconfiguration: InstanceType: t3.small # The additional security group # refer: https://github.com/awsdocs/elastic-beanstalk-samples/blob/master/configuration-files/aws-provided/security-configuration/securitygroup-addexisting.config SecurityGroups: default # aws:elasticbeanstalk:application:environment: {} packages: yum: htop: [] strace: [] files: # https://stackoverflow.com/a/55378320/3090068 # # Runs before `./10_bundle_install.sh`: "/opt/elasticbeanstalk/hooks/appdeploy/pre/09_gem_install_bundler.sh" : mode: "000775" owner: root group: users content: | #!/usr/bin/env bash EB_APP_STAGING_DIR=$(/opt/elasticbeanstalk/bin/get-config container -k app_staging_dir) EB_SCRIPT_DIR=$(/opt/elasticbeanstalk/bin/get-config container -k script_dir) . $EB_SCRIPT_DIR/use-app-ruby.sh cd $EB_APP_STAGING_DIR echo "Installing compatible bundler" gem install bundler -v 2.0.2 commands: 01-set_timezone: command: | cp /usr/share/zoneinfo/Japan /etc/localtime echo 'ZONE="Asia/Tokyo"' &gt; /etc/sysconfig/clock echo 'UTC=false' &gt; /etc/sysconfig/clock
正直、内容が高度に感じて何が書かれているのか良く分からない部分もありますが、とりあえず言われた通り「.ebextensions」を作成し、その中に各種「.config」ファイルをコピペで置いてみました。
bundler以外にyarnのインストールに関するファイルも作成してあります。
.ebextensionsを追加したところで、再度デプロイし直したいと思います。ただ、その前にgit commitしておくのを忘れずに。
$ git add . $ git commit -m '[Add] ebextensions'
先ほどすでに「eb create」を行っているので、2回目以降は「eb deploy」を使っていきます。
$ eb deploy Creating application version archive "app-072b-200106_055431". Uploading: [##################################################] 100% Done... 2020-01-05 20:55:18 INFO Environment update is starting. ... (中略) ...
これまた多少の時間がかかるので待ちます。
+ '[' -d /var/app/ondeck/vendor/cache ']' + bundle install Don't run Bundler as root. Bundler can ask for sudo if it is needed, and installing your bundle as root will break this application for all non-root users on this machine. Your Ruby version is 2.6.5, but your Gemfile specified 2.6.3 (Executor::NonZeroExitStatus)
数分後、画面を確認してみるとまたしてもエラー。ただ、内容が少し変わっています。これはつまり、先に進んでいるという事。ポジティブに考えて進んでいきましょう。
ざっくり読んでみると「あなたの環境におけるRubyのバージョンは2.6.5なのに、Gemfileには2.6.3が記述されてますよ」的な事を言われているぽいですね。
$ eb ssh [ec2-user@ip-000-00-0-000 ~] $ruby -v ruby 2.6.5p114 (2019-10-01 revision 67812) [x86_64-linux]
「eb ssh」でEC2内へログインし、Rubyのバージョンを確認してみると確かに2.6.5になっていました。
$ vi Gemfile source 'https://rubygems.org' git_source(:github) { |repo| "https://github.com/#{repo}.git" } ruby '2.6.5'
$ git add . $ git commit -m '[Modify] update Ruby version' $ eb deploy
忘れずに「git commit」した後、再度「eb deploy」。
Missing encryption key to decrypt file with. Ask your team for your master key and write it to /var/app/ondeck/config/master.key or put it in the ENV['RAILS_MASTER_KEY']. (Executor::NonZeroExitStatus)
ん~、なかなか思うようにいかない。またしてもエラー発生です。
「マスターキーが無いよ」と言われてしまっています。自分の場合「.gitignore」でmaster.keyをgitの管理対象から外してしまっているため、上手く読み込めていないと思われます。
まぁこれに関してはすぐに解決できそうですね。
$ eb setenv RAILS_MASTER_KEY='マスターキーの中身'
「eb setenv」コマンドでmaster.keyの中身を環境変数として設定すればOKです。
※もし他にも環境変数を使用している場合、ここで一気にセットしてしまうと後が楽になります。
$ eb deploy
一刻も早くデプロイできる事を夢見て再度「eb deploy」を叩きます。
+ cd /var/app/ondeck + su -s /bin/bash -c 'bundle exec /opt/elasticbeanstalk/support/scripts/check-for-rake-task.rb db:migrate' webapp + '[' false = true ']' + su -s /bin/bash -c 'leader_only bundle exec rake db:migrate' webapp rake aborted! Mysql2::Error::ConnectionError: Can't connect to local MySQL server through socket '/var/lib/mysql/mysql.sock' (2) /opt/rubies/ruby-2.6.5/bin/bundle:23:in `load' /opt/rubies/ruby-2.6.5/bin/bundle:23:in `&lt;main&gt;' Tasks: TOP =&gt; db:migrate (See full trace by running task with --trace) (Executor::NonZeroExitStatus)
しかし残念ながらまたエラー発生。今度はMySQLと接続ができないと怒られています。それもそのはず、そういえばまだデータベースを作成していません。
冒頭でElastic Beanstalkを使えばインフラの準備は不要と言いましたが、データベースは自分で用意する必要があるみたいですね。
データベースに関してはAWSが提供しているRDSを利用します。ダッシュボードの「設定」から最下部にある「データベースの変更」をクリック。
お好みの設定にして「適用」をクリックしましょう。
「RDS」→「データベース」のところにインスタンスが作成されていれば成功です。
詳細画面に記載されている「エンドポイント」は後ほど使う事になるのでメモしておいてください。
$ vi config/database.yml ... (中略) ... production: &amp;lt;&amp;lt;: *default adapter: mysql2 encoding: utf8 database: <%= ENV['RDS_DB_NAME'] %> host: <%= ENV['RDS_HOST_NAME'] %> username: <%= ENV['RDS_USER_NAME'] %> password: <%= ENV['RDS_PASSWORD'] %> port: 3306
「config/database.yml」のproduction部分を編集します。
$ git add . $ git commit -m '[Modify] config/database.yml'
$ eb setenv RDS_DB_NAME=ebdb RDS_HOST_NAME='エンドポイント' RDS_USER_NAME='ユーザー名' RDS_PASSWORD='パスワード'
忘れずに環境変数もセットしておきましょう。なお、Elastic Beanstalkで作成したRDSのデータベース名はデフォルトで「ebdb」になるようです。
$ eb deploy
長い戦いがようやく終わり、デプロイ成功です。ヘルスチェックにも合格して緑色になっています。
$ eb open
しかし、ここでまたもや問題発生。「eb open」でページにアクセスしてみると「このサイトにアクセスできません」との文字が…。
$ eb ssh [ec2-user@ip-000-00-0-000 ~] $ cd /var/app/current [ec2-user@ip-000-00-0-000 current]$ ls Gemfile README.md app config.ru log public storage yarn-error.log Gemfile.lock README_EN.md bin db node_modules spec tmp yarn.lock Procfile Rakefile config lib package.json static vendor
「/var/app/current」内にしっかりと各種ファイルが入っているので、デプロイ自体は上手くいっているはずなのですが。
ログを見ても特にヒントは見当たりません。というより、サーバー自体にアクセスが届いていないみたいで、ログが途切れてしまっています。これじゃ何の不具合が起きているのかもわからない…。
ここからまた長い旅が始まります。
「ElasticBeanstalk デプロイ後 このサイトにアクセスできません」という単純なキーワードから始まり、色々調べてみたのですが、一向にそれらしい回答はでてきませんでした。
ただ、この種のエラーが起きた場合の基本的な問題解決方法として、Cookieやキャッシュを削除すると良いかもといったものがあります。
「まさかそんなシンプルな方法で…」と半信半疑でしたが、これがまさかの正解。
ついにアクセスできるようになりました!
しかし、なぜかCSSが反映されていない様子。
$ eb ssh [ec2-user@ip-000-00-0-000 ~] $ cd /var/app/current/pubcic/assets [ec2-user@ip-000-00-0-000 current]$ ls ...........jpg ... (中略) ...
「pubcic/assets」内を見る限り、様々なファイルが存在するので「bundle exec rake assets:precompile RAILS_ENV=production」は上手くいっているはずです。画像も表示されていますし。
$ vi <span class="sc_title">config/environments/production.rb</span> Rails.application.configure do ... (中略) ... config.assets.debug = true # falseへ変更 ... (中略) ... end
おそらくconfig関連で何かミスがあると踏み、検索開始。するとこんな記事を発見。
参照記事:Railsはassetsに注意しろ
こちら参考にconfig/environments/production.rb内の「config.assets.debug = true」をfalse へ変更してみました。trueになっていると、application.css/jsと個別のファイルの二重読み込みがされて上手く作動しない事があるようです。
ついにそれらしい感じになりました。CSSも反映されてデプロイ完了です。
感想
「Elastic Beanstalkを使えばめちゃくちゃ簡単にデプロイできるよ~」なんて言われていますが、個人的にはかなり苦戦してしまいました。
僕の環境や設定が悪かったというのもあるかもしれません。エラーの連続でかなり疲労困憊状態…。
とはいえ、基本的にはしっかりとログを読んで、解決策をググれば何とか対応できると思います。発生するエラーは人それぞれ違うと思うので、必ずしもこの記事があなたにとって役に立つかはわかりませんが、一つでもヒントになれば幸いです。