管理变量、机密和事实

tech2025-11-28  6

管理变量、机密和事实

管理变量

Ansible变量简介

Ansible支持利用变量来存储值,并在Ansible项目的所有文件中重复使用这些值。这可以简化项目的创建和维护,并减少错误的数量。

通过变量,可以轻松地在Ansible项目中管理给定环境的动态值。例如:

要创建的用户要安装的软件包要重新启动的服务要删除的文件要从互联网检索的存档

命名变量

变量的名称必须以字母开头,并且只能包含字母、数字和下划线。

示例:web server:错误的变量名 web_server:正确的变量名


定义变量

可以在Ansible项目中的多个位置定义变量。这些变量大致可简化为三个范围级别:

全局范围:从命令行或Ansible配置设置的变量play范围:在play和相关结构中设置的变量主机范围:由清单、事实收集或注册的任务,在主机组和个别主机上设置的变量

如果在多个级别定义了相同名称的变量,则采用优先级别最高的变量。窄范围优先于更广泛的范围:由清单定义的变量将被playbook定义的变量覆盖,后者将被命令行中定义的变量覆盖。


playbook中的变量

变量在Ansible Playbook中发挥着重要作用,因为它们可以简化playbook中变量数据的管理。

在playbook中定义变量

Playbook变量可以通过多种方式定义。一种常见的方式是将变量放在playbook开头的vars块中: --- - hosts: 192.168.86.132 vars: package_name: httpd tasks: - name: install httpd yum: name: '{{ package_name }}' #变量一定要写用引号 state: present 也可以在外部文件中定义playbook变量。此时不使用playbook中的vars块,可以改为使用vars_files指令,后面跟上相对于playbook位置的外部变量文件名称列表: [root@ansible playbook]# mkdir vars && cd vars && echo "package_name: httpd" > httpd.yml [root@ansible playbook]# vim test.yml --- - hosts: 192.168.86.132 vars_files: - vars/httpd.yml tasks: - name: install httpd yum: name: '{{ package_name }}' state: present 声明了变量后,可以在任务中使用这些变量。若要引用变量,可以将变量名放在双大括号内。在任务执行时,Ansible会将变量替换为其值。需要注意的是:当变量用作开始一个值的第一元素时,必须使用引号。这可以防止Ansible将变量引用视为YAML字典的开头。

主机变量和组变量

直接应用于主机的清单变量分为两在类:

主机变量,应用于特定主机组管理,应用于一个主机组或一组主机组中的所有主机

主机变量优先于组变量,但playbook中定义的变量的优先级比这两者更高。

若要定义主机变量和组变量,一种方法是直接在清单文件中定义,它使得清单文件更难以处理,不建议采用

//定义192.168.86.132的ansible_user主机变量 [webservers] 192.168.86.132 ansible_user=joe //定义webservers主机组的user组变量: [webservers] 192.168.86.132 192.168.86.134 [webservers:vars] user=joe //定义servers组的user组变量,该组由两个主机组成,每个主机组有两个服务器 [webservers1] 123.example.com 456.example.com [webservers2] 789.example.com 666.example.com [webservers:children] webservers1 webservers2 [webservers:vars] user=joe

使用目录填充主机和组变量

定义主机和主机组的变量的首选做法是在与清单文件或目录相同的工作目录中,创建group_vars和host_vars两个目录。这两个目录分别包含用于定义组变量和主机变量的文件。

示例:

[root@ansible ~]# mkdir httpd [root@ansible ~]# cd httpd [root@ansible httpd]# ls files group_vars host_vars inventory playbook.yml [root@ansible httpd]# vim inventory 192.168.86.132 //此时配置文件中没有写指定远程登录的密码 [root@ansible httpd]# ansible 192.168.86.132 -i inventory -m ping 192.168.86.132 | UNREACHABLE! => { "changed": false, "msg": "Failed to connect to the host via ssh: root@192.168.86.132: Permission denied (publickey,gssapi-keyex,gssapi-with-mic,password).", "unreachable": true } //测试发现不能ping通 [root@ansible httpd]# vim host_vars/192.168.86.132 ansible_password: 123456 //在host_vars目录下创建一个和主机名一样名字的文件,注意一定要是一样的名字,然后定义文件的变量 [root@ansible httpd]# ansible 192.168.86.132 -i inventory -m ping 192.168.86.132 | SUCCESS => { "ansible_facts": { "discovered_interpreter_python": "/usr/bin/python" }, "changed": false, "ping": "pong" } //使用组变量 [root@ansible httpd]# vim inventory //定义一个组名,组里面可以包含多个主机 [webserve] 192.168.86.132 [root@ansible httpd]# vim group_vars/webserve ansible_password: 123456 [root@ansible httpd]# ansible 192.168.86.132 -i inventory -m ping 192.168.86.132 | SUCCESS => { "ansible_facts": { "discovered_interpreter_python": "/usr/bin/python" }, "changed": false, "ping": "pong" }

从命令行覆盖变量

清单变量可被playbook中设置的变量覆盖,这两种变量又可通过在命令行中传递参数到ansible或ansible-playbook命令来覆盖。在命令行上设置的变量称为额外变量,使用**-e**选择来指定额外变量 [root@ansible httpd]# ansible 192.168.86.132 -i inventory -e "ansible_passwrod=123456" -m ping 192.168.86.132 | SUCCESS => { "ansible_facts": { "discovered_interpreter_python": "/usr/bin/python" }, "changed": false, "ping": "pong" } //也可以把变量写在一个文件中用-e选项来指定 [root@ansible httpd]# vim xx ansible_password: 123456 [root@ansible httpd]# ansible 192.168.86.132 -i inventory -e @xx -m ping 192.168.86.132 | SUCCESS => { "ansible_facts": { "discovered_interpreter_python": "/usr/bin/python" }, "changed": false, "ping": "pong" }

使用数组作为变量

除了将同一元素相关的配置数据(软件包列表、服务列表和用户列表等)分配到多个变量外,也可以使用数组。这种做法的一个好处在于,数组是可以浏览的。

示例:

users: aa: xing: wang ming: wu home: /home/wangwu bb: xing: zhang ming: san home: /home/zhangsan //然后可以使用以下变量来访问用户数据 users.aa.xing (不推荐) //推荐使用中括号的方式,使用中括号表示法有助于避免冲突和错误。 users['aa']['xing']

上面介绍的两种语法都有效,但为了方便故障排除,建议在任何给定Ansible项目的所有文件中一致地采用一种语法,不要混用。


使用已注册变量捕获命令输出

可以使用register语句捕获命令输出。输出保存在一个临时变量中,然后在playbook中可用于调试用途或者达成其他目的 --- - name: hosts: 192.168.86.132 tasks: - name: user: name: cvg state: present register: useradd_result - debug: var=useradd_result //debug模块用于将install_result注册变量的值转储到终端。 [root@ansible ~]# ansible-playbook -C test.yml PLAY [useradd cvg] ************************************************************************************ TASK [Gathering Facts] ******************************************************************************** ok: [192.168.86.132] TASK [useradd cvg] ************************************************************************************ changed: [192.168.86.132] TASK [debug] ****************************************************************************************** ok: [192.168.86.132] => { "useradd_result": { "changed": true, "failed": false } } ...

管理机密

Ansible Vault

Ansible可能需要访问密码或API密钥等敏感数据,以便能配置受管主机,但是这些敏感数据是任何有权限访问Ansible文件的用户都能查看,这样是不安全。Ansible提供的Ansible Vault可以加密和解密任何由Ansible使用的结构化数据文件。

创建加密文件

使用ansible-vault create filename命令创建加密文件,然后输入的vault密码 [root@ansible group_vars]# ansible-vault create webserve New Vault password: Confirm New Vault password: 还可以用vault密码文件来存储vault密码,而不是通过标准输入途径输入vault密码。这样做需要使用文件权限和其他方式来严密保护该文件。 [root@ansible group_vars]# vim vault_password 123456 [root@ansible group_vars]# ansible-vault create --vault-password-file=vault_password webserve

查看加密的文件

使用ansible-vault view filename命令查看Ansible Vault加密的文件 [root@ansible group_vars]# ansible-vault view webserve Vault password: ansible_password: 123456

编辑现有的加密文件

使用ansible-vault edit filename命令。此命令将文件解密为一个临时文件,并允许编辑。保存时,它将复制其内容并删除临时文件。 [root@ansible group_vars]# ansible-vault edit webserve Vault password:

注意:不要用vi/vim直接编辑加密文件,这样会导致加密文件无法打开


加密现有的文件

使用ansible-vault encrypt filename命令。此命令可取多个想要加密文件的名称作为参数。 //创建加密文件 [root@ansible group_vars]# vim webserve ansible_password: 123456 //[root@ansible group_vars]# ansible-vault encrypt webserve New Vault password: Confirm New Vault password: Encryption successful

解密现有的文件

使用ansible-vault decrypt filename命令永久解密。在解密单个文件时,可使用**–output**选项以其他名称保存解密的文件。 [root@ansible group_vars]# ansible-vault decrypt webserve Vault password: Decryption successful [root@ansible group_vars]# cat webserve ansible_password: redhat

更改加密的密码

使用ansible-vault rekey filename命令更改加密文件的密码。此命令可一次性更新多个数据文件的密钥。它将提示提供原始密码和新密码。 [root@ansible group_vars]# ansible-vault rekey webserve Vault password: //当前密码 New Vault password: //输入要更改的新密码 Confirm New Vault password: //再次输入要更改的新密码 Rekey successful 在使用vault密码文件时,请使用–new-vault-password-file选项 ansible-vault rekey --new-vault-password-file=NEW_VAULT_PASSWORD_FILE webserve

playbook和ansible vault

要运行可访问通过Ansible Vault加密的文件的playbook,需要向ansible-playbook命令提供加密密码。如果不提供密码,playbook将返回错误 [root@ansible httpd]# ansible all -i inventory -m ping ERROR! Attempting to decrypt but no vault secrets found 要为playbook提供vault密码,可使用–vault-id @prompt选项。 [root@ansible httpd]# ansible 192.168.86.132 -i inventory --vault-id @prompt -m ping Vault password (default): 192.168.86.132 | SUCCESS => { "ansible_facts": { "discovered_interpreter_python": "/usr/bin/python" }, "changed": false, "ping": "pong" } 也可使用–vault-password-file选项指定以纯文本存储加密密码的文件。密码应当在该文件中存储为一行字符串。由于该文件包含敏感的纯文本密码,因此务必要通过文件权限和其他安全措施对其加以保护。 ansible all --vault-password-file=vault-pw-file webserve 也可以使用ANSIBLE_VAULT_PASSWORD_FILE环境变量,指定密码文件的默认位置。从Ansible2.4开始,可以通过ansible-playbook使用多个Ansible Vault密码。要使用多个密码,需要将多个**–vault-id或–vault-password-file选项传递给ansible-playbook**命令。 ansible all --vault-id one@prompt --vault-id two@prompt webserve //这种用法一般不会使用

注意:@prompt前面的vaultIDone和two可以是任何字符,甚至可以完全省略它们。不过,如果在使用ansible-vault命令加密文件时使用–vault-id id选项,则在运行ansible-playbook时,将最先尝试匹配ID的密码。如果不匹配,将会尝试用户提供的其他密码。没有ID的vaultID@prompt实际上是default@prompt的简写,这意味着提示输入vaultIDdefault的密码。


变量文件管理的推荐做法

设置Ansible项目,简化管理,使敏感变量和其他变量保存在相互独立的文件中。然后,包含敏感变量的文件可通过ansible-vault命令进行保护。

管理组变量和主机变量的首选方式是在playbook级别上创建目录。group_vars目录通常包含名称与它们所应用的主机组匹配的变量文件。host_vars目录通常包含名称与它们所应用的受管主机名称匹配的变量文件。

除了使用group_vars和host_vars中的文件外,也可对每一主机组或受管主机使用目录。这些目录可包含多个变量文件,它们都由该主机组或受管主机使用

[root@ansible httpd]# tree . . ├── files ├── group_vars │ └── webserve │ ├── vars //不需要加密的变量 │ └── vault //需要加密的变量,用ansible-vault进行加密 ├── host_vars ├── inventory └── playbook.yml

管理事实

描述Ansible事实

setup模块用来收集事实,每个play在执行第一个任务之前会先自动运行setup模块来收集事实。

Ansible事实是Ansible在受管主机上自动检测到的变量。事实中包含有与主机相关的信息,可以像play中的常规变量、条件、循环或依赖于从受管主机收集的值的任何其他语句那样使用。收集的事实可能包括:

主机名称内核版本网络接口IP地址操作系统版本各种环境变量CPU数量提供的或可用的内存可用磁盘空间

借助事实,可以方便地检索受管主机的状态,并根据该状态确定要执行的操作。

可以根据含有受管主机当前内核版本的事实运行条件任务,以此来重启服务器可以根据通过事实报告的可用内存来自定义MySQL配置文件可以根据事实的值设置配置文件中使用的IPv4地址

查看为受管主机收集的事实的一种方式是,运行一个收集事实并使用debug模块显示ansible_facts变量值的简短playbook。

--- - name: fact hosts: 192.168.86.132 tasks: - name: fact debug: var: ansible_facts //Playbook以JSON(字典)格式显示ansible_facts变量的内容。 [root@ansible ~]# ansible-playbook play.yml PLAY [fact] ********************************************************************************************** TASK [Gathering Facts] *********************************************************************************** ok: [192.168.86.132] TASK [fact] ********************************************************************************************** ok: [192.168.86.132] => { "ansible_facts": { "all_ipv4_addresses": [ "192.168.86.132" ], "all_ipv6_addresses": [ "fe80::20c:29ff:fe06:55a2" ], "ansible_local": {}, "apparmor": { "status": "disabled" ... Ansible事实的示例 事实变量短主机名ansible_facts[‘hostname’]完全限定域名ansible_facts[‘fqdn’]IPv4地址ansible_facts[ ‘default_ipv4’ ] [‘address’]所有网络接口的名称列表所有网络接口的名称列表/dev/vda1磁盘分区的大小ansible_facts[‘devices’] [‘vda’] [‘partitions’] [‘vda1’] ‘size’]DNS服务器列表ansible_facts[‘dns’] [‘nameservers’]当前运行的内核版本ansible_facts[‘kernel’]

示例:

--- - name: fact hosts: 192.168.86.132 tasks: - name: fact debug: var: ansible_facts['hostname'] [root@ansible ~]# ansible-playbook play.yml PLAY [fact] ******************************************************************************************* TASK [Gathering Facts] ******************************************************************************** ok: [192.168.86.132] TASK [fact] ******************************************************************************************* ok: [192.168.86.132] => { "ansible_facts['hostname']": "localhost" } PLAY RECAP ******************************************************************************************** 192.168.86.132 : ok=2 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0 //取出sda下的sda1的分区大小 --- - name: fact hosts: 192.168.86.132 tasks: - name: fact debug: var: ansible_facts['devices']['sda']['partitions']['sda1']['size'] [root@ansible ~]# ansible-playbook play.yml PLAY [fact] ****************************************************************************************************************************************************************************************************** TASK [Gathering Facts] ******************************************************************************************************************************************************************************************* ok: [192.168.86.132] TASK [fact] ****************************************************************************************************************************************************************************************************** ok: [192.168.86.132] => { "ansible_facts['devices']['sda']['partitions']['sda1']['size']": "1.00 GB" } PLAY RECAP ******************************************************************************************************************************************************************************************************* 192.168.86.132 : ok=2 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0

如果变量的值为散列/字典类型,则可使用下面这种语法来获取其值

ansible_facts[‘dns’] [‘nameservers’]也可以写为ansible_facts.dns.nameservers,不推荐使用这种方法 --- - name: fact hosts: 192.168.86.132 tasks: - name: fact debug: var: ansible_facts.dns.nameservers [root@ansible ~]# ansible-playbook play.yml PLAY [fact] ****************************************************************************************************************************************************************************************************** TASK [Gathering Facts] ******************************************************************************************************************************************************************************************* ok: [192.168.86.132] TASK [fact] ****************************************************************************************************************************************************************************************************** ok: [192.168.86.132] => { "ansible_facts.dns.nameservers": [ "114.114.114.114" ] } PLAY RECAP ******************************************************************************************************************************************************************************************************* 192.168.86.132 : ok=2 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0

在playbook中使用事实时,Ansible将事实的变量名动态替换为对应的值

--- - hosts: all tasks: - name: Prints various Ansible facts debug: msg: > The machine and ipv4 address of {{ ansible_facts['machine'] }} //ansible_machine前面的ansible_应该省略不写 is {{ ansible_facts.default_ipv4.address }} [root@ansible ~]# ansible-playbook play.yml PLAY [all] ******************************************************************************************************************************************************************************************************* TASK [Gathering Facts] ******************************************************************************************************************************************************************************************* ok: [192.168.86.132] TASK [Prints various Ansible facts] ****************************************************************************************************************************************************************************** ok: [192.168.86.132] => { "msg": "The machine and ipv4 address of x86_64 is 192.168.86.132\n" } PLAY RECAP ******************************************************************************************************************************************************************************************************* 192.168.86.132 : ok=2 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0

Ansible事实作为变量注入

在Ansible2.5之前,事实是作为前缀为字符串**ansible_**的单个变量注入,而不是作为ansible_facts变量的一部分注入。例如,ansible_facts[‘distribution’]事实会被称为ansible_distribution。

Ansible事实名称对比 Ansible_facts形式旧事实变量形式ansible_facts[‘hostname’]ansible_hostnameansible_facts[‘fqdn’]ansible_fqdnansible_facts[‘default_ipv4’] [‘address’]ansible_default_ipv4[‘address’]ansible_facts[‘interfaces’]ansible_interfacesansible_facts[‘dns’] [‘nameservers’]ansible_dns[‘nameservers’] 目前,Ansible同时识别新的事实命名系统(使用ansible_facts)和旧的2.5前“作为单独变量注入的事实”命名系统。如果将Ansible配置文件的**[default]部分中inject_facts_as_vars参数设置为False**,可关闭旧命名系统。默认设置目前为True。在未来的版本中旧的写法可能会默认更改为False,所有建议用新的形式 [root@ansible ~]# vim /etc/ansible/ansible.cfg ... # inject_facts_as_vars = True ...

关闭事实收集

关闭收集事实的原因可能有:

不准备使用任何事实希望加快play速度希望减小play在受管主机上造成的负载受管主机因为某种原因无法运行setup模块(如:受管主机可能是路由器、交换机等设备)需要安装一些必备软件后再收集事实

将gather_facts关键字设置为no可以禁用setup

--- - hosts: all gather_facts: no tasks: - name: Prints various Ansible facts debug: msg: > The machine and ipv4 address of {{ ansible_facts['machine'] }} is {{ ansible_facts.default_ipv4.address }} [root@ansible ~]# ansible-playbook play.yml PLAY [all] ******************************************************************************************** TASK [Prints various Ansible facts] ******************************************************************* fatal: [192.168.86.132]: FAILED! => {"msg": "The task includes an option with an undefined variable. The error was: 'dict object' has no attribute 'machine'\n\nThe error appears to be in '/root/play.yml': line 5, column 5, but may\nbe elsewhere in the file depending on the exact syntax problem.\n\nThe offending line appears to be:\n\n tasks:\n - name: Prints various Ansible facts\n ^ here\n"} PLAY RECAP ******************************************************************************************** 192.168.86.132 : ok=0 changed=0 unreachable=0 failed=1 skipped=0 rescued=0 ignored=0 即使play设置了gather_facts: no,也可以随时通过运行使用setup模块的任务来手动收集事实 --- - hosts: all gather_facts: no tasks: - name: start setup setup: - name: Prints various Ansible facts debug: msg: > The machine and ipv4 address of {{ ansible_facts['machine'] }} is {{ ansible_facts.default_ipv4.address }} [root@ansible ~]# ansible-playbook play.yml PLAY [all] ******************************************************************************************** TASK [start setup] *********************************************************************************** ok: [192.168.86.132] TASK [Prints various Ansible facts] ******************************************************************* ok: [192.168.86.132] => { "msg": "The machine and ipv4 address of x86_64 is 192.168.86.132\n" } PLAY RECAP ******************************************************************************************** 192.168.86.132 : ok=2 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0

创建自定义事实

除了使用系统捕获的事实外,我们还可以自定义事实,并将其本地存储在每个受管主机上。这些事实整合到setup模块在受管主机上运行时收集的标准事实列表中。它们让受管主机能够向Ansible提供任意变量,以用于调整play的行为,自定义事实可以在静态文件中定义,格式可为INI文件或采用JSON。它们也可以是生成JSON输出的可执行脚本,如同动态清单脚本一样

默认情况下,setup模块从各受管主机的**/etc/ansible/facts.d目录下的文件和脚本中加载自定义事实。各个文件或脚本的名称必须以.fact**结尾才能被使用。动态自定义事实脚本必须输出JSON格式的事实,而且必须是可执行文件。

使用INI格式编写自定义事实,INI格式的自定义事实文件包含由一个部分定义的顶层值,后跟用于待定义的事实的键值对

[webserves] web1 = 192.168.11.110 web2 = 192.168.66.66 使用JSON格式编写自定义事实,JSON数据可以存储在静态文本文件中,或者通过可执行脚本输出到标准输出 "webserves": { "web1": "192.168.11.110", "web2": "192.168.66.66" }

自定义事实文件不能采用playbook那样的YAML格式。JSON格式是最为接近的等效格式

自定义事实由setup模块存储在ansible_facts.ansible_local变量中。事实按照定义它们的文件的名称来整理 --- - hosts: all tasks: - name: Prints various Ansible facts debug: var: ansible_facts['ansible_local']['cwt']['webserves']['web1'] [root@ansible ~]# ansible-playbook play.yml PLAY [all] ******************************************************************************************************************************************************************************************************* TASK [Gathering Facts] ******************************************************************************************************************************************************************************************* ok: [192.168.86.132] TASK [Prints various Ansible facts] ****************************************************************************************************************************************************************************** ok: [192.168.86.132] => { "ansible_facts['ansible_local']['cwt']['webserves']['web1']": "192.168.11.110" } PLAY RECAP ******************************************************************************************************************************************************************************************************* 192.168.86.132 : ok=2 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0

使用魔法变量

一些变量并非事实或通过setup模块配置,但也由Ansible自动设置,我们称之为魔法变量,魔法变量可用于获取受管主机相关的信息。常用的有4个: 魔法变量说明hostvars包含受管主机的变量,可以用于获取另一台受管主机的变量的值。如果还没有为受管主机收集事实,则它不会包含该主机的事实。group_names列出当前受管主机所属的所有组groups列出清单中的所有组和主机inventory_hostname包含清单中配置的当前受管主机的主机名称。因为各种原因有可能与事实报告的主机名称不同 可以使用debug模块报告特定主机的groups变量的内容 [root@ansible ~]# ansible all -m debug -a 'var=groups' 192.168.86.132 | SUCCESS => { "groups": { "all": [ "192.168.86.132" ], "ungrouped": [ "192.168.86.132" ] } } 关于魔法变量的更多信息可以参考官方文档:https://docs.ansible.com/ansible/latest/reference_appendices/special_variables.html#special-variables
最新回复(0)