注:该文编写是结合了 Fabric 官方文档中的 defing-host-lists 以及 parallel execution 这两个章节来写的
当我们需要批量管理不同功能的服务器的时候,为了让不同功能的服务器执行不同的命令,这时候就需要用到 Fabric 的 Roles 的 env.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 会按照如下顺序执行:
- update on web1
- update on web2
- update on web3
- reload on web1
- reload on web2
- reload on web3
当使用并行执行的时候(通过查看 P 获取更多细节),变成了这样:
- update on web1, web2, and web3
- reload on web1, web2, and web3
这个是收益非常明显的 - 如果花费了 5s 运行 以及 2s 重载。串行执行会花费 (5+2)*3=21s的时间,而并行执行仅仅只需要花费其三分之一的时间,平均(5+2)=7s左右。
怎么使用并行
因为并行执行影响一个任务是最小的单元。这个功能可以通过使用 parallel 和 serial 装饰符来控制开关。例如, 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
连续执行的结果如下:
- runs_in_parallel on host1, host2, and host3
- runs_serially on host1
- runs_serially on host2
- 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 参数,用如下代替:
$ fab -P -z 5 heavy_task
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。