2018年1月1日に「個人でWebサービス開発します!」と宣言して今日でまるっと一ヶ月経ちました。
それっぽいものは作れたので、公開に向けてあとひと押しというところです。

で、そのひと押しの中に「サーバーにデプロイする」という工程がありまして、最初はHerokuで全部やってしまおうと考えていたのですが、リージョンが米国なので通信速度が出なかったりDyno再起動すると画像が消えたりして非常に辛い思いをしたのでAWSで構築することにしました。

料金体制とか不安なところはありますが、今後必要なる知識だと思うので思い切ってトライ。
実装手順などをメモ代わりに残しておこうと思います。

STEP1 ネットワーク環境設定

まず最初にネットワーク周りやセキュリティ周りの設定を行います。

VPCの作成

  1. ネームタグに「VPCforアプリ名」
  2. IPv4 CIDR blockに「10.0.0.0/16」
  3. その他デフォルトで「作成」

サブネットの作成

  1. ネームタグに「アプリ名-Subnet-1a」
  2. VPCに先ほど作成した「VPCforアプリ名」
  3. アベイラビリティーゾーンに「ap-northeast-1a」
  4. IPv4 CIDR blockに「10.0.0.0/24」
  5. その他デフォルトで「作成」

RDS用にもう一つサブネットを作成。
アベイラビリティーゾーンを「ap-northeast-1c」に変える
※ネームタグ、IPv4 CIDR blockも変更されることを確認

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

  1. ネームタグに「Gatewayforアプリ名」
  2. 「作成」
  3. 作成した項目を選択し「VPCにアタッチ」
  4. VPCは「VPCforアプリ名」を選択して「アタッチ」

ルートテーブルの作成

  1. ネームタグに「Tableforアプリ名」
  2. VPCは「VPCforアプリ名」を選択して「作成」
  3. 下部のタブの「ルート」を選択
  4. 「編集」から「別ルートの追加」を選択
  5. 送信先に「0.0.0.0/0」
  6. ターゲットを「Gatewayforアプリ名」を選択
  7. 「保存」

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

  1. 「アプリ名-Subnet-1a」を選択
  2. 下部のタブの「ルートテーブル」を選択
  3. 「編集」
  4. 「変更先」を「Tableforアプリ名」に変更
  5. 「保存」
  6. 「アプリ名-Subnet-1c」を選択
  7. 2-5と同様の操作を行う

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

  1. ネームタグに「アプリ名-SecurityGroup」
  2. グループ名に「アプリ名-SecurityGroup」
  3. 説明に「Security for アプリ名」
  4. VPCは「VPCforアプリ名」
  5. 「作成」
  6. 下部のタブの「インバウンドルール」を選択
  7. 「編集」
  8. タイプは「SSH(22)」、送信元は「アプリ名-SecurityGroup」を選択
  9. 「別のルールの追加」
  10. タイプは「HTTP(80)」、送信元は「0.0.0.0/0」
  11. 「保存」

STEP2 RDSの設定

簡単に言うとデータベースです。

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

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

DBインスタンスの作成

  1. 「DBインスタンスの起動」
  2. 好きなDBエンジンを選択 ※今回はMySQL
  3. DBエンジンのバージョンに「MySQL 5.7.19」、DBインスタンスクラスに「db.t2.micro」、マルチAZ配置を「いいえ」、ストレージタイプを「汎用(SSD)」に設定
  4. DBインスタンス識別子に「アプリ名-mysql」
  5. マスターユーザ、マスターパスワードを設定 ※よしなに
  6. 「次のステップ」
  7. VPCに「VPCforアプリ名」、サブネットグループに「アプリ名_DB-Subnet-Group」、パブリックアクセス「いいえ」、アベイラビリティーゾーン「指定なし」、VPCセキュリティグループに「アプリ名-SecurityGroup」
  8. データベースの名前に「アプリ名_production」、他はデフォルトでOK
  9. バックアップの保存期間を「1日」、他はデフォルトでOK
  10. 「DBインスタンスの作成」

ステータスが「利用可能」になるまでしばらく待ちましょう。

STEP3 EC2の設定

アプリケーションサーバーの中枢となる部分です。

インスタンスの作成

  1. リージョンが「アジアパシフィック(東京)」であることを確認
  2. 「インスタンスの作成」から「Amazon Linux」を選択
  3. インスタンスタイプは「t2.micro」を選択し「次の手順」
  4. ネットワークに「VPCforアプリ名」
  5. サブネットに「アプリ名-Subnet-1c」
  6. 念のため削除保護の有効化にチェック入れて「次の手順」
  7. ストレージの追加は行わずそのまま「次の手順」
  8. 「タグの追加」を選択し、キーに「Name」、値に「アプリ名」を入力し「次の手順」
  9. 「既存のセキュリティグループを選択する」にチェックを入れ「アプリ名-SecurityGroup」を選択
  10. 「確認と作成」
  11. 「起動」
  12. 「新しいキーペアの作成」を選択し、キーペア名に「アプリ名」
  13. 「キーペアのダウンロード」 ※あとで使う
  14. 「インスタンスの作成」からの「インスタンスの表示」

Elastic IPの割り当て

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

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

  1. 「セキュリティグループ」から作成した項目を選択し、下部のタブの「インバウンド」を選択
  2. 「編集」からタイプ「SSH」のソースを「マイIP」に変更 ※現在のグローバルIP以外のSSHログインを制限できる
  3. 「保存」
  4. 「キーペアのダウンロード」で保存したpemファイルを ~/.ssh/ へ移動
  5. 権限を変更

    1
    $ cd ~/.ssh/ && chmod 400 "アプリ名.pem"
  6. 「インスタンス」から作成したインスタンス項目上で右クリックし「接続」を選択

  7. 「例:」と書かれた箇所にある ssh -i "アプリ名.pem" ec2-user@割り当てたIPアドレス をコピーしターミナルへ貼り付け、実行

    1
    $ ssh -i "アプリ名.pem" ec2-user@割り当てたIPアドレス
  8. なんかyesかnoか聞かれたら「yes」と入力

  9. EC2のアスキーアートが表示されたらSSHログイン完了

STEP4 サーバー環境を構築する

EC2にログインできたらいよいよNginxやRuby、MySQLなどをインストールしていきます。

Nginxの設定

  1. サーバーをアップデート

    1
    $ sudo yum update
  2. Nginxをインストール

    1
    $ sudo yum install -y nginx
  3. 起動

    1
    $ sudo /etc/init.d/nginx start

管理ユーザーの作成

  1. ユーザーを作成

    1
    $ sudo adduser ユーザー名
  2. でマスター権限を付与

    1
    $ sudo usermod -G wheel ユーザー名
  3. パスワードを設定

    1
    $ sudo passwd ユーザー名
  4. 設定を編集

    1
    $ sudo visudo
  5. viが起動するので98行目あたりの %wheel ALL=(ALL) ALL のコメントアウト解除

  6. 保存して終了(:x)
  7. 認証キーをユーザーディレクトリへ移動

    1
    $ sudo rsync -a ~/.ssh/authorized_keys ~ユーザー名/.ssh/
  8. オーナーとグループを変更

    1
    $ sudo chown -R ユーザー名r:ユーザー名r ~ユーザー名r/.ssh
  9. 各権限を変更

    1
    $ sudo chmod -R go-rwx ~ユーザー名/.ssh
  10. ログインチェックのため一旦ログアウト

    1
    $ exit
  11. 再度ログイン

    1
    $ ssh -i "アプリ名.pem" ユーザー名@割り当てたIPアドレス
  12. EC2のアスキーアートが表示されたら作成したユーザーでのSSHログイン完了

※セキュリティ向上のためデフォルトのユーザー「ec2-user」でログインできないようにすると良いみたいです。ログイン後、 $ sudo vi /etc/ssh/sshd_config で設定ファイルを開き、ファイル末尾に

1
2
# ec2-userでのログインを禁止
DenyUsers ec2-user

を追記し $ sudo service sshd reload で反映します。

ドキュメントルート領域を作成

  1. var配下にwwwディレクトリを作成

    1
    $ sudo mkdir /var/www
  2. varへ移動

    1
    $ cd /var/
  3. でオーナーを変更

    1
    $ sudo chown ユーザー名 www

プラグインのインストール

  1. 下記コマンドを実行

    1
    $ 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
  2. MySQL 5.7 をインストール

    1
    2
    3
    $ sudo rpm -ivh http://dev.mysql.com/get/mysql57-community-release-el7-11.noarch.rpm
    $ sudo yum install mysql-devel mysql57 mysql57-server
  3. Node.js 6x をインストール

    1
    $ curl -sL https://rpm.nodesource.com/setup_6.x | sudo bash -
  4. Yarn をインストール

    1
    $ sudo npm install yarn -g
  5. rbenv をインストール

    1
    2
    3
    4
    $ 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
  6. ruby-build をインストール

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

    1
    2
    3
    4
    $ rbenv install -v 2.3.3
    $ rbenv global 2.3.3
    $ rbenv rehash
    $ ruby -v

MySQLをセットアップする

  1. RDSの「インスタンス」から「エンドポイント」を確認し、どこかにコピーしておく

    1
    2
    # こんな感じのやつ
    アプリ名-mysql.cp2gudorjg9e.ap-northeast-1.rds.amazonaws.com
  2. EC2の「セキュリティグループ」から作成した項目を選択し、「グループID」をコピー

  3. 下部のタブの「インバウンド」を選択し、「編集」をクリック
  4. 「ルールの追加」をクリック、タイプは「MYSQL/Aurora」を選択、ソースに先ほどコピーした「グループID」を入力
  5. 「保存」
  6. ターミナルからEC2へログインし、MySQLへログイン ※1で取得したエンドポイント値を利用

    1
    $ mysql -h エンドポイント -P 3306 -u root -p
  7. パスワードを聞かれたらRDSのパスワードを入力

  8. MySQLへログインできたらデータベースを確認しておく
    1
    2
    3
    4
    mysql> show databases;
    # データベース一覧が表示され、アプリ名_production が存在していればOK
    mysql> exit;

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

  1. RDSの「パラメータグループ」を選択、「パラメータグループの作成」をクリック
  2. パラメータグループファミリーは「mysql5.7」、グループ名は「アプリ名」、説明は「DB Parameter Group for アプリ名」など
  3. 「作成」
  4. 作成したパラメータグループを選択し、「パラメータの編集」をクリック
  5. 「フィルタパラメータ」に「character_set」と入力すると照合順序系のパラメータがヒットするので、右上の「パラメータの編集」をクリックし任意の値に設定を変更し保存する

    1
    2
    3
    4
    5
    character_set_client utf8mb4
    character_set_connection utf8mb4
    character_set_database utf8mb4
    character_set_results utf8mb4
    character_set_server utf8mb4
  6. 「フィルタパラメータ」から「skip-character-set-client-handshake」を検索して値を変更する

    1
    skip-character-set-client-handshake 1

STEP5 Railsアプリをデプロイする

GitHubにあるRailsアプリをEC2のNginxへデプロイします。

GitHubと連携する

  1. Gitの設定ファイルを編集

    1
    $ vi ~/.gitconfig
  2. 以下を追記して保存

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    [user]
    name = Githubのユーザー名
    email = Githubに登録しているメールアドレス
    [color]
    ui = true
    [url "github:"]
    InsteadOf = https://github.com/
    InsteadOf = git@github.com:
  3. 鍵を作成、鍵名は「aws_git_rsa」、パスワードは空欄

    1
    2
    3
    4
    $ cd
    $ chmod 700 .ssh
    $ cd .ssh
    $ ssh-keygen -t rsa
  4. 設定ファイルを作成

    1
    $ vi config
  5. 以下を追記して保存

    1
    2
    3
    4
    Host github
    Hostname github.com
    User git
    IdentityFile ~/.ssh/aws_git_rsa
  6. 鍵の内容を確認。どこかにコピーしておく

    1
    $ cat aws_git_rsa.pub
  7. GitHubへブラウザからアクセス、「Settings」の「SSH and GPG keys」を選択

  8. 「New SSH key」をクリック、Titleに「アプリ名 - EC2」、Keyに「先ほど作成した鍵の内容」を入力し「Add SSH key」で登録
  9. ターミナルへ戻り設定ファイルの権限を変更

    1
    $ chmod 600 config
  10. GitHubへの接続確認。途中の質問にはYesで。Githubのユーザー名が出てくれば成功

    1
    2
    3
    $ ssh -T github
    # Hi Githubのユーザー名! You've successfully authenticated, but GitHub does not provide shell access.

アプリケーション格納用の領域を作成

  1. www配下にRailsアプリを格納するディレクトリを作成
    1
    2
    3
    4
    5
    $ cd /
    $ sudo chown ユーザー名 var
    $ cd /var/www/
    $ sudo mkdir projects
    $ sudo chown ユーザー名 projects

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

  1. 作成したprojectsへ移動

    1
    $ cd /var/www/projects
  2. bundlerをインストール

    1
    $ gem install bundler
  3. Gemfileを作成

    1
    $ bundle init
  4. Gemfileを編集

    1
    $ vim Gemfile
  5. Railsバージョンを記載

    1
    gem "rails", '5.1.4'
  6. vendor/bundleへgemをインストール

    1
    2
    3
    4
    $ bundle install --path vendor/bundle --jobs=4
    $ bundle exec rails -v
    # Rails 5.1.4
  7. GitHubからアプリをクローン

    1
    2
    3
    4
    $ git clone git@github.com:GitHubのユーザー名/リポジトリ名.git
    $ ls
    # リポジトリ名 Gemfile Gemfile.lock vendor
  8. クローンしたディレクトリへ移動

    1
    $ cd リポジトリ名/
  9. gemインストール

    1
    $ bundle install --path vendor/bundle
  10. シークレットを生成し、どこかにコピーしておく

    1
    $ bundle exec rake secret
  11. .bash_profileを編集する

    1
    $ sudo vi ~/.bash_profile
  12. Railsの環境変数として先ほどのシークレットを末尾に記載する

    1
    export SECRET_KEY_BASE="生成したシークレットの値"
  13. 正しく設定されているか確認

    1
    2
    3
    $ echo $SECRET_KEY_BASE
    # 生成したシークレットの値が表示される

nginxの設定

  1. 設定ファイルを作成

    1
    $ sudo vi /etc/nginx/conf.d/アプリ名.conf
  2. 以下をまるっとコピペし、IPアドレスやアプリ名を記述して保存 ※IPアドレスがわからない場合はEC2のインスタンスの説明から確認可能

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    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;
    }
    }
  3. Nginxを再起動

    1
    $ sudo service nginx restart

Unicornの設定

GitHubのリポジトリの方にも反映したほうが良いかも。

  1. Gemfileを編集

    1
    $ vim Gemfile
  2. 以下を末尾に追記して保存

    1
    2
    3
    group :production do
    gem 'unicorn'
    end
  3. Unicornをインストール

    1
    $ bundle install
  4. Unicornの設定ファイルを作成

    1
    $ vim config/unicorn.conf.rb
  5. 以下をまるっとコピペし、アプリ名を記述して保存

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    # 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
  6. 権限を変更

    1
    $ sudo chmod -R 775 /var/lib/nginx/
  7. .bash_profileを編集する

    1
    $ sudo vi ~/.bash_profile
  8. Railsの環境変数としてproduction用のデータベース情報を追記して保存

    1
    2
    3
    4
    5
    export DB_NAME="アプリ名_production"
    export DB_USERNAME="RDSのユーザー名"
    export DB_PASSWORD="RDSのパスワード"
    export DB_HOSTNAME="RDSのエンドポイント"
    export DB_PORT="3306"
  9. 正しく設定されているか確認

    1
    2
    3
    4
    5
    6
    7
    $ echo $DB_NAME
    $ echo $DB_USERNAME
    $ echo $DB_PASSWORD
    $ echo $DB_HOSTNAME
    $ echo $DB_PORT
    # 登録したデータベース情報の値が表示される
  10. mysqldを起動

    1
    $ sudo service mysqld start
  11. マイグレーションを実行

    1
    $ bundle exec rake db:migrate RAILS_ENV=production

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

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

  1. EC2の「ロードバランサー」から「ロードバランサーの作成」をクリック
  2. Classic Load Balancerで「作成」
  3. ロードバランサー名に「アプリ名-ELB」、ロードバランサーをを作成する場所に「VPCforアプリ名」
  4. 利用可能なサブネットの「アクション」の「+」マーク2つをクリックして選択済みへ移動させ、「次の手順」をクリック
  5. セキュリティグループは「アプリ名-SecurityGroup」を選択し「次の手順」
  6. セキュリティ設定の構成は何もせず「次の手順」
  7. ヘルスチェックの設定はpingパスを「/」、間隔を「10」、正常のしきい値を「5」に設定し、「次の手順」 ※他はデフォルトでOK
  8. EC2インスタンスの追加は作成したインスタンスを選択し「次の手順」
  9. タグの追加ではキーに「Name」、値に「アプリ名-Webserver」と入力
  10. 「確認と作成」からの「作成」からの「閉じる」
  11. 一覧画面から作成したロードバランサーを選択し、下部のタブの「インスタンス」をクリック、状態が「InService」なら完了。「OutOfService」の場合は何か問題がある

STEP7 Railsアプリの起動

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

  1. EC2へSSHでログインし、Railsアプリをプリコンパイルする

    1
    $ bundle exec rake assets:precompile RAILS_ENV=production
  2. Nginxを再起動

    1
    $ sudo service nginx restart
  3. Unicornを起動

    1
    bundle exec unicorn_rails -c /var/www/projects/アプリ名/config/unicorn.conf.rb -D -E production
  4. Uniconの起動を確認

    1
    2
    3
    $ ps -ef | grep unicorn | grep -v grep
    # プロセスのリストが3行程表示されればOK
  5. ブラウザからIPを叩いてアクセス ※IPアドレスがわからない場合はEC2のインスタンスの説明から確認可能

    1
    http://IPアドレス/
  6. Railsアプリが無事動作すれば成功

参考

初心者向け:AWS(EC2)にRailsのWebアプリをデプロイする方法 目次

というかほぼそのまんまです。
初心者でも構築することが出来ました、丁寧なご説明本当にありがとうございます
※当記事では一部バージョンの変更や手順の省略を行っております

その他

  • だいたいイチから作業で6時間くらい掛かった
  • Elastic Beanstalk 使えば最初の工程をすっ飛ばして heroku 感覚でデプロイできるっぽい
  • EC2インスタンスログイン後、exitせずにターミナルを強制終了してしまった際にログインできなくなってしまった。必ず接続はexitで終了するように
  • 環境変数は .bash_profile に書くのが正解なのかどうかは分からない
  • 使ってないインスタンスは停止しておくべし

次回

Route53を利用した独自ドメイン周りの設定をメモります。
はー疲れた。