Elastic BeanstalkでAWS上にHTTPS対応のRailsアプリケーションを構築する

こんにちは。

私はネット上でよくディスられるているSIerの典型のような会社に勤めております。一応意地とプライドを持って取り組んではいるもののクラウド環境でバリバリ開発!とは無縁なお仕事です。

お恥ずかしながらAWSにもほとんど触れたことはなく、今回趣味の一環として脱AWS童貞に取り組みました。

実現したかったことはこんな感じのこと。

  • Git(およびGitHub)で管理しているWebアプリケーションをEC2へデプロイする。
  • HTTPSに対応する。
  • EC2に移行後も同じサーバ・ドメイン名で稼働させる。


ということで今回AWSで次の作業を実施しました。

んで、最終的な姿はこんな感じになりました。 AWS全体像


うーん、デザインセンス無さすぎてちょっとよく分かりませんねぇ(自画自賛

それはさておき今後AWSへの移行を考えている方に向けて少しでもヒントになればと思いこれらの手順をご紹介したいと思います。基本的に私のようなAWS童貞向けの超基礎的な内容です。

前提の環境など

AWS構築前の前提になる環境を示します。

  • Webアプリのプラットフォーム:Rails4.2、Ruby2.1、DB無し
  • ドメイン:wackwack.net(お名前.comで管理)
  • WebアプリのURL:hatenablog.wackwack.net
  • バージョン管理:Git(GitHubホスティング
  • 開発クライアント:macOS Sierra

移行するアプリケーションは自分で開発したChrome拡張機能はてなフォトライフアップローダー』です。
blog.wackwack.net

Elastic Beanstalkによるアプリケーションデプロイ

まずは今回の話の主役Elastic Beanstalkを使って既存のWebアプリケーションをEC2へデプロイしてみます。

驚くほど簡単にできますよ。

Elastic Beanstalkとは

AWS上でEC2にアプリケーションをデプロイおよびその後の管理を簡単に行うためのサービス。 docs.aws.amazon.com

インフラ構築を自動でやってくれる

Elastic Beanstalkを使うことで「サーバー入れて(EC2構築して)、NginxとRails入れて設定して……」といった一連のインフラの準備が全て不要になります。

もう一度言います。

インフラの準備が全て不要です。

DB無しの超シンプル構成とは言えこいつはすごい。

料金は不要

Elastic Beanstalkを使うに当たっては特に追加料金は必要ありません。アプリケーションをいくらデプロイしようと無料です。

EC2のお金は必要です

「Elastic Beanstalkでアプリのデプロイ環境を作る」ということは「EC2環境を新たに作る」ことになります。

つまり「Elastic Beanstalkは無料」と言っても必ずEC2が付いて回るのでEC2の料金は確実に発生します。……え?詐欺じゃないよ?

EB CLIの導入

Elastic Beanstalkでアプリケーションをデプロイするためには開発クライアントにEB CLIと呼ばれるコマンドラインツールを使います。
docs.aws.amazon.com

EB CLIは各プラットフォームごとに提供されており導入方法はそれぞれ異なります。今回のmacOS Serria環境においてはHomeBrewで導入しました。

以下のコマンドを叩き込めばOKです!

$ 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コマンドが利用できるようになります。

プロジェクトをElastic Beanstalk用にセットアップする

EB CLIを導入したら開発クライアント上のプロジェクトフォルダをセットアップします。

Gitプロジェクトの作成

Gitプロジェクトは新たに作成するも良し、GitHubなどからcloneしてくるも良しです。今回はGitHubホスティングしているプロジェクトを開発クライアントにダウンロードします。

$ git clone https://github.com/quotto/HatenaBlog.git
Cloning into 'HatenaBlog'...
remote: Counting objects: 6219, done.
remote: Total 6219 (delta 0), reused 0 (delta 0), pack-reused 6219
Receiving objects: 100% (6219/6219), 26.76 MiB | 4.83 MiB/s, done.
Resolving deltas: 100% (681/681), done.

EB CLIの設定

ダウンロードしたGitプロジェクトフォルダをElastic Beanstalk環境用に設定します。eb initを実行して対話形式で設定を行います。

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

Select an application to use
1) HatenaBlog
2) [ Create new Application ]
(default is 1): 

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

Select a platform version.
1) Ruby 2.3 (Puma)
2) Ruby 2.2 (Puma)
3) Ruby 2.1 (Puma)
4) Ruby 2.0 (Puma)
5) Ruby 2.3 (Passenger Standalone)
6) Ruby 2.2 (Passenger Standalone)
7) Ruby 2.1 (Passenger Standalone)
8) Ruby 2.0 (Passenger Standalone)
9) Ruby 1.9.3
(default is 1): 7
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): 
Do you want to set up SSH for your instances?
(y/n): y

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

以下、設定値を簡単に説明します。

  • Select a default region:デプロイ先のリージョンを選択します。EC2を構築するリージョンを選びましょう。
  • Select an application to use:アプリケーション名を入力します。デフォルトで勝手に名前を付けてくれます。
  • It appears you are using Ruby. Is this correct?:このアプリケーションのプラットフォームを確認してくれます。正しければyです。
  • Select a platform version.:プラットフォームのバージョンを選択しましょう。
  • Do you wish to continue with CodeCommit?:AWSのGitホスティングサービスを利用できます。詳しくはこちら。 aws.amazon.com
  • Select a keypair.:~/.sshd の中にAWSの認証キーを格納済みであればそれが選択できます。無い場合は新たに(または登録済みの)アクセスキーIDとアクセスシークレット情報を入力します。

Elastic Beanstalkで使うEC2環境の構築

続いてはAWS上にアプリケーションをデプロイするための環境、つまりEC2を立ち上げます。

と言ってもコマンドを一発叩くだけです。ターミナルからeb create 任意の名前(サーバ名)を実行するだけでElastic Beanstalk用のEC2が構築され起動します。

$ eb create hatenablog-env
Creating application version archive "app-f2f7-170205_195724".
Uploading HatenaBlog/app-f2f7-170205_195724.zip to S3. This may take a while.
Upload Complete.
Environment details for: hatenablog-env
  Application name: HatenaBlog
  Region: us-west-2
  Deployed Version: app-f2f7-170205_195724
  Environment ID: e-qqrbtvbfgt
  Platform: 64bit Amazon Linux 2016.09 v2.3.1 running Ruby 2.1 (Passenger Standalone)
  Tier: WebServer-Standard
  CNAME: UNKNOWN
  Updated: 2017-02-05 10:57:30.139000+00:00
Printing Status:
INFO: createEnvironment is starting.
INFO: Using elasticbeanstalk-us-west-2-188970983837 as Amazon S3 storage bucket for environment data.
INFO: Environment health has transitioned to Pending. Initialization in progress (running for 3 seconds). There are no instances.
INFO: Created security group named: sg-5eda4626
INFO: Created load balancer named: awseb-e-q-AWSEBLoa-5FOJ37VT5AIE
INFO: Created security group named: awseb-e-qqrbtvbfgt-stack-AWSEBSecurityGroup-5W25EYNX3260
INFO: Created Auto Scaling launch configuration named: awseb-e-qqrbtvbfgt-stack-AWSEBAutoScalingLaunchConfiguration-1B3MQII3ESLUS
INFO: Created Auto Scaling group named: awseb-e-qqrbtvbfgt-stack-AWSEBAutoScalingGroup-O5JRY2HPS2HO
INFO: Waiting for EC2 instances to launch. This may take a few minutes.
INFO: Added instance [i-009a59652a3bd1697] to your environment.
INFO: Created Auto Scaling group policy named: arn:aws:autoscaling:us-west-2:188970983837:scalingPolicy:7388e0ae-7614-4981-99fe-70053332553c:autoScalingGroupName/awseb-e-qqrbtvbfgt-stack-AWSEBAutoScalingGroup-O5JRY2HPS2HO:policyName/awseb-e-qqrbtvbfgt-stack-AWSEBAutoScalingScaleUpPolicy-DKND6FYLIJJV
INFO: Created Auto Scaling group policy named: arn:aws:autoscaling:us-west-2:188970983837:scalingPolicy:662f0597-28a0-4ac9-954a-0b204260e69c:autoScalingGroupName/awseb-e-qqrbtvbfgt-stack-AWSEBAutoScalingGroup-O5JRY2HPS2HO:policyName/awseb-e-qqrbtvbfgt-stack-AWSEBAutoScalingScaleDownPolicy-1X6WWI0UTKO2C
INFO: Created CloudWatch alarm named: awseb-e-qqrbtvbfgt-stack-AWSEBCloudwatchAlarmHigh-1561J7HK5BGU2
INFO: Created CloudWatch alarm named: awseb-e-qqrbtvbfgt-stack-AWSEBCloudwatchAlarmLow-3GG2V375R04J
INFO: Environment health has transitioned from Pending to Ok. Initialization completed 24 seconds ago and took 4 minutes.
INFO: Successfully launched environment: hatenablog-env

ここまで完了するとAWSマネジメントコンソールから作成したアプリケーションとEC2が構築されていることが確認できます。
Elastic Beanstalk マネジメントコンソール

またEC2初回構築時に自動でデプロイが行われたため、この時点でアプリケーションが稼働しています。ターミナルからeb openを実行するとブラウザが開きデプロイしたアプリケーションに接続します。URLが最初のセットアップで指定した環境名+リージョンドメインであることが分かります。

$ eb open

Elastic Beanstalk eb open

エラーが発生しています。こんな時はeb logsでエラーを確認します。

$ eb logs
……
2017/02/05 11:08:34 [error] 18360#0: *90 upstream prematurely closed connection while reading response header from upstream, client: xxx.xx.xx.xx, server: _, request: "GET / HTTP/1.1", upstream: "passenger:/tmp/passenger.1.0.18340/generation-0/request:", host: "hatenablog-env.gyx3d27kiy.us-west-2.elasticbeanstalk.com"
App 18408 stderr: [ 2017-02-05 11:08:47.5268 18423/0x0055920a42c768(Worker 1) utils.rb:84 ]: *** Exception RuntimeError in Rack application object (Missing `secret_token` and `secret_key_base` for 'production' environment, set these values in `config/secrets.yml`) (process 18423, thread 0x0055920a42c768(Worker 1)):
App 18408 stderr:       from /opt/rubies/ruby-2.1.10/lib/ruby/gems/2.1.0/gems/railties-4.2.5/lib/rails/application.rb:534:in `validate_secret_key_config!'
App 18408 stderr:       from /opt/rubies/ruby-2.1.10/lib/ruby/gems/2.1.0/gems/railties-4.2.5/lib/rails/application.rb:246:in `env_config'
App 18408 stderr:       from /opt/rubies/ruby-2.1.10/lib/ruby/gems/2.1.0/gems/railties-4.2.5/lib/rails/engine.rb:514:in `call'

これはRails特有のエラーです。こちらの過去記事でも紹介した「SECRET_KEY_BASEを環境変数に設定する必要がある」というものでした。
blog.wackwack.net

アプリケーションの更新

環境変数の設定

ということでデプロイ先のEC2に環境変数SECRET_KEY_BASEを設定します。 EB CLIeb setenvコマンドを使えばリモートログインすることなくElastic BeanstalkのEC2サーバーに環境変数が設定できます。

$ eb setenv SECRET_KEY_BASE=xxxxxxxxxxxxxxxxxxxxxxxxxxxxx
INFO: Environment update is starting.                               
INFO: Updating environment hatenablog-env's configuration settings. 
INFO: Environment health has transitioned from Ok to Info. Configuration update in progress (running for 26 seconds).
INFO: Successfully deployed new configuration to environment.      

eb printenvで設定内容が確認できます。

printenv
 Environment Variables:
     RACK_ENV = production
     SECRET_KEY_BASE = xxxxxxxxxxxxxxxxxxxxxxxxxxxxx
     RAILS_SKIP_MIGRATIONS = false
     RAILS_SKIP_ASSET_COMPILATION = false
     BUNDLE_WITHOUT = test:development

もう一度eb openを実行すると……

Elastic Beanstalk eb open

先程の502エラーはクリアしたもののURLのルート(Welcomeページ)が無いためエラーが出力されます。

再デプロイ

このアプリケーションは機能的にWelcomeページは不要なのですが、再デプロイの方法の確認も兼ねて更新してみます。

と言ってもファイルを更新してGitにコミットしてeb deployを実行するだけです。簡単!!

$ git commit -m "Add Welcome Page"
[ebtest 54cac0a] Add Welcome Page
 3 files changed, 9 insertions(+), 1 deletion(-)
 create mode 100644 app/views/hatena_blog/index.html.erb

$ eb deploy
Creating application version archive "app-54ca-170205_204743".
Uploading HatenaBlog/app-54ca-170205_204743.zip to S3. This may take a while.
Upload Complete.
INFO: Environment update is starting.                               
INFO: Deploying new version to instance(s).                         
INFO: New application version was deployed to running EC2 instances.
INFO: Environment update completed successfully.

再度eb openすると……

Elastic Beanstalk eb open

できたー!

AWS Certificate Managerによるサーバー証明書の取得

Webアプリケーションを速攻でデプロイできのたでHTTPS対応に向けてサーバー証明書を取得します。

これにはAWS Certificate ManagerACM)が使えます。ACMAmazonSSL/TLS証明書を発行してくれます。しかも無料です。
AWS Certificate Manager(SSL/TLS 証明書を無料で作成) | AWS

証明書の発行

まずはAWSマネジメントコンソールでACMを開き「今すぐ始める」から証明書の作成を開始します。
Certificate Managerの開始

ドメインの追加でSSLを適用するドメインを指定します。完全修飾名であれば当該のURLのみ、ワイルドカードを使うとドメイン全体に適用できます。
Certificate Manager ドメインの追加

次の画面で登録内容を確認したら「確定とリクエスト」を実行します。これでドメインの管理者へメールが送信されます。 Certificate Manager リクエストの確定

こんな感じのメールが届きます。
Certificate Manager リクエストメール

URLをクリックしてリクエストを承認します。
f:id:WorldWorldWorld:20170205205112p:plainf:id:WorldWorldWorld:20170205205113p:plain

AWSコンソールマネジメントからACMを開くと、リクエストしたサーバ証明書が作成されたことが確認できます。
Certificate Manager AWSマネジメントコンソール

アプリケーションの設定

ACMサーバ証明書を有効にするにはアプリケーション側でも設定が必要になります。

アプリケーションディレクトリの直下にebextensions/securelistener.configを以下のとおり作成してデプロイします。
このとき「SSLCertificateId」にはマネジメントコンソールに表示されている「ARN」の値を設定します。

option_settings:
  aws:elb:listener:443:
    SSLCertificateId: arn:aws:acm:us-west-2:xxxxxxxxxxx:certificate/xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
    ListenerProtocol: HTTPS
    InstancePort: 80

続いてElastic Beanstalkのマネジメントコンソールを開き「ロードバランシング」の「SSL 証明書 ID」に先程作成した証明書のドメイン名を設定します。 f:id:WorldWorldWorld:20170205205119p:plainf:id:WorldWorldWorld:20170205205120p:plainElastic Beanstalkマネジメントコンソール

これでアプリ側の設定は完了です。

Route53によるドメインの設定

最後にSSL適用ドメイン(wackwack.net)を構築済みのElastic Beanstalkアプリケーションに適用します。

これにはAWSRoute53を使います。
aws.amazon.com

Route 53はAWS上で動くDNSの総括的なサービスです。ドメインの取得からネームサーバの構築まで何でもござれ!

ただしこれもやはり従量課金制(月10億クエリまでで0.01$)であり、かつ無料期間に使えるサービスの対象外です。利用には注意しましょう。

ネームサーバの作成

初めにRoute 53からドメインのネームサーバを作成します。

Route 53のマネジメントコンソールを開き「Hosted Zones」を選択します。
Route53 Hosted zones

「CreateHostedZones」をクリックし登録するドメイン名を入力します。 f:id:WorldWorldWorld:20170205205124p:plain

「NS」レコードと「SOA」レコードが表示されていればネームサーバの作成は完了です。 Route53 ドメインの設定

ドメインのネームサーバを変更する

今回は既存のドメインAWSで使うためドメインのネームサーバーを先ほど作成したRoute53のネームサーバーに変更する必要があります。

ネームサーバの変更は各ドメイン運営会社のサイトから実施しますが今回は「お名前.com」の例を示します。

お名前.comのドメインNaviから「ドメイン設定」と進み対象のドイメイン(wackwack.net)にチェックを入れ「他のネームサーバを利用」を選択します。
お名前.com ネームサーバーの変更

「ネームサーバー情報の入力」欄に、Route 53から作成したNSレコードのアドレスを全て入力して登録は完了です。

これで「wackwack.net」ドメインにアクセスが発生するとRoute 53のネームサーバーにリクエストが送信され、そこから名前解決が行われます。
※ネームサーバーの設定変更は環境によって24時間〜72時間かかる可能性があります。

エイリアスレコードの登録

最後にエイリアスレコードを登録します。これにより特定のwackwack.netドメインへのリクエストをElastic Beanstalkで構築したEC2へ転送されます。

Route 53のマネジメントコンソールより先ほど登録したドメインのレコード一覧を開きます。

そこから「Create Record」をクリックし、次のとおり入力をしてエイリアスレコードを作成します。

  • Name:任意の名前(これが最終的な完全修飾ドメイン名になります)
  • Alias:Yes
  • Type:A
  • Alias Target:Elastic BeanstalkのURL(選択肢に自動で表示されます) Route 53 エイリアスレコード

これで準備はオールオッケー!

動作確認

登録した完全修飾ドメイン名にHTTPSでアクセスしてみると……
Elastic Beanstalk HTTPSアクセス

きちんとアクセスできました。サーバー証明書のチェーンを確認するとAmazonから発行された証明書が使われていることが分かります。 Route 53 サーバ証明書の確認


以上、Elastic Beanstalkでのアプリケーション作成からAWSCertificate Managerでのサーバー証明書作成、Route 53でのネームサーバーとエイリアスレコードの設定まで紹介しました。

長々とした手順に見えますが、実際はあっという間にできてしまいます。AWS、便利すぎますね。

ただし手軽さと可用性・スケーラビリティに優れているものの、従量課金制ということもあって個人的にコストはやや高めかなと思うので、個人が利用するにはよく考えてみましょう。

Amazon Web Services実践入門 (WEB+DB PRESS plus)

Amazon Web Services実践入門 (WEB+DB PRESS plus)