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

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

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

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

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

安装模块

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

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

Fabric简介和各个版本差异比较:http://www.mamicode.com/info-detail-2337088.html

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

安装成功

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:当命令执行失败,发出警告,而非默认中止任务。

有时,我们甚至不需要编写代码,就可以直接使用命令行形式执行远程操作:

[En]

Sometimes we can perform remote operations without even writing code, directly using the command line form:

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

运行结果:

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

不少人反映命令执行总有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")

运行结果:

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

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

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

运行结果:

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

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

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

运行结果:

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

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.

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

节目制作环境的发布是业务的最后一环,需要具备源代码打包、发布、切换、回滚、版本管理等功能。

[En]

The release of the program production environment is the last link of the business, which requires the functions of source code packaging, release, switching, rollback, version management and so on.

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

9.python 系统批量运维管理器之Fabric模块
#coding=utf-8from 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软链接做切换,管理员的版本发布、回滚操作用户无感知。

参考链接:

https://blog.csdn.net/freeking101/article/details/81103945

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实现无密码输入提示的远程自动部署:https://blog.csdn.net/slvher/article/details/50414675

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自动化运维技术与最佳实践》

Original: https://www.cnblogs.com/bmjoker/p/10633407.html
Author: bmjoker
Title: 9.python 系统批量运维管理器之Fabric模块

原创文章受到原创版权保护。转载请注明出处:https://www.johngo689.com/15755/

转载文章受原作者版权保护。转载请注明原作者出处!

(0)

大家都在看

发表回复

登录后才能评论
免费咨询
免费咨询
扫码关注
扫码关注
联系站长

站长Johngo!

大数据和算法重度研究者!

持续产出大数据、算法、LeetCode干货,以及业界好资源!

2022012703491714

微信来撩,免费咨询:xiaozhu_tec

分享本页
返回顶部