可変数のホスト変数をループする Ansible

可変数のホスト変数をループする Ansible

ユーザーが入力したホストのリストからホスト名と IP アドレスを取得し、その情報を中央サーバーに送信しようとしています。私が直面している主な問題は、ホストの数が大幅に異なる可能性があることです。たとえば、最初の実行でユーザーがホスト名を 1 つ入力し、2 回目の実行で 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

このユースケースには3つの部分があります: 1) 在庫管理、2) 収集クライアントホスト名そしてクライアントIP3) 中央サーバーに報告します。

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を使用して構造を作成します。タスクを1回実行し、それをセントラルサーバー

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 アドレスで更新します。

お役に立てれば。

関連情報