LabHub
学习 学习路径 课程

Ansible 基础

playbook — 写的是状态,不是命令

在 LabHub 中继续学习

一句话总结

playbook 中的每个 task 不是“执行这条命令”,而是声明“结束时应处于这种状态”。

概念图: “检查当前状态” · play 的列表 · task 从上到下按顺序执行。 · 每个 task 都要写 name。

为什么需要它

用 shell 脚本配置服务器的人都会经历脚本逐渐变长。最初只有 mkdir /opt/app 一行,第二次执行出现“已存在”错误,于是改成 mkdir -p;又担心权限不同,加上 chmod;想到权限已正确时无须执行,再套上 if。最终脚本的一半都变成**“检查当前状态”**的代码。

Ansible 模块承担了这半边工作。向 file 模块提供 pathstate=directorymode=0755,它会在目录不存在时创建,存在时保留,权限不同时修正,并且只在真实修改后报告 changed。这个报告正是下一模块中幂等性与 handler 的基础。

如何工作

playbook 是play 的列表,每个 play 包含“按什么顺序,对哪些 host 执行哪些 task”。

- name: 웹 서버 기본 설정          # 플레이 이름
  hosts: web                      # 대상 (인벤토리의 그룹/호스트/패턴)
  gather_facts: true              # 대상의 정보를 먼저 수집할지
  tasks:
    - name: 앱 디렉터리 준비       # 태스크 이름
      ansible.builtin.file:       # 모듈
        path: /opt/app
        state: directory
        mode: "0755"

需要记住三条规则。

  1. task 从上到下按顺序执行。 对多个 host 而言,一个 task 会在所有 host 完成后,才进入下一个 task。
  2. 每个 task 都要写 name 没有名称时,日志会输出完整模块名与参数,难以阅读,也难以通过 tag 选择。
  3. command/shell 是最后手段。 二者不了解状态,每次都会报告 changed。存在专用模块时应优先使用;不得不用 shell 时,应添加 creates 等 guard。

执行前也有检查手段。--syntax-check 检查 YAML 与 play 结构;--check 不实际修改,只报告“将会改变什么”;同时使用 --diff 还能看到文件内容差异。第一次在生产服务器执行的 playbook,必须先通过 --check --diff

在实际项目中

第一,tag 的两面性。 添加 tag 后,可以用 --tags config 只重新推送配置,很方便。但若养成只执行局部 tag 的习惯,就会出现“从未完整执行过的 playbook”,直到某天第一次全量运行才暴露错误。tag 是调试加速工具,不是正常执行路径。

第二,check mode 的局限。 只有模块支持时,--check 才准确。后续 task 若依赖 command 创建的结果,在 check mode 中可能得到错误结果。因此,check mode 没有问题与真实执行安全,是两个不同命题。

第三,名称就是文档。 事故发生时,人们读取的是执行日志,而不是代码。“Install nginx”不如“安装 nginx——配置文件由下一 task 覆盖”有用。

失败时在哪里停止

playbook 中途失败,会留下部分已应用、部分尚未应用的混合状态。Ansible 同时处理多个 host,比普通脚本更复杂;不了解默认行为,就难以判断事故后的状态。

默认只有失败的 host 退出。 某个 host 上 task 失败后,该 host 从后续 task 中排除,其余 host 继续。因此,十台中只有一台失败,其余九台会完成,留下集群分裂为两个版本的状态。

有几种机制可以改变行为。

serial 与 handler 的关系尤其容易忘记。 handler 默认在 play 结束时执行一次,但使用 serial 后会在每批结束时执行。因此,重启会按批次发生,这正是滚动部署需要的行为。

还应预先判断,失败后重新执行是否安全。所有 task 都幂等时直接重跑即可;只要中间有一个非幂等操作,重跑就可能恶化状态。前一模块要求用测试强制幂等性的原因,在这里显现。无法确认能否重跑的自动化,失败后仍然只能由人手工收拾。

下一实验要做什么

创建 /root/ans/play/site.yml,用 task 表达创建目录、部署配置文件、修改现有文件的一行,并亲自使用 tag 与 check mode。最后修复一个埋有四处错误的 playbook,直到通过。