自动化部署工具 Capistrano 与 Mina

jopen 10年前

最近折腾了一下 CapistranoMina 两个自动化部署工具, 总结一些个人的观点和经验.


Capistrano 

Capistrano(下文简称 Cap) 特别适合于 Rails 应用的自动化部署, 特别是 Cap3, 整合了很多与 Rails 相关自动部署的命令(可用 cap -T 查看).


0. 服务器目录结构

首先来看看经过多次部署后, 服务器会生成一个这样的目录结构

├── current -> /var/www/your_app/releases/20141201042659  ├── releases  │   ├── 20141201032351  │   ├── 20141201042256  │   ├── 20141201042659  ├── repo  │   ├── branches  │   ├── hooks  │   ├── info  │   ├── objects  │   └── refs  └── shared      ├── bin      ├── bundle      ├── config      ├── log      ├── public      ├── tmp      └── vendor
  • current 是指当前版本, link 到 release 下的指定版本目录(默认为最新的 releases)

  • releases 每次部署都会产成一个目录存放项目源码, 目录个数由:keep_releases变量来控制

  • repo 项目的 .git 目录

  • shared 是项目中共享的内容, 不会随部署而改变


1. 安装 Cap

# Gemfile  gem 'capistrano', '~> 3.2.0'  gem 'capistrano-rails', '~> 1.1.0'  gem 'capistrano-bundler', '~> 1.1.0'  gem 'capistrano-rvm', '~> 0.1.0'  gem 'capistrano3-unicorn', '~> 0.2.0'  # Add this if you're using rbenv  # gem 'capistrano-rbenv', '~> 2.0.0'


2. Cap install 生成相关部署文件

$ bundle install之后执行$ bundle exec cap install, 会自动生成以下几个文件

├── Capfile                   # Cap 配置文件  ├── config  │   ├── deploy  │   │   ├── production.rb     # 不同环境的部署配置  │   │   └── staging.rb  │   └── deploy.rb             # 公共变量  └── lib      └── capistrano              └── tasks         # 一些自定义的 task

3. Capfile 文件

Cap 的配置文件, 可按需求加载一些插件

require 'capistrano/setup'               # 加载 Cap 的 DSL  require 'capistrano/deploy'              # 加载 Cap 的 Workflow  require 'capistrano/rvm'                 # 加载 RVM 相关配置  # require 'capistrano/rbenv'             # 加载 Rbenv 相关配置  require 'capistrano/bundler'             # 加载 Bundle, 以便完成 bundle install  require 'capistrano/rails/assets'        # 加载 Rails 的 js, css 文件预编译  require 'capistrano/rails/migrations'    # 加载 Rails 的数据库自动迁移  require 'capistrano3/unicorn'            # 加载 Unicorn 服务    Dir.glob('lib/capistrano/tasks/*.rake').each { |r| import r }

其中,require 'capistrano3/unicorn'后, 会增加几个 Unicorn 的部署命令, 可在本地远程运行命令直接控制服务器 Unicorn 的服务

cap unicorn:add_worker               # 增加一个 Unicorn Worker  cap unicorn:duplicate                # Restart  cap unicorn:legacy_restart           # 传统方式 Restart, 使用 USR2 + QUIT  cap unicorn:reload                   # Reload  cap unicorn:remove_worker            # 减少一个 Unicorn Worker  cap unicorn:restart                  # 无缝 Restart, 使用 USR2  cap unicorn:start                    # 启动 Unicorn  cap unicorn:stop                     # 关闭 Unicorn (QUIT)


4. config/deploy.rb 文件

用来配置不同环境的一些公共变量

# config valid only for Capistrano 3.1  lock '3.2.1'    set :application, 'your app name'     # 设置部署项目的名字  set :repo_url, 'git@xxx.git'          # 设置项目的 git repo  set :deploy_to, "/var/www/xxx"        # 设置部署目录  set :deploy_user, 'deploy'            # 设置部署时的用户    set :scm, :git  set :format, :pretty  set :pty, true    # linked_dirs  # 是将项目的指定目录 link 到 shared 目录中, 这个操作会在从 repo 取下代码之后进行.  # 比如 log 目录, 每次部署完毕后新的版本中的 log 目录都会 link 到 shared/log 下  set :linked_dirs, %w{bin log tmp/pids tmp/cache tmp/sockets vendor/bundle public/system}    # linked_files  # 它是将 shared 目录中的文件 link 到项目中, 文件要首先存在于 shared 目录中, 不然 deploy 时会报错  # 在 Rails 项目中, 主要就是 database.yml, secret.yml 这样的敏感文件  set :linked_files, %w{config/database.yml}    # rvm_type  # :auto (default): just tries to find the correct path. ~/.rvm wins over /usr/local/rvm  # :system: defines the RVM path to /usr/local/rvm  # :user: defines the RVM path to ~/.rvm  set :rvm_type, :system  set :rvm_ruby_version, '2.1.2'  set :rvm_roles, [:app, :web, :db]    set :keep_releases, 10   # 最多存放十个部署版本    namespace :deploy do      # 自定义了一个部署任务, 即自动运行 rake RAILS_ENV=rails_env db:create    # 其中 release_path 指的是当前 release 目录    # `fetch(:rails_env)` 读取配置中的 rails_env 变量, 并在 rake 命令中带上 env 变量    task :create_database do      on roles(:db) do        within release_path do          with rails_env: fetch(:rails_env) do            execute :rake, 'db:create'          end        end      end    end      before :migrate, :create_database     # 在每次 rake db:migrate 前都运行 rake db:create    after  :finishing, 'deploy:cleanup'   # 在每次部署完毕后, 清理部署时生成的 tmp 文件及多余的版本  end

这里有几个细节需要特别注意:

  • deploy_user一定需要有/var/www/[deploy_to]的权限, 在自动化部署中, 部署者的权限是个很重要的因素!

  • deploy_user建议使用id_rsa.pub authorized_keys的方式进行免密码登陆, 这也是 cap3 中推荐的方法

  • 首次部署, 需要执行cap [environment] deploy:check命令, 把linked_files在服务器上创建!

  • 首次部署, 如果用 Mysql 的话, 服务器上是没有 database 的, 需要自己手动rake db:create, 这里我写了个create_database的 task 来执行rake db:create

  • rvm 与 rbenv 的安装路径一定要确认, 是在 root 下安装的还是在deploy_user下安装的, 这里强烈建议用deploy_user安装 rvm/rbenv, 并且把/var/www文件夹的权限赋予deploy_user, 这样会少走很多弯路


5. config/deploy/production.rb | staging.rb

Cap 中的运行环境(又称 Stage ), 默认建立了2个 Stage, 不同的 Stage 可以定义一些专有的变量, 并可以重写deploy.rb中定义的公共变量, 相当于不同环境下对应不同的服务器.

# Production Stage  set :stage, :production       # 设置 stage  set :branch, 'master'         # 设置 git branch  set :rails_env, :production   # 设置 rails_env    # 以下几个 server 会同时部署  server 'xxx.xxx.xxx.xxx', user: 'deploy', roles: %w(web app db), primary: true  server 'xxx.xxx.xxx.xxx', user: 'deploy', roles: %w(web app db)

这里需要说明的是roles的定义, 在 Cap 中, 不同的 server 是可以有不同的 role 的, 一些服务器负责 web 服务, 一些服务器负责 db 服务, 从上面的自定义create_database命令中, 可以看到,on roles(:db), 说明这条命令只有在指定了 role 为 db 的服务器上运行.

这里讲的是多台服务器同时部署, 那么如何实现多台服务器依次部署呢? 只要再写一个 Stage, 运行指定的 stage 就可以了


6. Cap Flow

部署的时候执行命令,cap [environment] deploy, Cap 会自动执行一系列 Task, 这些 Task 被称为 Cap Flow, 每个 Task 都可以通过before和after来添加自定义的 Task.

deploy    deploy:starting      [before]        deploy:ensure_stage        deploy:set_shared_assets      deploy:check                  # 检查 ssh, rvm/rbenv, linked_dirs, linked_files, git    deploy:started    deploy:updating      git:create_release      deploy:symlink:shared    deploy:updated      [before]        deploy:bundle               # 重新 bundle install      [after]        deploy:migrate              # rake db:migrate        deploy:compile_assets        deploy:normalize_assets    deploy:publishing      deploy:symlink:release      deploy:restart                # restart, 可自定义一些需要 restart 的服务    deploy:published    deploy:finishing      deploy:cleanup    deploy:finished      deploy:log_revision

7. Cap stage deploy

最后 show 一张运行cap [environment] deploy命令部署成功后的图片

自动化部署工具 Capistrano 与 Mina


Mina 自动化部署工具 Capistrano 与 Mina


Mina 相对于 Cap 来说, 较为简洁, 只有基本的几个 Task(可用 mina -T 查看), 有点 Sinatra VS Rails 的感觉, 其服务器部署结构与 Cap 类似.


1. 安装 Mina

# Gemfile  gem 'mina', '~> 0.3'

2. Mina init

$ bundle install之后执行$ mina init, 会生成config/deploy.rb文件, 定义了一系列的变量和 Task.

require 'mina/bundler'  require 'mina/rails'  require 'mina/git'  # require 'mina/rbenv'  # for rbenv support. (http://rbenv.org)  # require 'mina/rvm'    # for rvm support. (http://rvm.io)    # Basic settings:  #   domain       - The hostname to SSH to.  #   deploy_to    - Path to deploy into.  #   repository   - Git repo to clone from. (needed by mina/git)  #   branch       - Branch name to deploy. (needed by mina/git)    set :domain, 'foobar.com'  set :deploy_to, '/var/www/foobar.com'  set :repository, 'git://...'  set :branch, 'master'    # Manually create these paths in shared/ (eg: shared/config/database.yml) in your server.  # They will be linked in the 'deploy:link_shared_paths' step.  set :shared_paths, ['config/database.yml', 'log']    # Optional settings:  #   set :user, 'foobar'    # Username in the server to SSH to.  #   set :port, '30000'     # SSH port number.    # This task is the environment that is loaded for most commands, such as  # `mina deploy` or `mina rake`.  task :environment do    # If you're using rbenv, use this to load the rbenv environment.    # Be sure to commit your .rbenv-version to your repository.    # invoke :'rbenv:load'      # For those using RVM, use this to load an RVM version@gemset.    # invoke :'rvm:use[ruby-1.9.3-p125@default]'  end    # Put any custom mkdir's in here for when `mina setup` is ran.  # For Rails apps, we'll make some of the shared paths that are shared between  # all releases.  task :setup => :environment do    queue! %[mkdir -p "#{deploy_to}/shared/log"]    queue! %[chmod g+rx,u+rwx "#{deploy_to}/shared/log"]      queue! %[mkdir -p "#{deploy_to}/shared/config"]    queue! %[chmod g+rx,u+rwx "#{deploy_to}/shared/config"]      queue! %[touch "#{deploy_to}/shared/config/database.yml"]    queue  %[echo "-----> Be sure to edit 'shared/config/database.yml'."]  end    desc "Deploys the current version to the server."  task :deploy => :environment do    deploy do      # Put things that will set up an empty directory into a fully set-up      # instance of your project.      invoke :'git:clone'      invoke :'deploy:link_shared_paths'      invoke :'bundle:install'      invoke :'rails:db_migrate'      invoke :'rails:assets_precompile'        to :launch do        queue "touch #{deploy_to}/tmp/restart.txt"      end    end  end    # For help in making your deploy script, see the Mina documentation:  #  #  - http://nadarei.co/mina  #  - http://nadarei.co/mina/tasks  #  - http://nadarei.co/mina/settings  #  - http://nadarei.co/mina/helpers

3. Mina setup

部署的前执行命令,mina setup, 创建 shared 文件夹, 在编辑需要 shared 的文件后, 再执行mina deploy就可以部署了, 非常的简洁.

来自:http://blog.naixspirit.com/2014/12/14/cap_and_mina/