自定义路径下用host_group_vars加载嵌套文件的问题排查
你的问题核心在于错误地将解析后的字典转成了YAML字符串,导致后续无法访问嵌套属性;同时indent过滤器的使用场景不对,反而破坏了结构。下面一步步拆解问题并给出解决方案,同时兼顾配置复用的需求。
为什么会报错?
你在applications/application_foo.yml和deployment_vars/deployment_foo.yml中使用了to_nice_yaml | indent(6),这个操作会把原本解析好的字典转换成YAML格式的字符串,而不是保持字典类型。当你尝试访问deployment['containers']时,实际上是在对一个字符串做字典操作,自然会抛出'str object' has no attribute 'containers'的错误。
另外,indent过滤器是用来给文本字符串的每一行添加缩进的,不是用来处理YAML字典结构的——你不需要把字典转成字符串再缩进,直接保持字典类型,Ansible会自动处理YAML的嵌套结构。
解决方案1:移除字符串转换,直接加载为字典
修改所有层级的配置文件,让每一层的lookup结果直接解析为字典,保持整个applications结构都是嵌套的字典/列表:
1. group_vars/my_group.yml(保持不变)
这一步你做的是对的,用from_yaml把模板输出解析成了字典:
applications: application_foo: "{{ lookup('template', inventory_dir + '/applications/application_foo.yml') | from_yaml }}"
2. applications/application_foo.yml(移除to_nice_yaml和indent)
直接加载deployment配置为字典:
deployments: application_foo: deployment: "{{ lookup('template', inventory_dir + '/deployment_vars/deployment_foo.yml') | from_yaml }}" replicas: 1
3. deployment_vars/deployment_foo.yml(同样移除字符串转换)
直接加载容器配置为字典:
containers: container_bar: "{{ lookup('template', inventory_dir + '/container_vars/container_bar.yml') | from_yaml }}" container_baz: "{{ lookup('template', inventory_dir + '/container_vars/container_baz.yml') | from_yaml }}"
4. 容器配置文件(保持不变)
container_vars/container_bar.yml和container_baz.yml的纯YAML字典会被正确解析:
image_url: "https://example.com/image_bar" cpu_requests: "1" memory_requests: "512Mi" cpu_limit: "2" memory_limit: "1024Mi" readinessProbe: exec: command: - "sh" - "-c" - "true"
修改完成后,你的debug任务就能正常访问cpu_requests属性了——整个applications结构都是嵌套的字典,没有字符串类型的节点。
解决方案2:用include_vars实现更简洁的模块化加载(推荐)
如果你不需要对配置文件做模板渲染(比如没有动态变量替换),Ansible的include_vars是更原生的变量加载方式,代码更简洁,也更符合Ansible的最佳实践:
修改group_vars/my_group.yml
applications: application_foo: deployments: application_foo: replicas: 1 deployment: "{{ lookup('include_vars', inventory_dir + '/deployment_vars/deployment_foo.yml') }}"
修改deployment_vars/deployment_foo.yml
containers: container_bar: "{{ lookup('include_vars', inventory_dir + '/container_vars/container_bar.yml') }}" container_baz: "{{ lookup('include_vars', inventory_dir + '/container_vars/container_baz.yml') }}"
这种方式直接把变量文件加载为字典,省去了模板解析的步骤,性能更好,代码也更清晰。如果需要模板化配置(比如动态替换镜像地址),再换回lookup('template') | from_yaml的方式即可。
配置复用的最佳实践
要实现容器/部署配置的复用,你可以结合include_vars和combine过滤器,在通用配置的基础上覆盖特定值:
- 定义通用容器配置,比如
container_vars/common_web_container.yml:
cpu_requests: "1" memory_requests: "512Mi" cpu_limit: "2" memory_limit: "1024Mi" readinessProbe: exec: command: - "sh" - "-c" - "true"
- 在具体部署中复用并修改特定值:
# deployment_vars/deployment_foo.yml containers: container_bar: "{{ lookup('include_vars', inventory_dir + '/container_vars/common_web_container.yml') | combine({'image_url': 'https://example.com/custom-image-bar'}) }}" container_baz: "{{ lookup('include_vars', inventory_dir + '/container_vars/common_web_container.yml') | combine({'image_url': 'https://example.com/custom-image-baz', 'cpu_requests': '2'}) }}"
这样既复用了通用配置,又能灵活修改每个容器的专属属性。
内容的提问来源于stack exchange,提问作者Vladimir

