402v /posts/railskai-fa-huan-jing-capistrano-3shi-xian-railszi-dong-hua-bu-shu

Rails开发指南 - Capistrano 3实现Rails自动化部署

From Where: Rails开发指南 - Nginx+Unicorn的服务器配置

我们之所以使用rails的就是为了简单方便,于是就少不了自动化发布我们的项目,配置之后只要每次运行cap deploy就可以把本地开发的项目发布到服务器上,没有任何多余的操作而且是zero downtime的!

#基于Capistrano自动发布(Deploy)

Rails自动化发布通常有可以有两个不同的gem来实现,本文所讲的Capistrano是其中之一,另外一个mina算是较轻量级的deploy库,使用也很广泛,配置的方式与cap3比起来也是大同小异。

###Capistrano初始化 首先在gem中添加Capistrano相关的gems,因为除了cap的gem外,cap的官网上还提供了一些插件用于控制bundler、rails、rbenv等等。

group :development do

	# Use Capistrano for deployment
	gem 'capistrano', '~> 3.1.0'

	# rails specific capistrano funcitons
	gem 'capistrano-rails', '~> 1.0.0'

	# integrate bundler with capistrano
	gem 'capistrano-bundler'

	# if you are using RBENV
	gem 'capistrano-rbenv', "~> 2.0"
end

运行bundle install安装gems;

然后运行bundle exec cap install初始化capistrano的环境,会创建这样几个文件:

我们一个一个来解释:

  • deploy.rb是最主要的一个,运行cap deploy时的主要配置都在这里;

  • Capistrano有stage的概念,类似于rails的environments,默认有两个starge:staging和production。因此新版的cap运行deploy时需要指定stage:cap production deploy,后面会讲到如何指定一个默认的stage。deploy/staging.rbdeploy/production.rb两个文件就是在运行不同stage时特定配置的指定文件,有区别的配置放在这里,各个stages通用的配置尽量放在deploy.rb

  • lib/capistrano/tasks:之前提到lib文件夹时已经说过这个文件夹主要存放默写gem用到的资源和rake文件。Capistrano3整体迁移到rake上来,所以cap3的task编写基本和rake文件一样。

  • 除此之外还要介绍一个与Gemfile统计目录下的Capfile,这个文件主要是用来为cap模块载入需要的.cap文件和.rb文件。根据需要require对应的插件,Dir.glob('lib/capistrano/tasks/*.cap').each { |r| import r }这一句将tasks路径下的task文件都加载进来。当然还可以增加Dir.glob('lib/capistrano/tasks/*.rb').each { |r| import r }这样一行load所有的.rb文件。根据我们接下来需要的配置和已引入的插件gems,我们会开启下列几个require:

      require 'capistrano/rails'
      require 'capistrano/rbenv'
      require 'capistrano/bundler'
      require 'capistrano/rails/assets'
    

###Deploy文件配置 deploy文件分为两个主要部分:

  1. 参数配置部分(多数以set方法开头,设置各种各样的环境变量);
  2. task部分,默认只有deploy这一个namespace。

deploy文件(或者说Capistrano部署)的核心是deploy这个namespace下的task,Cap3在deploy的时候会按照一定的次序运行这些task来完成工作。

  1. 如果想要git update之后执行bundle install,需要rbenv和bundler两个plugin;
  2. /usr/bin/env bundle not found 问题的解决办法,原因是cap默认使用non-login, non-interactive shell这幅图解释了shell启动时加载环境变量的过程。
  3. app_name & git repo etc.
  4. default stage
  5. deploy_to: /var/www/app_name 需要root权限,可以发布到/home/#{fetch(:deploy_user)/apps/app_name
  6. linked_files & linked_dirs: 影响其他配置,如nginx、unicorn
  7. database.yml,
    • 如果现在deploy会报错:ERROR linked file /home/deploy/apps/projects/shared/config/database.yml does not exist on server.com,因为database.yml文件内配置的是数据库用户名密码等私密信息,因此不能通过放到git库中同步到server。

    • 原因如Capistrano官方所说敏感文件一定要在本地ignore掉,ignore这步rails new的时候已经做了;

    • 最正确的处理办法是通过scp把database.yml文件拷贝到server上:

        scp config/database.yml deploy@oneboxapp.com:/home/deploy/apps/projects/shared/config/database.yml
      
    • 这里还有一些关于database.yml等敏感文件处理的办法。

    • 自动化task的做法是新建setup task然后在udpated工作流之后运行它:

          task :setup do
            sh "scp config/database.yml deploy@oneboxapp.com:/home/deploy/apps/projects/shared/config/database.yml"
          end
      
  8. deploy成功,根据我们之前的配置,80端口收到的网络请求是通过nginx转发给unicorn的,此时需要重启之前配置好的unicorn_projects服务:
  9. assets:precompile,找不到database.yml的解决办法:

###需要自动化的部分

  1. git clone之后bundle install
  2. database.yml拷贝
  3. unicorn init程序
  4. nginx网站配置
  5. unicorn重启
  6. nginx重启

之前每次配置deploy.rb文件都是从网上down一份,没有仔细研究,所以这次手打了一份,从最基础的参数配置和task编写开始,因此中间也遇到一些比较初级的问题,希望对遇到这些问题的人有所帮助。

  1. deploy unicorn:

    • 遇到一个问题:无论如何deploy,production页面的assets MD5也不会改变。问题的原因是deploy的过程中没有重启unicorn,而unicorn在执行过程中会把整个rails缓存下来,因此无论如何生成都会展示老页面。
  2. asset-pipeline配置:(fingerprint,production,pre-compile)

###deploy tasks

  1. unicorn的重新启动,配置文件如下:

     #!/bin/sh
    
     ### BEGIN INIT INFO
     # Provides:          unicorn
     # Required-Start:    $all
     # Required-Stop:     $all
     # Default-Start:     2 3 4 5
     # Default-Stop:      0 1 6
     # Short-Description: starts the unicorn app server
     # Description:       starts unicorn using start-stop-daemon
     ### END INIT INFO
    
     set -e
    
     USAGE="Usage: $0 <start|stop|restart|upgrade|rotate|force-stop>"
    
     # app settings
     USER="deploy"
     APP_NAME="projects"
     APP_ROOT="/home/$USER/apps/$APP_NAME/current"
     ENV="production"
    
     # environment settings
     PATH="/home/$USER/.rbenv/shims:/home/$USER/.rbenv/bin:$PATH"
     # -su: bundle: command not found
     CMD="cd $APP_ROOT && /home/$USER/.rbenv/shims/bundle exec unicorn -c config/unicorn.rb -E $ENV -D"
     PID="$APP_ROOT/tmp/pids/unicorn.$APP_NAME.pid"
     OLD_PID="$PID.oldbin"
    
     # make sure the app exists
     cd $APP_ROOT || exit 1
    
     sig () {
       test -s "$PID" && kill -$1 `cat $PID`
     }
    
     oldsig () {
       test -s $OLD_PID && kill -$1 `cat $OLD_PID`
     }
    
     case $1 in
       start)
         sig 0 && echo >&2 "Already running" && exit 0
         echo "Starting $APP_NAME"
         su - $USER -c "$CMD"
         ;;
       stop)
         echo "Stopping $APP_NAME"
         sig QUIT && exit 0
         echo >&2 "Not running"
         ;;
       force-stop)
         echo "Force stopping $APP_NAME"
         sig TERM && exit 0
         echo >&2 "Not running"
         ;;
       restart|reload|upgrade)
         sig USR2 && echo "reloaded $APP_NAME" && exit 0
         echo >&2 "Couldn't reload, starting '$CMD' instead"
         $CMD
         ;;
       rotate)
         sig USR1 && echo rotated logs OK && exit 0
         echo >&2 "Couldn't rotate logs" && exit 1
         ;;
       *)
         echo >&2 $USAGE
         exit 1
         ;;
     esac
    

可以用两种方式: * 保存一个shell文件放在/etc/init.d下,每次通过service重启这个服务。

        sudo chmod 755 /etc/init.d/unicorn_appname
        sudo update-rc.d unicorn_appname defaults

 通过下面这条命令重启

            sudo service unicorn_appname start
* 另一种方式是将shell文件放在github库里面,通过capistrano的deploy命令触发它。

		# in deploy.rb
		set(:executable_config_files, %w(
		  unicorn_init.sh
		))

		# in some setup cap
		executable_files = fetch(:executable_config_files)
		executable_files.each do |file|
		  execute :chmod, "+x #{shared_path}/config/#{file}"
		end

 `#{shared_path}/config/#{file}`即为unicorn启动shell文件的路径。

###Stage文件配置及默认Stage Staging的概念可以看官方文档,如果不配置默认stage,每次运行是必须指定,比如: cap production deploy这样,在Capfile中添加:

Rake::Task[:production].invoke

即可指定默认stage为production,可参见so上的回答

#参考连接

  1. Capistrano 3 Tutorial - 通过配置文件控制deploy过程

Where to go: Rails开发指南 - 问题整理和常见命令

评论 · 0

还没有评论。