一、軟件安裝
簡介:Fabric是基於Python實現的SSH命令行工具,簡化了SSH的應用程序部署及系統管理任務,它提供了系統基礎的操作組件,可以實現本地或遠程shell命令,包括:命令執行、文件上傳、下載及完整執行日誌輸出等功能。Fabric在Paramiko的基礎上做了更高一層的封裝,操作起來會更加簡單。
1、系統環境
[root@ansible ~]# cat /etc/redhat-release
CentOS release 6.10 (Final)
[root@ansible ~]# uname -r
2.6.32-504.el6.x86_64
2、升級python到python2.7版本
1、提前安裝依賴包
yum install zlib zlib-devel gcc -y
2、先把python升級到python2.7
下載:
wget https://www.python.org/ftp/python/2.7.12/Python-2.7.12.tgz
#編譯安裝:
tar zxf Python-2.7.12.tgz
cd Python-2.7.12
./configure
make
make install
#修改python默認版本
mv /usr/bin/python /usr/bin/python2.6
#做個軟鏈接
ln -s /usr/local/bin/python2.7 /usr/bin/python
#爲防止yum執行異常,修改yum使用的python版本
vi /usr/bin/yum
將文件頭#!/usr/bin/python修改爲#!/usr/bin/python2.6
3、安裝模塊
1、setuptools(easy_install)模塊
下載地址:(打開網站下載)
https://pypi.python.org/pypi/setuptools/27.1.2
軟件包:setuptools-27.1.2.tar.gz
編譯安裝:
tar zxf setuptools-27.1.2.tar.gz
cd setuptools-27.1.2
python setup.py install
2、pip模塊
easy_install pip
3、安裝pycrypto
https://ftp.dlitz.net/pub/dlitz/crypto/pycrypto/pycrypto-2.6.1.tar.gz
#解壓
tar -xzvf pycrypto-2.6.tar.gz
cd pycrypto-2.6
python setup.py install
4、通過pip安裝常用模塊
[root@paramiko-server tools]# pip install paramiko
4、安裝Fabric
pip install Fabric
驗證是否安裝成功,出現如下圖所示,說明fabric模塊安裝成功:
[root@template tools]# python
Python 2.7.12 (default, Apr 27 2017, 00:01:51)
[GCC 4.4.7 20120313 (Red Hat 4.4.7-18)] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> from fabric.api import run
>>> exit()
二、使用Fabric模塊實現自動化運維
語法:
fab -p 123456 -H localhost -- 'uname -s' #--後面要加空格,主機密碼:123456
示例1:
編寫一個fabfile.py文件
vim fabfile.py
#!/usr/bin/env python
# -*- coding:utf-8 -*-
from fabric.api import run
#定義一個任務函數,通過run方法實現遠程執行‘uname -s’命令
def host_type():
run('uname -s')
執行結果跟下面一樣:
#直接在linux中執行命令。
[root@Fabric ~]# pwd
/root
[root@ansible ~]# fab -p 123456 -H localhost -- 'uname -s'
[localhost] Executing task '<remainder>'
[localhost] run: uname -s
[localhost] out: Linux
[localhost] out:
Done.
Disconnecting from localhost... done.
示例2:
[root@ansible ~]# fab -p 123wen -H localhost -- 'ifconfig'
[localhost] Executing task '<remainder>'
[localhost] run: ifconfig
[localhost] out: eth0 Link encap:Ethernet HWaddr 00:0C:29:A1:04:41
[localhost] out: inet addr:192.168.30.128 Bcast:192.168.30.255 Mask:255.255.255.0
[localhost] out: inet6 addr: fe80::20c:29ff:fea1:441/64 Scope:Link
[localhost] out: UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1
[localhost] out: RX packets:6079 errors:0 dropped:0 overruns:0 frame:0
[localhost] out: TX packets:3985 errors:0 dropped:0 overruns:0 carrier:0
[localhost] out: collisions:0 txqueuelen:1000
[localhost] out: RX bytes:563557 (550.3 KiB) TX bytes:449284 (438.7 KiB)
[localhost] out:
[localhost] out: lo Link encap:Local Loopback
[localhost] out: inet addr:127.0.0.1 Mask:255.0.0.0
[localhost] out: inet6 addr: ::1/128 Scope:Host
[localhost] out: UP LOOPBACK RUNNING MTU:65536 Metric:1
[localhost] out: RX packets:318 errors:0 dropped:0 overruns:0 frame:0
[localhost] out: TX packets:318 errors:0 dropped:0 overruns:0 carrier:0
[localhost] out: collisions:0 txqueuelen:0
[localhost] out: RX bytes:54589 (53.3 KiB) TX bytes:54589 (53.3 KiB)
[localhost] out:
[localhost] out:
Done.
Disconnecting from localhost... done.
fabfile文件的編寫
fab命令是結合fabfile.py文件(其他文件通過-f filename 參數來引用)來搭配使用的。fab的部分命令行參數還能通過相應的方法來代替。
如:
fab -H 192.168.1.21,192.168.1.22 ... ...
#可以在fabfile.py文件中用env.hosts來實現,命令行中就可以不用寫了
#可以在fabfile中這麼寫:
env.hosts = ['192.168.1.21','192.168.1.22']
三、fabfile之env對象
env對象的作用是定義fabfile的全局設定,就像上面的舉例。下面對各屬性進行說明:
env.hosts #定義目標主機,可以用IP或主機名錶示,以python的列表形式定義。如env.hosts=['192.168.1.21','192.168.1.22']
env.exclude_hosts #排除指定主機,如env.exclude_hosts=['192.168.1.21']
env.user #定義用戶名,如env.user='root'
env.port #定義端口,默認爲22,如env.port='22'
env.password #定義密碼,如env.password='123456'
env.passwords #定義多個密碼,不同主機對應不同密碼,如:env.passwords = {'[email protected]:22':'123456','[email protected]:22':'654321'}
env.gateway #定義網關(中轉、堡壘機)IP,如env.gateway='192.168.1.23
env.roledefs #定義角色分組,比如web組合db組主機區分開來:env.roledefs = {'webserver':['192.168.1.21','192.168.1.22'],'dbserver':['192.168.1.25','192.168.1.26']}
env.deploy_release_dir #自定義全局變量,格式:env. + '變量名稱',如env.age,env.sex等
env.roledefs的使用方法實例:
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')
四、Fabric常用API
在上面的簡單實例中使用了api函數run,下面再列舉幾個常用的api
local #執行本地命令,如local('uname -s')
lcd #切換本地目錄,如lcd('/home')
cd #切換遠程目錄
run #執行遠程命令
sudo #sudo方式執行遠程命令,如sudo('/etc/init.d/httpd start')
put #上次本地文件導遠程主機,如put('/home/user.info','/data/user.info')
get #從遠程主機下載文件到本地,如:get('/data/user.info','/home/user.info')
prompt #獲得用戶輸入信息,如:prompt('please input user password:')
confirm #獲得提示信息確認,如:confirm('Test failed,Continue[Y/N]?')
reboot #重啓遠程主機,如:reboot()
@task #函數修飾符,標識的函數爲fab可調用的,非標記對fab不可見,純業務邏輯
@runs_once #函數修飾符,標識的函數只會執行一次,不受多臺主機影響
Fabric應用示例說明
示例調用local方法執行本地命令,添加@runs_once修飾符保證任務函數只執行一次,調用run方法執行遠程命令
#!/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')
simple1.py
執行:
fab -f simple1.py local_task
fab -f simple1.py remote_task
五、應用實例
實例一:
1.查看本地信息
本示例調用local()方法執行本地(主控端)命令,添加"@runs_once"修飾符保證該任務函數只執行一次。調用run()方法執行遠程命令。
[devops@ansible ]$ pwd
/home/devops/devops
[devops@ansible ]$ cat simple1.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任務函數運行結果如下:
[devops@ansible ]$ fab -f simple1.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.
2.查看遠程主機信息
[devops@ansible ]$ cat simple2.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任務函數運行結果如下:
[devops@ansible ]$ fab -f simple2.py remote_task
[192.168.33.11] Executing task 'remote_task'
[192.168.33.11] run: ls -l
[192.168.33.11] out: total 4
[192.168.33.11] out: -rw-------. 1 root root 1273 Feb 20 20:21 anaconda-ks.cfg
[192.168.33.11] out:
Done.
Disconnecting from 192.168.33.11... done.
示例二:動態獲取遠程目錄列表
本示例使用"@task’修復符標誌入口函數go()對外部可見,配合"@runs_once"修飾符接受用戶輸入,最後調用worktask()任務函數實現遠程命令執行。
[devops@ansible ]$ cat simple3.py
#!/usr/bin/env python
from fabric.api import *
env.user = 'root'
env.hosts = ['192.168.33.11','192.168.33.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修復符來達到此目的。
執行結果如下:
[devops@ansible ]$ fab -f simple3.py go
[192.168.33.11] Executing task 'go'
Please input directory name: [/home] /root
[192.168.33.11] run: ls -l /root
[192.168.33.11] out: total 4
[192.168.33.11] out: -rw-------. 1 root root 1273 Feb 16 11:47 anaconda-ks.cfg
[192.168.33.11] out:
[192.168.33.12] Executing task 'go'
[192.168.33.12] run: ls -l /root
[192.168.33.12] out: total 4
[192.168.33.12] out: -rw-------. 1 root root 1273 Feb 16 11:59 anaconda-ks.cfg
[192.168.33.12] out:
Done.
Disconnecting from 192.168.33.11... done.
Disconnecting from 192.168.33.12... done.
示例三:網關模式文件上傳與執行
本示例通過Fabric的env對象定義網關模式,即俗稱的中轉、堡壘機環境。定義格式爲"env.gateway=‘192.168.33.11’",其中IP“192.168.33.11”爲堡壘機IP,再結合任務函數實現目標主機文件上傳與執行的操作。
[devops@ansible ]$ cat /home/devops/devops/simple4.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.33.11' #定義堡壘機IP,作爲文件上傳、執行的中轉設備
env.hosts = ['192.168.33.12','192.168.33.13']
env.passwords = {
'[email protected]:22':'1234567', #堡壘機賬號信息
'[email protected]:22':'1234567',
'[email protected]: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()
執行命令fab -f simple4.py go。運行結果如下:
[devops@ansible ]$ fab -f simple4.py go
[192.168.33.12] Executing task 'go'
[192.168.33.12] run: mkdir -p /tmp/install
[192.168.33.12] put: /home/install/nginx-1.6.3.tar.gz -> /tmp/install/nginx-1.6.3.tar.gz
[192.168.33.12] run: tar -xvf nginx-1.6.3.tar.gz
.....
.....
.....
[192.168.33.12] out: cp conf/nginx.conf '/usr/local/nginx/conf/nginx.conf.default'
[192.168.33.12] out: test -d '/usr/local/nginx/logs' || mkdir -p '/usr/local/nginx/logs'
[192.168.33.12] out: test -d '/usr/local/nginx/logs' || mkdir -p '/usr/local/nginx/logs'
[192.168.33.12] out: test -d '/usr/local/nginx/html' || cp -R html '/usr/local/nginx'
[192.168.33.12] out: test -d '/usr/local/nginx/logs' || mkdir -p '/usr/local/nginx/logs'
[192.168.33.12] out: make[1]: Leaving directory `/tmp/install/nginx-1.6.3'
[192.168.33.12] out: nginx: the configuration file /usr/local/nginx/conf/nginx.conf syntax is ok
[192.168.33.12] out: nginx: configuration file /usr/local/nginx/conf/nginx.conf test is successful
[192.168.33.12] out:
[192.168.33.13] Executing task 'go'
[192.168.33.13] run: mkdir -p /tmp/install
[192.168.33.13] put: /home/install/nginx-1.6.3.tar.gz -> /tmp/install/nginx-1.6.3.tar.gz
[192.168.33.13] run: tar -xvf nginx-1.6.3.tar.gz
....
....
....
[192.168.33.13] out: cp conf/nginx.conf '/usr/local/nginx/conf/nginx.conf.default'
[192.168.33.13] out: test -d '/usr/local/nginx/logs' || mkdir -p '/usr/local/nginx/logs'
[192.168.33.13] out: test -d '/usr/local/nginx/logs' || mkdir -p '/usr/local/nginx/logs'
[192.168.33.13] out: test -d '/usr/local/nginx/html' || cp -R html '/usr/local/nginx'
[192.168.33.13] out: test -d '/usr/local/nginx/logs' || mkdir -p '/usr/local/nginx/logs'
[192.168.33.13] out: make[1]: Leaving directory `/tmp/install/nginx-1.6.3'
[192.168.33.13] out: nginx: the configuration file /usr/local/nginx/conf/nginx.conf syntax is ok
[192.168.33.13] out: nginx: configuration file /usr/local/nginx/conf/nginx.conf test is successful
[192.168.33.13] out:
Done.
Disconnecting from 192.168.33.11... done.
Disconnecting from 192.168.33.12... done.
Disconnecting from 192.168.33.13... done.
運行結果,默認爲串行運行
執行命令fab -Pf simple4.py go。運行結果如下:
[devops@ansible ]$ fab -Pf simple4.py go
[192.168.33.12] Executing task 'go'
[192.168.33.13] Executing task 'go'
[192.168.33.12] run: mkdir -p /tmp/install
[192.168.33.13] run: mkdir -p /tmp/install
[192.168.33.12] put: /home/install/nginx-1.6.3.tar.gz -> /tmp/install/nginx-1.6.3.tar.gz
[192.168.33.13] put: /home/install/nginx-1.6.3.tar.gz -> /tmp/install/nginx-1.6.3.tar.gz
[192.168.33.12] run: tar -xvf nginx-1.6.3.tar.gz
....
....
....
[192.168.33.12] out: nginx-1.6.3/html/index.html
[192.168.33.12] out: nginx-1.6.3/README
[192.168.33.12] out: nginx-1.6.3/nginx_install.sh
[192.168.33.12] out: nginx-1.6.3/configure
[192.168.33.12] out:
[192.168.33.12] run: ./nginx_install.sh
[192.168.33.13] run: tar -xvf nginx-1.6.3.tar.gz
[192.168.33.13] out: nginx-1.6.3/
[192.168.33.13] out: nginx-1.6.3/src/
....
....
....
[192.168.33.12] out: make[1]: Leaving directory `/tmp/install/nginx-1.6.3'
[192.168.33.12] out: nginx: the configuration file /usr/local/nginx/conf/nginx.conf syntax is ok
[192.168.33.12] out: nginx: configuration file /usr/local/nginx/conf/nginx.conf test is successful
[192.168.33.12] out:
....
....
...
[192.168.33.13] out: make[1]: Leaving directory `/tmp/install/nginx-1.6.3'
[192.168.33.13] out: nginx: the configuration file /usr/local/nginx/conf/nginx.conf syntax is ok
[192.168.33.13] out: nginx: configuration file /usr/local/nginx/conf/nginx.conf test is successful
[192.168.33.13] out:
加P參數該爲異步並行執行,運行結果
示例四:文件打包、上傳與校驗
我們時常做一些文件包分發的工作,實施步驟一般是先壓縮打包,在批量上傳至目標服務器,最後做一致性校驗。本示例通過put()方法實現文件的上傳,通過對比本地與遠程主機文件的md5,最終實現文件一致性校驗。
[devops@ansible ]$ cat simple5.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.33.12','192.168.33.13']
env.passwords = {
'[email protected]:22':'1234567',
'[email protected]: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()
執行命令fab -f simple5.py go。運行結果如下:(此程序不支持-P參數並行執行、如需並行執行,程序需要做調整)
[devops@ansible ]$ fab -f simple5.py go
[192.168.33.12] Executing task 'go'
[localhost] local: tar -zcf devops.tar.gz *
[192.168.33.12] run: mkdir -p /root/devops
[192.168.33.12] put: /home/devops/devops/devops.tar.gz -> /root/devops/devops.tar.gz
[localhost] local: md5sum /home/devops/devops/devops.tar.gz
[192.168.33.12] run: md5sum /root/devops/devops.tar.gz
[192.168.33.12] out: a1cf2be82647cbed0d41514bd80373de /root/devops/devops.tar.gz
[192.168.33.12] out:
OK
[192.168.33.13] Executing task 'go'
[192.168.33.13] run: mkdir -p /root/devops
[192.168.33.13] put: /home/devops/devops/devops.tar.gz -> /root/devops/devops.tar.gz
[localhost] local: md5sum /home/devops/devops/devops.tar.gz
[192.168.33.13] run: md5sum /root/devops/devops.tar.gz
[192.168.33.13] out: a1cf2be82647cbed0d41514bd80373de /root/devops/devops.tar.gz
[192.168.33.13] out:
OK
Done.
Disconnecting from 192.168.33.12... done.
Disconnecting from 192.168.33.13... done.
示例五:部署LNMP業務服務環境
本示例通過env.roledefs定義不同主機角色,在使用"@roles(‘webservers’)"修復符綁定到對應的任務函數,實現不同角色主機的部署差異。
[devops@ansible ]$ cat simple6.py
#!/usr/bin/env python
from fabric.colors import *
from fabric.api import *
env.user = 'root'
env.roledefs = {
'webservers':['192.168.33.11','192.168.33.12'],
'dbservers':['192.168.33.13']
}
env.passwords = {
'[email protected]:22':'1234567',
'[email protected]:22':'1234567',
'[email protected]: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)
執行命令fab -Pf simple6.py deploy
[devops@ansible ]$ fab -Pf simple6.py deploy
[192.168.33.11] Executing task 'publictask'
[192.168.33.12] Executing task 'publictask'
[192.168.33.13] Executing task 'publictask'
Install epel ntp....
[192.168.33.13] run: wget -O /etc/yum.repos.d/epel.repo http://mirrors.aliyun.com/repo/epel-7.repo
Install epel ntp....
[192.168.33.12] run: wget -O /etc/yum.repos.d/epel.repo http://mirrors.aliyun.com/repo/epel-7.repo
Install epel ntp....
[192.168.33.11] run: wget -O /etc/yum.repos.d/epel.repo http://mirrors.aliyun.com/repo/epel-7.repo
[192.168.33.12] out: --2018-06-23 20:32:30-- http://mirrors.aliyun.com/repo/epel-7.repo
[192.168.33.11] out: --2018-06-23 20:32:30-- http://mirrors.aliyun.com/repo/epel-7.repo
[192.168.33.13] out: --2018-06-23 20:32:30-- http://mirrors.aliyun.com/repo/epel-7.repo
....
[192.168.33.13] run: yum -y install ntp
[192.168.33.12] run: yum -y install ntp
[192.168.33.11] run: yum -y install ntp
....
....
....
[192.168.33.11] Executing task 'webtask'
[192.168.33.12] Executing task 'webtask'
Install nginx php php-fpm...
[192.168.33.11] run: yum -y install nginx
Install nginx php php-fpm...
[192.168.33.12] run: yum -y install nginx
....
....
....
[192.168.33.13] Executing task 'dbtask'
Install Mysql...
[192.168.33.13] run: rpm -ivh http://dev.mysql.com/get/mysql-community-release-el6-5.noarch.rpm
.....
.....
.....
[192.168.33.13] run: chkconfig --levels 235 mysqld on
Done.
示例六:生產環境代碼包發佈管理
程序生產環境的發佈是業務上線的最後一個環境,要求具備源碼打包、發佈、切換、回滾、版本管理等功能。本示例實現了這一套流程功能,其中版本切換與回滾使用了Linux下的軟鏈接實現。
#!/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.33.12','192.168.33.13']
env.passwords = {
'[email protected]:22':'1234567',
'[email protected]: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()