LabHub
学习 学习路径 课程

Ansible 实战

role — 用约定折起来的复用单元

在 LabHub 中继续学习

一句话总结

role 是把 playbook 片段放入约定名称的目录中;正因为遵循这个约定,它无需额外配置即可复用。

概念图: 约定名称的目录 · 先 · 用户可能需要修改的值必须放在 defaults。 · 第一,名称冲突。

为什么需要它

把 nginx 安装、应用部署、日志轮转、监控 agent 全写进一个 playbook,很快就会达到 300 行。出现第二个服务时,人们会复制文件。从复制那一刻起,两份文件就以不同速度老化。六个月后,“为什么只有 A 服务器不清理日志”的答案总是“复制版本没有更新”。

role 用“通过文件系统结构强制复用单元”解决问题。role 内的 task、默认值、handler、template 各有固定位置,即使不打开别人编写的 role,也知道哪里放着什么。

如何工作

目录 内容 自动加载
tasks/main.yml 要执行的 task 调用 role 时
defaults/main.yml 预期由调用方覆盖的默认值 最低优先级
vars/main.yml 不应变化的内部值 高优先级
handlers/main.yml handler 自动注册
templates/ Jinja2 template template 模块无需路径即可查找
files/ 原样复制的文件 copy 模块无需路径即可查找
meta/main.yml 依赖 role、元数据 执行依赖 role

关键是区分 defaultsvars用户可能需要修改的值必须放在 defaults 放在 vars 中会因优先级过高而几乎无法从外部覆盖,role 也就失去复用能力。

meta/main.ymldependencies 声明“这个 role 之前需要先执行另一个 role”。同一依赖出现多次时,默认也只执行一次。

在实际项目中

第一,名称冲突。 多个 role 并存时,变量名可能重叠。惯例是为 role 变量添加 role 名称前缀,例如不要使用 port,而应使用 webapp_port

第二,不要在 role 中写绝对路径。 templates/files/ 会相对于 role 自动搜索,因此只写文件名即可。写死绝对路径后,该 role 只能在某一台服务器工作。

第三,role 也必须幂等。 连续两次执行使用该 role 的 playbook,检查第二次是否 changed=0,这条习惯同样适用。

变量优先级会制造事故

Ansible 变量有二十二级优先级。无需全部背诵,但要知道最常碰到的五级顺序(越往下越强)。

1. 롤의 defaults/main.yml     ← 가장 약하다. 덮어쓰라고 있는 것
2. inventory group_vars
3. inventory host_vars
4. play 의 vars
5. -e (extra vars)            ← 가장 강하다. 무엇도 못 이긴다

由此得到两条规则。

想知道值来自哪里,可以直接查询。

ansible -i inv host -m debug -a "var=app_port"
ansible-inventory -i inv --host web-01 --yaml     # 그 호스트에 적용된 전부

破坏幂等性的模块用法

Ansible 的价值在于“执行两次结果相同”。以下用法会破坏这一点。

不应使用 替代方案
shell: echo x >> /etc/hosts lineinfile, blockinfile
command: mkdir /opt/app file: state=directory
shell: sed -i … lineinfile, template
command: curl … | bash get_url + command(验证后)

必须使用 shell 时,要用 createsremoves 添加条件。

- name: 설치 스크립트 (한 번만)
  command: /opt/setup.sh
  args:
    creates: /opt/.installed      # 이 파일이 있으면 건너뛴다

changed_when: false 是另一回事。它声明“这条命令不改变状态”,只适用于查询。若加在真实修改状态的命令上,报告就会说谎。

如何划分 role

role 变大后自然会想拆分,但没有标准只会增加碎片。

第三点是实务陷阱。两个 role 都使用 port 时,后者会覆盖前者,而 Ansible 不会警告。为变量加 role 名称前缀的惯例就是为了避免它。

下一实验要做什么

从目录骨架开始创建 webapp role,填充默认值、task、template 与 handler,再把 baseline role 设为依赖并确认执行顺序。用不同变量调用同一 role 两次以证明可复用性,并让第二次执行得到 changed=0