前面介绍了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'...]...
参数详解:
有时候咱们甚至不须要写代码就能够执行远程操做,直接使用命令行形式:
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的全局设定,支持多个属性,包括目标主机,用户,密码,角色,各属性说明以下:
'root@192.168.0.122:22':'123456'
}
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支持经常使用的方法及说明以下:
下面结合一些示例来理解上面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 官网英文文档: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自动化运维技术与最佳实践》