V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
fir.im Rio
fir.im 平台更新日志
快速获取 UDID
1 - 3 分钟发布应用
同时支持 iOS 和 Android
灵活设置应用权限
实时查看应用动态消息
自定义显示历史版本
随时了解应用下载情况
如果你也喜欢简单快速又美观的工具平台,就用 fir.im 吧!
BugHD
Imshaha
V2EX  ›  fir.im

自动化部署工具 Capistrano 与 Mina

  •  
  •   Imshaha · 2015-01-15 20:11:00 +08:00 · 6138 次点击
    这是一个创建于 3396 天前的主题,其中的信息可能已经有所发展或是发生改变。

    这篇文章是 FIR.im Ruby 工程师黄轩宇的分享,文章总结了他如何使用 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, '[email protected]'          # 设置项目的 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 都可以通过 beforeafter 来添加自定义的 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 命令部署成功后的图片

    <img src="/images/deploy.png" alt="Capistrano Deploy">


    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 就可以部署了, 非常的简洁.


    REF

    Capistrano 3 实现Rails自动化部署

    Capistrano 3 Tutorial

    Mina For Deployment

    26 条回复    2017-12-28 10:32:57 +08:00
    est
        1
    est  
       2015-01-15 20:16:56 +08:00
    用mina中。通过环境变量指定部署哪个机器。

    mina deploy to=prod

    在config.rb里需要处理下 if ENV['to'] == 'prod' set :domain, 'prod.xxx.com' end 这样。
    Imshaha
        2
    Imshaha  
    OP
       2015-01-15 20:24:05 +08:00
    中间有张部署成功图片是这样的:(image)[http://blog.naixspirit.com/images/deploy.png]
    Imshaha
        3
    Imshaha  
    OP
       2015-01-15 20:29:58 +08:00
    不好意思,markdown 语法不熟练 T-T, 应该是这个:![img](http://blog.naixspirit.com/images/deploy.png)
    Imshaha
        4
    Imshaha  
    OP
       2015-01-15 20:31:00 +08:00
    泪奔,原来评论不支持。。。
    blacktulip
        5
    blacktulip  
       2015-01-15 20:32:32 +08:00 via iPhone
    @Imshaha 传 imgur,然后贴图片直链地址
    sennes
        6
    sennes  
       2015-01-15 20:35:52 +08:00
    用markdown发的主题看起来真的很舒服!
    lovewilliam
        7
    lovewilliam  
       2015-01-15 21:07:54 +08:00
    每次看到楼主头像都点进来看看
    Travis
        8
    Travis  
       2015-01-15 21:20:59 +08:00 via iPhone
    @Imshaha 我是来看楼主评论里秀markdown语法排列组合的
    bittenbydog
        9
    bittenbydog  
       2015-01-15 21:44:35 +08:00
    同7楼
    GuBonjour
        10
    GuBonjour  
       2015-01-15 22:15:59 +08:00
    @Imshaha 默默地过来看楼主秀Markdown技能的 XD
    dishonest
        11
    dishonest  
       2015-01-15 22:21:33 +08:00
    一直觉得capistrano 太复杂,耦合又深。

    如果rbenv等工具版本更新 那是不是cap也要跟着更新

    用Fabric直接run command,会不会更灵活点?
    naixspirit
        12
    naixspirit  
       2015-01-15 22:50:18 +08:00
    @dishonest rbenv/rvm 等工具版本更新后, cap 是不需要更新的, 使用 cap 的好处是, 它的 flow 已经集成了大部分 Rails 所需要的东西, 而且 github 上还有大量的插件, 可以跟 Rails 紧密的结合了起来. 事实上, 我们用 nodejs 开发的项目也用上了 cap.
    Imshaha
        13
    Imshaha  
    OP
       2015-01-15 22:53:37 +08:00
    @GuBonjour
    @Travis
    你们这样真的好么,我会好好练习的,掩面泪奔。
    Imshaha
        14
    Imshaha  
    OP
       2015-01-15 22:55:46 +08:00
    @lovewilliam
    @bittenbydog 好吧,头像是有美白处理过的,哈哈。言归正传,这真的是一篇严肃认真的技术分享文章,好么:D
    Imshaha
        15
    Imshaha  
    OP
       2015-01-15 22:56:43 +08:00
    @sennes 还在熟悉Markdown语法过程中~
    guoer
        16
    guoer  
       2015-01-15 23:00:54 +08:00
    看头像进来的
    不是说不准全文转载吗?
    naixspirit
        17
    naixspirit  
       2015-01-15 23:01:08 +08:00
    Imshaha
        18
    Imshaha  
    OP
       2015-01-15 23:17:14 +08:00
    @blacktulip 谢谢指导,原谅我没有仔细看 FAQ
    Imshaha
        19
    Imshaha  
    OP
       2015-01-15 23:18:29 +08:00
    @guoer 因为是我们团队工程师写的,并放在公司子节点而不是其他节点,所以是OK的啦~
    guoer
        20
    guoer  
       2015-01-15 23:19:59 +08:00
    @Imshaha 原来如此
    那姐姐约吗 XD
    Imshaha
        21
    Imshaha  
    OP
       2015-01-15 23:48:33 +08:00
    @guoer 。。。早点休息,小盆友
    redf
        22
    redf  
       2015-01-15 23:50:39 +08:00
    我可以证明,lz 确实很白
    Imshaha
        23
    Imshaha  
    OP
       2015-01-16 00:12:17 +08:00
    @redf 求不黑 T-T
    GuBonjour
        24
    GuBonjour  
       2015-01-16 12:49:21 +08:00 via iPhone
    @guoer 2333333 那姐姐约吗
    dishonest
        25
    dishonest  
       2015-01-16 15:50:44 +08:00
    @naixspirit 功能好是好, 总觉得ruby社区做的好多太复杂的东西了.

    我自己研究ror很久了, 之前很热衷用各种神奇的库, 单测试就数种高大上的工具, 后来发现好像搞复杂了. =.=b

    然后我就回python了 哈哈
    spkinger
        26
    spkinger  
       2017-12-28 10:32:57 +08:00
    这文档很有用,刚搜到的=。=,攒下
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   我们的愿景   ·   实用小工具   ·   2182 人在线   最高记录 6543   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 31ms · UTC 15:42 · PVG 23:42 · LAX 08:42 · JFK 11:42
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.