リア充爆発日記

You don't even know what ria-ju really is.

これで動いた!さくらのVPS+github+capistranoでrailsアプリ自動デプロイの設定ファイルを置いておく

ローカルPC→さくらVPSgithubという感じで、一回設定したらあとは全部ローカルで作業が完結するというラクラク設定。
公開鍵認証の設定とかgithubの設定とかnginxのインストールとかそういうのは抜きでスクリプト群のみをメモ代わりに置いておく。

Gemfile抜粋

group :assets do
  gem 'sass-rails',   '3.2.5'
  gem 'coffee-rails', '3.2.2'

  gem 'therubyracer'
  gem 'libv8', '~> 3.11.8'

  gem 'uglifier', '1.2.3'
end

gem 'jquery-rails', '2.1.4'

# Use unicorn as the app server
gem 'unicorn'

group :deployment do
  gem 'capistrano'
  gem 'capistrano_colors'
  gem 'capistrano-ext'
  gem 'capistrano_rsync_with_remote_cache'
end

therubyracerとかlibv8とかuglifierがないとasset compileができない。最初のbundle install時はlibv8ですごい時間がかかる(さくらのVPS 2Gで10分くらい?)けど焦ってはいけない。


Gemfile以外の以下のファイルは全部#{application_root}/configの下にぶっこんでおく

nginx.conf

upstream your-app-name {
  server unix:/var/www/your-app-name/shared/unicorn.sock;
}

server {
  listen 80;
  server_name your-app-name;

  root /var/www/your-app-name/public;
  access_log /var/www/your-app-name/current/log/access.log;
  error_log /var/www/your-app-name/current/log/error.log;
  location ~ ^(/assets/|/audios/) {
    gzip_static on;

    root /var/www/your-app-name/current/public;
    expires 1y;
    add_header Cache-Control public;

    add_header ETag "";
    break;
  }

  location / {
    if (-f $request_filename) { break; }
    # ファイルが存在しなければunicornにproxyする
    proxy_set_header X-Real-IP  $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header Host $http_host;
    proxy_pass http://your-app-name;
  }

}

/etc/nginx/conf.d/に置いておく



unicorn.rb

proj_root_dir = File.expand_path("../../../", __FILE__)
shared_path = "#{proj_root_dir}/shared"

worker_processes 4
timeout 30
preload_app true

listen      "#{shared_path}/unicorn.sock"
pid         "#{shared_path}/pids/unicorn.pid"
stderr_path "#{shared_path}/log/unicorn.log"
stdout_path "#{shared_path}/log/unicorn.log"

とくにコメント無し。


unicorn_init.sh

#!/bin/sh
set -e

# Feel free to change any of the following variables for your app:
TIMEOUT=${TIMEOUT-60}
ROOT=/var/www/your-app-name
APP_ROOT=$ROOT/current
PID=$ROOT/shared/pids/unicorn.pid
CMD="cd $APP_ROOT; bundle exec unicorn -D -c $APP_ROOT/config/unicorn.rb -E production"
AS_USER=your_linux_user
set -u

OLD_PIN="$PID.oldbin"

sig () {
  test -s "$PID" && kill -$1 `cat $PID`
}

oldsig () {
  test -s $OLD_PIN && kill -$1 `cat $OLD_PIN`
}

run () {
  if [ "$(id -un)" = "$AS_USER" ]; then
    eval $1
  else
    su -c "$1" - $AS_USER
  fi
}

case "$1" in
start)
  sig 0 && echo >&2 "Already running" && exit 0
  run "$CMD"
  ;;
stop)
  sig QUIT && exit 0
  echo >&2 "Not running"
  ;;
force-stop)
  sig TERM && exit 0
  echo >&2 "Not running"
  ;;
restart|reload)
  sig HUP && echo reloaded OK && exit 0
  echo >&2 "Couldn't reload, starting '$CMD' instead"
  run "$CMD"
  ;;
upgrade)
  if sig USR2 && sleep 2 && sig 0 && oldsig QUIT
  then
    n=$TIMEOUT
    while test -s $OLD_PIN && test $n -ge 0
    do
      printf '.' && sleep 1 && n=$(( $n - 1 ))
    done
    echo

    if test $n -lt 0 && test -s $OLD_PIN
    then
      echo >&2 "$OLD_PIN still exists after $TIMEOUT seconds"
      exit 1
    fi
    exit 0
  fi
  echo >&2 "Couldn't upgrade, starting '$CMD' instead"
  run "$CMD"
  ;;
reopen-logs)
  sig USR1
  ;;
*)
  echo >&2 "Usage: $0 <start|stop|restart|upgrade|force-stop|reopen-logs>"
  exit 1
  ;;
esac

これもなし。っていうかアプリ名とかルートディレクトリが分散するのがイマイチだなぁ、と思っているので環境変数とかに設定して全部そこから読み込んで設定するようにしたらベターだと思っているし、そのうち挑戦しようと思ってる。しなさそうだけど。


require 'capistrano_colors'
require "bundler/capistrano"

server "your_host", :web, :app, :db, primary: true
set :application, "your_app_name

proj_root_dir = File.expand_path("../../../../", __FILE__)
shared_path = "/var/www/#{application}/shared"

set :user, "your_linux_user_id"
set :deploy_to, "/var/www/#{application}"
set :deploy_via, :remote_cache

set :scm, :git
set :repository,  "git@github.com:your_github_user_id/#{application}.git"
set :branch, "master"

set :rails_env, "production"

default_run_options[:pty] = true
ssh_options[:port] = "your_ssh_port_number"
ssh_options[:forward_agent] = true
ssh_options[:keys] = [File.join(ENV["HOME"], ".ssh", "your_ssh_key")]

# precompile
load 'deploy/assets'

after "deploy", "deploy:cleanup" # keep only the last 5 releases

namespace :deploy do
  %w[start stop restart].each do |command|
    desc "#{command} unicorn server"
    task command, roles: :app, except: {no_release: true} do
      run "/etc/init.d/unicorn_#{application} #{command}"
    end
  end

  task :setup_config, roles: :app do
    sudo "ln -nfs #{current_path}/config/nginx.conf /etc/nginx/conf.d/#{application}.conf"
    sudo "ln -nfs #{current_path}/config/unicorn_init.sh /etc/init.d/unicorn_#{application}"
    run "mkdir -p #{shared_path}/config"
  #  put File.read("config/database.example.yml"), "#{shared_path}/config/database.yml"
    puts "Now edit the config files in #{shared_path}."
  end
  after "deploy:setup", "deploy:setup_config"

  #task :symlink_config, roles: :app do
  #  run "ln -nfs #{shared_path}/config/database.yml #{release_path}/config/database.yml"
  #end
  #after "deploy:finalize_update", "deploy:symlink_config"

  desc "Make sure local git is in sync with remote."
  task :check_revision, roles: :web do
    unless `git rev-parse HEAD` == `git rev-parse origin/master`
      puts "WARNING: HEAD is not the same as origin/master"
      puts "Run `git push` to sync changes."
      exit
    end
  end
  before "deploy", "deploy:check_revision"
end

niginx.confとかを適当な名前にして適当なディレクトに配布する感じの処理が入ってる。何かデプロイ時にやりたい処理があればアクションごとにここに書けばいい。


最初だけサーバ側で/var/www/your-app-nameディレクトリ作って、運用ユーザー所有にしておき、あとは、ローカルのプロジェクトルートでcapifyしてcap deploy:setupしてcap deployすればOK。再起動とかはcap deploy:(stop|start|restart)で。やっべチョー楽。
あ、cap:setup時にどういうわけか/var/www/your-app-name以下にいくつかrootでディレクトリが作られちゃって、2〜3回所有者を設定し直す、ということをやったけど、1回やればもうやらなくていいので原因は調べてない。デプロイ時には鍵のパスフレーズを2〜3回打つことになるけど、それはそういうもんだと思うのでしょうがないと思う。

最初に書いたとおり、nginxのインストールとかmysqlのインストールとかgithubに公開鍵の登録とかVPS秘密鍵の設置とか、インフラ周りでやっておくことはあるので、それは別途ググればいっぱいでてくると思います。