欢迎访问田飞雨的博客,如果想一起学习linux请加此群! linux学习小群    

系统批量运维管理器Fabric详解

python struggling 1602次浏览 已收录 0个评论

logo
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>

此脚本只是初步完成的,还在不断完善中,欢迎继续关注!



DevOps-田飞雨 》》转载请注明源地址
喜欢 (1)or分享 (0)
发表我的评论
取消评论
*

表情 贴图 加粗 链接 删除线 居中 斜体 签到

Hi,您需要填写昵称和邮箱!

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址