Ansible 最佳实战

作者: 运维开发_西瓜甜 | 来源:发表于2018-12-05 20:47 被阅读76次

    优化 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’.

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

    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'。

    • smart      --> 表示默认收集facts,但facts已有的情况下不会收集,即使用缓存facts;
    • implicit   --> 表示默认收集facts,要禁止收集,必须使用gather_facts: False;
    • explicit   --> 则表示默认不收集,要显式收集,必须使用gather_facts: Ture
    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的默认行为.

    • 另外,如果你不需要等待任务执行完毕,你可以指定 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
    

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

    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%"
    
    • 最大失败百分比
      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
    

    扩展 Ansible 组件

    扩展 facts

    扩展模块

    callback 插件

    lookup 插件

    Jinja2 filter


    保护敏感数据

    ansible-vault 介绍

    ansible-vault 使用

    典型应用场景


    Ansible 与云计算

    Ansible 与 OpenStack

    Ansible 与 Docker

    Ansible Jenkins

    实战

    部署 Zabbix 组件

    部署 HAProxy + LAMP 架构


    Python 和 Ansible

    相关文章

      网友评论

        本文标题:Ansible 最佳实战

        本文链接:https://www.haomeiwen.com/subject/bxklcqtx.html