9.python 系統批量運維管理器之Fabric模塊

 

前面介紹了paramiko,pexpect模塊,今天來講比較適合大型應用自動化部署的模塊,或者執行系統命令的模塊Fabric。php

Fabric 是一個 Python 的庫,同時它也是一個命令行工具。它提供了豐富的同 SSH 交互的接口,能夠用來在本地或遠程機器上自動化、流水化地執行 Shell 命令。使用 fabric 提供的命令行工具,能夠很方便地執行應用部署和系統管理等操做。所以它很是適合用來作應用的遠程部署及系統維護。其上手也極其簡單,你須要的只是懂得基本的 Shell 命令。html

fabric 依賴於 paramiko 進行 ssh 交互,fabric 的設計思路是經過幾個 API 接口來完成全部的部署,所以 fabric 對系統管理操做進行了簡單的封裝,好比執行命令,上傳文件,並行操做和異常處理等。python

paramiko 是一個用於作遠程控制的模塊,使用該模塊能夠對遠程服務器進行命令或文件操做,fabric ansible 內部的遠程管理就是使用的paramiko來現實mysql

安裝模塊nginx

Fabric的官網是 http://www.fabfile.org,源碼託管在github上,能夠把源碼包clone到本地,使用 python3 setup.py  develop 安裝。git

我這裏使用 pip install fabric3 安裝,不過 fab 命令默認安裝在python目錄下,須要建立軟鏈接:ln -s 默認安裝路徑  /usr/bin/fab  添加到環境變量便可github

Fabric簡介和各個版本差別比較:http://www.mamicode.com/info-detail-2337088.htmlweb

安裝成功sql

fab經常使用參數api

fab做爲Fabric程序的命令行入口,提供了豐富的參數調用,命令的格式以下:

fab [options] <command>[:arg1,arg2-val2,host=foo,host='h1;h2'...]...

參數詳解:

  • -l:顯示定義好的任務函數名,
  • -f:指定fab入口文件,默認入口文件名爲fabric.py,
  • -g:指定網關(中轉)設備,好比堡壘機環境,填寫堡壘機IP便可,
  • -H:指定目標主機,多臺用 "," 分割,
  • -P:以異步並行的方式運行多主機任務,默認爲串行運行,
  • -R:指定role(角色),以角色名區分不一樣業務組設備,
  • -t:設置設備鏈接超時時間,
  • -T:設置遠程主機命令執行超時時間(秒),
  • -w:當命令執行失敗,發出警告,而非默認停止任務。

有時候咱們甚至不須要寫代碼就能夠執行遠程操做,直接使用命令行形式:

fab -p 12580(密碼) -H 192.168.0.132 -- 'uname -a'

運行結果:

很多人反映命令執行總有paramiko的cryptography爆錯

應爲paramiko2.4.2依賴cryptography,而最新的cryptography==2.5裏有一些棄用的API。

只需卸載cryptography的2.5版本,而且安裝2.4.2便可

pip uninstall cryptography==2.5 pip install cryptography==2.4.2

fabfile的編寫以及全局屬性設定

fabfile的主體由多個自定義的任務函數組成,不一樣任務實現不一樣的操做邏輯。

env對象的做用是定義fabfile的全局設定,支持多個屬性,包括目標主機,用戶,密碼,角色,各屬性說明以下:

  • env.host:定義目標主機,能夠用IP或者主機名錶示,以python的列表形式定義,如env.hosts=['192.168.0.131','192.168.0.132']。
  • env.exclude_hosts:排除指定主機,如env.exlcue_hosts=['192.168.0.132']。
  • env.user:定義用戶名,如env.user='root'。
  • env.port:定義目標主機端口,默認爲22,如env.port='22'。
  • env.password:定義密碼,如env.password='123456'。
  • env.passwords:與password功能同樣,區別在於不一樣主機不一樣密碼的應用場景,須要注意的是,配置passwords時需配置用戶,主機,端口等信息,如 env.passwords = {

          'root@192.168.0.122:22':'123456'

          }

  • env.gateway:定義網關(中轉,堡壘機)IP,如env.gateway='192.168.0.132'。
  • env.deploy_release_dir:自定義全局變量,格式:env.+"變量名稱",如env.deploy_release_dir,env.age,env.sex等。
  • env.roledefs:定義角色分組,好比web組與db組主機區分開來,定義以下:

         env.roledefs = {

          'webservers':['192.168.0.1','192.168.0.2'],

          'dbservers':['192.168.0.3']

         }

引用時使用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 pubclitask(): run('uptime')
def deploy():
execute(webtask)
execute(dbtask)
execute(pubclitask)

在命令執行 fab -f deploy 就能夠實現不一樣角色執行不一樣的任務函數了。

經常使用API

Fabric提供了一組簡單但功能強大的fabric.api命令集,簡單的調用這些API就能完成大部分應用場景需求。Fabric支持經常使用的方法及說明以下:

  • local:執行本地命令,如 local('uname -a')
  • 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')
  • get:從遠程主機下載文件到本地,如 get('/data/user.info','/home/user.info')
  • prompt:得到用戶輸入信息,如 prompt('請輸入密碼:')
  • confirm:得到提示信息確認,如 confirm('tests failed,Continue[Y/N]?')
  • reboot:重啓遠程主機,如 reboot()
  • @task:函數修飾符,標識的函數爲 fab 可調用的,非標記的對 fab 不可見,純業務邏輯
  • @runs_once:函數修飾符,標識的函數只會執行一次,不受多臺主機影響。

下面結合一些示例來理解上面API

異常處理

fabric 當執行返回碼出現非0的命令時, 直接拋出異常退出的。這種異常不是Exception異常, 而是一個SystemExit異常。若是須要捕捉異常處理, 只須要

:::python try: fab_execute(publish_ccms_pd_root, host=self.host, info=self.info, functions=self.functions) except SystemExit: self.write_error()

或者:

:::python try: run('''ls -al ''') except SystemExit: event()

但不建議這樣作, 若是你僅僅是碰到錯誤仍是要繼續執行, 而不作異常的操做。可使用官方的  settings.warn_only = True , 這樣的話碰到不正常返回碼僅僅會拋出Warning 信息。

:::python from fabric.state import env env.warn_only = True

或者:

:::python from fabric.api import settings with settings(warn_only=True): run('ls -al')

一些示例

示例一   查看本地與遠程主機信息

經過調用local()方法執行本地(主控端)命令,添加 "@runs_once" 修飾符保證該任務只執行一次,調用run()方法執行遠程命令。

#coding=utf-8

from fabric.api import * env.user = "root" env.password = "12580" env.hosts = ['192.168.0.132'] 
#查看本地系統信息,當有多臺主機時只運行一次 @runs_once def local_task(): #本地任務函數 local(
"uname -a") def remote_task():
#with的做用是讓後面的表達式的語句繼承當前狀態,實現cd /root && ls -l 的效果 with cd(
"/root"): run("ls -l")

運行結果:

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

使用 "@task" 修飾符標誌入口函數go()對外部可見,配合 "@runs_once" 修飾符接收用戶輸入,最後調用worktask()任務函數實現遠程命令執行。

#coding=utf-8

from fabric.api import * env.user = 'root' env.password = '12580' env.hosts = ['192.168.0.132'] 
#主機遍歷過程當中,只有第一臺觸發此函數 @runs_once def input_raw():
return prompt("Please input directory name: ",default = "/home") def worktask(dirname): run("ls -l "+dirname)
#限定只有go函數對fab命令可見 @task def go(): getdirname
= input_raw() worktask(getdirname)

運行結果:

示例三  網關模式文件上傳與執行

經過Fabric的env對象定義網關模式,即中轉請求,堡壘機環境。定義格式爲"env.gateway='192.168.0.132'",其中IP "192.168.0.132"爲堡壘機IP,再結合任務函數實現目標主機文件上傳與執行的操做。

#coding=utf-8

from fabric.api import *
from fabric.context_managers import *
from fabric.contrib.console import confirm env.user = 'root' env.password = '12580' env.hosts = ['192.168.0.132']
#定義堡壘機IP,做爲文件上傳,執行的中轉設備 env.gateway
= '192.168.0.149'

#假如全部主機密碼都不同,能夠經過env.passwords字典變量一一指定
env.passwords = { 'root@192.168.0.132:22':'12580' 'root@192.168.0.133:22':'toor' #堡壘機帳號信息 } lpackpath = '/home/joker/test.tar.gz' #本地安裝包路徑 rpackpath = '/tmp/install/' #遠程安裝包路徑 @task def put_task(): run('mkdir -p /tmp/install') with settings(warn_only=True): result = put(lpackpath,rpackpath) if result.failed and not confirm('put file failed,Continue[Y/N]?'): abort('Aborting file put task!') @task def run_task(): with cd('/tmp/install'): run('tar -zxvf test.tar.gz') #使用with繼續繼承/tmp/install目錄位置狀態
with cd(
'test/'): run('./bash.sh'
) @task def go(): put_task() run_task()

運行結果:

Fabric應用示例

示例一  部署LNMP業務服務環境

業務上線以前最關鍵的一項任務是環境部署,每每一個業務涉及多種應用環境,好比WEB,DB,PROXY,CACHE等,本示例經過env.roledefs定義不一樣主機角色,再使用 "@roles('webservers')" 修飾符綁定到對應的任務函數,實現不一樣角色主機的部署差別。

#coding=utf-8 
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)

代碼執行結果:

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.repoInstall epel ntp....
[192.168.56.12] run: wget -O /etc/yum.repos.d/epel.repo http://mirrors.aliyun.com/repo/epel-7.repoInstall 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. 

 

示例二  生產環境代碼包發佈管理

程序生產環境的發佈是業務上線最後一個環節,要求具有源碼打包,發佈,切換,回滾,版本管理等功能。

生產環境代碼包發佈管理流程圖:

#coding=utf-8
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軟連接作切換,管理員的版本發佈、回滾操做用戶無感知。

 

參考連接:

http://www.javashuo.com/article/p-zrbyyaaa-d.html

Fabric API及實例講解

fabric 官網英文文檔:http://www.fabfile.org/

fabric 中文站點:http://fabric-chs.readthedocs.io/zh_CN/chs/

python三大神器之一fabric使用:https://www.cnblogs.com/rufus-hua/p/5144210.html

如何用Fabric實現無密碼輸入提示的遠程自動部署:http://www.javashuo.com/article/p-ctyavdee-gk.html

fabric實現遠程操做和部署:http://python.jobbole.com/83716/

自動化運維管理 fabric:http://www.ttlsa.com/python/automation-operation-and-maintenance-tool-fabric/

Python3自動化運維之Fabric模版詳解:https://www.imooc.com/article/38448

《Python自動化運維技術與最佳實踐》

相關文章
相關標籤/搜索