使用Fabric模块实现自动化运维

一、软件安装
简介: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 ''
[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 ''
[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()

你可能感兴趣的:(Linux相关教程,fabric,自动化运维,linux)