Python自動化運維工具-Fabric部署及使用總結

散盡浮華發表於2016-06-08

 

使用shell命令進行復雜的運維時,程式碼往往變得複雜難懂,而使用python指令碼語言來編寫運維程式,就相當於開發普通的應用一樣,所以維護和擴充套件都比較簡單,更重要的是python運維工具fabric能自動登入其他伺服器進行各種操作,這種實現使用shell是很難做到的,但是使用fabric實現就很簡單,所以對於程式設計師的日常運維部署,建議使用python編寫指令碼。Fabric是基於Python實現的SSH命令列工具,簡化了SSH的應用程式部署及系統管理任務,它提供了系統基礎的操作元件,可以通過 SSH 的方式與遠端伺服器進行自動化互動, 實現本地或遠端shell命令,包括:命令執行、檔案上傳、下載及完整執行日誌輸出等功能。Fabric在Paramiko的基礎上做了更高一層的封裝,操作起來會更加簡單。Fabric官網地址為:http://www.fabfile.org/

一. Fabric安裝

Linux下預設有python環境,安裝fabric有兩種方式: 一是通過pip方式安裝; 而是通過fabric原始碼方式安裝.
一般選擇pip方式安裝, 安裝過程如下:

先安裝一些依賴
[root@kevin ~]# yum install make gcc gcc-c++ python-devel python-setuptools -y

安裝pip
首先下載py檔案:https://bootstrap.pypa.io/get-pip.py
或者百度雲盤下載地址:https://pan.baidu.com/s/1o7KylCm      提取密碼:eucx

[root@kevin ~]# cat /etc/redhat-release 
CentOS Linux release 7.5.1804 (Core) 

[root@kevin ~]# python -V
Python 2.7.5

[root@kevin ~]# wget https://bootstrap.pypa.io/get-pip.py
[root@kevin ~]# chmod 755 get-pip.py
[root@kevin ~]# python get-pip.py
Collecting pip
  Downloading https://files.pythonhosted.org/packages/c2/d7/90f34cb0d83a6c5631cf71dfe64cc1054598c843a92b400e55675cc2ac37/pip-18.1-py2.py3-none-any.whl (1.3MB)
    100% |████████████████████████████████| 1.3MB 56kB/s 
Collecting wheel
  Downloading https://files.pythonhosted.org/packages/ff/47/1dfa4795e24fd6f93d5d58602dd716c3f101cfd5a77cd9acbe519b44a0a9/wheel-0.32.3-py2.py3-none-any.whl
Installing collected packages: pip, wheel
Successfully installed pip-18.1 wheel-0.32.3

接著使用pip安裝fabric
[root@kevin ~]# pip install fabric

稍等一會就安裝完畢了,這時輸入fab就會彈出對應的選項
[root@kevin ~]# fab --version
Fabric 2.4.0
Paramiko 2.4.2
Invoke 1.2.0

[root@kevin ~]# fab --help
Usage: fab [--core-opts] task1 [--task1-opts] ... taskN [--taskN-opts]

Core options:

  --complete                         Print tab-completion candidates for given parse remainder.
  --hide=STRING                      Set default value of run()'s 'hide' kwarg.
  --no-dedupe                        Disable task deduplication.
  --print-completion-script=STRING   Print the tab-completion script for your preferred shell (bash|zsh|fish).
  --prompt-for-login-password        Request an upfront SSH-auth password prompt.
  --prompt-for-passphrase            Request an upfront SSH key passphrase prompt.
  --prompt-for-sudo-password         Prompt user at start of session for the sudo.password config value.
  --write-pyc                        Enable creation of .pyc files.
  -c STRING, --collection=STRING     Specify collection name to load.
  -d, --debug                        Enable debug output.
  -D INT, --list-depth=INT           When listing tasks, only show the first INT levels.
  -e, --echo                         Echo executed commands before running.
  -f STRING, --config=STRING         Runtime configuration file to use.
  -F STRING, --list-format=STRING    Change the display format used when listing tasks. Should be one of: flat (default), nested, json.
  -h [STRING], --help[=STRING]       Show core or per-task help and exit.
  -H STRING, --hosts=STRING          Comma-separated host name(s) to execute tasks against.
  -i, --identity                     Path to runtime SSH identity (key) file. May be given multiple times.
  -l [STRING], --list[=STRING]       List available tasks, optionally limited to a namespace.
  -p, --pty                          Use a pty when executing shell commands.
  -r STRING, --search-root=STRING    Change root directory used for finding task modules.
  -S STRING, --ssh-config=STRING     Path to runtime SSH config file.
  -V, --version                      Show version and exit.
  -w, --warn-only                    Warn, instead of failing, when shell commands fail.

=======================================================
溫馨提示:
如果安裝的是pip3, 則使用"pip3 install fabric3" 安裝fabric
=======================================================

二. Fabric 使用

Fabric命令說明
1) fab命令格式

fab是fabric的命令列入口 

命令的格式為:
# fab [options] <command>[:arg1,arg2=val2,host=foo,hosts='h1;h2',...] ...

2) fab命令常用引數

# fab --help     檢視幫助
 
 常用引數
-l  顯示定義好的任務函式名
-f  指定fab入口檔案,預設入口檔名為fabfile.py.. 即指定fabfile檔案
-g  指定閘道器(中轉)裝置,即HOST逗號分隔要操作的主機, 比如堡壘機環境,填寫堡壘機IP即可. 
-H  指定目標主機,多臺主機用‘,’號分隔
-p  遠端賬號的密碼,fab執行時預設使用root賬戶
-P  以非同步並行方式執行多主機任務,預設為序列執行
-R  指定role(角色),以角色名區分不同業務組裝置
-t  設定裝置連線超時時間(秒)
-T  設定遠端主機命令執行超時時間(秒)
-w  當命令執行失敗,發出警告,而非預設中止任務。

其他引數:
--set=KEY=VALUE,...     逗號分隔,設定環境變數
--shortlist             簡短列印可用命令
-c PATH                 指定本地配置檔案
-D                      不載入使用者known_hosts檔案
-i PATH                 指定私鑰檔案
-k                      不載入來自~/.ssh下的私鑰檔案
--port=PORT             指定SSH連線埠
-R ROLES                根據角色操作,逗號分隔
-s SHELL                指定新shell,預設是'/bin/bash -l -c'
--show=LEVELS           以逗號分隔的輸出
--ssh-config-path=PATH  SSH配置檔案路徑
-T N                    設定遠端命令超時時間,單位秒
-u USER                 連線遠端主機使用者名稱
-x HOSTS                以逗號分隔排除主機
-z INT                  併發程式數
 
例1:  通過遠端主機查詢172.16.50.45 (該主機的root密碼為123456)的主機名
[root@kevin ~]# fab -f fabtest.py -p 123456 -H 172.16.50.45 -- 'hostname'

例2: 本地執行命令
[root@kevin ~]# vim fabtest.py
from fabric.api import local
def command():
    local('ls')

[root@kevin ~]# fab -f fabtest.py command
[localhost] local: ls
fabfile.py  fabfile.pyc  tab.py  tab.pyc
Done.

例3: 遠端執行命令
[root@kevin ~]# vim fabtest.py

from fabric.api import run
def command():
    run('ls')

[root@kevin ~]# fab -f fabtest.py -H 192.168.1.120 -u user command
[192.168.1.120] Executing task 'command'[192.168.1.120] run: ls
[192.168.1.120] Login password for 'user':
[192.168.1.120] out: access.log  a.py
[192.168.1.120] out:
Done.
Disconnecting from 192.168.1.120... done.

如果在多臺主機執行,只需要-H後面的IP以逗號分隔即可。

例4:  給指令碼函式傳入位置引數
[root@kevin ~]# vim fabfile.py

from fabric.api import run
def hello(name="world"):
    print("Hello %s!" % name)

[root@kevin ~]# fab -H localhost hello
[localhost] Executing task 'hello'Hello world!
Done.

[root@kevin ~]# fab -H localhost hello:name=Python
[localhost] Executing task 'hello'Hello Python!
Done.

例5: 主機列表組
[root@kevin ~]# vim fabfile.py
from fabric.api import run, env
env.hosts = ['root@192.168.1.120:22', 'root@192.168.1.130:22']
env.password = '123.com'env.exclude_hosts = ['root@192.168.1.120:22']   # 排除主機
def command():
   run('ls')

[root@kevin ~]# fab command
env作用是定義fabfile全域性設定,類似於變數。還有一些常用的屬性:


例6: 定義角色分組
[root@kevin ~]# vim install.py
from fabric.api import run, env
env.roledefs = {    'web': ['192.168.1.10', '192.168.1.20'],    'db': ['192.168.1.30', '192.168.1.40']
}
env.password = '123'@roles('web')
def task1():
   run('yum install httpd -y')
@roles('db')
def task2():
   run('yum install mysql-server -y')
def deploy():
   execute(task1)
   execute(task2)

[root@kevin ~]# fab -f install.py deploy

例7: 上傳目錄到遠端主機
[root@kevin ~]# vim haha.py
from fabric.api import *
env.hosts = ['192.168.1.120']
env.user = 'user'env.password = '123.com'

def task():
   put('/root/abc', '/home/user')
   run('ls -l /home/user')

[root@kevin ~]# fab -f haha.py task

例8: 從遠端主機下載目錄
[root@kevin ~]# vim heihei.py
from fabric.api import *
env.hosts = ['192.168.1.120']
env.user = 'user'env.password = '123.com'
def task():
   get('/home/user/b', '/opt')
   local('ls -l /opt')

[root@kevin ~]# fab -f heihei.py task

例9: 列印顏色,有助於關鍵地方醒目
[root@kevin ~]# vim bobo.py
from fabric.colors import *
def show():
   print green('Successful.')
   print red('Failure!')
   print yellow('Warning.')

[root@kevin ~]# fab -f bobo.py show

3) fabfile檔案的編寫 (預設的檔名稱為fabfile)

fab命令是結合fabfile.py檔案(其他檔案通過-f filename 引數來引用)來搭配使用的。fab的部分命令列引數還能通過相應的方法來代替。

先來看一個小例子
[root@kevin ~]# cat fabfile.py
#!/usr/bin/env python
from fabric.api import run
 
#定義一個任務函式,通過run方法實現遠端執行"uname -s"命令
def host_type():
    run('uname -s')
 
[root@kevin ~]# fab -H localhost host_type
[localhost] Executing task 'host_type'
[localhost] run: uname -s
[localhost] Login password for 'devops':
[localhost] out: Linux
[localhost] out:
 
Done.
Disconnecting from localhost... done.
 
其中,必須要明白的是, fab命令引用的預設檔名fabfile.py!
如果使用的是預設檔名稱, 則fab執行命令中就不需要跟檔名.
如果使用非預設檔名稱,比如這裡不是fabfile.py, 而是host_type.py 檔案, 則需要通過"-f"來指定:
[root@kevin ~]# fab -H localhost -f host_type.py host_type
 
如果目標主機未配置金鑰認證信任,將會提示輸入目標主機對應賬號登入密碼。
 
再來看一個小例子
[root@kevin ~]# vim fabric.py
#!/usr/bin/python
# -*- coding:utf-8 -*-
  
 from fabric.api import *
  
 # 設定伺服器登入引數
 env.roledefs = {
     # 操作一致的放一組,一組執行同一個操作
     'servers1':['root@linux2:22',],
     # 第二組
     'servers2':['root@linux3:22',]
 }
  
 # 本機操作
 def localtask():
     local('/usr/local/nginx/nginx')
  
 # servers1伺服器組操作
 @roles('servers1')
 def task1():
     run('/usr/local/tomcat/bin/startup.sh')
  
 # servers2 伺服器組操作
 @roles('servers2')
 def task2():
     run('/usr/local/tomcat/bin/startup.sh')
  
 # 執行任務
 def doworks():
     execute(localtask)
     execute(task1)
     execute(task2)
 
 
以上Fabric配置,實現的目的是:
簡單的在本地啟動nginx伺服器, 在linux1和linux2上啟動了tomcat伺服器, 為了接受nginx伺服器的代理,這裡專門使用分組的方式為了適應機器比較多的叢集的需要;
另外這裡沒有設定伺服器的密碼,一是為了伺服器的安全;而是叢集間建議設定ssh免密登入,指令碼就不用設定密碼了;
方法doworks執行的就是最終彙總的任務;
 
開始執行
[root@kevin ~]# fab -f fabric.py doworks

4) fabfile全域性屬性 (env物件) 

fabfile之env物件的作用是定義fabfile的全域性設定,支援多個屬性,包含目標主機、使用者名稱、密碼、等角色.
env各屬性說明如下:
evn.host:           定義目標主機,可以用IP或主機名錶示,以Python的列表形式定義,如evn.hosts['192.168.56.133','192.168.56.134']。
env.exclude_hosts:  排除指定主機,如env.exclude_hosts=['192.168.56.133']。
env.user:           定義使用者名稱,如env.user="root"。
env.port:           定義目標主機埠,預設為22,如env.port="22"。
env.password:       定義密碼,如env.password='1234567'。
env.passwords:      與password功能一樣,區別在於不同主機不同密碼的應用場景,需要注意的是,配置passwords是需配置使用者、主機、埠等資訊;
env.gateway:        定義閘道器(中轉、堡壘機)IP,如env.gateway = '192.168.56.1'。
env.deploy_release_dir:  自定義全域性變數,格式:env.+"變數名稱",如env.deploy_release_dir、env.age、env.sex等。
env.roledefs:       定義角色分組,比如web組與db組主機區分開來;

比如
[root@kevin ~]# vim fabfile.py
..........
env.passwords = {
    'root@192.168.56.131:22':'1234567',
    'root@192.168.56.132:22':'1234567',
    'root@192.168.56.133:22':'1234567',
    'root@192.168.56.134:22':'1234567'
}

[root@kevin ~]# vim fabfile.py
..........
env.roledefs = {
    'webservers':['192.168.56.131','192.168.56.132','192.168.56.133'],
    'dbserver':['192.168.56.134','192.168.56.135']
}

env.roledefs的使用方法例項:
[root@kevin ~]# vim fabfile.py
..........
env.roledefs = {'webserver':['192.168.1.21','192.168.1.22'],'dbserver':['192.168.1.25','192.168.1.26']}
#引用分組時使用python裝飾器方式來進行,如:
@roles('webserver')
def webtask():
    run('/usr/local/nginx/sbin/nginx')

@roles('webserver','dbserver')
def publictask():
    run('uptime')


引用時使用Python修飾符的形式進行,角色修飾符下面的任務函式為其作用域,下面來看一個示例:
[root@kevin ~]# vim fabfile.py
..........
@roles('webservers')
def webtask():
    run('/etc/init.d/nginx start')


@roles('dbservers')
def dbtask():
    run('/etc/init.d/mysql start')


@roles('webservers','dbservers')
def pubclitasj():
    run('uptime')

def deploy():
    execute(webtask)
    execute(dbtask)
    execute(pubclitask)

在命令執行fab deploy就可以實現不同角色執行不同的任務函式了。

5) Fabric常用API

Fabric提供了一組簡單但功能強大的fabric.api命令集,簡單地呼叫這些API就能完成大部分應用場景需求。Fabric常用方法及說明如下:
local     執行本地命令,如:local('uname -s');
lcd       切換本地目錄,如:lcd('/home');
cd        切換遠端目錄,如:cd('/data/logs');
run       執行遠端命令,如:run('free -m');
sudo      sudo方式執行遠端命令,如:sudo('/etc/init.d/httpd start');
put       傳本地檔案到遠端主機,如:put('/home/user.info','/data/user.info');
prompt    獲得使用者輸入資訊,如:prompt('please input user password:');
confirm   獲得提示資訊確認,如:confirm("Tests failed. Continue[Y/N]?");
reboot    重啟遠端主機,如:reboot();
@task     函式修飾符,標識的函式為fab可呼叫的,非標記對fab不可見,純業務邏輯;
@runs_once 函式修復符,標識的函式只會執行一次,不受多臺主機影響。

6) Fabric應用示例說明 

示例一:  檢視本地和遠端主機資訊

檢視本地資訊
本示例呼叫local()方法執行本地(主控端)命令,新增"@runs_once"修飾符保證該任務函式只執行一次。呼叫run()方法執行遠端命令。
[root@kevin ~]# vim fabric1.1.py
#!/usr/bin/env python

from fabric.api import *

env.user = 'devops'
env.hosts = ['localhost']
env.password = '1234567'

@runs_once              #檢視本地系統資訊,當有多臺主機時只執行一次
def local_task():       #本地任務函式
    local("uname -a")

通過fab命令呼叫local_task任務函式執行結果如下:
[root@kevin ~]# fab -f fabric1.1.py local_task
[localhost] Executing task 'local_task'
[localhost] local: uname -a
Linux devops-virtual-machine 4.15.0-20-generic #21-Ubuntu SMP Tue Apr 24 06:16:15 UTC 2018 x86_64 x86_64 x86_64 GNU/Linux

Done.


檢視遠端主機資訊
[root@kevin ~]# vim fabric1.2.py
#!/usr/bin/env python

from fabric.api import *

env.user = 'root'
env.hosts = ['192.168.56.11']
env.password = '1234567'

def remote_task():
    with cd('/root'):        #"with"的作用是讓後面的表示式的語句繼承當前狀態,實現"cd /root/ && ls -l'的效果
        run('ls -l')

呼叫remote_task任務函式執行結果如下:
[root@kevin ~]# fab -f fabric1.2.py local_task
[192.168.56.11] Executing task 'remote_task'
[192.168.56.11] run: ls -l
[192.168.56.11] out: total 4
[192.168.56.11] out: -rw-------. 1 root root 1273 May 29 11:47 anaconda-ks.cfg
[192.168.56.11] out:

Done.
Disconnecting from 192.168.56.11... done.


如果將上面兩個檔案的需求, 放在一起
[root@kevin ~]# vim fabric1.py
#!/usr/bin/env python
# -*- encoding: utf-8 -*-

from fabric.api import *

env.user = 'root'
env.hosts = ['192.168.1.22']
env.password = '123456'

@runs_once   #檢視本地系統資訊,當有多臺主機時只執行一次
def local_task():   #本地任務函式
    local('uname -a')
    
def remote_task():
    with cd('/var/logs'):   #with的作用是讓後面的表示式語句繼承當前狀態,實現:cd /var/logs  && ls -l的效果
        run('ls -l')


[root@kevin ~]# fab -f fabric1.py local_task
[root@kevin ~]# fab -f fabric1.py remote_task

示例二:動態獲取遠端目錄列表

本示例使用"@task'修復符標誌入口函式go()對外部可見,配合"@runs_once"修飾符接受使用者輸入,最後呼叫worktask()任務函式實現遠端命令執行。
[root@kevin ~]# vim fabric2.py
#!/usr/bin/env python

from fabric.api import *

env.user = 'root'
env.hosts = ['192.168.56.11','192.168.56.12']
env.password = '1234567'


@runs_once           #主機遍歷過程中,只有第一臺觸發此函式
def input_raw():
    return prompt("Please input directory name:",default="/home")


def worktask(dirname):
    run("ls -l "+dirname)


@task           #限定只有go函式對fab命令可見
def go():
    getdirname = input_raw()
    worktask(getdirname)


解釋說明:
該示例實現了一個動態輸入遠端目錄名稱,再獲取目錄列表的功能,由於我們只要求輸入一次,在顯示所有主機上該目錄的列表資訊,呼叫一個子函式input_raw()同時配置@runs_once修復符來達到此目的。

執行結果如下:
[root@kevin ~]# fab -f fabric2.py go
[192.168.56.11] Executing task 'go'
Please input directory name: [/home] /root
[192.168.56.11] run: ls -l /root
[192.168.56.11] out: total 4
[192.168.56.11] out: -rw-------. 1 root root 1273 May 29 11:47 anaconda-ks.cfg
[192.168.56.11] out:

[192.168.56.12] Executing task 'go'
[192.168.56.12] run: ls -l /root
[192.168.56.12] out: total 4
[192.168.56.12] out: -rw-------. 1 root root 1273 May 29 11:59 anaconda-ks.cfg
[192.168.56.12] out:


Done.
Disconnecting from 192.168.56.11... done.
Disconnecting from 192.168.56.12... done.

示例三: 閘道器模式檔案上傳與執行

本示例通過Fabric的env物件定義閘道器模式,即俗稱的中轉、堡壘機環境。定義格式為"env.gateway='192.168.56.11'",其中IP“192.168.56.11”為堡壘機IP,
再結合任務韓素實現目標主機檔案上傳與執行的操作。
[root@kevin ~]# vim fabric3.py
#!/usr/bin/env python

from fabric.api import *
from fabric.context_managers import *
from fabric.contrib.console import confirm


env.user = 'root'
env.gateway = '192.168.56.11'                           #定義堡壘機IP,作為檔案上傳、執行的中轉裝置
env.hosts = ['192.168.56.12','192.168.56.13']

env.passwords = {
    'root@192.168.56.11:22':'1234567',                  #堡壘機賬號資訊
    'root@192.168.56.12:22':'1234567',
    'root@192.168.56.13:22':'1234567'
}

l_pack_path = "/home/install/nginx-1.6.3.tar.gz"        #本地安裝包路徑
r_pack_path = "/tmp/install"                            #遠端安裝包路徑


@task
def put_task():
    run("mkdir -p /tmp/install")
    with settings(warn_only=True):
        result = put(l_pack_path,r_pack_path)          #上傳安裝包
    if result.failed and not confirm("put file failed, Continue[Y/N]?"):
        abort("Aborint file put task!")


@task
def run_task():                    #執行遠端命令,安裝nginx
    with cd(r_pack_path):
        run("tar -xvf nginx-1.6.3.tar.gz")
        with cd("nginx-1.6.3/"):                     #使用with繼續繼承/tmp/install目錄位置狀態
            run("./nginx_install.sh")

@task
def go():       #上傳、安裝
    put_task()
    run_task()


如下命令執行結果, 預設為序列執行
[root@kevin ~]# fab -f fabric3.py go
[192.168.56.12] Executing task 'go'
[192.168.56.12] run: mkdir -p /tmp/install
[192.168.56.12] put: /home/install/nginx-1.6.3.tar.gz -> /tmp/install/nginx-1.6.3.tar.gz
[192.168.56.12] run: tar -xvf nginx-1.6.3.tar.gz
.....
.....
.....
[192.168.56.12] out: cp conf/nginx.conf '/usr/local/nginx/conf/nginx.conf.default'
[192.168.56.12] out: test -d '/usr/local/nginx/logs'         || mkdir -p '/usr/local/nginx/logs'
[192.168.56.12] out: test -d '/usr/local/nginx/logs' ||         mkdir -p '/usr/local/nginx/logs'
[192.168.56.12] out: test -d '/usr/local/nginx/html'         || cp -R html '/usr/local/nginx'
[192.168.56.12] out: test -d '/usr/local/nginx/logs' ||         mkdir -p '/usr/local/nginx/logs'
[192.168.56.12] out: make[1]: Leaving directory `/tmp/install/nginx-1.6.3'
[192.168.56.12] out: nginx: the configuration file /usr/local/nginx/conf/nginx.conf syntax is ok
[192.168.56.12] out: nginx: configuration file /usr/local/nginx/conf/nginx.conf test is successful
[192.168.56.12] out:

[192.168.56.13] Executing task 'go'
[192.168.56.13] run: mkdir -p /tmp/install
[192.168.56.13] put: /home/install/nginx-1.6.3.tar.gz -> /tmp/install/nginx-1.6.3.tar.gz
[192.168.56.13] run: tar -xvf nginx-1.6.3.tar.gz
....
....
....
[192.168.56.13] out: cp conf/nginx.conf '/usr/local/nginx/conf/nginx.conf.default'
[192.168.56.13] out: test -d '/usr/local/nginx/logs'         || mkdir -p '/usr/local/nginx/logs'
[192.168.56.13] out: test -d '/usr/local/nginx/logs' ||         mkdir -p '/usr/local/nginx/logs'
[192.168.56.13] out: test -d '/usr/local/nginx/html'         || cp -R html '/usr/local/nginx'
[192.168.56.13] out: test -d '/usr/local/nginx/logs' ||         mkdir -p '/usr/local/nginx/logs'
[192.168.56.13] out: make[1]: Leaving directory `/tmp/install/nginx-1.6.3'
[192.168.56.13] out: nginx: the configuration file /usr/local/nginx/conf/nginx.conf syntax is ok
[192.168.56.13] out: nginx: configuration file /usr/local/nginx/conf/nginx.conf test is successful
[192.168.56.13] out:


Done.
Disconnecting from 192.168.56.11... done.
Disconnecting from 192.168.56.12... done.
Disconnecting from 192.168.56.13... done.


再如下執行結果, 加P引數為非同步並行執行結果
[root@kevin ~]# fab -Pf fabric3.py go
[192.168.56.12] Executing task 'go'
[192.168.56.13] Executing task 'go'
[192.168.56.12] run: mkdir -p /tmp/install
[192.168.56.13] run: mkdir -p /tmp/install
[192.168.56.12] put: /home/install/nginx-1.6.3.tar.gz -> /tmp/install/nginx-1.6.3.tar.gz
[192.168.56.13] put: /home/install/nginx-1.6.3.tar.gz -> /tmp/install/nginx-1.6.3.tar.gz
[192.168.56.12] run: tar -xvf nginx-1.6.3.tar.gz
....
....
....
[192.168.56.12] out: nginx-1.6.3/html/index.html
[192.168.56.12] out: nginx-1.6.3/README
[192.168.56.12] out: nginx-1.6.3/nginx_install.sh
[192.168.56.12] out: nginx-1.6.3/configure
[192.168.56.12] out:

[192.168.56.12] run: ./nginx_install.sh
[192.168.56.13] run: tar -xvf nginx-1.6.3.tar.gz
[192.168.56.13] out: nginx-1.6.3/
[192.168.56.13] out: nginx-1.6.3/src/
....
....
....
[192.168.56.12] out: make[1]: Leaving directory `/tmp/install/nginx-1.6.3'
[192.168.56.12] out: nginx: the configuration file /usr/local/nginx/conf/nginx.conf syntax is ok
[192.168.56.12] out: nginx: configuration file /usr/local/nginx/conf/nginx.conf test is successful
[192.168.56.12] out:
....
....
...
[192.168.56.13] out: make[1]: Leaving directory `/tmp/install/nginx-1.6.3'
[192.168.56.13] out: nginx: the configuration file /usr/local/nginx/conf/nginx.conf syntax is ok
[192.168.56.13] out: nginx: configuration file /usr/local/nginx/conf/nginx.conf test is successful
[192.168.56.13] out:

示例四:  檔案打包, 上傳與校驗

我們時常做一些檔案包分發的工作,實施步驟一般是先壓縮打包,在批量上傳至目標伺服器,最後做一致性校驗。
本示例通過put()方法實現檔案的上傳,通過對比本地與遠端主機檔案的md5,最終實現檔案一致性校驗。
[root@kevin ~]# vim fabric4.py
#!/usr/bin/env python

from fabric.api import *
from fabric.context_managers import *
from fabric.contrib.console import confirm

env.user = 'root'
env.hosts = ['192.168.56.12','192.168.56.13']
env.passwords = {
    'root@192.168.56.12:22':'1234567',
    'root@192.168.56.13:22':'1234567',
}


@runs_once
def tar_task():             #本地打包任務函式,只執行一次
    with lcd('/home/devops/devops'):
        local("tar -zcf devops.tar.gz  *")


@task
def put_task():                 #上傳檔案任務函式
    run("mkdir -p /root/devops")
    with cd("/root/devops"):
        with settings(warn_only=True):                  #put(上傳)出現異常時繼續執行,非終止
            result = put("/home/devops/devops/devops.tar.gz","/root/devops/devops.tar.gz")
        if result.failed and not confirm("put file failed.Continue[Y/N]?"):
            abort("Aborting file put task!")                        #出現異常時,確認使用者是否繼續,(Y繼續)



@task
def check_task():               #校驗檔案任務函式
    with settings(warn_only=True):
        #本地local命令需要配置capture=True才能捕獲返回值
        lmd5 = local("md5sum /home/devops/devops/devops.tar.gz",capture=True).split(' ')[0]
        rmd5 = run("md5sum /root/devops/devops.tar.gz").split(' ')[0]
    if lmd5 == rmd5:                #對比本地及遠端檔案md5資訊
        prompt("OK")
    else:
        prompt("ERROR")


@task
def go():
    tar_task()
    put_task()
    check_task()


執行命令, 執行結果如下:(提示此程式不支援-P引數並行執行、如需並行執行,程式需要做調整). 如果只打包, 則"fab -f fabric4.py tar_task", 如果只上傳, 則"fab -f fabric4.py put_task"
[root@kevin ~]# fab -f fabric4.py go
[192.168.56.12] Executing task 'go'
[localhost] local: tar -zcf devops.tar.gz  *
[192.168.56.12] run: mkdir -p /root/devops
[192.168.56.12] put: /home/devops/devops/devops.tar.gz -> /root/devops/devops.tar.gz
[localhost] local: md5sum /home/devops/devops/devops.tar.gz
[192.168.56.12] run: md5sum /root/devops/devops.tar.gz
[192.168.56.12] out: a1cf2be82647cbed0d41514bd80373de  /root/devops/devops.tar.gz
[192.168.56.12] out:

OK
[192.168.56.13] Executing task 'go'
[192.168.56.13] run: mkdir -p /root/devops
[192.168.56.13] put: /home/devops/devops/devops.tar.gz -> /root/devops/devops.tar.gz
[localhost] local: md5sum /home/devops/devops/devops.tar.gz
[192.168.56.13] run: md5sum /root/devops/devops.tar.gz
[192.168.56.13] out: a1cf2be82647cbed0d41514bd80373de  /root/devops/devops.tar.gz
[192.168.56.13] out:

OK

Done.
Disconnecting from 192.168.56.12... done.
Disconnecting from 192.168.56.13... done.

示例五: 部署LNMP業務服務環境

本示例通過env.roledefs定義不同主機角色,在使用"@roles('webservers')"修復符繫結到對應的任務函式,實現不同角色主機的部署差異。
[root@kevin ~]# vim fabric5.py
#!/usr/bin/env python

from fabric.colors import *
from fabric.api import *

env.user = 'root'
env.roledefs = {
    'webservers':['192.168.56.11','192.168.56.12'],
    'dbservers':['192.168.56.13']
}

env.passwords = {
    'root@192.168.56.11:22':'1234567',
    'root@192.168.56.12:22':'1234567',
    'root@192.168.56.13:22':'1234567',
}

@roles('webservers')                      #使用webtask任務函式引用'webservers'角色修復符
def webtask():
    print(yellow('Install nginx php php-fpm...'))
    with settings(warn_only=True):
        run("yum -y install nginx")
        run("yum -y install php-fpm php-mysql php-mbstring php-xml php-mcrypt php-gd")
        run("chkconfig --levels 235 php-fpm on")
        run("chkconfig --levels 235 nginx on")


@roles('dbservers')                       #dbtask任務函式引用'dbservers'角色修復符
def dbtask():
    print(yellow("Install Mysql..."))
    with settings(warn_only=True):
        run("yum -y install mysql mysql-server")
        run("chkconfig --levels 235 mysqld on")


@roles('webservers','dbservers')           #publictask任務函式同時引用兩個角色修復符
def publictask():                          #部署公共類環境,如epel、ntp等
    print(yellow("Install epel ntp...."))
    with settings(warn_only=True):
        run("wget -O /etc/yum.repos.d/epel.repo http://mirrors.aliyun.com/repo/epel-7.repo")
        run("yum -y install ntp")

def deploy():
    execute(publictask)
    execute(webtask)
    execute(dbtask)


執行命令,結果如下:
[root@kevin ~]# fab -Pf fabric5.py deploy
[192.168.56.11] Executing task 'publictask'
[192.168.56.12] Executing task 'publictask'
[192.168.56.13] Executing task 'publictask'
Install epel ntp....
[192.168.56.13] run: wget -O /etc/yum.repos.d/epel.repo http://mirrors.aliyun.com/repo/epel-7.repo
Install epel ntp....
[192.168.56.12] run: wget -O /etc/yum.repos.d/epel.repo http://mirrors.aliyun.com/repo/epel-7.repo
Install epel ntp....
[192.168.56.11] run: wget -O /etc/yum.repos.d/epel.repo http://mirrors.aliyun.com/repo/epel-7.repo
[192.168.56.12] out: --2018-06-23 20:32:30--  http://mirrors.aliyun.com/repo/epel-7.repo
[192.168.56.11] out: --2018-06-23 20:32:30--  http://mirrors.aliyun.com/repo/epel-7.repo
[192.168.56.13] out: --2018-06-23 20:32:30--  http://mirrors.aliyun.com/repo/epel-7.repo
....
[192.168.56.13] run: yum -y install ntp
[192.168.56.12] run: yum -y install ntp
[192.168.56.11] run: yum -y install ntp
....
....
....
[192.168.56.11] Executing task 'webtask'
[192.168.56.12] Executing task 'webtask'
Install nginx php php-fpm...
[192.168.56.11] run: yum -y install nginx
Install nginx php php-fpm...
[192.168.56.12] run: yum -y install nginx
....
....
....
[192.168.56.13] Executing task 'dbtask'
Install Mysql...
[192.168.56.13] run: rpm -ivh http://dev.mysql.com/get/mysql-community-release-el6-5.noarch.rpm
.....
.....
.....
[192.168.56.13] run: chkconfig --levels 235 mysqld on

Done.

示例六:  分享一個生產環境程式碼包釋出管理的配置

程式生產環境的釋出是業務上線的最後一個環境,要求具備原始碼打包、釋出、切換、回滾、版本管理等功能。
本示例實現了這一套流程功能,其中版本切換與回滾使用了Linux下的軟連結實現。
[root@kevin ~]# vim fabric6.py
#!/usr/local/env python

from fabric.api import *
from fabric.colors import *
from fabric.context_managers import *
from fabric.contrib.console import confirm
import time

env.user = 'root'
env.host = ['192.168.56.12','192.168.56.13']
env.passwords = {
    'root@192.168.56.12:22':'1234567',
    'root@192.168.56.13:22':'1234567',
}

env.project_dev_source = '/data/dev/Lwebadmin/'              #開發伺服器專案主目錄
env.project_tar_source = '/data/dev/releases/'               #開發伺服器專案壓縮包儲存目錄
env.project_pack_name = 'release'                            #專案壓縮包字首,檔名為release.tar.gz

env.deploy_project_root = '/data/www/Lwebadmin/'            #專案生產環境主目錄
env.deploy_release_dir = 'releases'                         #專案釋出目錄,位於主目錄下面
env.deploy_current_dir = 'current'                          #對外服務的當前版本軟連結
env.deploy_version = time.strftime("%Y%m%d")+"v2"           #版本號

@runs_once
def input_versionid():                                      #獲得使用者輸入的版本號,以便做版本回滾操作
    return prompt("Please input project rollback version ID:",default="")


@task
@runs_once
def tar_source():                                           #打包本地專案主目錄,並將壓縮包儲存到本地壓縮包目錄
    prompt(yellow("Creating source package...."))
    with lcd(env.project_dev_source):
        local("tar -zcf %s.tar.gz ." %(env.project_tar_source + env.project_pack_name))
    prompt(green("Creating source package success!"))


@task
def put_package():                                          #上傳任務函式
    prompt(yellow("Start put package...."))
    with settings(warn_only=True):
        with cd(env.deploy_project_root + env.deploy_release_dir):
            run("mkdir %s" %(env.deploy_version))           #建立版本目錄
    env.deploy_full_path = env.deploy_project_root + env.deploy_release_dir + "/" + env.deploy_version
    with settings(warn_only=True):                          #上傳專案壓縮包至此目錄
        result = put(env.project_tar_source + env.project_pack_name + ".tar.gz",env.deploy_full_path)
    if result.failed and not ("put file failed,Continue[Y/N]?"):
        abort("Aborting file put task!")

    with cd(env.deploy_full_path):                          #成功解壓後刪除壓縮包
        run("tar -zxvf %s.tar.gz" %(env.project_pack_name))
        run("rm -rf %s.tar.gz" %(env.project_pack_name))

    print(green("Put & untar package success!"))


@task
def make_symlink():                                         #為當前版本目錄做軟連結
    print(yellow("update current symlink"))
    env.deploy_full_path = env.deploy_project_root + env.deploy_release_dir + "/" + env.deploy_version
    with settings(warn_only=True):                           #刪除軟連結,重新建立並指定軟連結源目錄,新版本生效
        run("rm -rf %s" %(env.deploy_project_root + env.deploy_current_dir))
        run("ln -s %s %s" %(env.deploy_full_path,env.deploy_project_root + env.deploy_current_dir))
    print(green("make symlink success!"))


@task
def rollback():                                             #版本回滾任務函式
    print(yellow("rollback project version"))
    versionid = input_versionid()                           #獲取使用者輸入的回滾版本號
    if versionid == '':
        abort("Project version ID error,abort!")

    env.deploy_full_path = env.deploy_project_root + env.deploy_release_dir + "/" + versionid
    run("rm -r %s" %(env.deploy_project_root + env.deploy_current_dir))
    run("ln -s %s %s" %(env.deploy_full_path,env.deploy_project_root + env.deploy_current_dir))     #刪除軟連結,重新建立並指定軟連結源目錄,新版本生效
    print(green("rollback sucess!"))


@task
def go():               #自動化程式版本釋出入口函式
    tar_source()
    put_package()
    make_symlink()

# 需要注意: 在生產環境中將站點的根目錄指向"/data/www/Lwebadmin/current",由於使用Linux軟連結做切換,管理員的版本釋出、回滾操作使用者無感知。
[root@kevin ~]# fab -f fabric6.py go 

示例七: 分享一個自動化部署 Django 專案的配置

[root@kevin ~]# vim fabric7.py
# -*- coding: utf-8 -*-
# 檔名要儲存為 fabfile.py
 
from __future__ import unicode_literals
from fabric.api import *
 
# 登入使用者和主機名:
env.user = 'root'
# 如果沒有設定,在需要登入的時候,fabric 會提示輸入
env.password = 'youpassword'
# 如果有多個主機,fabric會自動依次部署
env.hosts = ['www.example.com']
 
TAR_FILE_NAME = 'deploy.tar.gz'
 
def pack():
            """
            定義一個pack任務, 打一個tar包
            :return:
            """
            tar_files = ['*.py', 'static/*', 'templates/*', 'vue_app/', '*/*.py', 'requirements.txt']
            exclude_files = ['fabfile.py', 'deploy/*', '*.tar.gz', '.DS_Store', '*/.DS_Store',
                                                             '*/.*.py', '__pycache__/*']
            exclude_files = ['--exclude=\'%s\'' % t for t in exclude_files]
            local('rm -f %s' % TAR_FILE_NAME)
            
            local('tar -czvf %s %s %s' % (TAR_FILE_NAME, ' '.join(exclude_files), ' '.join(tar_files)))
            print('在當前目錄建立一個打包檔案: %s' % TAR_FILE_NAME)
 
 
def deploy():
            """
            定義一個部署任務
            :return:
            """
            # 先進行打包
            pack()
 
            # 遠端伺服器的臨時檔案
            remote_tmp_tar = '/tmp/%s' % TAR_FILE_NAME
            run('rm -f %s' % remote_tmp_tar)
            # 上傳tar檔案至遠端伺服器, local_path, remote_path
            put(TAR_FILE_NAME, remote_tmp_tar)
            # 解壓
            remote_dist_base_dir = '/home/python/django_app'
            # 如果不存在, 則建立資料夾
            run('mkdir -p %s' % remote_dist_dir)
 
 # cd 命令將遠端主機的工作目錄切換到指定目錄 
            with cd(remote_dist_dir):
                        print('解壓檔案到到目錄: %s' % remote_dist_dir)
                        run('tar -xzvf %s' % remote_tmp_tar)
                        print('安裝 requirements.txt 中的依賴包')
                        # 我使用的是 python3 來開發
                        run('pip3 install -r requirements.txt')
                        remote_settings_file = '%s/django_app/settings.py' % remote_dist_dir
                        settings_file = 'deploy/settings.py' % name
                        print('上傳 settings.py 檔案 %s' % settings_file)
                        put(settings_file, remote_settings_file)
 
                        nginx_file = 'deploy/django_app.conf'
                        remote_nginx_file = '/etc/nginx/conf.d/django_app.conf'
                        print('上傳 nginx 配置檔案 %s' % nginx_file)
                        put(nginx_file, remote_nginx_file)
            
 # 在當前目錄的子目錄 deploy 中的 supervisor 配置檔案上傳至伺服器
            supervisor_file = 'deploy/django_app.ini'
            remote_supervisor_file = '/etc/supervisord.d/django_app.ini'
            print('上傳 supervisor 配置檔案 %s' % supervisor_file)
            put(supervisor_file, remote_supervisor_file)
            
 # 重新載入 nginx 的配置檔案
            run('nginx -s reload')
            run('nginx -t')
            # 刪除本地的打包檔案
            local('rm -f %s' % TAR_FILE_NAME)
            # 載入最新的配置檔案,停止原有程式並按新的配置啟動所有程式
            run('supervisorctl reload')
            # 執行 restart all,start 或者 stop fabric 都會提示錯誤,然後中止執行
            # 但是伺服器上檢視日誌,supervisor 有重啟
            # run('supervisorctl restart all')


執行 pack 任務
[root@kevin ~]# fab -f fabric7.py pack

執行 deploy 任務
[root@kevin ~]# fab -f fabric7.py deploy

示例八:  程式碼的自動化部署

[root@kevin ~]# vim fabric8.py
#coding=utf-8
from fabric.api import local, abort, settings, env, cd, run
from fabric.colors import *
from fabric.contrib.console import confirm
 
env.hosts = ["root@115.28.×××××"]
env.password = "×××××"
 
 
def get_git_status():
  git_status_result = local("git status", capture=True)
  if "無檔案要提交,乾淨的工作區" not in git_status_result:
    print red("****當前分支還有檔案沒有提交")
    print git_status_result
    abort("****已經終止")
 
 
def local_unit_test():
  with settings(warn_only=True):
    test_result = local("python manage.py test")
    if test_result.failed:
      print test_result
      if not confirm(red("****單元測試失敗,是否繼續?")):
        abort("****已經終止")
 
 
def server_unit_test():
  with settings(warn_only=True):
    test_result = run("python manage.py test")
    if test_result.failed:
      print test_result
      if not confirm(red("****單元測試失敗,是否繼續?")):
        abort("****已經終止")
 
 
def upload_code():
  local("git push origin dev")
  print green("****程式碼上傳成功")
 
 
def deploy_at_server():
  print green("****ssh到伺服器進行下列操作")
  with cd("/var/www/××××××"):
    #print run("pwd")
    print green("****將在遠端倉庫下載程式碼")
    run("git checkout dev")
    get_git_status()
    run("git pull origin dev")
    print green("****將在伺服器上執行單元測試")
    server_unit_test()
    run("service apache2 restart", pty=False)
    print green("****重啟apache2成功")
    print green("********程式碼部署成功********")
 
 
def deploy():
  get_git_status()
  local("git checkout dev", capture=False)
  print green("****切換到dev分支")
  get_git_status()
  print green("****將開始執行單元測試")
  local_unit_test()
  print green("****單元測試完成,開始上傳程式碼")
  upload_code()
  deploy_at_server()


fabric可以將自動化部署或者多機操作的命令固化到一個指令碼里,從而減少手動的操作。上面是今天第一次接觸這東西后寫的,確實很實用。
執行
[root@kevin ~]# fab -ff abric8.py deploy

主要邏輯就是將本地的dev分支跑單元測試,然後提交到伺服器,ssh登陸到伺服器,然後pull下來,再跑單元測試,然後重啟apache2。
這個寫的還是比較簡單的。

===============這裡貼出之前線上環境使用過的一個Fabric自動化配置===============

1) 通過Fabric配置的自動化python上線指令碼(包括回滾指令碼):
[work@qd-op-zhongkong op]$ cat xcspam-celery.py
from fabric.api import *
from fabric.context_managers import *
import datetime


env.hosts=['qd-vpc-op-rule01']

def antiwater():
    with cd('/app/release'):
        date=datetime.datetime.now().strftime("%Y%m%d%H%M%S")
        repo='http://git.kevinweb.com/zuiyou_server/xcspam.git';
        run('git clone --depth=1 %s' % repo)
        newNmae="xcspam"+"-"+date
        run('mv xcspam %s ' % newNmae)

    with cd('/app/web/xcspam'):
        newRelease=run('ls /app/release/ |tail -1f')
        run('cp -rp /app/release/%s /app/web/xcspam/ ' % newRelease)
        run('unlink bin')
        run('ln -sn %s bin' % newRelease)

    run('superctl restart antiwater:*')

def rollantiwater():
    with cd('/app/web/xcspam'):
        lastrelease=run('ls -rtd xcspam* |tail -2 |head -1')
        run('unlink bin')
        run('ln -sn %s bin' % lastrelease)

    run('superctl restart antiwater:*')

def report():
    with cd('/app/release'):
        date=datetime.datetime.now().strftime("%Y%m%d%H%M%S")
        repo='http://git.kevinweb.com/zuiyou_server/xcspam.git';
        run('git clone --depth=1 %s' % repo)
        newNmae="xcspam"+"-"+date
        run('mv xcspam %s ' % newNmae)

    with cd('/app/web/xcspam'):
        newRelease=run('ls /app/release/ |tail -1f')
        run('cp -rp /app/release/%s /app/web/xcspam/ ' % newRelease)
        run('unlink bin')
        run('ln -sn %s bin' % newRelease)

    run('superctl restart report')

def rollreport():
    with cd('/app/web/xcspam'):
        lastrelease=run('ls -rtd xcspam* |tail -2 |head -1')
        run('unlink bin')
        run('ln -sn %s bin' % lastrelease)

    run('superctl restart report')

def chat():
    with cd('/app/release'):
        date=datetime.datetime.now().strftime("%Y%m%d%H%M%S")
        repo='http://git.kevinweb.com/zuiyou_server/xcspam.git';
        run('git clone --depth=1 %s' % repo)
        newNmae="xcspam"+"-"+date
        run('mv xcspam %s ' % newNmae)

    with cd('/app/web/xcspam'):
        newRelease=run('ls /app/release/ |tail -1f')
        run('cp -rp /app/release/%s /app/web/xcspam/ ' % newRelease)
        run('unlink bin')
        run('ln -sn %s bin' % newRelease)

    run('superctl restart chat')

def rollchat():
    with cd('/app/web/xcspam'):
        lastrelease=run('ls -rtd xcspam* |tail -2 |head -1')
        run('unlink bin')
        run('ln -sn %s bin' % lastrelease)

    run('superctl restart chat')


可以在一個指令碼中定義多個上線專案,上線的時候可以選擇,如下(回滾的時候選擇對應的roll即可):
[work@qd-op-zhongkong op]$ fab -f xcspam-celery.py antiwater
[work@qd-op-zhongkong op]$ fab -f xcspam-celery.py report
[work@qd-op-zhongkong op]$ fab -f xcspam-celery.py chat


2) 指令碼2,其實跟上面無異:
[work@qd-op-zhongkong op]$ cat xcspam-consumer.py
from fabric.api import *
from fabric.context_managers import *
import datetime


env.hosts=['qd-vpc-op-consumer01','qd-vpc-op-consumer02']

def xcspam():
    with cd('/app/release'):
        date=datetime.datetime.now().strftime("%Y%m%d%H%M%S")
        repo='http://git.kevinweb.com/zuiyou_server/xcspam.git';
        run('git clone --depth=1 %s' % repo)
        newNmae="xcspam"+"-"+date
        run('mv xcspam %s ' % newNmae)

    with cd('/app/web/xcspam'):
        newRelease=run('ls /app/release/ |tail -1f')
        run('cp -rp /app/release/%s /app/web/xcspam/ ' % newRelease)
        run('unlink bin')
        run('ln -sn %s bin' % newRelease)

    run('superctl restart xcspam:*')

def rollxcspam():
    with cd('/app/web/xcspam'):
        lastrelease=run('ls -rtd xcspam* |tail -2 |head -1')
        run('unlink bin')
        run('ln -sn %s bin' % lastrelease)

    run('superctl restart xcspam:*')

def chatxcspam():
    with cd('/app/release'):
        date=datetime.datetime.now().strftime("%Y%m%d%H%M%S")
        repo='http://git.kevinweb.com/zuiyou_server/xcspam.git';
        run('git clone --depth=1 %s' % repo)
        newNmae="xcspam"+"-"+date
        run('mv xcspam %s ' % newNmae)

    with cd('/app/web/xcspam'):
        newRelease=run('ls /app/release/ |tail -1f')
        run('cp -rp /app/release/%s /app/web/xcspam/ ' % newRelease)
        run('unlink bin')
        run('ln -sn %s bin' % newRelease)

    run('superctl restart chatxcspam:*')

def chatxcspam():
    with cd('/app/web/xcspam'):
        lastrelease=run('ls -rtd xcspam* |tail -2 |head -1')
        run('unlink bin')
        run('ln -sn %s bin' % lastrelease)

    run('superctl restart chatxcspam:*’)

3) 指令碼3
[work@qd-op-zhongkong op]$ cat xcspam-consumer-all.py
from fabric.api import *
from fabric.context_managers import *
import datetime


env.hosts=['qd-vpc-op-consumer01','qd-vpc-op-consumer02']

def xcspam():
    with cd('/app/release'):
        date=datetime.datetime.now().strftime("%Y%m%d%H%M%S")
        repo='http://git.kevinweb.com/zuiyou_server/xcspam.git';
        run('git clone --depth=1 %s' % repo)
        newNmae="xcspam"+"-"+date
        run('mv xcspam %s ' % newNmae)

    with cd('/app/web/xcspam'):
        newRelease=run('ls /app/release/ |tail -1f')
        run('cp -rp /app/release/%s /app/web/xcspam/ ' % newRelease)
        run('unlink bin')
        run('ln -sn %s bin' % newRelease)

    run('superctl restart xcspam:*')

def rollxcspam():
    with cd('/app/web/xcspam'):
        lastrelease=run('ls -rtd xcspam* |tail -2 |head -1')
        run('unlink bin')
        run('ln -sn %s bin' % lastrelease)

    run('superctl restart xcspam:*')

def chatxcspam():
    with cd('/app/release'):
        date=datetime.datetime.now().strftime("%Y%m%d%H%M%S")
        repo='http://git.kevinweb.com/zuiyou_server/xcspam.git';
        run('git clone --depth=1 %s' % repo)
        newNmae="xcspam"+"-"+date
        run('mv xcspam %s ' % newNmae)

    with cd('/app/web/xcspam'):
        newRelease=run('ls /app/release/ |tail -1f')
        run('cp -rp /app/release/%s /app/web/xcspam/ ' % newRelease)
        run('unlink bin')
        run('ln -sn %s bin' % newRelease)

    run('superctl restart chatxcspam:*')

def chatxcspam():
    with cd('/app/web/xcspam'):
        lastrelease=run('ls -rtd xcspam* |tail -2 |head -1')
        run('unlink bin')
        run('ln -sn %s bin' % lastrelease)

    run('superctl restart chatxcspam:*')


def all():
    with cd('/app/release'):
        date=datetime.datetime.now().strftime("%Y%m%d%H%M%S")
        repo='http://git.kevinweb.com/zuiyou_server/xcspam.git';
        run('git clone --depth=1 %s' % repo)
        newNmae="xcspam"+"-"+date
        run('mv xcspam %s ' % newNmae)

    with cd('/app/web/xcspam'):
        newRelease=run('ls /app/release/ |tail -1f')
        run('cp -rp /app/release/%s /app/web/xcspam/ ' % newRelease)
        run('unlink bin')
        run('ln -sn %s bin' % newRelease)

    run('superctl restart all')

def rollall():
    with cd('/app/web/xcspam'):
        lastrelease=run('ls -rtd xcspam* |tail -2 |head -1')
        run('unlink bin')
        run('ln -sn %s bin' % lastrelease)

    run('superctl restart all')


可以根據需求去選擇具體對那個專案進行上線,上述指令碼定義了兩個專案上線,第三個(all)即表示同時上線兩個專案。
[work@qd-op-zhongkong op]$ fab -f xcspam-consumer-all.py xcspam
[work@qd-op-zhongkong op]$ fab -f xcspam-consumer-all.py chatxcspam
[work@qd-op-zhongkong op]$ fab -f xcspam-consumer-all.py all

相關文章