1. mina是什么?

开发完了网站要上线或正在开发,要测试,那得把应用部署到线上,也就是互联网上,那就是真实环境,客户或用户能访问到的环境。公司的测试人员也能在上面测试,你总不能用你的localhost环境映射ip地址后给非程序员的测试人员测试吧。所以需要把本地上的应用部署到线上。说到部署,其实就是让线上能跑通程序,最简单的,就是把代码上传到线上主机。以前,可能会用到ftp,这种最简单了,但太粗糙了,又麻烦,改一点东西就要打开ftp工具,然后上传。后来有了版本控制工具。虽然比ftp好。但还是不够灵活。每次改完东西,你就得登录主机,然后clone代码,做些操作,最后重启服务。这一系列的过程你不得不重复。老是重复一件相同的事,既浪费人力也浪费时间。所以有了自动化。反正做的事都是一样,就用程序来代劳吧。ruby on rails的自动化部署工具有capistrano。这个比较为大家所熟知,不过我们介绍的不是这个,是另一个,它的名字叫mina

2. 为什么选择mina?

capistrano是很强大的,使用的人数也很多,插件也多,它慢慢地从部署工具上升到运维工具的层面了。我们不来比较capistrano,我们只说mina。mina很简单,上手很快,速度也比较快,也很灵活,插件也是有不少,功能也算比较强大。当你部署多台主机,多个环境(staging)时选择mina是不错的,用它写自定义脚本也是比较轻松的。下面会慢慢体会到,也会简单说说mina的部署原理,让你知其然又知其所有然。我们通过学习mina来理解自动化部署,你会了mina,理解了自动化部署,相信掌握capistrano也就不在话下,因为它们只不过是两种方式,思想是差不多的。

3. 使用mina

假设你已经拥有了一个rails项目,想要部署。

首先你得阅读一下mina官方github上的readme文件吧。

打开Gemfile文件,添加下面这行。

gem 'mina', require: false

执行bundle install,安装gem。

3.1 初始化创建config/deploy.rb部署配置文件
$ mina init

这个跟着做就好,只是创建了config/deploy.rb文件,创建完后可以打开来看看。

这个文件内容的几个选项是很重要的,理解了这些,你才能理解mina,才能理解自动化部署。

set :user, 'yinsigan'
set :domain, 'rails365.net'
set :deploy_to, '/home/yinsigan/rails365'
set :repository, 'git@github.com:yinsigan/rails365.git'
set :branch, 'master'
set :term_mode, nil

我们先分开来说,然后再合起来说。user就是你部署主机上的用户,你在部署的主机上创建一个账号(除了root)供日常使用,那个账号就可以用来做部署用(可以参考这篇文章zai-a-li-yun-ubuntu-zhu-ji-shang-an-zhuang-ruby-on-rails-bu-shu-huan-jing)。

domain是指域名,你也可以直接写ip地址。这个是你部署主机的域名或ip地址,指向的就是你的主机。通过这个就能找到主机,就能找到用户,相当于能执行ssh yinsigan@rails365.net,有了域名和用户,再加上密码,就能登录了吧。当你部署的时候,会提示你输入密码的,这个先不用管。

那部署到哪里呢,总要有一个地方来放程序代码吧,那就是deploy_to指定的路径,是部署主机上的绝对目录。

代码从哪里来呢。一般来说,我们是用git来作为版本控制工具,用它来下载代码。所以repository指定的就是代码的来源之处。可以是github、coding、gitlab等源码托管工具的地址。最好是git协议开头的地址,就像上面的示例代码那样的。

git仓库是有很多分支的,你的应用可未必是master分支,可以用develop等分支来部署测试环境的。所以需要指定分支,就是branch这个参数。

注意:你要确保你的主机是能下载到代码的,所以有必要将主机的ssh public key上传到托管源码库的地方

term_mode这个参数你可以先不加,它是解决一个问题,但你在部署时,发现等了很多都没有任何输出时,就可以用它,它好像跟终端的模式有关吧。我一般都会加上这个参数,它有利而无害。

综上所述,有主机,有用户,有部署目录,有部署的代码来源,基本上部署的要素就够了。

文件的其他部分我们先不管,我们接着一步步来。

3.2 进入部署主机创建部署目录

要手动进入部署主机,并创建上面参数deploy_to指定过的目录。

$ ssh yinsigan@rails365.net

$ mkdir /home/yinsigan/rails365
$ chown -R yinsigan /home/yinsigan/rails365

也就几个命令的问题。

3.3 部署前准备运行 'mina setup'

在部署之前有些准备工具要做。我们就说clone代码这个,你的部署主机要能从源码托管平台下载代码,必须把你的部署主机上的public key传到托管平台上吧。这样才能下载代码。另外,还有一些共享的文件也要处理。例如日志文件,配置文件等 。先想一个问题。上一步我们是创建了部署目录,这个目录是会供nginx来读取的。但是是空的。还没有任何东西存在。不是说过可以从源码库clone代码下来吧,那也行,clone下来后那就是有大部分的代码了。为什么说有大部分呢,因为有些文件还是不能放在源码库中的。

什么文件呢,例如log下的日志文件,数据库的配置文件config/database.yml。先说说日志,存放的是访问的日志,这些东西可没必要放源码中,因为一点意义都没有。还有config/database.yml文件,也不放源码库中,因为每个人安装的系统库的情况不一样,或许用户不同,或许数据库名不同,线上和开发环境更是不一样,这个文件是因人而异的。

一般来说,在一些开源项目中,别人是放database.yml.example文件,供人下载,然后改成自己需要的。那这样怎么办,mina是这样处理的。我们先在一个地方放置好这几个共享的文件,每次部署时,clone代码下来,然后通过软链接,建立到这几个文件的链接就好。也就是说,每次部署时就几个文件都是你事先放好的文件,他们部署后的只不过是链接而已。这样就实现了共享。

mina本身就提供了这种解决方案。

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

用的是shared_paths这个变量,数据里面列出的就是要共享的文件,也就是说,这里面的文件会被链接。

mina也提供了命令来处理这些文件。假如是config/database.yml,你要先创建这个文件,然后再填一些内容。mina setup就是来办这些事的。例如:

task :setup => :environment do
  queue! %[mkdir -p "#{deploy_to}/#{shared_path}/log"]
  queue! %[chmod g+rx,u+rwx "#{deploy_to}/#{shared_path}/log"]

  queue! %[mkdir -p "#{deploy_to}/#{shared_path}/config"]
  queue! %[chmod g+rx,u+rwx "#{deploy_to}/#{shared_path}/config"]

  queue! %[touch "#{deploy_to}/#{shared_path}/config/database.yml"]
  queue  %[echo "-----> Be sure to edit '#{deploy_to}/#{shared_path}/config/database.yml'."]

  queue! %[touch "#{deploy_to}/#{shared_path}/config/application.yml"]
  queue  %[echo "-----> Be sure to edit '#{deploy_to}/#{shared_path}/config/application.yml'."]

  queue! %[mkdir -p "#{deploy_to}/#{shared_path}/tmp/sockets"]
  queue! %[chmod g+rx,u+rwx "#{deploy_to}/#{shared_path}/tmp/sockets"]

  queue! %[mkdir -p "#{deploy_to}/#{shared_path}/tmp/pids"]
  queue! %[chmod g+rx,u+rwx "#{deploy_to}/#{shared_path}/tmp/pids"]

  queue! %[mkdir -p "#{deploy_to}/shared/pids/"]
end

运行mina setup命令。

创建好database.ymlapplication.yml等文件后,再进服务器填内容就好了。

3.5 部署 'mina deploy'

在部署前有个要注意的地方,我们线上的ruby是用rbenv来装的。所以需要加载rbenv这个库。

mina的readme文件里搜索一下rbenv,很简单,一个require语句,再加上三行代码。

require 'mina/rbenv'

task :environment do
  invoke :'rbenv:load'
end

然后到服务器创建好数据库。

接下来运行mina deploy就好了。

desc "Deploys the current version to the server."
task :deploy => :environment do
  to :before_hook do

  end
  deploy do
    invoke :'sidekiq:quiet'
    invoke :'git:clone'
    invoke :'deploy:link_shared_paths'
    invoke :'bundle:install'
    invoke :'rails:db_migrate'
    invoke :'rails:assets_precompile'
    invoke :'deploy:cleanup'

    to :launch do
      invoke :'unicorn:restart'
      invoke :'sidekiq:restart'
    end
  end
end

关于mina和unicorn或puma的结合我们在另外的章节介绍。到此,整个部署完毕。