1、ansible日志显示及收集

问: ansible 错误日志显示不够灵活,例如:错误日志没有换行 错误之后其他进程日志会覆盖,不容易定位问题。

答: ansible 执行过程中出错,信息都是紧凑输出,没有分行的,暂时无解。应该是考虑到大批量执行的时候,部分主机出错是可以忽略可以再重试的。建议采用以下几种处理方式。

  • playbook 里增加 any_errors_fatal: yes,发生任何错误都中止执行,错误输出可以停留在当前屏幕。
  - hosts: masters
    any_errors_fatal: yes
  • 如果 ansible 在执行过程中有失败的主机,结束后会生成一个 retry 文件,实际就是一个主机列表。
TASK [file] ********************************************************************************************************************************************************
fatal: [hd1-master]: FAILED! => {"changed": false, "failed": true, "msg": "file (/tmp/hello) is absent, cannot continue", "path": "/tmp/hello", "state": "absent"}
  to retry, use: --limit @./retry/temp.retry

里面包含失败了的 hosts,只需加 --limit 参数重新执行,例如

ansible-playbook temp.yml --limit @./retry/temp.retry

如果错误还存在,第二次执行时可以看到。

  • 如果是执行 shell 失败并且错误内容比较长,需要把 shell 的输出重定向到一个文本文件里,发生失败时,把错误日志文件 fetch 回本机查看。
代码样例
- name: Run '{{ prj_name }}' startup.sh
  shell: '~/dist/{{ prj_name }}_startup.sh > ~/dist/logs/{{ prj_name }}.log'
  args:
    chdir: '~/dist/'
  register: startup

- name: Fetch log file
  fetch:
    src: '~/dist/logs/{{ prj_name }}.log'
    dest: '/tmp/{{ ansible_hostname }}_{{ prj_name }}.log'
    flat: yes
  when: "'ERROR' in startup.stdout"
  • 使用 register 记录 task 的输出,再用 debug 查看,可以使用 stdout_lines 获得格式化后的信息。
代码样例
- name: Run '{{ prj_name }}' startup.sh
  shell: '~/dist/{{ prj_name }}_startup.sh'
  args:
  chdir: '~/dist/'
  register: startup

- debug:
    var: startup.stdout_lines
  • 使用 block 和 rescue 组合,在发生错误时做补救或回滚。
代码样例
- block:
    - name: Create '{{ prj_name }}' scripts
      template:
        src: '{{ item }}.sh.j2'
        dest: '~/dist/{{ prj_name }}_{{ item }}.sh'
        mode: 0700
      with_items:
        - startup
        - rollback
        - clean

    - name: Run '{{ prj_name }}' startup.sh
      shell: '~/dist/{{ prj_name }}_startup.sh'
      args:
        chdir: '~/dist/'
      register: startup
      failed_when: "'ERROR' in startup.stdout"

  rescue:
    - name: Fetch log file
      fetch:
        src: '~/dist/logs/{{ prj_name }}.log'
        dest: '/tmp/{{ ansible_hostname }}_{{ prj_name }}.log'
        flat: yes

    - name: Roll back when '{{ prj_name }}' failed
      shell: '~/dist/{{ prj_name }}_{{ item }}.sh'
      args:
        chdir: '~/dist'
      with_items:
        - rollback
        - startup

  always:
    - name: Clean '{{ prj_name }}' old files
      shell: '~/dist/{{ prj_name }}_clean.sh'
      args:
        chdir: '~/dist'

以上 block 代码里做了 3 件事:

1.  正常的升级流程;
2.  如果升级中捕捉到 ERROR 日志,执行 rescue 部分的操作,取回启动日志文件,并且执行回滚脚本。
3.  无论升级是否成功,都清除符合条件的旧文件。
  • 使用 do-until 重复执行次数
代码样例
- action:
    shell /usr/bin/foo
  register: result
  until: result.stdout.find("all systems go") != -1
  retries: 5
  delay: 10

2、定义变量方式

问: 他们想了解 ansibe 变量定义方式,例如 group_vars、 host_vars 以及 --extra-vars 方式。

答: group_vars 和 host_vars 的变量定义就是标准的 yaml 格式,例如

ansible_user: ''
ansible_ssh_pass: ''
become_user: 'root'
ansible_become_pass: ''

rabbitmq_user: 'nsc'
rabbitmq_pass: ''
rabbitmq_erlang_cookie: ''

redis_pass: ''

centos_iso:
  - name: CentOS-7-x86_64-Everything-1611.iso
    url_prefix: 'http://mirrors.163.com/centos/7/isos/x86_64/'
    mount_point: 'centos7'
    checksum: 'sha256:af4969ebbdc479d330de97c5bfbb37eedc64c369f009cb15a97f9553ba441c88'

但优先级别里,host_vars 比 group_vars 更高。

而 --extra-vars 是在命令行里直接给出,它的变量优先级是顶级的,会覆盖所有同名变量。格式是

ansible-playbook temp.yml --extra-vars="A=a B=b"

ansible 对变量的优先级定义,后一个比前一个优先,同名变量会覆盖。

  • role defaults
  • inventory INI or script group vars
  • inventory group_vars/all
  • playbook group_vars/all
  • inventory group_vars/*
  • playbook group_vars/*
  • inventory INI or script host vars
  • inventory host_vars/*
  • playbook host_vars/*
  • host facts
  • play vars
  • play vars_prompt
  • play vars_files
  • role vars (defined in role/vars/main.yml)
  • block vars (only for tasks in block)
  • task vars (only for the task)
  • role (and include_role) params
  • include params
  • include_vars
  • set_facts / registered vars
  • extra vars (always win precedence)

所以,变量级别依次是 --extra-vars > host_vars > group_vars

3、每次批量执行多台机器(30台左右)性能影响,了解 subprocess

问: 目前他们每次发布服务器需要 30 台左右(测试环境,生产环境大约 100 台左右),ansible默认同时可操作主机为 5 台。 他们想了解如果添加节点会对主机性能等方式有无影响。

答: ansible 默认应该是全部目标机器一起操作。使用 serial 参数,对同时操作的主机数量做编排很重要,对失败风险实现可控。也是灰度发布的标准做法。

代码实例
- hosts: masters
  any_errors_fatal: yes
  serial:
    - 1
    - 3
  roles:
    - upgrade.masters

- hosts: slaves
  serial: 3
  roles:
    - upgrade.slaves

以上例子里,playbook 会先在第一台 master 主机执行剧本,如果通过了,每次 3 台进行更新。更新完 masters 就可以更新 slaves 了。 ansible 使用的是 ssh 协议,只要网络可达,控制机的网络连接数不受限,性能方面没有影响。我们生产环境 56 台机器同时执行一个 playbook,没问题。