本記事は、開発環境のCloud9からAWSサービスを連携してデプロイすることをゴールにした連載記事です。

Railsアプリをデプロイしよう!!の全体目次はこちらをご覧ください。

本編では、前編に続き、Cloud9から本番環境へデプロイするための設定を進めていきます。

事前準備

S3へバケット作成

新たにバケットを作成します。

サービス:S3

下記にて作成しました。
バケット名:dc-app-bucket
AWS リージョン:東京
このバケットのブロックパブリックアクセス設定:パブリックアクセスをすべて ブロック
デフォルトの暗号化:有効→Amazon S3 キー (SSE-S3)

ロール作成①

CodeDeployからEC2にアクセスためのロールを作成

ロールを作成します。

AWSサービス > CodeDeployを選択します。

ユースケースの選択でCodeDeployを選択します。

ロールの作成では、特に指定せずに次に進みます。

タグの追加は任意です。

ロール名を指定して作成完了です。

ポリシー作成

IAM > ポリシーを選択して、ポリシーを作成します。

[JSON]タブに以下を貼り付けて次に進みます。

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Action": [
                "s3:Get*",
                "s3:List*"
            ],
            "Effect": "Allow",
            "Resource": "*"
        }
    ]
}

ポリシーの名前は任意の名前をつけて、作成します。

ロール作成②

追加でロールを作成します。

AWSサービス > EC2を選択します。

先ほど作成したポリシーをアタッチします。

タグ名はそのままスキップして、最後にロール名を入力して作成します。

続いて、EC2で該当のインスタンスに対してIAMロールを変更します。

先ほど作成したロールをアタッチします。

本番環境にCodeDeployAgentをインストール・起動

AWS CodeDeploy を使用するには、事前にCodeDeployエージェントのインストールが必要です。
下記コマンドをそれぞれ実行します

ターミナル(ssh)

$ sudo yum update
$ sudo yum install ruby
$ sudo yum install wget
$ cd /home/ec2-user

# このURLは東京リージョンのCodeDeployリソースキットファイルが置いてある場所です
$ wget https://aws-codedeploy-ap-northeast-1.s3.ap-northeast-1.amazonaws.com/latest/install

$ chmod +x ./install
$ sudo ./install auto

そして下記コマンドで実行中か確認できればOKです。

ターミナル(ssh)

$ sudo service codedeploy-agent status
 =>The AWS CodeDeploy agent is running as PID 22231

AppSpec file作成

appspec.ymlをアプリケーションソースコードのルートに作成し、デプロイの定義を書いてきます。

中身は適宜変更してください。

version: 0.0  # 0.0固定
os: linux     # デプロイ先サーバーのOS
files:        # アプリケーションの配置場所
  - source: /
    destination: /var/www/rails/DC_product
permissions:  # 配置ディレクトリのパーミッション
  - object: /var/www/rails/DC_product
    owner: ec2-user #{ユーザー名}
    group: ec2-user #{ユーザーグループ名}
    pattern: "**"
    mode: 775
    type:
      - file
      - directory
hooks:        # デプロイのライフサイクルイベント
  ApplicationStop:
    - location: deployment_scripts/stop_application.sh
      runas: root #{シェルスクリプトの実行ユーザー名}
  BeforeInstall:
    - location: deployment_scripts/remove_old_files.sh
      runas: root #{シェルスクリプトの実行ユーザー名}
  AfterInstall:
    - location: deployment_scripts/make_sockets_directory.sh
      runas: root #{シェルスクリプトの実行ユーザー名}
    - location: deployment_scripts/install_gems.sh
      runas: root #{シェルスクリプトの実行ユーザー名}
    - location: deployment_scripts/run_db_migrations.sh
      runas: root #{シェルスクリプトの実行ユーザー名}
    - location: deployment_scripts/compile_assets.sh
      runas: root #{シェルスクリプトの実行ユーザー名}
  ApplicationStart:
    - location: deployment_scripts/start_application.sh
      runas: root #{シェルスクリプトの実行ユーザー名}

デプロイライフサイクルイベントの作成

appspec.ymlに記載したデプロイのライフサイクルイベントを設定します
アプリケーションルートにdeployment_scriptsディレクトリを作成し、

各スクリプトを新規ファイルとして作成します。
stop_application.sh
remove_old_files.sh
make_sockets_directory.sh
install_gems.sh
run_db_migrations.sh
compile_assets.sh
start_application.sh

下記のスクリプトを参考にしてみてください。

# Unicornの動作状況を確認し、起動していれば停止させる。
if [ -e /var/www/rails/DC_product/tmp/pids/unicorn.pid ]; then
   sudo kill -QUIT `cat /var/www/rails/DC_product/tmp/pids/unicorn.pid`
fi
sudo rm -rf /var/www/rails/DC_product
sudo mkdir /var/www/rails/DC_product
sudo mkdir -p /var/www/rails/DC_product/run/sockets
sudo chown -R ec2-user /var/www/rails/DC_product
su -l ec2-user -c 'cd /var/www/rails/DC_product && bundle install'
su -l ec2-user -c 'cd /var/www/rails/DC_product && bundle exec rake db:migrate RAILS_ENV=production'
sudo touch /var/www/rails/DC_product/yarn-error.log
sudo chown -R ec2-user /var/www/rails/DC_product/yarn-error.log

sudo mkdir /var/www/rails/DC_product/node_modules
sudo chown -R ec2-user /var/www/rails/DC_product/node_modules

sudo mkdir /var/www/rails/DC_product/tmp
sudo chown -R ec2-user /var/www/rails/DC_product/tmp

sudo mkdir /var/www/rails/DC_product/public/
sudo chown -R ec2-user /var/www/rails/DC_product/public/

npm install -g node-sass

su -l ec2-user -c 'cd /var/www/rails/DC_product && RAILS_ENV=production bundle exec rake assets:precompile'
#nginxの再起動
sudo systemctl restart nginx

#unicornの起動
su -l ec2-user -c 'cd /var/www/rails/DC_product && bundle exec unicorn_rails -c /var/www/rails/DC_product/config/unicorn.conf.rb -E production -D'  

開発環境へgem ‘unicorn’追加

gem ‘unicorn’をインストールします。

group :production do
    gem 'unicorn'
end

config以下にunicorn.conf.rbを新規作成します。

unicorn.conf.rbは下記を参考にしてください。

  $worker  = 2
  
  $timeout = 30
  $app_dir = "/var/www/rails/DC_product" #自分のアプリケーション名
  $listen  = File.expand_path 'run/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

開発環境のdatabase.ymlを編集

database.ymlをRDS用に書き換えます。

#production以下をRDS用に書き換え

production:
  <<: *default
  # RDS MySQL用
  database: <%= Rails.application.credentials.database[:database_rds] %>
  username: <%= Rails.application.credentials.database[:username_rds] %>
  password: <%= Rails.application.credentials.database[:password_rds] %>
  host: <%= Rails.application.credentials.database[:host_rds] %>

そして、credentials.yml.encへ環境変数を追加しておきます。

ターミナル(cloud9)

$ EDITOR="vi" bin/rails credentials:edit

i で編集モードにして

database:
 database_rds: dc_production #データベースの名前
 username_rds: root #RDSで設定したユーザー名
 password_rds: ***** #RDSで設定したパスワード
 host_rds: dc********.ap-northeast-1.rds.amazonaws.com #RDSのエンドポイント

esc で編集終了後、:wq で上書き保存します。

CodeDeploy

デプロイのアプリケーションを作成します。

サービス:CodeDeploy

アプリケーション名を指定し、コンピューティングプラットフォームでEC2/オンプレミスを選択して作成します。

続いて、デプロイグループの作成をします。

下記にて設定して作成します。

デプロイグループ名の入力:DC-production-Deployment-Group
サービスロール:先ほど作成したロールを指定
デプロイタイプ:インプレース
環境設定:Amazon EC2 インスタンス(デプロイ先のEC2のキーとタグを指定します)
デプロイ設定:CodeDeployDefault.OneAtATime
Load balancer:「ロードバランシングを有効にする」のチェックを外す

CodePipeline

続いてCodePipelineの設定を進めていきます。

まず、パイプラインを作成します。

下記にて設定します。
高度な設定 > アーティファクトストアにてカスタムロケーションを指定して、バケットを今回作成したapp-bucketを指定します。

続いて、ソースステージを追加します。
下記にて設定します。

ビルドステージは、今回作成していないのでスキップします。

続いて、デプロイステージを追加します。
赤枠の箇所をそれぞれ指定します。

最後に確認画面をチェックして、作成完了です。

これでmasterブランチにpushすると自動でデプロイが実行されます。

試しにmasterブランチにpushすると、自動でCodePipelineが実行されました。
Sourceは成功、Deployは失敗でした。

Deployに関しては、筆者は40回程繰り返して、エラー解消しつつ成功しました。
今回紹介しているコードを使用すれば、もっと少ない回数で成功できると思います。

本編は以上です。お疲れ様でした。