前阵子要用脚本远程重启linux系统,开始用shell脚本没有实现,后面用pexpect实现了,后面发现pexpect太麻烦,又用了paramiko来实现,最近看了一下一个更强大的远程工具fabric,也有更好的实现方式。

这里记录一下:

摘自https://www.cnblogs.com/zhoujinyi/p/6023839.html

背景:

      关于Fabric的介绍,可以看官网说明。简单来说主要功能就是一个基于Python的服务器批量管理库/工具,Fabric 使用 ssh(通过 paramiko 库)在多个服务器上批量执行任务、上传、下载。在使用Fabric之前,都用Python的paramiko模块来实现需求,相比之后发现Fabric比paramiko模块强大很多。具体的使用方法和说明可以看官方文档介绍。下面写类一个用paramiko(apt-get install python-paramiko)封装的远程操作类的模板: 

fabric执行远程Linux命令
#!/usr/bin/python
# -*- encoding: utf-8 -*-

import paramiko
import sys

reload(sys)
sys.setdefaultencoding('utf8')

class Remote_Ops():
    def __init__(self,hostname,ssh_port,username='',password=''):
        self.hostname = hostname
        self.ssh_port = ssh_port
        self.username = username
        self.password = password

#密码登入的操作方法
    def ssh_connect_exec(self,cmd):
        try:
            ssh_key = paramiko.SSHClient()
            ssh_key.set_missing_host_key_policy(paramiko.AutoAddPolicy())
            ssh_key.connect(hostname=self.hostname, port=self.ssh_port, username=self.username, password=self.password, timeout=10)
#            paramiko.util.log_to_file('syslogin.log')
        except Exception, e:
            print('Connect Error:ssh %s@%s: %s' % (self.username, self.hostname, e))
            exit()
        stdin, stdout, stderr = ssh_key.exec_command(cmd,get_pty=True)
#切换root
        stdin.write(self.password+'\n') 
        stdin.flush()
        err_list = stderr.readlines()
        if len( err_list ) > 0:
            print 'ERROR:' + err_list[0]
            exit()
#        print stdout.read()
        for item in stdout.readlines()[2:]:
            print item.strip()
        ssh_key.close()
#ssh登陆的操作方法
    def ssh_connect_keyfile_exec(self,file_name,cmd):
        try:
            ssh_key = paramiko.SSHClient()
            ssh_key.set_missing_host_key_policy(paramiko.AutoAddPolicy())
            ssh_key.connect(hostname=self.hostname, port=self.ssh_port, key_filename=file_name, timeout=10)
#            paramiko.util.log_to_file('syslogin.log')
        except Exception, e:
            print e
            exit()
        stdin, stdout, stderr = ssh_key.exec_command(cmd)
        err_list = stderr.readlines()
        if len( err_list ) > 0:
            print 'ERROR:' + err_list[0]
            exit()
        for item in stdout.readlines():
            print item.strip()
        ssh_key.close()
    
if __name__ == '__main__':
#密码登陆的操作方法:
    test = Remote_Ops('10.211.55.11', 22, 'zjy', 'zhoujinyi')
    test.ssh_connect_exec('sudo ls -lh /var/lib/mysql/')
#ssh key登陆的操作方法:(需要到root下运行)
    file_name = '/var/root/.ssh/id_rsa'
    test1 = Remote_Ops('10.211.55.11', 22)
    test1.ssh_connect_keyfile_exec(file_name,'apt-get update')
    
fabric执行远程Linux命令

关于更多的paramiko信息可以看官方文档python运维之paramikopython远程连接paramiko 模块。本文将要介绍的是Fabric的使用方法。

说明:

1.安装

$ pip install fabric
OR
$ sudo apt-get install fabric

2.参数(fab -h)

fabric执行远程Linux命令
~$ fab -h                                                                                                                              
Usage: fab [options] <command>[:arg1,arg2=val2,host=foo,hosts='h1;h2',...] ...   #命令行的使用方法

Options:
  -h, --help            show this help message and exit
  -d NAME, --display=NAME
                        print detailed info about command NAME
  -F FORMAT, --list-format=FORMAT
                        formats --list, choices: short, normal, nested
  -I, --initial-password-prompt
                        Force password prompt up-front
  --initial-sudo-password-prompt
                        Force sudo password prompt up-front
  -l, --list            print list of possible commands and exit                  #显示出可以执行的命令函数名
  --set=KEY=VALUE,...   comma separated KEY=VALUE pairs to set Fab env vars
  --shortlist           alias for -F short --list
  -V, --version         show program's version number and exit
  -a, --no_agent        don't use the running SSH agent
  -A, --forward-agent   forward local agent to remote end
  --abort-on-prompts    abort instead of prompting (for password, host, etc)      #出现提示就中指,如密码、主机提示
  -c PATH, --config=PATH
                        specify location of config file to use
  --colorize-errors     Color error output                                        #输出颜色错误
  -D, --disable-known-hosts
                        do not load user known_hosts file
  -e, --eagerly-disconnect
                        disconnect from hosts as soon as possible
  -f PATH, --fabfile=PATH                                                          #指定fab执行的文件,默认是fabfile.py
                        python module file to import, e.g. '../other.py'
  -g HOST, --gateway=HOST                                                          #指定堡垒机(中转机)的地址
                        gateway host to connect through
  --gss-auth            Use GSS-API authentication
  --gss-deleg           Delegate GSS-API client credentials or not
  --gss-kex             Perform GSS-API Key Exchange and user authentication
  --hide=LEVELS         comma-separated list of output levels to hide              #output隐藏的等级设置,多个等级逗号分隔
  -H HOSTS, --hosts=HOSTS                                                          #指定操作的服务器,多个用逗号分隔
                        comma-separated list of hosts to operate on
  -i PATH               path to SSH private key file. May be repeated.             #指定私钥文件
  -k, --no-keys         don't load private key files from ~/.ssh/
  --keepalive=N         enables a keepalive every N seconds                        #
  --linewise            print line-by-line instead of byte-by-byte
  -n M, --connection-attempts=M
                        make M attempts to connect before giving up
  --no-pty              do not use pseudo-terminal in run/sudo
  -p PASSWORD, --password=PASSWORD                                                #指定远程登陆的密码包括sudo
                        password for use with authentication and/or sudo
  -P, --parallel        default to parallel execution method                      #指定是否并行执行的
  --port=PORT           SSH connection port                                       #指定ssh的默认端口
  -r, --reject-unknown-hosts                                                      #指定拒绝的主机
                        reject unknown hosts
  --sudo-password=SUDO_PASSWORD                                                   #指定sudo密码
                        password for use with sudo only
  --system-known-hosts=SYSTEM_KNOWN_HOSTS
                        load system known_hosts file before reading user
                        known_hosts
  -R ROLES, --roles=ROLES                                                         #指定role,以role来区分不同执行函数 
                        comma-separated list of roles to operate on
  -s SHELL, --shell=SHELL                                                         #指定的shell的执行环境,
                        specify a new shell, defaults to '/bin/bash -l -c'
  --show=LEVELS         comma-separated list of output levels to show             #指定显示output的等级,多个用逗号分隔
  --skip-bad-hosts      skip over hosts that can't be reached                     #指定跳过不能到达的主机
  --skip-unknown-tasks  skip over unknown tasks                                   #指定跳过不识别的执行函数
  --ssh-config-path=PATH                                                          #指定ssh配置文件的路径
                        Path to SSH config file
  -t N, --timeout=N     set connection timeout to N seconds                       #指定连接超时时间
  -T N, --command-timeout=N                                                       #指定远程命令超时时间
                        set remote command timeout to N seconds 
  -u USER, --user=USER  username to use when connecting to remote hosts           #指定远程登陆用户
  -w, --warn-only       warn, instead of abort, when commands fail                #指定命令错误发出警告而不是中止
  -x HOSTS, --exclude-hosts=HOSTS
                        comma-separated list of hosts to exclude                  #指定排除的主机
  -z INT, --pool-size=INT
                        number of concurrent processes to use in parallel mode    #指定并发线程的数量
fabric执行远程Linux命令

使用

①:命令行接口

~$ fab -u zjy -p zhoujinyi -H 10.211.55.9,10.211.55.11 -- 'ls -lh /tmp/'  

效果:

fabric执行远程Linux命令
[10.211.55.9] Executing task '<remainder>'
[10.211.55.9] run: ls -lh /tmp/
[10.211.55.9] out: 总用量 16K
[10.211.55.9] out: -rw-rw-r-- 1 zjy zjy  853 11月 10 18:42 change_pwd.py
[10.211.55.9] out: 

[10.211.55.11] Executing task '<remainder>'
[10.211.55.11] run: ls -lh /tmp/
[10.211.55.11] out: 总用量 12K
[10.211.55.11] out: -rw-rw-r-- 1 zjy     zjy     2.4K 11月 10 18:29 remote_ops.py
[10.211.55.11] out: 
fabric执行远程Linux命令

不推荐使用命令行,最好都写到一个文件脚本里,方便也安全。

②:脚本

注:默认fabric使用一个名为fabfile.py文件里,如:

#!/usr/bin/python
from fabric.api import local, lcd

def lsfab():
    with lcd('/tmp/'):
        local('ls')

效果:

[localhost] local: ls
com.apple.launchd.ESurbfatee    com.apple.launchd.ykbSkcZdfZ    mykey.txt            parallels_crash_dumps

Done.

如果写到其他文件则需要通过-f来指定执行:

~$ mv fabfile.py ttt.py
~$ fab -f ttt.py lsfab

③:参数

定义的执行函数里带参数:

#!/usr/bin/python
from fabric.api import *

def hello(name,age):
    print "hello,%s,%s" %(name,age)

带参数的执行函数执行:

 ~$ fab hello:zhoujy,123
hello,zhoujy,123

Done.

④:模块说明

fabric执行远程Linux命令
1:from fabric.api import *
local    #执行本地命令,如local('uname -s')
lcd      #切换本地目录,如lcd('/home')
cd       #切换远程目录,如cd('/var/logs')
run      #执行远程命令,如run('free -m')
sudo     #sudo方式执行远程命令,如sudo('/etc/init.d/httpd start')
put      #上次本地文件导远程主机,如put('/home/user.info','/data/user.info')
get      #从远程主机下载文件到本地,如:get('/data/user.info','/home/user.info')
prompt   #获得用户输入信息,如:prompt('please input user password:')
confirm  #获得提示信息确认,如:confirm('Test failed,Continue[Y/N]?')
reboot   #重启远程主机,如:reboot()
@task    #函数修饰符,标识的函数为fab可调用的,非标记对fab不可见,纯业务逻辑
@runs_once   #函数修饰符,标识的函数只会执行一次,不受多台主机影响
@roles() #运行指定的角色组里,通过env.roledefs里的定义
2:from fabric.colors import * print blue(text) print cyan(text) print green(text) print magenta(text) print red(text) print white(text) print yellow(text) 3:
...
用到再补充 ...
fabric执行远程Linux命令

基础实例说明

实例1: 本地操作

fabric执行远程Linux命令
from fabric.api import *

def lsfab():
    with lcd('/tmp/'):  #本地切换目录
        local('ls')     #本地执行命令

def host_name():
    local('uname -s')   #本地执行命令
fabric执行远程Linux命令

-l查看可以执行的命令函数:

~$ fab -f fab_ops.py -l
Available commands:

    host_name
    lsfab

可以执行上面2个执行函数,执行:

~$ fab -f fab_ops.py host_name
[localhost] local: uname -s
Darwin

Done.

上面2个函数合并,并且对外只显示一个执行入口函数(@task):

fabric执行远程Linux命令
from fabric.api import *

def lsfab():
    with lcd('/tmp/'):
        local('ls')

def host_name():
    local('uname -s')

@task
def go():
    lsfab()
    host_name()
fabric执行远程Linux命令

执行:

fabric执行远程Linux命令
~$ fab -f fab_ops.py go
[localhost] local: ls
com.apple.launchd.ESurbfatee    com.apple.launchd.ykbSkcZdfZ    parallels_crash_dumps
[localhost] local: uname -s
Darwin

Done.
fabric执行远程Linux命令

实例2:远程操作,env变量

fabric执行远程Linux命令
from fabric.api import * 

#配置远程服务器
env.hosts = [
'10.211.55.9',
'10.211.55.11'
]
#端口
env.port = '22'
#用户
env.user = 'zjy'
#密码,远程服务器密码都一样
env.password = 'zhoujinyi'


def lsfab():
    with cd('/tmp/'):   #远程切换目录
        run('ls')       #远程命令运行

def host_name():
    run('uname -s')

@task
def go():
    lsfab()
    host_name()
fabric执行远程Linux命令

执行看到的信息:执行的函数,命令和命令的输出结果。

fabric执行远程Linux命令
~$ fab -f fab_ops.py go              
[10.211.55.9] Executing task 'go'
[10.211.55.9] run: ls
[10.211.55.9] out: 1  2  3
[10.211.55.9] out: 

[10.211.55.9] run: uname -s
[10.211.55.9] out: Linux
[10.211.55.9] out: 

[10.211.55.11] Executing task 'go'
[10.211.55.11] run: ls
[10.211.55.11] out: a  b  c  mongodb-27017.sock
[10.211.55.11] out: 

[10.211.55.11] run: uname -s
[10.211.55.11] out: Linux
[10.211.55.11] out: 

Done.
Disconnecting from 10.211.55.11... done.
Disconnecting from 10.211.55.9... done.
fabric执行远程Linux命令

远程机器的密码不一致,怎么配置?这时可以用env.passwords来替换env.password:注意格式:user@ip:pwd

env.passwords = {
    'zjy@10.211.55.9:22' : 'zjy',
    'zjy@10.211.55.11:22': 'zhoujinyi',
}

实例3:如何让不同服务器组执行不同的操作?如DB和WEB服务器各自执行自己的操作。这里需要用env.roledefs来定义角色组,根据不同的roles来使用execute进行不同的操作。

fabric执行远程Linux命令
#!/usr/bin/python
# -*- encoding: utf-8 -*-

from fabric.api import * 

#定义角色,操作一致的服务器可以放在一组。因为服务器的用户端口不一样,需要在role里指定用户、IP和端口
env.roledefs = {
    'dbserver':['zjy@10.211.55.9:22','zjy@10.211.55.11:22'],
    'webserver':['zhoujy@192.168.200.25:221'],
}

#密码,远程服务器密码不一致时使用,格式user@host:port:pwd
env.passwords = {
    'zjy@10.211.55.9:22' : 'zjy',
    'zjy@10.211.55.11:22': 'zhoujinyi',
    'zhoujy@192.168.200.25:221':'123456',
}

@task                             #入口
@roles('dbserver')                #角色修饰符
def get_memory():
    run('free -m')

@task
@roles('webserver')
def mkfile_task():
    with cd('/home/zhoujy/'):
        run('touch xxxx.log')
fabric执行远程Linux命令

执行效果:执行get_memory函数,在dbserver中的主机上执行,mkfile_touch函数则在webserver中的主机上执行。

通过env.roledefsenv.passwords指定好了用户名、端口和密码,这时上面2个函数合并,并且对外只显示一个执行函数(@task),还要注意的是因为各自的执行函数处于不同的roles下执行的,要放到一个函数里执行,需要添加:execute(),这样可以在一个执行函数里操作多个远程的机器。 

fabric执行远程Linux命令
...
...

@task
def go():
    execute(get_memory)
    execute(mkfile_task)
fabric执行远程Linux命令

效果:

View Code

实例4:多台服务器并行执行,@parallel

fabric执行远程Linux命令
#!/usr/bin/python
# -*- encoding: utf-8 -*-

from fabric.api import * 

#要是各个服务器的端口、用户名不一样,就不能配置env.port、env.user了,需要在env.hosts中设置端口:用户@IP:端口 env.hosts = ['zjy@10.211.55.9:22','zjy@10.211.55.11:22','zhoujy@192.168.200.25:221'] #密码,远程服务器密码不一致时使用,格式user@host:port:pwd env.passwords = { 'zjy@10.211.55.9:22' : 'zjy', 'zjy@10.211.55.11:22': 'zhoujinyi', 'zhoujy@192.168.200.25:221':'123456', } @task #入口 @parallel def get_memory(): run('free -m')
fabric执行远程Linux命令

执行效果:执行函数同时在多个主机上运行,加快执行效率

fabric执行远程Linux命令
[zjy@10.211.55.9:22] Executing task 'get_memory'
[zjy@10.211.55.11:22] Executing task 'get_memory'
[zhoujy@192.168.200.25:221] Executing task 'get_memory'
[zhoujy@192.168.200.25:221] run: free -m
[zjy@10.211.55.11:22] run: free -m
[zjy@10.211.55.9:22] run: free -m
[zjy@10.211.55.9:22] out:              total       used       free     shared    buffers     cached
[zjy@10.211.55.9:22] out: Mem:           990        749        240          0         17        136
[zjy@10.211.55.9:22] out: -/+ buffers/cache:        595        395
[zjy@10.211.55.9:22] out: Swap:         1021          0       1021
[zjy@10.211.55.9:22] out: 

[zjy@10.211.55.11:22] out:              total       used       free     shared    buffers     cached
[zjy@10.211.55.11:22] out: Mem:          3949        943       3006          0         13        230
[zjy@10.211.55.11:22] out: -/+ buffers/cache:        699       3249
[zjy@10.211.55.11:22] out: Swap:         1021          0       1021
[zjy@10.211.55.11:22] out: 

[zhoujy@192.168.200.25:221] out:              total       used       free     shared    buffers     cached
[zhoujy@192.168.200.25:221] out: Mem:          3993       2747       1245          0        464       1271
[zhoujy@192.168.200.25:221] out: -/+ buffers/cache:       1011       2981
[zhoujy@192.168.200.25:221] out: Swap:            0          0          0
[zhoujy@192.168.200.25:221] out: 


Done.
fabric执行远程Linux命令

相关文章: