Python远程部署利器Fabric详解
http://www.bjhee.com/fabric.html
Fabric是一個Python的庫,它提供了豐富的同SSH交互的接口,可以用來在本地或遠程機器上自動化、流水化地執行Shell命令。因此它非常適合用來做應用的遠程部署及系統維護。其上手也極其簡單,你需要的只是懂得基本的Shell命令。本文將為大家詳細介紹Fabric的使用。
內容索引
- 安裝Fabric
- 第一個例子
- 執行本地命令
- 執行遠程命令
- SSH功能函數
- 上下文管理器
- 錯誤處理
- 并行執行
- 補充
安裝Fabric
首先Python的版本必須是2.7以上,可以通過下面的命令查看當前Python的版本:
$ python -VFabric的官網是www.fabfile.org,源碼托管在Github上。你可以clone源碼到本地,然后通過下面的命令來安裝。
$ python setup.py develop在執行源碼安裝前,你必須先將Fabric的依賴包Paramiko裝上。所以,個人還是推薦使用pip安裝,只需一條命令即可:
$ pip install fabric第一個例子
萬事從Hello World開始,我們創建一個”fabfile.py”文件,然后寫個hello函數:
| 1 2 | def hello(): ????print "Hello Fabric!" |
現在,讓我們在”fabfile.py”的目錄下執行命令:
$ fab hello你可以在終端看到”Hello Fabric!”字樣。
簡單解釋下,”fabfile.py”文件中每個函數就是一個任務,任務名即函數名,上例中是”hello”。”fab”命令就是用來執行”fabfile.py”中定義的任務,它必須顯式地指定任務名。你可以使用參數”-l”來列出當前”fabfile.py”文件中定義了哪些任務:
$ fab -l任務可以帶參數,比如我們將hello函數改為:
| 1 2 | def hello(name, value): ????print "Hello Fabric! %s=%s" % (name,value) |
此時執行hello任務時,就要傳入參數值:
$ fab hello:name=Year,value=2016Fabric的腳本建議寫在”fabfile.py”文件中,如果你想換文件名,那就要在”fab”命令中用”-f”指定。比如我們將腳本放在”script.py”中,就要執行:
$ fab -f script.py hello執行本地命令
“fabric.api”包里的”local()”方法可以用來執行本地Shell命令,比如讓我們列出本地”/home/bjhee”目錄下的所有文件及目錄:
| 1 2 3 4 | from fabric.api import local def hello(): ????local('ls -l /home/bjhee/') |
“local()”方法有一個”capture”參數用來捕獲標準輸出,比如:
| 1 2 | def hello(): ????output = local('echo Hello', capture=True) |
這樣,Hello字樣不會輸出到屏幕上,而是保存在變量output里。”capture”參數的默認值是False。
執行遠程命令
Fabric真正強大之處不是在執行本地命令,而是可以方便的執行遠程機器上的Shell命令。它通過SSH實現,你需要的是在腳本中配置遠程機器地址及登錄信息:
| 1 2 3 4 5 6 7 8 | from fabric.api import run, env env.hosts = ['example1.com', 'example2.com'] env.user = 'bjhee' env.password = '111111' def hello(): ????run('ls -l /home/bjhee/') |
“fabric.api”包里的”run()”方法可以用來執行遠程Shell命令。上面的任務會分別到兩臺服務器”example1.com”和”example2.com”上執行”ls -l /home/bjhee/”命令。這里假設兩臺服務器的用戶名都是”bjhee”,密碼都是6個1。你也可以把用戶直接寫在hosts里,比如:
| 1 | env.hosts = ['bjhee@example1.com', 'bjhee@example2.com'] |
如果你的”env.hosts”里沒有配置某個服務器,但是你又想在這個服務器上執行任務,你可以在命令行中通過”-H”指定遠程服務器地址,多個服務器地址用逗號分隔:
$ fab -H bjhee@example3.com,bjhee@example4.com 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) |
現在讓我們執行:
$ fab task這時Fabric會先在一臺build服務器上執行build任務,然后在兩臺staging服務器上分別執行deploy任務。”@roles”裝飾器指定了它所裝飾的任務會被哪個角色的服務器執行。
如果某一任務上沒有指定某個角色,但是你又想讓這個角色的服務器也能運行該任務,你可以通過”-R”來指定角色名,多個角色用逗號分隔:
$ fab -R build deploy這樣”build”和”staging”角色的服務器都會運行”deploy”任務了。注:”staging”是裝飾器默認的,因此不用通過”-R”指定。
此外,上面的例子中,服務器的登錄密碼都是明文寫在腳本里的。這樣做不安全,推薦的方式是設置SSH自動登錄,具體方法大家可以去網上搜搜。
SSH功能函數
到目前為止,我們介紹了”local()”和”run()”函數分別用來執行本地和遠程Shell命令。Fabric還提供了其他豐富的功能函數來輔助執行命令,這里我們介紹幾個常用的:
功能類似于”run()”方法,區別是它相當于在Shell命令前加上了”sudo”,所以擁有超級用戶的權限。使用此功能前,你需要將你的用戶設為sudoer,而且無需輸密碼。具體操作可參見我的這篇文章。
| 1 2 3 4 5 6 7 | from fabric.api import env, sudo env.hosts = ['bjhee@example1.com', 'bjhee@example2.com'] env.password = '111111' def hello(): ????sudo('mkdir /var/www/myapp') |
?
它的工作原理是基于scp命令,使用的方法如下:
| 1 2 3 4 5 6 7 | from fabric.api import env, get env.hosts = ['bjhee@example.com',] env.password = '111111' def hello(): ????get('/var/log/myapp.log', 'myapp-0301.log') |
上述任務將遠程機上”/var/log/myapp.log”文件下載到本地當前目錄,并命名為”myapp-0301.log”。
同get一樣,put方法也是基于scp命令,使用的方法如下:
| 1 2 3 4 5 6 7 | from fabric.api import env, put env.hosts = ['bjhee@example1.com', 'bjhee@example2.com'] env.password = '111111' def hello(): ????put('/tmp/myapp-0301.tar.gz', '/var/www/myapp.tar.gz') |
上述任務將本地”/tmp/myapp-0301.tar.gz”文件分別上傳到兩臺遠程機的”/var/www/”目錄下,并命名為”myapp.tar.gz”。如果遠程機上的目錄需要超級用戶權限才能放文件,可以在”put()”方法里加上”use_sudo”參數:
| 1 | ????put('/tmp/myapp-0301.tar.gz', '/var/www/myapp.tar.gz', use_sudo=True) |
?
該方法類似于Shell中的”read”命令,它會在終端顯示一段文字來提示用戶輸入,并將用戶的輸入保存在變量里:
| 1 2 3 4 5 6 7 8 | from fabric.api import env, get, prompt env.hosts = ['bjhee@example.com',] env.password = '111111' def hello(): ????filename = prompt('Please input file name: ') ????get('/var/log/myapp.log', '%s.log' % filename) |
現在下載后的文件名將由用戶的輸入來決定。我們還可以對用戶輸入給出默認值及類型檢查:
| 1 | port = prompt('Please input port number: ', default=8080, validate=int) |
執行任務后,終端會顯示:
Please input port number: [8080]如果你直接按回車,則port變量即為默認值8080;如果你輸入字符串,終端會提醒你類型驗證失敗,讓你重新輸入,直到正確為止。
看方法名就猜到了,有時候安裝好環境后,需要重啟服務器,這時就要用到”reboot()”方法,你可以用”wait”參數來控制其等待多少秒后重啟,沒有此參數則代表立即重啟:
| 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) |
上面的restart任務將在一分鐘后重啟服務器。
上下文管理器
Fabric的上下文管理器是一系列與Python的”with”語句配合使用的方法,它可以在”with”語句塊內設置當前工作環境的上下文。讓我們介紹幾個常用的:
“cd()”方法在之前的范例中出現過,”with 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') |
上例中的文件會上傳到遠程機的”/var/www/”目錄下。出了”with cd()”語句塊后,工作目錄就回到初始的狀態,也就是”bjhee”用戶的根目錄。
“lcd()”就是”local cd”的意思,用法同”cd()”一樣,區別是它設置的是本地的工作目錄:
| 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') |
這個例子的執行效果跟上個例子一樣。
?
| 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') |
假設我們的PATH環境變量默認是”/sbin:/bin”,在上述”with path()”語句塊內PATH變量將變為”/sbin:/bin:/home/bjhee/tmp”。出了with語句塊后,PATH又回到原來的值。
Fabric環境變量即是我們例子中一直出現的”fabric.api.env”,它支持的參數可以從官方文檔中查到。
| 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') |
我們將環境參數”warn_only”暫時設為True,這樣遇到錯誤時任務不會退出。
可以用來臨時設置遠程和本地機上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') |
?
?
| 1 2 3 4 5 6 7 8 9 | from fabric.api import env, run, local, prefix env.hosts = ['bjhee@example1.com', ] env.password = '111111' def hello(): ????with prefix('echo Hi'): ????????run('pwd') ????????local('pwd') |
在上述”with prefix()”語句塊內,所有的”run()”或”local()”方法的執行都會加上”echo Hi && “前綴,也就是效果等同于:
| 1 2 | ????????run('echo Hi && pwd') ????????local('echo Hi && pwd') |
配合后一節我們會講到的錯誤處理,它可以確保在”prefix()”方法上的命令執行成功后才會執行語句塊內的命令。
錯誤處理
默認情況下,Fabric在任務遇到錯誤時就會退出,如果我們希望捕獲這個錯誤而不是退出任務的話,就要開啟”warn_only”參數。在上面介紹”settings()”上下文管理器時,我們已經看到了臨時開啟”warn_only”的方法了,如果要全局開啟,有兩個辦法:
?
| 1 2 3 | from fabric.api import env env.warn_only = True |
現在遇到錯誤時,控制臺會打出一個警告信息,然后繼續執行后續任務。那我們怎么捕獲錯誤并處理呢?像”run()”, “local()”, “sudo()”, “get()”, “put()”等SSH功能函數都有返回值。當返回值的”succeeded”屬性為True時,說明執行成功,反之就是失敗。你也可以檢查返回值的”failed”屬性,為True時就表示執行失敗,有錯誤發生。在開啟”warn_only”后,你可以通過”failed”屬性檢查捕獲錯誤,并執行相應的操作。
| 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) |
?
并行執行
我們在介紹執行遠程命令時曾提到過多臺機器的任務默認情況下是串行執行的。Fabric支持并行任務,當服務器的任務之間沒有依賴時,并行可以有效的加快執行速度。怎么開啟并行執行呢?辦法也是兩個:
?
| 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 |
這樣即便并行未開啟,”runs_in_parallel()”任務也會并行執行。反過來,我們可以在任務函數上加上”@serial”裝飾器:
| 1 2 3 4 5 6 7 8 | from fabric.api import serial def runs_in_parallel(): ????pass @serial def runs_serially(): ????pass |
這樣即便并行已經開啟,”runs_serially()”任務也會串行執行。
補充
這個部分用來補充Fabric的一些特別功能:
- 終端輸出帶顏色
我們習慣上認為綠色表示成功,黃色表示警告,而紅色表示錯誤,Fabric支持帶這些顏色的輸出來提示相應類型的信息:
| 1 2 3 4 5 6 | from fabric.colors import * def hello(): ????print green("Successful") ????print yellow("Warning") ????print red("Error") |
?
- 限制任務只能被執行一次
通過”execute()”方法,可以在一個”fab”命令中多次調用同一任務,如果想避免這個發生,就要在任務函數上加上”@runs_once”裝飾器。
| 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) |
現在不管我們”execute”多少次hello任務,都只會輸出一次”Hello Fabric!”字樣
更多內容請參閱Fabric的官方文檔。本篇中的示例代碼可以在這里下載。
轉載于:https://www.cnblogs.com/davidwang456/articles/9548813.html
總結
以上是生活随笔為你收集整理的Python远程部署利器Fabric详解的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 你所不知道的日志异步落库
- 下一篇: Flask部署和分发