You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Ansible动态inventory的compose段中如何访问hostvars?

Ansible AWS动态Inventory无法获取堡垒机IP的问题解决

问题背景

我编写了如下inventory_aws_ec2动态Inventory文件:

---
plugin: aws_ec2
filters:
  tag:Name:
    - "do-compose-*"
groups:
  bastion: "'mgmt-bastion' in tags.get('Name', '')"
  apps: "'apps' in tags.get('Name', '')"
hostnames:
  - tag:Name
compose:
  ansible_user: '"ubuntu"'
  ansible_host: public_ip_address if "mgmt-bastion" in tags.get('Name', '') else private_ip_address
  ansible_ssh_common_args: >-
    "" 
    if "mgmt-bastion" in tags.get('Name', '') 
    else "-o StrictHostKeyChecking=no -o 'ProxyCommand ssh -o StrictHostKeyChecking=no -W %h:%p ubuntu@{{ hostvars[groups['bastion'][0]].public_ip_address }}'"

尝试在ansible_ssh_common_args中引用堡垒机的public_ip_address时,发现hostvars在compose段不可用;同时在group_vars/common_vars中直接使用hostvars['do-compose-mgmt-bastion'].public_ip_address也无法获取到值。需要确认hostvars的可用范围,并找到替代方案。

关键结论

hostvars确实仅在Playbook执行阶段(包括tasks、handlers、templates等)可用。动态Inventory的compose段属于Inventory生成阶段,此时Ansible尚未加载所有主机的变量和组信息,因此无法访问hostvars或groups这类运行时变量。

可行解决方法

方法1:在Playbook中通过set_fact动态设置代理参数

这是最推荐的原生解决方案,利用Playbook阶段已加载所有主机变量的特性:

  1. 先修改动态Inventory,移除compose中的ansible_ssh_common_args配置:
---
plugin: aws_ec2
filters:
  tag:Name:
    - "do-compose-*"
groups:
  bastion: "'mgmt-bastion' in tags.get('Name', '')"
  apps: "'apps' in tags.get('Name', '')"
hostnames:
  - tag:Name
compose:
  ansible_user: '"ubuntu"'
  ansible_host: public_ip_address if "mgmt-bastion" in tags.get('Name', '') else private_ip_address
  1. 在Playbook开头添加set_fact任务,针对不同组设置SSH代理参数:
---
- name: 配置SSH代理参数
  hosts: all
  gather_facts: false
  tasks:
    - name: 为apps主机设置堡垒机代理
      set_fact:
        ansible_ssh_common_args: >-
          -o StrictHostKeyChecking=no -o 'ProxyCommand ssh -o StrictHostKeyChecking=no -W %h:%p ubuntu@{{ hostvars[groups['bastion'][0]].public_ip_address }}'
      when: "'apps' in group_names"

    - name: 为bastion主机清空代理参数
      set_fact:
        ansible_ssh_common_args: ""
      when: "'bastion' in group_names"

方法2:通过外部脚本提前获取堡垒机IP并写入静态Inventory

如果需要在Inventory阶段就配置好代理参数,可以用AWS CLI提前提取堡垒机IP,生成静态Inventory片段,与动态Inventory配合使用:

# 获取堡垒机公网IP
BASTION_IP=$(aws ec2 describe-instances --filters "Name=tag:Name,Values=do-compose-mgmt-bastion" --query "Reservations[*].Instances[*].PublicIpAddress" --output text)

# 生成静态Inventory文件
cat > static_bastion.ini <<EOF
[bastion]
do-compose-mgmt-bastion ansible_host=$BASTION_IP

[apps:vars]
ansible_ssh_common_args='-o StrictHostKeyChecking=no -o ProxyCommand="ssh -o StrictHostKeyChecking=no -W %h:%p ubuntu@$BASTION_IP"'
EOF

然后在ansible.cfg中配置多Inventory源:

[defaults]
inventory = ./inventory_aws_ec2.yml, ./static_bastion.ini

方法3:固定堡垒机公网IP(仅适用于静态环境)

如果堡垒机的公网IP是固定不变的,可以直接在动态Inventory的compose段写死IP:

compose:
  # ... 其他配置
  ansible_ssh_common_args: >-
    "" 
    if "mgmt-bastion" in tags.get('Name', '') 
    else "-o StrictHostKeyChecking=no -o 'ProxyCommand ssh -o StrictHostKeyChecking=no -W %h:%p ubuntu@1.2.3.4'"

此方法不适合IP会动态变化的环境。


内容的提问来源于stack exchange,提问作者Max Koretskyi

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.17 03:23:23