Fabric是基於Python(2.7,3.4+以上版本)實現的SSH命令行工具,簡化了SSH的應用程序部署及系統管理任務,它提供了系統基礎的操做組件,能夠實現本地或遠程shell命令,包括命令執行、文件上傳、下載及完整執行日誌輸出等功能。Fabric在paramiko的基礎上作了更高一層的封裝,操做起來會更加簡單。 Fabric官網地址爲:http://www.fabfile.org/。
Mac os及CentOS 安裝php
pip3 install fabric3
Ubuntu安裝html
devops@devops-virtual-machine:~$ python --version Python 3.6.5 devops@devops-virtual-machine:~$ pip3 install fabric3
詳細請參閱官方文檔python
參考官方資料:https://github.com/fabric/fabric/tree/1.13.1mysql
http://www.fabfile.org/changelog-v1.html?highlight=1.xnginx
https://github.com/mathiasertl/fabric/git
#!/usr/bin/env python from fabric.api import run
#定義一個任務函數,經過run方法實現遠程執行"uname -s"命令 def host_type(): run('uname -s')
運行結果github
devops@devops-virtual-machine:~/devops$ 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,若是使用非默認文件名稱,則須要經過"-f"來指定,示例:web
fab -H localhost -f host_type.py host_type
若是目標主機未配置密鑰認證信任,將會提示輸入目標主機對應帳號登陸密碼。sql
官方文檔:http://docs.fabfile.org/en/1.14/shell
fab做爲Fabric程序的命令行入口,提供了豐富的參數調用,命令格式以下:
fab [options] <command>[:arg1,arg2-val2,host=foo,host='h1;h2'...]...
經常使用參數介紹。更多參數可以使用fab -help查看。
devops@devops-virtual-machine:~/devops$ fab -l Available commands: host_type
直接使用命令行執行遠程操做,示例:
devops@devops-virtual-machine:~/devops$ fab -p 1234567 -H localhost -- 'uname -s' [localhost] Executing task '<remainder>' [localhost] run: uname -s [localhost] out: Linux [localhost] out: Done. Disconnecting from localhost... done.
fab命令好似結合咱們編寫的fabfile.py(其它文件名必須添加-f filename應用)來搭配使用的,部分命令行參數能夠經過相應的方法來替代,使之更加靈活,例如"-H 192.168.56.133,192.168.56.134",咱們能夠經過定義env.hosts來實現,如"env.hosts=['192.168.56.133,192.168.56.134']"。fabfile的主體由多個自定義的任務函數組成,不一樣任務實現不一樣的操做邏輯。
env對象的做用是定義fabfile的全局設定,支持多個屬性,包含目標主機、用戶名、密碼、等角色,各屬性說明以下:
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' }
env.roledefs = { 'webservers':['192.168.56.131','192.168.56.132','192.168.56.133'], 'dbserver':['192.168.56.134','192.168.56.135'] }
引用時使用Python修飾符的形式進行,角色修飾符下面的任務函數爲其做用域,下面來看一個示例:
@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就能夠實現不一樣角色執行不一樣的任務函數了。
Fabric提供了一組簡單但功能強大的fabric.api命令集,簡單地調用這些API就能完成大部分應用場景需求。Fabric經常使用方法及說明以下:
本示例調用local()方法執行本地(主控端)命令,添加"@runs_once"修飾符保證該任務函數只執行一次。調用run()方法執行遠程命令。
devops@devops-virtual-machine:~/devops$ pwd /home/devops/devops devops@devops-virtual-machine:~/devops$ 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@devops-virtual-machine:~/devops$ 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.
devops@devops-virtual-machine:~/devops$ 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@devops-virtual-machine:~/devops$ fab -f simple2.py remote_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.
本示例使用"@task'修復符標誌入口函數go()對外部可見,配合"@runs_once"修飾符接受用戶輸入,最後調用worktask()任務函數實現遠程命令執行。
devops@devops-virtual-machine:~/devops$ cat simple3.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修復符來達到此目的。
執行結果以下:
devops@devops-virtual-machine:~/devops$ fab -f simple3.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,再結合任務韓素實現目標主機文件上傳與執行的操做。
devops@devops-virtual-machine:/home/install$ 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.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()
執行命令fab -f simple4.py go。運行結果以下:
devops@devops-virtual-machine:~/devops$ fab -f simple4.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.
執行命令fab -Pf simple4.py go。運行結果以下:
devops@devops-virtual-machine:~/devops$ fab -Pf simple4.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,最終實現文件一致性校驗。
devops@devops-virtual-machine:~/devops$ 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.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()
執行命令fab -f simple5.py go。運行結果以下:(提示此程序不支持-P參數並行執行、如需並行執行,程序須要作調整)
devops@devops-virtual-machine:~/devops$ fab -f simple5.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.
本示例經過env.roledefs定義不一樣主機角色,在使用"@roles('webservers')"修復符綁定到對應的任務函數,實現不一樣角色主機的部署差別。
devops@devops-virtual-machine:~/devops$ cat simple6.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)
執行命令fab -Pf simple6.py deploy
devops@devops-virtual-machine:~/devops$ fab -Pf simple6.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下的軟連接實現。
#!/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軟連接作切換,管理員的版本發佈、回滾操做用戶無感知。