fabric是什么?
Fabric 是一个Python库,可以通过SSH在多个host上批量执行任务。你可以编写任务脚本,然后通过Fabric在本地就可以使用SSH在大量远程服务器上自动运行。这些功能非常适合应用的自动化部署,或者执行系统管理任务。
让我们首先看一个例子。我们知道在*NIX下面,uname命令是查看系统的发行版。可以写这样一个Fabric脚本:
from fabric.api import run
def host_type():
run('uname -s')
将上面的脚本保存为fabfile.py,就可以通过fab命令在多个主机上执行host_type脚本了:
$ fab -H localhost,linuxbox host_type
[localhost] run: uname -s
[localhost] out: Darwin
[linuxbox] run: uname -s
[linuxbox] out: Linux
任务函数
Fabric中的任务就是一个python函数,姑且让我们称之为“任务函数”。既然是python函数,那么对函数的一些用法也适用于任务函数。比如传递参数、互相调用、返回值等等。首先看一个传递参数的例子:
def
hello(name="
world
"
):
print
("
Hello %s!
"
% name)
在执行任务的时候,可以通过fab的命令行参数为任务函数传递参数:
$ fab hello:name=Holbrook
Hello Holbrook!
组合任务的例子如下:
from fabric.api import run
def host_type():
run('uname -s')
def hello(name="world"):
print("Hello %s!" % name)
def composite(name="world"):
hello(name)
host_type()
Fabric提供的命令
前面我们见过了fabric.api模块中的run函数,其功能是在远端主机上执行命令。fabric.api中还提供了local函数,用于执行本地(Fabric所在的主机)命令。如下:
from
fabric.api import
local
def
lslocal():
local('
ls
'
)
类似远端命令和本地命令,Fabric也区分远端目录和本地目录。Fabric提供的对远端和本地目录的操作分别是cd和lcd。如果你用过命令行的ftp,这很容易理解。让我们看一个例子:
def filepath():
remote_dir = '/opt/xxx'
with cd(remote_dir):
run("touch README")
管理服务器连接
前面的例子中,都需要在fab命令行参数中指定server。当要管理大量服务器时很麻烦。Fabric提供了环境变量的字典env,其中就包含了hosts字典项,可以定义需要连接的server。如下:
from
fabric.api import
env, run
env.hosts = ['
host1
'
, '
host2
'
]
def
mytask():
run('
ls /var/www
'
)
也可以为每个任务单独指定要执行该任务的host列表:
from fabric.api import env, run
def set_hosts():
env.hosts = ['host1', 'host2']
def mytask():
run('ls /var/www')
一个比较完整的例子
import re
import json
import os
import sys
import time
from fabric.api import local, roles, run, execute, parallel, cd, put
from fabric.api import env
from fabric.state import output
from stat import *
log_file ="/duitang/logs/sys/fab.log"
class Logger(object):
def __init__(self, filename="Default.log"):
self.terminal = sys.stdout
self.log = open(filename, "a")
def write(self, message):
self.terminal.write(message)
self.log.write(message)
def flush(self):
self.terminal.flush()
self.log.flush()
def close(self):
self.terminal.close()
self.log.close()
def isatty(self):
return False
sys.stdout = Logger(log_file)
sys.stderr = sys.stdout
def load_config():
"""加载配置"""
result = {}
file = open("env.conf", "rb")
configs = [config.strip() for config in file.readlines() \
if config and config.strip() and not config.strip().startswith("#")]
for config in configs:
name, val = config.split("=")
name, val = name.strip().lower(), val.strip()
try:
val =json.loads(val)
except:
pass
result[name] = val
for name, val in result.items():
if isinstance(val, str):
pattern = re.compile(r"\$\{\s*(?P<name>\w+)\s*\}")
cnames = re.findall(pattern, val)
for cname in cnames:
pattern = re.compile(r"\$\{\s*%s\s*\}" % cname)
val, count = re.subn(pattern, result.get(cname.lower()), val)
if count:
result[name] = val
return result
@roles("gunicorn")
def where(image):
run("ls %s; echo ok" % image)
config = load_config()
static_dir, duitang_dir = config.get("static_dir"), config.get("duitang_dir")
local_template_dir, online_template_dir = os.path.join(duitang_dir, "templates"), os.path.join(static_dir, "templates")
local_templatesopen_dir, online_templatesopen_dir = os.path.join(duitang_dir, "templatesopen"), os.path.join(static_dir, "templatesopen")
local_templatesmobile_dir, online_templatesmobile_dir = os.path.join(duitang_dir, "templatesmobile"), os.path.join(static_dir, "templatesmobile")
env.roledefs = {
'nginx': config.get("nginx").split(","),
'gunicorn': env.hosts or config.get("gunicorn").split(","),
}
def deploy_static(restart=True):
execute(compress_style)
local("python gerenate_version.py %s %s" % (static_dir, static_dir))
execute(push_static_version, restart=False)
if restart:
execute(restart_all_servers)
@parallel
@roles("gunicorn")
def push_static_version(restart=True):
fversion_path = os.path.join(static_dir, "fversion.json")
put(fversion_path, duitang_dir)
if restart:
run("kill -HUP `more %s`" % "/duitang/data/work/gunicorn.pid")
@parallel
@roles("nginx")
def compress_style():
with cd("/duitang/dist/app/bin/"):
run("sh single_style.sh")
# gfw is not to deploy in this way!!!!!!!!!!
@roles("gunicorn")
def deploy_gfw(restart=True):
svnup('/duitang/dist/app/trunk/duitang/gfw/')
if restart:
run("touch %s" % "/duitang/data/work/gunicorn.reload")
# gfw is not to deploy in this way!!!!!!!!!!
@roles("gunicorn")
def deploy_api(restart=True):
svnup('/duitang/dist/app/trunk/duitang/api/')
if restart:
run("touch %s" % "/duitang/data/work/gunicorn.reload")
@roles("gunicorn")
def deploy_beansdb(restart=True):
svnup("/duitang/dist/app/trunk/duitang/dynamicloader/middleware.py")
#run("python /duitang/dist/app/trunk/duitang/beansdb/import_to_db.py %s" % date)
@roles("gunicorn")
def deploy_template(restart=True):
svnup(local_template_dir)
svnup(local_templatesopen_dir)
svnup(local_templatesmobile_dir)
# run("python /duitang/dist/app/bin/compress_template.py")
# if restart:
# run("touch %s" % "/duitang/data/work/gunicorn.reload")
@roles("gunicorn")
def deploy_python(restart=True):
svnup(duitang_dir)
if restart:
run("kill -HUP `more %s`" % ("/duitang/data/work/gunicorn.pid"))
time.sleep(2)
@roles("gunicorn")
def restart_all_servers():
run("kill -HUP `more %s`" % ("/duitang/data/work/gunicorn.pid"))
time.sleep(2)
@roles("nginx")
def restart_all_nginx():
time.sleep(5)
run("sudo nginx -c /duitang/dist/conf/nginx/nginx.conf -s reload")
@roles("gunicorn")
def check_gu_timeout(last=8):
run("/duitang/dist/app/bin/check_timeout.py %d"%(last))
@roles("gunicorn")
def start_all_gu():
#safe even if gunicorn is already started
run("gunicorn_django -c /duitang/dist/conf/gunicorn/gunicorn.conf", pty=False)
def xrun(command, hidden='', *args, **kwargs):
old_state = output.running
output.running = False
print '[%s] run: %s' % (env.host_string, command)
run(command + hidden, *args, **kwargs)
output.running = command
def svnup(path):
run("svn update %s %s" % (path, '--username app --password "x6QN)TnX"'))
def deploy():
execute(deploy_static, restart=False)
execute(deploy_template, restart=False)
execute(deploy_python, restart=False)
execute(restart_all_servers)
参考:http://smilejay.com/2013/03/fabric-introduction/
http://www.libaoyin.com/2013/07/21/使用python-fabric批量执行远端命令/