优化 Ansible 速度
设置 SSH 为长连接
openssh5.6 版本后支持 Multiplexing
检查控制机器的 ssh 版本
ssh -V
假如不是 5.6 版本以上的,可以用下面的办法升级
- 配置 Centos6 系统的 YUM 源(使用 Centos6)
➜ ansible cat /etc/yum.repos.d/openssh.repo
[CentALT]
name=CentALT Packages for Enterprise Linux 6 - $basearch
baseurl=http://mirror.neu.edu.cn/CentALT/6/$basearch/
enable=1
gpgcheck=0
- 执行升级命令
yum update openssh-clients
升级完成后,不必重启任何服务,因为我们的控制机是使用 ssh 的客户端
- 设置 ansible 配置文件
➜ ansible grep sh_args /etc/ansible/ansible.cfg
ssh_args = -C -o ControlMaster=auto -o ControlPersist=10d
# ControlPersist=10d 表示保持长连接 10 天。
# 60s 是 60 秒
设置好后,重新连接一次被控主机,即可让控制主机和被控主机之间建立长连接
- 检查长连接
ss -an
输出中 有 ESTAB 就代表是长连接
同时会在主控机当前用户的家目录下的 .ansibl/cp/ 目录下生成对应的 socket 文件
开启 pipelining
我们知道默认情况下 Ansible 执行过程中会把生成好的本地 python 脚本文件 PUT 到 远端机器。如果我们开启了 ssh 的 pipelining 特性,这个过程就会在 SSH 的会话中进行。
在不通过实际文件传输的情况下执行ansible模块来使用管道特性,从而减少执行远程模块SSH操作次数.如果开启这个设置,将显著提高性能. 然而当使用”sudo:”操作的时候, 你必须在所有管理的主机的/etc/sudoers中禁用’requiretty’.
下面的步骤是实现这个特性的步骤
- 在 ansible.cfg 配置文件中设置 pipelining 为 True
➜ ~ grep pipelining /etc/ansible/ansible.cfg
# Enabling pipelining reduces the number of SSH operations required to
pipelining = True
- 配置被控主机的 /etc/sdoers 文件,添加下面的内容
#Defaults requiretty
开启 accelerate(加速) 模式
如果你能够使用pipelining,它支持大文件,并且 pipelining 几乎在所有的情况下优于加速模式.
加速模式将为了支持那些仍使用红帽企业版 Linux 6 做主控机或因其他环境因素受限制而保留
参考: https://ansible-tran.readthedocs.io/en/latest/docs/playbooks_acceleration.html
设置 Ansible 的执行策略
默认的执行策略是按批并行处理的,假如总共 15 台主机,每次并发 5 个线程执行的策略如下:
h1/h2/h3/h4h5 ---------------------> h6/h7/h8/h9/h10 -----> h11/h12/h13/h14/h15
全部执行完后,进入下一批 依次类推
从 asible2.0 开始,可以通过在 playbook 中设置 strategy 的值改变这策略,改变后的策略,可以前赴后继的对主机进行执行 task,执行模式如下:
假如 h4 主机先执行完,会及时的让 下一个排队的主机进入到 执行的队列中
h1/h2/h3/h4/h5 ------> h1/h2/h3/h6/h5 -------> h1/h2/h3/h6/h7 -----> ...
strategy 默认的值的是 linear ,就是按批并行处理,下面是配置为 free 的方式实例:
---
- hosts: all
strategy: free
gather_facts: no
tasks:
- name: test free mode
ping:
设置 facts 缓存
默认情况下,Ansible 每次执行 playbook 时的第一个 Task 就是 获取每台主机的 facts 信息。假如不需要可以设置
gather_facts = no 关闭,以提高执行 playbook 的效率。
假如想获取 facts 信息,同时又想加速这个 task 的效率,就需要设置 facts 缓存。
缓存 facts 信息可以存档 JSON 文件中,也可以方式 redis 和 memcached 中。
- 首先是可以在 ansible.cfg 文件中设置
grep gathering /etc/ansible/ansible.cfg
gathering = smart
ansible的配置文件中可以修改'gathering'的值为'smart'、'implicit'或者'explicit'。
- smart --> 表示默认收集facts,但facts已有的情况下不会收集,即使用缓存facts;
- implicit --> 表示默认收集facts,要禁止收集,必须使用gather_facts: False;
- explicit --> 则表示默认不收集,要显式收集,必须使用gather_facts: Ture
- 在playbook 中设置
---
- hosts: all
gather_facts: yes # 显式定义收集
gather_facts: no # 显式定义不收集
gather_facts: smart # 显式定义收集
配置缓存的目标
- 缓存到文件
在 ansible.cfg 文件中配置缓存到 file
gather_facts: smart
fact_caching = jsonfile # 缓存到 json 文件
fact_caching_connection = /dev/shm/ansible_fact_cache
fact_caching_timeout = 86400 # 缓存数据时间是一天
fact_caching_connection 是一个放置在可读目录(如果目录不存在,ansible会试图创建它)中的本地文件路径,文件名是 inventory hostname .
- 缓存到 redis(目前不支持远端)
在控制主机上,安装 redis 服务和 python 的 redis 库
➜ ~ yum install redis
➜ ~ pip install redis
在 ansible.cfg 文件中配置缓存到 redis
gathering = smart
fact_caching_timeout = 86400 # 缓存数据时间是一天
fact_caching = redis # 缓存到 json 文件
- 缓存到 memcached
在 ansible.cfg 配置文件中设置如下内容:
gathering = smart
fact_caching_timeout = 86400
fact_caching = memcached
再谈异步(Playbook)
模拟长时间运行(15秒),等待最多45秒,每5秒轮询一次
为了异步启动一个任务,可以指定其最大超时时间以及轮询其状态的频率.如果你没有为 poll 指定值,那么默认的轮询频率是10秒钟:
---
- hosts: all
remote_user: root
tasks:
- name: simulate long running op (15 sec), wait for up to 45 sec, poll every 5 sec
command: /bin/sleep 15
async: 45
poll: 5
async 并没有默认值,如果你没有指定 async 关键字,那么任务会以同步的方式运行,这是Ansible的默认行为.
- 另外,如果你不需要等待任务执行完毕,你可以指定 poll 值为0而启用 “启动并忽略”
---
- hosts: all
remote_user: root
tasks:
- name: simulate long running op, allow to run for 45 sec, fire and forget
command: /bin/sleep 15
async: 45
poll: 0
==对于要求排它锁的操作,如果你需要在其之后对同一资源执行其它任务,那么你不应该对该操作使用”启动并忽略”.比如yum事务.==
- 当你想对 “启动并忽略” 做个变种,改为”启动并忽略,稍后再检查”,你可以使用以下方式执行任务:
---
# Requires ansible 1.8+
- name: 'YUM - fire and forget task'
yum: name=docker-io state=installed
async: 1000
poll: 0
register: yum_sleeper
- name: 'YUM - check on fire and forget task'
async_status: jid={{ yum_sleeper.ansible_job_id }}
register: job_result
until: job_result.finished
retries: 30
==如果 async: 值太小,可能会导致 “稍后检查” 任务执行失败,因为 async_status:: 的临时状态文件还未被写入信息,而”稍后检查”任务就试图读取此文件.==
目录结构
使用官方建议的目录机构来组织很多 role 和 playbook 文件是个很棒的建议。
假如你用 role 封装了 playbook,并且任务依赖文件或者依赖其他的任务时,建议使用目录机构管理。
假如是一个简单的独立任务, 只使用 playbook 文件即可,这样会方便我们在其他地方进行引用。
下面结束官网最佳实战中推荐的目录结构
production # 关于生产环境服务器的清单文件
stage # 关于 stage 环境的清单文件
group_vars/
group1 # 这里我们给特定的组赋值
group2 # ""
host_vars/
hostname1 # 如果系统需要特定的变量,把它们放置在这里.
hostname2 # ""
library/ # 如果有自定义的模块,放在这里(可选)
filter_plugins/ # 如果有自定义的过滤插件,放在这里(可选)
site.yml # 主 playbook,playbook 的统一入口文件
webservers.yml # Web 服务器的 playbook
dbservers.yml # 数据库服务器的 playbook
roles/ # role 存放目录
common/ # common 角色的目录
tasks/ #
main.yml # <-- tasks file can include smaller files if warranted
handlers/ #
main.yml # <-- handlers file
templates/ # <-- files for use with the template resource
ntp.conf.j2 # <------- templates end in .j2
files/ #
bar.txt # <-- files for use with the copy resource
foo.sh # <-- script files for use with the script resource
vars/ #
main.yml # <-- variables associated with this role
defaults/ #
main.yml # <-- default lower priority variables for this role
meta/ #
main.yml # <-- role dependencies 角色的依赖文件
webtier/ # 这些都是和 common 同级的目录,是另外的一些角色
monitoring/ # ""
fooapp/ # ""
定义多环境
在实际的工作中可能会遇到不同环境的机器。比如 生产、存储、开发等
在对这些环境部署的工程中,可能会出现很多重复的 play,如何讲重复的提取处理,变成可重复调用的呢?
并且根据不同的环境,同过设置相应的特殊变量、参数,来调用这些对应的 play。下面就介绍一些思路:
可以写个脚本,从公司的 CMDB 里拉取这些环境的主机信息。根据这些信息,生成这三个环境对应的 Inventory 文件(production、stage和 feature),最后采用多 Invertory 方式进行引用。
在这些文件里面再进行分小组,例如 production 环境下有 mongeodb,就定义个 P@mongodb 组。
之后根据不同的环境配置管理中的配置方法存在哪些异同进行整合。
根据不同的环境引入不同的 task,可以通过 when 方式去判断当前的主机信息存在哪个环境中,然后进行引用。
生产环境示例:
让我们展示一个静态清单示例。下面,生产文件包含所有生产主机的库存。
建议您根据主机(角色)的目的,以及地理位置或数据中心位置定义组(如果适用)。
# file: production
[beijing-webservers]
www-bj-1.example.com
www-bj-2.example.com
[shanghai-webservers]
www-sh-1.example.com
www-sh-2.example.com
[beijing-dbservers]
db-bj-1.example.com
db-bj-2.example.com
[shanghai-dbservers]
db-sh-1.example.com
# webservers in all geos
[webservers:children]
beijing-webservers
shanghai-webservers
# dbservers in all geos
[dbservers:children]
beijing-dbservers
shanghai-dbservers
# everything in the beijing go
[beijing:children]
beijing-webservers
beijing-dbservers
# everything in the shanghai geo
[shangai:children]
shanghai-webservers
shanghai-dbservers
Group And Host Variables
针对 组 和 主机 的变量请始终用 group_vars 和 host_vars 目录下 定义他们。
这是非常好的方式。
---
# file: group_vars/shanghai
ntp: ntp-beijing.example.com
backup: backup-beijing.example.com
---
# file: group_vars/webservers
apacheMaxRequestsPerChild: 3000
apacheMaxClients: 900
---
# file: host_vars/db-shanghai-1.example.com
foo_agent_port: 86
bar_agent_port: 99
接上一小节示例,使用 role 编写
- 在顶层设置 site.yml
---
# file: site.yml
- include: webservers.yml
- include: dbservers.yml
- 在诸如 like webservers.yml 的文件中(同样也在顶层结构),我们仅仅将 Web 服务器组与对应的 role 行为做映射
---
# file: webservers.yml
- hosts: webservers
roles:
- common
- webtier
- 这样的话我们可以对 site.yml 的运行来对整个主机进行配置。
也可以对某类业务的主机进行配置,比如 webservers,就像是用 --limit;但是这样会更显式和易读
比如:
ansible-playbook site.yml --limit webservers
ansible-playbook webservers.yml
- 接下来的示例任务文件展示了一个 role 是如何工作的
---
# file: roles/common/tasks/main.yml
- name: be sure ntp is installed
yum: pkg=ntp state=installed
tags: ntp
- name: be sure ntp is configured
template: src=ntp.conf.j2 dest=/etc/ntp.conf
notify:
- restart ntpd
tags: ntp
- name: be sure ntpd is running and enabled
service: name=ntpd state=running enabled=yes
tags: ntp
可以使用 tags 来代表一组有依赖性质的 task
- 当然还有 handlers
---
# file: roles/common/handlers/main.yml
- name: restart ntpd
service: name=ntpd state=restarted
如何使用上面这个组织结构:
- 若我想重新配置整个基础设施,如此即可:
ansible-playbook -i production site.yml
- 那只重新配置所有的 NTP 呢?太容易了.:
ansible-playbook -i production site.yml --tags ntp
- 只重新配置我的 Web 服务器呢?:
ansible-playbook -i production webservers.yml
- 只重新配置我在上海的 Web服务器呢?:
ansible-playbook -i production webservers.yml --limit shanghai
- 前10台 和 接下来的10台呢?
ansible-playbook -i production webservers.yml --limit boston[0-10]
ansible-playbook -i production webservers.yml --limit boston[10-20]
==列表中,不会包含最后一个主机==
- 只是使用 Ad-Hoc
ansible boston -i production -m ping
ansible boston -i production -m command -a '/sbin/reboot'
- 其他一些参数
# 可以列出指定标志名的 task
ansible-playbook -i production webservers.yml --tags ntp --list-tasks
# 列出上海的主机列表
ansible-playbook -i production webservers.yml --limit shanghai --list-hosts
处理不同的操作系统
当处理在不同操作系统间参数值不同的参数时,使用 group_by 模块是个好主意.
这使宿主机的动态分组有了匹配的标准,即使该分组尚未在清单文件中被定义
---
# talk to all hosts just so we can learn about them
- hosts: all
tasks:
- group_by: key=os_{{ ansible_distribution }}
# now just on the CentOS hosts...
- hosts: os_CentOS
gather_facts: False
tasks:
- # tasks that only happen on CentOS go here
使用动态 Inventoy 文件
如果你正在使用云服务,你不应该在一个静态文件管理你的清单.
详见 动态 Inventory.
灰度发布与检测
语法检测
运行 playbook 前,先检查自己的 playbook 语法是否这个正确
➜ ~ ansible-playbook uptime.yml --syntax-check
playbook: uptime.yml
灰度发布
就是先挑选一台机器进行测试,测试结果假如是我们想要的,再进行批量执行。
可以把一个或者多个 task 使用 delegate_to 指定一台机器。
使用’serial’关键词来控制一定数量的主机也是一个好想法:
---
- hosts: webservers
serial: 5
tasks:
- name: take out of load balancer pool
command: /usr/bin/take_out_of_pool {{ inventory_hostname }}
delegate_to: 127.0.0.1
- name: actual steps would go here
yum: name=acme-web-stack state=latest
- name: add back to load balancer pool
command: /usr/bin/add_back_to_pool {{ inventory_hostname }}
delegate_to: 127.0.0.1
验证结果
使用 --diff 参数来比较配置前后的文件变化
使用 –check 选项 ansible 命令将会报告出 Ansible 使系统进入一个期望状态所做的任何更改.
ansible-playbook test.yml --diff --check
更多请参考官方权威中文文档
测试策略
在指定的主机上运行一次性的 play
- command: /opt/application/upgrade_db.py
run_once: true
delegate_to: web01.example.org
滚动更新配置
设置 serial 的值,可以每次对一个目标主机组中的一部分执行 play,比如总共 10 台,可以设置每次 3 台
- name: test play
hosts: webservers
serial: 3
1.8 后,这个值可以是百分比
- name: test play
hosts: websevers
serial: "30%"
- 最大失败百分比
New in version 1.3.
ansible 允许设置在上面情况中,执行 play 过程中失败的百分比
==百分比的值必须被超过,不是等于==
例如如果serial值设置为10,并且你希望任务主动在5个系统失败时候放弃.那么这个百分比应该设置为49而不是50.
- hosts: webservers
max_fail_percentage: 49 # 这里是百分比值
serial: 10
==鼓励使用空格来分隔内容,用 ‘#’ 来写注释.==
Task 应该总是起名字
Keep It Simple(保持简单)
当你能简单的搞定某事时,就简单的搞定.不要试图一次性使用 Ansible 的所有的特性.仅仅使用对你有用的即可. 比如说你基本上不会需要一次性使用 vars , vars_files , vars_prompt 和 --extra-vars 同时还是用一个外部的节点配置文件.
如果你感觉任务很复杂时,它可能真的很复杂,这也许是个简化它的好机会.
Version Control
请使用版本控制.保持你的 playbook 和 清单文件 在 git(或其他版本控制系统)中,并将你的修改做提交. 这样你就有审计轨迹来描述什么时候以及为什么你做了这样的修改.
使用 ansible-shell 交互命令行
安装办法
➜ ~ git clone https://github.com/dominis/ansible-shell.git
➜ ~ cd ansible-shell
➜ ansible-shell ~ python setup.py install
网友评论