使用git capistrano nginx Unicorn搭建无缝部署服务器

一直做的是一些前端的工作,对服务器的配置部署没有很完整的经验。这次自己完整的配置了一台服务器,记录一下同时也可以让有相同需求的人少走一些弯路。

如果配置完成的话,只要修改好本地的版本,然后git push一下,最后运行命令 cap deploy,你的本地版本就会部署到服务器上,这个过程新旧版本是无缝切换的,听上去是不是非常的棒呢!

环境安装

首先是环境安装,这部分用了个脚本

https://raw.github.com/panlilu/scripts/master/stackscript

请务必完整的看一下脚本再进行安装

把这个wget到服务器上,适当编辑后(修改deploy账号密码,根据个人需要调整等)然后执行

$ wget https://raw.github.com/panlilu/scripts/master/stackscript
$ vi stackscript
$ sudo bash stackscript

然后就可以开始配置了,首先要对nginx进行配置:

Nginx

$ cd /usr/local/nginx/conf
$ vi nginx.conf

编辑成这样

#/usr/local/nginx/conf/nginx.conf
worker_processes  2;
worker_cpu_affinity 01 10;
worker_rlimit_nofile 51200;

error_log  /home/deploy/log/nginx/error.log;
#error_log  logs/error.log  notice;
#error_log  logs/error.log  info;

#pid        logs/nginx.pid;

events {
    use epoll;
    worker_connections  51200;
}

http {
    include       mime.types;
    default_type  application/octet-stream;
    charset utf-8;
    access_log  /home/deploy/log/nginx/access.log;
    sendfile        on;
    tcp_nopush     on;

    keepalive_timeout  65;
    tcp_nodelay on;

    gzip on;
    gzip_min_length  1k;
    gzip_buffers     4 16k;
    gzip_http_version 1.0;
    gzip_comp_level 2;
    gzip_types       text/plain application/x-javascript text/css application/xml text/javascript;
    gzip_vary on;

    include /usr/local/nginx/conf/vhosts/*.conf;
}

注意上面配置文件倒数第二行,主配置文件会包含 /usr/local/nginx/conf/vhosts/ 下的.conf后缀配置文件作为虚拟主机配置

我们在/usr/local/nginx/conf/vhosts/下新建配置文件 <your_site>.com.conf

注意替换<your_site>作为你自己的域名

#/usr/local/nginx/conf/vhosts/<your_site>.com.conf

limit_conn_zone $binary_remote_addr zone=default:10m;
upstream <your_site>_backend {
    server unix:/tmp/unicorn.<your_site>.sock fail_timeout=0;
}
server {
    listen      80;
    server_name <your_site>.com www.<your_site>.com;
    root /home/deploy/app/<your_site>/current/public;
    try_files $uri/index.html $uri.html $uri @unicorn;
    location ~* ^(/assets|/favicon.ico) {
        gzip_static on;
        access_log        off;
        expires           max;
    }
    location @unicorn {
        limit_conn default 5;
        proxy_redirect     off;
        proxy_set_header   Host $host;
        proxy_set_header   X-Forwarded-Host $host;
        proxy_set_header   X-Forwarded-Server $host;
        proxy_set_header   X-Real-IP        $remote_addr;
        proxy_set_header   X-Forwarded-For  $proxy_add_x_forwarded_for;
        proxy_buffering    on;
        proxy_pass         http://<your_site>_backend;
    }
}

配置完成后重启nginx

$ sudo /etc/init.d/nginx restart

如果重启成功没有返回什么错误,这个时候你如果域名绑定成功了,访问域名出现一个503bad gateway错误的话,说明你的配置大概已经成功了。

接下来需要配置应用了

关于如何配置本地的rails环境,我就不多说了,参考rails官网。

新建一个rails应用后,你需要对其进行一些修改:

Unicorn

首先我们要修改应用的gemfile使其包含unicorn

# Gemfile
gem "unicorn"

group :development do
  gem "capistrano"
end

然后 bundle intall 一下就安装好了

然后对unicorn进行配置

#config/unicorn.rb
rails_env = ENV['RAILS_ENV'] || 'production'
app_path = "/home/deploy/app/<your_site>/current"
listen "/tmp/unicorn.<your_site>.sock"
worker_processes 4
pid "#{app_path}/tmp/pids/unicorn.pid"
stderr_path "#{app_path}/log/unicorn.log"
stdout_path "#{app_path}/log/unicorn.log"
preload_app true
timeout 30

before_fork do |server, worker|
  old_pid = "#{app_path}/tmp/pids/unicorn.pid.oldbin"
  if File.exists?(old_pid) && server.pid != old_pid
    begin
      Process.kill("QUIT", File.read(old_pid).to_i)
    rescue Errno::ENOENT, Errno::ESRCH
      puts "Send 'QUIT' signal to unicorn error!"
    end
  end
end

这样就完成了。

capistrano

先要安装capistrano
和安装unicorn差不多只要配置gemfile然后bundle install就行

# Gemfile
group :development do
  gem "capistrano"
end

bundle install完成之后还需要运行下面命令来生成一些文件

capify .

配置capistrano比较的麻烦,我直接帖出我已经配置好的文件

#config/deploy.rb
require "bundler/capistrano"
set :application, "<your_site>"
set :user, "deploy"
set :use_sudo, false
set :repository,  "deploy@git.<your_site>.com:repo/<your_site>.git" #请改成自己的git源
set :scm, :git
# Or: `accurev`, `bzr`, `cvs`, `darcs`, `git`, `mercurial`, `perforce`, `subversion` or `none`

set :deploy_to, "/home/deploy/app/#{application}"
set :deploy_via, :remote_cache
set :domain, "feather.<your_site>.com"

set :normalize_asset_timestamps, false   #for Solve the probem: Not precomplie assets

role :web, domain                          # Your HTTP server, Apache/etc
role :app, domain                         # This may be the same as your `Web` server
role :db,  domain, :primary => true # This is where Rails migrations will run

set :rails_env, "production"

after "deploy", "deploy:cleanup"
namespace :deploy do
  task :start, :roles => :app do
    run "cd #{deploy_to}/current/; RAILS_ENV=production bundle exec unicorn_rails -c config/unicorn.rb -D"
  end

  task :stop, :roles => :app do
    run "kill -QUIT `cat #{deploy_to}/current/tmp/pids/unicorn.pid`"
  end

  desc "Zero-downtime restart of Unicorn"
  task :restart, :roles => :app do
    run "kill -USR2 `cat #{deploy_to}/current/tmp/pids/unicorn.pid`"
  end

  task :setup_config, roles: :app do
    sudo "ln -nfs #{current_path}/config/unicorn_ini.sh /etc/init.d/unicorn_#{application}"
    run "mkdir -p #{shared_path}/config"
  end
  after "deploy:setup", "deploy:setup_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

#  desc "run 'bundle install' to install Bundler's packaged gems for the current deploy"
#  task :bundle_install, :roles => :app do
#    run "cd #{current_path} && bundle install"
#  end

  before "deploy", "deploy:check_revision"

  after 'deploy:update_code' do
    run "cd #{release_path}; RAILS_ENV=production rake assets:precompile"
  end
end

# if you're still using the script/reaper helper you will need
# these http://github.com/rails/irs_process_scripts

# If you are using Passenger mod_rails uncomment this:
# namespace :deploy do
#   task :start do ; end
#   task :stop do ; end
#   task :restart, :roles => :app, :except => { :no_release => true } do
#     run "#{try_sudo} touch #{File.join(current_path,'tmp','restart.txt')}"
#   end
# end

task :mongoid_create_indexes, :roles => :web do
  run "cd #{deploy_to}/current/; RAILS_ENV=production bundle exec rake db:mongoid:create_indexes"
end

Unicorn_ini.sh 这个脚本配置完成后会自动软链接到/etc/init.d/unicorn_<your_site> 的,方便控制unicorn的运行

#config/unicorn_ini.sh

#!/bin/sh
### BEGIN INIT INFO
# Provides:          unicorn
# Required-Start:    $remote_fs $syslog
# Required-Stop:     $remote_fs $syslog
# Default-Start:     2 3 4 5
# Default-Stop:      0 1 6
# Short-Description: Manage unicorn server
# Description:       Start, stop, restart unicorn server for a specific application.
### END INIT INFO
set -e

# Feel free to change any of the following variables for your app:
TIMEOUT=${TIMEOUT-60}
APP_ROOT=/home/deploy/app/<your_site>/current
PID=$APP_ROOT/tmp/pids/unicorn.pid
CMD="cd $APP_ROOT; bundle exec unicorn -D -c $APP_ROOT/config/unicorn.rb -E production"
AS_USER=deploy
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

配置完成后,本地运行:

cap deploy:setup

补充

为了防止每次静态文件编译过慢,我们需要引入一个新的gem

gem 'turbo-sprockets-rails3'

服务器的路径结构是这样的

~/app/<your_site> 应用的存放目录

~/app/<your_site>/current 当前的版本

~/app/<your_site>/releases 最近的releases

~/app/<your_site>/shared 应用的公用文件,比如日志等

~/app/repo 存放git仓库

~/log log的链接

~/conf 配置文件的链接

至于git的配置我想大家都轻车熟路,我就不多说了。

参考

http://ariejan.net/2011/09/14/lighting-fast-zero-downtime-deployments-with-git-capistrano-nginx-and-unicorn/

https://gist.github.com/jrochkind/2161449

以上

发表评论

电子邮件地址不会被公开。 必填项已用*标注