前言:
这篇文章主要分为2个部分,一部分是介绍一个python的常用运维工具ansible,二是介绍了基于ansible的原理设计的服务器性能测试自动化平台的实践以及相关的收益成果。
ansible简介
- ansible是近年越来越火的一款运维自动化工具,其主要功能是帮忙运维实现IT工作的自动化、降低人为操作失误、提高业务自动化率、提升运维工作效率,常用于软件部署自动化、配置自动化、管理自动化、系统化系统任务、持续集成等。
- 真正具有批量部署的是ansible所运行的模块,ansible只是提供一种框架。主要包括:
(1) 连接插件connection plugins:负责和被监控端实现通信;
(2) host inventory:指定操作的主机,是一个配置文件里面定义监控的主机;
(3) 各种模块核心模块、command模块、自定义模块;
(4) 借助于插件完成记录日志邮件等功能;
(5) playbook:编写的执行任务的yml脚本
ansible ad-hoc
Ansible命令行执行方式有Ad-Hoc、Ansible-playbook两种方式,Web化执行方式其官方提供了付费产品Tower(10台以内免费),个人的话可以基于其提供的API开发类似的Web化产品。
Ad-Hoc主要用于临时命令的执行,Ansibel-playbook可以理解为Ad-Hoc的集合,通过一定的规则编排在一起。
Ansible <host-pattern> [options]
<host-pattern>是Inventory中定义的主机或主机组,可以为ip、hostname、Inventory中的group组名、具有“.”或“*”或“:”等特殊字符的匹配型字符串,<>表示该选项是必须项,不可忽略。
[options]是Ansible的参数选项,[ ]表示该选项中的参数任选其一
例如:
ansible all -m ping -u tiger
ansible push_node -m copy -a "src=/etc/hosts dest=/tmp/hosts"
ansible push_node -m file -a "dest=/tmp/hosts mode=600"
ansible -i ~/myhosts push_node -m ping
ansible-doc -l //列出支持的模块
ansible-doc ping //模块功能说明
----------------------------------------------------------------------------------------------
ansible-playbook
- playbook意思是“剧本”,是预先编排好的按顺序执行的任务。运行playbook命令:
ansible-playbook myplaybook.yml
一些可选的参数如下:
--ask-vault-pass:加密playbook文件时提示输入密码
--extra-vars=EXTRA_VARS:在Playbook中引入外部变量
-i inventory:指定要读取的Inventory文件.Inventory是Ansible管理主机信息的配置文件,相当于 系统hosts文件的功能,默认存放在/etc/ansible/hosts
--list-tasks:列出所有即将被执行的任务
--limit push_node : 外层限制只对特定主机生效
编写一个playbook的范例:
--- #必须以---开头,类似于shell的 #!/bin/bash
- hosts: all #指定执行任务的主机
gather_facts: false
tasks: #一系列的任务
# file模块主要用于远程主机上的文件操作,创建一个目录nss,如果它不存在
- name: mkdir /opt/tiger/toutiao/app/videoarch/nss on remote
file:
path: /opt/tiger/toutiao/app/videoarch/nss
state: directory
mode: 0755
#下载nss的指定版本包,解压到远程机器上的目录下
- name: get nss scm pkg
get_url:
url: http://d.scm.byted.org/api/v2/download/ceph:toutiao.videoarch.nss_{{nss_version}}.tar.gz
dest: /opt/tiger/toutiao/app/videoarch/nss/toutiao.videoarch.nss_{{nss_version}}.tar.gz
#解压nss的指定版本包,解压到远程机器上的目录下
- name: unarchive nss pkg
unarchive:
copy: no
src: /opt/tiger/toutiao/app/videoarch/nss/toutiao.videoarch.nss_{{nss_version}}.tar.gz
dest: /opt/tiger/toutiao/app/videoarch/nss
执行playbook
ansible-playbook get_nss_pkg.yml --extra-vars "nss_version=1.0.1.512"
ansible-playbook get_nss_pkg.yml --extra-vars "nss_version=1.0.1.512" --limit push_node
ansible-playbook -i ~/myhosts get_nss_pkg.yml --extra-vars "nss_version=1.0.1.512"
ansible-playbook get_nss_pkg.yml --extra-vars "nss_version=1.0.1.512" --list-tasks
----------------------------------------------------------------------------------------------
ansible api调用在服务器压测自动化的实践
【压测自动化方案分析】
- 通过调用ansible模块的api, 也可以执行ad-hoc和playbook的任务
- 单独的playbook只能完成一次任务任务的执行,适合临时的短暂需求
- 通过api调用触发playbook,支持灵活的代码编写,可以通过Python控制上层的业务逻辑,维护资源,测试用例执行调度,测试结果分析等
- 服务器的压测自动化需要实现:
- 测试环境初始化
- 自动部署服务器节点
- 自动修改配置&拓扑
- 自动发压
- 自动启停服务器
- 自动收集分析测试数据
- 自动生成测试报告
Python 调用ansible模块执行playbook的一个范例
#需要导入的相关模块
from ansible import context
from ansible.module_utils.common.collections import ImmutableDict
from ansible.parsing.dataloader import DataLoader
from ansible.vars.manager import VariableManager
from ansible.inventory.manager import InventoryManager
from ansible.executor.playbook_executor import PlaybookExecutor
#执行playbook的一段代码示例 (基于ansible2.8):
def exec_playbook(host_source,extra_vars,pb_path):
loader = DataLoader()
passwords = dict()
inventory = InventoryManager(loader=loader, sources=host_source)
# variable设置
variable_manager = VariableManager(loader=loader, inventory=inventory)
variable_manager._extra_vars = extra_vars
perf_config = get_perf_config()
private_key_file = perf_config[u'private_key_file']
ssh_args = '-o StrictHostKeyChecking=no'
# context参数设置
context.CLIARGS = ImmutableDict(connection='ssh', module_path=None, become=None,become_method=None,forks=5, start_at_task=None,become_user=None, check=False, diff=False, syntax=None,ansible_cfg=None,
private_key_file=private_key_file,ssh_common_args=ssh_args)
playbook_executor = PlaybookExecutor(pb_path, inventory, variable_manager, loader, passwords)
result = playbook_executor.run() #返回0代表run成功
Python + Flask + Ansible API + Web前端实现服务器的压测自动化设计图
如上图所示,是基于ansible的api调用能力,结合python的flask框架和web前端页的开发,实现的一个针对后端服务器的性能测试自动化平台。
通过前端页面可以方便用户选择,触发,浏览和管理服务器上软件的各种版本的测试任务,测试结果记录,测试结果分析等等数据,相当于构建了一个性能测试自动化的控制台。
前端操作触发后端的业务层的接口,由python的flask框架实现。业务上层逻辑主要包括测试任务触发,测试任务状态管理,测试结果分析,测试报警通知等模块。
测试任务触发:底层通过封装ansible的远程控制脚本,通过ansible实现对远端服务器的登录,文件操作,被测软件对应版本的拉取和安装部署,软件对应配置的获取和更新,软件在服务器的集群分布等等,实现测试任务的触发。
测试任务状态管理:测试任务执行完每一步骤,都会触发一个打点操作,在系统记录测试任务的进度。
测试结果分析:测试结果hi分析也是非常复杂的模块。因为对服务器的发压后的性能分析,不仅仅要分析服务器本身的cpu, 内存,网卡带宽等指标,还需要分析服务器的日志,以及业务功能打点在ES中聚合的数据是否符合预期。即要先确认发压是否正确,在此基础上,再检查服务器对当前压力的承受能力。
测试报警通知:测试结果分析完成之后,还要对该报警的预警消息通知给对应的任务触发人,并且还需要针对当前测试的软件版本,压测的目标场景,性能结果数据,风险点和明显不符合预期的点等生成测试报告,对有问题的服务端日志进行过滤上传tos系统,保存日志现场。
性能自动化测试平台的2项能力:
(1)软件的性能准入测试
通过以往的测试数据经验,在该性能自动化平台预先内置一部分性能准入的测试用例,作为测试测试的准入基线用例。这种使用场景非常适合用在上线流程卡点。每一次的软件新版本发布,需要经过功能测试,但是如果不进行性能测试,就会有性能风险。尤其是对于CDN这种分布式节点的服务,单台实例的性能可能影响一个区或者一个城市的用户,而且对于业务高峰的帐量比较猛的情景,很容易产生事故。性能基线的卡点,意思就是,基于历史经验制定出一系列的性能基线数据,并要求,此后发布的每一个新版本,性能数据都必须优于该基线,不可以出现版本性能劣化,才允许上线。
经过一段时间的发版和性能优化之后。性能准入case的基线数据还可以配置化的调整,通过调整,就能够将该软件的性能基线不断上调,不断推动软件性能优化。
(2)软件的性能上限探索
由于该性能自动化测试平台,还支持自定义用例的功能,用户可以根据自己的目标软件版本的测试需要,在平台上新增自定义测试用例,可以将发压范围设置的相对较高,用来探索该软件的性能上限。并且每次自定义的测试用例,都会自动保存,下次无需再手动新建。
同时,对于组里专门从事软件性能优化方向的研发同学,该功能是其测试,探索,和验证优化方向,优化方案和优化效果的一个很好的工具,可以极大地节省研发同学的时间。
性能自动化测试平台的收益:
目前,在投入使用该性能自动测试平台之后,产生了较好的收益。
一是研发在线上测试feature分支的时候,就能通过该自动化平台,测试出自己feature的改动是否对整体软件的性能产生了影响并及时修复,提早发现性能问题;二是在软件上线的发版过程,强制卡性能基线数据,可以阻止性能不符合要求的版本上线;三是以往人工进行性能校验,每个版本都需要QA一周的时间+研发2-3天的跟进时间,成本非常高,有了自动测试平台之后,一个版本的测试1天就可以跑完,并且能自动分析结果,节省了很多时间。
具体的数据收益:成功在线下feature阶段拦截了20+软件性能或者是隐藏较深的功能问题;卡住了7次大的版本发版性能问题;对内存泄露这种问题的定位率达到了100%不遗漏。
附:ansible一些模块使用示例
sudo
CI系统的playbook一般是用tiger用户登录slave机器操作,但有时候需要执行root权限的操作,需要进行sudo,虽然task可以有sudo或sudo_user参数,但是这个方式将来会被淘汰,更推荐的做法是指定become和become_user:
- name: mkdir /var/log/tower/
file:
path: /var/log/tower/
state: directory
mode: 0755
group: tiger
owner: tiger
become: true
become_user: root
注意:光有become_user是不够的,必须指定become为true
copy
从本地上传文件的场景下,copy命令的src可以是绝对或相对路径,如果是相对路径,则是相对于yml文件本身所在目录的路径
dest最好统一用绝对路径,这里有个小坑,如果传的是文件,或某个目录下的所有文件(即src以"/"结尾),dest指定相对路径是可能上传成功的,这时候的相对路径是相对于登录用户的HOME路径(比如CI系统的tiger用户,就是"/home/tiger"),但在传目录的时候,会报错目标目录不存在,可能是个bug,原因未知
copy还有个大坑:mode属性可以用8进制指定权限(如0644),但是directory_mode属性的字符串会按十进制解析,例如0755会被解析为493然后直接拼接到chmod等命令中,会导致命令出现错误,因此后者请直接用十进制来表示
另外由于src和dst的一些语法规定有点复杂(比如末尾是否有"/"会导致行为不同),写代码的时候小心一些,建议:
1 上传之前保证本地的src目录或文件的名称和上传后的期望相同
2 dst写需要上传的目录位置并以"/"结尾,这样会自动创建目标目录
例如要上传本地目录"/tmp/a"到目标机器的/opt/tiger目录下,且名字为b,那么首先应该在本地将a拷贝出一个名为b的目录,即先按需求构建目标机器上的数据,然后指定:
src: /tmp/b
dest: /opt/tiger/
这样b目录就会原样被上传到/opt/tiger/下,若目标机器没有/opt/tiger目录也会自动创建,或者可以通过将目录a下所有文件拷贝到目标位置来实现:
src: /tmp/a/
dest: /opt/tiger/b/
当然如果对目标目录的存在性有要求则应该用全名拷贝(文件的话)或做必要的检查,不过一般这种需求比较少
copy在碰到symbol link的时候,会将其作为实际引用的目录或文件来拷贝,如果只需要将link "copy"到远端,则需要在playbook中用shell执行ln -s,或用tar的方式(tar命令在打包的时候默认会将link本身打入),并保证远端的link有效
copy在拷贝一个文件到远端的时候比较耗时,传文件请尽量用synchronize
synchronize
synchronize用的是rsync,我们的机器都有,可以正常使用,速度比copy快很多
src和dest的格式和copy相同
和copy有区别,需要注意的点:
1 synchronize是在远端启动rsync,接收文件并落盘,而不是scp
2 owner、mode选项的含义和copy不同,请参考文档,直接写src和dest传送的话,mode会保留,owner为目标机器的登录用户
3 设置become root好像没有用(原因没细查),不能操作无权限的目录
4 对于软连接,会传送连接本身,而不是连接对应的文件或目录
注意dest的目录最好预先建好,似乎synchronize只会直接调用mkdir而非mkdir -p,倒数第二级的需要建好才行
script
建议都使用python脚本
脚本中不要假设自己所在的位置等信息,因为是playbook上传到目标机器执行的
虽然官网文档中有executable参数可以指定用什么命令运行,但这个在2.6版本才有,目前的通用做法是在脚本开头写上"#!/usr/bin/python"来制定解释器
script指定脚本文件的时候可以用相对路径,是相对于yml文件所在目录的路径
参考文档:
github地址: https://github.com/ansible/ansible
《Ansible权威指南》李松涛 魏巍 甘捷