Fabric是一个Python(2.5-2.7)实现的ssh命令行工具,简化了SSH的应用程序部署及系统管理任务。它提供的操作包括:执行本地或远程shell命令,上传/下载文件,以及其他辅助功能,如提示用户输入、中止执行等。Fabric在paramiko的基础上做了更高一层的封装,操作起来会更加的简单。Fabric的官网地址为http://www.fabfile.org/。
1,安装
支持pip,easy_install及源码编译安装:
<code> [root@sta ~]# pip install fabric </code>
2,fab常用参数
Fabric工具提供了一个简单的构建工具fab,其作为Fabric程序的命令行入口,提供了丰富的参数调用。
常用的fab命令选项和参数:
<code> -l 显示可用的task -H 指定host,多个host用逗号分开 -R 指定role,多个role用逗号分开 -P 并发数,默认是串行 -w warn_only,默认为遇到异常直接放弃执行并退出 -f 指定入口文件,fab默认入口文件是:fabfile/fabfile.py </code>
首先来看一个简单的示例:
<code> def hello(): print("Hello world!") </code>
把上述代码放在你当前的工作目录中一个名为 fabfile.py 的 Python 模块文件中。然后这个 hello 函数就可以用 fab 工具(随 Fabric 一并安装的命令)来执行了,输出的结果会是这样:
<code> $ fab hello Hello world! Done. </code>
fab 工具所做的只是导入 fabfile 并执行了相应一个或多个的函数,这里并没有任何魔法——任何你能在一个普通 Python 模块中做的事情同样可以在一个 fabfile 中完成。
Fabric 能够加载 Python 模块(如: fabfile.py )和包(如 fabfile/ ),默认情况下,它会根据 Python 包的导入机制加载 fabfile -可以是 fabfile/ 也可以是 fabfile.py 。
3. 环境字典 env
Fabric 中有一个简单但是必不可少的部分叫做“环境”:它是 Python 字典的子类,既用作设置,也用于任务间数据空间共享。
目前,环境字典 fabric.state.env 是作为全局的单例实现的,为方便使用也包含在 fabric.api 中。 env 中的键通常也被称为“环境变量”。
<code> env.host : 主机ip,也可以使用fab选项-H参数来指定 env.roledefs : 角色分组,如:{'web': ['x', 'y'], 'db': ['z']} env.all_hosts:Default 为 [],由 fab 设置的当前正在执行命令的主机列表。仅用于显示信息。 env.exclude_hosts : Default 为 [],指定一个主机串列表, fab 执行期间会跳过列表中的主机。例:env.exclude_hosts = [ '192.168.1.102' ] env.port:定义目标主机端口,默认为22。例:env.port = '80'。 env.password : SSH密码,若已经设置好无密码登录,则可以忽略 env.passwords:与 password 功能 一样,区别在于不同主机不同密码的应用场景,需要注意的是,配置passweords时需要配置用户,主机,端口等信息。例: env.passwords = { 'root@192.168.1.104:22':'123', 'root@192.168.1.86:22':'789', 'root@222.24.51.147:22':'345643', } env.parallel :全局并行参数,例 env.parallel = True 。 </code>
4,常用api
<code> local('pwd') : 执行本地命令,这样运行命令的操作会返回一个包含执行结果( .failed 或 .return_code 属性)的对象。 lcd('/tmp') : 切换本地目录 cd('/tmp') : 切换远程目录 run('uname -s') : 执行远程命令 sudo('service httpd restart') : 执行远程sudo,注意pty选项 put(lpackpath,rpackpath):上传本地文件到远程主机。 get(rpackpath,lpackpath):从远程主机下载文件到本地 prompt('please input project rollback version ID:',default=''):获得用户的输入信息 confirm("put file failed,continue[Y/N]?"):提示信息确认,Fabric contrib.console 子模块提供了 confirm 函数,用于简单的 yes/no 提示。 reboot():重启远程主机。 abort ():用于手动停止任务的执行。 </code>
5,装饰器
fabric.decorators.hosts(*host_list)中的所有装饰器:
<code> @hosts('user1@host1', 'host2', 'user2@host3') : 定义按主机来执行。 @roles('webserver', 'dbserver') : 定义按角色分组来执行。 @runs_once :表示的函数只会执行一次,不收多台主机影响,runs_once 无法和任务并行执行同时生效。 @parallel 和 @serial :任务并行或串行执行,如果任务同时被 serial 和 parallel 装饰器装饰,parallel 的优先级更高。 @task :表示的函数为 fab 可调用的,如果写错函数名,则函数名会出现在 Available commands 中,非标记的对 fab 不可见,纯业务逻辑。 @with_settings(warn_only=True) :warn_only表示是否当在远程机器上执行命令,出现错误时,fabric是否退出。将整个函数封装起来,其效果类似于执行在 settings 上下文管理器中。如果你想要修改函数的设置,但不愿改动其缩进时,它会很有用。 </code>
Fabric的功能很强大,还能直接与django进行集成,更多用法清查看官方文档 Fabric 中文文档
6,示例一: 查看本地与远程主机的信息
<code> #!/usr/bin/python #coding:utf-8 import os from fabric.api import * ''' 查看本地与远程主机的信息 www.tianfeiyu.com ''' env.user = 'root' env.hosts = ['192.168.1.104','192.168.1.86'] env.password = '123' env.cwd = '/root/' #默认为'' @runs_once #查看本地系统信息,当有多台主机时只运行一次,即只运行env.hosts中定义的第一个主机 def local_task(): #本地任务函数 local('uname -a') @task def remote_task(): #远程任务函数 with cd('/tmp'): #‘with’的作用是让后面的表达式语句继承当前状态,即实现‘cd('/tmp') && ls’ 的效果 run('ls') @task def go(): print(env.all_hosts) local_task() remote_task() </code>
运行结果为:
<code> [root@sta fabric]# fab -f local.py -l #查看可以执行的所有任务 Available commands: go remote_task [root@sta fabric]# fab -f local.py local_task #执行local_task函数 [192.168.1.104] Executing task 'local_task' [localhost] local: uname -a Linux sta.com 2.6.32-573.7.1.el6.x86_64 #1 SMP Tue Sep 22 22:00:00 UTC 2015 x86_64 x86_64 x86_64 GNU/Linux Done. [root@sta fabric]# fab -f local.py remote_task #执行remote_task函数 [192.168.1.104] Executing task 'remote_task' [192.168.1.104] run: ls [192.168.1.104] out: keyring-tmVX1b VMwareDnD vmware-root-3986279221 [192.168.1.104] out: pulse-eawOp58AQu3G vmware-root yum_save_tx-2015-08-07-06-58nSV3VH.yumtx [192.168.1.104] out: [192.168.1.86] Executing task 'remote_task' [192.168.1.86] run: ls [192.168.1.86] out: orbit-feiyu orbit-gdm pulse-eawOp58AQu3G sta_file vmware-root-4034286326 [192.168.1.86] out: Done. Disconnecting from 192.168.1.86... done. Disconnecting from 192.168.1.104... done. </code>
示例二:自动化部署LNMP环境
<code> #!/usr/bin/python #coding:utf-8 from fabric.colors import * from fabric.api import * ''' 自动化部署LNMP环境 www.tianfeiyu.com ''' env.roledefs = { #定义业务角色分组 'webserver':['192.168.1.86'], 'dbserver':['192.168.1.104'] } env.passwords = { 'root@192.168.1.86:22':'123', 'root@192.168.1.104:22':'123', } @roles('webserver') def webtask(): #部署nginx php php-fpm等环境 print('install nginx php php-fpm...') with settings(warn_only=True): run('yum install nginx -y') run('yum install php-fpm php-mysql php-mbstring php-xml php-mcrypt php-gd -y') run('chkconfig --levels 35 php-fpm on') run('chkconfig --levels 35 nginx on') @roles('dbserver') def dbtask(): #部署mysql环境 print(yellow('install mysql...')) with settings(warn_only=True): run('yum install mysql-server -y') run('chkconfig --levels 35 on') @roles('webserver','dbserver') def pubtask(): #部署公共环境,如epel,ntp等 print(yellow('install epel ntp...')) with settings(warn_only=True): run('wget -P /etc/yum.repos.d/ http://mirrors.aliyun.com/repo/Centos-6.repo') run('yum install ntp') def deploy(): execute(pubtask) execute(webtask) execute(dbtask) </code>
示例三:生产环境代码包发布管理
<code> #!/usr/bin/python #coding:utf-8 from fabric.api import * from fabric.colors import * from fabric.context_managers import * from fabric.contrib.console import confirm import time ''' 生产环境代码包发布管理 www.tianfeiyu.com ''' env.user = 'root' env.hosts = ['192.168.1.86','192.168.1.104'] env.password = '123' env.colorize_errors = True #设置为 True 时,终端输出的错误信息会显示为红色,警告信息则是洋红色 env.project_dev_source = '/data/dev/feiyu/' #开发项目主目录 env.project_tar_source = '/data/dev/releases/' #开发项目压缩包目录 env.project_pack_name = 'release' #项目压缩包名前缀,文件名为release.tar.gz env.deploy_project_root = '/data/www/feiyu/' #项目生产环境主目录 env.deploy_release_dir = 'releases' #项目发布目录,位于主目录下面 env.deploy_current_dir = 'current' #对外服务的当前版本软连接 env.deploy_version = time.strftime('%Y%m%d')+'v2' #版本号,形式如:20151122v2 @runs_once def input_versionid(): #获得用户输入的版本号,以便做版本回滚操作 return prompt('please input project rollback version ID:',default='') @task @runs_once def tar_source(): #打包本地项目主目录,并将压缩包存储到本地压缩包目录 with lcd(env.project_dev_source): local('tar cf %s.tar.gz .' % (env.project_tar_source + env.project_pack_name)) print(green('creating source package success!')) @task def put_package(): #上传任务函数 print(yellow('start put package...')) with settings(warn_only=True): #当在远程机器上执行命令,出现错误时,是否退出 #if env.deploy_project_root+env.deploy_release_dir : run('rm -rf %s' %(env.deploy_project_root+env.deploy_release_dir)) run('mkdir %s' %(env.deploy_project_root+env.deploy_release_dir)) #创建项目发布目录 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 confirm('put file failed,continue?',default=False): #直接回车相当于false abort('Aborting file put task!') with cd(env.deploy_full_path): #成功解压后删除压缩包 run('tar xf %s.tar.gz' %(env.project_pack_name)) run('rm -rf %s.tar.gz' %(env.project_pack_name)) print(green('put & remove 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 -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('rollback success!')) @task def go(): #自动化发布函数入口 print('') print(white('----------start executing : all hosts is %s') % env.all_hosts) print(white('-----------------------------------------------------------------------------------------')) print('') try: tar_source() put_package() make_symlink() except Exception as e: print(red('Error:'+str(e))) finally: print(white('-----------------------------------------------------------------------------------------')) print(white('ALL tasks is complete!')) </code>
运行结果为:
<code> [root@sta fabric]# fab -f pub_source.py go [192.168.1.86] Executing task 'go' ----------start executing : all hosts is ['192.168.1.86', '192.168.1.104'] ----------------------------------------------------------------------------------------- [localhost] local: tar cf /data/dev/releases/release.tar.gz . creating source package success! start put package... [192.168.1.86] run: rm -rf /data/www/feiyu/releases [192.168.1.86] run: mkdir /data/www/feiyu/releases [192.168.1.86] run: mkdir 20151122v2 [192.168.1.86] put: /data/dev/releases/release.tar.gz -> /data/www/feiyu/releases/20151122v2/release.tar.gz [192.168.1.86] run: tar xf release.tar.gz [192.168.1.86] run: rm -rf release.tar.gz put & remove package success! update current symlink! [192.168.1.86] run: rm -rf /data/www/feiyu/current [192.168.1.86] run: ln -s /data/www/feiyu/releases/20151122v2 /data/www/feiyu/current make symlink success! ----------------------------------------------------------------------------------------- ALL tasks is complete! [192.168.1.104] Executing task 'go' ----------start executing : all hosts is ['192.168.1.86', '192.168.1.104'] ----------------------------------------------------------------------------------------- start put package... [192.168.1.104] run: rm -rf /data/www/feiyu/releases [192.168.1.104] run: mkdir /data/www/feiyu/releases [192.168.1.104] run: mkdir 20151122v2 [192.168.1.104] put: /data/dev/releases/release.tar.gz -> /data/www/feiyu/releases/20151122v2/release.tar.gz [192.168.1.104] run: tar xf release.tar.gz [192.168.1.104] run: rm -rf release.tar.gz put & remove package success! update current symlink! [192.168.1.104] run: rm -rf /data/www/feiyu/current [192.168.1.104] run: ln -s /data/www/feiyu/releases/20151122v2 /data/www/feiyu/current make symlink success! ----------------------------------------------------------------------------------------- ALL tasks is complete! Done. Disconnecting from 192.168.1.104... done. Disconnecting from 192.168.1.86... done. </code>
此脚本只是初步完成的,还在不断完善中,欢迎继续关注!