转载

Rails 中自动布署工具 mina 的经验谈

自动布署工具的意义

在敏捷开发中, 如果说自动化测试是它的一条腿, 自动化布署就是它的另一条腿, 缺一不可.

Rails 在 assets pipeline 的支援下, 是拥有着到目前为止最佳的布署方案, 布署复杂度就相对较高, 但如果手工来处理, 太繁琐了.

所以说, 自动布署在 Rails 中, 甚为有用.

下面先来 PK 下 Rails 中自动布署工具.

mina VS Capistrano

Capistrano 是 Rails 中最常见的选择, 尤其是 Capistrano2. 它的工作原理如下:

Rails 中自动布署工具 mina 的经验谈

即, 先针对 server 打开一个 ssh 隧道, 然后不断的发送命令给 server 执行, 每个命令是由本地 PC 生成的, 然后一一发送给

这样的优势在于, 命令更加容易通过 Ruby 脚本来控制, 易于编写更强大的插件, 维护更多的服务端实例.

而 mina 则不同, 它的特点就是更快, 工作原理类似于:

Rails 中自动布署工具 mina 的经验谈

即, 本地直接生成完整的发布脚本( bash 脚本 ), 通过 ssh 隧道, 一起上传给 server 执行.

这样的优势就是非常快, 几分钟的发布过程, 只需要十几秒完成. 缺点是多环境与多服务端会有一些特别的注意事项.

如果你对上面的工具还不熟悉, 建议分别去官方网站看看.

下面我想将 mina 的生态作一些介绍, 这样, 才能用的放心与舒心.

先讲讲最为常见的多环境发布支持.

多环境发布

所谓多环境发布是指, 在开发过程中, 我们要发布代码到测试环境, 发布代码到生产环境, 甚至英文与中文两个环境等等.

我都希望一条命令就可以搞定发布:

mina staging deploy  mina production deploy 

mina 本质上只是一个基础脚本生成框架, 帮助我们快速生成对应的发布脚本, 完成自动上传 server 并执行的框架. 所以并没有对多环境发布提供官方支持.

但设计良好的系统, 扩展起来非常简单.

经过实践, 自己编写一个扩展可行, 但没有必要, 直接用 mina-multistage 即可,

对应的 deploy.rb 举例如下:

set :stages, %w(en zh) set :default_stage, 'zh'  require 'mina/multistage' require 'mina/bundler' require 'mina/rails' require 'mina/git' require 'mina/rvm'    # for rvm support. (http://rvm.io) require 'mina/unicorn' require 'mina_sidekiq/tasks'  # 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/mongoid.yml', 'config/application.yml', 'log', 'tmp', 'public/uploads', 'public/personal' ]  task :environment do   queue! %[source /usr/local/rvm/scripts/rvm]   queue! %[rvm use 2.0.0] end 

然后在 config/deploy/ 创建两个对应的文件:

en.rb:

set :domain, 'yafeilee.me' set :deploy_to, '/home/ruby/wblog_en' set :repository, 'git@github.com:windy/wblog.git' set :branch, 'master' set :user, 'ruby' set :unicorn_config, -> { "#{deploy_to}/#{current_path}/config/unicorn/en.rb" } 

zh.rb

set :domain, 'yafeilee.me' set :deploy_to, '/home/ruby/wblog' set :repository, 'git@github.com:windy/wblog.git' set :branch, 'master' set :user, 'ruby' set :unicorn_config, -> { "#{deploy_to}/#{current_path}/config/unicorn/zh.rb" } 

你可以尽情地为每个环境配置自己的变量. 之后就可以用:

# 发布英文版本 mina en deploy 
# 发布中文版本 mina zh deploy 

非常方便. 需要注意的是, 在一些要引用其他变量的地方, 需要用 lambda 做成延迟加载.

自动发布最佳实践

  1. 无论何时何地, 可以快速回滚

mina 是通过以下目录结构实现回滚的:

/var/www/flipstack.com/     # The deploy_to path  |-  releases/              # Holds releases, one subdir per release  |   |- 1/  |   |- 2/  |   |- 3/  |   '- ...  |-  shared/                # Holds files shared between releases  |   |- logs/               # Log files are usually stored here  |   `- ...  '-  current/               # A symlink to the current release in releases/ 

通过 current 软链接到 releases/x 中, 可以方便回滚代码. 理解这个后, 方便配置的时候路径的调整.

  1. 配置文件是链接过来, 而不是写死的

shared 中存放所有配置文件, 并软链到 current 中.

这在 mina 上是默认非常简单的, 只要以下配置:

set :shared_paths, ['config/database.yml', 'config/application.yml', 'log', 'tmp/sockets', 'tmp/pids', 'public/uploads'] 

然后会自动软链接过去.

  1. 保持环境是独成一体的

我们经常会将两套环境放在一个 server 上, 所以保持环境独立是非常重要的, 主要是指如果有依赖 redis 之类的, 要设定好命名空间.

插件生态

我个人偏好使用 rbenv + unicorn + nginx 来布署 Rails 应用. 常用的组件如下:

  1. mina-unicorn

默认使用热布署方案, 非常方便:

# config/unicorn/zh.rb app_path = File.expand_path( File.join(File.dirname(__FILE__), '..', '..')) worker_processes   1 timeout            180 listen             '/tmp/unicorn_zh.sock' pid                "#{app_path}/tmp/pids/unicorn.pid" stderr_path        "log/unicorn.log" stdout_path        "log/unicorn.log"  before_fork do |server, worker|   if defined?(ActiveRecord::Base)     ActiveRecord::Base.connection.disconnect!   end    old_pid = "#{server.config[:pid]}.oldbin"   if File.exists?(old_pid) && server.pid != old_pid     begin       Process.kill("QUIT", File.read(old_pid).to_i)     rescue Errno::ENOENT, Errno::ESRCH       # someone else did our job for us     end   end end  after_fork do |server, worker|   if defined?(ActiveRecord::Base)     ActiveRecord::Base.establish_connection   end end  before_exec do |server| # fix hot restart Gemfile   ENV["BUNDLE_GEMFILE"] = "#{app_path}/Gemfile" end 
  1. mina/rbenv , mina/whenever

内置插件, 非常方便.

  1. mina-sidekiq

更新与重启 sidekiq 使用

自定义插件

mina 编写自己的插件非常简单, 只要是符合 Rake 的 task 就可以自动加入, 有几个 API 说明一下:

  1. queue

这个命令可以将你自己的插件代码插入到整个发布脚本中. queue! 只是详细模式.

  1. echo_cmd

将命令带上输出, 方便测试.

可以看一个例子:

mina-unicorn-tasks

除此之外, 我推荐一些方便的小贴士出来:

# add this after config/deploy.rb desc "Shows logs." task :logs do   queue %[cd #{deploy_to!}/current && tail -f log/production.log] end 

使用 mina logs 来实时查看生产环境的日志, 非常方便.

desc "Display the unicorn logs." task :unicorn_logs do   queue 'echo "Contents of the unicorn log file are as follows:"'   queue "tail -f #{deploy_to}/current/log/unicorn.log" end 

使用 mina unicorn_logs 来实时查看 unicorn 日志.

如果遇到问题, 可以使用:

mina deploy -v 来详细查看对应的命令执行过程.

如果遇到死活排查不出的问题, 可以用 mina deploy -S 来查看整个发布脚本生成情况, 然后手动到发布环境去执行对应的命令.

我的开源博客系统, 也采用的 mina, 可以做为本篇文章的参考实例: wblog mina example

选择 mina 还是 Capistrano

实际上 Capistrano 的生态环境更加良好, 但 mina 设计超级简单.

如果你像我一样, 对任何事情都想必须弄明白, 而且理解简单这个理念的重要性:

如无必要, 勿增实体.

那么强烈建议你选用 mina, 无论是发布速度, 还是未来的自动布署扩展性都会掌控在你的手中.

如果非常在意生态环境, 建议一定要选择 Capistrano, 作为 Rails 的发布工具, mina 可以与 Capistrano 叫板, 但 Capistrano 不仅如此, 它还是一个很成功的运维工具, 其设计理念, 复杂度都上升了一个维度.

这个时候, 就建议你试试 Capistrano.

但无论如何, 都强烈希望你能重视自动发布.

也许第一次, 你花了几天才把它调试成功, 但以后每次, 它都将节省你大量的时间.

关于真正的代码持续发布的特性( 本质上就是 webhook + hook system ), 留在以后的文章中介绍.

正文到此结束
Loading...