我正在尝试从用户输入的主机列表中获取主机名和 IP 地址,并将该信息发送到中央服务器。我遇到的主要问题是主机数量可能相差很大。例如,第一次运行时用户可能输入 1 个主机名,第二次运行时输入 30 个,下一次运行时输入 5 个。我希望能够使用单个剧本,无论用户输入 1 个还是 100 个主机。
运行 Ansible Tower 模板时,通过“额外变量”提示收集主机名:
client_hosts: 'host1,host2'
然后在剧本中引用:
- name: Gather client information
hosts:
- "{{ client_hosts | default(omit) }}"
tasks:
- name: Grab client hostname
shell: cat /etc/hostname
register: client_hostname
- name: Grab client IP address
shell: hostname -i | sed -n '1 p'
register: client_ip
在剧本的下面,我想将这些 IP + 主机名添加到特定中央服务器上的文件中(服务器主机名不变):
- name: Update server
hosts: central.server
tasks:
- name: Update client host list
lineinfile:
path: /path/to/file
line: "{{ hostvars['client_hosts']['client_ip'] }} - {{ hostvars['client_hosts']['client_hostname'] }}"
上述方法对于单个主机来说工作正常,但是当指定多个主机(例如client_hostname [1,2,*]?)时,我该如何循环注册变量,并在我不知道要提前输入多少个主机时使用这些值更新服务器?
答案1
此用例分为三个部分:1)管理库存,2)收集客户端主机名和客户端 IP,以及3)向中央服务器报告。
1. 管理库存
关于如何管理库存和收集,有很多选择客户端主机名和客户端 IP。例如,使用添加主机范围如果您计划创建数百个主机,并使用简单的名称,例如host001, hosts002, ..., host999
。例如,在下面的清单中添加中央服务器,为简单起见,使用 localhost,组中有 100 个主机测试
shell> cat inventory/01-hosts
central_server ansible_host=localhost
[test]
host[001:100]
[test:vars]
ansible_connection=ssh
ansible_user=admin
ansible_become=yes
ansible_become_user=root
ansible_become_method=sudo
ansible_python_interpreter=/usr/local/bin/python3.8
简要测试库存
- hosts: all
gather_facts: false
tasks:
- debug:
var: ansible_play_hosts|length
run_once: true
略述
ansible_play_hosts|length: '101'
如果要显示完整库存,请运行以下命令
shell> ansible-inventory -i inventory --list --yaml
然后有很多关于如何选择主机的选项。请参阅模式:针对主机和群组。 例如,限制将清单分发给特定主机或组。使用下面的简单剧本进行测试
shell> cat pb1.yml
- hosts: all
gather_facts: false
tasks:
- debug:
var: inventory_hostname
给
shell> ansible-playbook -i inventory pb1.yml -l host001,host002
PLAY [all] ***********************************************************************************
TASK [debug] *********************************************************************************
ok: [host001] =>
inventory_hostname: host001
ok: [host002] =>
inventory_hostname: host002
PLAY RECAP ***********************************************************************************
host001: ok=1 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
host002: ok=1 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
使用库存插件建如果您想将清单限制在较大的主机组内。请参阅
shell> ansible-doc -t inventory constructed
例如,额外变量主机数用于以下示例中创建组我的组包含受此变量值限制的主机
shell> cat inventory/02-constructed.yml
plugin: constructed
strict: true
use_extra_vars: true
compose:
my_group_count: count_hosts|default(0)
groups:
my_group: inventory_hostname[-2:]|int < my_group_count|int
测试一下
shell> ansible-playbook -i inventory pb.yml -e count_hosts=10 -l my_group
PLAY [all] ***********************************************************************************
TASK [debug] *********************************************************************************
ok: [host001] =>
ansible_play_hosts|length: '11'
PLAY RECAP ***********************************************************************************
host001: ok=1 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
如果要显示完整库存,请运行以下命令
shell> ansible-inventory -i inventory -e count_hosts=10 --list --yaml
在建插件,您可以创建复杂的条件。例如,将主机限制在特定间隔内并创建组我的组2
shell> cat inventory/02-constructed.yml
plugin: constructed
strict: true
use_extra_vars: true
compose:
my_group_count: count_hosts|default(0)
my_group_start: start_hosts|default(0)
my_group_stop: stop_hosts|default(0)
groups:
my_group1: inventory_hostname[-2:]|int < my_group_count|int
my_group2: inventory_hostname[-2:]|int >= my_group_start|int and
inventory_hostname[-2:]|int < my_group_stop|int
测试一下
shell> ansible-playbook -i inventory pb1.yml -e start_hosts=10 -e stop_hosts=15 -l my_group2
PLAY [all] ***********************************************************************************
TASK [debug] *********************************************************************************
ok: [host010] =>
inventory_hostname: host010
ok: [host011] =>
inventory_hostname: host011
ok: [host012] =>
inventory_hostname: host012
ok: [host013] =>
inventory_hostname: host013
ok: [host014] =>
inventory_hostname: host014
...
2. 收集客户端主机名和客户端 IP
您可以使用模块设置或自行收集事实。由于便携性设置应优先考虑。
查看模块设置关于如何收集有关远程主机的事实。例如,下面的剧本收集有关机器和网络并创建变量客户端主机名和客户端 IP
shell> cat pb2.yml
- hosts: all
gather_facts: false
tasks:
- setup:
gather_subset:
- machine
- network
- set_fact:
client_hostname: "{{ ansible_hostname }}"
- debug:
var: client_hostname
- debug:
var: ansible_default_ipv4
- debug:
var: ansible_all_ipv4_addresses
- set_fact:
client_ip: "{{ ansible_all_ipv4_addresses|last }}"
- debug:
var: client_ip
给
shell> ansible-playbook -i inventory -l host011 pb2.yml
PLAY [all] ***********************************************************************************
TASK [setup] *********************************************************************************
ok: [host011]
TASK [set_fact] ******************************************************************************
ok: [host011]
TASK [debug] *********************************************************************************
ok: [host011] =>
client_hostname: test_11
TASK [debug] *********************************************************************************
ok: [host011] =>
ansible_default_ipv4: {}
TASK [debug] *********************************************************************************
ok: [host011] =>
ansible_all_ipv4_addresses:
- 10.1.0.61
TASK [set_fact] ******************************************************************************
ok: [host011]
TASK [debug] *********************************************************************************
ok: [host011] =>
client_ip: 10.1.0.61
PLAY RECAP ***********************************************************************************
host011: ok=7 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
事实的结构和格式可能因操作系统而异。
你可以自己收集事实。例如,下面的剧本
shell> cat pb3.yml
- hosts: all
gather_facts: false
tasks:
- name: Grab client hostname
command: cat /etc/hostname
register: out
- set_fact:
client_hostname: "{{ out.stdout }}"
- debug:
var: client_hostname
- name: Grab client IP address
shell: hostname -i | sed -n '1 p'
register: out
- set_fact:
client_ip: "{{ out.stdout|split|last }}"
- debug:
var: client_ip
在Linux上运行
shell> ansible-playbook -i inventory -l central_server pb3.yml
PLAY [all] ***********************************************************************************
TASK [Grab client hostname] ******************************************************************
changed: [central_server]
TASK [set_fact] ******************************************************************************
ok: [central_server]
TASK [debug] *********************************************************************************
ok: [central_server] =>
client_hostname: central_server
TASK [Grab client IP address] ****************************************************************
changed: [central_server]
TASK [set_fact] ******************************************************************************
ok: [central_server]
TASK [debug] *********************************************************************************
ok: [central_server] =>
client_ip: 10.1.0.22
PLAY RECAP ***********************************************************************************
central_server: ok=6 changed=2 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
实用程序的输出可能因操作系统而异。
3)报告给中央服务器
使用 Jinja 创建结构。运行一次任务并将其委托给中央服务器
shell> cat pb4.yml
- hosts: all
gather_facts: false
tasks:
- setup:
gather_subset:
- machine
- network
- set_fact:
client_hostname: "{{ ansible_hostname }}"
client_ip: "{{ ansible_all_ipv4_addresses|last }}"
- copy:
dest: /tmp/test_host_ip.txt
content: |
{% for host in ansible_play_hosts %}
{{ hostvars[host]['client_hostname'] }} - {{ hostvars[host]['client_ip'] }}
{% endfor %}
run_once: true
delegate_to: central_server
给
shell> ansible-playbook -i inventory -l host011,host013 pb4.yml
PLAY [all] ***********************************************************************************
TASK [setup] *********************************************************************************
ok: [host013]
ok: [host011]
TASK [set_fact] ******************************************************************************
ok: [host011]
ok: [host013]
TASK [copy] **********************************************************************************
changed: [host011 -> central_server(localhost)]
PLAY RECAP ***********************************************************************************
host011: ok=3 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
host013: ok=2 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
剧本在以下位置创建了文件中央服务器
shell> cat /tmp/test_host_ip.txt
test_11 - 10.1.0.61
test_13 - 10.1.0.63
使用模块行输入文件如果你想在文件中添加行。下面的剧本是幂等的
shell> cat pb5.yml
- hosts: all
gather_facts: false
tasks:
- setup:
gather_subset:
- machine
- network
- set_fact:
client_hostname: "{{ ansible_hostname }}"
client_ip: "{{ ansible_all_ipv4_addresses|last }}"
- lineinfile:
path: /tmp/test_host_ip.txt
line: |-
{{ hostvars[item]['client_hostname'] }} - {{ hostvars[item]['client_ip'] }}
loop: "{{ ansible_play_hosts }}"
run_once: true
delegate_to: central_server
在同一主机上重复运行不会有任何变化
shell> ansible-playbook -i inventory -l host011,host013 pb5.yml
PLAY [all] ***********************************************************************************
TASK [setup] *********************************************************************************
ok: [host011]
ok: [host013]
TASK [set_fact] ******************************************************************************
ok: [host011]
ok: [host013]
TASK [lineinfile] ****************************************************************************
ok: [host011 -> central_server(localhost)] => (item=host011)
ok: [host011 -> central_server(localhost)] => (item=host013)
PLAY RECAP ***********************************************************************************
host011: ok=3 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
host013: ok=2 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
如果剧本在新主机上运行,则会在文件中添加新行
shell> ansible-playbook -i inventory -l central_server pb5.yml
PLAY [all] ***********************************************************************************
TASK [setup] *********************************************************************************
ok: [central_server]
TASK [set_fact] ******************************************************************************
ok: [central_server]
TASK [lineinfile] ****************************************************************************
changed: [central_server] => (item=central_server)
PLAY RECAP ***********************************************************************************
central_server: ok=3 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
新行已附加到文件
shell> cat /tmp/test_host_ip.txt
test_11 - 10.1.0.61
test_13 - 10.1.0.63
central_server - 10.1.0.184
答案2
您可以使用剧本中的 with_items 指令循环遍历 client_hosts 变量。然后,您可以在循环中使用 item 变量引用每个单独的主机。
以下是如何修改剧本以处理多个主机的示例:
- name: Gather client information
hosts: "{{ client_hosts | default(omit) }}"
tasks:
- name: Grab client hostname and IP address
shell: |
hostname -i | sed -n '1 p' > /tmp/client_ip
cat /etc/hostname > /tmp/client_hostname
register: gather_client_info
become: true
- name: Set client hostname and IP address as variables
set_fact:
client_hostname: "{{ hostvars[item]['gather_client_info'].stdout_lines[1] }}"
client_ip: "{{ hostvars[item]['gather_client_info'].stdout_lines[0] }}"
with_items: "{{ client_hosts | default(omit) }}"
- name: Update server
hosts: central.server
tasks:
- name: Update client host list
lineinfile:
path: /path/to/file
line: "{{ client_ip }} - {{ client_hostname }}"
with_items: "{{ client_hosts | default(omit) }}"
此剧本将循环遍历 client_hosts 变量中的每个主机,并使用 shell 模块收集主机名和 IP 地址。然后,它将使用 set_fact 模块将这些值设置为变量。最后,它将再次循环遍历 client_hosts 变量,并使用 lineinfile 模块使用每个主机的主机名和 IP 地址更新中央服务器上的文件。
希望这可以帮助。