自动化运维工具Fabric - 角色管理以及并行执行

注:该文编写是结合了 Fabric 官方文档中的 defing-host-lists 以及 parallel execution 这两个章节来写的

当我们需要批量管理不同功能的服务器的时候,为了让不同功能的服务器执行不同的命令,这时候就需要用到 FabricRolesenv.roledefs 这个功能,

角色管理

主机字符串匹配单个主机,但是有时候根据主机分组是非常有用的。或许你有一组在负载均衡下的 Web 服务器,并且你想对它们做全部更新操作,或者是在所有的客户端服务器上运行一个任务。 Roles 提供了一种正确给主机字符串分组的字符串定义方式,能通过该字符串代替整个主机组。

这个映射被定义成字典的方式。 env.roledefs 为了被使用,必须在 fabfile 中被定义。下面是一个简单的例子:

from fabric.api import env

env.roledefs['webservers'] = ['www1', 'www2', 'www3']

因为 env.roledefs 默认是是空值。或许你需要选择重新分配值而不用为丢失任何信息而担心(当你在修改它的时候得保证你没有加载其他任何的 fabflies)。

from fabric.api import env

env.roledefs = {
    'web': ['www1', 'www2', 'www3'],
    'dns': ['ns1', 'ns2']
}

除了列表目标主机的类型之外,env.roledefs 的值是立即生效的。当在查找以及代替模块加载时间在运行的时候就被调用了(and will thus be called when looked up when tasks are run instead of at module load time)【注:求高手正确翻译】。(例如,你可以连接远程服务器取得角色定义,并且在被调用的时候不用担心在 fabfile 文件加载的时候引起延迟。比如: fab --list)。
使用 roles 没有任何其他要求 -- 它仅仅是在你有服务器分组需求的情况下能提供一种便利的方式。

个人实践并且在生产应用了的代码,修改后删减如下:

#!/usr/bin/python env
from fabric.api import env
from fabric.api import run
from fabric.api import roles
from fabric.api import execute
#from fabric.context_managers import execute




#env.user = 'username'
env.password = 'password'
env.roledefs = {
    'test1': ['host1', 'host2'],
    'test2': ['host3', 'host4']
}

@roles('test1') 
def get_version():
    run('cat /etc/issue')

@roles('test2')
def get_host_name():
    run('hostname')

def execute_all():
   execute(get_version)
   execute(get_host_name)

当你想在 web1 组里面只执行一台服务器的时候,你可以使用 env.exclude_hosts或者是 -x这个命令行参数来排除

env.exclude_hosts=['host1']

fab -R web1 -x host1,host5 get_version

这样执行的时候就会把 host1 这台服务器给排除在外了。

并行执行

Fabric 默认是串行执行的(更多细节请参考 Execution strategy )。这个部分描述了在多个主机上并行执行任务的选择。可以通过每个任务的装饰器或者是命令行的全局开关来处理。

注:该功能只在 1.3 以及以后版本有。

因为 Fabric 1.x 默认不是线程安全的(并且因为在一般情况下,任务功能之间不会相互影响)这个功能是通过 Python 的 multiprocessing 模块实现的。它为每一个任务以及主机组合创建了一个新的线程。随意的使用了滑动窗口来避免在同一时间运行太多线程。

例如,设想一个你想更新许多 Web 服务器应用程序代码的场景。一旦代码分发出去,立即重载 web 服务(当更新失败的时候,允许很容易的回滚),如下的 fabfile 能实现如上需求:

from fabric.api import *

def update():
    with cd("/srv/django/myapp"):
        run("git pull")

def reload():
    sudo("service apache2 reload")

然后在3台 web 服务器上串行的执行,如下:

$ fab -H web1,web2,web3 update reload

正常的情况下,没有使用并行执行, Fabric 会按照如下顺序执行:

  1. update on web1
  2. update on web2
  3. update on web3
  4. reload on web1
  5. reload on web2
  6. reload on web3

当使用并行执行的时候(通过查看 P 获取更多细节),变成了这样:

  1. update on web1, web2, and web3
  2. reload on web1, web2, and web3

这个是收益非常明显的 - 如果花费了 5s 运行 以及 2s 重载。串行执行会花费 (5+2)*3=21s的时间,而并行执行仅仅只需要花费其三分之一的时间,平均(5+2)=7s左右。

怎么使用并行

因为并行执行影响一个任务是最小的单元。这个功能可以通过使用 parallelserial 装饰符来控制开关。例如, fabfile 如下:

from fabric.api import *

#并行执行任务
@parallel
def runs_in_parallel():
    pass

#串行执行任务
def runs_serially():
    pass

当按照下面这种方式运行的时候:

$ fab -H host1,host2,host3 runs_in_parallel runs_serially

连续执行的结果如下:

  1. runs_in_parallel on host1, host2, and host3
  2. runs_serially on host1
  3. runs_serially on host2
  4. runs_serially on host3

命令行标记

你可以通过使用命令行标记 -P 或者是环境变量 env.parallel 强制所有的任务并行执行,尽管如此,任何使用 serial 特别标记了的任务,都将忽略并行标记,继续串行执行。

例如:

from fabric.api import *

def runs_in_parallel():
    pass

@serial
def runs_serially():
    pass

当这样调用的时候:

$ fab -H host1,host2,host3 -P runs_in_parallel runs_serially

如以前,runs_in_parallel 并行执行,runs_serially 串行执行。

Bubble size (队列池样的概念?)

在大批量主机的情况下,用户的 Fabric 本地主机因为运行了太多的 Fabric 进程可能会不堪重负,导致 Fabric 主机负载太高。因为这个,你可能需要选择一个 moving bubble 的方法来限制 Fabric 活跃并行进程的指定数量。

默认的情况下,Fabric 是没有 bubble 被使用的,所有的主机运行在一个并行池里面。你可以通过为没一个任务给 parallel 来指定 pool_size 关键字来重写它。或者是通过全局的 -z 来设置。

例如,在某一时间运行5台主机:

from fabric.api import *

@parallel(pool_size=5)
def heavy_task():
    # lots of heavy local lifting or lots of IO here
    #

或者是跳过 pool_size 参数,用如下代替:

相关推荐