fir.im Rio
fir.im 平台更新日志
快速获取 UDID
1 - 3 分钟发布应用
同时支持 iOS 和 Android
灵活设置应用权限
实时查看应用动态消息
自定义显示历史版本
随时了解应用下载情况
如果你也喜欢简单快速又美观的工具平台,就用 fir.im 吧!
BugHD
Imshaha
V2EX  ›  fir.im

自动化部署工具 Capistrano 与 Mina

  •  
  •   Imshaha · Jan 15, 2015 · 7179 views
    This topic created in 4190 days ago, the information mentioned may be changed or developed.

    这篇文章是 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 replies    2017-12-28 10:32:57 +08:00
    est
        1
    est  
       Jan 15, 2015
    用mina中。通过环境变量指定部署哪个机器。

    mina deploy to=prod

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

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

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

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

    然后我就回python了 哈哈
    spkinger
        26
    spkinger  
       Dec 28, 2017
    这文档很有用,刚搜到的=。=,攒下
    About   ·   Help   ·   Advertise   ·   Blog   ·   API   ·   FAQ   ·   Solana   ·   3131 Online   Highest 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 133ms · UTC 14:11 · PVG 22:11 · LAX 07:11 · JFK 10:11
    ♥ Do have faith in what you're doing.