Python自动化运维工具-Fabric部署及使用总结
使用shell命令进行复杂的运维时,代码往往变得复杂难懂,而使用python脚本语言来编写运维程序,就相当于开发普通的应用一样,所以维护和扩展都比较简单,更重要的是python运维工具fabric能自动登录其他服务器进行各种操作,这种实现使用shell是很难做到的,但是使用fabric实现就很简单,所以对于程序员的日常运维部署,建议使用python编写脚本。Fabric是基于Python实现的SSH命令行工具,简化了SSH的应用程序部署及系统管理任务,它提供了系统基础的操作组件,可以通过 SSH 的方式与远程服务器进行自动化交互, 实现本地或远程shell命令,包括:命令执行、文件上传、下载及完整执行日志输出等功能。Fabric在Paramiko的基础上做了更高一层的封装,操作起来会更加简单。Fabric官网地址为:http://www.fabfile.org/
一. Fabric安装
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 | Linux下默认有python环境,安装fabric有两种方式: 一是通过pip方式安装; 而是通过fabric源码方式安装. 一般选择pip方式安装, 安装过程如下: 先安装一些依赖 [root@kevin ~] # yum install make gcc gcc-c++ python-devel python-setuptools -y 安装pip 首先下载py文件:https: //bootstrap .pypa.io /get-pip .py 或者百度云盘下载地址:https: //pan .baidu.com /s/1o7KylCm 提取密码:eucx [root@kevin ~] # cat /etc/redhat-release CentOS Linux release 7.5.1804 (Core) [root@kevin ~] # python -V Python 2.7.5 [root@kevin ~] # wget https://bootstrap.pypa.io/get-pip.py [root@kevin ~] # chmod 755 get-pip.py [root@kevin ~] # python get-pip.py Collecting pip Downloading https: //files .pythonhosted.org /packages/c2/d7/90f34cb0d83a6c5631cf71dfe64cc1054598c843a92b400e55675cc2ac37/pip-18 .1-py2.py3-none-any.whl (1.3MB) 100% |████████████████████████████████| 1.3MB 56kB /s Collecting wheel Downloading https: //files .pythonhosted.org /packages/ff/47/1dfa4795e24fd6f93d5d58602dd716c3f101cfd5a77cd9acbe519b44a0a9/wheel-0 .32.3-py2.py3-none-any.whl Installing collected packages: pip, wheel Successfully installed pip-18.1 wheel-0.32.3 接着使用pip安装fabric [root@kevin ~] # pip install fabric 稍等一会就安装完毕了,这时输入fab就会弹出对应的选项 [root@kevin ~] # fab --version Fabric 2.4.0 Paramiko 2.4.2 Invoke 1.2.0 [root@kevin ~] # fab --help Usage: fab [--core-opts] task1 [--task1-opts] ... taskN [--taskN-opts] Core options: --complete Print tab-completion candidates for given parse remainder. --hide=STRING Set default value of run() 's ' hide' kwarg. --no-dedupe Disable task deduplication. --print-completion-script=STRING Print the tab-completion script for your preferred shell ( bash |zsh|fish). --prompt- for -login-password Request an upfront SSH-auth password prompt. --prompt- for -passphrase Request an upfront SSH key passphrase prompt. --prompt- for - sudo -password Prompt user at start of session for the sudo .password config value. --write-pyc Enable creation of .pyc files. -c STRING, --collection=STRING Specify collection name to load. -d, --debug Enable debug output. -D INT, --list-depth=INT When listing tasks, only show the first INT levels. -e, -- echo Echo executed commands before running. -f STRING, --config=STRING Runtime configuration file to use. -F STRING, --list- format =STRING Change the display format used when listing tasks. Should be one of: flat (default), nested, json. -h [STRING], --help[=STRING] Show core or per-task help and exit . -H STRING, --hosts=STRING Comma-separated host name(s) to execute tasks against. -i, --identity Path to runtime SSH identity (key) file . May be given multiple times . -l [STRING], --list[=STRING] List available tasks, optionally limited to a namespace. -p, --pty Use a pty when executing shell commands. -r STRING, --search-root=STRING Change root directory used for finding task modules. -S STRING, -- ssh -config=STRING Path to runtime SSH config file . -V, --version Show version and exit . -w, --warn-only Warn, instead of failing, when shell commands fail. ======================================================= 温馨提示: 如果安装的是pip3, 则使用 "pip3 install fabric3" 安装fabric ======================================================= |
二. Fabric 使用
Fabric命令说明
1) fab命令格式
1 2 3 4 | fab是fabric的命令行入口 命令的格式为: # fab [options] <command>[:arg1,arg2=val2,host=foo,hosts='h1;h2',...] ... |
2) fab命令常用参数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 | # fab --help 查看帮助 常用参数 -l 显示定义好的任务函数名 -f 指定fab入口文件,默认入口文件名为fabfile.py.. 即指定fabfile文件 -g 指定网关(中转)设备,即HOST逗号分隔要操作的主机, 比如堡垒机环境,填写堡垒机IP即可. -H 指定目标主机,多台主机用‘,’号分隔 -p 远程账号的密码,fab执行时默认使用root账户 -P 以异步并行方式运行多主机任务,默认为串行运行 -R 指定role(角色),以角色名区分不同业务组设备 -t 设置设备连接超时时间(秒) -T 设置远程主机命令执行超时时间(秒) -w 当命令执行失败,发出警告,而非默认中止任务。 其他参数: -- set =KEY=VALUE,... 逗号分隔,设置环境变量 --shortlist 简短打印可用命令 -c PATH 指定本地配置文件 -D 不加载用户known_hosts文件 -i PATH 指定私钥文件 -k 不加载来自~/. ssh 下的私钥文件 --port=PORT 指定SSH连接端口 -R ROLES 根据角色操作,逗号分隔 -s SHELL 指定新shell,默认是 '/bin/bash -l -c' --show=LEVELS 以逗号分隔的输出 -- ssh -config-path=PATH SSH配置文件路径 -T N 设置远程命令超时时间,单位秒 -u USER 连接远程主机用户名 -x HOSTS 以逗号分隔排除主机 -z INT 并发进程数 例1: 通过远程主机查询172.16.50.45 (该主机的root密码为123456)的主机名 [root@kevin ~] # fab -f fabtest.py -p 123456 -H 172.16.50.45 -- 'hostname' 例2: 本地执行命令 [root@kevin ~] # vim fabtest.py from fabric.api import local def command (): local ( 'ls' ) [root@kevin ~] # fab -f fabtest.py command [localhost] local : ls fabfile.py fabfile.pyc tab.py tab.pyc Done. 例3: 远程执行命令 [root@kevin ~] # vim fabtest.py from fabric.api import run def command (): run( 'ls' ) [root@kevin ~] # fab -f fabtest.py -H 192.168.1.120 -u user command [192.168.1.120] Executing task 'command' [192.168.1.120] run: ls [192.168.1.120] Login password for 'user' : [192.168.1.120] out: access.log a.py [192.168.1.120] out: Done. Disconnecting from 192.168.1.120... done . 如果在多台主机执行,只需要-H后面的IP以逗号分隔即可。 例4: 给脚本函数传入位置参数 [root@kevin ~] # vim fabfile.py from fabric.api import run def hello(name= "world" ): print( "Hello %s!" % name) [root@kevin ~] # fab -H localhost hello [localhost] Executing task 'hello' Hello world! Done. [root@kevin ~] # fab -H localhost hello:name=Python [localhost] Executing task 'hello' Hello Python! Done. 例5: 主机列表组 [root@kevin ~] # vim fabfile.py from fabric.api import run, env env .hosts = [ 'root@192.168.1.120:22' , 'root@192.168.1.130:22' ] env .password = '123.com' env .exclude_hosts = [ 'root@192.168.1.120:22' ] # 排除主机 def command (): run( 'ls' ) [root@kevin ~] # fab command env 作用是定义fabfile全局设定,类似于变量。还有一些常用的属性: 例6: 定义角色分组 [root@kevin ~] # vim install.py from fabric.api import run, env env .roledefs = { 'web' : [ '192.168.1.10' , '192.168.1.20' ], 'db' : [ '192.168.1.30' , '192.168.1.40' ] } env .password = '123' @roles( 'web' ) def task1(): run( 'yum install httpd -y' ) @roles( 'db' ) def task2(): run( 'yum install mysql-server -y' ) def deploy(): execute(task1) execute(task2) [root@kevin ~] # fab -f install.py deploy 例7: 上传目录到远程主机 [root@kevin ~] # vim haha.py from fabric.api import * env .hosts = [ '192.168.1.120' ] env .user = 'user' env .password = '123.com' def task(): put( '/root/abc' , '/home/user' ) run( 'ls -l /home/user' ) [root@kevin ~] # fab -f haha.py task 例8: 从远程主机下载目录 [root@kevin ~] # vim heihei.py from fabric.api import * env .hosts = [ '192.168.1.120' ] env .user = 'user' env .password = '123.com' def task(): get( '/home/user/b' , '/opt' ) local ( 'ls -l /opt' ) [root@kevin ~] # fab -f heihei.py task 例9: 打印颜色,有助于关键地方醒目 [root@kevin ~] # vim bobo.py from fabric.colors import * def show(): print green( 'Successful.' ) print red( 'Failure!' ) print yellow( 'Warning.' ) [root@kevin ~] # fab -f bobo.py show |
3) fabfile文件的编写 (默认的文件名称为fabfile)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 | fab命令是结合fabfile.py文件(其他文件通过-f filename 参数来引用)来搭配使用的。fab的部分命令行参数还能通过相应的方法来代替。 先来看一个小例子 [root@kevin ~] # cat fabfile.py #!/usr/bin/env python from fabric.api import run #定义一个任务函数,通过run方法实现远程执行"uname -s"命令 def host_type(): run( 'uname -s' ) [root@kevin ~] # fab -H localhost host_type [localhost] Executing task 'host_type' [localhost] run: uname -s [localhost] Login password for 'devops' : [localhost] out: Linux [localhost] out: Done. Disconnecting from localhost... done . 其中,必须要明白的是, fab命令引用的默认文件名fabfile.py! 如果使用的是默认文件名称, 则fab执行命令中就不需要跟文件名. 如果使用非默认文件名称,比如这里不是fabfile.py, 而是host_type.py 文件, 则需要通过 "-f" 来指定: [root@kevin ~] # fab -H localhost -f host_type.py host_type 如果目标主机未配置密钥认证信任,将会提示输入目标主机对应账号登录密码。 再来看一个小例子 [root@kevin ~] # vim fabric.py #!/usr/bin/python # -*- coding:utf-8 -*- from fabric.api import * # 设置服务器登录参数 env .roledefs = { # 操作一致的放一组,一组执行同一个操作 'servers1' :[ 'root@linux2:22' ,], # 第二组 'servers2' :[ 'root@linux3:22' ,] } # 本机操作 def localtask(): local ( '/usr/local/nginx/nginx' ) # servers1服务器组操作 @roles( 'servers1' ) def task1(): run( '/usr/local/tomcat/bin/startup.sh' ) # servers2 服务器组操作 @roles( 'servers2' ) def task2(): run( '/usr/local/tomcat/bin/startup.sh' ) # 执行任务 def doworks(): execute(localtask) execute(task1) execute(task2) 以上Fabric配置,实现的目的是: 简单的在本地启动nginx服务器, 在linux1和linux2上启动了tomcat服务器, 为了接受nginx服务器的代理,这里专门使用分组的方式为了适应机器比较多的集群的需要; 另外这里没有设置服务器的密码,一是为了服务器的安全;而是集群间建议设置 ssh 免密登录,脚本就不用设置密码了; 方法doworks执行的就是最终汇总的任务; 开始执行 [root@kevin ~] # fab -f fabric.py doworks |
4) fabfile全局属性 (env对象)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 | fabfile之 env 对象的作用是定义fabfile的全局设定,支持多个属性,包含目标主机、用户名、密码、等角色. env 各属性说明如下: evn.host: 定义目标主机,可以用IP或主机名表示,以Python的列表形式定义,如evn.hosts[ '192.168.56.133' , '192.168.56.134' ]。 env .exclude_hosts: 排除指定主机,如 env .exclude_hosts=[ '192.168.56.133' ]。 env .user: 定义用户名,如 env .user= "root" 。 env .port: 定义目标主机端口,默认为22,如 env .port= "22" 。 env .password: 定义密码,如 env .password= '1234567' 。 env .passwords: 与password功能一样,区别在于不同主机不同密码的应用场景,需要注意的是,配置passwords是需配置用户、主机、端口等信息; env .gateway: 定义网关(中转、堡垒机)IP,如 env .gateway = '192.168.56.1' 。 env .deploy_release_dir: 自定义全局变量,格式: env .+ "变量名称" ,如 env .deploy_release_dir、 env .age、 env .sex等。 env .roledefs: 定义角色分组,比如web组与db组主机区分开来; 比如 [root@kevin ~] # vim fabfile.py .......... env .passwords = { 'root@192.168.56.131:22' : '1234567' , 'root@192.168.56.132:22' : '1234567' , 'root@192.168.56.133:22' : '1234567' , 'root@192.168.56.134:22' : '1234567' } [root@kevin ~] # vim fabfile.py .......... env .roledefs = { 'webservers' :[ '192.168.56.131' , '192.168.56.132' , '192.168.56.133' ], 'dbserver' :[ '192.168.56.134' , '192.168.56.135' ] } env .roledefs的使用方法实例: [root@kevin ~] # vim fabfile.py .......... env .roledefs = { 'webserver' :[ '192.168.1.21' , '192.168.1.22' ], 'dbserver' :[ '192.168.1.25' , '192.168.1.26' ]} #引用分组时使用python装饰器方式来进行,如: @roles( 'webserver' ) def webtask(): run( '/usr/local/nginx/sbin/nginx' ) @roles( 'webserver' , 'dbserver' ) def publictask(): run( 'uptime' ) 引用时使用Python修饰符的形式进行,角色修饰符下面的任务函数为其作用域,下面来看一个示例: [root@kevin ~] # vim fabfile.py .......... @roles( 'webservers' ) def webtask(): run( '/etc/init.d/nginx start' ) @roles( 'dbservers' ) def dbtask(): run( '/etc/init.d/mysql start' ) @roles( 'webservers' , 'dbservers' ) def pubclitasj(): run( 'uptime' ) def deploy(): execute(webtask) execute(dbtask) execute(pubclitask) 在命令执行fab deploy就可以实现不同角色执行不同的任务函数了。 |
5) Fabric常用API
1 2 3 4 5 6 7 8 9 10 11 12 | Fabric提供了一组简单但功能强大的fabric.api命令集,简单地调用这些API就能完成大部分应用场景需求。Fabric常用方法及说明如下: local 执行本地命令,如: local ( 'uname -s' ); 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' ); prompt 获得用户输入信息,如:prompt( 'please input user password:' ); confirm 获得提示信息确认,如:confirm( "Tests failed. Continue[Y/N]?" ); reboot 重启远程主机,如:reboot(); @task 函数修饰符,标识的函数为fab可调用的,非标记对fab不可见,纯业务逻辑; @runs_once 函数修复符,标识的函数只会执行一次,不受多台主机影响。 |
6) Fabric应用示例说明
示例一: 查看本地和远程主机信息
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 | 查看本地信息 本示例调用 local ()方法执行本地(主控端)命令,添加 "@runs_once" 修饰符保证该任务函数只执行一次。调用run()方法执行远程命令。 [root@kevin ~] # vim fabric1.1.py #!/usr/bin/env python from fabric.api import * env .user = 'devops' env .hosts = [ 'localhost' ] env .password = '1234567' @runs_once #查看本地系统信息,当有多台主机时只运行一次 def local_task(): #本地任务函数 local ( "uname -a" ) 通过fab命令调用local_task任务函数运行结果如下: [root@kevin ~] # fab -f fabric1.1.py local_task [localhost] Executing task 'local_task' [localhost] local : uname -a Linux devops-virtual-machine 4.15.0-20-generic #21-Ubuntu SMP Tue Apr 24 06:16:15 UTC 2018 x86_64 x86_64 x86_64 GNU/Linux Done. 查看远程主机信息 [root@kevin ~] # vim fabric1.2.py #!/usr/bin/env python from fabric.api import * env .user = 'root' env .hosts = [ '192.168.56.11' ] env .password = '1234567' def remote_task(): with cd ( '/root' ): #"with"的作用是让后面的表达式的语句继承当前状态,实现"cd /root/ && ls -l'的效果 run( 'ls -l' ) 调用remote_task任务函数运行结果如下: [root@kevin ~] # fab -f fabric1.2.py local_task [192.168.56.11] Executing task 'remote_task' [192.168.56.11] run: ls -l [192.168.56.11] out: total 4 [192.168.56.11] out: -rw-------. 1 root root 1273 May 29 11:47 anaconda-ks.cfg [192.168.56.11] out: Done. Disconnecting from 192.168.56.11... done . 如果将上面两个文件的需求, 放在一起 [root@kevin ~] # vim fabric1.py #!/usr/bin/env python # -*- encoding: utf-8 -*- from fabric.api import * env .user = 'root' env .hosts = [ '192.168.1.22' ] env .password = '123456' @runs_once #查看本地系统信息,当有多台主机时只运行一次 def local_task(): #本地任务函数 local ( 'uname -a' ) def remote_task(): with cd ( '/var/logs' ): #with的作用是让后面的表达式语句继承当前状态,实现:cd /var/logs && ls -l的效果 run( 'ls -l' ) [root@kevin ~] # fab -f fabric1.py local_task [root@kevin ~] # fab -f fabric1.py remote_task |
示例二:动态获取远程目录列表
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 | 本示例使用 "@task'修复符标志入口函数go()对外部可见,配合" @runs_once"修饰符接受用户输入,最后调用worktask()任务函数实现远程命令执行。 [root@kevin ~] # vim fabric2.py #!/usr/bin/env python from fabric.api import * env .user = 'root' env .hosts = [ '192.168.56.11' , '192.168.56.12' ] env .password = '1234567' @runs_once #主机遍历过程中,只有第一台触发此函数 def input_raw(): return prompt( "Please input directory name:" ,default= "/home" ) def worktask( dirname ): run( "ls -l " + dirname ) @task #限定只有go函数对fab命令可见 def go(): getdirname = input_raw() worktask(getdirname) 解释说明: 该示例实现了一个动态输入远程目录名称,再获取目录列表的功能,由于我们只要求输入一次,在显示所有主机上该目录的列表信息,调用一个子函数input_raw()同时配置@runs_once修复符来达到此目的。 执行结果如下: [root@kevin ~] # fab -f fabric2.py go [192.168.56.11] Executing task 'go' Please input directory name: [ /home ] /root [192.168.56.11] run: ls -l /root [192.168.56.11] out: total 4 [192.168.56.11] out: -rw-------. 1 root root 1273 May 29 11:47 anaconda-ks.cfg [192.168.56.11] out: [192.168.56.12] Executing task 'go' [192.168.56.12] run: ls -l /root [192.168.56.12] out: total 4 [192.168.56.12] out: -rw-------. 1 root root 1273 May 29 11:59 anaconda-ks.cfg [192.168.56.12] out: Done. Disconnecting from 192.168.56.11... done . Disconnecting from 192.168.56.12... done . |
示例三: 网关模式文件上传与执行
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 | 本示例通过Fabric的 env 对象定义网关模式,即俗称的中转、堡垒机环境。定义格式为 "env.gateway='192.168.56.11'" ,其中IP“192.168.56.11”为堡垒机IP, 再结合任务韩素实现目标主机文件上传与执行的操作。 [root@kevin ~] # vim fabric3.py #!/usr/bin/env python from fabric.api import * from fabric.context_managers import * from fabric.contrib.console import confirm env .user = 'root' env .gateway = '192.168.56.11' #定义堡垒机IP,作为文件上传、执行的中转设备 env .hosts = [ '192.168.56.12' , '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' } l_pack_path = "/home/install/nginx-1.6.3.tar.gz" #本地安装包路径 r_pack_path = "/tmp/install" #远程安装包路径 @task def put_task(): run( "mkdir -p /tmp/install" ) with settings(warn_only=True): result = put(l_pack_path,r_pack_path) #上传安装包 if result.failed and not confirm( "put file failed, Continue[Y/N]?" ): abort( "Aborint file put task!" ) @task def run_task(): #执行远程命令,安装nginx with cd (r_pack_path): run( "tar -xvf nginx-1.6.3.tar.gz" ) with cd ( "nginx-1.6.3/" ): #使用with继续继承/tmp/install目录位置状态 run( "./nginx_install.sh" ) @task def go(): #上传、安装 put_task() run_task() 如下命令运行结果, 默认为串行运行 [root@kevin ~] # fab -f fabric3.py go [192.168.56.12] Executing task 'go' [192.168.56.12] run: mkdir -p /tmp/install [192.168.56.12] put: /home/install/nginx-1 .6.3. tar .gz -> /tmp/install/nginx-1 .6.3. tar .gz [192.168.56.12] run: tar -xvf nginx-1.6.3. tar .gz ..... ..... ..... [192.168.56.12] out: cp conf /nginx .conf '/usr/local/nginx/conf/nginx.conf.default' [192.168.56.12] out: test -d '/usr/local/nginx/logs' || mkdir -p '/usr/local/nginx/logs' [192.168.56.12] out: test -d '/usr/local/nginx/logs' || mkdir -p '/usr/local/nginx/logs' [192.168.56.12] out: test -d '/usr/local/nginx/html' || cp -R html '/usr/local/nginx' [192.168.56.12] out: test -d '/usr/local/nginx/logs' || mkdir -p '/usr/local/nginx/logs' [192.168.56.12] out: make [1]: Leaving directory ` /tmp/install/nginx-1 .6.3' [192.168.56.12] out: nginx: the configuration file /usr/local/nginx/conf/nginx .conf syntax is ok [192.168.56.12] out: nginx: configuration file /usr/local/nginx/conf/nginx .conf test is successful [192.168.56.12] out: [192.168.56.13] Executing task 'go' [192.168.56.13] run: mkdir -p /tmp/install [192.168.56.13] put: /home/install/nginx-1 .6.3. tar .gz -> /tmp/install/nginx-1 .6.3. tar .gz [192.168.56.13] run: tar -xvf nginx-1.6.3. tar .gz .... .... .... [192.168.56.13] out: cp conf /nginx .conf '/usr/local/nginx/conf/nginx.conf.default' [192.168.56.13] out: test -d '/usr/local/nginx/logs' || mkdir -p '/usr/local/nginx/logs' [192.168.56.13] out: test -d '/usr/local/nginx/logs' || mkdir -p '/usr/local/nginx/logs' [192.168.56.13] out: test -d '/usr/local/nginx/html' || cp -R html '/usr/local/nginx' [192.168.56.13] out: test -d '/usr/local/nginx/logs' || mkdir -p '/usr/local/nginx/logs' [192.168.56.13] out: make [1]: Leaving directory ` /tmp/install/nginx-1 .6.3' [192.168.56.13] out: nginx: the configuration file /usr/local/nginx/conf/nginx .conf syntax is ok [192.168.56.13] out: nginx: configuration file /usr/local/nginx/conf/nginx .conf test is successful [192.168.56.13] out: Done. Disconnecting from 192.168.56.11... done . Disconnecting from 192.168.56.12... done . Disconnecting from 192.168.56.13... done . 再如下运行结果, 加P参数为异步并行执行结果 [root@kevin ~] # fab -Pf fabric3.py go [192.168.56.12] Executing task 'go' [192.168.56.13] Executing task 'go' [192.168.56.12] run: mkdir -p /tmp/install [192.168.56.13] run: mkdir -p /tmp/install [192.168.56.12] put: /home/install/nginx-1 .6.3. tar .gz -> /tmp/install/nginx-1 .6.3. tar .gz [192.168.56.13] put: /home/install/nginx-1 .6.3. tar .gz -> /tmp/install/nginx-1 .6.3. tar .gz [192.168.56.12] run: tar -xvf nginx-1.6.3. tar .gz .... .... .... [192.168.56.12] out: nginx-1.6.3 /html/index .html [192.168.56.12] out: nginx-1.6.3 /README [192.168.56.12] out: nginx-1.6.3 /nginx_install .sh [192.168.56.12] out: nginx-1.6.3 /configure [192.168.56.12] out: [192.168.56.12] run: . /nginx_install .sh [192.168.56.13] run: tar -xvf nginx-1.6.3. tar .gz [192.168.56.13] out: nginx-1.6.3/ [192.168.56.13] out: nginx-1.6.3 /src/ .... .... .... [192.168.56.12] out: make [1]: Leaving directory ` /tmp/install/nginx-1 .6.3' [192.168.56.12] out: nginx: the configuration file /usr/local/nginx/conf/nginx .conf syntax is ok [192.168.56.12] out: nginx: configuration file /usr/local/nginx/conf/nginx .conf test is successful [192.168.56.12] out: .... .... ... [192.168.56.13] out: make [1]: Leaving directory ` /tmp/install/nginx-1 .6.3' [192.168.56.13] out: nginx: the configuration file /usr/local/nginx/conf/nginx .conf syntax is ok [192.168.56.13] out: nginx: configuration file /usr/local/nginx/conf/nginx .conf test is successful [192.168.56.13] out: |
示例四: 文件打包, 上传与校验
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 | 我们时常做一些文件包分发的工作,实施步骤一般是先压缩打包,在批量上传至目标服务器,最后做一致性校验。 本示例通过put()方法实现文件的上传,通过对比本地与远程主机文件的md5,最终实现文件一致性校验。 [root@kevin ~] # vim fabric4.py #!/usr/bin/env python from fabric.api import * from fabric.context_managers import * from fabric.contrib.console import confirm env .user = 'root' env .hosts = [ '192.168.56.12' , '192.168.56.13' ] env .passwords = { 'root@192.168.56.12:22' : '1234567' , 'root@192.168.56.13:22' : '1234567' , } @runs_once def tar_task(): #本地打包任务函数,只执行一次 with lcd( '/home/devops/devops' ): local ( "tar -zcf devops.tar.gz *" ) @task def put_task(): #上传文件任务函数 run( "mkdir -p /root/devops" ) with cd ( "/root/devops" ): with settings(warn_only=True): #put(上传)出现异常时继续执行,非终止 result = put( "/home/devops/devops/devops.tar.gz" , "/root/devops/devops.tar.gz" ) if result.failed and not confirm( "put file failed.Continue[Y/N]?" ): abort( "Aborting file put task!" ) #出现异常时,确认用户是否继续,(Y继续) @task def check_task(): #校验文件任务函数 with settings(warn_only=True): #本地local命令需要配置capture=True才能捕获返回值 lmd5 = local ( "md5sum /home/devops/devops/devops.tar.gz" ,capture=True). split ( ' ' )[0] rmd5 = run( "md5sum /root/devops/devops.tar.gz" ). split ( ' ' )[0] if lmd5 == rmd5: #对比本地及远程文件md5信息 prompt( "OK" ) else : prompt( "ERROR" ) @task def go(): tar_task() put_task() check_task() 执行命令, 运行结果如下:(提示此程序不支持-P参数并行执行、如需并行执行,程序需要做调整). 如果只打包, 则 "fab -f fabric4.py tar_task" , 如果只上传, 则 "fab -f fabric4.py put_task" [root@kevin ~] # fab -f fabric4.py go [192.168.56.12] Executing task 'go' [localhost] local : tar -zcf devops. tar .gz * [192.168.56.12] run: mkdir -p /root/devops [192.168.56.12] put: /home/devops/devops/devops . tar .gz -> /root/devops/devops . tar .gz [localhost] local : md5sum /home/devops/devops/devops . tar .gz [192.168.56.12] run: md5sum /root/devops/devops . tar .gz [192.168.56.12] out: a1cf2be82647cbed0d41514bd80373de /root/devops/devops . tar .gz [192.168.56.12] out: OK [192.168.56.13] Executing task 'go' [192.168.56.13] run: mkdir -p /root/devops [192.168.56.13] put: /home/devops/devops/devops . tar .gz -> /root/devops/devops . tar .gz [localhost] local : md5sum /home/devops/devops/devops . tar .gz [192.168.56.13] run: md5sum /root/devops/devops . tar .gz [192.168.56.13] out: a1cf2be82647cbed0d41514bd80373de /root/devops/devops . tar .gz [192.168.56.13] out: OK Done. Disconnecting from 192.168.56.12... done . Disconnecting from 192.168.56.13... done . |
示例五: 部署LNMP业务服务环境
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 | 本示例通过 env .roledefs定义不同主机角色,在使用 "@roles('webservers')" 修复符绑定到对应的任务函数,实现不同角色主机的部署差异。 [root@kevin ~] # vim fabric5.py #!/usr/bin/env python 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) 执行命令,结果如下: [root@kevin ~] # fab -Pf fabric5.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 .repo Install epel ntp.... [192.168.56.12] run: wget -O /etc/yum .repos.d /epel .repo http: //mirrors .aliyun.com /repo/epel-7 .repo Install 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. |
示例六: 分享一个生产环境代码包发布管理的配置
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 | 程序生产环境的发布是业务上线的最后一个环境,要求具备源码打包、发布、切换、回滚、版本管理等功能。 本示例实现了这一套流程功能,其中版本切换与回滚使用了Linux下的软链接实现。 [root@kevin ~] # vim fabric6.py #!/usr/local/env python 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软链接做切换,管理员的版本发布、回滚操作用户无感知。 [root@kevin ~] # fab -f fabric6.py go |
示例七: 分享一个自动化部署 Django 项目的配置
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 | [root@kevin ~] # vim fabric7.py # -*- coding: utf-8 -*- # 文件名要保存为 fabfile.py from __future__ import unicode_literals from fabric.api import * # 登录用户和主机名: env .user = 'root' # 如果没有设置,在需要登录的时候,fabric 会提示输入 env .password = 'youpassword' # 如果有多个主机,fabric会自动依次部署 env .hosts = [ 'www.example.com' ] TAR_FILE_NAME = 'deploy.tar.gz' def pack(): "" " 定义一个pack任务, 打一个 tar 包 : return : "" " tar_files = [ '*.py' , 'static/*' , 'templates/*' , 'vue_app/' , '*/*.py' , 'requirements.txt' ] exclude_files = [ 'fabfile.py' , 'deploy/*' , '*.tar.gz' , '.DS_Store' , '*/.DS_Store' , '*/.*.py' , '__pycache__/*' ] exclude_files = [ '--exclude=\'%s\'' % t for t in exclude_files] local ( 'rm -f %s' % TAR_FILE_NAME) local ( 'tar -czvf %s %s %s' % (TAR_FILE_NAME, ' ' . join (exclude_files), ' ' . join (tar_files))) print( '在当前目录创建一个打包文件: %s' % TAR_FILE_NAME) def deploy(): "" " 定义一个部署任务 : return : "" " # 先进行打包 pack() # 远程服务器的临时文件 remote_tmp_tar = '/tmp/%s' % TAR_FILE_NAME run( 'rm -f %s' % remote_tmp_tar) # 上传tar文件至远程服务器, local_path, remote_path put(TAR_FILE_NAME, remote_tmp_tar) # 解压 remote_dist_base_dir = '/home/python/django_app' # 如果不存在, 则创建文件夹 run( 'mkdir -p %s' % remote_dist_dir) # cd 命令将远程主机的工作目录切换到指定目录 with cd (remote_dist_dir): print( '解压文件到到目录: %s' % remote_dist_dir) run( 'tar -xzvf %s' % remote_tmp_tar) print( '安装 requirements.txt 中的依赖包' ) # 我使用的是 python3 来开发 run( 'pip3 install -r requirements.txt' ) remote_settings_file = '%s/django_app/settings.py' % remote_dist_dir settings_file = 'deploy/settings.py' % name print( '上传 settings.py 文件 %s' % settings_file) put(settings_file, remote_settings_file) nginx_file = 'deploy/django_app.conf' remote_nginx_file = '/etc/nginx/conf.d/django_app.conf' print( '上传 nginx 配置文件 %s' % nginx_file) put(nginx_file, remote_nginx_file) # 在当前目录的子目录 deploy 中的 supervisor 配置文件上传至服务器 supervisor_file = 'deploy/django_app.ini' remote_supervisor_file = '/etc/supervisord.d/django_app.ini' print( '上传 supervisor 配置文件 %s' % supervisor_file) put(supervisor_file, remote_supervisor_file) # 重新加载 nginx 的配置文件 run( 'nginx -s reload' ) run( 'nginx -t' ) # 删除本地的打包文件 local ( 'rm -f %s' % TAR_FILE_NAME) # 载入最新的配置文件,停止原有进程并按新的配置启动所有进程 run( 'supervisorctl reload' ) # 执行 restart all,start 或者 stop fabric 都会提示错误,然后中止运行 # 但是服务器上查看日志,supervisor 有重启 # run('supervisorctl restart all') 执行 pack 任务 [root@kevin ~] # fab -f fabric7.py pack 执行 deploy 任务 [root@kevin ~] # fab -f fabric7.py deploy |
示例八: 代码的自动化部署
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 | [root@kevin ~] # vim fabric8.py #coding=utf-8 from fabric.api import local , abort, settings, env , cd , run from fabric.colors import * from fabric.contrib.console import confirm env .hosts = [ "root@115.28.×××××" ] env .password = "×××××" def get_git_status(): git_status_result = local ( "git status" , capture=True) if "无文件要提交,干净的工作区" not in git_status_result: print red( "****当前分支还有文件没有提交" ) print git_status_result abort( "****已经终止" ) def local_unit_test(): with settings(warn_only=True): test_result = local ( "python manage.py test" ) if test_result.failed: print test_result if not confirm(red( "****单元测试失败,是否继续?" )): abort( "****已经终止" ) def server_unit_test(): with settings(warn_only=True): test_result = run( "python manage.py test" ) if test_result.failed: print test_result if not confirm(red( "****单元测试失败,是否继续?" )): abort( "****已经终止" ) def upload_code(): local ( "git push origin dev" ) print green( "****代码上传成功" ) def deploy_at_server(): print green( "****ssh到服务器进行下列操作" ) with cd ( "/var/www/××××××" ): #print run("pwd") print green( "****将在远程仓库下载代码" ) run( "git checkout dev" ) get_git_status() run( "git pull origin dev" ) print green( "****将在服务器上运行单元测试" ) server_unit_test() run( "service apache2 restart" , pty=False) print green( "****重启apache2成功" ) print green( "********代码部署成功********" ) def deploy(): get_git_status() local ( "git checkout dev" , capture=False) print green( "****切换到dev分支" ) get_git_status() print green( "****将开始运行单元测试" ) local_unit_test() print green( "****单元测试完成,开始上传代码" ) upload_code() deploy_at_server() fabric可以将自动化部署或者多机操作的命令固化到一个脚本里,从而减少手动的操作。上面是今天第一次接触这东西后写的,确实很实用。 运行 [root@kevin ~] # fab -ff abric8.py deploy 主要逻辑就是将本地的dev分支跑单元测试,然后提交到服务器, ssh 登陆到服务器,然后pull下来,再跑单元测试,然后重启apache2。 这个写的还是比较简单的。 |
===============这里贴出之前线上环境使用过的一个Fabric自动化配置===============
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 | 1) 通过Fabric配置的自动化python上线脚本(包括回滚脚本): [work@qd- op -zhongkong op ]$ cat xcspam-celery.py from fabric.api import * from fabric.context_managers import * import datetime env .hosts=[ 'qd-vpc-op-rule01' ] def antiwater(): with cd ( '/app/release' ): date =datetime.datetime.now().strftime( "%Y%m%d%H%M%S" ) repo= 'http://git.kevinweb.com/zuiyou_server/xcspam.git' ; run( 'git clone --depth=1 %s' % repo) newNmae= "xcspam" + "-" + date run( 'mv xcspam %s ' % newNmae) with cd ( '/app/web/xcspam' ): newRelease=run( 'ls /app/release/ |tail -1f' ) run( 'cp -rp /app/release/%s /app/web/xcspam/ ' % newRelease) run( 'unlink bin' ) run( 'ln -sn %s bin' % newRelease) run( 'superctl restart antiwater:*' ) def rollantiwater(): with cd ( '/app/web/xcspam' ): lastrelease=run( 'ls -rtd xcspam* |tail -2 |head -1' ) run( 'unlink bin' ) run( 'ln -sn %s bin' % lastrelease) run( 'superctl restart antiwater:*' ) def report(): with cd ( '/app/release' ): date =datetime.datetime.now().strftime( "%Y%m%d%H%M%S" ) repo= 'http://git.kevinweb.com/zuiyou_server/xcspam.git' ; run( 'git clone --depth=1 %s' % repo) newNmae= "xcspam" + "-" + date run( 'mv xcspam %s ' % newNmae) with cd ( '/app/web/xcspam' ): newRelease=run( 'ls /app/release/ |tail -1f' ) run( 'cp -rp /app/release/%s /app/web/xcspam/ ' % newRelease) run( 'unlink bin' ) run( 'ln -sn %s bin' % newRelease) run( 'superctl restart report' ) def rollreport(): with cd ( '/app/web/xcspam' ): lastrelease=run( 'ls -rtd xcspam* |tail -2 |head -1' ) run( 'unlink bin' ) run( 'ln -sn %s bin' % lastrelease) run( 'superctl restart report' ) def chat(): with cd ( '/app/release' ): date =datetime.datetime.now().strftime( "%Y%m%d%H%M%S" ) repo= 'http://git.kevinweb.com/zuiyou_server/xcspam.git' ; run( 'git clone --depth=1 %s' % repo) newNmae= "xcspam" + "-" + date run( 'mv xcspam %s ' % newNmae) with cd ( '/app/web/xcspam' ): newRelease=run( 'ls /app/release/ |tail -1f' ) run( 'cp -rp /app/release/%s /app/web/xcspam/ ' % newRelease) run( 'unlink bin' ) run( 'ln -sn %s bin' % newRelease) run( 'superctl restart chat' ) def rollchat(): with cd ( '/app/web/xcspam' ): lastrelease=run( 'ls -rtd xcspam* |tail -2 |head -1' ) run( 'unlink bin' ) run( 'ln -sn %s bin' % lastrelease) run( 'superctl restart chat' ) 可以在一个脚本中定义多个上线项目,上线的时候可以选择,如下(回滚的时候选择对应的roll即可): [work@qd- op -zhongkong op ]$ fab -f xcspam-celery.py antiwater [work@qd- op -zhongkong op ]$ fab -f xcspam-celery.py report [work@qd- op -zhongkong op ]$ fab -f xcspam-celery.py chat 2) 脚本2,其实跟上面无异: [work@qd- op -zhongkong op ]$ cat xcspam-consumer.py from fabric.api import * from fabric.context_managers import * import datetime env .hosts=[ 'qd-vpc-op-consumer01' , 'qd-vpc-op-consumer02' ] def xcspam(): with cd ( '/app/release' ): date =datetime.datetime.now().strftime( "%Y%m%d%H%M%S" ) repo= 'http://git.kevinweb.com/zuiyou_server/xcspam.git' ; run( 'git clone --depth=1 %s' % repo) newNmae= "xcspam" + "-" + date run( 'mv xcspam %s ' % newNmae) with cd ( '/app/web/xcspam' ): newRelease=run( 'ls /app/release/ |tail -1f' ) run( 'cp -rp /app/release/%s /app/web/xcspam/ ' % newRelease) run( 'unlink bin' ) run( 'ln -sn %s bin' % newRelease) run( 'superctl restart xcspam:*' ) def rollxcspam(): with cd ( '/app/web/xcspam' ): lastrelease=run( 'ls -rtd xcspam* |tail -2 |head -1' ) run( 'unlink bin' ) run( 'ln -sn %s bin' % lastrelease) run( 'superctl restart xcspam:*' ) def chatxcspam(): with cd ( '/app/release' ): date =datetime.datetime.now().strftime( "%Y%m%d%H%M%S" ) repo= 'http://git.kevinweb.com/zuiyou_server/xcspam.git' ; run( 'git clone --depth=1 %s' % repo) newNmae= "xcspam" + "-" + date run( 'mv xcspam %s ' % newNmae) with cd ( '/app/web/xcspam' ): newRelease=run( 'ls /app/release/ |tail -1f' ) run( 'cp -rp /app/release/%s /app/web/xcspam/ ' % newRelease) run( 'unlink bin' ) run( 'ln -sn %s bin' % newRelease) run( 'superctl restart chatxcspam:*' ) def chatxcspam(): with cd ( '/app/web/xcspam' ): lastrelease=run( 'ls -rtd xcspam* |tail -2 |head -1' ) run( 'unlink bin' ) run( 'ln -sn %s bin' % lastrelease) run('superctl restart chatxcspam:*’) 3) 脚本3 [work@qd- op -zhongkong op ]$ cat xcspam-consumer-all.py from fabric.api import * from fabric.context_managers import * import datetime env .hosts=[ 'qd-vpc-op-consumer01' , 'qd-vpc-op-consumer02' ] def xcspam(): with cd ( '/app/release' ): date =datetime.datetime.now().strftime( "%Y%m%d%H%M%S" ) repo= 'http://git.kevinweb.com/zuiyou_server/xcspam.git' ; run( 'git clone --depth=1 %s' % repo) newNmae= "xcspam" + "-" + date run( 'mv xcspam %s ' % newNmae) with cd ( '/app/web/xcspam' ): newRelease=run( 'ls /app/release/ |tail -1f' ) run( 'cp -rp /app/release/%s /app/web/xcspam/ ' % newRelease) run( 'unlink bin' ) run( 'ln -sn %s bin' % newRelease) run( 'superctl restart xcspam:*' ) def rollxcspam(): with cd ( '/app/web/xcspam' ): lastrelease=run( 'ls -rtd xcspam* |tail -2 |head -1' ) run( 'unlink bin' ) run( 'ln -sn %s bin' % lastrelease) run( 'superctl restart xcspam:*' ) def chatxcspam(): with cd ( '/app/release' ): date =datetime.datetime.now().strftime( "%Y%m%d%H%M%S" ) repo= 'http://git.kevinweb.com/zuiyou_server/xcspam.git' ; run( 'git clone --depth=1 %s' % repo) newNmae= "xcspam" + "-" + date run( 'mv xcspam %s ' % newNmae) with cd ( '/app/web/xcspam' ): newRelease=run( 'ls /app/release/ |tail -1f' ) run( 'cp -rp /app/release/%s /app/web/xcspam/ ' % newRelease) run( 'unlink bin' ) run( 'ln -sn %s bin' % newRelease) run( 'superctl restart chatxcspam:*' ) def chatxcspam(): with cd ( '/app/web/xcspam' ): lastrelease=run( 'ls -rtd xcspam* |tail -2 |head -1' ) run( 'unlink bin' ) run( 'ln -sn %s bin' % lastrelease) run( 'superctl restart chatxcspam:*' ) def all(): with cd ( '/app/release' ): date =datetime.datetime.now().strftime( "%Y%m%d%H%M%S" ) repo= 'http://git.kevinweb.com/zuiyou_server/xcspam.git' ; run( 'git clone --depth=1 %s' % repo) newNmae= "xcspam" + "-" + date run( 'mv xcspam %s ' % newNmae) with cd ( '/app/web/xcspam' ): newRelease=run( 'ls /app/release/ |tail -1f' ) run( 'cp -rp /app/release/%s /app/web/xcspam/ ' % newRelease) run( 'unlink bin' ) run( 'ln -sn %s bin' % newRelease) run( 'superctl restart all' ) def rollall(): with cd ( '/app/web/xcspam' ): lastrelease=run( 'ls -rtd xcspam* |tail -2 |head -1' ) run( 'unlink bin' ) run( 'ln -sn %s bin' % lastrelease) run( 'superctl restart all' ) 可以根据需求去选择具体对那个项目进行上线,上述脚本定义了两个项目上线,第三个(all)即表示同时上线两个项目。 [work@qd- op -zhongkong op ]$ fab -f xcspam-consumer-all.py xcspam [work@qd- op -zhongkong op ]$ fab -f xcspam-consumer-all.py chatxcspam [work@qd- op -zhongkong op ]$ fab -f xcspam-consumer-all.py all |
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
· 没有源码,如何修改代码逻辑?
· 一个奇形怪状的面试题:Bean中的CHM要不要加volatile?
· [.NET]调用本地 Deepseek 模型
· 一个费力不讨好的项目,让我损失了近一半的绩效!
· 微软正式发布.NET 10 Preview 1:开启下一代开发框架新篇章
· 没有源码,如何修改代码逻辑?
· PowerShell开发游戏 · 打蜜蜂
· 在鹅厂做java开发是什么体验
· WPF到Web的无缝过渡:英雄联盟客户端的OpenSilver迁移实战