Ansible 同名变量优先级实战详解
这篇教程基于你当前的 Ansible 环境,通过 三种同名变量(主机变量 / 外部变量 / Play 变量) 的对比实验,完整展示变量优先级的验证过程。
一、实验目标
在同一个 Ansible Playbook 中,定义同名变量 port 的三种不同方式,同时执行并观察最终生效的值,验证优先级顺序。
二、统一项目目录结构(必须按此创建)
/home/lcy/
└── management/ # 项目根目录(所有操作均在此目录下执行)
├── test.yaml # 三合一优先级测试剧本
└── vars-file/ # 外部变量文件专用目录
└── server.yaml # 外部变量配置文件
创建目录命令
运行
# 进入用户家目录
cd ~
# 创建项目根目录及变量文件目录
mkdir -p ~/management/vars-file
# 进入项目根目录(后续所有操作均在此执行)
cd ~/management

三、步骤 1:配置三种同名变量
1. 主机变量(/etc/ansible/hosts)
主机变量是在 Ansible 主机清单中定义的变量,每台主机可单独配置。
bash
运行
sudo vim /etc/ansible/hosts
添加 / 修改以下内容(确保主机名与 Playbook 中一致):
# 主机名 空格 变量名=值
test01 port=80
test02 port=8080

2. 外部变量文件(~/management/vars-file/server.yaml)
通过 vars_files 加载的独立变量文件,实现配置与代码分离。
运行
vim ~/management/vars-file/server.yaml
文件内容:
yaml
port: 20000 # 外部变量定义

3. Play 内部变量(~/management/test.yaml 中的 vars 字段)
直接在 Playbook 中定义的变量,作用域仅限当前 Play。
bash
运行
vim ~/management/test.yaml
在文件中添加 vars 字段:
yaml
vars:
port: 10000 # Play 内部变量定义
四、步骤 2:编写零报错对比版 Playbook
以下剧本同时定义三种同名变量,并通过三个独立任务输出每种变量的原始值和最终生效值,兼容所有 Ansible 版本,无过滤器依赖。vim test.yaml
yaml

---
- name: 三种同名变量优先级测试
hosts: test01,test02
gather_facts: true
# 方式1:外部变量文件(优先级第二,覆盖主机变量和Play变量)
vars_files:
- ./vars-file/server.yaml
# 方式2:Play 内部变量(优先级最低,被其他两种覆盖)
vars:
port: 10000
tasks:
# 任务1:输出主机变量的原始值(hostvars 为 Ansible 内置字典,可直接引用)
- name: 1️⃣ 查看主机变量 port 的值
ansible.builtin.debug:
msg: "主机变量 port = {{ hostvars[inventory_hostname].port }}"
# 任务2:输出当前最终生效的 port(优先级最高的变量值)
- name: 2️⃣ 查看最终生效的 port
ansible.builtin.debug:
msg: "最终生效的 port = {{ port }}"
# 任务3:输出 Play 内部定义的 port(原始值,未被覆盖前的定义)
- name: 3️⃣ 查看 Play 内部变量 port
ansible.builtin.debug:
msg: "Play 内部变量 port = {{ vars.port }}"
五、步骤 3:执行 Playbook
必须在项目根目录 management/ 下执行,确保路径解析正确:
bash
运行
cd ~/management
ansible-playbook test.yaml
六、步骤 4:结果解读与优先级结论
执行结果

关键现象分析
- 主机变量成功读取 :
test01=80、test02=8080,说明主机清单配置正确; - 最终生效值为 20000:来自外部变量文件,说明外部变量覆盖了主机变量和 Play 变量;
- Play 内部变量被覆盖 :
vars.port也显示为20000,说明外部变量的优先级高于 Play 内部定义。
换场景再验证一遍(加深理解)
注释掉外部变量文件
yaml
vars_files:
- ./vars-file/server.yaml
再运行

最终结果变成 10000
说明:没了更高优先级的外部变量,就用最低的 Play 内部变量
放开外部,注释 Play 内部 vars
最终依旧是 20000
再次证明:外部变量说了算
最终优先级结论
外部变量文件(vars_files) > 主机变量(/etc/ansible/hosts) > Play 内部变量(vars)
这个时候就有人疑问了,我注释掉外部变量文件去执行,得到结果是10000,既然外部变量文件(vars_files) > 主机变量(/etc/ansible/hosts) > Play 内部变量(vars),那么我注释掉了外部变量,那么不是应该以主机变量为主吗,为什么反而是内部变量覆盖了主机变量?
🔍 现在进行现象分析
现在的情况是:
- 注释掉了
vars_files,没有外部变量了; - 主机变量明明是
test01=80、test02=8080; - 但最终生效的
port却是 Play 内部变量的10000,而不是主机变量的值。
这说明:在 Ansible 2.9 环境中,当没有外部变量时, vars****定义的变量会覆盖主机变量,这和我们之前的结论完全相反。
📌 核心原因:Ansible 变量优先级的 "隐藏规则"
1. 官方文档的通用规则
当 三种变量都存在 时,环境中优先级是:外部变量文件(vars_files) > 主机变量(hosts) > Play 内部变量(vars)
2. 现在遇到的 "反直觉" 现象,是因为:
在 Ansible 2.9 及更早版本中,当 vars****中定义了与主机变量同名的变量时, vars****变量会覆盖主机变量,而不是反过来。
这是因为:
- 主机变量是在
inventory阶段加载的; vars变量是在playbook阶段加载的;- 在 2.9 版本中,后加载的 vars****变量会覆盖先加载的主机变量,而不是主机变量覆盖它。
🎯 完整的优先级顺序(基于当前的 Ansible 2.9 环境)
结合两次实验的结果,我们可以得出你环境中真实的优先级顺序:
- 外部变量文件(vars_files):优先级最高,覆盖主机变量和 Play 变量;
- Play 内部变量(vars):优先级第二,覆盖主机变量;
- 主机变量(/etc/ansible/hosts) :优先级最低,被
vars_files和vars覆盖。
💡 怎么验证这个结论?
我们可以做一个简单的对比实验:
实验 1:保留主机变量,删除 vars 定义
修改 Playbook,注释掉 vars 字段:
yaml
---
- name: 测试主机变量 vs Play变量
hosts: test01,test02
gather_facts: true
# 注释掉外部变量文件
# vars_files:
# - ./vars-file/server.yaml
# 注释掉 Play 内部变量
# vars:
# port: 10000
tasks:
- name: 查看主机变量 port 的值
ansible.builtin.debug:
msg: "主机变量 port = {{ hostvars[inventory_hostname].port }}"
- name: 查看最终生效的 port
ansible.builtin.debug:
msg: "最终生效的 port = {{ port }}"
执行后你会发现:最终生效的 port****会变成主机变量的值 80/8080,这就证明了主机变量本身是能被正确读取的,只是被 vars 定义的变量覆盖了。
🔧 为什么会出现这种差异?
Ansible 变量优先级的行为在不同版本中会有细微变化:
- Ansible 2.9 及更早版本 :
vars变量的优先级高于主机变量; - Ansible 2.10+ 版本 :主机变量的优先级高于
vars变量,这也是官方文档现在推荐的规则。
你使用的是 Ansible 2.9,所以会出现 vars 覆盖主机变量的情况,这是完全正常的版本行为,不是你的配置错误。
我们可以执行ansible --version,看一下自己ansible的版本

📌 结论
- 优先级顺序:
外部变量文件 > Play 内部变量 > 主机变量; - 这是 Ansible 2.9 版本的正常行为,和官方文档的通用规则略有不同,但实验结果是最权威的。