Mina 简介使用

说下 Mina

一、基本使用

1
2
3
gem 'ruby'

mina init    =>  生成 config/deploy.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
# config/deploy.rb
set :user, 'username'  # 部署用户
set :domain, 'your.server.com'   # 域名
set :deploy_to, '/var/www/flipstack.com' # 部署路径
set :repository, 'git@github.......xxx' #git 仓库
set :branch, 'master' #分支
set :shared_paths, ['config/database.yml', 'public/system' ...] #共享文件夹

然后:

1.
task: environment do
  invoke :'rvm:use[ruby-2.0.0@rails4.0.4]'
end

2.
# 主要为 deploy 做准备,创建各个文件夹
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! %[mkdir -p "#{deploy_to}/shared/public/qrcodes"]
  queue! %[mkdir -p "#{deploy_to}/shared/public/qrcodesZip"]

  queue! %[mkdir -p "#{deploy_to}/shared/public/uploads"]
  queue! %[mkdir -p "#{deploy_to}/shared/public/system"]

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

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

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

3.

# 编写部署任务
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 :'sidekiq:quiet'
    invoke :'git:clone'
    invoke :'deploy:link_shared_paths'
    invoke :'bundle:install'
    invoke :'rails:db_migrate'
    invoke :'rails:assets_precompile'

    to :launch do
      queue "if [ -d #{deploy_to}/current/tmp ]
      then
        touch #{deploy_to}/current/tmp/restart.txt
      else
        mkdir #{deploy_to}/current/tmp
        touch #{deploy_to}/current/tmp/restart.txt 
      fi"
      invoke :'sidekiq:restart'
    end
  end
end

终端执行

1
2
3
mina setup

mina deploy

二、应该要注意的 point

1、我们可以自己写 task 任务

2、queue 命令用在执行 bash 命令,如:

1
2
3
4
task :logs do
     queue 'echo "Contents of the log file are as follows:"'
     queue "tail -f /var/log/apache.log"
end

3、invoke 命令用在引用已经写好的 task 任务,如:

1
2
3
4
5
6
7
task :down do
     invoke :maintenance
end

task :maintenance do
     queue 'touch maintenance.txt'
end

4、Mina 已经有一些已经写好的 task 任务,如:

1
2
3
4
invoke :'git:clone'
invoke :'bundle:install'

#我一开始以为这是执行 bash 命令,让我理不清 invoke 跟 queue 的关系

5、run! 这个命令是指 SSH 进主机,然后执行所有已经 queue 的命令。这个命令会在 Rake 退出前,自动调用。

1
2
3
4
# 当 rake 退出时会调用。
  def mina_cleanup
    run! if commands.any?
  end

6、command 包含所有已经 queue 的任务

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
queue "sudo restart"
queue "true"

to :clean do     # 这里 clean 的queue 会被放到 :clean 命名空间下
  queue "rm"
end

commands == ["sudo restart", "true"]
commands(:clean) == ["rm"]


注意了,commands 是一个方法,而不是变量

# 源代码
def commands(aspect=:default)
  (@commands ||= begin
    @to = :default
    Hash.new { |h, k| h[k] = Array.new }
  end)[aspect]
end

因此如果要清空 commands,应该是执行:

@commands[:default] = []

7、isolate 命令会开辟一个新的 block,包含 queue 的任务

1
2
3
4
5
6
7
8
9
10
11
queue "sudo restart"
queue "true"

commands.should == ['sudo restart', 'true']

isolate do
  queue "reload"
  commands.should == ['reload']
end

commands.should == ['sudo restart', 'true']

8、已被 invoke 的 task, 再次被 invoke 时,是不会再次被执行的,除非:

1
2
3
4
# 该参数表明再次invoke时会继续执行一次。
invoke :setup, {reenable: true}

# 注意,任务是否已经被 invoke 过,并不是通过 commands 内的值来判断的,因此就算 commands 清空了,task 还是会被标记成已 invoke 过的。

9、

1
2
3
4
5
6
7
8
9
10
11
12
13
14
task :hello do
  set :deploy_to, 'hellooooooooo'
end

task :world do
  p deploy_to
end

$-> mina hello world

===>  'hellooooooooo'

说明 mina task1 task2 是可以串行执行的,并且任务1的环境会跟任务2相连。(同个执行环境)
通常用在设置不同的 stage

三、多机部署

一、将同一项目部署到多个服务器

1
2
3
4
5
6
7
8
9
10
11
12
13
set :domains, %w[192.168.0.12 192.168.0.13]

desc "multi deploy"
task :multi_deploy do
    domains.each_with_index do |domain, index|

      p "begin to deploy#{domain}"
      set :domain, domain
      invoke :deploy
      run!    => # 注意这里一定要加上 run! ,才会立即运行命令
      p "finish to deploy"
    end
end

自己写的一个 task, 这时候遇到一个难题,发现 invoke :deploy ,当第一次循环的时候正常,第二次循环的时候,会部署两次。

效果是这样的:

1
2
3
4
5
6
7
8
9
10
11
12
13
 begin to deploy server 192.168.0.12

    deploying.....

 finish to deploy server 192.168.0.12

 begin to deploy server 192.168.0.13

    deploying.....

 finish to deploy server 192.168.0.13

    deploying......  ( server 192.168.0.13 )

如果我的 domains 是

1
set :domains, %w[192.168.0.12 192.168.0.13 192.168.0.14]

那么效果会是:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
 begin to deploy server 192.168.0.12

    deploying.....

 finish to deploy server 192.168.0.12

 begin to deploy server 192.168.0.13

    deploying.....

 finish to deploy server 192.168.0.13

 begin to deploy server 192.168.0.14

    deploying.....

 finish to deploy server 192.168.0.14

    deploying......  ( server 192.168.0.14 )

发觉最终总是会多部署最后一个server

于是我用

1
p "命令集合:#{commands(:default)}"

来查看,也没有什么异常。

然后我又做了下实验:

1
2
3
4
5
6
7
8
9
task :deploy_primary do
  p "begin to deploy primary #{primary_domain}"
  set :domain, primary_domain
  invoke :deploy
  invoke :mysql_sync
  run!
  p "finish to deploy primary #{primary_domain}"
  p commands
end

原因

在 rake 退出前会自动调用 run! 这个方法,因此到最后是会多执行一次 commands 内的命令的。

1
2
3
def mina_cleanup!
  run! if commands.any?
end

解决方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
set :domains, %w[192.168.0.12 192.168.0.13]


desc "multi deploy"
task :multi_deploy do
   isolate do   # =>  开辟一个新的 block (这样isolate外面的commands为[])
    domains.each_with_index do |domain, index|
      p "begin to deploy#{domain}"
      set :domain, domain
      # 这里其实只 invoke 了一次,之所以执行了三次,是因为有三次 run!
      # 而 domain 参数并不是写进 commands 的,而是在 run! 的时候进行处理
      # 因此这里可以成功的分别部署到三台机器
      invoke :deploy
      run!
      p "finish to deploy"
    end
  end
  # rake 任务退出时执行 run!, 但此时 commands 为 []
end

二、将同一项目部署到同一机器的两个目录

因为该项目需要做全站国际化,因此我将它分别部署为两套 server.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
task :deploy_all do
  isolate do
    2.times do |sequence|
      p "begin to deploy #{sequence}"
      if sequence == 0
        set :deploy_to, '/home/deploy/xshare_en'
      else
        set :deploy_to, '/home/deploy/xshare_zh'
      end
      invoke :deploy
      run!
      p "end to deploy #{sequence}"
    end
  end
end

这么做的后果是:部署了两次到 /home/deploy/xshare_en去了。

原因

1、 deploy_to 的值是直接写进 commands 内的

1
[...Setting up /home/deploy/xshare_zh\" && (\n  mkdir -p \"/home/deploy/xshare_zh\" &&\n  chown -R `whoami` \"/home/deploy/xshare_zh\" &&\n  chmod g+rx,u+rwx \"/home/deploy/xshare_zh\" &&\....]

2、这里 :deployinvoke 了一次,因此 commands 内的值是没有变化的,所以相当于执行了两次一样的第一次commands

尝试1

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
task :deploy_all do
  isolate do
    2.times do |sequence|
      p "begin to deploy #{sequence}"
      if sequence == 0
        set :deploy_to, '/home/deploy/xshare_en'
      else
        set :deploy_to, '/home/deploy/xshare_zh'
      end
      # 重复 invoke
      invoke :deploy, reenable: true
      run!
      # 清空 commands
      @commands[:default] = []
      p "end to deploy #{sequence}"
    end
  end
end

结果还是没达到预料中的结果。

因为task deploy 中还 invoke 了其他的 task,比如git:clone,因此其他的task只算invoke了一次,就算我手动将deploy所引用的task全部都加reenable参数,还是不行,因为难保其他task没invoke另外的task

尝试2

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
task :deploy_all do
  2.times do |sequence|
  # 尝试将两者区分开block
    isolate do
      p "begin to deploy #{sequence}"
      if sequence == 0
        set :deploy_to, '/home/deploy/xshare_en'
      else
        set :deploy_to, '/home/deploy/xshare_zh'
      end
      # 重复 invoke
      invoke :deploy
      run!
      # 清空 commands
      @commands[:default] = []
      p "end to deploy #{sequence}"
    end
  end
end

但还是失败,:deploy 是否被 invoke,跟 isolate 无关,因为 isolate 控制的只是 commands 而已。

解决

没找到好办法,暂时分开task执行,不过这种使用场景也比较少。

单独执行命令

1
2
3
task :mysql_sync do
  queue %[cd #{deploy_to!}/current && whenever -i 'mysql_sync']
end

当初单独执行这个 task 的时候,一直提示找不到 whenever 这个命令,令人纳闷,最终发现需要加上环境。

1
2
3
task :mysql_sync => :environment do
  queue %[cd #{deploy_to!}/current && whenever -i 'mysql_sync']
end

重复的 queue

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
set :domains, %w[192.168.0.12 192.168.0.13]


desc "multi deploy"
task :multi_deploy do
   isolate do   # =>  开辟一个新的 block ( 不过为何能解决我还没弄懂)
    domains.each_with_index do |domain, index|
      p "begin to deploy#{domain}"
      set :domain, domain
      invoke :deploy
      if index == 1
         queue 'do something one'
      else
         queue 'do something two'
      end
      run!    => # 注意这里一定要加上 run! ,才会立即运行命令
      p "finish to deploy"
    end
  end
end

这样子的话,当第二次循环时,会执行 onetwo 两个 queue, 因为第一次循环时, one 已经 queued 了,进入了 commands,因此第二次循环时,会执行这个命令 ( 执行所有 commands)

这是个难点。我还没找到解决方法。只能说分开来执行,或者写脚本根据不同服务器执行不同命令