Use shell Command complex operations , Code tends to get complicated and hard to understand , While using python Script language to write operation and maintenance program , It's the same as developing common applications , So maintenance and expansion are relatively simple , what's more python Operation and maintenance tools fabric It can automatically log in to other servers for various operations , This implementation uses shell It's hard to do , But use fabric The implementation is simple , So for the programmer's daily operation and maintenance deployment , It is recommended to use python Write a script .Fabric Is based on Python Realized SSH Command line tools , To simplify the SSH Application deployment and system management tasks , It provides the basic operating components of the system , Can pass SSH Automatic interaction with remote server in the same way , Achieve local or remote shell command , Include : Command execution 、 Upload files 、 Download and complete log output and other functions .

One . Fabric install

Linux There is... By default python Environmental Science , install fabric There are two ways :  One is to pass. pip Way to install ;  But through fabric Source code installation .
General choice pip Way to install ,  The installation process is as follows :
Install some dependencies first
[root@kevin ~]# yum install make gcc gcc-c++ python-devel python-setuptools -y
install pip
First download py file :https://bootstrap.pypa.io/get-pip.py
Or Baidu cloud disk download address :https://pan.baidu.com/s/1o7KylCm       Extract password :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
Then use pip install fabric
[root@kevin ~]# pip install fabric
Just a moment, the installation will be finished , Enter fab The corresponding options will pop up
[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.
=======================================================
reminder :
If you install pip3,  Then use "pip3 install fabric3"  install fabric
=======================================================

Two . Fabric Use

Fabric Command specification
1) fab Command format

fab yes fabric Command line entry for  
The format of the command is :
# fab [options] <command>[:arg1,arg2=val2,host=foo,hosts='h1;h2',...] ...

2) fab Common parameters of commands

# fab --help      view help
 
  Common parameters
-l   Display the defined task function name
-f   Appoint fab Entrance file , The default entry file name is fabfile.py..  Designated fabfile file
-g   Specify the gateway ( transit ) equipment , namely HOST Commas separate the hosts to be operated on ,  Like fortress environment , Fill in the fortress machine IP that will do . 
-H   Specify the target host , For multiple hosts ‘,’ No separation
-p   The password of the remote account ,fab The default is root Account
-P   Running multi host tasks in asynchronous parallel , The default is serial operation
-R   Appoint role( role ), Distinguish different business group devices by role name
-t   Set device connection timeout ( second )
-T   Set the remote host command execution timeout ( second )
-w   When the command fails , warn , Instead of suspending the task by default .
The other parameters :
--set=KEY=VALUE,...      Comma separated , Set the environment variable
--shortlist              Short print commands are available
-c PATH                  Specify the local profile
-D                       Do not load users known_hosts file
-i PATH                  Specify private key file
-k                       Don't load from ~/.ssh The private key file under
--port=PORT              Appoint SSH Connection port
-R ROLES                 Operate according to the role , Comma separated
-s SHELL                 Specify new shell, The default is '/bin/bash -l -c'
--show=LEVELS            Comma separated output
--ssh-config-path=PATH  SSH Profile path
-T N                     Set remote command timeout , Unit second
-u USER                  Connect to remote host user name
-x HOSTS                 Exclude hosts separated by commas
-z INT                   Number of concurrent processes
 
example 1:   Query through remote host 172.16.50.45 ( The host's root The password for 123456) The host name
[root@kevin ~]# fab -f fabtest.py -p 123456 -H 172.16.50.45 -- 'hostname'
example 2:  Local execution orders
[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.
example 3:  Remote execution command
[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.
If executed on multiple hosts , It only needs -H hinder IP Separated by commas .
example 4:   Pass in positional parameters to script functions
[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.
example 5:  Host list group
[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']   #  Exclude the host
def command():
   run('ls')
[root@kevin ~]# fab command
env The function is to define fabfile Global settings , Similar to variables . There are also some common attributes :
example 6:  Define role groups
[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
example 7:  Upload directory to remote host
[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
example 8:  Download directory from remote host
[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
example 9:  Print colors , It helps to make key points stand out
[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 Compilation of documents ( The default file name is fabfile)

fab Command is a combination of fabfile.py file ( Other documents passed through -f filename  Parameter to reference ) To use with .fab Some of the command line parameters can be replaced by corresponding methods .
Let's start with a small example
[root@kevin ~]# cat fabfile.py
#!/usr/bin/env python
from fabric.api import run
 
# Define a task function , adopt run Method to implement remote execution "uname -s" command
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.
 
among , It must be understood that , fab The default file name referenced by the command fabfile.py!
If you are using the default file name ,  be fab There is no need to follow the file name in executing the command .
If you use a non default file name , Not here, for example fabfile.py,  It is host_type.py  file ,  You have to go through "-f" To specify the :
[root@kevin ~]# fab -H localhost -f host_type.py host_type
 
If the target host is not configured with key authentication trust , You will be prompted to enter the login password of the corresponding account of the target host .
 
Let's take another small example
[root@kevin ~]# vim fabric.py
#!/usr/bin/python
# -*- coding:utf-8 -*-
  
 from fabric.api import *
  
 #  Set the server login parameters
 env.roledefs = {
     #  Put in a group with consistent operation , A group performs the same operation
     'servers1':['root@linux2:22',],
     #  The second group
     'servers2':['root@linux3:22',]
 }
  
 #  Local operation
 def localtask():
     local('/usr/local/nginx/nginx')
  
 # servers1 Server group operations
 @roles('servers1')
 def task1():
     run('/usr/local/tomcat/bin/startup.sh')
  
 # servers2  Server group operations
 @roles('servers2')
 def task2():
     run('/usr/local/tomcat/bin/startup.sh')
  
 #  Perform tasks
 def doworks():
     execute(localtask)
     execute(task1)
     execute(task2)
 
 
above Fabric To configure , The goal is :
Simply start locally nginx The server ,  stay linux1 and linux2 Started on tomcat The server ,  In order to accept nginx Proxy for the server , In order to meet the needs of clusters with more machines, the grouping method is specially used here ;
In addition, the password of the server is not set here , One is for the security of the server ; It's suggested to set it between clusters ssh Password free login , Scripts don't have to set passwords ;
Method doworks It's the final summary task ;
 
Start execution
[root@kevin ~]# fab -f fabric.py doworks

4) fabfile Global properties (env object ) 

fabfile And env The purpose of an object is to define fabfile The global settings of , Support multiple properties , Contains the target host 、 user name 、 password 、 Wait for the character .
env The attributes are described below :
evn.host:            Define target host , It can be used IP Or the host name indicates , With Python List form definition of , Such as evn.hosts['192.168.56.133','192.168.56.134'].
env.exclude_hosts:   Exclude the specified host , Such as env.exclude_hosts=['192.168.56.133'].
env.user:            Define user name , Such as env.user="root".
env.port:            Define the target host port , The default is 22, Such as env.port="22".
env.password:        Define password , Such as env.password='1234567'.
env.passwords:       And password Function as , The difference lies in the application scenarios of different hosts and different passwords , It should be noted that , To configure passwords You need to configure users 、 host 、 Port and other information ;
env.gateway:         Define gateway ( transit 、 Fortress machine )IP, Such as env.gateway = '192.168.56.1'.
env.deploy_release_dir:   Custom global variables , Format :env.+" Variable name ", Such as env.deploy_release_dir、env.age、env.sex etc. .
env.roledefs:        Define role groups , such as web Group and db Group hosts are distinguished ;
such as
[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 Example of how to use :
[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']}
# Use when grouping references python Decorator way to carry out , Such as :
@roles('webserver')
def webtask():
    run('/usr/local/nginx/sbin/nginx')
@roles('webserver','dbserver')
def publictask():
    run('uptime')
Use... When quoting Python Modifier in the form of , The task function under the role modifier is its scope , Let's look at an example :
[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)
In command execution fab deploy Different roles can perform different task functions .

5) Fabric Commonly used API

Fabric Provides a simple but powerful set of fabric.api Command set , Simply call these API Can complete most of the application scenario requirements .Fabric The common methods and explanations are as follows :
local      Execute local orders , Such as :local('uname -s');
lcd        Switch local directory , Such as :lcd('/home');
cd         Switch remote directory , Such as :cd('/data/logs');
run        Execute remote command , Such as :run('free -m');
sudo      sudo Mode to execute a remote command , Such as :sudo('/etc/init.d/httpd start');
put        Transfer local files to remote host , Such as :put('/home/user.info','/data/user.info');
prompt     Get user input , Such as :prompt('please input user password:');
confirm    Get prompt confirmation , Such as :confirm("Tests failed. Continue[Y/N]?");
reboot     Restart the remote host , Such as :reboot();
@task      Function modifiers , The function identified is fab Callable , Unmarked pairs fab invisible , Pure business logic ;
@runs_once  Function fix , The identified function will only execute once , Not affected by multiple hosts .

6) Fabric The application example shows  

Example 1 :  View local and remote host information

 Check local information
This example call local() Method to execute local ( Main control end ) command , add to "@runs_once" The modifier guarantees that the task function is executed only once . call run() Method to execute a remote command .
[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              # View local system information , Run only once when there are multiple hosts
def local_task():       # Local task functions
    local("uname -a")
adopt fab Command invocation local_task The task function runs as follows :
[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.
View remote host information
[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" The function of is to make the statement of the following expression inherit the current state , Realization "cd /root/ && ls -l' The effect of
        run('ls -l')
call remote_task The task function runs as follows :
[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.
If the requirements of the above two documents ,  Put together
[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   # View local system information , Run only once when there are multiple hosts
def local_task():   # Local task functions
    local('uname -a')
    
def remote_task():
    with cd('/var/logs'):   #with The function of is to let the following expression statement inherit the current state , Realization :cd /var/logs  && ls -l The effect of
        run('ls -l')
[root@kevin ~]# fab -f fabric1.py local_task
[root@kevin ~]# fab -f fabric1.py remote_task

Example 2 : Get the remote directory list dynamically

 This example uses "@task' Fix flag entry function go() Visible to the outside , coordination "@runs_once" The modifier accepts user input , Last call worktask() The task function implements remote command execution .
[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           # During host traversal , Only the first one triggers this function
def input_raw():
    return prompt("Please input directory name:",default="/home")
def worktask(dirname):
    run("ls -l "+dirname)
@task           # The limit is go Function pair fab The command is visible
def go():
    getdirname = input_raw()
    worktask(getdirname)
interpretative statement :
This example implements a dynamic input remote directory name , Then get the function of directory list , Since we only require input once , Display the list information of the directory on all hosts , Call a child function input_raw() Simultaneous configuration @runs_once To do this .
The results are as follows :
[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.

Example 3 : Gateway mode file upload and execution

 This example uses Fabric Of env Object defines the gateway pattern , It is commonly known as transit 、 Fortress environment . The definition format is "env.gateway='192.168.56.11'", among IP“192.168.56.11” For the fortress IP,
Combined with the task, we can upload and execute the target host file .
[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'                           # Define fortress machine IP, Upload as a file 、 Transfer equipment for execution
env.hosts = ['192.168.56.12','192.168.56.13']
env.passwords = {
    'root@192.168.56.11:22':'1234567',                  # Fortress account information
    '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"        # Local installation package path
r_pack_path = "/tmp/install"                            # Remote installation package path
@task
def put_task():
    run("mkdir -p /tmp/install")
    with settings(warn_only=True):
        result = put(l_pack_path,r_pack_path)          # Upload installation package
    if result.failed and not confirm("put file failed, Continue[Y/N]?"):
        abort("Aborint file put task!")
@task
def run_task():                    # Execute remote command , install nginx
    with cd(r_pack_path):
        run("tar -xvf nginx-1.6.3.tar.gz")
        with cd("nginx-1.6.3/"):                     # Use with Continue to inherit /tmp/install Directory location status
            run("./nginx_install.sh")
@task
def go():       # Upload 、 install
    put_task()
    run_task()
The following command runs the result ,  The default is serial operation
[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.
And then the results are as follows ,  Add P The parameter is the result of asynchronous parallel execution
[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:

Example 4 :  collect files , Upload and verify

 We do a lot of package distribution work from time to time , The implementation steps are generally compression and packaging first , In batch upload to the target server , Finally, do consistency check .
This example uses put() Method to upload files , By comparing local and remote host files md5, Finally, the file consistency verification is realized .
[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():             # Local packaging task function , Only once
    with lcd('/home/devops/devops'):
        local("tar -zcf devops.tar.gz  *")
@task
def put_task():                 # Upload file task function
    run("mkdir -p /root/devops")
    with cd("/root/devops"):
        with settings(warn_only=True):                  #put( Upload ) Continue when exception occurs , Non termination
            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!")                        # When something unusual happens , Confirm whether the user continues ,(Y continue )
@task
def check_task():               # Check file task function
    with settings(warn_only=True):
        # Local local The command needs to be configured capture=True To capture the return value
        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:                # Compare local and remote files md5 Information
        prompt("OK")
    else:
        prompt("ERROR")
@task
def go():
    tar_task()
    put_task()
    check_task()
Carry out orders ,  The operation results are as follows :( Prompt that this program does not support -P Parameters are executed in parallel 、 To execute in parallel , The program needs to be adjusted ).  If you just pack ,  be "fab -f fabric4.py tar_task",  If you just upload ,  be "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.

Example 5 :  Deploy LNMP Business service environment

 This example uses env.roledefs Define different host roles , In the use of "@roles('webservers')" The fixer is bound to the corresponding task function , Realize the deployment differences of different role hosts .
[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')                      # Use webtask Task function reference 'webservers' Character fix
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 Task function reference 'dbservers' Character fix
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 The task function references two role modifiers at the same time
def publictask():                          # Deploy the common class environment , Such as epel、ntp etc.
    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)
Carry out orders , give the result as follows :
[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.

Example 6 :  Share a production environment code package release management configuration

 The release of program production environment is the last environment for business online , Source code packaging is required 、 Release 、 Switch 、 Roll back 、 Version management and other functions .
This example implements this set of process functions , Version switching and rollback use Linux Soft link implementation under .
[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/'              # Development server project home directory
env.project_tar_source = '/data/dev/releases/'               # Development server project compressed package storage directory
env.project_pack_name = 'release'                            # Project zip prefix , The file named release.tar.gz
env.deploy_project_root = '/data/www/Lwebadmin/'            # Project production environment master directory
env.deploy_release_dir = 'releases'                         # Project release Directory , Under the home directory
env.deploy_current_dir = 'current'                          # Soft links to the current version of external services
env.deploy_version = time.strftime("%Y%m%d")+"v2"           # Version number
@runs_once
def input_versionid():                                      # Get the version number entered by the user , In order to do version rollback operation
    return prompt("Please input project rollback version ID:",default="")
@task
@runs_once
def tar_source():                                           # Package the local project home directory , And store the compressed package to the local compressed package directory
    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():                                          # Upload task function
    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))           # Create a version catalog
    env.deploy_full_path = env.deploy_project_root + env.deploy_release_dir + "/" + env.deploy_version
    with settings(warn_only=True):                          # Upload the project package to this directory
        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):                          # Delete the compressed package after decompression
        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():                                         # Make a soft link for the current version Directory
    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):                           # Delete soft links , Recreate and specify the soft link source directory , The new version takes effect
        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():                                             # Version rollback task function
    print(yellow("rollback project version"))
    versionid = input_versionid()                           # Get the rollback version number entered by the user
    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))     # Delete soft links , Recreate and specify the soft link source directory , The new version takes effect
    print(green("rollback sucess!"))
@task
def go():               # Automation program version release entry function
    tar_source()
    put_package()
    make_symlink()
#  We need to pay attention to :  In the production environment, point the root directory of the site to "/data/www/Lwebadmin/current", Due to the use Linux Soft link to switch , Administrator's version release 、 The rollback operation is not user aware .
[root@kevin ~]# fab -f fabric6.py go

Example 7 : Share an automated deployment Django Project configuration

[root@kevin ~]# vim fabric7.py
# -*- coding: utf-8 -*-
#  Save the file name as  fabfile.py
 
from __future__ import unicode_literals
from fabric.api import *
 
#  Login user and host name :
env.user = 'root'
#  If not set , When you need to log in ,fabric  Will prompt for
env.password = 'youpassword'
#  If there are multiple hosts ,fabric It will be automatically deployed in turn
env.hosts = ['www.example.com']
 
TAR_FILE_NAME = 'deploy.tar.gz'
 
def pack():
            """
             Define a pack Mission ,  Make a tar package
            :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(' Create a package file in the current directory : %s' % TAR_FILE_NAME)
 
 
def deploy():
            """
             Define a deployment task
            :return:
            """
            #  Package first
            pack()
 
            #  Temporary files for remote servers
            remote_tmp_tar = '/tmp/%s' % TAR_FILE_NAME
            run('rm -f %s' % remote_tmp_tar)
            #  Upload tar File to remote server , local_path, remote_path
            put(TAR_FILE_NAME, remote_tmp_tar)
            #  decompression
            remote_dist_base_dir = '/home/python/django_app'
            #  If it doesn't exist ,  Create a folder
            run('mkdir -p %s' % remote_dist_dir)
 
 # cd  Command to switch the working directory of the remote host to the specified directory  
            with cd(remote_dist_dir):
                        print(' Unzip the file to the directory : %s' % remote_dist_dir)
                        run('tar -xzvf %s' % remote_tmp_tar)
                        print(' install  requirements.txt  Dependency packages in ')
                        #  I'm using  python3  To develop
                        run('pip3 install -r requirements.txt')
                        remote_settings_file = '%s/django_app/settings.py' % remote_dist_dir
                        settings_file = 'deploy/settings.py' % name
                        print(' Upload  settings.py  file  %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(' Upload  nginx  The configuration file  %s' % nginx_file)
                        put(nginx_file, remote_nginx_file)
            
 #  In the subdirectory of the current directory  deploy  Medium  supervisor  Upload the configuration file to the server
            supervisor_file = 'deploy/django_app.ini'
            remote_supervisor_file = '/etc/supervisord.d/django_app.ini'
            print(' Upload  supervisor  The configuration file  %s' % supervisor_file)
            put(supervisor_file, remote_supervisor_file)
            
 #  Reload  nginx  Configuration file for
            run('nginx -s reload')
            run('nginx -t')
            #  Delete the local package file
            local('rm -f %s' % TAR_FILE_NAME)
            #  Load the latest configuration file , Stop the original process and start all processes according to the new configuration
            run('supervisorctl reload')
            #  perform  restart all,start  perhaps  stop fabric  They all prompt for mistakes , And then stop running
            #  But looking at the logs on the server ,supervisor  There is a restart
            # run('supervisorctl restart all')
perform  pack  Mission
[root@kevin ~]# fab -f fabric7.py pack
perform  deploy  Mission
[root@kevin ~]# fab -f fabric7.py deploy

Example 8 :  Automated deployment of code

[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 " No documents to submit , Clean work area " not in git_status_result:
    print red("**** There are still files not submitted in the current branch ")
    print git_status_result
    abort("**** Has terminated ")
 
 
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("**** Unit test failure , Whether or not to continue ?")):
        abort("**** Has terminated ")
 
 
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("**** Unit test failure , Whether or not to continue ?")):
        abort("**** Has terminated ")
 
 
def upload_code():
  local("git push origin dev")
  print green("**** The code was uploaded successfully ")
 
 
def deploy_at_server():
  print green("****ssh Go to the server and do the following ")
  with cd("/var/www/××××××"):
    #print run("pwd")
    print green("**** The code will be downloaded from the remote repository ")
    run("git checkout dev")
    get_git_status()
    run("git pull origin dev")
    print green("**** Unit tests will be run on the server ")
    server_unit_test()
    run("service apache2 restart", pty=False)
    print green("**** restart apache2 success ")
    print green("******** Code deployed successfully ********")
 
 
def deploy():
  get_git_status()
  local("git checkout dev", capture=False)
  print green("**** Switch to dev Branch ")
  get_git_status()
  print green("**** Will start running unit tests ")
  local_unit_test()
  print green("**** Unit testing is complete , Start uploading code ")
  upload_code()
  deploy_at_server()
fabric The commands of automatic deployment or multi machine operation can be solidified into a script , So as to reduce the manual operation . It's written after I first touched it today , It's really practical .
function
[root@kevin ~]# fab -ff abric8.py deploy
The main logic is to put the local dev Branch run unit test , And then submit it to the server ,ssh Log in to the server , then pull Come down , Unit test again , And then restart apache2.
This is a relatively simple one .

=============== Here's a post from the previous online environment Fabric Automatic configuration ===============

1)  adopt Fabric Automation of configuration python Online scripts ( Including rollback scripts ):
[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')
You can define multiple online projects in one script , When you go online, you can choose , as follows ( When rolling back, select the corresponding roll that will do ):
[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)  Script 2, It's the same as above :
[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)  Script 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')
You can choose which project to launch according to your needs , The above script defines two projects to go online , Third (all) That means two projects are launched at the same time .
[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