Python自动化运维经典案例入门

Python 是一种动态的高级编程语言,语法非常简洁,初学者很容易上手。Python 语言表达力非常强大,三两行代码即可完成其他编程语言可能要写几十上百行的功能,开发效率非常高。因此,它经常作为胶水式语言,在自动化运维等开发领域大显身手。

  • 语法简洁,易于学习
  • 表达力强大,开发效率高
  • 执行效率不高,不擅长大项目

Python能做什么

Python 号称自动化运维领域的编程语言,常见的应用场景均能胜任:

  • 采集性能数据,比如调用 psutils 采集系统和进程指标;
  • 发送数据报表;
    • 调用 xlrdxlwt 生成或处理 Excel 报表;
    • 调用 matplotlib 生成统计图表,例如折线图、直方图、饼图、热力图……
    • 调用 smtplib 发送邮件报告;
  • 自动执行命令;
    • 调用 pexpect 自动执行交互式命令,支持批量执行;
    • 调用 paramiko 登录 SSH 服务器,执行命令或拷贝文件( scp );
    • 调用 ansible 在主机集群上执行 playbook ,类似 ansible 的工具还有 fabric
  • 开发 Web 接口,利用 fastapiflask 等框架实现取数接口;
  • etc

安装第三方包

Python 之所以这么强大,很大程度上得益于它极其丰富的第三方包生态。不管您想做什么,基本都要现成的包!我们直接把包安装好,然后调用就是了。举个例子,想要请求 HTTP 接口,我们可以使用 requests 包。

那么,怎么安装 Python 第三方包呢?答案是 pip 命令,以安装 requests 包为例:

1
pip install requests

调用接口

运维攻城狮经常需要跟各种接口打交道,比如 拉取 或者 提交 一些数据,其中又以 HTTP 接口最为常见。那么,如何用 Python 来调用 HTTP 接口呢?

标准库

Python 内置的标准库,提供了 urllib 包,可以用来发起 HTTP 请求。这是一个简单的例子:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
import json
import urllib.request 

# 待请求URL
url = 'https://fasionchan.com/data/books.json'

# 发起请求,默认为GET
with urllib.request.urlopen(url) as r:
    # 读服务器返回的数据,这个接口为json数据
    body = r.read(102400)

    # 解析json数据
    books = json.loads(body)

    # 数据为一份书单,逐一遍历并输出书名
    for book in books:
        print(book['name'])

这个段代码请求一个 HTTP 接口,获得 JSON 书单数据,然后遍历打印书名。

requests

与其他编程语言相比,urllib 包调用 HTTP 接口的代码已经足够简洁,但这对 Python 来说还远远不够。

HTTP 领域,有一个杀手锏级别的第三方包 requests 必须强烈推荐!引入 requests 后,仅仅一行代码即可完成 HTTP 请求,这是一个等价的例子:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
import requests

# 待请求URL
url = 'https://fasionchan.com/data/books.json'

# 发起GET请求,并解析服务器返回的json数据
books = requests.get(url).json()

# 数据为一份书单,逐一遍历并输出书名
for book in books:
    print(book['name'])

执行命令

运维免不了要经常跟命令打交道,运维攻城狮通常写 Shell 脚本来执行命令,不过 Python 也可以。Python 标准库中的 subprocess 包,就是用来执行命令的。

简单执行

调用 subprocess 包中的 run 函数,一行代码即可执行指定命令:

1
2
3
4
5
import subprocess

# 执行:ls -l
p = subprocess.run(['ls', '-l'])
print('returncode:', p.returncode)

传给 run 函数的参数是一个字符串,或者一个列表,用来描述要执行的命令,以及执行参数。例子中传的是列表,第一项是命令名 ls ,第二项是 ls-l 参数。这个列表相当于 main 函数中的 argv 数组。

处理命令输出

如果想要获取命令输出的内容,我们可以创建一个管道,作为命令的标准输出,然后从管道读取命令输出的内容。这个例子我们执行 uptime 命令获取系统的平均负载:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
import subprocess

with subprocess.Popen(['uptime'], stdout=subprocess.PIPE) as p:
    output = p.stdout.read(1024).decode()
    # 19:40:05 up 664 days,  5:52,  1 user,  load average: 0.00, 0.03, 0.00

    values = [
      float(v)
      for v in output.split('load average:')[1].split(',') # ['0.00', '0.03', '0.00']
    ]

    kvs = zip(
        ('w1', 'w5', 'w15'),
        values,
    ) # [('w1', 0.00), ('w5', 0.03), ('w15', 0.00)]

    print(dict(kvs))

SSH

运维攻城狮也经常要通过 SSH 连接到服务器上执行命令、拷贝文件,Python 也可以帮我们做到!同样,我们站在巨人的肩膀上—— paramiko 包,这样安装:

1
pip install paramiko

执行命令

装好 paramiko 包后,引入 SSHClient 类,即可大展身手了!这是一个简单的例子:

 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
from paramiko.client import (
    SSHClient,
)

# 创建连接对象
client = SSHClient()

# 加载主机指纹,以防伪装
# 默认从~/.ssh/known_hosts文件加载,也通过传参自定义
client.load_system_host_keys()

# 连接到服务器,密码通过password参数指定
# 也可通过SSH密钥进行认证,默认从~/.ssh/加载密钥,参数也支持指定密钥文件
client.connect('10.128.4.19', username='root')

# 执行命令,返回标准输入、标准输出、以及标准错误输出的读写对象
# - 写到stdin的数据,将送到服务器作为命令的标准输入
# - 而命令的标准输出可以通过stdout读取,标准错误输出可以通过stderr读取
stdin, stdout, stderr = client.exec_command('uptime')
if stderr:
    # 处理数据
    pass

# 读取命令输出内容,并打印到屏幕
print(stdout.read(10240).decode())

拷贝文件

paramiko 也提供了 SFTP 功能,可以用来上传或下载文件。不过,运维人员可能更喜欢用 scp 协议。没事,装个 scp 包即可:

1
pip install scp

现在,我们先用 paramiko 来建立 SSH 连接;再通过 scp 包来拷贝文件。例子如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
from paramiko.client import (
    SSHClient,
)
from scp import (
    SCPClient,
)

# 连接到SSH服务器
ssh = SSHClient()
ssh.load_system_host_keys()
ssh.connect('10.128.4.19', username='root')

# 初始化SSH客户端
client = SCPClient(ssh.get_transport())

# 上传文件,当前目录下的test.txt文件上传到服务器/tmp目录下
client.put('test.txt', '/tmp')

# 下载文件,将服务器文件/tmp/test.txt下载到本地,保存为test2.txt
client.get('/tmp/test.txt', 'test2.txt')

操作数据库

开发经常会提一些数据维护需求给运维或 DBA ,因此数据库操作能力不可或缺。借助第三方包,Python 可以操作各种数据库。以 MySQL 为例,我们可以用 pymysql 包:

1
pip install pymysql

我们来看一个简单的例子,这段代码连到 MySQL 数据库执行 SQL 语句查询数据:

 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
import pymysql.cursors

# 连接到MySQL数据库
conn = pymysql.connect(
    host='127.0.0.1',
    port=3306,
    user='root',
    password='123456',
    database='demos',
    # 游标类决定数据返回格式
    cursorclass=pymysql.cursors.DictCursor,
)

# 连接对象是一个上下文管理器
# 执行with内的代码前,Python会执行连接对象的初始化方法
# 执行with内的代码后,Python会执行连接对象的销毁方法
with conn:
    # 获取一个游标对象
    with conn.cursor() as cursor:
        # 准备要执行的SQL语句
        sql = 'select * from kvs'
        # 通过游标对象执行SQL语句
        cursor.execute(sql)
        # 获取执行结果
        records = cursor.fetchall()
        # 遍历并数据每条记录
        for record in records:
            print('{}: {}'.format(record.get('key'), record.get('value')))

注意到,连接数据库时传了 cursorclass 参数指定连接类,它将决定数据的返回格式,默认为列表。举个例子,假设我们有一张键值对表 kvs ,它有 idkeyvalue 三个字段:

id key value
1 one apple
2 two banana
3 three cat

当我们执行上述 SQL 语句时,pymysql 默认返回三条数据,每条都是一个列表:

1
2
3
4
5
[
    [1, "one", "apple"],
    [2, "two", "banana"],
    [3, "three", "cat"]
]

如果将游标类指定为 DictCursor ,那么返回的数据每条都是一个字典( dict ):

1
2
3
4
5
[
    {"id": 1, "key": "one", "value": "apple"},
    {"id": 2, "key": "two", "value": "banana"},
    {"id": 3, "key": "three", "value": "cat"}
]

【小菜学Python】系列文章首发于公众号【小菜学编程】,敬请关注:

【小菜学Python】系列文章首发于公众号【小菜学编程】,敬请关注: