12
15

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

capistranoでnginx, unicornの設定ファイルを管理しつつrailsのdeploy

Posted at

概要

ruby on railsをcapistranoでdeployするための設定方法。
nginxとunicornを使用する。
railsプロジェクトはconfig/database.ymlとconfig/secrets.ymlがgitignoreされており、
config/database.example.ymlとconfig/secrets.example.ymlがレポジトリ内に
存在するとしておく。

全体の流れ

  1. サーバ設定など
  2. Gemfileに追加しbundle
  3. cap install
  4. Capfile, deploy.rb編集
  5. tasksファイル(unicorn, nginx関係)とテンプレート作成
  6. staging.rb, production.rb編集
  7. cap production deploy:check
  8. server上でdatabase.yml, secrets.ymlなどを直す
  9. cap production deploy

サーバ上での前設定

新しいサーバで必要な作業。すでにdeployユーザの作成などができている場合は飛ばして良い。

deployユーザの作成

sudo adduser deploy

deployユーザをsudoerにする。

sudo adduser deploy sudo

deployするためのディレクトリを作成。deployユーザに権限を与える。

sudo mkdir /var/www
sudo chown deploy /var/www
sudo chgrp deploy /var/www

bundlerなど実行できるようにしておく。

nginxをインストールし、defaultの設定を消しておく。

sudo aptitude install nginx
sudo rm /etc/nginx/sites-enabled/default

log formatでltsvを使いたいので/etc/nginx/conf.d/ltsv.confを作成。

/etc/nginx/conf.d/ltsv.conf
log_format  ltsv  'time:$time_local\t'
                  'host:$remote_addr\t'
                  'request:$request\t'
                  'status:$status\t'
                  'size:$body_bytes_sent\t'
                  'referer:$http_referer\t'
                  'ua:$http_user_agent\t'
                  'reqtime:$request_time\t'
                  'upsttime:$upstream_response_time';

sudoが必要なnginxの起動や、unicornサーバの起動、また設定ファイルの配置などを
deployユーザがパス無しでできるように設定しておくために/etc/sudoersに以下を追記する。
/etc/sudoersの編集はvisudoなどで行う。
sudo visudoで編集ができ、閉じるときにSyntax errorがある場合は、表示してくれる。)

/etc/sudoers
# 以下を追記
deploy ALL=NOPASSWD: /usr/bin/apt-get up*
deploy ALL=NOPASSWD: /usr/sbin/service /usr/sbin/nginx*
deploy ALL=NOPASSWD: /bin/rm -f /etc/nginx/sites-enabled/default
deploy ALL=NOPASSWD: /bin/mv /tmp/unicorn.service /etc/systemd/system/unicorn*
deploy ALL=NOPASSWD: /bin/mv /tmp/nginx_conf /etc/nginx/sites-enabled/*
deploy ALL=NOPASSWD: /bin/mv /tmp/nginx_default_conf /etc/nginx/sites-enabled/*
deploy ALL=NOPASSWD: /usr/sbin/service nginx *
deploy ALL=NOPASSWD: /bin/systemctl * unicorn*
deploy ALL=NOPASSWD: /bin/systemctl daemon-reload
deploy ALL=NOPASSWD: /bin/mkdir -p /var/log/nginx/*

capistranoの設定

Gemfileにcapistrano関連を登録する。

Gemfile
group :development do
  gem 'capistrano-rails'
  gem 'capistrano-rbenv', '~> 2.0.4'
  gem 'capistrano-bundler'
end

bundleして、ライブラリをインストールした後、
capistranoで必要なファイルをcap installで作成。

% bundle
% bundle exec cap install                                                                                                                                                 kenmbp.local
mkdir -p config/deploy
create config/deploy.rb
create config/deploy/staging.rb
create config/deploy/production.rb
mkdir -p lib/capistrano/tasks
create Capfile
Capified

Capfile

Capfileを修正。
rbenv, bundler, rails/assets, rails/migrationsは最初はコメントアウトされている。
rbenv_typeは:userと:systemの二種類だが、rbenvをローカルインストールしている場合は:userを指定。
rbenv_rubyで使用するrubyのバージョンを指定。

Capfile
require 'capistrano/setup'
require 'capistrano/deploy'
require 'capistrano/rbenv'
require 'capistrano/bundler'
require 'capistrano/rails/assets'
require 'capistrano/rails/migrations'

set :rbenv_type, :user
set :rbenv_ruby, '2.3.1'

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

config/deploy.rb

config/deploy.rbには、stagingとproductionで共通に使う設定を書いていく。

capistrano内でset設定した変数はfetchで取得できる。
他のset変数をsetで使いたい場合は、lamda(->となっているところ)式で呼び出す。
これで変数が後から代入することができるようになる。

デプロイ先:

  1. /var/www/app_name/production
  2. /var/www/app_name/staging
config/deploy.rb
set :default_stage, 'staging'

set :deploy_to, -> { "/var/www/app_name/#{fetch(:stage)}" }
set :deploy_via, :remote_cache

set :scm, :git
set :repo_url, 'repository_url'
set :keep_releases, 5

set :pty, true
set :ssh_options, {
  forward_agent: true,
  auth_methods: %w(publickey)
}

set :untracked_files, %w(config/database.yml,
                         config/secrets.yml,
                         config/email.yml)
append :linked_files,
       'config/database.yml',
       'config/secrets.yml',
       'config/email.yml'
append :linked_dirs,
       'log',
       'tmp/pids',
       'tmp/cache',
       'tmp/sockets',
       'vendor/bundle',
       'public/system',
       'bin'

自分でタスクを定義

基本的なタスク

lib/capistrano/tasks/deploy.rakeにいくつか共通し使う関数とタスクを定義しておく。

lib/capistrano/tasks/deploy.rake
namespace :deploy do
  def remote_file_exists?(full_path)
    test "[ -e #{full_path} ]"
  end

  def template(from, to)
    erb = File.read(File.expand_path("../../templates/#{from}", __FILE__))
    upload! StringIO.new(ERB.new(erb).result(binding)), to
  end

  task :untracked_files do
    on roles(:web) do
      unless remote_file_exists?("#{shared_path}/config/database.yml")
        upload! File.open('config/database.example.yml'),
                "#{shared_path}/config/database.yml"
      end
      unless remote_file_exists?("#{shared_path}/config/secrets.yml")
        upload! File.open('config/secrets.example.yml'),
                "#{shared_path}/config/secrets.yml"
      end
      puts "Now edit the config files in #{shared_path}."
    end
  end
  after 'deploy:check:make_linked_dirs', 'deploy:untracked_files'

  desc 'Make sure local git is in sync with remote.'
  task :check_revision do
    on roles(:web) 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` to sync changes.'
        exit
      end
    end
  end
  before 'deploy', 'deploy:check_revision'
end

nginx関連のタスク及び設定

nginxの設定もcapistranoで作成するようにする。
lib/capistrano/templates/nginx_unicorn.erbに以下のような設定を作成。

lib/capistrano/templates/nginx_unicorn.erb
upstream unicorn_<%= fetch(:application) %> {
  server unix:/tmp/unicorn.<%= fetch(:application) %>.sock fail_timeout=0;
}

server {
  listen 80;
  server_name <%= fetch(:server_name) %>;
  root <%= current_path %>/public;
  <%= "auth_basic \"Restricted\";\n  auth_basic_user_file /etc/nginx/.htpasswd;" unless fetch(:rails_env) == 'production' %>

  client_max_body_size 100m;
  error_page 404 /404.html;
  error_page 500 502 503 504 /500.html;
  keepalive_timeout 10;
  access_log /var/log/nginx/app_name/<%= fetch(:stage) %>/<%= fetch(:application) %>.log ltsv;

  location ^~ /assets/ {
    gzip_static on;
    expires max;
    add_header Cache-Control public;
  }

  try_files $uri/index.html $uri @unicorn_<%= fetch(:application) %>;
  location @unicorn_<%= fetch(:application) %> {
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header Host $http_host;
    proxy_redirect off;
    proxy_pass http://unicorn_<%= fetch(:application) %>;
  }
}

production以外ではbasic認証を書けるように設定している。
(nginxでbasic認証)

lib/capistrano/tasks/nginx.rakeを作成。

lib/capistrano/tasks/nginx.rake
namespace :nginx do
  set :nginx_conf, -> { "/etc/nginx/sites-enabled/#{fetch(:application)}" }

  desc 'Setup nginx configuration for this application'
  task :setup do
    on roles(:web) do
      sudo "mkdir -p /var/log/nginx/app_name/#{fetch(:stage)}"
      template 'nginx_unicorn.erb', '/tmp/nginx_conf'
      sudo "mv /tmp/nginx_conf #{fetch :nginx_conf}"
      sudo 'service nginx restart'
    end
  end

  task :initialize do
    on roles(:app) do
      invoke 'nginx:setup' unless remote_file_exists? fetch :nginx_conf
    end
  end
  after 'deploy:check', 'nginx:initialize'

  %w(start stop restart).each do |command|
    desc "#{command} nginx"
    task command do
      on roles(:web) do
        sudo "service nginx #{command}"
      end
    end
  end
end

nginx:setupを実行するとtemplateから作成したnginxの設定ファイルをサーバに配置できる。
ここではafter "deploy:check", :setupを設定しており、
後ほど実行するdeploy:checkの時に自動的に実行されるので、
今は実行しなくても良い

cap prodution nginx:setup

ただし、nginx設定ファイルを書きかえた場合にはnginx:setupを実行する必要がある。

またnginxをcapistranoからstart, stop, restartできるようにタスクを作ってある。

cap production nginx:start
cap production nginx:stop
cap production nginx:restart

unicorn関連のタスク及び設定

nginxと同様にunicornもtemplateを作成しておく。
unicornは起動スクリプトと、unicornの設定の二種類のtemplateを作成する。

unicornの設定のtemplateをlib/capistrano/templates/unicorn.rb.erbに作成。

lib/capistrano/templates/unicorn.rb.erb
working_directory "<%= current_path %>"
pid "<%= current_path %>/tmp/pids/unicorn.pid"
stderr_path "<%= shared_path %>/log/unicorn.log"
stdout_path "<%= shared_path %>/log/unicorn.log"

listen "/tmp/unicorn.<%= fetch(:application) %>.sock"
worker_processes 2
timeout 30

unicorn起動のためのスクリプトtemplateをlib/capistrano/templates/unicorn.service.erbに作成。

lib/capistrano/templates/unicorn.service.erb
[Unit]
Description=Unicorn server for <%= fetch(:application) %>

[Service]
SyslogIdentifier=unicorn-<%= fetch :application %>
User=deploy
PIDFile=<%= current_path %>/tmp/pids/unicorn.pid
WorkingDirectory=<%= current_path %>
Type=forking
Environment=RAILS_ENV=<%= fetch :rails_env %>

ExecStart=/home/deploy/.rbenv/bin/rbenv exec bundle exec unicorn -D -c <%= shared_path %>/config/unicorn.rb -E <%= fetch :rails_env %>
ExecStop=/bin/kill -s QUIT $MAINPID
ExecReload=/bin/kill -s HUP $MAINPID

[Install]
WantedBy=multi-user.target

これがあるとサーバを再起動した時などに、
自動的にunicornを起動させることができる。

unicorn関連のタスクをlib/capistrano/tasks/unicorn.rakeに設定

lib/capistrano/tasks/unicorn.rake
namespace :unicorn do
  desc 'Setup Unicorn initializer and app configuration'
  task :setup do
    on roles(:app) do
      execute "mkdir -p #{shared_path}/config"
      template 'unicorn.rb.erb', "#{shared_path}/config/unicorn.rb"
      template 'unicorn.service.erb', '/tmp/unicorn.service'
      target_file = "/etc/systemd/system/unicorn_#{fetch(:application)}.service"
      sudo "mv /tmp/unicorn.service #{target_file}"
      sudo 'systemctl daemon-reload'
      sudo "systemctl start unicorn_#{fetch(:application)}"
    end
  end

  task :initialize do
    on roles(:app) do
      init_file_path =
        "/etc/systemd/system/unicorn_#{fetch(:application)}.service"
      config_file_path = "#{shared_path}/config/unicorn.rb"
      unless remote_file_exists?(init_file_path) &&
             remote_file_exists?(config_file_path)
        invoke 'unicorn:setup'
      end
    end
  end
  after 'deploy:check', 'unicorn:initialize'

  %w(start stop reload status is-active).each do |command|
    desc "#{command} unicorn"
    task command do
      on roles(:app) do
        sudo "systemctl #{command} unicorn_#{fetch(:application)}"\
             "#{command == 'status' ? ' | cat' : nil}"
      end
    end
  end

  after 'deploy:finished', 'unicorn:reload'
  after 'deploy:finished', 'unicorn:is-active'
end

nginxの場合と同様に、unicorn:setupを実行すると
templateから作成したunicornの設定ファイルをサーバに配置できる。
ここでもafter "deploy:check", :setupを設定しており、
後ほど実行するdeploy:checkの時に自動的に実行されるので、
今は実行しなくても良い

cap production unicorn:setup

設定を変更した場合は、再びunicorn:setupを実行する必要がある。

またnginxと同様にローカルからstart, stop, restartができるようになっている。

cap production unicorn:start
cap production unicorn:stop
cap production unicorn:restart

staging及びproduction独自の設定

staging, production独自の設定に関しては、
それぞれconfig/deploy/staging.rbとconfig/deploy/production.rb
に書く。

config/deploy/staging.rb
set :application, 'app_name_staging'
set :branch, :staging
set :rails_env, :staging
server '0.0.0.0', user: 'deploy', roles: %w{app db web}
set :server_name, 'staging.example.com'
config/deploy/production.rb
set :application, 'app_name'
set :branch, :master
set :rails_env, :production
server '0.0.0.0', user: 'deploy', roles: %w{app db web}
set :server_name, 'example.com'

capistranoによるサーバ上のファイル設定

cap deploy:checkで設定ファイルをサーバ上に設置する。

cap production deploy:check

deploy:checkはdeployのときに実行されるタスクではあるが、
database.yml, secrets.ymlがただしく設定できていないので、deployを実行するのではなく
deploy:checkを実行する。

たまにdeploy:checkあたりで、gitにアクセス出来ないというエラーが出ることがある。

Please make sure you have the correct access rights
and the repository exists.
git stderr: Nothing written

これはdeployユーザの公開鍵をrepositoryサーバに登録しなくても、ローカルのユーザの
鍵を使ってpullできるようにuser agentの登録をローカルで行えば良い。

ssh-add ~/.ssh/id_rsa

サーバ上のdatabase.ymlやsecret.ymlを変更した後、cap deployでdeployができる。

cap production deploy

参考

capistranoタスクの流れ

deploy

deploy:starting    - start a deployment, make sure everything is ready
deploy:started     - started hook (for custom tasks)
deploy:updating    - update server(s) with a new release
deploy:updated     - updated hook
deploy:publishing  - publish the new release
deploy:published   - published hook
deploy:finishing   - finish the deployment, clean up everything
deploy:finished    - finished hook

rollback

deploy:starting
deploy:started
deploy:reverting           - revert server(s) to previous release
deploy:reverted            - reverted hook
deploy:publishing
deploy:published
deploy:finishing_rollback  - finish the rollback, clean up everything
deploy:finished

deploy:check

deploy:check:directories
deploy:check:linked_dirs
deploy:check:make_linked_dirs
deploy:check:linked_files

#実際に陥りやすいポイント

unicorn.rbが移行されず、エラーが出て止まる

上記の順番で実行した時に、unicorn.rbがうまくshared/config/以下に移せなかった。
この時には、cap production unicorn:setupを実行するとよい。
もしかすると、remote_exist_file?らへんの条件がうまく機能していないのかもしれない。

No passrd for user '....' というエラーで止まる

database.ymlの設定で、dbにつなげていない場合に起こる。
解決方法の一つとして、deployでログインして、createdb app_db_nameを実行(Postgresの場合)し、deployとそのパスワードをdatabase.ymlに書くことで解決するかも。

## The deploy has failed with an error

The deploy has failed with an error: Exception while executing as deploy@example.com: rake exit status: 1
rake stdout: config.eager_load is set to nil. Please update your config/environments/*.rb files accordingly:

  * development - set it to false
  * test - set it to false (unless you use a tool that preloads your test environment)
  * production - set it to true

rake aborted!

cp config/environments/production.rb config/enviroments/staging.rb
を実行し、staging.rbを作る

PG::ConnectionBad: FATAL: password authentication failed for user "deploy"

database.ymlの情報ミス

sudo stdout: appname_staging: unrecognized service

 aborted!

...
SSHKit::Command::Failed: sudo exit status: 1
...
Tasks: TOP => unicorn:restart
(See full trace by running task with --trace)

sudo stderr: Nothing written

deploy staging unicorn:setupして解消

12
15
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
12
15

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?