Ansible 迴圈遍歷可變數量的 Hostvar

Ansible 迴圈遍歷可變數量的 Hostvar

我試圖從使用者輸入的主機清單中取得主機名稱和 IP 位址,並將該資訊傳送到中央伺服器。我遇到的主要問題是主機數量可能相差很大。例如,在第一次執行時,使用者可以輸入 1 個主機名,第二次執行時輸入 30,下次執行時間輸入 5。

執行 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.例如,在下方的庫存中添加中央伺服器,為了簡單起見,本地主機,以及群組中的一百台主機測試

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

您可以使用 playbook 中的 with_items 指令循環存取 client_hosts 變數。然後,您可以透過在循環中使用 item 變數來引用每個單獨的主機。

以下是如何修改 playbook 以處理多個主機的範例:

- 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 位址更新中央伺服器上的檔案。

希望這可以幫助。

相關內容