Ansible 变量全解析与实践
一、Ansible 变量基础
1.1 变量定义规则
- 命名规范:由字母、数字、下划线组成,必须以字母开头,不可使用 Ansible 内置关键字(如 tasks、hosts 等)。
- 变量优先级(从高到低):
- Global 范围:命令行传递(-e 选项)、Ansible 配置文件中设置的变量;
- Play 范围:Play 内 vars/vars_files 定义、角色(Role)内变量;
- Host 范围:Inventory 主机 / 组变量、事实变量(Facts)、注册变量(Register)。
- 若变量重复定义,优先级高的变量会覆盖优先级低的变量。
二、变量定义与使用方式(含实操示例)
2.1 直接定义变量(vars/vars_files)
2.1.1 vars 直接定义(剧本内)
在 Play 中通过 vars 关键字定义变量,支持普通变量和字典型变量,仅在当前 Play 生效。
示例剧本(test.yml):
cat test.yml
---
- name: abhosts: node1vars: - aa: 11- bb: b1: 21b2: 22 tasks: - name: abbdebug: msg: "{{aa}}"-----输出11- name: accdebug: msg: "{{bb.b1}}"------输出21
2.1.2 vars_files 引用外部变量文件
当变量较多时,可将变量存入独立文件,通过 vars_files 引入剧本,便于维护。
步骤 1:创建外部变量文件(/etc/ansible/a.yml):
cat a.ymlaa: 11bb: b1: 21b2: 22
步骤 2:剧本引用变量文件:
cat test.yml
---
- name: abhosts: node1vars_files: /home/student/ansible/a.yml tasks: - name: abbdebug: msg: "{{aa}}"-----输出11- name: accdebug: msg: "{{bb.b2}}"------输出22
2.2 注册变量(register)
通过 register 将任务执行结果(如命令输出、返回码)赋值给变量,后续可调用结果中的子信息(如 stdout、rc)。
示例 1:注册命令执行结果
cat test.yml---
- name: abhosts: node1tasks: - name: acshell: cmd: echo aaregister: rz- name: addebug: var: rz
执行结果(关键部分):
"rz": {"changed": true,"cmd": "echo aa","delta": "0:00:00.014507","end": "2025-09-01 16:34:13.808155","failed": false,"msg": "","rc": 0,----命令返回码(0表示成功)"start": "2025-09-01 16:34:13.793648","stderr": "",---标准错误输出"stderr_lines": [],"stdout": "aa",----标准输出,输出内容"stdout_lines": ["aa"
示例 2:调用注册变量的子键(主变量.子变量)
cat test.yml---
- name: abhosts: node1tasks: - name: acshell: cmd: echo aaregister: rz- name: addebug: var: rz.stdout
2.3 事实变量(Facts)
事实变量是 Ansible 自动收集的 “受控主机系统信息”(如 IP 地址、主机名、内存大小),无需手动定义,可直接调用。通过 setup 模块可查看所有事实变量。
1. 查看所有事实变量
# 将 node1 的所有事实变量保存到文件(便于查找)ansible node1 -m setup > a.txt
2. 常用事实变量示例
cat test.yml---
- name: abhosts: node1tasks: - name: addebug: var: ansible_fqdn- name: acdebug: var: ansible_default_ipv4.address- name: afdebug:var: ansible_hostname- name: agdebug: var: ansible_memtotal_mb- name: ahdebug:var: ansible_bios_version- name: akdebug: var: ansible_devices.vda.size
结果:
ok: [node1] => {"ansible_fqdn": "node1.example.com"
}TASK [ac] *********************************************************************************************
ok: [node1] => {"ansible_default_ipv4.address": "192.168.122.10"
}TASK [af] *********************************************************************************************
ok: [node1] => {"ansible_hostname": "node1"
}TASK [ag] *********************************************************************************************
ok: [node1] => {"ansible_memtotal_mb": 1969
}TASK [ah] *********************************************************************************************
ok: [node1] => {"ansible_bios_version": "1.16.1-1.el9"
}TASK [ak] *********************************************************************************************
ok: [node1] => {"ansible_devices.vda.size": "20.00 GB"
}
2.4 命令行传递变量(-e 选项)
执行剧本时通过 -e 选项动态传递变量,优先级最高,适合临时修改变量值。
示例剧本
cat test.yml---
- name: abhosts: node1tasks: - name: addebug:msg: "{{ name1 }}"
执行命令(传递变量):
ansible-playbook test.yml -e 'name1=aa'
执行结果:
2.5 主机清单中的变量
在 Inventory 文件(如 /home/student/ansible/inventory)或 host_vars/group_vars 目录中定义变量,对目标主机 / 组生效。
2.5.1 清单文件内直接定义( /home/student/ansible/inventory)
# 单个主机变量(直接跟在主机后)node1 name1=aanode2# 主机组 net 及组变量([组名:vars] 定义)[net]node1node2[net:vars]test1=hello # 组内所有主机共享的变量test2=hi
2.5.2 host_vars/group_vars 目录定义
在 /home/student/ansible 下创建 host_vars(主机专属变量)和 group_vars(组专属变量)目录,变量文件以 “主机名” 或 “组名” 命名(支持无后缀或 .yml 后缀)。
目录结构:
/home/student/ansible
├── host_vars/ # 主机专属变量目录
│ ├── node1 # node1 专属变量文件(无后缀)
│ └── node1.yml # node1 专属变量文件(.yml 后缀)
└── group_vars/ # 组专属变量目录
└── net.yml # net 组专属变量文件
优先级验证:
- 若 node1 和 node1.yml 同时存在,node1 优先级更高(执行剧本时会优先使用 node1 中的变量);
- 删除 node1 后,会自动使用 node1.yml 中的变量。
- node1>node1.yml>主机清单单用户>主机清单主机组
2.6 内置变量
Ansible 内置变量用于获取 “Ansible 版本”“清单结构” 等信息,无需手动定义,常用内置变量如下:
内置变量 | 作用 | 示例结果 |
ansible_version | Ansible 版本信息 | {"full": "2.9.18", "major": 2} |
inventory_hostname | 清单中定义的主机名 | node1 |
play_hosts | 当前 Play 涉及的所有主机列表 | ["node1", "node2"] |
groups | 所有主机组及组内主机 | {"all": ["node1","node2"], "net": ["node1","node2"]} |
group_names | 当前主机所属的所有组 | ["net"] |
inventory_dir | 清单文件所在目录 | /etc/ansible |
示例剧本
cat test.yml---
- name: abhosts: node1tasks: - name: addebug:var: ansible_version----ansible版本- name: afdebug: var: inventory_hostname----输出清单主机名- name: agdebug: var: play_hosts---输出当前play主机列表- name: ahdebug: var: groups---输出所有主机组- name: ajdebug: var: group_names---输出当前主机所属组- name: akdebug:var: inventory_dir----输出清单目录
结果:
ok: [node1] => {"ansible_version": {"full": "2.13.3","major": 2,"minor": 13,"revision": 3,"string": "2.13.3"}
}TASK [af] *******************************************************************************************************************
ok: [node1] => {"inventory_hostname": "node1"
}TASK [ag] *******************************************************************************************************************
ok: [node1] => {"play_hosts": ["node1"]
}TASK [ah] *******************************************************************************************************************
ok: [node1] => {"groups": {"all": ["node1","node2","node5","node3","node4"],"test01": ["node1"],"test02": ["node2"],"test05": ["node5"],"ungrouped": [],"web": ["node3","node4"],"webtest": ["node3","node4"]}
}TASK [aj] *******************************************************************************************************************
ok: [node1] => {"group_names": ["test01"]
}TASK [ak] *******************************************************************************************************************
ok: [node1] => {"inventory_dir": "/home/student/ansible"
}
2.7 叠加变量(with_items)
通过 with_items 为变量赋予多个值,任务会循环执行(每次取一个值),循环中固定用 item 表示当前值,支持注册循环结果并调用。
示例剧本
cat test.yml---
- name: abhosts: node1tasks: - name: adshell: cmd: echo "{{ item }}"with_items: - haha- heihei- heheregister: rz- name: afdebug: var: rz.results[0].stdout- name: agdebug: var: rz.results[1].stdout- name: ahdebug: var: rz.results[2].stdout
结果:
TASK [ad] *******************************************************************************************************************
changed: [node1] => (item=haha)
changed: [node1] => (item=heihei)
changed: [node1] => (item=hehe)TASK [af] *******************************************************************************************************************
ok: [node1] => {"rz.results[0].stdout": "haha"
}TASK [ag] *******************************************************************************************************************
ok: [node1] => {"rz.results[1].stdout": "heihei"
}TASK [ah] *******************************************************************************************************************
ok: [node1] => {"rz.results[2].stdout": "hehe"
}
三、Ansible 机密管理(ansible-vault)
当剧本 / 变量文件包含敏感信息(如密码、API 密钥)时,使用 ansible-vault 加密文件,防止信息泄露。支持创建、编辑、加解密文件等操作。
3.1 核心操作命令
命令 | 作用 | 示例 |
ansible-vault create 文件名 | 创建新的加密文件(默认用 vi 编辑) | ansible-vault create secret.yml |
ansible-vault view 文件名 | 查看加密文件内容(需输入密码) | ansible-vault view secret.yml |
ansible-vault edit 文件名 | 编辑加密文件(需输入密码) | ansible-vault edit secret.yml |
ansible-vault encrypt 文件名 | 加密已存在的普通文件 | ansible-vault encrypt plain.yml |
ansible-vault decrypt 文件名 | 永久解密加密文件(需输入密码) | ansible-vault decrypt secret.yml |
ansible-vault rekey 文件名 | 修改加密文件的密码 | ansible-vault rekey secret.yml |
ansible-vault encrypt 文件名 --vault-id 密码文件 | 用密码文件加密(无交互) | ansible-vault encrypt e.yml --vault-id secret.txt |
3.2 执行加密剧本
加密后的文件无法直接执行,需通过以下方式提供密码:
方式 1:交互式输入密码(--ask-vault-pass)
ansible-playbook --ask-vault-pass secret.yml# 执行时会提示输入加密密码
方式 2:用密码文件执行(无交互,--vault-id)
- 创建密码文件(仅 root 可读,确保安全):
echo "123456" > secret.txt # 密码为 123456chmod 600 secret.txt # 限制权限,防止他人读取
- 用密码文件执行加密剧本:
ansible-playbook --vault-id secret.txt secret.yml
3.3 示例:创建并执行加密剧本
步骤 1:创建加密剧本
假设需要创建一个剧本 aa.yml,通过 ansible-vault create 直接创建加密文件:
# 创建加密剧本,执行后会提示输入密码(需牢记,后续执行/编辑需用到)ansible-vault create aa.yml
输入密码后,会进入 vi 编辑器,在编辑器中写入剧本内容
保存退出 vi 编辑器后,aa.yml 会以加密形式存储,直接用 cat 查看会显示乱码(确保敏感信息安全):
步骤 2:查看加密剧本内容
若需确认加密剧本中的内容,不能直接用 cat,需通过 ansible-vault view 并输入密码查看:
ansible-vault view aa.yml
输入创建时设置的密码后,会正常显示剧本内容
步骤 3:编辑加密剧本
若需修改加密剧本(如更新数据库密码),通过 ansible-vault edit 操作,同样需要输入密码:
ansible-vault edit aa.yml
输入密码后进入 vi 编辑器, 若修改过文件,保存退出后,加密文件会自动更新(无需重新加密)。
步骤 4:执行加密剧本
加密剧本无法直接用 ansible-playbook aa.yml 执行,会报错 “ERROR! Attempted to read encrypted file without password”,需通过以下两种方式提供密码:
方式 1:交互式输入密码(适合临时执行)
执行时加 --ask-vault-pass 选项,会提示输入密码:
ansible-playbook --ask-vault-pass aa.yml
输入正确密码后,剧本正常执行,输出结果
方式 2:无交互执行(适合自动化场景)
若需在脚本或自动化流程中执行加密剧本,可先创建 “密码文件”,避免手动输入密码:
- 创建密码文件(仅 root 可读,防止密码泄露):
echo "123456" > secret.txt # 密码为 123456chmod 600 secret.txt # 限制权限,防止他人读取
- 通过 --vault-id 指定密码文件执行:
ansible-playbook --vault-id secret.txt aa.yml
执行后无需手动输入密码,剧本会自动读取密码文件并执行。
步骤 5:解密 / 重新加密剧本(可选)
- 永久解密:若后续无需加密,可将加密文件解密为普通文件(不可逆,需谨慎):
ansible-vault decrypt aa.yml
输入密码后,db_deploy.yml 会变为普通文件,可用 cat 直接查看。
- 重新加密:若需修改加密密码,通过 ansible-vault rekey 操作:
ansible-vault rekey aa.yml
执行后会先提示输入 “旧密码”,验证通过后再输入 “新密码”,完成后文件会用新密码重新加密。