自動化部署工具 Capistrano 與 Mina

jopen 9年前發布 | 23K 次閱讀 Capistrano

最近折騰了一下 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/

 本文由用戶 jopen 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!