做出可复用的 role
目标
把 playbook 片段整理为 role,形成可复用的单元,并用不同参数多次调用同一个 role。
为什么重要
role 的本质不是“拆分代码”,而是**“预先约定规范,使其无需额外配置即可复用”**。放在 templates/ 中的文件无需写完整路径即可找到,handlers/main.yml 中的 handler 也无需注册便可使用。借助这些约定,即使是别人编写的 role,也能推断其结构。真正决定可复用性的,是对 defaults 与 vars 的选择——如果把用户需要修改的值放入 vars,它会因优先级过高而无法从外部覆盖,最终这个 role 只能被复制、修改并再次分叉。当 role 超过两个时,为变量名加上 role 名称前缀的习惯也会开始体现价值。
步骤
- 在
/root/ans/roles/roles/webapp下创建tasks、defaults、handlers、templates、meta目录以及tasks/main.yml。 - 在
/root/ans/roles/roles/webapp/defaults/main.yml中定义webapp_port: 8080和webapp_root: /root/ans/roles/artifacts。 - 在
/root/ans/roles/roles/webapp/tasks/main.yml中加入至少两个有名称的 task,执行后应创建/root/ans/roles/artifacts目录。 - 创建
/root/ans/roles/roles/webapp/templates/webapp.conf.j2,并将其渲染到/root/ans/roles/artifacts/webapp.conf。结果中必须有port = 8080行,且模板必须引用webapp_port变量。 - 在
/root/ans/roles/roles/webapp/handlers/main.yml中定义restart webapphandler,并从/root/ans/roles/roles/webapp/tasks/main.yml的 task 中通过notify调用它。handler 要创建/root/ans/roles/artifacts/restart.marker。 - 创建
/root/ans/roles/roles/baselinerole(入口为/root/ans/roles/roles/baseline/tasks/main.yml),让它留下/root/ans/roles/artifacts/baseline.stamp,并在/root/ans/roles/roles/webapp/meta/main.yml的dependencies中声明该依赖。 - 在
/root/ans/roles/site.yml中调用webapprole 两次:一次使用默认值(webapp.conf,port 8080),另一次使用webapp_port: 9443创建/root/ans/roles/artifacts/staging.conf。不得复制 role 目录。 - 将第二次运行 playbook 的输出保存到
/root/ans/roles/out/run2.txt,将ansible-lint结果保存到/root/ans/roles/out/lint.txt。第二次运行必须为changed=0,且不得触发 handler。
参考
- 每个实验都会启动全新的实验 Pod。如果
/root/ans/inventory/hosts.ini不存在,请先重新创建与第一个实验相同的 inventory(web1·web2·db1,ansible_host=127.0.0.1、ansible_port=2222、ansible_user=root,并在[prod:children]中加入 web·db)。结构可以参考/opt/lab/fixtures/ansible/inventory.sample.ini。 - 使用
ansible-galaxy role init roles/webapp可以一次生成标准结构。 - 调用 role 时,可以在
- role: webapp下写入只对该次调用生效的变量。 - 常见错误 1:在模板路径中包含
templates/。在 role 内只需写文件名。 - 常见错误 2:为了完成第 7 步而复制整个 role。本题的重点是两次调用同一份源码。
创建 role 骨架目录
在 /root/ans/roles/roles/webapp 下创建 tasks、defaults、handlers、templates、meta 目录以及 tasks/main.yml。
ansible-galaxy role init 会生成标准结构。所需目录为 tasks/defaults/handlers/templates/meta。
定义两个默认值
在 /root/ans/roles/roles/webapp/defaults/main.yml 中定义 webapp_port: 8080 和 webapp_root: /root/ans/roles/artifacts。
预期可被覆盖的值应放在 defaults 中,并为名称加上 role 名称前缀。
编写并运行 role task
在 /root/ans/roles/roles/webapp/tasks/main.yml 中加入至少两个有名称的 task,执行后应创建 /root/ans/roles/artifacts 目录。
tasks/main.yml 是入口。至少要有两个 task,并且每个都必须有名称。
用 role 模板生成配置文件
创建 /root/ans/roles/roles/webapp/templates/webapp.conf.j2,并将其渲染到 /root/ans/roles/artifacts/webapp.conf。结果中必须有 port = 8080 行,且模板必须引用 webapp_port 变量。
templates/ 中的文件只写文件名即可找到。不要硬编码端口值,应使用变量。
定义并触发 role handler
在 /root/ans/roles/roles/webapp/handlers/main.yml 中定义 restart webapp handler,并从 /root/ans/roles/roles/webapp/tasks/main.yml 的 task 中通过 notify 调用它。handler 要创建 /root/ans/roles/artifacts/restart.marker。
handlers/main.yml 中的 handler 会自动注册。运行后请留下标记文件。
声明依赖 role 并使其先运行
创建 /root/ans/roles/roles/baseline role(入口为 /root/ans/roles/roles/baseline/tasks/main.yml),让它留下 /root/ans/roles/artifacts/baseline.stamp,并在 /root/ans/roles/roles/webapp/meta/main.yml 的 dependencies 中声明该依赖。
单独创建 baseline role,并写入 meta/main.yml 的 dependencies。依赖 role 会先运行。
用不同参数调用同一 role 两次
在 /root/ans/roles/site.yml 中调用 webapp role 两次:一次使用默认值(webapp.conf,port 8080),另一次使用 webapp_port: 9443 创建 /root/ans/roles/artifacts/staging.conf。不得复制 role 目录。
可以在 play 的 roles 段中将变量与 role 名称一起传入。不得复制 role。
验证重跑幂等性并通过 lint
将第二次运行 playbook 的输出保存到 /root/ans/roles/out/run2.txt,将 ansible-lint 结果保存到 /root/ans/roles/out/lint.txt。第二次运行必须为 changed=0,且不得触发 handler。
第二次运行应为 changed=0,handler 也应保持静默。还要保存 ansible-lint 结果。