Fabric 代码发布
基礎使用
fabric的典型使用方式就是,創建一個Python文件,該文件包含一到多個函數,然后使用fab命令調用這些函數。這些函數在fabric中成為task,下面是一個例子。
from fabric.api import *env.hosts = ['10.0.1.15', '10.0.1.16', '10.0.1.17'] env.port = 22 env.user = 'root' env.password = '123456'def get_host_name():run('hostname')def get_local_file(path='.'):run('ls %s' % (path))def get_tail(path='/etc/passwd', linne=10):run('tail -n %s %s' % (linne, path))def hello():with settings(hide('everything'), warn_only=True):result = run('netstat -lntup|grep -w 25')print(result)print(result.return_code)print(result.failed)命令行執行結果
獲取列表
(play-test) D:\Users\CMSZ\Desktop\2020-Python\play-test\fabric>fab -f f1.py --list Available commands:get_host_nameget_local_fileget_tailhello 獲取列表執行函數
--
(play-test) D:\Users\CMSZ\Desktop\2020-Python\play-test\fabric>fab -f f1.py --list Available commands:get_host_nameget_local_fileget_tailhello(play-test) D:\Users\CMSZ\Desktop\2020-Python\play-test\fabric>fab -f f1.py get_host_name [10.0.1.15] Executing task 'get_host_name' [10.0.1.15] run: hostname [10.0.1.15] out: cs7-1 [10.0.1.15] out:[10.0.1.16] Executing task 'get_host_name' [10.0.1.16] run: hostname [10.0.1.16] out: cs7-2 [10.0.1.16] out:[10.0.1.17] Executing task 'get_host_name' [10.0.1.17] run: hostname [10.0.1.17] out: cs7-3 [10.0.1.17] out:Done. Disconnecting from 10.0.1.15... done. Disconnecting from 10.0.1.16... done. Disconnecting from 10.0.1.17... done. fab -f f1.py get_host_name-
(play-test) D:\Users\CMSZ\Desktop\2020-Python\play-test\fabric>fab -f f1.py hello [10.0.1.15] Executing task 'hello' /bin/bash: netstat: command not found 1 True [10.0.1.16] Executing task 'hello' /bin/bash: netstat: command not found 1 True [10.0.1.17] Executing task 'hello' /bin/bash: netstat: command not found 1 TrueDone. Disconnecting from 10.0.1.15... done. Disconnecting from 10.0.1.16... done. Disconnecting from 10.0.1.17... done. fab -f f1.py hello (play-test) D:\Users\CMSZ\Desktop\2020-Python\play-test\fabric>fab -f f1.py get_local_file:/ [10.0.1.15] Executing task 'get_local_file' [10.0.1.15] run: ls / [10.0.1.15] out: bin dev home lib64 mnt proc run srv sys usr var [10.0.1.15] out: boot etc lib media opt root sbin swapfile tmp vagrant [10.0.1.15] out:[10.0.1.16] Executing task 'get_local_file' [10.0.1.16] run: ls / [10.0.1.16] out: bin dev home lib64 mnt proc run srv sys usr var [10.0.1.16] out: boot etc lib media opt root sbin swapfile tmp vagrant [10.0.1.16] out:[10.0.1.17] Executing task 'get_local_file' [10.0.1.17] run: ls / [10.0.1.17] out: bin dev home lib64 mnt proc run srv sys usr var [10.0.1.17] out: boot etc lib media opt root sbin swapfile tmp vagrant [10.0.1.17] out:Done. Disconnecting from 10.0.1.15... done. Disconnecting from 10.0.1.16... done. Disconnecting from 10.0.1.17... done. 多個參數情況需要注意的是:
- 一次可以多個task,按照順序執行: fab -f f1.py get_host_name get_local_file
- 給task傳遞參數使用task:參數,多個參數按照位置進行傳遞(和Python相同,對于關鍵字的參數可以,在命令行中指定:fab get_local_file:path=/home)
fabric的命令行參數
fab命令作為fabric程序的入口提供了,豐富的參數調用.
-l:查看task列表
-f:指定fab的入口文件,默認是fabfile.py
-g:指定網管設備,比如堡壘機環境下,填寫堡壘機的IP
-H:在命令行指定目標服務器,用逗號分隔多個服務器
-P:以并行方式運行任務,默認為串行
-R:以角色區分不同的服務
-t:連接超時的時間,以秒為單位
-w:命令執行失敗時的警告,默認是終止任務
-- Fabric參數,其他包含fabric腳本的中的參數的快捷操作,比如--user,--port,或者直接跟要執行的Linux命令
如下例子,不寫一行代碼獲取所有主機的ip地址
(play-test) D:\Users\CMSZ\Desktop\2020-Python\play-test>fab -H 10.0.1.16 --port 22 --user=root --password=123456 -- ip a [10.0.1.16] Executing task '<remainder>' [10.0.1.16] run: ip a [10.0.1.16] out: 1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000 [10.0.1.16] out: link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00 [10.0.1.16] out: inet 127.0.0.1/8 scope host lo [10.0.1.16] out: valid_lft forever preferred_lft forever [10.0.1.16] out: inet6 ::1/128 scope host [10.0.1.16] out: valid_lft forever preferred_lft forever [10.0.1.16] out: 2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000 [10.0.1.16] out: link/ether 52:54:00:75:dc:3d brd ff:ff:ff:ff:ff:ff [10.0.1.16] out: inet 10.0.2.15/24 brd 10.0.2.255 scope global noprefixroute dynamic eth0 [10.0.1.16] out: valid_lft 76820sec preferred_lft 76820sec [10.0.1.16] out: inet6 fe80::5054:ff:fe75:dc3d/64 scope link [10.0.1.16] out: valid_lft forever preferred_lft forever [10.0.1.16] out: 3: eth1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000 [10.0.1.16] out: link/ether 08:00:27:3e:37:98 brd ff:ff:ff:ff:ff:ff [10.0.1.16] out: inet 10.0.1.16/24 brd 10.0.1.255 scope global noprefixroute eth1 [10.0.1.16] out: valid_lft forever preferred_lft forever [10.0.1.16] out: inet6 fe80::a00:27ff:fe3e:3798/64 scope link [10.0.1.16] out: valid_lft forever preferred_lft forever [10.0.1.16] out:常用的對象和方法介紹
介紹fabric中的env對象,以及其他的比如執行命令模塊,上傳文件等。
fabric中的env
env是一個全局唯一的字典,保存了Fabric所有的配置,在Fabric的實現中,他是一個_AttributeDict()對象,之所以封裝成_AttributeDict()對象,是覆蓋了__getattr__和__setattr__,使我們可以使用“對象.屬性=值”的方式,操作字典。
我們可以通過源碼的方式,查看env的配置參數,或者使用如下方式查看:
import json from fabric.api import envprint(type(env)) print(json.dumps(env,indent=4)) <class 'fabric.utils._AttributeDict'> {"abort_exception": null,"again_prompt": "Sorry, try again.","all_hosts": [],"combine_stderr": true,"colorize_errors": false,"command": null,"command_prefixes": [],"cwd": "","dedupe_hosts": true,"default_port": "22","eagerly_disconnect": false,"echo_stdin": true,"effective_roles": [],"exclude_hosts": [],"gateway": null,"gss_auth": null,"gss_deleg": null,"gss_kex": null,"host": null,"host_string": null,"lcwd": "","local_user": "CMSZ","output_prefix": true,"passwords": {},"path": "","path_behavior": "append","port": "22","real_fabfile": null,"remote_interrupt": null,"roles": [],"roledefs": {},"shell_env": {},"skip_bad_hosts": false,"skip_unknown_tasks": false,"ssh_config_path": "C:\\Users\\CMSZ\\.ssh\\config","sudo_passwords": {},"ok_ret_codes": [0],"sudo_prefix": "sudo -S -p '%(sudo_prompt)s' ","sudo_prompt": "sudo password:","sudo_user": null,"tasks": [],"prompts": {},"use_exceptions_for": {"network": false},"use_shell": true,"use_ssh_config": false,"user": "CMSZ","version": "1.14.post1","no_agent": false,"forward_agent": false,"abort_on_prompts": false,"rcfile": "C:\\Users\\CMSZ/.fabricrc","disable_known_hosts": false,"fabfile": "fabfile","hide": ["NO","DEFAULT"],"hosts": [],"key_filename": null,"no_keys": false,"keepalive": 0,"linewise": false,"connection_attempts": 1,"always_use_pty": true,"password": null,"parallel": false,"reject_unknown_hosts": false,"sudo_password": null,"system_known_hosts": null,"shell": "/bin/bash -l -c","show": ["NO","DEFAULT"],"timeout": 10,"command_timeout": null,"warn_only": false,"pool_size": 0 }Process finished with exit code 0 輸出結果常用的env配置如下:
- env.hosts:定義目標服務器列表
- env.exclude_hosts:排除特定的服務器
- env.user SSH:到遠程服務器的用戶名
- env.port:遠程服務器的端口號
- env.key_filename:私鑰文件的位置
- env.password SSH:到遠程服務器的密碼
針對不同主機不同密碼的情況,可以使用如下的方式:
env.hosts = ['root@192.168.10.201:22','root@192.168.10.202:22','root@192.168.10.203:22' ] env.passwords = {'root@192.168.10.201:22':'123456201','root@192.168.10.202:22':'123456202','root@192.168.10.203:22':'123456203' }fabric提供的命令
run():在遠程服務器上執行Linux命令,還有一個重要的參數pty,如果我們執行命令以后需要有一個常駐的服務進程,那么就需要設置pty=False,避免因為Fabric退出導致進程的退出
run('service mysqld start',pty=False)PS:執行完畢會返回輸出的信息,我們可以定義變量接受,同時這個返回信息有一個方法return_code,當返回的是正確執行的結果時code為0,否則不為0
def hello():with settings(hide('everything'),warn_only=True): # 關閉顯示result = run('anetstat -lntup|grep -w 25')print(result) # 命令執行的結果print(result.return_code) # 返回碼,0表示正確執行,1表示錯誤輸出
(play-test) D:\Users\CMSZ\Desktop\2020-Python\play-test\fabric>fab -f f1.py hello [10.0.1.15] Executing task 'hello' /bin/bash: netstat: command not found 1 Truesudo():與run類似,使用管理員權限在遠程服務器上執行shell命令,還有一個重要的參數pty,如果我們執行命令以后需要有一個常駐的服務進程,那么就需要設置pty=False,避免因為Fabric退出導致進程的退出。
local():用以執行本地命令,返回要執行的命令,local是對Python的Subprocess模塊的封裝,更負載的功能可以直接使用Subprocess模塊,包含capture參數,默認為False,表示subprocess輸出的信息進行顯示,如果不想顯示,那么指定capture=True即可
def test():result = local('make test',capture=True)print(result)print(result.failed)print(result.succeeded)# 返回執行的命令 # 如果執行失敗那么 result.failed 為True # 如果執行成功那么 result.succeeded 為Trueget():從遠程服務器上獲取文件,通過remote_path參數聲明從何處下載,通過local_path表示下載到何處。remote_path支持通配符。
get(remote_path='/etc/passwd',local_path='/tmp/passwd')put():將本地的文件上傳到遠程服務器,參數與get相似,此外,還可以通過mode參數執行遠程文件的權限配置。
get(remote_path='/etc/passwd',local_path='/tmp/passwd')reboot():重啟遠程服務器,可以通過wait參數設置等待幾秒鐘重啟
reboot(wait=30)propmt():用以在Fabric執行任務的過程中與管理員進行交互,類似于python的input
key = prompt('Please specify process nice level:',key='nice',validate=int) # 會返回采集到的keyfabric的上下文管理器
env中存儲的是全局配置,有時候我們并不希望修改全局配置參數,只希望臨時修改部分配置,例如:修改當前工作目錄,修改日志輸出級別等。
在fabric中我們可以通過上下文管理器臨時修改參數配置,而不會影響全局配置。當程序進入上下文管理器的作用域時,臨時修改就會起作用;當程序離開上下文管理器時,臨時修改就會消失。
cd():切換遠程目錄
def change(dir='/tmp'):with cd(dir):run('pwd') # /tmprun('pwd') # /rootlcd():切換本地目錄
path():配置遠程服務器PATH環境變量,只對當前會話有效,不會影響遠程服務器的其他操作,path的修改支持多種模式
- append:默認行為,將給定的路徑添加到PATH后面。
- prepend:將給定的路徑添加到PATH的前面。
- replace:替換當前環境的PATH變量。
prefix():前綴,它接受一個命令作為參數,表示在其內部執行的代碼塊,都要先執行prefix的命令參數。
def testprefix():with cd('/tmp'):with prefix('echo 123'):run('echo 456')run('echo 789')# 轉換為Linux命令為: cd /tmp && echo '123' && echo '456' cd /tmp && echo '123' && echo '789'shell_env():設置shell腳本的環境變量
def setenv():with shell_env(HTTP_PROXY='1.1.1.1'):run('echo $HTTP_PROXY')run('echo $HTTP_PROXY')# 等同于shell中的export export HTTP_PROXY='1.1.1.1'settings():通用配置,用于臨時覆蓋env變量
def who():with settings(user='dev'): # 臨時修改用戶名為devrun('who')run('who')remote_tunnel():通過SSH的端口轉發建立的鏈接
with remote_tunnel(3306):run('mysql -uroot -p password')hide():用于隱藏指定類型的輸出信息,hide定義的可選類型有7種
- status:狀態信息,如服務器斷開鏈接,用戶使用ctrl+C等,如果Fabric順利執行,不會有狀態信息
- aborts:終止信息,一般將fabric當作庫使用的時候需要關閉
- warnings:警告信息,如grep的字符串不在文件中
- running:fabric運行過程中的數據
- stdout:執行shell命令的標準輸出
- stderr:執行shell命令的錯誤輸出
- user:用戶輸出,類似于Python中的print函數
為了方便使用,fabric對以上其中類型做了進一步的封裝
- output:包含stdout,stderr
- everything:包含stdout,stderr,warnings,running,user
- commands:包含stdout,running
show():與hide相反,表示顯示指定類型的輸出
def hello():with settings(show('everything'), warn_only=True):result = run('ss -lntup|grep')print('1=' + result)print('2=' + str(result.return_code))print('3=' + str(result.failed)) (play-test) D:\Users\CMSZ\Desktop\2020-Python\play-test\fabric>fab -f f2.py hello [10.0.1.15] Executing task 'hello' [10.0.1.15] run: ss -lntup|grep [10.0.1.15] out: Usage: grep [OPTION]... PATTERN [FILE]... [10.0.1.15] out: Try 'grep --help' for more information. [10.0.1.15] out:Warning: run() received nonzero return code 2 while executing 'ss -lntup|grep'!NoneType: None1=Usage: grep [OPTION]... PATTERN [FILE]... Try 'grep --help' for more information. 2=2 3=True 結果quiet():隱藏全部輸出,僅在執行錯誤的時候發出告警信息,功能等同于 with settings(hide('everything'),warn_only=True) .
# 比如創建目錄的時候,如果目錄存在,默認情況下Fabric會報錯退出,我們是允許這種錯誤的,所以針對這種錯誤,我們進行如下設置,使fabric只打出告警信息而不會中斷執行。 with settings(warn_only=True)裝飾器
Fabric提供的命令一般都是執行某一個具體的操作,提供的上下文管理器一般都是用于臨時修改配置參數,而fabric提供的裝飾器,既不是執行具體的操作,也不是修改參數,而是控制如何執行這些操作,在那些服務器上執行這些操作,fabric的裝飾器與人物執行緊密相關。下面從幾個方面來進行說明
- hosts:定制執行task的服務器列表
- roles:定義執行task的role列表
- parallel:并行執行task
- serial:串行執行task
- task:定義一個task
- runs_once:該task只執行一次
fabric的task
task就是fabric需要在遠程服務器上執行的函數,在fabric中有3中方法定義一個task
PS:默認情況下,fabfile中的所有函數對象都是一個task,但是如果我們使用了task裝飾器,顯示的定義了一個task,那么,其他沒有通過task裝飾器裝飾的函數將不會被認為是一個task。
fabric的host
為了方便我們的使用,fabric提供了非常靈活的方式指定對哪些遠程服務器執行操作,根據我們前面的知識,我們知道有兩種方式:通過env.hosts來執行,或者在fab執行命令的時候使用-H參數,除此之外,還有以下需要注意的地方
fabric的role
role是對服務器進行分類的手段,通過role可以定義服務器的角色,以便對不同的服務器執行不同的操作,Role邏輯上將服務器進行了分類,分類以后,我們可以對某一類服務器指定一個role名即可。進行task任務時,對role進行控制。
# role在env.roledefs中進行定義 env.roledefs = {'web':['root@192.168.10.201','192.168.10.202'] # role名稱為:web'db':['root@192.168.10.203',] # role名稱為:db }當我們定義好role以后,我們就可以通過roles裝飾器來指定在哪些role上運行task。 from fabric.api import *env.roledefs = {'web':['root@192.168.10.201:22','root@192.168.10.202:22',],'db':['root@192.168.10.203:22',] } env.passwords = {'root@192.168.10.201:22':'123456201','root@192.168.10.202:22':'123456202','root@192.168.10.203:22':'123456203' }@roles('db') # 只對role為db的主機進行操作 @task def hello():run('ifconfig br0')注意:hosts裝飾器可以和roles裝飾器一起使用(全集),看起來容易造成混亂,不建議混搭。
fabric的執行模型
fabric執行任務的步驟如下:
PS:關于并行模式:
其他裝飾器
前面介紹了task,hosts,roles和parallel裝飾器,此外還有兩個裝飾器比較常用
- runs_once:只執行一次,防止task被多次調用。例如,對目錄打包進行上傳,上傳動作對不同的服務器可能會執行多次,但是打包的動作只需要執行一次即可。
- serial:強制當前task穿行執行。使用該參數時優先級最高,即便是制定了并發執行的參數
常用的功能函數
fabric中還有其他的一些好用的函數
封裝task
fabric提供了一個execute函數,用來對task進行封裝。它最大的好處就是可以將一個大的任務拆解為很多小任務,每個小任務互相獨立,互不干擾
from fabric.api import *env.roledefs = {'web':['root@192.168.10.201:22','root@192.168.10.202:22',],'db':['root@192.168.10.203:22',] } env.passwords = {'root@192.168.10.201:22':'123456201','root@192.168.10.202:22':'123456202','root@192.168.10.203:22':'123456203' }@roles('db') def hello():run('echo hello')@roles('web') def world():run('echo world')@task def helloworld():execute(hello)execute(world)# 函數helloworld作為入口,分別調用兩個task,對不同的主機進行操作
utils函數
包含一些輔助行的功能函數,這些函數位于fabric.utils下,常用的函數如下:
帶顏色的輸出
fabric為了讓輸出日志更具有可讀性,對命令行中斷的顏色輸出進行了封裝,使用print打印帶有不同顏色的文本,這些顏色包含在fabric.colors中。像warn,puts打印輸出的,也可以直接渲染顏色
- blue(text,blod=False)? 藍色
- cyan(text,blod=False)? 淡藍色
- green(text,blod=False)? 綠色
- magenta(text,blod=False)? 紫色
- red(text,blod=False)? 紅色
- white(text,blod=False)? 白色
- yellow(text,blod=False)?? 黃色
確認信息
有時候我們在某一步執行錯誤,會給用戶提示,是否繼續執行時,confirm就非常有用了,它包含在 fabric.contrib.console中
def testconfirm():result = confirm('Continue Anyway?')print(result)# 會提示輸入y/n # y 時 result為True # n 時 result為False使用Fabric源碼安裝redis
下載一個redis的包和fabfile.py放在同級目錄即可,不同目錄需要修改包的位置,這里使用的是redis-4.0.9版本。
#!/usr/bin/env python3 from fabric.api import * from fabric.contrib.console import confirm from fabric.utils import abort from fabric.colors import *env.hosts = ['192.168.10.202',] env.user = 'root' env.password = '123456202'@runs_once @task def test():with settings(warn_only=True):local('tar xf redis-4.0.9.tar.gz')with lcd('redis-4.0.9'):result = local('make test',capture=True)if result.failed and not confirm('Test is Faild Continue Anyway?'):abort('Aborting at user request.')with lcd('redis-4.0.9'):local("make clean")local('tar zcvf redis-4.0.10.tar.gz redis-4.0.9')@task def deploy():put('redis-4.0.10.tar.gz','/tmp/')with cd('/tmp'):run('tar xf redis-4.0.10.tar.gz')with cd('redis-4.0.9'):sudo('make install')@task def start_redis():with settings(warn_only=True):result = run('netstat -lntup | grep -w redis-server')if result.return_code == 0:print(green('redis is started!'))else:run('set -m ; /usr/local/bin/redis-server &') # 用pty=False, fabric進程退不出來,不知道為啥,所以這里用set -mprint(green('redis start Successful'))@task def clean_local_file():local('rm -rf redis-4.0.10.tar.gz')@task def clean_file():with cd('/tmp'):sudo('rm -rf redis-4.0.9')sudo('rm -rf redis-4.0.10.tar.gz')@task def install():execute(test)execute(deploy)execute(clean_file)execute(clean_local_file)<br><code class="python spaces hljs"> </code><code class="python plain hljs">execute(start_redis)</code>1 2 3 PS:關于set -m 的作用如下:"set -m" turns on job control, you can run processes in a separate process group.<br>理解:在一個獨立的進程組里面運行我們的進程。
參考鏈接
https://www.cnblogs.com/xiao-apple36/p/9124292.html#_label6_5
總結
以上是生活随笔為你收集整理的Fabric 代码发布的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 博客园文章目录结构
- 下一篇: 4 Oracle 操作表中数据