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阶段已加载所有主机变量的特性:
- 先修改动态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
- 在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
相关产品推荐
相关产品推荐

