role — 用约定折起来的复用单元
一句话总结
role 是把 playbook 片段放入约定名称的目录中;正因为遵循这个约定,它无需额外配置即可复用。
为什么需要它
把 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 |
关键是区分 defaults 与 vars。用户可能需要修改的值必须放在 defaults。 放在 vars 中会因优先级过高而几乎无法从外部覆盖,role 也就失去复用能力。
meta/main.yml 的 dependencies 声明“这个 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) ← 가장 강하다. 무엇도 못 이긴다
由此得到两条规则。
- role 默认值放在
defaults/。 放入vars/会因优先级过高而无法被用户覆盖。“修改了 role 变量却不生效”通常就是这个原因。 -e只用于调试。 若长期写在脚本中,inventory 配置会全部被忽略,日后没人能判断某个值究竟从何而来。
想知道值来自哪里,可以直接查询。
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 时,要用 creates 或 removes 添加条件。
- name: 설치 스크립트 (한 번만)
command: /opt/setup.sh
args:
creates: /opt/.installed # 이 파일이 있으면 건너뛴다
changed_when: false 是另一回事。它声明“这条命令不改变状态”,只适用于查询。若加在真实修改状态的命令上,报告就会说谎。
如何划分 role
role 变大后自然会想拆分,但没有标准只会增加碎片。
- 能否原样用于其他地方——不能复用就没有拆分理由。
- 依赖是否单向——role A 调用 B、B 又调用 A,就是循环。
- 变量名是否冲突——每个 role 都使用前缀(
nginx_port)。
第三点是实务陷阱。两个 role 都使用 port 时,后者会覆盖前者,而 Ansible 不会警告。为变量加 role 名称前缀的惯例就是为了避免它。
下一实验要做什么
从目录骨架开始创建 webapp role,填充默认值、task、template 与 handler,再把 baseline role 设为依赖并确认执行顺序。用不同变量调用同一 role 两次以证明可复用性,并让第二次执行得到 changed=0。