Fabric是一个python的远程执行shell的库,同时它也是一个命令行工具。它提供了丰富的同 SSH 交互的接口,可以用来在本地或远程机器上自动化、流水化地执行 Shell 命令。
Python 的 Fabric 是一个基于 SSH 协议的自动化部署与运维工具,它在 Paramiko(底层 SSH 实现)的基础上封装了更简洁的 API,支持通过任务(Task) 定义批量操作(如远程命令执行、文件传输、环境配置等),并可通过命令行直接调用,极大简化了多服务器集群的管理工作。
一、Fabric 版本与安装
Fabric 有两个主要版本,API 差异较大:
- Fabric 1.x:较早版本,使用 fab 命令和 env 全局变量配置,目前已停止维护。
- Fabric 2.x+:重构后的新版本,采用面向对象 API(如 Connection 类),支持更灵活的任务定义,推荐使用。
本文以 Fabric 2.x+ 为例讲解。
安装 Fabric
pip install fabric # 安装最新版本(2.x+)
二、核心概念:任务(Task)与连接(Connection)
Fabric 的核心是任务(Task) 和连接(Connection):
- 任务(Task):用 @task 装饰器定义的函数,封装了一组操作(如执行命令、传输文件),可通过命令行调用。
- 连接(Connection):代表与远程服务器的 SSH 连接,通过它执行远程命令(run)、sudo 操作(sudo)、文件传输(put/get)等。
三、基础使用:定义与执行任务
1. 编写任务文件(fabfile.py)
Fabric 默认读取当前目录下的 fabfile.py(或 fabfile/ 目录)中的任务,文件名固定(方便命令行识别)。
# fabfile.py
from fabric import task
from fabric.connection import Connection
# 定义任务:通过装饰器 @task
@task
def hello(c):
"""简单任务:打印远程服务器信息"""
# c 是 Connection 对象(由 Fabric 自动传入,代表与远程服务器的连接)
c.run("uname -a") # 执行远程命令(如查看系统信息)
c.run("echo 'Hello from remote server!'")
2. 执行任务(命令行)
通过 fab 命令调用任务,需指定远程服务器的连接信息(主机、用户等)。
# 格式:fab [任务名] -H [主机] -u [用户] -p [密码]
# 示例:执行 hello 任务,连接到 192.168.1.100,用户 root,密码 123456
fab hello -H 192.168.1.100 -u root -p 123456
输出类似:
Linux server 5.4.0-100-generic #113-Ubuntu SMP Thu Feb 3 18:43:29 UTC 2022 x86_64 x86_64 x86_64 GNU/Linux
Hello from remote server!
四、核心操作详解
1. 连接配置(避免硬编码)
直接在命令行传递密码(-p)不安全,推荐通过配置文件或密钥认证管理连接信息。
(1)通过参数指定连接信息
# 密钥认证(推荐,无需密码)
fab hello -H 192.168.1.100 -u root -i ~/.ssh/id_rsa # -i 指定私钥路径
(2)在任务中硬编码连接(适合固定服务器)
@task
def hello_fixed(c):
# 直接创建 Connection 对象(手动指定连接信息)
conn = Connection(
host="192.168.1.100",
user="root",
connect_kwargs={
"key_filename": "/home/user/.ssh/id_rsa" # 私钥路径
}
)
conn.run("uname -a")
2. 执行远程命令(run 与 sudo)
c.run(command):以当前用户权限执行命令。c.sudo(command):以sudo权限执行命令(需输入密码时,可通过password参数指定)。
@task
def manage_service(c):
"""管理远程服务器的 Nginx 服务"""
# 查看 Nginx 状态(普通用户权限)
c.run("systemctl status nginx")
# 重启 Nginx(需要 sudo 权限,假设 sudo 密码为 '123456')
c.sudo("systemctl restart nginx", password="123456") # 若免密 sudo 可省略 password
# 检查重启后状态
c.run("systemctl status nginx | grep 'active (running)'")
3. 文件传输(put 与 get)
通过 c.put() 上传本地文件到远程,c.get() 从远程下载文件到本地,类似 SFTP。
@task
def transfer_files(c):
"""上传本地文件到远程,下载远程文件到本地"""
# 上传本地文件(local_path → remote_path)
c.put("local_script.sh", "/tmp/remote_script.sh") # 上传当前目录的脚本到远程 /tmp
c.run("chmod +x /tmp/remote_script.sh") # 赋予执行权限
# 下载远程文件(remote_path → local_path)
c.get("/var/log/syslog", "local_syslog.log") # 下载远程日志到本地
4. 批量操作多台服务器
通过 -H 参数指定多个主机(逗号分隔),Fabric 会按顺序在每台服务器上执行任务。
# 同时在 192.168.1.100 和 192.168.1.101 上执行 hello 任务
fab hello -H 192.168.1.100,192.168.1.101 -u root -i ~/.ssh/id_rsa
5. 任务依赖(@task(pre=[...]))
通过 pre 参数指定“前置任务”,执行当前任务前会先执行前置任务(类似流水线)。
@task
def prepare(c):
"""前置任务:更新系统包"""
c.sudo("apt update -y", password="123456")
@task(pre=[prepare]) # 执行 install_nginx 前先执行 prepare
def install_nginx(c):
"""安装 Nginx(依赖 prepare 任务)"""
c.sudo("apt install nginx -y", password="123456")
c.run("nginx -v") # 验证安装
执行 install_nginx 任务时,会先自动执行 prepare:
fab install_nginx -H 192.168.1.100 -u root -i ~/.ssh/id_rsa
五、进阶:动态主机与并行执行
1. 动态生成主机列表(适合动态集群)
通过 @task 的 hosts 参数或在任务中动态返回主机列表,灵活适配动态变化的服务器集群。
@task(hosts=["192.168.1.100", "192.168.1.101"]) # 静态指定主机
def check_all(c):
c.run("hostname")
# 动态生成主机列表(例如从配置文件读取)
def get_dynamic_hosts():
return ["192.168.1.102", "192.168.1.103"] # 实际可从数据库/API 读取
@task
def check_dynamic(c):
for host in get_dynamic_hosts():
conn = Connection(host, user="root", connect_kwargs={"key_filename": "~/.ssh/id_rsa"})
conn.run("hostname")
2. 并行执行任务(加速多服务器操作)
默认情况下,Fabric 按顺序在多台服务器上执行任务。通过 --parallel(或 -P)参数可并行执行,大幅提升效率(适合无依赖的批量操作)。
# 并行在 10 台服务器上执行任务(同时运行,而非顺序)
fab update_config -H host1,host2,...,host10 -u root -P
六、Fabric 与 Paramiko 的区别
| 工具 | 定位 | 优势 | 适用场景 |
|---|---|---|---|
| Paramiko | 底层 SSH 协议实现 | 灵活,可自定义任意 SSH 交互逻辑 | 复杂 SSH 交互(如交互式命令) |
| Fabric | 高层自动化运维工具 | 简化 API,任务化组织,命令行集成 | 批量部署、标准化运维、重复任务自动化 |
七、应用场景
Fabric 适合以下自动化场景:
1. 批量部署应用:在多台服务器上同步代码、安装依赖、启动服务(如部署 Django/Flask 应用)。
2. 服务器配置标准化:统一设置时区、防火墙规则、安装基础工具(如 git、python)。
3. 日志收集与监控:定期从多台服务器下载日志文件,或执行性能检查命令(如 top、free)。
4. 应急操作:批量重启服务、临时修改配置(如应对流量峰值)。
总结
Fabric 是基于 Paramiko 的高层封装,核心优势在于:
- 任务化组织:用 @task 装饰器定义可复用的操作,逻辑清晰。
- 命令行集成:通过 fab 命令直接调用任务,无需编写完整脚本。
- 批量与并行:轻松支持多服务器操作,并行执行提升效率。
对于需要频繁操作多台远程服务器的场景(如运维、部署),Fabric 能显著减少重复劳动,是自动化工具链中的重要组件。
安装Fabric
Fabric的官网是 www.fabfile.org,源码托管在Github上。你可以clone源码到本地,然后通过下面的命令来安装。但是在源码安装前,你必须先将Fabric的依赖包Paramiko装上。
python setup.py develop
同时也可以使用pip安装,因为fabric是python的一个第三方库,只需一条命令即可:
pip install fabric
python3 安装时使用的是fabric3 :( 安装fabric3之前,需要先卸载fabric.)
# fabric3 支持 python3
pip uninstall fabric
pip3 install fabric3
fabric 不只是一个Python 模块,fabric 还是一个命令行工具,可以使用fab -h查看帮助信息
E:\my_data\hk-project>fab -V
Fabric3 1.14.post1
Paramiko 2.4.2
E:\my_data\hk-project>fab -h
入门使用
fabric的使用方式是通过编写一个python文件,该文件中包含多个函数,然后使用fab命令调用这些函数,做相应的任务。这些函数在fabric中称为task。
# filename:abc.py
from fabric.api import *
def task1():
print("hello")
def hello():
print("hello world")
写好这个python文件后,在当前目录的路径下使用fab工具执行文件中的函数
[root@localhost python文件所在的目录]# fab -f abc.py hello
hello world
# -f 指定fabfile文件,默认为fabfile.py,若文件名是当前目录下的fabfile.py则无需指定
任务参数
此时你可能会想,如果这个函数有参数怎么办呢?应该如何传递参数给函数呢?Fabric 支持 Shell 兼容的参数用法: <任务名>:<参数>, <关键字参数名>=<参数值>,... 用起来就是这样。
def hello(name="world"):
print("hello {}".format(name))
我们可以这样去指定参数
$ fab hello:name=Jeff # 或者 fab hello:Jeff
hello Jeff
Done.
小试牛刀
现在我们假设需要写一个fabfile.py,能够在每次web项目代码更新后使用git提交并远程服务器拉去最新代码并运行,需求描述清楚了,开干吧!
# fabfile.py
# 这里建议将该文件放入项目文件的根目录中,方便git提交
from fabric.api import local
def test():
local('python manage.py test myapp')
# 测试是否能正常运行
def commit():
local('git add -p && git commit -m "for test"')
def push():
local('git push')
def prepare_deploy():
test()
commit()
push()
这个 prepare_deploy 任务可以单独调用,也可以调用更细粒度的子任务。
故障
Fabric 会检查被调用程序的返回值,如果这些程序没有干净地退出,Fabric 会终止操作。我们什么都不用做,Fabric 检测到了错误并终止,不会继续执行 commit 任务。
我们也可以对故障进行一定的处理和判断
from fabric.api import local, settings, abort
from fabric.contrib.console import confirm
def test():
with settings(warn_only=True):
result = local('./manage.py test my_app', capture=True)
# result.return_code返回码(0/1)和result.failed
if result.failed and not confirm("Tests failed. Continue anyway?"): # confirm判断用户输入
abort("Aborting at user request.") # 指定错误退出信息
# 一个名为 warn_only 的设置(或着说 环境变量 ,通常缩写为 env var )可以把退出换为警告,以提供更灵活的错误处理。如果设置为False,则一条命令运行失败会就会退出,不再执行后面的命令。
建立连接
终于到了连接了,这个工具主要作用就是在远程执行命令呀,学会了这个,我们就可以在本地执行远程服务器的命令了。
from fabric.api import *
env.hosts = ['root@192.168.10.11:22']
def deploy():
run('ls') # run()用于执行远程命令,local()执行本地命令
# 执行后会提示你输入密码,输入密码即可
使用fabric安装lamp
# 采用ThreadingGroupd对象并发执行
from fabric import ThreadingGroup as Group
hosts = (
"root@192.168.10.50", "root@192.168.10.51"
)
group = Group(*hosts, connect_kwargs={"password": "abc123"})
print("自动安装LAMP ......")
# 安装Apache服务器
group.run("yum install httpd -y")
# 安装并启动MariaDB服务器
group.run("yum install mariadb mariadb-server -y")
group.run("systemctl start mariadb")
group.run("systemctl enable mariadb")
# 以非交互方式运行MariaDB数据库安全配置向导
group.run("echo -e '\ny\nabc123\nabc123\ny\ny\ny\ny\n' | /usr/bin/mysql_secure_installation")
# 安装PHP
group.run(
"yum install pcre gcc-c++ zlib* php php-mysqlnd php-gd libjpeg* php-ldap php-odbc php-pear php-xml* php-json php-mbstring php-bcmath php-mhash -y")
# 生成PHP测试文件
group.run("echo '<?php phpinfo(); ?>' | tee /var/www/html/test.php")
# 启动Apache服务器
group.run("systemctl start httpd")
group.run("systemctl enable httpd")
# 防火墙开启HTTP和HTTPS服务
group.run("systemctl start firewalld", warn=True)
group.run("firewall-cmd --permanent --zone=public --add-service=http --add-service=https", warn=True)
group.run("firewall-cmd --reload")
# 安装phpMyAdmin
group.run("curl -o phpMyAdmin.zip https://files.phpmyadmin.net/phpMyAdmin/4.9.10/phpMyAdmin-4.9.10-all-languages.zip")
group.run("mv phpMyAdmin.zip /var/www/html")
group.run("unzip -d /var/www/html /var/www/html/phpMyAdmin.zip")
group.run("rm /var/www/html/phpMyAdmin.zip")
group.run("mv /var/www/html/phpMyAdmin-4.9.10-all-languages /var/www/html/phpmyadmin")
group.run("mv /var/www/html/phpmyadmin/config.sample.inc.php /var/www/html/phpmyadmin/config.inc.php")
group.close()
参考链接:
fabric官方中文文档:https://fabric-chs.readthedocs.io/zh_CN/chs/tutorial.html