Capistrano 3 实现Rails自动化部署 - tianlu1677/tianlu1677.github.io GitHub Wiki
Offical Site: http://capistranorb.com/
Github: https://github.com/capistrano/capistrano
# unicorn 4.8.3
passenger
rails 4.1.4
ruby 2.1.2
capistrano 3.4.1
在Gemfile中添加Capistrano和其它用到的插件
Gemfile
group :development do
gem 'capistrano', '3.4.1'
gem 'capistrano-bundler'
gem 'capistrano-rails'
#gem 'capistrano-rbenv'
# Add this if you're using rvm
gem 'capistrano-rvm'
end
这个时候bundle一下
$ cap install
会生成如下目录文件,Capfile用来配置Capistrano,deploy.rb是一些共用task的定义,而production.rb/staging.rb用来定义具体的stage的tasks。
├── Capfile
├── config
│ ├── deploy
│ │ ├── production.rb
│ │ └── staging.rb
│ └── deploy.rb
└── lib
└── capistrano
└── tasks
安装完成之后,通过 cap T 来查看当前项目的可执行任务列表。
cap bundler:install # Install the current Bundler environment
cap deploy # Deploy a new release
cap deploy:check # Check required files and directories exist
cap deploy:check:directories # Check shared and release directories exist
cap deploy:check:linked_dirs # Check directories to be linked exist in shared
cap deploy:check:linked_files # Check files to be linked exist in shared
cap deploy:check:make_linked_dirs # Check directories of files to be linked exist in shared
cap install # Install Capistrano, cap install STAGES=staging,production
这些命令其中就包括 刚刚执行的cap install,其它用到的,后面再讲。
your_app/Capfile
require 'capistrano/setup'
require 'capistrano/deploy'
#require 'capistrano/rbenv'
require 'capistrano/rvm'
require 'capistrano/bundler'
require 'capistrano/rails/assets'
require 'capistrano/rails/migrations'
Dir.glob('lib/capistrano/tasks/*.cap').each { |r| import r }
# config valid only for Capistrano 3.1
lock '3.4.1'
set :application, 'qdaily4_app'
#if ENV['branch']
# set :application, "cms_#{ENV['branch']}"
#end
set :rails_env, 'production'
set :scm, :copy
# set :repository, "."
# set :deploy_via, :copy
# set :repo_url, '[email protected]:kaikeba/cms.git'
# Default branch is :master
# ask :branch, proc { `git rev-parse --abbrev-ref HEAD`.chomp }.call
# Default deploy_to directory is /var/www/my_app
set :deploy_to, "/home/qdaily4_app/projects/#{fetch(:application)}"
# Default value for :scm is :git
# set :scm, :git
# Default value for :format is :pretty
# set :format, :pretty
# Default value for :log_level is :debug
# set :log_level, :debug
# Default value for :pty is false
# set :pty, true
# Default value for :linked_files is []
# set :linked_files, %w{config/database.yml}
# Default value for linked_dirs is []
# set :linked_dirs, %w{bin log tmp/pids tmp/cache tmp/sockets vendor/bundle public/system}
# deploy.rb or stage file (staging.rb, production.rb or else)
#set :rvm_type, 'kkb' # Defaults to: :auto
# set :rvm_ruby_version, '2.1.1p76' # Defaults to: 'default'
#set :rvm_custom_path, '~/.rvm' # only needed if not detected
set :linked_files, %w{Gemfile.lock config/database.yml config/settings.yml config/sunspot.yml public/robots.txt}
set :linked_dirs, %w{log tmp vendor/bundle public/system public/uploads } #app/assets/subsystems/website/node_modules
#set
SSHKit.config.command_map[:rake] = "bundle exec rake"
SSHKit.config.command_map[:rails] = "bundle exec rails"
# Default value for default_env is {}
# set :default_env, { path: "/home/kkb/.rvm/rubies/default/bin:/home/kkb/.rvm/bin:/home/kkb/.rvm/gems/ruby-2.1.1/bin:$PATH" }
set :default_shell, '/bin/bash -l'
set :bundle_flags, '--quiet'
# Default value for keep_releases is 5
# set :keep_releases, 5
namespace :deploy do
desc 'grunt'
task :grunt do
on roles(:app), in: :sequence, wait: 5 do
grunt_dirs = %w{app/assets/subsystems/website/node_modules app/assets/subsystems/website/bower_components app/assets/subsystems/website/05-resource/lib} #app/assets/subsystems/website/node_modules
grunt_dirs.each do |dir|
execute " ln -nfs /home/kkb/projects/#{fetch(:application)}/shared/#{dir} /home/kkb/projects/#{fetch(:application)}/current/#{dir}"
end
execute "cd /home/kkb/projects/#{fetch(:application)}/current/app/assets/subsystems/website && bower update 'kkb-restful-service' --verbose && grunt" #, raise_on_non_zero_exit: false
end
end
# after "assets:precompile", :grunt
desc 'Restart application'
task :restart do
on roles(:app), in: :sequence, wait: 5 do
# Your restart mechanism here, for example:
# execute :touch, release_path.join('tmp/restart.txt')
# execute "cd /home/kkb/projects/#{fetch(:application)}/current/app/assets/subsystems/website && npm install && grunt"
execute " kill -USR2 `cat /home/kkb/projects/#{fetch(:application)}/current/tmp/pids/unicorn.pid` "
execute " ln -nfs /home/kkb/projects/#{fetch(:application)}/shared/public/uploads /home/kkb/projects/#{fetch(:application)}/current/public/uploads "
# execute :bundle, " exec unicorn_rails -c #{File.join(current_path,'config', 'unicorn.rb')} -D -E staging "
#execute :bundle, "exec sidekiq -d -L /home/kkb/projects/cms/current/log/sidekiq.log -q mailer -q default -q often -q seldom -e production"
end
end
after :publishing, :grunt
after :publishing, :restart
# after :restart, :clear_cache do
# on roles(:web), in: :groups, limit: 3, wait: 10 do
# # Here we can do anything such as:
# within release_path do
# execute :rake, 'cache:clear'
# end
# end
# end
after :finishing, "deploy:cleanup"
end
# 假设你服务器的ip是 67.87.98.78, 用户名是jack
# role :app, %w{server}
# role :web, %w{server}
# role :db, %w{server}
role :app, %w{67.87.98.78}
role :web, %w{67.87.98.78}
role :db, %w{67.87.98.78}
# server 'server', user: 'usernam', roles: %w{web app}, my_property: :my_value
server '67.87.98.78', user: 'jack', roles: %w{web app}, my_property: :my_value
你先要有服务器上的 key 值 cap production
关于Stage,详见:
一个很重要的配置是Role、Server(User)以及其对应关系,为了方便,Cap3中提供了多种配置形式,各有不同的侧重,但用途都是一样的,见下面。
stage中的role/server
# 以role为中心的写法
role :app, %w{[email protected], [email protected]}
role :web, %w{[email protected]}
role :db, %w{[email protected]}
# 以server为中心的写法,上面的写法可以用以下写法代替:
server 'example.com', user: 'deploy', roles: %w{web app db}
server 'example.local', user: 'deploy', roles: %w{app}
# 如果要对某服务器配置SSH等更多时,也可以用这种写法:
server 'example.com',
user: 'user_name',
roles: %w{web app},
ssh_options: {
user: 'user_name', # overrides user setting above
keys: %w(/home/user_name/.ssh/id_rsa),
forward_agent: false,
auth_methods: %w(publickey password)
# password: 'please use keys'
}
Stage示例
test.rb
set :stage, :test
set :branch, 'develop'
server '192.168.1.1', user: 'ares', roles: %w{web app db}
set :deploy_to, "/home/#{fetch(:deploy_user)}/apps/appname"
# dont try and infer something as important as environment from
# stage name.
set :rails_env, :test
# number of unicorn workers, this will be reflected in
# the unicorn.rb and the monit configs
set :unicorn_worker_count, 5
# whether we're using ssl or not, used for building nginx
# config file
set :enable_ssl, false
– 部署
$ cap production deploy --dry-run
$ cap production deploy
运行多次deploy之后会生成这样的目录结构:
.
├── current -> /home/ares/apps/appname/releases/20140325071623
├── releases
│ ├── 20140325065734
│ ├── 20140325071310
│ ├── 20140325071623
│ └── 20140325074922
├── repo
│ ├── branches
│ ├── config
│ ├── description
│ ├── FETCH_HEAD
│ ├── HEAD
│ ├── hooks
│ ├── info
│ ├── objects
│ ├── packed-refs
│ └── refs
├── revisions.log
└── shared
├── bin
├── bundle
├── config
├── log
├── public
├── tmp
└── vendor
release 每次发布都会产成一个目录,该目录下存放着Rails项目源码,多个目录是为了rollback而设。
current 是指当前版本,软链接到release下的某个版本目录。
repo 存的是项目的.git目录
shared 是项目中共享的内容,如config文件,不随每次发布而改动。
$ cap production deploy:rollback
Rollback其实就是把current目录指向到releases里上次发布的目录。
系统默认包含下面的这些Task,Task是有顺序的,在每个Task之前和之后可以通过before, after添加自定义的Task。更多Flow介绍,见这里
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
在这些Flow之外也有一些常用的Task:
deploy:check
对应Cap 2.x中的 deploy:setup
。
check不属于deploy flow,它的目的主要是在服务器上创建所需要的目录(主要是shared, release),然后就是对应的linked_dir & linked_files操作。
deploy:cold in 2.x
新的系统中,核心的deploy task是幂等(idempotent)的,所以像cap deploy:cold这样的预执行命令(创建目录结构)就不再被需要了。
dry run
dry-run主要是保证每一个命令都能被执行到,但不会在服务器上产生任何改动。
bash $ cap production deploy —dry-run
Cap中有和Rails一样的运行环境(Environment)的概念(Cap中叫作Stage,默认建了2个Stage,staging, production),就是不同环境下对应不同的服务器。
deploy.rb中定义着公用的变量,对应到不同的Stage,可以定义一些专有的变量,同时也可以覆写deploy.rb中的公用变量。
注:Stage名字默认对应Rails的environment名字(development, test, production)。可通过设置进行映射。
set :rails_env, :test
– linked_files & linked_dirs
Capistrano使用Shared目录来管理那些在不同Release中共用的文件,最主要的一个shared/config中包含每个发布所需要的配置文件
linked_dirs
是将项目的指定目录链接到shared目录中。这个操作会在从repo取下代码之后进行。
linked_files
和linked_dirs相反,它是将shared中的文件链接到项目中,文件要首先存在于shared目录中,不然deploy时会报错。 Rails项目中,主要就是database.yml,secret.yml这样的敏感文件。对于这些文件最好的做法就是从Git中过滤掉,然后每个开发者和服务器都单独配置。
在尝试使用SQLite3进行Cap测试通过后,将DB换成MySQL,然后又出错了。错误是数据库不存在,一直以为是在创建数据库时未指定RAILS_EVN,后来发现migrations在执行前也没有运行db:create来创建数据库。
在运行deploy前,自己手工创建数据库,然后deploy就能正常运行了。猜想:create属于一次性操作,所以没有将其作为task放入deploy中。
####– Ask
在deploy.rb和stage文件中,我们可以设置变量的值。但有时,值会在运行时才被确定,这样就可以通过ask来设定。
ask :branch, ‘my_default_branch’
这样在branch变量第一次被使用时,会有输入提示,就可以动态设置该值了。
####– Role
角色的目的是让Task可以运行在不同的机器上。更多
一个线上程序中有着多种服务器,像DB Server, APP server, Web Server,在不同的Server上要运行不同的部署方案。Cap也把这些也考虑到了,可以通过创建不同的Role来对这些服务器进行归类,为Task指定Role来运行。
查看官网的wifi中 task
可以在发布进行之前做一些准备工作,比如,创建linked_files、Unicorn、Nginx的配置及创建Service的工作。
现行的作法是,定制一个task,在Deploy之前进行,在服务器创建目录,然后从本地将文件预先upload!)到服务器的相应目录(主要是shared/config)。
Capistrano 3 推荐使用 passwordless sudo。这样非root用户也可以直接使用sudo命令,而不必通过PTY来输入密码。Guide:
其实就是在系统里,给指定用户赋上某些指令的sudo权限。在Ubuntu下,修改 /etc/sudoers来添加要使用的命令。
/etc/sudoers
ares ALL=NOPASSWD:/usr/sbin/service, /bin/ln
#也可以将所有程序都设置为不要密码,不过太不安全,不建议
#ares ALL=(ALL) NOPASSWD: ALL
PTY就是让用户在当前Terminal中执行任务时,可以进行交互。比如说执行sudo任务时,可以远程输入密码。 更多
ps :试着启用pts,但输入密码后无反应。
每次运行cap都要输入密码,可以将本地的ssh公钥存到server上,就可以省下很多时间。
ssh-copy-id就是这么一个将本机的公钥复制到远程机器的authorized_keys文件的工具,其也能让你拥有远程机器的home, ~./ssh , 和 ~/.ssh/authorized_keys的权利。
首先本地机器上要创建ssh key
$ ssh-keygen
$ ssh-copy-id [email protected]
Mac OSX 上 ssh-copy-id不是默认安装的,可通过Homebrew进行安装
$ brew install ssh-copy-id
-linked_file内容详解
Railscasts Video: deploying-to-a-vps base on 2.x
基于Capistrano工具的Rails程序部署方案
Happycasts Capistrano
Capistrano 3 Upgrade Guide
Capistrano 3 Tutorial with Unicorn **
ow to Deploy a Rails 4 App With Git and Capistrano 2.x
Capistrano 3.1 + Rails 4 + RVM Simple Single-Stage Configuration
Capistrano Version 3 Guide
How-To Deploy Rails Applications Using Capistrano 3.1 and Windows 7
How To Use Capistrano to Automate Deployments: Getting Started
How To Automate Ruby On Rails Application Deployments Using Capistrano