Ansible:获取一个组中所有IP地址

70

让我们想象一个像这样的清单文件:

node-01 ansible_ssh_host=192.168.100.101
node-02 ansible_ssh_host=192.168.100.102
node-03 ansible_ssh_host=192.168.100.103
node-04 ansible_ssh_host=192.168.100.104
node-05 ansible_ssh_host=192.168.100.105

[mainnodes]
node-[01:04]

在我的playbook中,我现在想创建一些包含组mainnodes的IP地址的变量:

vars:
  main_nodes_ips: "192.168.100.101,192.168.100.102,192.168.100.103,192.168.100.104"
  main_nodes_ips_with_port: "192.168.100.101:3000,192.168.100.102:3000,192.168.100.103:3000,192.168.100.104:3000"

这是我目前为止得到的:

vars:
  main_nodes_ips: "{{groups['mainnodes']|join(',')}}"
  main_nodes_ips_with_port: "{{groups['mainnodes']|join(':3000,')}}"

但那将使用主机名而不是IP地址。

有任何想法如何实现吗?

更新:

看了一段时间的文档,我认为这将允许我循环遍历所有IP地址:

{% for host in groups['mainnodes'] %}
    {{hostvars[host]['ansible_ssh_host']}}
{% endfor %}

但我现在还不能想出如何创建一个包含所有这些IP的数组。这样我就可以在它们上使用|join()命令。

更新2:
我刚才以为我已经想出来了……但事实证明你不能在playbook中使用{% %}语法……或者我可以吗? 嗯,在vars部分不行。 :/

vars:
  {% set main_nodes_ip_arr=[] %}
  {% for host in groups['mesos-slave'] %}
     {% if main_nodes_ip_arr.insert(loop.index,hostvars[host]['ansible_ssh_host']) %} {% endif %}
  {% endfor %}
  main_nodes_ips: "{{main_nodes_ip_arr|join(',')}}"
  main_nodes_ips_with_port: "{{main_nodes_ip_arr|join(':3000,')}}"

7
对于使用版本2.0+的任何人,ansible_ssh_host已被弃用。此解决方案仍然有效,但您需要将其替换为ansible_host - Pudding
你写道你对自己的答案并不太满意。你已经非常接近最佳实践了。请现在将McKelvins设置为被接受的答案。https://dev59.com/JFoV5IYBdhLWcg3wXN7j#39932728 谢谢。 - wedi
10个回答

100

我在这里找到了神奇的map extract

main_nodes_ips: "{{ groups['mainnodes'] | map('extract', hostvars, ['ansible_host']) | join(',') }}"
main_nodes_ips_with_port: "{{ groups['mainnodes'] | map('extract', hostvars, ['ansible_host']) | join(':3000,') }}:3000"

一种替代方案(灵感来自这里):

main_nodes_ips: "{{ groups['mainnodes'] | map('extract', hostvars, ['ansible_eth0', 'ipv4', 'address']) | join(',') }}"

(假设接口为eth0)


注意,仅当所有服务器的主接口均设置为eth0时,备选方案才可行。 - mperrin
3
请注意,自ansible 2.0以来,ansible_ssh_host已更名为ansible_host - mperrin
1
如果您需要向结果列表中的每个项目添加一些后缀(例如端口号),则可以使用以下代码: "{{ groups['backend_servers'] | map('extract', hostvars, ['ansible_host']) | map('regex_replace', '^(.*)', '\\1:80') | list }}" - Stepan Kokhanovskiy
5
第二部分中,ansible_default_ipv4可能更加可靠,因为接口名称已变得不可预测。 - Gert van den Berg

15

我之前遇到过这个问题,这是我想出来的解决方法(不是最优解,但可以工作)

---
# playbook.yml
  - hosts: localhost
    connection: local

    tasks:
      - name: create deploy template
        template:
          src: iplist.txt
          dest: /tmp/iplist.txt
      - include_vars: /tmp/iplist.txt

      - debug: var=ip

并且模板文件是

ip:
{% for h in groups['webservers'] %}
 - {{ hostvars[h].ansible_ssh_host }}
{% endfor %}

2
创建一个任务似乎有点过度杀伐了。 - Forivin

9
这对我很管用。不依赖于接口名称。
- main_nodes_ips: "{{ groups['mainnodes'] | map('extract', hostvars, ['ansible_default_ipv4', 'address']) | join(',') }}"

3
- name: Create List of nodes to be added into Cluster
  set_fact: nodelist={%for host in groups['mygroup']%}"{{hostvars[host].ansible_eth0.ipv4.address}}"{% if not loop.last %},{% endif %}{% endfor %}

 - debug: msg=[{{nodelist}}]

 - name: Set Cluster node list in config file
   lineinfile:
         path: "/etc/myfonfig.cfg"
         line: "hosts: [{{ nodelist }}]"

作为结果,您将在配置文件中看到以下行:
hosts: ["192.168.126.38","192.168.126.39","192.168.126.40"]

2

我现在已经自己解决了。虽然不是很满意这个解决方案,但它能够正常使用:

main_nodes_ips: "{% set IP_ARR=[] %}{% for host in groups['mainnodes'] %}{% if IP_ARR.insert(loop.index,hostvars[host]['ansible_ssh_host']) %}{% endif %}{% endfor %}{{IP_ARR|join(',')}}"
main_nodes_ips_with_port: "{% set IP_ARR=[] %}{% for host in groups['mainnodes'] %}{% if IP_ARR.insert(loop.index,hostvars[host]['ansible_ssh_host']) %}{% endif %}{% endfor %}{{IP_ARR|join(':3000,')}

8
@McKelvin的回答应该被接受为解决方案,因为它比这个更优秀,并且是Ansible官方文档推荐的方法。 - Strahinja Kustudic

2

我已经使用ansible事实(facts)在playbook中完成了这个任务。此playbook采用ansible_all_ipv4_addresses列表和ansible_nodename(实际上是完全合格的域名),遍历所有主机并将数据保存在本地路径localpath_to_save_ips文件中。您可以将localpath_to_save_ips更改为本地主机上的绝对路径。

---
- hosts: all
  become: yes
  gather_facts: yes

  tasks:
  - name: get ip
    local_action: shell echo {{ ansible_all_ipv4_addresses }} {{ ansible_nodename }} >> localpath_to_save_ips


虽然这段代码片段可能解决了问题,但包括解释有助于提高您的回答质量。请记住,您正在为未来的读者回答问题,而这些人可能不知道您提出代码建议的原因。 - Stefan Crain

1

我发现了访问其他组IP的“唯一方法”,当以下任意一项为真时:

  • 某些成员尚未通过ansible进行引导
  • 使用串行方式
  • 该组不是playbook的一部分

如下所示:

{% set ips=[] %}{% for host in groups['othergroup'] %}{% if ips.append(lookup('dig', host)) %}{% endif %}{% endfor %}{{ ips }}

需要在运行ansible的机器上安装dnspython,可通过以下方式安装
sudo apt-get install python-dnspython

如果有更好的方法,请告诉我,我希望能够摆脱这个可怕的东西。


1
我遇到了一個類似的問題,即獲取另一個組中節點的IP地址。 使用如下構造: the_ip:"{{ hostvars [groups ['master'] [0]] ['ansible_default_ipv4'] .address }}" 僅在運行組master時才有效,而這並不是我的Playbook的一部分(我正在本地主機上運行)。 我通過向Playbook添加一個額外的play來解決了這個問題,如下:
- hosts: master
  gather_facts: yes
  become: no
  vars: 
    - the_master_ip: "{{ hostvars[groups['master'][0]]['ansible_default_ipv4'].address }}"
  tasks:
  - debug: var=the_master_ip
  - set_fact: the_ip={{ the_master_ip }}

然后我可以在playbook的下一个步骤中使用the_ip

这也可能解决@Petroldrake提到的问题吗?


很奇怪,我在其他地方无法让它工作。 最终,我使用了一个从https://serverfault.com/questions/962040/sharing-ansible-variable-between-plays获取的使用`hostvars`的解决方案。 - Monger39

1

这是我为了不依赖于eth0所做的事情(感谢ADV-IT的答案):

- name: gathering facts
  hosts: mainnodes
  gather_facts: true

- hosts: mainnodes
  tasks:
    - name: Create List of nodes
      set_fact: nodelist={%for host in groups['mainnodes']%}"{{hostvars[host]['ansible_env'].SSH_CONNECTION.split(' ')[2]}}"{% if not loop.last %},{% endif %}{% endfor %}


0

##只需使用-ansible_default_ipv4.address-获取IP并重定向到本地文件,然后使用它

  • 名称:收集事实 主机:主机 收集事实:真 任务:

    • 名称:重定向到文件 shell: echo "{{ansible_default_ipv4.address}}" >>ipss.txt delegate_to: localhost

你的回答可以通过提供更多支持信息来改进。请编辑以添加进一步的细节,例如引用或文档,以便他人可以确认你的答案是正确的。您可以在帮助中心中找到有关如何编写良好答案的更多信息。 - Community

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接