如何使用fabric 自動化平常管理任務和部署

來源:http://ruiaylin.github.io/2014/11/24/fabric/ python

fabric的官方介紹:

看一下官方介紹,來一個清晰的認識mysql

Fabric is a Python (2.5-2.7) library and command-line tool for streamlining the use of SSH 
for application deployment or systems administration tasks.
    More specifically, Fabric is: A tool that lets you execute arbitrary Python functions via 
the command line;A library of subroutines (built on top of a lower-level library) to make executing shell commands over SSH easy and Pythonic. Naturally, most users combine these 
two things, using Fabric to write and execute Python functions, or tasks, to automate
interactions with remote servers.

fabric 能夠爲哪些人服務

Python 開發者
系統管理員 [ SA , ASA, DBA ...]
應用開發者

fabric 經常使用接口

fabric是對ssh的一個集成工具,對咱們而言只須要使用相應的接口,來高效的完成工做,咱們經常使用到的功能基本是 : 本地或者遠端執行命令, 分發文件,收集文件,還有一些權限相關的操做。 這些fabric都給咱們提供了對應的接口。
以下所示:linux

run (fabric.operations.run)
sudo (fabric.operations.sudo)local (fabric.operations.local)get (fabric.operations.get)put (fabric.operations.put)
prompt (fabric.operations.prompt)
reboot (fabric.operations.reboot)

fabric 還提供了上下文管理器

接口部分提供了命令運行的方式,不過都沒法保持上下文關係,爲了解決這個問題,fabric的context manager 就派上了用場:git

cd (fabric.context_managers.cd)lcd (fabric.context_managers.lcd)path (fabric.context_managers.path)settings (fabric.context_managers.settings)prefix (fabric.context_managers.prefix)

fabric 安裝

easy_install fabric

fabric 編程模型介紹

因爲fabric是基於python的,因此寫fabric腳本就是寫python腳本,你能夠像寫python腳本同樣,能夠依賴其餘模塊或者其餘工具來完成工做。Fabric 腳本,經過fab工具運行fabric python腳本。fab工具默認執行fabfile.py ,也能夠經過-f 參數指定 腳本文件名。fabric優點多多,簡單,方便,日誌輸出清晰,命令
中可使用AWK 命令 下面咱們看一個 hello world 程序。github

from fabric.api import *def helloworld(who='world'):
    print  "Hello {0}!".format(who) 
def helloworld1(you='world',me='ruiaylin'):
    print  "Hello {0}! i am {1} ! ".format(you,me)

執行命令(其中參數的傳遞直接跟在任務後跟變量名和參數):sql

  fabric  fab -f helloword.py  helloworldHello world! Done.
  fabric  fab -f helloword.py  helloworld1:you='ruichao',me='ruiaylin'Hello ruichao! i am ruiaylin ! Done.

fabric主要接口方法

咱們已經看了一個簡單例子下面咱們來看一下fabric的主要接口。shell

run (fabric.operations.run)

Fabric 中使用最多的就是 run 方法了。run是用來在一臺或者多臺遠程主機上面執行shell 命令。數據庫

  • 方法的返回值是能夠經過變量來進行捕獲編程

  • 能夠經過變量的.failed 和 .succeeded 來檢查命令是否執行成功ubuntu

  • 還有一個很讚的就是 run 方法中執行命令的時候,能夠支持awk 很給力

使用方法:

# creat a directoryrun(" mkdir /tmp/testdir/ -p ")# check proce***esult = run("ps -ef |grep mysqld|grep -v safe |grep -v grep  | wc -l "#Check if commandresult.failed
sudo (fabric.operations.sudo)

使用 sudo 命令執行對頂的命令。使用方法與run 相似。

local (fabric.operations.local)

local 命令是執行本機的命令或者腳本.使用方法和run 還有sudo相似,可是有一個區別
就是: 捕獲結果的時候,是經過指定 capture=False 或者capture=True來肯定。來看
實例:

# example like this : def helloworld(who='world'):
    print  "Hello {0}!".format(who) 
    yy = local(" pwd ", capture=False)    print 'start :  yy = ' , yy , ' : ::  ',yy.succeeded
    zz = local(" pwd ", capture=True)    print 'start :  zz = ' , zz , ' : ::  ',zz.succeeded#result :  fabric  fab -f helloword.py  helloworld  -H 10.211.55.3 -u root
[10.211.55.3] Executing task 'helloworld'Hello world!
[localhost] local:  pwd 
/Users/ruiaylin/Documents/workpython/fabric
start :  yy =    : ::   True[localhost] local:  pwd 
start :  zz =  /Users/ruiaylin/Documents/workpython/fabric  : ::   True
get (fabric.operations.get)

get 方法是從遠程主機 copy file 到本地,功能跟scp同樣。能夠從遠程主機下載
備份,或者日誌文件等等。

  • 經過參數 remote_path 指定遠程文件的路徑

  • 經過參數 local_path 指定遠程文件的路徑

使用方法以下:

# Download some logsget(remote_path="/tmp/xxx.log", local_path="/tmp/xxx.log")  
# Download a database back-upget("/backup/db.gz", "./db.gz")
put (fabric.operations.put)

某些須要上傳和分發文件的時候,put命令就派上了用場,使用方式相似 get。也一樣能夠
經過.failed .succeeded進行命令是否執行成功的判斷。

  • local_path - 本地路徑

  • remote_path - 遠程路徑

  • mode - 文件屬性

以下例子:

upload = put("requirements.txt", "requirements.txt", mode=664)
並行執行

目前官方來看 1.X 版本的fabric 並行執行的時候不是thread safe的。若是須要並行執行task。須要在方法上面使用註解 @parallel 爲了防止管控機器上面過多的併發任務能夠經過 @parallel(pool_size=5)來設置. 並行的執行輸出都會輸出到一個終端上面,比較混亂。最好是寫到日誌,以task爲維度。跟下面的代碼相似。

還有幾個接口你們能夠查閱fabric 文檔 :

MySQL 安裝實例

安裝步驟以下

  • 獲取主機ip

  • check主機可達性

  • 檢查linux平臺詳情

  • 是否有運行的mysql實例

  • 若是有獲取對應的端口

  • 檢查是否和要安裝的端口衝突

  • 處理mysql用戶以及屬組

  • 處理安裝相關目錄和權限

  • copy 安裝包到目標機

  • 解壓處理,將主要軟件工具軟鏈接到path路徑中

  • 生成對應標準配置文件並分發到目標機對應目錄

  • 初始化數據庫

  • 啓動數據庫

  • 基本步驟安裝完畢

基本腳本以下:

script 1 sub task :

from fabric.api import *from fabric.colors import green,red,blue,cyan,yellowimport os , sysimport socketimport datetimeimport loggingimport logging.handlers#get logger for logging def initLoggerWithRotate():
    logname=''.join(env.host_string.split('.'))+'.log'
    logFileName="logs/%s"%logname
    logger = logging.getLogger("fabric")
    formater = logging.Formatter("%(asctime)s %(name)s %(levelname)s %(message)s","%Y-%m-%d %H:%M:%S")
    file_handler = logging.handlers.RotatingFileHandler(logFileName, maxBytes=104857600, backupCount=5)
    file_handler.setFormatter(formater)
    stream_handler = logging.StreamHandler(sys.stderr)
    logger.addHandler(file_handler)
    logger.addHandler(stream_handler)
    logger.setLevel(logging.INFO)    return logger#mkdirdef runmkdir(dir):
    run(''' mkdir -p %s '''%dir)#stp 1 check hostdef checkhost(logger):
     host = env.host_string 
     s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
     flag_c = 0
     try:
         s.connect((host, 22))
         flag_c = 1
         logger.info( green( ' --> host %s can be reachable ' %host ) )     except socket.error as e: 
         logger.warning( yellow( ' --> Error on connect %s' %e ) )
     s.close()     return flag_c#stp 2 check alive instance on target host def checkmysqlinstance(logger):
    try:
        wc = run(''' ps -ef |grep mysqld|grep  -v safe | grep -v grep | wc -l  ''') 
        if int(wc) > 0  : 
            logger.warning(yellow( ' --> %sinstance exist on the target host  '%wc )) 
            portraw = run('''  ps -ef |grep mysqld|grep -v safe |grep -v grep  |awk ' {for(i=1;i<=NF;i++){if($i ~/--port/ ){print $i}}}' |awk -F '=' '{print $2}'
            ''')
            ports = [x.strip() for x in portraw.split() ]
            logger.warning( yellow( ' --> existing instance port : [ %s ] '%( ','.join( ports ))))            if port in ports:
                logger.error( red( ' --> Install port %s exist , install failed '%port))
                logger.error( red( ' <<<exit>>>>>  task on host %s stop & exit() '%thost))
                sys.exit()    except Exception, e:
        logger.warning(yellow( ' --> checkmysqlinstance() exception : %s '%e )) 
        raise e 
#stp 3 initdir for installationdef createUser(logger,user='mysql',group='dba'):
    try:        if int(run('grep "^mysql" /etc/passwd|wc -l')) == 0 :
            run('groupadd dba ')
            run('useradd -c "mysql software owner" -g dba -G dba mysql')
            run('mkdir -p /home/mysql ; chown -R mysql.dba /home/mysql ')
            logger.info(cyan( ' --> create user [ mysql ] in group [ dba ]  success ' )) 
        else : 
            logger.info(yellow ( ' --> user [ mysql ] in group [ dba ] exist & skip  ' )) 
    except Exception, e:
        logger.warning(yellow( ' --> createUser() exception : %s '%e )) 
        raise e#stp 4 initail directory for mysql        def initdir(logger,port=3306):  
    try :
        logger.info( green( ' --> begin to create dirs for installation '))
        datadir='/data/'
        logdir ='/log/'
        mandir = 'mysql%s'%port
        subddir ='/data/mysql%s/{data,log,run,tmp}'%(port)
        subldir ='/log/mysql%s/{binlog,iblog}'%(port) 
        #data
        ck1 = run(' df -vh  | grep  /data | wc -l ')        if ck1  == 0 : 
            logger.error(green(' --> no /data/ partition exist' ) )            #sys.exit()
        if int( run(' ls /  | grep  /data | wc -l ')) == 0 or int( run(' ls /data/ | grep -w %s | wc -l '%mandir) ) == 0 : 
            runmkdir(subddir) 
            logger.info(green(' --> /data/*** create Ok ' ) )        else : 
            logger.info(green(' --> /data/mysql%s exsit '%port ))
            logger.info(green(' --> pls,handle it and restart this task '))
            sys.exit()        #log 
        ck2 = run(' df -vh | grep /log/  | wc -l  ')        if int( run(' df -vh | grep /log/  | wc -l  ') ) == 0  and int( run(' ls / | grep -w log  | wc -l  ') ) == 0: 
            logger.warning( yellow(' --> no /log/ partition exist') ) 
            logger.warning( yellow(' --> create link for /log/ --> /data/log/') ) 
            runmkdir('/data/log')
            run('ln -s /data/log  /log ')
            runmkdir(subldir) 
            logger.info(green(' --> /log/*** create Ok ' ) )        else : 
            if  int(run(' ls /log/ | grep -w %s | wc -l '%mandir)) == 0: 
                runmkdir(subldir) 
                logger.info(green(' --> /log/*** create Ok ' ) )            else : 
                logger.info(yellow(' --> /log/mysql%s exsit '%port ))
                logger.error(red(' --> pls,handle it and restart this task ' ))
                sys.exit() 
        #change 
        runmkdir('/data/tmp')
        logger.info(green(' --> change dirs owner&privs start'))
        run('chown -R mysql:dba /data/*')
        run('chown -R mysql:dba /log') 
        logger.info(green(' --> change dirs owner&privs done'))    except Exception, e:
        logger.warning(yellow( ' --> initdir() exception : %s '%e )) 
        raise e 
#stp 5 put mysql install packagedef copymysql(logger,version='5.7'): 
    try:
        dits = {        'ubuntu':'mysql-server_5.6.21-1ubuntu12.04_amd64.deb-bundle.tar',        'centos':'mysql-server.tar.gz'
        }
        issue = run ('cat /etc/issue') 
        ss = issue.lower()
        logger.info( green( ' %s '%ss))        if int ( run( ' ls /usr/local/ | grep mysql | wc -l ') ) > 0 : 
            logger.info( yellow( ' --> mysql software installed , skip   ' )) 
            return
        plats = dits.keys()        for x in plats: 
            if ss.find(x) != -1: 
                logger.info( green( ' --> the target host platform is %s'% x ) )
                put( local_path="configs/%s"%dits[x],remote_path="/tmp/%s"%dits[x] )
                logger.info( green( ' --> tar the ball to prop dir '))
                run( 'tar zxvf /tmp/%s -C /usr/local/ '%dits[x] )
                run( 'ln -s /usr/local/%s  /usr/local/mysql  '%dits[x][:-7] )                break 
    except Exception, e:
        logger.warning(yellow( ' --> copymysql() exception : %s '%e )) 
        raise e 
#gen my.cnf file def getnewServerId(logger,port):  
    host = env.host_string    print 'getnewServerId : ',host
    pics = host.split('.')
    a=int(pics[0])
    b=int(pics[1])
    c=int(pics[2])
    d=int(pics[3])
    suf = int(port) % 256
    server_id =  b * 256 * 256 * 256 + c * 256 * 256 + d * 256 + suf
    logger.info( cyan( ' --> gen server_id done , %s %s is %s '%( host , port , server_id) ) )    return server_iddef genmycnf(logger,port=3306,itype='h'):
    host = env.host_string
    bps={    "a":"48|32|3100|3000",    "b":"62|40|4600|4500",    'c':'94|64|7600|7500',    'd':'94|32|3100|3000',    'e':'125|75|10100|10000',    'f':'188|120|15100|15000',    'g':'188|60|7600|7500',    'h':'1|256M|800|750'
    } 
    try:
        myfile=''.join(host.split('.'))+'.cnf'
        cpmycnf="""cp configs/my.cnf  tmp/%s """%myfile 
        local( 'rm -f  tmp/%s'%myfile  )
        local("cp configs/my.cnf tmp/%s "%myfile )  
        sid=getnewServerId(logger,port)
        keys=bps.keys()
        bpxs=bps[itype]
        mem,bpsize,maxc,maxuc=bpxs.split('|')        if bpsize[-1] != "M":
            bpsize = bpsize +'g'
        chrgcmd="""  sed -i -e "s/3306/%s/g" -e "s/server_id=10000/server_id=%s/g" -e "s/=32g/=%s/g" -e "s/max_connections=3100/max_connections=%s/g" -e "s/max_user_connections=3000/max_user_connections=%s/g" tmp/%s """
        local( chrgcmd%(port,sid,bpsize,maxc,maxuc,myfile) ) 
        logger.info( green( ' --> gen my.cnf success  ') )
        logger.info( green( ' --> copy my.cnf to dist host ') )
        put( local_path="tmp/%s"%myfile, remote_path="/data/mysql%s/my.cnf"%(port) )    except Exception, e:
        logger.warning(yellow( ' --> genmycnf() exception : %s '%traceback.format_exc()  ) ) 
        raise e

script 2 whole task :

import inst_utils
from inst_utils import *
def install_mysql(port):
    logger = initLoggerWithRotate()
    thost = env.host_string
    try:
        logger.info(green( 'stp 1 get the host %s '%thost ))        #check host reachable  
        rs1 = checkhost(logger )
        if int(rs1)== 0 :
            logger.info(red( 'stp 2 check the host is reachable failed ' ))
        logger.info(green( 'stp 2 check the host is reachable OK ' ))
        plat_type = run(''' uname -o ''')
        if plat_type !=  'GNU/Linux' :
            logger.warning(yellow('stp 3 target platform is not GNU/Linux & exit() '))  
            sys.exit()
        logger.info(green('stp 3 target platform is GNU/Linux')) 
        #check target host exsist mysql instance 
        logger.info(green( 'stp 4 checkmysqlinstance  ' ))        checkmysqlinstance(logger)        #create MySQL user 
        logger.info( green( 'stp 5 createUser ' ))        createUser(logger) 
        put(local_path="configs/bash_profile", remote_path="/home/mysql/.bash_profile")  
        #checking dir
        logger.info( green( 'stp 6 initdir ' ))        initdir(logger,port) 
        #copy file 
        logger.info( green( 'stp 7 copymysql ' ))        copymysql(logger)
        logger.info( green( 'stp 8  genmycnf  ') ) 
        genmycnf(logger,port,'h')
    except Exception, e:
        print  'main : exception : ' ,  e

如上腳本完成了,基本的安裝,並無啓動mysql實例,和一些db初始化工做, 有興趣的同窗能夠本身來完成,總之,fabric必定是一個運維利器 。。。

相關文章
相關標籤/搜索