Python 應用自動化部署工具Fabirc
介紹
Fabirc是基於python實現的SSH命令列工具,非常適合應用的自動化部署,或者執行系統管理任務。
python2:pip3 install fabric
python3:pip3 install fabric3
簡單的例子:
root@openstack:~# cat fabfile.py
def hello():
print('hello world!')
root@openstack:~# fab hello
hello world!
這個fab簡單地匯入了fabfile,並執行定義的hello函式。
命令列啟動
fab作為Fabric程式的命令列入口,提供了豐富的引數呼叫,命令格式如下:
root@openstack:~# fab --help
Usage: fab [options] <command>[:arg1,arg2=val2,host=foo,hosts='h1;h2',...] ...
各引數含義如下:
引數項 | 含義 |
---|---|
-l | 顯示可用任務函式名 |
-f | 指定fab入口檔案,預設為fabfile.py |
-g | 指定閘道器(中轉裝置),比如堡壘機環境,填寫堡壘機IP即可 |
-H | 指定目標主機,多臺主機用“,”分隔 |
-P | 以非同步並行方式執行多臺主機任務,預設為序列執行 |
-R | 指定角色(Role) |
-t | 設定裝置連線超時時間 |
-T | 設定遠端主機命令執行超時時間 |
-w | 當命令執行失敗,發出告警,而非預設終止任務 |
fabfile全域性屬性設定
env物件的作用是定義fabfile的全域性設定,各屬性說明如下:
屬性 | 含義 |
---|---|
env.host | 定義目標主機,以python的列表表示,如env.host=['xx.xx.xx.xx','xx.xx.xx.xx'] |
env.exclude_hosts | 排除指定主機,以python的列表表示 |
env.port | 定義目標主機埠,預設為22 |
env.user | 定義使用者名稱 |
env.password | 定義密碼 |
env.passwords | 與password功能一樣,區別在於不同主機配置不同密碼的應用情景,配置此項的時候需要配置使用者、主機、埠等資訊,如:env.passwords = {'[email protected]:22': '123', '[email protected]':'234'} |
env.getway | 定義閘道器 |
env.deploy_release_dir | 自定義全域性變數 |
env.roledefs | 定義角色分組 |
常用的API
Fabric支援常用的方法及說明如下:
方法 | 說明 | |
---|---|---|
local | 執行本地命令,如:local('hostname') | |
lcd | 切換本地目錄,lcd('/root') | |
cd | 切換遠端目錄,cd('cd') | |
run | 執行遠端命令,如:run('hostname') | |
sudo | sudo執行遠端命令,如:sudo('echo “123456″ | passwd --stdin root') |
put | 上傳本地檔案到遠端主機,如:put(src,des) | |
get | 從遠端主機下載檔案到本地,如:get(des,src) | |
prompt | 獲取使用者輸入資訊,如:prompt(‘please enter a new password:’) | |
confirm | 獲取提示資訊確認,如:confirm('failed.Continue[Y/n]?') | |
reboot | 重啟遠端主機,reboot() | |
@task | 函式修飾符,標識的函式為fab可呼叫的 | |
@runs_once | 函式修飾符,表示的函式只會執行一次 |
從一個例項入手
假設我們需要為一個 web 應用建立 fabfile 。具體的情景如下:這個 web 應用的程式碼使用 git 託管在一臺遠端伺服器vcshost
上,我們把它的程式碼庫克隆到了本地localhost
中。我們希望在我們把修改後的程式碼 push 回 vcshost 時,自動把新的版本安裝到另一臺遠端伺服器my_server
上。我們將通過自動化本地和遠端 git 命令來完成這些工作。
關於 fabfile 檔案放置位置的最佳時間是專案的根目錄:
. |-- __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
註解
在這裡我們使用一個 Django 應用為例——不過 Fabric 並s依賴於外部程式碼,除了它的 SSH 庫。
作為起步,我們希望先執行測試準備好部署後,再提交到 VCS(版本控制系統):
from fabric.api import local
def prepare_deploy():
local("./manage.py test my_app")
local("git add -p && git commit")
local("git push")
這段程式碼的輸出會是這樣:
$ fab prepare_deploy [localhost] run: ./manage.py test my_app Creating test database... Creating tables Creating indexes .......................................... ---------------------------------------------------------------------- Ran 42 tests in 9.138s OK Destroying 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.
這段程式碼很簡單,匯入一個 Fabric API:local
,然後用它執行本地 Shell 命令並與之互動,剩下的 Fabric API 也都類似——它們都只是 Python。
參見
用你的方式來組織
因為 Fabric “只是 Python”,所以你可以按你喜歡的方式來組織 fabfile 。比如說,把任務分割成多個子任務:
from fabric.api import local
def 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()
這個prepare_deploy
任務仍可以像之前那樣呼叫,但現在只要你願意,就可以呼叫更細粒度的子任務。
故障
我們的基本案例已經可以正常工作了,但如果測試失敗了會怎樣?我們應該抓住機會即使停下任務,並在部署之前修復這些失敗的測試。
Fabric 會檢查被呼叫程式的返回值,如果這些程式沒有乾淨地退出,Fabric 會終止操作。下面我們就來看看如果一個測試用例遇到錯誤時會發生什麼:
$ fab prepare_deploy [localhost] run: ./manage.py test my_app Creating test database... Creating tables Creating indexes .............E............................ ====================================================================== ERROR: testSomething (my_project.my_app.tests.MainTests) ---------------------------------------------------------------------- Traceback (most recent call last): [...] ---------------------------------------------------------------------- Ran 42 tests in 9.138s FAILED (errors=1) Destroying test database... Fatal error: local() encountered an error (return code 2) while executing './manage.py test my_app' Aborting.
太好了!我們什麼都不用做,Fabric 檢測到了錯誤並終止,不會繼續執行 commit 任務。
參見
Failure handling (usage documentation)
故障處理但如果我們想更加靈活,給使用者另一個選擇,該怎麼辦?一個名為warn_only的設定(或著說環境變數,通常縮寫為env var)可以把退出換為警告,以提供更靈活的錯誤處理。
讓我們把這個設定丟到test
函式中,然後注意這個local
呼叫的結果:
from __future__ import with_statement
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)
if result.failed and not confirm("Tests failed. Continue anyway?"):
abort("Aborting at user request.")
[...]
為了引入這個新特性,我們需要新增一些新東西:
-
在 Python 2.5 中,需要從
__future__
中匯入with
; -
Fabric
contrib.console
子模組提供了confirm
函式,用於簡單的 yes/no 提示。 -
settings
上下文管理器提供了特定程式碼塊特殊設定的功能。 -
local
這樣執行命令的操作會返回一個包含執行結果(.failed
或.return_code
屬性)的物件。 -
abort
函式用於手動停止任務的執行。
即使增加了上述複雜度,整個處理過程仍然很容易理解,而且它已經遠比之前靈活。
參見
建立連線
讓我們回到 fabfile 的主旨:定義一個deploy
任務,讓它在一臺或多臺遠端伺服器上執行,並保證程式碼是最新的:
def deploy():
code_dir = '/srv/django/myproject'
with cd(code_dir):
run("git pull")
run("touch app.wsgi")
這裡再次引入了一些新的概念:
-
Fabric 是 Python——所以我們可以自由地使用變數、字串等常規的 Python 程式碼;
-
cd
函式是一個簡易的字首命令,相當於執行cd/to/some/directory
,和lcd
函式類似,只不過後者是在本地執行。 -
~fabric.operations.run` 和
local
類似,不過是在遠端而非本地執行。
我們還需要保證在檔案頂部匯入了這些新函式:
from __future__ import with_statement
from fabric.api import local, settings, abort, run, cd
from fabric.contrib.console import confirm
改好之後,我們重新部署:
$ fab deploy No 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 依舊不知道該在哪裡執行這些遠端命令。遇到這種情況時,Fabric 會在執行時提示我們。連線的定義使用 SSH 風格的“主機串”(例如:user@host:port ),預設使用你的本地使用者名稱——所以在這個例子中,我們只需要指定主機名my_server
。
與遠端互動
如果你已經得到了程式碼,說明gitpull
執行非常順利——但如果這是第一次部署呢?最好也能應付這樣的情況,這時應該使用gitclone
來初始化程式碼庫:
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")
和上面呼叫local
一樣,run
也提供基於 Shell 命令構建乾淨的 Python 邏輯。這裡最有趣的部分是gitclone
:因為我們是用 git 的 SSH 方法來訪問 git 伺服器上的程式碼庫,這意味著我們遠端執行的run
需要自己提供身份驗證。
舊版本的 Fabric(和其他類似的高層次 SSH 庫)像在監獄裡一樣執行遠端命令,無法提供本地互動。當你迫切需要輸入密碼或者與遠端程式互動時,這就很成問題。
Fabric 1.0 和後續的版本突破了這個限制,並保證你和另一端的會話互動。讓我們看看當我們在一臺沒有 git checkout 的新伺服器上執行更新後的 deploy 任務時會發生什麼:
$ fab deploy No 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.
注意那個Password:
提示——那就是我們在 web 伺服器上的遠端git
應用在請求 git 密碼。我們可以在本地輸入密碼,然後像往常一樣繼續克隆。
參見
預定義連線
在執行輸入連線資訊已經是非常古老的做法了,Fabric 提供了一套在 fabfile 或命令列中指定伺服器資訊的簡單方法。這裡我們不展開說明,但是會展示最常用的方法:設定全域性主機列表env.hosts。
env是一個全域性的類字典物件,是 Fabric 很多設定的基礎,也能在 with 表示式中使用(事實上,前面見過的~fabric.context_managers.settings
就是它的一個簡單封裝)。因此,我們可以在模組層次上,在 fabfile 的頂部附近修改它,就像這樣:
from __future__ import with_statement
from fabric.api import *
from fabric.contrib.console import confirm
env.hosts = ['my_server']
def test():
do_test_stuff()
當fab
載入 fabfile 時,將會執行我們對env
的修改並儲存設定的變化。最終結果如上所示:我們的deploy
任務將在my_server
上執行。
這就是如何指定 Fabric 一次性控制多臺遠端伺服器的方法:env.hosts
是一個列表,fab
對它迭代,對每個連線執行指定的任務。
參見
環境字典 env,How host lists are constructed
總結
雖然經歷了很多,我們的 fabfile 檔案仍然相當短。下面是它的完整內容:
from __future__ import with_statement
from fabric.api import *
from fabric.contrib.console import confirm
env.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")
但它已經涉及到了 Fabric 中的很多功能:
-
定義 fabfile 任務,並用fab執行;
-
用
local
呼叫本地 shell 命令; -
通過
settings
修改 env 變數; -
處理失敗命令、提示使用者、手動取消任務;
-
以及定義主機列表、使用
run
來執行遠端命令。
還有更多這裡沒有涉及到的內容,你還可以看看所有“參見”中的連結,以及索引頁的內容表。
更多請參考:https://fabric-chs.readthedocs.io/zh_CN/chs/tutorial.html