Python應(yīng)用自動(dòng)化部署工具Fabric原理及使用解析
介紹
Fabirc是基于python實(shí)現(xiàn)的SSH命令行工具,非常適合應(yīng)用的自動(dòng)化部署,或者執(zhí)行系統(tǒng)管理任務(wù)。
python2:pip3 install fabric
python3:pip3 install fabric3
簡(jiǎn)單的例子:
root@openstack:~# cat fabfile.pydef hello(): print(’hello world!’) root@openstack:~# fab hellohello world!
這個(gè)fab簡(jiǎn)單地導(dǎo)入了fabfile,并執(zhí)行定義的hello函數(shù)。
命令行啟動(dòng)
fab作為Fabric程序的命令行入口,提供了豐富的參數(shù)調(diào)用,命令格式如下:
root@openstack:~# fab --helpUsage: fab [options] <command>[:arg1,arg2=val2,host=foo,hosts=’h1;h2’,...] ...
各參數(shù)含義如下:
參數(shù)項(xiàng) 含義 -l 顯示可用任務(wù)函數(shù)名 -f 指定fab入口文件,默認(rèn)為fabfile.py -g 指定網(wǎng)關(guān)(中轉(zhuǎn)設(shè)備),比如堡壘機(jī)環(huán)境,填寫堡壘機(jī)IP即可 -H 指定目標(biāo)主機(jī),多臺(tái)主機(jī)用“,”分隔 -P 以異步并行方式運(yùn)行多臺(tái)主機(jī)任務(wù),默認(rèn)為串行運(yùn)行 -R 指定角色(Role) -t 設(shè)置設(shè)備連接超時(shí)時(shí)間 -T 設(shè)置遠(yuǎn)程主機(jī)命令執(zhí)行超時(shí)時(shí)間 -w 當(dāng)命令執(zhí)行失敗,發(fā)出告警,而非默認(rèn)終止任務(wù)
fabfile全局屬性設(shè)定
env對(duì)象的作用是定義fabfile的全局設(shè)定,各屬性說明如下:
屬性 含義 env.host 定義目標(biāo)主機(jī),以python的列表表示,如env.host=[’xx.xx.xx.xx’,’xx.xx.xx.xx’] env.exclude_hosts 排除指定主機(jī),以python的列表表示 env.port 定義目標(biāo)主機(jī)端口,默認(rèn)為22 env.user 定義用戶名 env.password 定義密碼 env.passwords 與password功能一樣,區(qū)別在于不同主機(jī)配置不同密碼的應(yīng)用情景,配置此項(xiàng)的時(shí)候需要配置用戶、主機(jī)、端口等信息,如:env.passwords = {’[email protected]:22’: ’123’, ’[email protected]’:’234’} env.getway 定義網(wǎng)關(guān) env.deploy_release_dir 自定義全局變量 env.roledefs 定義角色分組
常用的API
Fabric支持常用的方法及說明如下:
方法 說明 local 執(zhí)行本地命令,如:local(’hostname’) lcd 切換本地目錄,lcd(’/root’) cd 切換遠(yuǎn)程目錄,cd(’cd’) run 執(zhí)行遠(yuǎn)程命令,如:run(’hostname’) sudo sudo執(zhí)行遠(yuǎn)程命令,如:sudo(’echo “123456″ passwd --stdin root’) put 上傳本地文件到遠(yuǎn)程主機(jī),如:put(src,des) get 從遠(yuǎn)程主機(jī)下載文件到本地,如:get(des,src) prompt 獲取用戶輸入信息,如:prompt(‘please enter a new password:’) confirm 獲取提示信息確認(rèn),如:confirm(’failed.Continue[Y/n]?’) reboot 重啟遠(yuǎn)程主機(jī),reboot() @task 函數(shù)修飾符,標(biāo)識(shí)的函數(shù)為fab可調(diào)用的 @runs_once 函數(shù)修飾符,表示的函數(shù)只會(huì)執(zhí)行一次
從一個(gè)實(shí)例入手
假設(shè)我們需要為一個(gè) web 應(yīng)用創(chuàng)建 fabfile 。具體的情景如下:這個(gè) web 應(yīng)用的代碼使用 git 托管在一臺(tái)遠(yuǎn)程服務(wù)器 vcshost 上,我們把它的代碼庫(kù)克隆到了本地 localhost 中。我們希望在我們把修改后的代碼 push 回 vcshost 時(shí),自動(dòng)把新的版本安裝到另一臺(tái)遠(yuǎn)程服務(wù)器 my_server 上。我們將通過自動(dòng)化本地和遠(yuǎn)程 git 命令來(lái)完成這些工作。
關(guān)于 fabfile 文件放置位置的最佳時(shí)間是項(xiàng)目的根目錄:
.|-- __init__.py|-- app.wsgi|-- fabfile.py <-- our fabfile!|-- manage.py`-- my_app |-- __init__.py |-- models.py |-- templates | `-- index.html |-- tests.py |-- urls.py `-- views.py
注解
在這里我們使用一個(gè) Django 應(yīng)用為例——不過 Fabric 并s依賴于外部代碼,除了它的 SSH 庫(kù)。
作為起步,我們希望先執(zhí)行測(cè)試準(zhǔn)備好部署后,再提交到 VCS(版本控制系統(tǒng)):
from fabric.api import localdef prepare_deploy(): local('./manage.py test my_app') local('git add -p && git commit') local('git push')
這段代碼的輸出會(huì)是這樣:
$ fab prepare_deploy[localhost] run: ./manage.py test my_appCreating test database...Creating tablesCreating indexes..........................................----------------------------------------------------------------------Ran 42 tests in 9.138sOKDestroying test database...[localhost] run: git add -p && git commit<interactive Git add / git commit edit message session>[localhost] run: git push<git push session, possibly merging conflicts interactively>Done.
這段代碼很簡(jiǎn)單,導(dǎo)入一個(gè) Fabric API: local ,然后用它執(zhí)行本地 Shell 命令并與之交互,剩下的 Fabric API 也都類似——它們都只是 Python。
用你的方式來(lái)組織
因?yàn)?Fabric “只是 Python”,所以你可以按你喜歡的方式來(lái)組織 fabfile 。比如說,把任務(wù)分割成多個(gè)子任務(wù):
from fabric.api import localdef test(): local('./manage.py test my_app')def commit(): local('git add -p && git commit')def push(): local('git push')def prepare_deploy(): test() commit() push()
這個(gè) prepare_deploy 任務(wù)仍可以像之前那樣調(diào)用,但現(xiàn)在只要你愿意,就可以調(diào)用更細(xì)粒度的子任務(wù)。
故障
我們的基本案例已經(jīng)可以正常工作了,但如果測(cè)試失敗了會(huì)怎樣?我們應(yīng)該抓住機(jī)會(huì)即使停下任務(wù),并在部署之前修復(fù)這些失敗的測(cè)試。
Fabric 會(huì)檢查被調(diào)用程序的返回值,如果這些程序沒有干凈地退出,F(xiàn)abric 會(huì)終止操作。下面我們就來(lái)看看如果一個(gè)測(cè)試用例遇到錯(cuò)誤時(shí)會(huì)發(fā)生什么:
$ fab prepare_deploy[localhost] run: ./manage.py test my_appCreating test database...Creating tablesCreating indexes.............E............................======================================================================ERROR: testSomething (my_project.my_app.tests.MainTests)----------------------------------------------------------------------Traceback (most recent call last):[...]----------------------------------------------------------------------Ran 42 tests in 9.138sFAILED (errors=1)Destroying test database...Fatal error: local() encountered an error (return code 2) while executing ’./manage.py test my_app’Aborting.
太好了!我們什么都不用做,F(xiàn)abric 檢測(cè)到了錯(cuò)誤并終止,不會(huì)繼續(xù)執(zhí)行 commit 任務(wù)。
參見
Failure handling (usage documentation)
故障處理但如果我們想更加靈活,給用戶另一個(gè)選擇,該怎么辦?一個(gè)名為 warn_only 的設(shè)置(或著說 環(huán)境變量 ,通常縮寫為 env var )可以把退出換為警告,以提供更靈活的錯(cuò)誤處理。
讓我們把這個(gè)設(shè)置丟到 test 函數(shù)中,然后注意這個(gè) local 調(diào)用的結(jié)果:
from __future__ import with_statementfrom fabric.api import local, settings, abortfrom fabric.contrib.console import confirmdef test(): with settings(warn_only=True): result = local(’./manage.py test my_app’, capture=True) if result.failed and not confirm('Tests failed. Continue anyway?'): abort('Aborting at user request.')[...]
為了引入這個(gè)新特性,我們需要添加一些新東西:
在 Python 2.5 中,需要從 __future__ 中導(dǎo)入 with ;
Fabric contrib.console 子模塊提供了 confirm 函數(shù),用于簡(jiǎn)單的 yes/no 提示。
settings 上下文管理器提供了特定代碼塊特殊設(shè)置的功能。
local 這樣運(yùn)行命令的操作會(huì)返回一個(gè)包含執(zhí)行結(jié)果( .failed 或 .return_code 屬性)的對(duì)象。
abort 函數(shù)用于手動(dòng)停止任務(wù)的執(zhí)行。
即使增加了上述復(fù)雜度,整個(gè)處理過程仍然很容易理解,而且它已經(jīng)遠(yuǎn)比之前靈活。
建立連接
讓我們回到 fabfile 的主旨:定義一個(gè) deploy 任務(wù),讓它在一臺(tái)或多臺(tái)遠(yuǎn)程服務(wù)器上運(yùn)行,并保證代碼是最新的:
def deploy():code_dir = ’/srv/django/myproject’with cd(code_dir):run('git pull')run('touch app.wsgi')
這里再次引入了一些新的概念:
Fabric 是 Python——所以我們可以自由地使用變量、字符串等常規(guī)的 Python 代碼;
cd 函數(shù)是一個(gè)簡(jiǎn)易的前綴命令,相當(dāng)于運(yùn)行 cd /to/some/directory ,和 lcd 函數(shù)類似,只不過后者是在本地執(zhí)行。
~fabric.operations.run` 和 local 類似,不過是在 遠(yuǎn)程 而非本地執(zhí)行。
我們還需要保證在文件頂部導(dǎo)入了這些新函數(shù):
from __future__ import with_statementfrom fabric.api import local, settings, abort, run, cdfrom fabric.contrib.console import confirm
改好之后,我們重新部署:
$ fab deployNo hosts found. Please specify (single) host string for connection: my_server[my_server] run: git pull[my_server] out: Already up-to-date.[my_server] out:[my_server] run: touch app.wsgi
Done.
我們并沒有在 fabfile 中指定任何連接信息,所以 Fabric 依舊不知道該在哪里運(yùn)行這些遠(yuǎn)程命令。遇到這種情況時(shí),F(xiàn)abric 會(huì)在運(yùn)行時(shí)提示我們。連接的定義使用 SSH 風(fēng)格的“主機(jī)串”(例如: user@host:port ),默認(rèn)使用你的本地用戶名——所以在這個(gè)例子中,我們只需要指定主機(jī)名 my_server 。
與遠(yuǎn)程交互
如果你已經(jīng)得到了代碼,說明 git pull 執(zhí)行非常順利——但如果這是第一次部署呢?最好也能應(yīng)付這樣的情況,這時(shí)應(yīng)該使用 git clone 來(lái)初始化代碼庫(kù):
def deploy(): code_dir = ’/srv/django/myproject’ with settings(warn_only=True): if run('test -d %s' % code_dir).failed: run('git clone user@vcshost:/path/to/repo/.git %s' % code_dir) with cd(code_dir): run('git pull') run('touch app.wsgi')
和上面調(diào)用 local 一樣, run 也提供基于 Shell 命令構(gòu)建干凈的 Python 邏輯。這里最有趣的部分是 git clone :因?yàn)槲覀兪怯?git 的 SSH 方法來(lái)訪問 git 服務(wù)器上的代碼庫(kù),這意味著我們遠(yuǎn)程執(zhí)行的 run 需要自己提供身份驗(yàn)證。
舊版本的 Fabric(和其他類似的高層次 SSH 庫(kù))像在監(jiān)獄里一樣運(yùn)行遠(yuǎn)程命令,無(wú)法提供本地交互。當(dāng)你迫切需要輸入密碼或者與遠(yuǎn)程程序交互時(shí),這就很成問題。
Fabric 1.0 和后續(xù)的版本突破了這個(gè)限制,并保證你和另一端的會(huì)話交互。讓我們看看當(dāng)我們?cè)谝慌_(tái)沒有 git checkout 的新服務(wù)器上運(yùn)行更新后的 deploy 任務(wù)時(shí)會(huì)發(fā)生什么:
$ fab deployNo hosts found. Please specify (single) host string for connection: my_server[my_server] run: test -d /srv/django/myproject
Warning: run() encountered an error (return code 1) while executing ’test -d /srv/django/myproject’
[my_server] run: git clone user@vcshost:/path/to/repo/.git /srv/django/myproject[my_server] out: Cloning into /srv/django/myproject...[my_server] out: Password: <enter password>[my_server] out: remote: Counting objects: 6698, done.[my_server] out: remote: Compressing objects: 100% (2237/2237), done.[my_server] out: remote: Total 6698 (delta 4633), reused 6414 (delta 4412)[my_server] out: Receiving objects: 100% (6698/6698), 1.28 MiB, done.[my_server] out: Resolving deltas: 100% (4633/4633), done.[my_server] out:[my_server] run: git pull[my_server] out: Already up-to-date.[my_server] out:[my_server] run: touch app.wsgi
Done.
注意那個(gè) Password: 提示——那就是我們?cè)?web 服務(wù)器上的遠(yuǎn)程 git 應(yīng)用在請(qǐng)求 git 密碼。我們可以在本地輸入密碼,然后像往常一樣繼續(xù)克隆。
參見
與遠(yuǎn)程程序集成
預(yù)定義連接
在運(yùn)行輸入連接信息已經(jīng)是非常古老的做法了,F(xiàn)abric 提供了一套在 fabfile 或命令行中指定服務(wù)器信息的簡(jiǎn)單方法。這里我們不展開說明,但是會(huì)展示最常用的方法:設(shè)置全局主機(jī)列表 env.hosts 。
env 是一個(gè)全局的類字典對(duì)象,是 Fabric 很多設(shè)置的基礎(chǔ),也能在 with 表達(dá)式中使用(事實(shí)上,前面見過的 ~fabric.context_managers.settings 就是它的一個(gè)簡(jiǎn)單封裝)。因此,我們可以在模塊層次上,在 fabfile 的頂部附近修改它,就像這樣:
from __future__ import with_statementfrom fabric.api import *from fabric.contrib.console import confirmenv.hosts = [’my_server’]def test(): do_test_stuff()
當(dāng) fab 加載 fabfile 時(shí),將會(huì)執(zhí)行我們對(duì) env 的修改并保存設(shè)置的變化。最終結(jié)果如上所示:我們的 deploy 任務(wù)將在 my_server 上運(yùn)行。
這就是如何指定 Fabric 一次性控制多臺(tái)遠(yuǎn)程服務(wù)器的方法: env.hosts 是一個(gè)列表, fab 對(duì)它迭代,對(duì)每個(gè)連接運(yùn)行指定的任務(wù)。
總結(jié)
雖然經(jīng)歷了很多,我們的 fabfile 文件仍然相當(dāng)短。下面是它的完整內(nèi)容:
from __future__ import with_statementfrom fabric.api import *from fabric.contrib.console import confirmenv.hosts = [’my_server’]def test(): with settings(warn_only=True): result = local(’./manage.py test my_app’, capture=True) if result.failed and not confirm('Tests failed. Continue anyway?'): abort('Aborting at user request.')def commit(): local('git add -p && git commit')def push(): local('git push')def prepare_deploy(): test() commit() push()def deploy(): code_dir = ’/srv/django/myproject’ with settings(warn_only=True): if run('test -d %s' % code_dir).failed: run('git clone user@vcshost:/path/to/repo/.git %s' % code_dir) with cd(code_dir): run('git pull') run('touch app.wsgi')
但它已經(jīng)涉及到了 Fabric 中的很多功能:
定義 fabfile 任務(wù),并用 fab 執(zhí)行;
用 local 調(diào)用本地 shell 命令;
通過 settings 修改 env 變量;
處理失敗命令、提示用戶、手動(dòng)取消任務(wù);
以及定義主機(jī)列表、使用 run 來(lái)執(zhí)行遠(yuǎn)程命令。
還有更多這里沒有涉及到的內(nèi)容,你還可以看看所有“參見”中的鏈接,以及 索引頁(yè) 的內(nèi)容表。
更多請(qǐng)參考:https://fabric-chs.readthedocs.io/zh_CN/chs/tutorial.html
常用示例
1、上傳文件
fabric可以將本地文件上傳到遠(yuǎn)程服務(wù)器上,這個(gè)操作要用到put函數(shù)
2、示例代碼
#coding=utf-8from fabric.api import *from fabric.contrib.console import confirmimport hashlibhost = ’[email protected]:22’password = ’123456’env.hosts=[host]env.password = passworddef md5sum(filename): fd = open(filename,'r') fcont = fd.read() fd.close() fmd5 = hashlib.md5(fcont) return fmd5def upload_file(filename): run('mkdir -p /root/upload') with cd(’/root/upload’): with settings(warn_only=True): res = put(filename,filename) if res.failed and not confirm('put file failed, Continue[Y/N]?'): abort(u’終止上傳文件’)def checkmd5(filename): fmd5 = md5sum(filename) lmd5 = fmd5.hexdigest() target = ’/root/upload/’+filename rmd5=run('md5sum ' + target).split(’ ’)[0] if lmd5 == rmd5: print ’ok,the file uploaded’ else: print ’error’def uploadfile(filename): upload_file(filename) checkmd5(filename)
執(zhí)行命令 fab -f uploadfile.py uploadfile:filename=fabricdemo1.py
3、程序分析
在執(zhí)行fab命令時(shí),可以指定函數(shù)的參數(shù),多個(gè)參數(shù)之間用逗號(hào)分隔
with settings(warn_only=True) 的作用,是在發(fā)生錯(cuò)誤時(shí),不中斷執(zhí)行,只會(huì)輸出警告信息
4、上傳文件夾
其實(shí)fabric也是可以上傳文件夾的,但是很多教程里都沒有提及,示例代碼如下
def uploadfolder(): run('mkdir -p /root/upload') with cd(’/root/upload’): with settings(warn_only=True): res = put(’testfolder’,’.’)
在uploadfile.py 同目錄下,有一個(gè)testfolder的文件夾,上面的代碼可以將這個(gè)文件夾上傳到/root/upload目錄下,主要注意的是put的第二個(gè)參數(shù),我這里放的是’.’,就表明要把本地的testfolder放到/root/upload目錄下。
不同于上傳文件,文件夾上上傳過程中是不能設(shè)置目標(biāo)文件夾的名字的,目標(biāo)文件夾必須先存在
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持好吧啦網(wǎng)。
相關(guān)文章:
1. ASP常用日期格式化函數(shù) FormatDate()2. chat.asp聊天程序的編寫方法3. CSS 使用Sprites技術(shù)實(shí)現(xiàn)圓角效果4. phpstudy apache開啟ssi使用詳解5. 詳解瀏覽器的緩存機(jī)制6. ASP中if語(yǔ)句、select 、while循環(huán)的使用方法7. 怎樣才能用js生成xmldom對(duì)象,并且在firefox中也實(shí)現(xiàn)xml數(shù)據(jù)島?8. HTML中的XML數(shù)據(jù)島記錄編輯與添加9. 利用FastReport傳遞圖片參數(shù)在報(bào)表上展示簽名信息的實(shí)現(xiàn)方法10. 推薦一個(gè)好看Table表格的css樣式代碼詳解
