devops:改变思维,让一切更加高效

Ansible 最佳实战

2018-12-05  本文已影响76人  运维开发_西瓜甜

优化 Ansible 速度

设置 SSH 为长连接

openssh5.6 版本后支持 Multiplexing

检查控制机器的 ssh 版本

ssh -V

假如不是 5.6 版本以上的,可以用下面的办法升级

➜  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 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’.

下面的步骤是实现这个特性的步骤

  1. 在 ansible.cfg 配置文件中设置 pipelining 为 True
➜  ~ grep pipelining /etc/ansible/ansible.cfg
# Enabling pipelining reduces the number of SSH operations required to
pipelining = True
  1. 配置被控主机的 /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 中。

  1. 首先是可以在 ansible.cfg 文件中设置
grep gathering /etc/ansible/ansible.cfg
gathering = smart

ansible的配置文件中可以修改'gathering'的值为'smart'、'implicit'或者'explicit'。

  1. 在playbook 中设置
---
- hosts: all
  gather_facts: yes    # 显式定义收集
  gather_facts: no     # 显式定义不收集
  gather_facts: smart  # 显式定义收集

配置缓存的目标

  1. 缓存到文件
    在 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 .

  1. 缓存到 redis(目前不支持远端)

在控制主机上,安装 redis 服务和 python 的 redis 库

➜  ~ yum install redis
➜  ~ pip install redis

在 ansible.cfg 文件中配置缓存到 redis

gathering = smart
fact_caching_timeout = 86400  # 缓存数据时间是一天 
fact_caching = redis       # 缓存到 json 文件
  1. 缓存到 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的默认行为.

---

- 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 编写

---
# file: site.yml
- include: webservers.yml
- include: dbservers.yml
---
# file: webservers.yml
- hosts: webservers
  roles:
    - common
    - webtier

也可以对某类业务的主机进行配置,比如 webservers,就像是用 --limit;但是这样会更显式和易读
比如:

ansible-playbook site.yml --limit webservers
ansible-playbook webservers.yml
---
# 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

---
# file: roles/common/handlers/main.yml
- name: restart ntpd
  service: name=ntpd state=restarted

如何使用上面这个组织结构:

  1. 若我想重新配置整个基础设施,如此即可:
ansible-playbook -i production site.yml
  1. 那只重新配置所有的 NTP 呢?太容易了.:
ansible-playbook -i production site.yml --tags ntp
  1. 只重新配置我的 Web 服务器呢?:
ansible-playbook -i production webservers.yml
  1. 只重新配置我在上海的 Web服务器呢?:
ansible-playbook -i production webservers.yml --limit shanghai
  1. 前10台 和 接下来的10台呢?
ansible-playbook -i production webservers.yml --limit boston[0-10] 
ansible-playbook -i production webservers.yml --limit boston[10-20]

==列表中,不会包含最后一个主机==

  1. 只是使用 Ad-Hoc
ansible boston -i production -m ping
ansible boston -i production -m command -a '/sbin/reboot'
  1. 其他一些参数
# 可以列出指定标志名的 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%"

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

扩展 Ansible 组件

扩展 facts

扩展模块

callback 插件

lookup 插件

Jinja2 filter


保护敏感数据

ansible-vault 介绍

ansible-vault 使用

典型应用场景


Ansible 与云计算

Ansible 与 OpenStack

Ansible 与 Docker

Ansible Jenkins

实战

部署 Zabbix 组件

部署 HAProxy + LAMP 架构


Python 和 Ansible

上一篇下一篇

猜你喜欢

热点阅读