Python自动化运维工具-Fabric部署及使用总结
?
使用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 pipDownloading https://files.pythonhosted.org/packages/c2/d7/90f34cb0d83a6c5631cf71dfe64cc1054598c843a92b400e55675cc2ac37/pip-18.1-py2.py3-none-any.whl (1.3MB)100% |████████████████████████████████| 1.3MB 56kB/s Collecting wheelDownloading 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命令格式
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.pyfrom 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.pyfrom 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 show3) 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 doworks4)?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 pythonfrom 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/LinuxDone.查看遠程主機信息 [root@kevin ~]# vim fabric1.2.py #!/usr/bin/env pythonfrom 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 pythonfrom 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 pythonfrom fabric.api import * from fabric.context_managers import * from fabric.contrib.console import confirmenv.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(): #執行遠程命令,安裝nginxwith 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 pythonfrom fabric.api import * from fabric.context_managers import * from fabric.contrib.console import confirmenv.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:OKDone. 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 pythonfrom 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 onDone.示例六:? 分享一個生產環境代碼包發布管理的配置
程序生產環境的發布是業務上線的最后一個環境,要求具備源碼打包、發布、切換、回滾、版本管理等功能。 本示例實現了這一套流程功能,其中版本切換與回滾使用了Linux下的軟鏈接實現。 [root@kevin ~]# vim fabric6.py #!/usr/local/env pythonfrom fabric.api import * from fabric.colors import * from fabric.context_managers import * from fabric.contrib.console import confirm import timeenv.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.gzenv.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_versionwith 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_versionwith 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 + "/" + versionidrun("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.pyfrom __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_NAMErun('rm -f %s' % remote_tmp_tar)# 上傳tar文件至遠程服務器, local_path, remote_pathput(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_dirsettings_file = 'deploy/settings.py' % nameprint('上傳 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 confirmenv.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_resultabort("****已經終止")def local_unit_test():with settings(warn_only=True):test_result = local("python manage.py test")if test_result.failed:print test_resultif 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_resultif 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 datetimeenv.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"+"-"+daterun('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"+"-"+daterun('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"+"-"+daterun('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 chat2) 腳本2,其實跟上面無異: [work@qd-op-zhongkong op]$ cat xcspam-consumer.py from fabric.api import * from fabric.context_managers import * import datetimeenv.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"+"-"+daterun('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"+"-"+daterun('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 datetimeenv.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"+"-"+daterun('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"+"-"+daterun('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"+"-"+daterun('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總結
以上是生活随笔為你收集整理的Python自动化运维工具-Fabric部署及使用总结的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Linux安装FTP服务-----vsf
- 下一篇: Jfinal碰到的问题记录