一、管理机密
1、什么是Ansible Vault
Ansible提供的Ansible Vault可以加密和解密任何由Ansible使用的结构化数据文件 若要使用Ansible Vault,可通过一个名为ansible-vault的命令行工具创建、编辑、加密、解密和查看文件 Ansible Vault可以加密任何由Ansible使用的结构化数据文件。 包括清单变量、playbook中含有的变量文件、以及在执行playbook时作为参数传递的变量文件,或者Ansible角色中定义的变量
2、创建加密文件
1)用ansible-vault filename命令创建加密的文件,该命令在执行时会提示输入新的vault密码,然后利用默认编辑器vi打开文件
[root@ansible ansible]
New Vault password: //输入密码
Confirm New Vault password: //在此输入密码
[root@ansible ansible]
$ANSIBLE_VAULT;1.1;AES256
66366535343734366239343665613664613630613533306437353663653966373265626232343161
6230366139323732386561393462643439336335643934350a653563393234333363373236666565
35636232656264323937303833386562343736303834386638653562333963383564623333386237
3863383861616233620a623139343361386561643431363664373063323265326563353636363164
3664 //已经加密
2)使用vault密码文件来存储vault密码,而不是通过标准输入途径输入vault密码,这样做需要使用文件权限和其他方式来严密保护该文件
//首先创建密码文件
[root@ansible ansible]
123456
[root@ansible ansible]
[root@ansible ansible]
-rw------- 1 root root 7 7月 25 01:27 password
//创建加密的YAML文件
[root@ansible ansible]
[root@ansible ansible]
$ANSIBLE_VAULT;1.1;AES256
64396164613463393661613035346161366162393939323732633663353964346236333065656536
3763393863363938316633346365646462623539343833620a386134373233363932383035303462
61646466626464393461386339373936623937366662366537366130306333653030346364643962
3732333438323835660a303432623666633038386438303561663662363035653034653765643536
3461
3、查看已经加密的文件
使用ansible-vault view filename命令查看Ansible Vault加密的文件,而不必打开它进行编辑
[root@ansible playbook]
$ANSIBLE_VAULT;1.1;AES256
64336531303962353132623036393334666432343235623338303562613135336230663838353934
6636333232343536363063363537643132333736333037640a656430353134613738646261343334
34373339396165363866343533656335346637316665663261613536623037376262353864383931
6133366332353062350a303361373432653439623534616464343931383431623862353134373738
35353133393337636664653133346662353234303838663563323263316432303536
[root@ansible playbook]
Vault password:
password: 123456
4、编辑已经加密的文件
Ansible Vault提供ansible-vault edit filename命令继续编辑已经加密文件;工作时将文件解密为一个临时文件,并允许编辑;保存时,它将复制其内容并删除临时文件
//查看已经加密文件内容
[root@ansible playbook]
Vault password:
password: 123456
//继续添加内容
[root@ansible playbook]
Vault password:
[root@ansible playbook]
Vault password:
password: 123456
hello lh
5、加密和解密现有的未加密的文件
使用ansible-vault encrypt filename加密现有的未加密的文件;此命令可以一次加密多个现有的未加密文件
其次还可以使用–output选项将需要加密的文件加密后另存为具有新名称的文件,一次只能有一个输入文件
ansible-vault encrypt playbook.yml --output=test.yml
使用anisble-vault decrypt filename解密现有的已经加密的文件,并且是永久解密 解密也可以是使用–output选项,在解密单个文件时加改选项将解密文件以另一个新名称来保存文件
ansible-vault decrypt playbook.yml --output=test.yml
实例:
[root@ansible playbook]
---
- hosts: apache
vars:
name: yy
uid: 2000
tasks:
- name: create user
user:
name: "{{ name }}"
uid: "{{ uid }}"
state: present
//加密
[root@ansible playbook]
New Vault password:
Confirm New Vault password:
Encryption successful
[root@ansible playbook]
$ANSIBLE_VAULT;1.1;AES256
36363533306262313933346138613331643034346438633330383432336338633266656466306539
3266343965316361613866343830643265663238633462610a346436363434346266376633373762
32373930353239313261623466613433613034656136333135643032383264383737336532396430
6434383939376136390a313631636330383331313064363364666434633731663733303561396135
62353439326433303834353963333565323565636432306336653261383534323466613636336136
33333761636638643030393862323333376239346438646630366238303239343562376135626462
33376431373339376234393466363636663630656537383539643038383636376564373836663566
62656632346238623330366231653032653635636634633033646166376336366534303464313238
39616538376138636536383264313566633238306264313662633837643734316539383637306232
34336436343636613765313533643036323438333631363632393232393765353036346561366166
32336133373639386535376362366535633866376461643630396431353837366635343031646537
35366233376532333964343430303063353433633565626531376632646632373931666438353432
65313533613337313664363966636130363564306238373166386134666438366161
//解密
[root@ansible playbook]
Vault password:
Decryption successful
[root@ansible playbook]
---
- hosts: apache
vars:
name: yy
uid: 2000
tasks:
- name: create user
user:
name: "{{ name }}"
uid: "{{ uid }}"
state: present
[root@ansible playbook]
6、更改已经加密文件的密码
使用ansible-vault rekey filename 命令更改已经加密文件的密码;此命令一次可以更改多个已经加密文件密码;并且需要提供原始密码
[root@ansible playbook]
Vault password: //此处我设置的密码是1
---
- hosts: apache
vars:
name: yy
uid: 2000
tasks:
- name: create user
user:
name: "{{ name }}"
uid: "{{ uid }}"
state: present
//更改密码为111
[root@ansible playbook]
Vault password: //输入原始密码
New Vault password: //输入新密码
Confirm New Vault password:
Rekey successful
[root@ansible playbook]
除此之外还可以使用vault文件作为面密码:使用–new-vault-password-file选项指定更改的密码
ansible-vault rekey --new-vault-password-file=./new_password test.yml
7、执行ansible-playbook与交互密码
通过Ansible Vault加密的文件的playbook,需要向ansible-playbook命令提供加密密码 为playbook提供vault密码,可使用–vault-id选项,以交互方式提供vault密码
要为playbook提供vault密码,可使用–vault-id选项。例如,要以交互方式提供vault密码,请使用下例中所示的–vault-id @prompt: 实例:
[root@ansible playbook]
Vault password (default):
PLAY [apache] *************************************************************************
TASK [Gathering Facts] ****************************************************************
ok: [192.168.235.141]
TASK [create user] ********************************************************************
ok: [192.168.235.141]
PLAY RECAP ****************************************************************************
192.168.235.141 : ok=2 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
此外,也可以使用–vault-password-file选项指定密码文件;密码应当在该文件中存储为一行字符串
[root@ansible ansible]
PLAY [create user] ********************************************************************
TASK [Gathering Facts] ****************************************************************
ok: [192.168.235.141]
TASK [create user] ********************************************************************
changed: [192.168.235.141]
PLAY RECAP ****************************************************************************
192.168.235.141 : ok=2 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
也可以使用ANSIBLE_VAULT_PASSWORD_FILE环境变量,指定密码文件的默认位置 从Ansible2.4开始,可以通过ansible-playbook使用多个Ansible Vault密码。要使用多个密码,需要将多个–vault-id或–vault-password-file选项传递给ansible-playbook命令 实例:
[root@ansible ansible]
Vault password (1):
PLAY [create user] ********************************************************************
TASK [Gathering Facts] ****************************************************************
ok: [192.168.235.141]
TASK [create user] ********************************************************************
ok: [192.168.235.141]
PLAY RECAP ****************************************************************************
192.168.235.141 : ok=2 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
8、变量文件管理的推荐做法
1??:简化管理,务必要设置Ansible项目,使敏感变量和其他变量保存在相互独立的文件中;包含敏感变量的文件可通过ansible-vault命令进行保护
2??:管理组变量和主机变量的首选方式是在playbook级别上创建目录;
group_vars目录通常包含名称与它们所应用的主机组匹配的变量文件 host_vars目录通常包含名称与它们所应用的受管主机名称匹配的变量文件 3??:Playbook变量(与清单变量相对)也可通过Ansible Vault加密保护;敏感的playbook变量可以放在单独的文件中,此文件通过Ansible Vault加密,并且vars_files指令包含在该playbook中
4??:如果需要在playbook中使用多个vault密码,请确保每个加密文件分配一个vaultID,并在运行playbook时输入具有该vaultID的匹配密码
二、管理事实
1、Ansible事实描述
Ansible事实是Ansible在受管主机上自动检测到的变量
事实(fact)中包含有与主机相关的信息,可以像play中的常规变量、条件、循环或依赖于从受管主机收集的值的任何其他语句那样使用
一些事实可能包括: 主机名称、内核版本、网络接口、IP地址、操作系统版本、各种环境变量、CPU数量、提供的或可用的内存、可用磁盘空间等等
借助事实,可以方便地检索受管主机的状态,并根据该状态确定要执行的操作
例如:
- 可以根据含有受管主机当前内核版本的事实运行条件任务,以此来重启服务器
- 可以根据通过事实报告的可用内存来自定义MySQL配置文件
- 可以根据事实的值设置配置文件中使用的IPv4地址
每个play在执行第一个任务之前会先自动运行setup模块来收集事实 实例:
[root@ansible playbook]# cat facts.yml
---
- hosts: apache
tasks:
- name: 收集事实
debug:
var: ansible_facts
[root@ansible playbook]# ansible-playbook facts.yml
PLAY [apache] *************************************************************************
TASK [Gathering Facts] ****************************************************************
ok: [192.168.235.141]
TASK [收集事实] ***************************************************************************
ok: [192.168.235.141] => {
"ansible_facts": {
"all_ipv4_addresses": [
"192.168.235.141",
"192.168.122.1"
],
"all_ipv6_addresses": [
"fe80::20c:29ff:fed7:4b2"
],
"ansible_local": {},
"apparmor": {
"status": "disabled"
},
"architecture": "x86_64",
"bios_date": "02/27/2020",
"bios_version": "6.00",
"cmdline": {
"BOOT_IMAGE": "(hd0,msdos1)/vmlinuz-4.18.0-193.el8.x86_64",
"biosdevname": "0",
"crashkernel": "auto",
"net.ifnames": "0",
"quiet": true,
"rd.lvm.lv": "rhel/swap",
"resume": "/dev/mapper/rhel-swap",
"rhgb": true,
"ro": true,
"root": "/dev/mapper/rhel-root"
},
"date_time": {
"date": "2021-07-25",
"day": "25",
"epoch": "1627203996",
"hour": "05",
"iso8601": "2021-07-25T09:06:36Z",
"iso8601_basic": "20210725T050636627687",
"iso8601_basic_short": "20210725T050636",
"iso8601_micro": "2021-07-25T09:06:36.627687Z",
"minute": "06",
"month": "07",
"second": "36",
"time": "05:06:36",
"tz": "EDT",
"tz_offset": "-0400",
"weekday": "星期日",
"weekday_number": "0",
"weeknumber": "29",
"year": "2021"
},
············略
Playbook以JSON格式显示ansible_facts变量的内容 Ansible事实的示例:
事实 | 变量 |
---|
短主机名 | ansible_facts[‘hostname’] | 完全限定域名 | ansible_facts[‘fqdn’] | IPv4地址 | ansible_facts[‘default_ipv4’][‘address’] | 所有网络接口的名称列表 | ansible_facts[‘interfaces’] | /dev/vda1磁盘分区的大小 | ansible_facts[‘devices’][‘vda’][‘partitions’][‘vda1’][‘size’] | DNS服务器列表 | ansible_facts[‘dns’][‘nameservers’] | 当前运行的内核版本 | ansible_facts[‘kernel’] |
在playbook中使用事实时,Ansible将事实的变量名动态替换为对应的值 实例:
[root@ansible playbook]
---
- hosts: apache
tasks:
- name: 收集事实
debug:
msg: >
this host ip is {{ ansible_facts['default_ipv4']['address'] }}
this hostname is {{ ansible_facts['hostname']}}
[root@ansible playbook]
PLAY [apache] *************************************************************************
TASK [Gathering Facts] ****************************************************************
ok: [192.168.235.141]
TASK [收集事实] ***************************************************************************
ok: [192.168.235.141] => {
"msg": "this host ip is 192.168.235.141 this hostname is apache\n"
}
PLAY RECAP ****************************************************************************
192.168.235.141 : ok=2 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
2、Ansible事实作为变量注入
较早的变量注入: 在Ansible2.5之前,事实是作为前缀为字符串ansible_的单个变量注入,而不是作为ansible_facts变量的一部分注入 ansible_facts[‘distribution’]事实会被称为ansible_distribution 可以使用临时命令来运行setup模块,以此形式显示所有事实的值: 实例:
[root@ansible playbook]
192.168.235.141 | SUCCESS => {
"ansible_facts": {
"ansible_all_ipv4_addresses": [
"192.168.235.141",
"192.168.122.1"
],
"ansible_all_ipv6_addresses": [
"fe80::20c:29ff:fed7:4b2"
],
"ansible_apparmor": {
"status": "disabled"
},
·················
选定的Ansible事实名称比较
ansible_facts形式 | 旧事实变量形式 |
---|
ansible_facts[‘hostname’] | ansible_hostname | ansible_facts[‘fqdn’] | ansible_fqdn | ansible_facts[‘default_ipv4’][‘address’] | ansible_default_ipv4[‘address’] | ansible_facts[‘interfaces’] | ansible_interfaces | ansible_facts[‘devices’][‘vda’][‘partitions’][‘vda1’][‘size’] | ansible_devices[‘vda’][‘partitions’][‘vda1’][‘size’] | ansible_facts[‘dns’][‘nameservers’] | ansible_dns[‘nameservers’] | ansible_facts[‘kernel’] | ansible_kernel |
将Ansible配置文件的[default]部分中inject_facts_as_vars参数设置为False,可关闭旧命名系统。默认设置为True
inject_facts_as_vars的默认值在Ansible的未来版本中可能会更改为False
如果设置为False,则只能使用新的ansible_facts.*命名系统引用Ansible事实
3、关闭事实收集
有时我们不想为play收集事实。这样做的原因可能有:
- 不准备使用任何事实
- 希望加快play速度
- 希望减小play在受管主机上造成的负载
- 受管主机因为某种原因无法运行setup模块
- 需要安装一些必备软件后再收集事实
要为play禁用事实收集功能,可将gather_facts关键字设置为no:
[root@ansible playbook]
---
- hosts: apache
gather_facts: no
tasks:
- name: test file
debug:
var: anisble_facts
即使play设置了gather_facts: no,也可以随时通过运行使用setup模块的任务来手动收集事实
[root@ansible playbook]
---
- hosts: apache
gather_facts: no
tasks:
- name: get ansible_facts
setup:
- name: test debug
debug:
var: ansible_facts
4、创建自定义事实
除了使用系统捕获的事实外,我们还可以自定义事实,并将其本地存储在每个受管主机上;
默认情况下,setup模块从各受管主机的( /etc/ansible/facts.d 目录下的文件和脚本中加载自定义事实
各个文件或脚本的名称必须以.fact结尾才能被使用;动态自定义事实脚本必须输出JSON格式的事实,而且必须是可执行文件
实例: 方式一:采用INI格式编写的静态自定义事实文件
//在受管主机上创建自定义文件
[root@apache ~]
[root@apache ~]
[root@apache facts.d]
[users]
user_one: zhangsan
user_two: lisi
[servers]
service_one: httpd
service_two: mariadb
//在控制节点上查看
"ansible_local": {
"example": {
"servers": {
"service_one": "httpd",
"service_two": "mariadb"
},
"users": {
"user_one": "zhangsan",
"user_two": "lisi"
}
}
},
方式二: 以JSON格式提供事实,JSON事实等同于INI格式指定的事实,JSON数据可以存储在静态文本文件中
//查看用JSON格式写对fact文件
[root@apache facts.d]# pwd
/etc/ansible/facts.d
[root@apache facts.d]# cat test.fact
{
"users": {
"user_one": "zhangsan",
"user_two": "lisi"
},
"servers": {
"service_one": "httpd",
"service_two": "vsftpd"
}
}
//在控制节点上查看
"ansible_local": {
"test": {
"servers": {
"service_one": "httpd",
"service_two": "vsftpd"
},
"users": {
"user_one": "zhangsan",
"user_two": "lisi"
}
}
},
自定义事实由setup模块存储在ansible_facts[‘ansible_local’]变量中
事实按照定义它们的文件的名称来整理;例如:自定义事实由受管主机上保存为/etc/ansible/facts.d/example.fact的文件,在这种情况下,ansible_facts[‘ansible_local’][‘example’][‘users’][‘user_two’]的值为lisi 实例:
[root@ansible playbook]# cat playbook.yml
---
- hosts: apache
tasks:
- name: 输出某个用户
debug:
msg: 该用户是:{{ ansible_facts['ansible_local']['example']['users']['user_one'] }}
[root@ansible playbook]# ansible-playbook playbook.yml
PLAY [apache] *************************************************************************
TASK [Gathering Facts] ****************************************************************
ok: [192.168.235.141]
TASK [输出某个用户] *************************************************************************
ok: [192.168.235.141] => {
"msg": "该用户是:zhangsan"
}
PLAY RECAP ****************************************************************************
192.168.235.141 : ok=2 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
[root@ansible playbook]# ansible all -m setup |less
"ansible_local": {
"example": {
"servers": {
"service_one": "httpd",
"service_two": "vsftpd"
},
"users": {
"user_one": "zhangsan",
"user_two": "lisi"
}
}
},
5、使用魔法变量
一些变量并非事实或通过setup模块配置,但也由Ansible自动设置。这些魔法变量也可用于获取与特定受管主机相关的信息。
最常用的有四个:
魔法变量 | 说明 |
---|
hostvars | 包含受管主机的变量,可以用于获取另一台受管主机的变量的值。如果还没有为受管主机收集事实,则它不会包含该主机的事实。 | group_names | 列出当前受管主机所属的所有组 | groups | 列出清单中的所有组和主机 | inventory_hostname | 包含清单中配置的当前受管主机的主机名称。因为各种原因有可能与事实报告的主机名称不同 |
三、编写循环
利用循环迭代任务
Ansible支持使用loop关键字对一组项目迭代任务,可以配置循环以利用列表中的各个项目、列表中各个文件的内容、生成的数字序列或更为复杂的结构来重复任务
1、简单循环
简单循环对一组项目迭代任务。loop关键字添加到任务中,将应对其迭代任务的项目列表取为值。循环变量item保存每个迭代过程中使用的值
[root@ansible playbook]
PLAY [apache] *************************************************************************
TASK [Gathering Facts] ****************************************************************
ok: [192.168.235.141]
TASK [create user] ********************************************************************
changed: [192.168.235.141] => (item=zhangsan)
changed: [192.168.235.141] => (item=lisi)
PLAY RECAP ****************************************************************************
192.168.235.141 : ok=2 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
[root@ansible playbook]
---
- hosts: apache
tasks:
- name: create user
user:
name: "{{ item }}"
state: present
loop:
- zhangsan
- lisi
[root@apache ~]
uid=6678(zhangsan) gid=6678(zhangsan) 组=6678(zhangsan)
[root@apache ~]
uid=6679(lisi) gid=6679(lisi) 组=6679(lisi)
此外、还可以通过变量给loop提供所使用的的列表
[root@ansible playbook]
---
- hosts: all
vars:
users:
- zhangsan
- lisi
- wangwu
tasks:
- name: create user
user:
name: "{{ item }}"
state: present
loop: "{{ users }}"
[root@ansible playbook]
PLAY [all] ****************************************************************************
TASK [Gathering Facts] ****************************************************************
ok: [192.168.235.141]
TASK [create user] ********************************************************************
ok: [192.168.235.141] => (item=zhangsan)
ok: [192.168.235.141] => (item=lisi)
changed: [192.168.235.141] => (item=wangwu)
PLAY RECAP ****************************************************************************
192.168.235.141 : ok=2 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
2、循环散列或字典列表
loop列表不需要是简单值列表
---
- hosts: all
tasks:
- name: create user
user:
name:"{{ item.name }}"
group:"{{ item.group}}"
state: present
loop:
- name: zhangsan
group: zhangsan
- name: lisi
group: lisi
[root@ansible playbook]
PLAY [all] ****************************************************************************
TASK [Gathering Facts] ****************************************************************
ok: [192.168.235.141]
TASK [create user] ********************************************************************
ok: [192.168.235.141] => (item={'name': 'zhangsan', 'group': 'zhangsan'})
ok: [192.168.235.141] => (item={'name': 'lisi', 'group': 'lisi'})
PLAY RECAP ****************************************************************************
192.168.235.141 : ok=2 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
3、将Register变量于Loop一起使用
[root@ansible playbook]
---
- hosts: all
tasks:
- name: show some user
shell: 'echo this user is {{ item }}'
loop:
- zhangsan
- lisi
- wangwu
register: result
- name: show these user
debug:
var: result
[root@ansible playbook]
PLAY [all] ****************************************************************************
TASK [Gathering Facts] ****************************************************************
ok: [192.168.235.141]
TASK [show some user] *****************************************************************
changed: [192.168.235.141] => (item=zhangsan)
changed: [192.168.235.141] => (item=lisi)
changed: [192.168.235.141] => (item=wangwu)
TASK [show these user] ****************************************************************
ok: [192.168.235.141] => {
"result": {
"changed": true,
"msg": "All items completed",
"results": [
{
"ansible_loop_var": "item",
"changed": true,
"cmd": "echo this user is zhangsan",
"delta": "0:00:00.005864",
"end": "2021-07-25 09:48:37.743754",
"failed": false,
"invocation": {
"module_args": {
"_raw_params": "echo this user is zhangsan",
"_uses_shell": true,
"argv": null,
"chdir": null,
"creates": null,
"executable": null,
"removes": null,
"stdin": null,
"stdin_add_newline": true,
"strip_empty_ends": true,
"warn": true
}
},
"item": "zhangsan",
"rc": 0,
"start": "2021-07-25 09:48:37.737890",
"stderr": "",
"stderr_lines": [],
"stdout": "this user is zhangsan",
"stdout_lines": [
"this user is zhangsan"
]
},
{
"ansible_loop_var": "item",
"changed": true,
"cmd": "echo this user is lisi",
"delta": "0:00:00.003339",
"end": "2021-07-25 09:48:38.240460",
"failed": false,
"invocation": {
"module_args": {
"_raw_params": "echo this user is lisi",
"_uses_shell": true,
"argv": null,
"chdir": null,
"creates": null,
"executable": null,
"removes": null,
"stdin": null,
"stdin_add_newline": true,
"strip_empty_ends": true,
"warn": true
}
},
"item": "lisi",
"rc": 0,
"start": "2021-07-25 09:48:38.237121",
"stderr": "",
"stderr_lines": [],
"stdout": "this user is lisi",
"stdout_lines": [
"this user is lisi"
]
},
{
"ansible_loop_var": "item",
"changed": true,
"cmd": "echo this user is wangwu",
"delta": "0:00:00.002984",
"end": "2021-07-25 09:48:38.742151",
"failed": false,
"invocation": {
"module_args": {
"_raw_params": "echo this user is wangwu",
"_uses_shell": true,
"argv": null,
"chdir": null,
"creates": null,
"executable": null,
"removes": null,
"stdin": null,
"stdin_add_newline": true,
"strip_empty_ends": true,
"warn": true
}
},
"item": "wangwu",
"rc": 0,
"start": "2021-07-25 09:48:38.739167",
"stderr": "",
"stderr_lines": [],
"stdout": "this user is wangwu",
"stdout_lines": [
"this user is wangwu"
]
}
]
}
}
PLAY RECAP ****************************************************************************
192.168.235.141 : ok=3 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
|