Ansible 自动化多版本管理-针对不同python3版本(含 Terraform 入门联动)
这是一篇迟来的总结。在经历了与两台云服务器的艰苦"搏斗"后,我终于让 Ansible 成功问候了它们。本文记录了我踩过的坑、背后的原理,以及如何让 Ansible 与 Terraform 优雅配合,成为基础设施即代码(IaC)的黄金搭档。
目录
- 背景:混合环境下的自动化需求
- 环境描述与拓扑
- [Ansible 初安装:看似美好,实则暗礁](#Ansible 初安装:看似美好,实则暗礁)
- [主机清单配置与免密 SSH](#主机清单配置与免密 SSH)
- 第一道坎:ModuleNotFoundError
- 表象与误导
- 根本原因:控制节点 Ansible 版本过旧
- 升级 Ansible 的正确姿势
- 第二道坎:SyntaxError
- 版本升级后的新问题
- Python 3 版本兼容性黑洞
- 解决方案:统一解释器版本
- 生产环境其他常见故障速览
- [Ansible + Terraform:迈向真正的基础设施即代码](#Ansible + Terraform:迈向真正的基础设施即代码)
- Terraform 创建资源,Ansible 配置系统
- 动态清单实战:让 Terraform 输出直接驱动 Ansible
- 总结:一条稳定的自动化管理链路
一、背景:混合环境下的自动化需求
在实际工作中,我们常常需要同时管理不同操作系统、不同 Python 版本的云服务器。手工逐个登录配置不仅效率低下,还容易出错。Ansible 作为无代理(agentless)的自动化工具,只需 SSH 和 Python 就能完成批量管理,是我选择它的核心理由。
我的目标很简单:一台 Ubuntu 22.04 虚拟机作为控制节点,同时管理两台云服务器(一台旧版 CentOS 7,一台全新 Ubuntu 24.04),实现免密 SSH 并执行 ansible all -m ping 全部返回 pong。
后续,我们还将引入 Terraform 来声明式创建基础设施,并与 Ansible 配合,形成完整的 IaC 工作流。
二、环境描述与拓扑
| 主机名 | 角色 | 公网IP(脱敏示例) | 操作系统 | Python 版本 |
|---|---|---|---|---|
| control-node | Ansible 控制节点 | 内网 VM,无公网 | Ubuntu 22.04 | Python 3.10 |
| cloud-server-a | 被管节点 | 1.2.3.4 | CentOS 7 | 系统自带 Python 2.7 / Python 3.6.8 |
| cloud-server-b | 被管节点 | 5.6.7.8 | Ubuntu 24.04 | Python 3.12 |
所有被管节点已开启 SSH(端口 22),且控制节点能通过互联网访问它们的公网 IP。
三、Ansible 初安装:看似美好,实则暗礁
在 Ubuntu 22.04 上,我习惯性地使用 apt 安装 Ansible:
bash
sudo apt update
sudo apt install ansible -y
安装结束后,验证版本:
bash
ansible --version
# ansible [core 2.10.8]
此时版本为 2.10.8。我心里还想:官方源出来的,应该稳了吧?但后面的连环故障证明这个想法太天真了。
四、主机清单配置与免密 SSH
首先打通控制节点到两台服务器的 SSH 免密:
bash
ssh-keygen -t rsa -b 4096
ssh-copy-id root@1.2.3.4
ssh-copy-id root@5.6.7.8
然后创建 Ansible 主机清单文件 /etc/ansible/hosts:
ini
[web_servers]
cloud-server-a ansible_host=1.2.3.4
cloud-server-b ansible_host=5.6.7.8
[web_servers:vars]
ansible_user=root
清单配置无误后,执行排查命令:
bash
ansible all -m ping
结果立刻出现了报错。
五、第一道坎:ModuleNotFoundError
cloud-server-b 返回错误:
ModuleNotFoundError: No module named 'ansible.module_utils.six.moves'
而 cloud-server-a 却 ping 成功了。一种常见的误导是:认为目标节点缺少 python3-six 包。于是我登录 cloud-server-b 手动安装:
bash
apt install python3-six -y
却发现它已是最新版,错误依旧。
根本原因:控制节点 Ansible 版本过旧
深入分析错误栈:Ansible 在目标节点执行模块时,将控制节点的 module_utils 代码打包传输并运行。旧版 Ansible(2.10.x)的模块代码依赖 Python 3.5/3.6 时代的内部 six 路径机制,而 Ubuntu 24.04 自带的 Python 3.12 已不再兼容。
结论:问题不在目标节点,而在控制节点的 Ansible 版本。需要将 Ansible 升级到至少 2.16 以上版本,才能原生支持 Python 3.12。
升级 Ansible 的正确姿势
使用 Ansible 官方 PPA 源进行升级:
bash
sudo apt-add-repository ppa:ansible/ansible -y
sudo apt update
sudo apt install ansible -y
过程中若遇到老版本文件冲突(如 ansible-core 覆盖 /usr/bin/ansible),可用 dpkg --force-overwrite 或彻底清理旧包解决。最终版本提升到 ansible-core 2.17.14。
bash
ansible --version
# ansible [core 2.17.14]
再次执行 ansible all -m ping,cloud-server-b 终于成功:
json
cloud-server-b | SUCCESS => {
"ping": "pong"
}
但 cloud-server-a 却又爆出新错误。
六、第二道坎:SyntaxError
cloud-server-a | FAILED! => {
"module_stdout": "...SyntaxError: future feature annotations is not defined"
}
版本升级后的新问题
看起来是新版 Ansible 的模块代码里用了 from __future__ import annotations 语法,这要求 Python 3.7+,而 cloud-server-a 的默认 Python 3 解释器竟然是 Python 3.6.8(CentOS 7 自带)。
Python 3 版本兼容性黑洞
两个节点一个太新一个太旧,导致同一个控制端无法同时兼容:
| 主机 | Python 版本 | 兼容 Ansible 2.17? |
|---|---|---|
| cloud-server-a (CentOS 7) | 3.6.8 | ❌ 需 Python ≥3.7 |
| cloud-server-b (Ubuntu 24.04) | 3.12 | ✅ |
解决方案:统一解释器版本
方案A :让 cloud-server-a 使用 Python 2.7,因旧版 Ansible 模块在 Python 2 下可正常运行。
在主机清单中显式指定解释器:
ini
cloud-server-a ansible_host=1.2.3.4 ansible_python_interpreter=/usr/bin/python2
一试即通。但 Python 2 已退休,不适合长期维护。
方案B (推荐):在 cloud-server-a 上安装更高版本的 Python 3,并更新软链接。实际操作:
bash
# 在 cloud-server-a 上安装 Python 3.8(来自 SCL 源)
yum install -y centos-release-scl
yum install -y rh-python38
# 将旧 python3 移走,创建新软链接指向 3.8
mv /usr/bin/python3 /usr/bin/python36_backup
ln -s /opt/rh/rh-python38/root/usr/bin/python3.8 /usr/bin/python3
之后无需在清单中指定解释器,让 Ansible 自动发现即可。双节点最终全部 ping 通。
七、生产环境其他常见故障速览
-
SSH 密钥权限错误
~/.ssh/id_rsa权限要 600,~/.ssh目录要 700,否则 Ansible 会拒绝读取。 -
目标节点 Python 未安装或路径不一致
可通过
ansible_python_interpreter在清单中统一路径,或提前通过raw模块安装 Python。 -
云安全组/防火墙未开放 22 端口
导致连接受阻,表现为
Connection refused或超时。 -
SELinux 或 AppArmor 限制
可能阻止 Ansible 在目标节点创建临时目录,可通过
semanage或修改策略解决。 -
Ansible 控制节点 DNS 解析异常
在虚拟化 NAT 模式下尤其常见,需正确配置
/etc/resolv.conf或systemd-resolved。
八、Ansible + Terraform:迈向真正的基础设施即代码
仅用 Ansible 管理现有机器是"配置管理",要形成完整的 IaC ,还需要 Terraform 负责资源的创建与销毁,Ansible 负责系统配置。
Terraform 创建资源,Ansible 配置系统
以腾讯云为例,Terraform 可以声明两台云服务器:
resource "tencentcloud_instance" "web" {
count = 2
image_id = "img-..."
instance_type = "S5.MEDIUM2"
...
}
资源创建后,Terraform 可以输出这些实例的公网 IP:
output "instance_ips" {
value = tencentcloud_instance.web[*].public_ip
}
随后,我们可以将输出的 IP 列表交给 Ansible 动态清单。
动态清单实战:让 Terraform 输出直接驱动 Ansible
编写一个简单的脚本 inventory.sh,调用 terraform output -json 并解析生成 Ansible 所需的 JSON 格式:
json
{
"web_servers": {
"hosts": ["1.2.3.4", "5.6.7.8"],
"vars": { "ansible_user": "root" }
}
}
然后在 Ansible 命令中使用它:
bash
ansible-playbook -i inventory.sh site.yml
这样,每次 terraform apply 创建新资源后,Ansible 可以无缝接管配置,实现从"点到点"到"生产级流水线"的跃升。
九、总结:一条稳定的自动化管理链路
本文通过真实案例,揭示了 Ansible 在生产中常见的版本兼容性陷阱 和Python 环境差异问题。核心经验:
- 控制节点 Ansible 版本需与目标节点的 Python 版本匹配,建议 Ansible ≥ 2.16;
- 混合 OS 环境统一 Python 解释器(如都使用 Python 3.8+)是最佳实践;
- 基础设施即代码应结合 Terraform 与 Ansible,前者管资源,后者管配置,各自发挥长处。
下次再搭建 Ansible 控制节点时,不妨直接用官方 PPA 安装最新版;接到目标机器后,先确认 python3 --version,省去一堆弯路。
自动化之路,坑不少,但趟平之后便是快速、可靠、可复制的运维体验。