S_lion's Studio

(七)收集节点信息facts

字数统计: 2k阅读时长: 9 min
2021/12/01 Share

收集节点信息

ansible可以获取目标主机的节点信息,如ip地址,主机名、系统版本、内核版本等等,对于运维来说是一个很好的功能,但是正是它收集的信息很全面,运行也很慢,在日常工作中,没有特殊的需求会在playbook中将其关闭,或者开启facts缓存,在运行任务前手动执行一次。

ansible中提供了两个模块(setupgather_facts)来实现收集主机facts,其中setup是最早的模块,gather_facts是基于setup二次开发的模块(2.8版本以上),gather_facts相比setup多了个并行收集facts的功能(自动识别)。

在playbook中可以通过gather_facts: BOOL,来控制是否收集节点facts,默认是true,在开始执行定义好的任务前会先执行gather_facts.

访问节点信息

收集到目标节点信息之后,各信息都保存在一个名为ansible_facts的变量中,如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
"ansible_facts": {
"ansible_all_ipv4_addresses": [
"192.168.100.10"
],
"ansible_all_ipv6_addresses": [
"fe80::8199:4454:db1e:911c"
],
"ansible_apparmor": {
"status": "disabled"
},
"ansible_architecture": "x86_64",
"ansible_bios_date": "07/22/2020",
"ansible_bios_version": "6.00",
"ansible_cmdline": {
...
},
"ansible_date_time": {
...
},
"ansible_default_ipv4": {
"address": "192.168.100.10",
"alias": "ens33",
"broadcast": "192.168.100.255",
"gateway": "192.168.100.2",
"interface": "ens33",
"macaddress": "00:0c:29:ec:61:46",
"mtu": 1500,
"netmask": "255.255.255.0",
"network": "192.168.100.0",
"type": "ether"
},
"ansible_distribution": "CentOS",
"ansible_distribution_file_parsed": true,
"ansible_distribution_file_path": "/etc/redhat-release",
"ansible_distribution_file_variety": "RedHat",
"ansible_distribution_major_version": "7",
"ansible_distribution_release": "Core",
"ansible_distribution_version": "7.6",
"ansible_dns": {
"nameservers": [
"114.114.114.114"
]
},
...

有了这些信息,就可以去访问这些信息。比较常用有获取目标主机名,系统版本号、ip地址:

1
2
3
4
5
6
- debug:
var: ansible_hostname
- debug:
var: ansible_distribution_version
- debug:
var: ansible_all_ipv4_addresses

获取到所需的信息可以添加一些判断的逻辑:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
---
- hosts: test
gather_facts: true
tasks:
- name: template and copy centos6 yum repo
template:
src: centos6.repo.j2
dest: /tmp/centos6.repo
when: ansible_distribution == "CentOS" and ansible_distribution_major_version == "6"

- name: template and copy centos7 yum repo
template:
src: centos7.repo.j2
dest: /tmp/centos7.repo
when: ansible_distribution == "CentOS" and ansible_distribution_major_version == "7"
---
# 更精简的写法:
---
- hosts: test
gather_facts: true
tasks:
- name: template and copy yum repo
template:
src: "{{'centos6.repo.j2' if (ansible_distribution_major_version == 6) else 'centos7.repo.j2'}}"
dest: /tmp/centos.repo
when: ansible_distribution == 'CentOS'

local fact

Ansible除了远程收集目标节点的Facts信息,还允许用户在目标节点上自定义该节点的Facts信息,这种方式收集到的Facts称为local Facts。

要自定义local Facts,需要在目标节点的/etc/ansible/facts.d/目录下创建以.fact为后缀的文件,并在该文件中定义Facts变量信息。该文件要求是json、ini格式或能够输出json、ini数据格式的可执行文件,比如shell脚本。也可以更改默认的local fact目录,在play关键字中指定就好。

如下示例:

在第一台目标节点编写local fact如下

1
2
3
4
5
6
[root@slions_pc1 facts.d]# ls
myfact.fact
[root@slions_pc1 facts.d]# cat myfact.fact
[model]
pc=del_G3
[root@slions_pc1 facts.d]# pwd

在第二台目标节点编写local fact如下

1
2
3
4
5
6
7
[root@slions_pc2 facts.d]# ls
myfact.fact
[root@slions_pc2 facts.d]# pwd
/home/ansible_poc/facts.d
[root@slions_pc2 facts.d]# cat myfact.fact
[model]
pc=macbook x1

playbook如下

1
2
3
4
5
6
7
- hosts: test
fact_path: /home/ansible_poc/facts.d/
tasks:
- debug:
var: ansible_facts.ansible_local.myfact.model.pc
#也可省略ansible_facts
#var: ansible_local.myfact.model.pc

运行后可以看到能正常访问到自定义的本地fact。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
PLAY [test] **************************************************************************************************

TASK [Gathering Facts] ***************************************************************************************
ok: [192.168.100.11]
ok: [192.168.100.10]

TASK [debug] *************************************************************************************************
ok: [192.168.100.10] => {
"ansible_facts.ansible_local.myfact.model.pc": "del_G3"
}
ok: [192.168.100.11] => {
"ansible_facts.ansible_local.myfact.model.pc": "macbook x1"
}

PLAY RECAP ***************************************************************************************************
192.168.100.10 : ok=2 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
192.168.100.11 : ok=2 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0

使用Local Facts并不方便,这需要单独去目标节点创建/home/ansible_poc/facts.d/目录,还要将写好的.fact文件拷贝过去,然后在下一个play中使用(如果在当前play中使用,需要先手动调用setup模块收集信息再使用)。

换句话说,使用Local Facts的一般流程可能是这样的:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
---
- name: play1
hosts: test
gather_facts: false
tasks:
- block:
- name: mkdir /home/ansible_poc/facts.d
file:
name: /home/ansible_poc/facts.d
state: directory
- name: copy a.fact
copy:
src: myfact.fact
dest: /home/ansible_poc/facts.d
when: inventory_hostname == "192.168.100.10"

- name: play2 use local facts
hosts: test
gather_facts: true
tasks:
- debug:
var: ansible_local.myfact.model.pc
when: inventory_hostname == "192.168.100.10"

虽然local Facts不太方便,但它支持可执行文件的方式。用户可以直接写一个可执行文件来动态生成Facts信息而不是预先以静态的方式写好的Facts变量。

例如,想要收集mysql galera集群中节点的状态,编写如下可执行Facts文件:

1
2
3
4
#!/bin/bash

echo '[cluster_status]'
mysql -e "SHOW GLOBAL STATUS LIKE 'wsrep_cluster_status'\G" | awk '/Value/{print "status="$2}'

之后便可以在playbook中使用ansible_local.FACT_FILENAME.cluster_status.status来访问对应节点的状态。

set_fact模块

Facts的各种信息实际上是以变量方式保存的。

我们还可以在任务中通过set_fact模块直接定义Facts,其效果就是定义了一个变量,和register指令的功能类似,只不过register指令是将模块的执行返回值赋值给变量名,而set_fact是手动指定变量的值。

示例:

1
2
3
4
5
6
7
8
9
10
11
- hosts: localhost
gather_facts: false
tasks:
- name: define some var
set_fact:
name: slions
age: 29

- name: use var
debug:
msg: "name: {{name}}, age: {{age}}"

set_fact经常用于临时设置变量,也非常方便,比如可以在将shell执行结果通过register注册变量之后,立即使用set_fact将命令的标准输出定义成变量。如下:

1
2
3
4
5
6
7
8
9
10
11
- hosts: localhost
gather_facts: false
tasks:
- shell: |
echo 183@163.com
register: echo_res
- set_fact: echo_var={{echo_res.stdout}}

- name: use var
debug:
var: echo_var

facts缓存

收集fact是耗时的,ansible支持fact缓存,通过设置Ansible配置文件,然后就可以在任何时间点通过一个独立的收集任务去收集,并将收集的Facts缓存下来,以后使用Facts变量时就不用再显式地低效收集。

配置Ansible开启Facts缓存的方式非常简单,修改Ansible配置文件(默认是/etc/ansible/ansible.cfg或其它位置)。目前Ansible支持以下几种缓存模式:

  • redis:缓存在redis服务中,直到目前(Ansible 2.9)为止,Ansible还不支持指定连接redis的端口、密码等
  • memcached:缓存在memcache文件中
  • mongodb:缓存在mongodb文件中
  • jsonfile:缓存在本地的json文件中
  • yaml:缓存在本地的yaml文件中
  • pickle:缓存在本地的pickle序列化文件中

配置方式,可参考如下配置项:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# jsonfile缓存模式
gathering = smart或explicit或implicit
fact_caching = jsonfile
fact_caching_connection = DIRNAME
fact_caching_timeout=TIMEOUT

# redis缓存模式
gathering = smart或explicit或implicit
fact_caching = redis
fact_caching_timeout=TIMEOUT
fact_caching_connection = localhost:6379:0

# mongodb缓存模式
fact_caching = mongodb
fact_caching_timeout = 60
fact_caching_connection = mongodb://localhost:32770/ansible_cache

配置项解析:

gathering:控制Ansible是否自动收集Facts,它有三种值:

  • implicit:这是默认值,表示执行play时会自动收集Facts,除非显式指定gather_facts: false禁止收集
  • explicit:不自动收集Facts,除非显式指定gather_facts: true开启收集
  • smart:自动收集Facts,但如果已存在(缓存)则不重复收集

fact_caching_connection:指定本地目录用于存放Facts的缓存文件,如果目录不存在则Ansible会尝试创建
fact_caching_timeout:缓存的有效时长

当前工作中我们使用的jsonfile配置如下:

1
2
3
gathering = smart
fact_caching = jsonfile
fact_caching_connection = facts

当第一次执行playbook后,会将收集到的内容缓存到当前目录下的facts目录下:

1
2
3
4
5
[root@slions_pc1 ansible_poc]# ll facts
总用量 48
-rw-r--r--. 1 root root 22845 12月 1 15:06 192.168.100.10
-rw-r--r--. 1 root root 23039 12月 1 15:06 192.168.100.11

之后便可以直接去引用这些缓存下来的Facts信息,甚至访问不在该play中的节点的Facts信息。例如,playbook内容如下:

1
2
3
4
5
6
- hosts: localhost
gather_facts: false
tasks:
- name: use var
debug:
var: hostvars['192.168.100.11'].ansible_hostname
CATALOG
  1. 1. 收集节点信息
  2. 2. 访问节点信息
  3. 3. local fact
  4. 4. set_fact模块
  5. 5. facts缓存