Ansible Role必须严格遵循目录约定:tasks/main.yml为必需文件,templates路径相对Role内templates目录,vars/变量优先级高于defaults/且不可被外部覆盖,include_vars加载变量作用域限于当前play。

Role目录结构必须严格遵循Ansible约定
Ansible不会自动识别任意命名的文件夹为Role,roles/ 下每个子目录必须包含 tasks/main.yml(或 .yml),否则执行时直接报错 ERROR! the role 'xxx' was not found。常见错误是把配置文件放在 files/ 但忘了在 tasks/main.yml 里用 copy 或 template 加载。
-
roles/nginx/是合法路径;roles/nginx_config/也合法,但调用时得写role: nginx_config -
defaults/main.yml和vars/main.yml都能定义变量,但优先级不同:vars/覆盖defaults/,且vars/不允许被 playbook 外部用vars:覆盖 - 如果要用 Jinja2 模板生成配置,
templates/nginx.conf.j2必须和tasks/main.yml在同一 Role 内,不能跨 Role 引用
用 include_vars 动态加载环境相关配置
硬编码不同环境的配置值(比如 dev/staging/prod 的端口、域名)会让 Role 失去复用性。直接在 vars/main.yml 里写死,等于把环境耦合进 Role 本身。
- 推荐做法:在
group_vars/或host_vars/中按环境组织变量文件,再用include_vars在tasks/main.yml开头加载 - 示例:
- include_vars: "{{ env }}.yml",配合extra_vars: -e env=prod运行,避免修改 Role 内容 - 注意:
include_vars加载的变量作用域是当前 play,不能跨 play 传递;若需全局可用,改用vars_files:在 playbook 级声明
copy 和 template 的选择取决于配置是否含变量
静态配置文件(如预编译好的 nginx.conf)用 copy 最快;只要涉及任何变量替换(哪怕只是 {{ ansible_hostname }}),就必须用 template,否则变量原样写入目标文件。
-
copy支持backup: true,覆盖前自动备份旧文件;template默认不备份,需手动加backup: true -
template的路径是相对于templates/目录的,不是相对于 playbook 当前路径 —— 常见错误是写成src: ./templates/nginx.conf.j2,实际应写src: nginx.conf.j2 - 模板中避免用
{% if %}嵌套太深,Ansible 渲染失败时只报AnsibleError: template error,不提示具体哪行出错
Role间依赖容易引发循环引用或顺序错乱
在 meta/main.yml 里声明 dependencies: 看似方便,但一旦两个 Role 互相 require,Ansible 直接退出并报错 ERROR! Circular dependency detected。更隐蔽的问题是:依赖声明不控制执行顺序,只控制是否提前加载。
- 真正需要“先装包再写配置”的逻辑,应该拆成两个独立 Role,并在 playbook 中显式排列顺序:
- role: nginx_install然后- role: nginx_config -
dependencies只适合强耦合场景(比如java_app必须依赖jdk8),且所有依赖 Role 必须出现在同一个roles_path下,不能混用ansible-galaxy install和本地路径 - 若依赖的是 Galaxy 上的 Role,务必固定版本号,例如
src: geerlingguy.nginx, version: 4.0.0,否则下次galaxy install可能拉下不兼容的新版
Role 的模块化价值不在目录多整齐,而在变量边界是否清晰、依赖是否可预测、模板渲染是否可控。最容易被忽略的是 vars/ 和 defaults/ 的优先级差异,以及 template 路径的相对基准 —— 这两个点出问题时,Ansible 往往只报泛泛的“undefined variable”或“file not found”,得倒着查路径和作用域。

















