見出し画像

Ubuntu16.04 + Ruby on Rails 5.1 + Puma + Nginx + Capistranoでstaging環境を構築

はじめに

Railsアプリケーションを本番環境やstaging環境に反映させたいときに、手軽にデプロイできるようにすることは必須と言えます。

この記事ではCapistranoを使用して、staging環境にcapコマンドでデプロイできるようにするまでを紹介しています。

一度設定を行ってしまえば、今後はローカルのターミナルで

$ bundle exec cap staging deploy

を叩くだけでremoteのmasterブランチがstaging環境に反映されます。

また、ブランチを指定したデプロイも可能です。stagingブランチをデプロイしたい場合

$ bundle exec cap staging deploy BRANCH=staging

のように指定できます。

Rails

secrets.ymlの設定

config/secrets.yml

staging:
  secret_key_base: <%= ENV["SECRET_KEY_BASE"] %>

secret_key_baseは環境変数で設定する想定なので、ここには記述しません。

database.ymlの設定

config/database.yml

staging:
  <<: *default
  database: sample_app_staging
  username: <%= ENV["SAMPLE_APP_DATABASE_USERNAME"] %>
  password: <%= ENV["SAMPLE_APP_DATABASE_PASSWORD"] %>

こちらも同じく環境変数で設定します。

environtemsにファイルを作成

とりあえずconfig/production.rbをコピーします。

$ cp config/production.rb config/staging.rb 

SSH

deployユーザーを作成します。ここからサーバ側の設定になります。

$ ssh root@XX.XX.XX.XX

$ adduser deploy
$ gpasswd -a deploy sudo
$ mkdir /home/deploy/.ssh
$ cp /root/.ssh/authorized_keys /home/deploy/.ssh/authorized_keys
$ chmod 700 /home/deploy/.ssh/authorized_keys
$ chown -R deploy:deploy /home/deploy/.ssh/
$ gpasswd -a deploy root
$ ssh deploy@XX.XX.XX.XX

ローカルからdeployユーザーとしてsshログインできればOKです。

sudo

パスワードなしでsudoできるようにします。

$ sudo visudo

# 以下のように編集
-%sudo  ALL=(ALL:ALL) ALL
+%sudo  ALL=(ALL:ALL) NOPASSWD:ALL

/var/www/作成

アプリケーションを配置するフォルダを作成します。

$ mkdir /var/www
$ sudo chown deploy:deploy /var/www

TimeZone

TimeZoneをTokyoに設定します。

$ sudo ln -sf /usr/share/zoneinfo/Asia/Tokyo /etc/localtime
$ date

Ruby

rbenvを使用してrubyをインストールします。

$ sudo apt-get update
$ sudo apt-get upgrade
$ sudo apt-get install git build-essential libssl-dev
$ git clone https://github.com/sstephenson/rbenv.git ~/.rbenv
$ git clone https://github.com/sstephenson/ruby-build.git ~/.rbenv/plugins/ruby-build
$ vim .profile

# 以下追記
export PATH="$HOME/.rbenv/bin:$PATH"
eval "$(rbenv init -)"

追記内容を反映させます。

$ source ~/.profile

ruby2.4.1をインストールします。

$ rbenv install --list
$ rbenv install 2.4.1
$ rbenv rehash
$ rbenv global 2.4.1
$ ruby -v

$ gem install bundler --no-ri --no-rdoc

# nodejsをいれておく
$ sudo apt-get install nodejs

2.4.1のインストールで失敗しました。

Downloading ruby-2.4.1.tar.bz2...
-> https://cache.ruby-lang.org/pub/ruby/2.4/ruby-2.4.1.tar.bz2
Installing ruby-2.4.1...

BUILD FAILED (Ubuntu 16.04 using ruby-build 20170523-25-g476d09b)

Inspect or clean up the working tree at /tmp/ruby-build.20170725024731.10932
Results logged to /tmp/ruby-build.20170725024731.10932.log

Last 10 log lines:
installing rdoc:              /home/deploy/.rbenv/versions/2.4.1/share/ri/2.4.0/system
installing capi-docs:         /home/deploy/.rbenv/versions/2.4.1/share/doc/ruby
The Ruby readline extension was not compiled.
ERROR: Ruby install aborted due to missing extensions
Try running `yum install -y readline-devel` to fetch missing dependencies.

Configure options used:
  --prefix=/home/deploy/.rbenv/versions/2.4.1
  LDFLAGS=-L/home/deploy/.rbenv/versions/2.4.1/lib 
  CPPFLAGS=-I/home/deploy/.rbenv/versions/2.4.1/include 

https://gist.github.com/urouro/532f24ad6a52a96a921e

こちらを参考にインストールしました。

Nginx

公式を参考にインストールします。

$ curl http://nginx.org/keys/nginx_signing.key | sudo apt-key add -
$ sudo sh -c "echo 'deb http://nginx.org/packages/ubuntu/ xenial nginx' >> /etc/apt/sources.list"
$ sudo sh -c "echo 'deb-src http://nginx.org/packages/ubuntu/ xenial nginx' >> /etc/apt/sources.list"
$ sudo apt-get update
$ sudo apt-get install nginx

環境変数

環境変数の設定を行います。

$ sudo vim /etc/environment

# 以下記述
SECRET_KEY_BASE=""
sample_app_DATABASE_USERNAME=""
sample_app_DATABASE_PASSWORD=""

Capistrano

ここからはローカル側です。

Gemfile

group :development do
  gem "capistrano",         require: false
  gem "capistrano-rails",   require: false
  gem "capistrano-bundler", require: false
  gem "capistrano3-puma",   require: false
  gem "capistrano-rbenv",   require: false
end

Gemfileに書いたら、ローカルで以下を実行します。

$ bundle install --path vendor/bundle
$ bundle exec cap install

関連ファイルが自動生成されるので、以下を記述します。

Capfile

require "capistrano/setup"
require "capistrano/deploy"
require "capistrano/scm/git"
install_plugin Capistrano::SCM::Git

require "capistrano/rails"
require "capistrano/rbenv"
require "capistrano/bundler"
require "capistrano/puma"
install_plugin Capistrano::Puma

Dir.glob("lib/capistrano/tasks/*.rake").each { |r| import r }

config/deploy.rb

# config valid only for current version of Capistrano
lock "3.8.2"

set :application,     "sample-app"
set :repo_url,        "git@github.com:your_account/sample-app.git"
set :user,            "deploy"
set :ssh_options,     {
  forward_agent: true,
  user: fetch(:user),
  keys: %w(~/.ssh/id_rsa)
}

set :deploy_to,       "/var/www/#{fetch(:application)}"
set :deploy_via,      :remote_cache

set :puma_threads,    [4, 16]
set :puma_workers,    0
set :pty,             true
set :use_sudo,        false
set :puma_bind,       "unix://#{shared_path}/tmp/sockets/#{fetch(:application)}-puma.sock"
set :puma_state,      "#{shared_path}/tmp/pids/puma.state"
set :puma_pid,        "#{shared_path}/tmp/pids/puma.pid"
set :puma_access_log, "#{release_path}/log/puma.error.log"
set :puma_error_log,  "#{release_path}/log/puma.access.log"
set :puma_preload_app, true
set :puma_worker_timeout, nil
set :puma_init_active_record, true
set :rbenv_ruby, '2.4.1'
set :linked_dirs, fetch(:linked_dirs, []).push(
  'log',
  'tmp/pids',
  'tmp/cache',
  'tmp/sockets',
  'vendor/bundle',
  'public/system',
  'public/uploads'
)
set :linked_files, fetch(:linked_files, []).push(
  'config/database.yml',
  'config/secrets.yml'
)

namespace :puma do
  desc 'Create Directories for Puma Pids and Socket'
  task :make_dirs do
    on roles(:app) do
      execute "mkdir #{shared_path}/tmp/sockets -p"
      execute "mkdir #{shared_path}/tmp/pids -p"
    end
  end
  before :start, :make_dirs
end

namespace :deploy do
  desc "Make sure local git is in sync with remote."
  task :check_revision do
    on roles(:app) do
      unless `git rev-parse HEAD` == `git rev-parse origin/#{fetch(:branch)}`
        puts "WARNING: HEAD is not the same as origin/#{fetch(:branch)}"
        puts "Run `git push origin HEAD` to sync changes."
        exit
      end
    end
  end

  desc 'Initial Deploy'
  task :initial do
    on roles(:app) do
      before 'deploy:restart', 'puma:start'
      invoke 'deploy'
    end
  end

  desc 'Restart application'
  task :restart do
    on roles(:app), in: :sequence, wait: 5 do
      invoke 'puma:restart'
    end
  end

  desc '必要なファイルをアップロード'
  task :upload do
    on roles(:app) do |host|
      if test "[ ! -d #{shared_path}/config ]"
        execute "mkdir -p #{shared_path}/config"
      end
      upload!('config/database.yml', "#{shared_path}/config/database.yml")
      upload!('config/secrets.yml', "#{shared_path}/config/secrets.yml")
    end
  end

  before :starting,     :check_revision
  after  :finishing,    :compile_assets
  after  :finishing,    :cleanup
end

config/deploy/staging.rb

server "XX.XX.XX.XX", user: "deploy", roles: %w{app db web}, primary: true
set :branch,    ENV["BRANCH"] || "master"
set :stage,     :staging
set :rails_env, :staging

事前にgithubなど、登録しているリポジトリに自分の公開鍵id_rsa.pubを登録してから、ローカルで以下を実行します。

$ ssh-add ~/.ssh/id_rsa
$ bundle exec cap staging deploy:upload
$ bundle exec cap staging deploy:initial

mysqlで失敗したのでサーバーにログインしてインストールします。

$ sudo apt-get install libmysqlclient-dev

mysqlのsocketが違うと怒られたので指定します。

config/database.yml

staging:
  socket: /var/run/mysqld/mysqld.sock

databaseが作られていなかったのでサーバー内に入って作成します。

mysql> CREATE DATABASE sample_app_staging;

もう一度実行します。

$ bundle exec cap staging deploy:initial

全て完了すると
- git clone
- bundle install
- rake assets:precompile
- rake db:migrate
- pume起動
などが全て自動で完了されています。

Nginx設定

これで最後です。サーバに入って、Nginxの設定を行います。

$ sudo vi /etc/nginx/conf.d/sample-app.conf

# 以下記述
upstream sample-app {
  server unix:/var/www/sample-app/shared/tmp/sockets/sample-app-puma.sock fail_timeout=0;
}

server {
  listen 80;
  server_name XX.XX.XX.XX;
  root /var/www/sample-app/current/public;

  location ~ ^/assets/ {
    root /var/www/sample-app/current/public;
  }

  try_files $uri/index.html $uri @sample-app;

  location / {
    proxy_pass http://sample-app;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header Host $http_host;
    proxy_redirect off;
  }

  error_page 500 502 503 504 /500.html;
  client_max_body_size 4G;
  keepalive_timeout 10;
}

Nginxを起動します。

$ sudo service nginx configtest
$ sudo service nginx start

これで、http://XX.XX.XX.XX/ にアクセスすることでstaging環境が確認できるようになります。

Capistranoの構成

サーバー内では /var/www/sample-app/current に現在のrails環境が展開されています。

その実態は /var/www/sample-app/releases 配下に展開されている中から最新のものに向けてシンボリックリンクが貼られています。

おわりに

Capistranoは自前タスクを追加することができるので、様々にカスタマイズできます。また、コマンド1つでデプロイが可能なので、CIツールとの相性も良いです。

ぜひ使ってみてください。

私が所属している株式会社ACESでは、Deep Learningを用いた画像認識技術を中心に、APIによるアルゴリズムパッケージの提供や、共同研究開発を行なっています。特に、ヒトの認識・解析に強みを持って研究開発を行っておりますので、ご興味のある方は、ぜひお問い合わせください!
【詳細・お問い合わせはこちら↓】

◆画像認識アルゴリズム「SHARON」について
ヒトの行動や感情の認識、モノの検知などを実現する画像認識アルゴリズムを開発しています。スポーツにおけるパフォーマンス分析やマーケティングにおけるヒトの心の動きの可視化、ストレスなどの可視化による健康状態の管理を始めとするAIアルゴリズムを提供しています。


この記事が気に入ったらサポートをしてみませんか?