RailsアプリをAWSで公開する(Rails+Unicorn+Nginx)

railsで作った簡単なチャットアプリをAWSのEC2+RDSで公開してみました。今回はその手順を自分の備忘録にメモ。

AWS側の下準備

VPCの作成

VPCとは、Virtual Private Cloudの略で、AWS上の中で自分の使う領域となります。この自分の領域の中に、サーバーやDBなど、アプリケーションを動かすにの必要なものを作っていきます。

以下で設定しました

・ネームタグ: VPC_for_アプリ名
・CIDRブロック:10.0.0.0/16
・テナンシー:デフォルト

※1 CIDRブロック:このVPCが使用できるIPの範囲を決める(10.0.0.0/28(16IPアドレス) ~ 10.0.0.0/16(65,536IPアドレス)まで選択可能)
※2 テナンシー: VPCを作る際にハードウェアを占有するかどうかを選択できる(占有すると追加料金がかかる)

サブネットの作成

サブネットとは、通信グレープ分けしてくれるものです。RDSでサブネットグループを作る必要があるので、ここで2つのサブネットを作成します。

・ネームタグ: アプリ名-Subnet1
・VPC: VPC_for_アプリ名
・アベイラビリティゾーン: 「ap-northeast-1a」
・CIDRブロック: 10.0.0.0/24
・ネームタグ: アプリ名-Subnet2
・VPC: VPC_for_アプリ名 
・アベイラビリティゾーン: 「ap-northeast-1c」
・CIDRブロック: 10.0.1.0/24

インターネットゲートウェイの作成

インターネットゲートウェイとは、 「インターネットの入り口」 です。作成したVPCはAWS内での占有領域ですので、外部との接続を行う際は、その領域から外に出ていかなければなりません。その入り口となるのが、インターネットゲートウェイです。

・ネームタグ: Gateway_for_アプリ名
・作成した項目を選択し「VPCにアタッチ」
・「VPCforアプリ名」を選択して「アタッチ」

ルートテーブルの作成

ルートテーブルとは、 通信に関するルールブックみたいなものです。

・ネームタグ: Table_for_アプリ名

このルートテーブルを、VPCと紐付ける

そしてこのルートテーブルに、「インターネットゲートウェイをルーティングするというルール」を記載していきます。

作成したルートテーブルを選択した状態で、下タブの「ルート」を選択し、「別ルートを追加」を選択します。

・送信先: 0.0.0.0/0 
・ターゲット: Gateway_for_アプリ名

サブネットとルートテーブルの関連付け

1.「アプリ名-Subnet-1a」を選択
2.下部のタブの「ルートテーブル」を選択
3.「編集」
4.「変更先」を「Tableforアプリ名」に変更
5.「保存」

6.「アプリ名-Subnet-1c」を選択
2-5と同様の操作を行う

セキュリティーグループの作成

セキュリティグループは、セキュリティーに関するルールを記載したものです。「この通信は許可」「この通信は拒否」といった具合にグループ毎にルールを設定できます。

・ネームタグ: アプリ名-SecurityGroup
・グループ名: アプリ名-SecurityGroup
・説明: SecurityGroup_for_アプリ
・VPC: VPC_for_アプリ名

設定には「インバウンドルール」「アウトバウンドルール」という2種類の項目が存在します。

・インバウンドルール: どんな通信が来たら許可するのか
・アウトバウンドルール: どんな通信を送ると許可されるのか
  • インバウンドルール
タイプは「SSH(22)」、送信元は「アプリ名-SecurityGroup」を選択
「別のルールの追加」
タイプは「HTTP(80)」、送信元は「0.0.0.0/0」
「ルールの保存」

これで、AWS側の下準備が完了です。

サブネットグループの作成

名前に「アプリ名_DB-Subnet-Group」
説明に「DB Subnet Group for アプリ名」
VPC IDに「VPCforアプリ名」
「すべてのサブネットを追加」をクリック
自動的に追加されたことを確認し「作成」

RDSインスタンスの作成

RDSとは、Relational Database Serviceの略であり、簡単に言ってしまえばDBのことです。

まずは、RDSインスタンスを作成に必要な「サブネットグループ」を作成していきます。

Top画面 → RDS → インスタンス → DBインスタンスの起動を選択します。

はじめに「本番用のDBかどうか」が聞かれます。無料枠で抑えたい方はテンプレートで「無料利用枠」を選択しましょう。

・DBエンジン:Mysql(選択済)
・DBインスタンスのクラス:t2 micro
・マルチAZ配置:いいえ
・ストレージタイプ:汎用(SSD)
・DBインスタンスの識別子: アプリ名-mysql
・マスターユーザーの名前: 任意
・パスワード: 任意

# ネットワーク&セキュリティ
 ・VPC: VPC_for_アプリ名
 ・サブネットグループ: アプリ名-SubnetGroup
 ・パブリックアクセス可能:いいえ
 ・アベイラビリティゾーン:指定なし
 ・セキュリティグループ: アプリ名-SecurityGroup

# データベースの名前
 ・データベースの名前:空白
 ・データベースのポート:3306
 ・DBパラメーターグループ:default:mysql5.6
 ・オプショングループ:default:mysql5.6
 ・タグをスナップショットにコピー:チェック無し

# バックアップ
 ・バックアップの保存期間: 1日
 ・バックアップウィンドウ(バックアップを実行する時間):指定なし

# モニタリング
 ・拡張モニタリングを有効にする(DB監視・分析レポートの、拡張版を利用するかどうか): いいえ

# メンテナンス
 ・マイナーバージョンの自動アップグレード: はい
 ・メンテナンスウィンドウ(メンテナンス発生時に、それを実行する時間を指定): 指定なし

EC2インスタンスの作成

EC2とは、Elastic Compute Cloudの略で、クラウド上に存在する仮想のサーバーです。

早速、Top→EC2→インスタンス→インスタンスの起動を選択します。

はじめに、AMIを選択する画面へと移行します。

AMIとは、Amazon Machine Imageの略で、今作成しようとしているパソコン(EC2)のタイプを選択するものです。今回は、無料枠の範囲内で、一番上のAmazon Linuxを利用していきましょう。

次は、EC2インスタンスのタイプ選択です。「無料枠」のインスタンスタイプを選択して次の設定をします。

・インスタンス数:1(作成するインスタンスの数を選択)
・スポットインスタンスのリクエスト:チェックなし(後述)
・ネットワーク:VPC_for_アプリ名
・サブネット:アプリ名-Subnet1
・自動割り当てパブリックIP:有効化
・IAMロール:なし(後述)
・シャットダウン動作(EC2をシャットダウンした際に、削除するのか、それとも停止させるだけなのかを選択):停止
・削除保護の有効化(インスタンスの削除を禁止するかどうか):チェック
・モニタリング:チェックなし
・テナンシー(ハードウェアを占有するか、それとも他の利用者と共有するかを選択):共有

ストレージの追加は行わずそのまま「次の手順」

「タグの追加」を選択し、キーに「Name」、値に「アプリ名」を入力し「次の手順」
「既存のセキュリティグループを選択する」にチェックを入れ「アプリ名-SecurityGroup」を選択
「確認と作成」「起動」
「新しいキーペアの作成」を選択し、キーペア名に「アプリ名」
「キーペアのダウンロード」 

Elastic IPの作成、紐付け。

インターネットに接続を行う場合、どのサーバーも例外なくグローバルIPというものを持たなければなりません。先ほど作成したEC2インスタンスにも作成時にパブリックIPが自動で割り振られるのですが、サーバーを再起動させるたびにこのパブリックIPが変わってしまうという欠点を持っています。そこで、ElasticIPを使用し、先に固定のIPアドレスを他に使用されないように押さえてしまい、それをパブリックIPとしてEC2インスタンスに紐付けることで、インスタンスの起動、停止に関わらず常に同じIPで通信をしようというものです。

「新しいアドレスの割り当て」から「割り当て」を実行
成功したら「閉じる」
作成されたIPを選択し、「アクション」から「アドレスの関連付け」を選択
インスタンスに作成したインスタンス名(手順通りだとアプリ名のはず)を選択
「関連付け」

インスタンスにSSHでログイン

awscliの導入がまだの方は、下準備でこれらをインストールしていきます

$ sudo brew install python
$ sudo easy_install pip
$ sudo pip install awscli

AWSで設定した「セキュリティグループ」から作成した項目を選択し、下部のタブの「インバウンド」を選択

1. 「セキュリティグループ」>「インバウンド」の SSH を「マイIP」に変更し「保存」
2. 「キーペアのダウンロード」で保存したpemファイルを ~/.ssh/ へ移動
 $ mv Downloads/アプリ名.pem .ssh/
3. 権限を変更
 $ cd ~/.ssh/ && chmod 400 "アプリ名.pem"
4. SSHログイン
 $ ssh -i "アプリ名.pem" ec2-user@割り当てたIPアドレス
5. > 「yes」
 ターミナルにEC2マークが表示されたら成功

「割り当てたIPアドレス」はEC2ダッシュボードのElastic IPから確認できます。

接続できない場合は、「セキュリティグループ」の「インバウンド」で、自分の通信アドレスでsshを許容してあげてください。

サーバーをアップデート
 $ sudo yum update
 Nginxをインストール
 $ sudo yum install -y nginx
 起動
 $ sudo /etc/init.d/nginx start
 管理ユーザーの作成
 ユーザーを作成
 $ sudo adduser ユーザー名
 マスター権限を付与
 $ sudo usermod -G wheel ユーザー名
 パスワードを設定
 $ sudo passwd ユーザー名
 設定を編集
 $ sudo visudo
 viが起動するので98行目あたりの %wheel ALL=(ALL) ALL のコメントアウト解除
 保存して終了(:x)
 認証キーをユーザーディレクトリへ移動
 $ sudo rsync -a ~/.ssh/authorized_keys ~ユーザー名/.ssh/
 オーナーとグループを変更
 $ sudo chown -R ユーザー名r:ユーザー名r ~ユーザー名r/.ssh
 読み出し、書き込み、実行の許可を取り去ります
 $ sudo chmod -R go-rwx ~ユーザー名/.ssh
 ログインチェックのため一旦ログアウト
 $ exit
 ログインするユーザー名を変更し、再度sshでログイン
 $ ssh -i "アプリ名.pem" ユーザー名@割り当てたIPアドレス
 セキュリティー強化のため、ec2-userからアクセスできないようにします。
 $ sudo vi /etc/ssh/sshd_config
 ファイルの末尾に追記します。
 # ec2-userでのログインを禁止
 DenyUsers ec2-user
 設定を反映させます。
 $ sudo service sshd reload
 ailsアプリを配置する作業用のディレクトリを作成します。
 $ sudo mkdir /var/www
 varに移動します。
 $ cd /var/
 オーナーを変更し、パスワードを入力します。
 $ sudo chown アプリ名 www

プラグインの導入

以下のコマンドを実行し、インストールします。

$ sudo yum install git make gcc-c++ patch openssl-devel libyaml-devel libffi-devel libicu-devel libxml2 libxslt libxml2-devel libxslt-devel zlib-devel readline-devel ImageMagick ImageMagick-devel epel-release

mysql 5.6をインストールする
 $ sudo yum install http://dev.mysql.com/get/mysql-community-release-el6-5.noarch.rpm

インストールします。
 $ sudo yum install mysql mysql-devel mysql-server mysql-utilities

node.jsをインストールします。
 $ sudo yum install nodejs npm --enablerepo=epel

rbenvをインストールします。
 $ git clone https://github.com/sstephenson/rbenv.git ~/.rbenv
 $ echo 'export PATH="$HOME/.rbenv/bin:$PATH"' >> ~/.bash_profile
 $ echo 'eval "$(rbenv init -)"' >> ~/.bash_profile
 $ source ~/.bash_profile

ruby-build をインストール
 $ git clone https://github.com/sstephenson/ruby-build.git ~/.rbenv/plugins/ruby-build
 $ rbenv rehash

Ruby 2.5.1 をインストール
 $ rbenv install -v 2.5.1
 $ rbenv global 2.5.1
 $ rbenv rehash
 $ ruby -v

Githubと連携

gitの設定ファイルを作成します
 $ vi ~/.gitconfig
 設定ファイルに以下を記載します。
 [user]
   name = Githubのユーザー名
   email = Githubに登録しているメールアドレス
 [color]
   ui = true
 [url "github:"]
   InsteadOf = https://github.com/
   InsteadOf = git@github.com:
 www配下にRailsを格納するフォルダを作成します。
 $ cd /
 $ sudo chown test_user var
 $ cd /var/www/
 $ sudo mkdir projects
 $ sudo chown test_user projects
 鍵を作成
 $ cd
 $ chmod 700 .ssh
 $ cd .ssh
 $ ssh-keygen -t rsa
 Generating public/private rsa key pair.
 Enter file in which to save the key (/home/test_user/.ssh/id_rsa): aws_git_rsa
 # パスワードを入力
 Enter passphrase (empty for no passphrase):
 Enter same passphrase again:
 鍵を確認します。
 $ ls
 authorized_keys  aws_git_rsa  aws_git_rsa.pub
 設定ファイルを作成します。
 $ vi config
 以下を記載します。
 Host github
   Hostname github.com
   User git
   IdentityFile ~/.ssh/aws_git_rsa
 暗号を確認します。
 $ cat aws_git_rsa.pub
 ssh-rsa 鍵の暗号が表示
 Githubに鍵を追加
 Githubにアクセスします。
 Settings > SSH and GPG keys > 「New SSH key」
 Title: アプリ名 -EC2
 Key: [cat aws_git_rsa.pub で表示された暗号(ssh-rsaも含める)]
 設定ファイルの権限を変更します。
 $ chmod 600 config
 Githubの接続を確認します。
 $ ssh -T github

RDSの確認

RDSの「インスタンス」から「エンドポイント」を確認し、どこかにコピーしておく
 「アプリ名-mysql.cp2gudorjg9e.ap-northeast-1.rds.amazonaws.com」
 EC2の「セキュリティグループ」から作成した項目を選択し、「グループID」をコピー
 下部のタブの「インバウンド」を選択し、「編集」をクリック
 「ルールの追加」をクリック、タイプは「MYSQL/Aurora」を選択、ソースに先ほどコピーした「グループID」を入力
 「保存」
 $ mysql -h エンドポイント -P 3306 -u root -p
 パスワードを聞かれたらRDSのパスワードを入力
 入れない時は、以下を検討する
 ・EC2の「セキュリティグループ」>「インバウンド」に「MYSQL/Aurora」で「グループID」は登録されているか?
 ・RDSのマスターパスワードは設定されているか?(RDSの「変更」から設定可能)
 MySQLへログインできたらデータベースを確認しておく
 mysql> show databases;
 # データベース一覧が表示され、アプリ名_production が存在していればOK
 mysql> exit;

パラメータグループの作成

RDS > パラメータグループ >「パラメータグループ作成」

パラメーターグループファミリー: mysql5.6
 グループ名: アプリ名
 説明: DB Prameter Group for kapi-tech
 「作成」
 「パラメータの編集」
 character_set_client: utf8
 character_set_connection: utf8
 character_set_database: utf8
 character_set_filesystem:
 character_set_results: utf8
 character_set_server: utf8

GitHubからRailsアプリをクローン

作成したprojectsへ移動
 $ cd /var/www/projects
 bundlerをインストール
 $ gem install bundler
 Gemfileを作成
 $ bundle init
 Gemfileを編集
 $ vim Gemfile
 Railsバージョンを記載
 gem "rails", '5.1.4'
 vendor/bundleへgemをインストール
 $ bundle install --path vendor/bundle --jobs=4
 $ bundle exec rails -v
 # Rails 5.1.4

 GitHubからアプリをクローン
 $ git clone git@github.com:GitHubのユーザー名/リポジトリ名.git
 $ ls
 # リポジトリ名  Gemfile  Gemfile.lock  vendor

 クローンしたディレクトリへ移動
 $ cd リポジトリ名/

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

 シークレットを生成し、どこかにコピーしておく
 $ bundle exec rake secret

 数字が表示
 設定ファイルに記載します。
 $ vi config/secrets.yml
----------------------------------
 production:
   secret_key_base: ここにsecretを貼り付ける
----------------------------------

nginxの設定

  • 設定ファイルを作成します。
$ sudo vi /etc/nginx/conf.d/アプリ名.conf
以下を記載します。

 --------------------------------------------------------------------------
 upstream unicorn_server {
     server unix:/var/www/projects/アプリ名/tmp/sockets/.unicorn.sock
     fail_timeout=0;
 }
 server {
     listen 80;
     client_max_body_size 4G;
     server_name IPアドレス;
     keepalive_timeout 5;
     # Location of our static files
     root /var/www/projects/アプリ名/public;
     location ~ ^/assets/ {
         root /var/www/projects/アプリ名/public;
     }
     location / {
         proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
         proxy_set_header Host $http_host;
         proxy_redirect off;
         if (!-f $request_filename) {
             proxy_pass http://unicorn_server;
             break;
         }
     }
     error_page 500 502 503 504 /500.html;
     location = /500.html {
         root /var/www/projects/アプリ名/public;
     }
 }
 --------------------------------------------------------------------------

  • Nginxをリスタートします
$ sudo service nginx restart
 Stopping nginx:                                            [  OK  ]
 Starting nginx:                                            [  OK  ]

Unicornの設定

Gemfileを編集します。

$ vim Gemfile
 以下をファイルの末尾に追記します。
------------------------
 group :production do
     gem 'unicorn'
 end
------------------------

 インストールします。
 $ bundle install

 unicornの設定ファイルを作成します。
 $ vim config/unicorn.conf.rb
 以下を記載します。
 --------------------------------------------------------------------------
 # set lets
 $worker  = 2
 $timeout = 30
 $app_dir = "/var/www/projects/アプリ名"
 $listen  = File.expand_path 'tmp/sockets/.unicorn.sock', $app_dir
 $pid     = File.expand_path 'tmp/pids/unicorn.pid', $app_dir
 $std_log = File.expand_path 'log/unicorn.log', $app_dir
 # set config
 worker_processes  $worker
 working_directory $app_dir
 stderr_path $std_log
 stdout_path $std_log
 timeout $timeout
 listen  $listen
 pid $pid
 # loading booster
 preload_app true
 # before starting processes
 before_fork do |server, worker|
   defined?(ActiveRecord::Base) and ActiveRecord::Base.connection.disconnect!
   old_pid = "#{server.config[:pid]}.oldbin"
   if old_pid != server.pid
     begin
       Process.kill "QUIT", File.read(old_pid).to_i
     rescue Errno::ENOENT, Errno::ESRCH
     end
   end
 end
 # after finishing processes
 after_fork do |server, worker|
   defined?(ActiveRecord::Base) and ActiveRecord::Base.establish_connection
 end
 --------------------------------------------------------------------------
権限を変更します。
$ sudo chmod -R 775 /var/lib/nginx/
  • database.ymlの設定

Ruby アプリケーション環境に Amazon RDS DB インスタンスを追加

念のために database.yml をコピーします
 $ cp config/database.yml config/database.yml.org
 $ vim config/database.yml
 以下を記載します。
-------------------------------
 $ production:
   <<: *default
   adapter: mysql2
   encoding: utf8
   database: データベース名
   username: root
   password: 設定したパスワード
   host: RDSエンドポイント
   port: 3306
-------------------------------

 mysqldを起動します。
 $ sudo service mysqld start
 Starting mysqld:                                           [  OK  ]

 production環境でDB作成・マイグレーションを実行
 $ bundle exec rake db:create db:migrate RAILS_ENV=production

ロードバランサーを設定する

Elastic Load Balancing を利用してロードバランサーの設定を行います。

EC2の「ロードバランサー」から「ロードバランサーの作成」をクリック

Classic Load Balancerで「作成」

ロードバランサー名に「アプリ名-ELB」、ロードバランサーをを作成する場所に「VPCforアプリ名」

利用可能なサブネットの「アクション」の「+」マーク2つをクリックして選択済みへ移動させ、「次の手順」をクリック

セキュリティグループは「アプリ名-SecurityGroup」を選択し「次の手順」

セキュリティ設定の構成は何もせず「次の手順」

ヘルスチェックの設定はpingパスを「/」、間隔を「10」、正常のしきい値を「5」に設定し、「次の手順」 ※他はデフォルトでOK

EC2インスタンスの追加は作成したインスタンスを選択し「次の手順」

タグの追加ではキーに「Name」、値に「アプリ名-Webserver」と入力

「確認と作成」からの「作成」からの「閉じる」

一覧画面から作成したロードバランサーを選択し、下部のタブの「インスタンス」をクリック、状態が「InService」なら完了。「OutOfService」の場合は何か問題がある

Railsアプリの起動

長い工程を経てやっとアプリの起動です。

EC2へSSHでログインし、Railsアプリをプリコンパイルします
 $ bundle exec rake assets:precompile RAILS_ENV=production
 Nginxを再起動
 $ sudo service nginx restart
 Unicornを起動
 $ bundle exec unicorn_rails -c /var/www/projects/アプリ名/config/unicorn.conf.rb -D -E production
 Uniconの起動を確認
 $ ps -ef | grep unicorn | grep -v grep
 # プロセスのリストが3行程表示されればOK

ブラウザからIPを叩いてアクセス ※IPアドレスがわからない場合はEC2のインスタンスの説明から確認可能

http://IP
アドレス/

Railsアプリが無事動作すれば成功

① nginxを停止する
 $ sudo service nginx stop

 ② unicorn のプロセスを終了させる
 kill -QUIT `cat tmp/pids/unicorn.pid`

 ③ Unicornの起動確認
 ps -ef | grep unicorn | grep -v grep

 (何も出ないことを確認)
 ④ assets/ にコンパイル
 $ bundle exec rake assets:precompile RAILS_ENV=production

 ⑤ unicorn起動
 $ bundle exec unicorn_rails -c /var/www/projects/アプリ名/config/unicorn.conf.rb -D -E production

 ⑥ nginxを起動する
 $ sudo service nginx start

 ⑦ 以下のURLをブラウザで確認する 
http://[EC2のIPアドレス]

※ ElasticIPは、EC2のダッシュボードで確認できる

EC2のダッシュボードで、Elastic IPを確認できる

コメント