Python远程部署利器Fabric详解

本文主要介绍Centos6.x下Python远程部署利器Fabric的使用。
本文参考:http://python.jobbole.com/87241/

1.安装Fabric

前提是python版本必须是2.7以上。

1
2
# python -V
Python 2.7.13

推荐用pip来安装,也可以从github上clone下来安装。

1
2
3
4
5
# pip install fabric==1.14.0

# fab -V
Fabric 1.14.0
Paramiko 2.4.1

2.第一个例子

创建一个fabfile.py文件,然后写入:

1
2
def hello():
print "Hello Fabric!"

fabfile.py文件中每个函数就是一个任务,任务名即函数名,上例中是”hello”。

查看任务

1
2
3
4
# fab -l
Available commands:

hello

任务可以带参数,将hello函数改为:

1
2
def hello(name, value):
print "Hello Fabric! %s=%s" % (name,value)

此时执行hello任务时,就要传入参数值:

1
# fab hello:name=Year,value=2018

Fabric的脚本建议写在”fabfile.py”文件中,如果想要想换文件名就要在”fab”命令中用”-f”指定:

1
# fab -f script.py hello

3.执行本地命令

“fabric.api”包里的”local()”方法可以用来执行本地Shell命令。

1
2
3
4
from fabric.api import local

def hello():
local('ls -l /')

“local()”方法有一个”capture”参数用来捕获标准输出,比如:

1
2
3
4
5
from fabric.api import local

def hello():
output = local('echo Hello', capture=True)
print output

4.执行远程命令

1
2
3
4
5
6
7
8
9
from fabric.api import run, env

env.hosts = ['192.168.0.213', '192.168.0.214']
# env.hosts = ['root@192.168.0.213', 'root@192.168.0.214']
env.user = 'root'
env.password = '123456'

def hello():
run('ls -l /home/')

“fabric.api”包里的”run()”方法可以用来执行远程Shell命令。
如果不在env.hosts里面指定host的信息,可以在命令行上指定。

1
# fab -H root@192.168.0.213,root@192.168.0.214 hello

对于不同的服务器,我们想执行不同的任务,上面的方法似乎做不到,我们要对服务器定义角色:

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
from fabric.api import env, roles, run, execute, cd

env.roledefs = {
'staging': ['bjhee@example1.com','bjhee@example2.com'],
'build': ['build@example3.com']
}

env.passwords = {
'staging': '11111',
'build': '123456'
}

@roles('build')
def build():
with cd('/home/build/myapp/'):
run('git pull')
run('python setup.py')

@roles('staging')
def deploy():
run('tar xfz /tmp/myapp.tar.gz')
run('cp /tmp/myapp /home/bjhee/www/')

def task():
execute(build)
execute(deploy)

执行:

1
# fab task

如果某一任务上没有指定某个角色,但是你又想让这个角色的服务器也能运行该任务,你可以通过”-R”来指定角色名,多个角色用逗号分隔:

1
# fab -R build deploy

5.SSH功能函数

  • get(remote, local): 从远程机器上下载文件到本地
1
2
3
4
5
6
7
8
from fabric.api import get, env

env.hosts = ['192.168.0.213']
env.user = 'root'
env.password = '123456'

def hello():
get('/root/boot.log', '/root/')
  • put(local, remote): 从本地上传文件到远程机器上
1
2
3
4
5
6
7
8
from fabric.api import put, env

env.hosts = ['192.168.0.213']
env.user = 'root'
env.password = '123456'

def hello():
put('/root/install.log', '/var/log/')
  • prompt: 提示输入
1
2
3
4
5
6
7
8
9
from fabric.api import env, get, prompt

env.hosts = ['bjhee@example.com',]
env.password = '111111'

def hello():
filename = prompt('Please input file name: ')
# port = prompt('Please input port number: ', default=8080, validate=int)
get('/var/log/myapp.log', '%s.log' % filename)
  • reboot: 重启服务器
1
2
3
4
5
6
7
from fabric.api import env, reboot

env.hosts = ['bjhee@example.com',]
env.password = '111111'

def restart():
reboot(wait=60)

6.上下文管理器

  • cd: 设置远程机器的当前工作目录
1
2
3
4
5
6
7
8
from fabric.api import env, cd, put

env.hosts = ['bjhee@example1.com', ]
env.password = '111111'

def hello():
with cd('/var/www/'):
put('/tmp/myapp-0301.tar.gz', 'myapp.tar.gz')
  • lcd: 设置本地工作目录
1
2
3
4
5
6
7
8
9
from fabric.api import env, cd, lcd, put

env.hosts = ['bjhee@example1.com', ]
env.password = '111111'

def hello():
with cd('/var/www/'):
with lcd('/tmp/'):
put('myapp-0301.tar.gz', 'myapp.tar.gz')
  • path: 添加远程机的PATH路径
1
2
3
4
5
6
7
8
9
from fabric.api import env, run, path

env.hosts = ['bjhee@example1.com', ]
env.password = '111111'

def hello():
with path('/home/bjhee/tmp'):
run('echo $PATH')
run('echo $PATH')
  • settings: 设置Fabric环境变量参数
1
2
3
4
5
6
7
8
from fabric.api import env, run, settings

env.hosts = ['bjhee@example1.com', ]
env.password = '111111'

def hello():
with settings(warn_only=True):
run('echo $USER')
  • shell_env: 设置Shell环境变量
1
2
3
4
5
6
7
8
9
from fabric.api import env, run, local, shell_env

env.hosts = ['bjhee@example1.com', ]
env.password = '111111'

def hello():
with shell_env(JAVA_HOME='/opt/java'):
run('echo $JAVA_HOME')
local('echo $JAVA_HOME')
  • prefix: 设置命令执行前缀
1
2
3
4
5
6
7
8
9
from fabric.api import env, run, local, shell_env

env.hosts = ['bjhee@example1.com', ]
env.password = '111111'

def hello():
with shell_env(JAVA_HOME='/opt/java'):
run('echo $JAVA_HOME')
local('echo $JAVA_HOME')

7.错误处理

默认情况下,Fabric在任务遇到错误时就会退出,如果我们希望捕获这个错误而不是退出任务的话,就要开启”warn_only”参数。
在上面介绍”settings()”上下文管理器时,我们已经看到了临时开启”warn_only”的方法了,如果要全局开启,有两个办法:
1.在命令行添加

1
# fab -w hello

2.设置”env.warn_only”环境参数为True

1
2
3
from fabric.api import env

env.warn_only = True

如何捕捉错误?

1
2
3
4
5
6
7
8
9
10
11
from fabric.api import env, cd, put

env.hosts = ['bjhee@example1.com', ]
env.password = '111111'

def hello():
with cd('/var/www/'):
upload = put('/tmp/myapp-0301.tar.gz', 'myapp.tar.gz')
if upload.failed:
sudo('rm myapp.tar.gz')
put('/tmp/myapp-0301.tar.gz', 'myapp.tar.gz', use_sudo=True)

8.并行执行

怎么开启并行执行呢?办法有两个
1.在命令行加参数

1
# fab -P hello

2.设置”env.parallel”环境参数为True

1
2
3
from fabric.api import env

env.parallel = True

如果只想对某一任务做并行的话,我们可以在任务函数上加上”@parallel”装饰器:

1
2
3
4
5
6
7
8
from fabric.api import parallel

@parallel
def runs_in_parallel():
pass

def runs_serially():
pass

9.补充

  • 终端输出带颜色

    1
    2
    3
    4
    5
    6
    from fabric.colors import *

    def hello():
    print green("Successful")
    print yellow("Warning")
    print red("Error")
  • 限制任务只能被执行一次

    1
    2
    3
    4
    5
    6
    7
    8
    9
    from fabric.api import execute, runs_once

    @runs_once
    def hello():
    print "Hello Fabric!"

    def test():
    execute(hello)
    execute(hello)
---------------- The End ----------------