fabric是一個Python的庫,同時它也是一個命令行工具。使用fabric提供的命令行工具,能夠很方便地執行應用部署和系統管理等操做。html
fabric依賴於paramiko進行ssh交互,fabric的設計思路是經過幾個API接口來完成全部的部署,所以fabric對系統管理操做進行了簡單的封裝,好比執行命令,上傳文件,並行操做和異常處理等。python
#安裝 # fabric3支持python3 pip3 install fabric3
因爲fabric比較特殊它仍是一個命令行工具,能夠經過help進行命令的瞭解mysql
pyvip@Vip:~/utils$ fab --help Usage: fab [options] <command>[:arg1,arg2=val2,host=foo,hosts='h1;h2',...] ... Options: -h, --help show this help message and exit -d NAME, --display=NAME print detailed info about command NAME -F FORMAT, --list-format=FORMAT formats --list, choices: short, normal, nested -I, --initial-password-prompt Force password prompt up-front --initial-sudo-password-prompt Force sudo password prompt up-front -l, --list print list of possible commands and exit --set=KEY=VALUE,... comma separated KEY=VALUE pairs to set Fab env vars ……
錯誤的提示linux
# fab -help Traceback (most recent call last): File "/usr/local/python3/bin/fab", line 11, in <module> load_entry_point('Fabric==1.14.0', 'console_scripts', 'fab')() File "/usr/local/python3/lib/python3.6/site-packages/pkg_resources/__init__.py", line 480, in load_entry_point return get_distribution(dist).load_entry_point(group, name) File "/usr/local/python3/lib/python3.6/site-packages/pkg_resources/__init__.py", line 2693, in load_entry_point return ep.load() File "/usr/local/python3/lib/python3.6/site-packages/pkg_resources/__init__.py", line 2324, in load return self.resolve() File "/usr/local/python3/lib/python3.6/site-packages/pkg_resources/__init__.py", line 2330, in resolve module = __import__(self.module_name, fromlist=['__name__'], level=0) File "/usr/local/python3/lib/python3.6/site-packages/fabric/main.py", line 13, in <module> from operator import isMappingType ImportError: cannot import name 'isMappingType' # 說明你使用的python版本可fabric版本不一樣,python3 安裝時使用的是fabric3
fabric的典型使用方式就是,建立一個Python文件,該文件包含一到多個函數,而後使用fab命令調用這些函數。這些函數在fabric中成爲task,下面是一個例子web
from fabric.api import * from fabric.contrib.console import confirm from fabric.utils import abort from fabric.colors import * env.hosts = ['192.168.5.128'] env.port = 22 env.user = 'root' env.password = 'mysql123' def hostname(): run('hostname') def ls(path='.'): run('ls {0}'.format(path)) def tail(path='/etc/pas', line=10): run('tail -n {0} {1}'.format(line, path)) def hello(): with settings(hide('everything'),warn_only=True): # 關閉顯示 result = run('anetstat -lntup|grep -w 25') print(result) # 命令執行的結果 print(result.return_code) # 返回碼,0表示正確執行,1表示錯誤 print(result.failed)
PS:fab命令執行時,默認引用一個名爲fabfile.py的文件,咱們也能夠經過-f來進行指定(文件名不能爲abc.py,會衝突).redis
這裏使用了三個fabric的封裝:sql
一、獲取任務列表 pyvip@Vip:~/utils$ fab -f fab_utils.py --list Available commands: hello hostname ls tail pyvip@Vip:~/utils$ fab -f fab_utils.py --list Available commands: hello hostname ls tail # 二、執行hostname函數 pyvip@Vip:~/utils$ fab -f fab_utils.py hostname [192.168.5.128] Executing task 'hostname' [192.168.5.128] run: hostname [192.168.5.128] out: china [192.168.5.128] out: Done. Disconnecting from 192.168.5.128... done. # 三、多個參數的狀況 pyvip@Vip:~/utils$ fab -f fab_utils.py ls:/ [192.168.5.128] Executing task 'ls' [192.168.5.128] run: ls / [192.168.5.128] out: bin boot data dev etc home lib lib64 lost+found media misc mnt net opt proc root sbin selinux srv sys tmp usr var [192.168.5.128] out: Done. Disconnecting from 192.168.5.128... done.
須要注意的是:shell
fab命令做爲fabric程序的入口提供了,豐富的參數調用.json
# -l:查看task列表 # -f:指定fab的入口文件,默認是fabfile.py # -g:指定網管設備,好比堡壘機環境下,填寫堡壘機的IP # -H:在命令行指定目標服務器,用逗號分隔多個服務器 # -P:以並行方式運行任務,默認爲串行 # -R:以角色區分不一樣的服務 # -t:鏈接超時的時間,以秒爲單位 # -w:命令執行失敗時的警告,默認是終止任務 # -- Fabric參數,其餘包含fabric腳本的中的參數的快捷操做,好比--user,--port,或者直接跟要執行的Linux命令
以下例子,不寫一行代碼獲取全部主機的ip地址api
pyvip@Vip:~/utils$ fab -H 192.168.5.128 --port 22 --user='root' --password='mysql123' -- 'ip a ' [192.168.5.128] Executing task '<remainder>' [192.168.5.128] run: ip a [192.168.5.128] out: 1: lo: <LOOPBACK,UP,LOWER_UP> mtu 16436 qdisc noqueue state UNKNOWN [192.168.5.128] out: link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00 [192.168.5.128] out: inet 127.0.0.1/8 scope host lo [192.168.5.128] out: inet6 ::1/128 scope host [192.168.5.128] out: valid_lft forever preferred_lft forever [192.168.5.128] out: 2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP qlen 1000 [192.168.5.128] out: link/ether 00:0c:29:96:0a:a0 brd ff:ff:ff:ff:ff:ff [192.168.5.128] out: inet 192.168.5.128/24 brd 192.168.5.255 scope global eth0 [192.168.5.128] out: inet6 fe80::20c:29ff:fe96:aa0/64 scope link [192.168.5.128] out: valid_lft forever preferred_lft forever [192.168.5.128] out: 3: pan0: <BROADCAST,MULTICAST> mtu 1500 qdisc noop state DOWN [192.168.5.128] out: link/ether 7a:4d:51:6c:c2:cd brd ff:ff:ff:ff:ff:ff
介紹fabric中的env對象,以及其餘的好比執行命令模塊,上傳文件等。
env是一個全局惟一的字典,保存了Fabric全部的配置,在Fabric的實現中,他是一個_AttributeDict()對象,之因此封裝成_AttributeDict()對象,是覆蓋了__getattr__和__setattr__,使咱們可使用「對象.屬性=值」的方式,操做字典。
咱們能夠經過源碼的方式,查看env的配置參數,或者使用以下方式查看:
import json from fabric.api import env print(json.dumps(env, indent=3)) def hell(name='world'): print('hello %s' % name) ----------------------------------------------- 結果 pyvip@Vip:~/utils$ fab -f fab_utils.py -l { "show": null, "": true, "sudo_user": null, "default_port": "22", "key_filename": null, "path": "", "hosts": [ "192.168.5.128" ], "host_string": null, "ok_ret_codes": [ 0 ], "always_use_pty": true, "fabfile": "fab_utils.py", "echo_stdin": true, "again_prompt": "Sorry, try again.", "command": null, "forward_agent": false, "command_prefixes": [], "cwd": "", "connection_attempts": 1, "linewise": false, "gateway": null, "use_exceptions_for": { "network": false ……
經常使用的env配置以下:
針對不一樣主機不一樣密碼的狀況,可使用以下的方式:
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'
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表示錯誤
結果
[192.168.5.128] Executing task 'hello' /bin/bash: anetstat: command not found 1 True Done. Disconnecting from 192.168.5.128... done.
sudo():與run相似,使用管理員權限在遠程服務器上執行shell命令,還有一個重要的參數pty,若是咱們執行命令之後須要有一個常駐的服務進程,那麼就須要設置pty=False,避免由於Fabric退出致使進程的退出。
local():用以執行本地命令,返回要執行的命令,local是對Python的Subprocess模塊的封裝,更負載的功能能夠直接使用Subprocess模塊,包含capture參數,默認爲False,表示subprocess輸出的信息進行顯示,若是不想顯示,那麼指定capture=True便可
ef test(): result = local('make test',capture=True) print(result) print(result.failed) print(result.succeeded) # 返回執行的命令 # 若是執行失敗那麼 result.failed 爲True # 若是執行成功那麼 result.succeeded 爲True
get():從遠程服務器上獲取文件,經過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) # 會返回採集到的key
env中存儲的是全局配置,有時候咱們並不但願修改全局配置參數,只但願臨時修改部分配置,例如:修改當前工做目錄,修改日誌輸出級別等。
在fabric中咱們能夠經過上下文管理器臨時修改參數配置,而不會影響全局配置。當程序進入上下文管理器的做用域時,臨時修改就會起做用;當程序離開上下文管理器時,臨時修改就會消失。
cd():切換遠程目錄
def change(dir='/tmp'): with cd(dir): run('pwd') # /tmp run('pwd') # /root
lcd():切換本地目錄
path():配置遠程服務器PATH環境變量,只對當前會話有效,不會影響遠程服務器的其餘操做,path的修改支持多種模式
def addpath(): with path('/tmp','prepend'): run("echo $PATH") run("echo $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'): # 臨時修改用戶名爲dev run('who') run('who')
remote_tunnel():經過SSH的端口轉發創建的連接
with remote_tunnel(3306): run('mysql -uroot -p password')
hide():用於隱藏指定類型的輸出信息,hide定義的可選類型有7種
爲了方便使用,fabric對以上其中類型作了進一步的封裝
show():與hide相反,表示顯示指定類型的輸出
def hello(): with settings(show('everything'),warn_only=True): # 顯示全部 result = run('netstat -lntup|grep') print('1='+result) # 命令執行的結果 print('2='+str(result.return_code)) # 返回碼,0表示正確執行,1表示錯誤 print('3='+str(result.failed))
結果
pyvip@Vip:~/utils$ fab -f fab_utils.py hello [192.168.5.128] Executing task 'hello' [192.168.5.128] run: netstat -lntup|grep [192.168.5.128] out: 用法: grep [選項]... PATTERN [FILE]... [192.168.5.128] out: 試用‘grep --help’來得到更多信息。 [192.168.5.128] out: Warning: run() received nonzero return code 2 while executing 'netstat -lntup|grep'! NoneType 1=用法: grep [選項]... PATTERN [FILE]... 試用‘grep --help’來得到更多信息。 2=2 3=True Done.
quiet():隱藏所有輸出,僅在執行錯誤的時候發出告警信息,功能等同於 with settings(hide('everything'),warn_only=True) .
# 好比建立目錄的時候,若是目錄存在,默認狀況下Fabric會報錯退出,咱們是容許這種錯誤的,因此針對這種錯誤,咱們進行以下設置,使fabric只打出告警信息而不會中斷執行。 with settings(warn_only=True)
Fabric提供的命令通常都是執行某一個具體的操做,提供的上下文管理器通常都是用於臨時修改配置參數,而fabric提供的裝飾器,既不是執行具體的操做,也不是修改參數,而是控制如何執行這些操做,在那些服務器上執行這些操做,fabric的裝飾器與人物執行緊密相關。下面從幾個方面來進行說明
task就是fabric須要在遠程服務器上執行的函數,在fabric中有3中方法定義一個task
from fabric.api import * env.user='root' env.password='mysql123' @task def hello(): run('echo hello') def world(): run('echo world')
PS:默認狀況下,fabfile中的全部函數對象都是一個task,可是若是咱們使用了task裝飾器,顯示的定義了一個task,那麼,其餘沒有經過task裝飾器裝飾的函數將不會被認爲是一個task。
爲了方便咱們的使用,fabric提供了很是靈活的方式指定對哪些遠程服務器執行操做,根據咱們前面的知識,咱們知道有兩種方式:經過env.hosts來執行,或者在fab執行命令的時候使用-H參數,除此以外,還有如下須要注意的地方
from fabric.api import * 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' } @hosts('root@192.168.10.201:22') @task def hello(): run('ifconfig br0') # 命令行的方式: fab hello:hosts="root@192.168.10.201;root@192.168.10.202"
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執行任務的步驟以下:
PS:關於並行模式:
前面介紹了task,hosts,roles和parallel裝飾器,此外還有兩個裝飾器比較經常使用
fabric中還有其餘的一些好用的函數
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,對不一樣的主機進行操做
包含一些輔助行的功能函數,這些函數位於fabric.utils下,經常使用的函數以下:
def helloworld(): execute(hello) abort('----->abort') # 執行到這裏時,直接退出 warn('----->warn') # 會發出提示信息,不會退出 puts('----->puts') # 會打印括號中的信息 execute(world)
fabric爲了讓輸出日誌更具備可讀性,對命令行中斷的顏色輸出進行了封裝,使用print打印帶有不一樣顏色的文本,這些顏色包含在fabric.colors中。像warn,puts打印輸出的,也能夠直接渲染顏色
def ls(path='.'): run('ls {0}'.format(path)) def hello(): execute(hell) # task任務hell warn(yellow('----->warn')) # 會發出提示信息,不會退出 puts(green('----->puts')) # 會打印括號中的信息 execute(ls) # task任務ls print(green('the text is green')) # 單純的渲染文字: def hell(name='world'): print('hello %s' % name)
有時候咱們在某一步執行錯誤,會給用戶提示,是否繼續執行時,confirm就很是有用了,它包含在 fabric.contrib.console中
def testconfirm(): result = confirm('Continue Anyway?') print(result) # 會提示輸入y/n # y 時 result爲True # n 時 result爲False
下載一個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 -m print(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)
execute(start_redis)
PS:關於set -m 的做用以下: "set -m" turns on job control, you can run processes in a separate process group.
理解:在一個獨立的進程組裏面運行咱們的進程。
http://www.cnblogs.com/dachenzi/p/8695330.html