python运维管理系统_9.python 系统批量运维管理器之Fabric模块

前面介绍了paramiko,pexpect模块,今天来说比较适合大型应用自动化部署的模块,或者执行系统命令的模块Fabric。

Fabric 是一个 Python 的库,同时它也是一个命令行工具。它提供了丰富的同 SSH 交互的接口,可以用来在本地或远程机器上自动化、流水化地执行 Shell 命令。使用 fabric 提供的命令行工具,可以很方便地执行应用部署和系统管理等操作。因此它非常适合用来做应用的远程部署及系统维护。其上手也极其简单,你需要的只是懂得基本的 Shell 命令。

fabric 依赖于 paramiko 进行 ssh 交互,fabric 的设计思路是通过几个 API 接口来完成所有的部署,因此 fabric 对系统管理操作进行了简单的封装,比如执行命令,上传文件,并行操作和异常处理等。

paramiko 是一个用于做远程控制的模块,使用该模块可以对远程服务器进行命令或文件操作,fabric和 ansible内部的远程管理就是使用的paramiko来现实

安装模块

Fabric的官网是 http://www.fabfile.org,源码托管在github上,可以把源码包clone到本地,使用 python3 setup.py develop 安装。

我这里使用 pip install fabric3 安装,不过 fab 命令默认安装在python目录下,需要创建软连接:ln -s 默认安装路径 /usr/bin/fab 添加到环境变量即可

1344396-20190331210629031-901161956.png

安装成功

fab常用参数

fab作为Fabric程序的命令行入口,提供了丰富的参数调用,命令的格式如下:

fab [options] [: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'

运行结果:

1344396-20190331213900246-326391027.png

不少人反映命令执行总有paramiko的cryptography爆错

应为paramiko2.4.2依赖cryptography,而最新的cryptography==2.5里有一些弃用的API。

只需卸载cryptography的2.5版本,并且安装2.4.2即可

pip uninstall cryptography==2.5pip 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异常。如果需要捕捉异常处理, 只需要

:::pythontry:

fab_execute(publish_ccms_pd_root, host=self.host, info=self.info, functions=self.functions)

except SystemExit:

self.write_error()

或者:

:::pythontry:

run('''ls -al''')

except SystemExit:event()

但不建议这样做, 如果你仅仅是碰到错误还是要继续执行, 而不做异常的操作。可以使用官方的 settings.warn_only = True , 这样的话碰到不正常返回码仅仅会抛出Warning 信息。

:::pythonfromfabric.state import env

env.warn_only= True

或者:

:::pythonfromfabric.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")

运行结果:

1344396-20190401164609408-1784888140.png

示例二 动态获取远程目录列表

使用 "@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)

运行结果:

1344396-20190401210134879-871332733.png

示例三 网关模式文件上传与执行

通过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 confirmenv.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()

运行结果:

1344396-20190402101149193-1440618461.png

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 235mysqld on

Done.

示例二 生产环境代码包发布管理

程序生产环境的发布是业务上线最后一个环节,要求具备源码打包,发布,切换,回滚,版本管理等功能。

生产环境代码包发布管理流程图:

1344396-20190402112559224-531341044.png

#coding=utf-8

from fabric.api import *

from fabric.colors import *

from fabric.context_managers import *

fromfabric.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软链接做切换,管理员的版本发布、回滚操作用户无感知。

参考链接:

《Python自动化运维技术与最佳实践》

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值