使用 Fabric 批量执行服务器任务

我们的服务器和虚拟机的环境配置都由 puppet 管理,但有时候需要临时执行某些任务和操作,比如同时更换500台服务器的密码、同时更新或者重启500台虚拟机、在特定几台服务器上添加或者一个用户、上传一个特定文件/脚本到1000台服务器等等。这些任务用 Puppet 可以做,但是不是最简洁的办法。我们需要一种工具能完成大量服务器上的批量操作,并且要简单可编程,Fabric 就是这样一个基于 Python 的服务器批量管理库/工具,Fabric 使用 ssh(通过 paramiko 库)在多个服务器上批量执行任务,我们只需要用 Python 编写这些任务脚本并指定要执行这些任务的服务器就可以了。

Fabric 依赖 paramiko,所以需要安装这两个:

$ sudo pip install fabric
$ sudo pip install paramiko

编写一个简单的 Fabric 例子,在1台服务器上打印系统信息(uname -s):

$ vi fabfile.py
#!/usr/bin/python

from fabric.api import run

def host_os():
    run('uname -s')

在 vpsee.com 这台主机(host)上使用 root 帐户执行上面的任务 host_os:

$ fab -H root@vpsee.com host_os
[root@vpsee.com] Executing task 'host_os'
[root@vpsee.com] run: uname -s
[root@vpsee.com] Login password for 'root': 
[root@vpsee.com] out: Linux

Done.
Disconnecting from root@vpsee.com... done.

来看一个更复杂点的例子,在多个服务器(grid00, grid02, …, grid05)上更换 root 密码(假设原密码是 root),注意加上 @parallel,这样任务是并行执行的,在大量服务器上会快很多:

#!/usr/bin/python
# -*- coding: utf-8 -*-
from fabric.api import *
import string
from random import choice
import socket
import paramiko

env.user = 'root'
env.password = 'root'
env.hosts = [ 'grid00', 'grid01', 'grid02', 'grid03', 'grid04', 'grid05']

@task
@parallel
def passwd(user, passwd=False):
    with settings(hide('running', 'stdout', 'stderr'), warn_only=True):
        if isup(env.host):
            if not passwd:
                passwd = genpass()
            sudo("echo -e '%s\n%s' | passwd %s" % (passwd, passwd, user))

def genpass(length=10):
    return ''.join(choice(string.ascii_letters + string.digits) for _ in range(length))

def isup(host):
    print 'connecting host: %s' % host
    timeout = socket.getdefaulttimeout()
    socket.setdefaulttimeout(1)
    up = True
    try:
        paramiko.Transport((host, 22))
    except Exception, e:
        up = False
        print '%s down, %s' % (host, e)
    finally:
        socket.setdefaulttimeout(timeout)
        return up

使用 fab -l 查看我们刚编写的 fabfile.py 里面的可用命令,这个命令就是那个函数名 def passwd(user, passwd=False):

$ fab -l
Available commands:

    passwd

使用这个命令批量更换 grid00-grid05 的 root 密码为 test,passwd 传递参数的时候接冒号,并且用户名和密码参数用逗号隔开:

$ fab passwd:root,test
[grid00] Executing task 'passwd'
[grid01] Executing task 'passwd'
[grid02] Executing task 'passwd'
[grid03] Executing task 'passwd'
[grid04] Executing task 'passwd'
[grid05] Executing task 'passwd'
connecting host: grid05
connecting host: grid04
connecting host: grid02
connecting host: grid03
connecting host: grid01
connecting host: grid00

Done.

Fabric 的强大之处在于可以用 Python 编写各种任务(函数),这些任务可以指定到任何服务器上(通过 ssh),非常适合管理大量服务器、批量处理任务。另外 Fabric 工作方式非常简单容易理解,就是简单的 ssh 而已,没有内幕没有架构,不像其他工具什么客户端服务器端、什么中间代码、什么 DSL 领域专属语言(嗯,说的就是 puppet, chef)。Fabric 使用起来只需要懂点 Python 就可以了,不需要学命令、api、框架之类的。我们喜欢简单、容易理解的工具。

评论 (14 Comments)

  1. 喜欢,学习了

  2. Ansible怎么样?
    http://ansible.cc/faq.html

  3. 强大~

  4. ansible 和 fabric 不太算一类,ansible 应该和 puppet, chef, cfengine 等在一起算作 configuration management 工具,fabric 更像是 deployment/automation 工具。ansible 后面设计的想法不错,虽然其作者(也是 cobbler, func 的作者)的想法是不用像 puppet, chef 那样需要琢磨透一种配置语言,不过 ansible 还是需要一种 YAML 格式的配置语言 Playbooks.

  5. 那么saltstack呢?
    http://saltstack.org/

  6. fabric支持ssh跳板登录么?

  7. ansible好像支持fabric的功能,比如更改用户密码:

    ansible 127.0.0.1 -K -m shell -a “/bin/echo username:mypassword | /usr/sbin/chpasswd”

    127.0.0.1可以用一组主机,就可以同时在多台主机上执行命令。

    ansible简介:https://github.com/hyao/ansible/blob/devel/README_zh.md
    playbook简介(翻译中):https://github.com/hyao/ansible-docs/blob/master/zh/playbooks.rst

  8. 如果所有的虚拟机在客户手上,而我想对虚拟机进行某个设置(比如升级下内核),又无法获得客户虚拟机管理权限,这种情况怎么办???

  9. 技术流啊,网站非常不错,推荐了。

  10. 既然用 puppet,可以考虑下 mcollective。

    fabric依赖与ssh,稳定性不是很好,实际使用中容易出现问题。

    mcollective使用消息中间件来做,不同的是需要在客户机有个daemon来接收处理这个消息,比较满足自家机器的管理,灵活性更高。

  11. @nomyself
    嗯,考虑过 MCollective,配置比 Fabric 复杂,MCollective 依赖 ActiveMQ 中间消息件,ActiveMQ 又依赖 Java 工具包。我们喜欢简单的工具~

  12. MCollective用做跨idc,分布式部署很有用。想想,每个IDC里的内部服务器不能直接访问,ssh通过跳板又比较麻烦。

  13. 你好!我的执行结果如下,不知道为啥报错啦!请教下:
    [root@linuxblind fabric]# fab passwd:root,test
    [linuxblind] Executing task ‘passwd’
    [linuxblind2] Executing task ‘passwd’
    [linuxblind3] Executing task ‘passwd’
    connecting host: linuxblind3
    connecting host: linuxblind2
    connecting host: linuxblind

    Fatal error: Needed to prompt for a connection or sudo password (host: linuxblind), but input would be ambiguous in parallel mode

    Aborting.

    Fatal error: Needed to prompt for a connection or sudo password (host: linuxblind3), but input would be ambiguous in parallel mode

    Aborting.

    Fatal error: Needed to prompt for a connection or sudo password (host: linuxblind2), but input would be ambiguous in parallel mode

    Aborting.

    Fatal error: One or more hosts failed while executing task ‘passwd’

    Aborting.

  14. 楼主给力的,简单顺手才是王道。
    其实自己写一个也很容易,我手里自己写的一个ssh_sudo.rb,只有51行。
    通过ssh做类似的事情。中间还增加了sudo这个操作。
    [build101:~/bin]$ wc -l ssh_sudo.rb
    51 ssh_sudo.rb
    [build101:~/bin]$
    [build101:~/bin]$ ssh_sudo.rb logs101 whoami
    root
    [build101:~/bin]$

发表评论