你的 JumpServer 突然无法连接新纳管的 Linux 服务器?SSH 免密明明是好的,但点"测试资产可连接性"就报错?

别急,这不是免密问题,而是一个你可能没想到的版本兼容性地雷

问题现象

在 JumpServer 中添加新版本的 Linux 资产(如 Rocky Linux 10、RHEL 10 等 2024 年之后发布的发行版)时,执行连接性测试报错:

ModuleNotFoundError: No module named 'ansible.module_utils.six.moves'

关键症状: - ✅ 手工 ssh <目标机> 能成功登录(免密正常) - ✅ 网络和 22 端口通畅 - ❌ JumpServer 的 Ansible 任务直接报错(错误来自 Python 模块层)

根源分析

这是一个控制端 vs 被控端的 Python 版本兼容性问题,不在网络层。

问题架构全景

JumpServer 与目标机的问题链路

问题链路:JumpServer(ansible-core 2.10)→ 通过正常网络发起 Ansible 任务 → 目标机(Python 3.12)上用 3.12 加载老版模块 → ansible.module_utils.six.moves 兼容层失效 → ModuleNotFoundError

问题的三个要素

  1. JumpServer 内置的 Ansible 版本很老 - JumpServer v2.x 系列通常内置 ansible-core 2.10.x - 这个版本有个兼容包 ansible.module_utils.six.moves,用来适配 Python 2.x

  2. 新发行版自带最新的 Python 3.12+ - Rocky Linux 10、RHEL 10、CentOS 10 等默认 python3 是 3.12 或更高 - 老版 ansible-core 内置的那份 six 兼容层,在 Python 3.12 上加载不起来

⚠️ 注意措辞:six 是 PyPI 上的第三方包,从来不在 Python 标准库里, 所以并不存在"Python 3.12 删除了 six"这回事。真正失效的是 ansible 自带的那份 vendored 副本——它依赖了 3.12 中被移除的旧导入机制(如 imp)。 结论和修复方法不受影响,但如果你要转述这个问题,请按这个说法转述。

  1. 两者碰在一起就出问题 - 老版 ansible-core 的自动发现机制会尝试多个 Python 解释器候选路径 - 最终命中新系统的 Python 3.12,加载老版 Ansible 代码 → 找不到 six.moves → 报错

Ansible 解释器自动发现的陷阱

Ansible 解释器自动发现流程

老版 ansible-core(2.10.x)会按照预设顺序尝试寻找 Python 解释器,一旦找到就停止。问题根源是:

  • 新发行版删除了 /usr/bin/python(Python 2 时代遗物)
  • Ansible 因此跳过这个候选,直接命中 /usr/libexec/platform-python
  • 但这个路径在新系统中指向 Python 3.12,导致加载老版 Ansible 时 six.moves 不可用
  • 最后报错 ModuleNotFoundError

新发行版的问题: - /usr/bin/python 被删除(Python 2 时代遗物) - /usr/libexec/platform-python 指向了新的 Python 3.12 - Ansible 加载老代码 + 新 Python = 找不到 six.moves = 报错

排查步骤

第一步:确认目标机的 Python 版本

登录目标服务器运行:

cat /etc/os-release
python3 --version
ls -la /usr/libexec/platform-python 2>&1

如果看到: - 系统是新发行版(2024 年后) - python3 版本 ≥ 3.12 - /usr/libexec/platform-python 也指向新版本 Python

基本确诊是本问题

第二步:排除本地已装 Ansible 的干扰

rpm -qa | grep -i ansible
which ansible ansible-playbook 2>/dev/null
python3 -c "import ansible; print(ansible.__file__)" 2>&1

如果都返回"未找到",说明不是本地 Ansible 冲突,问题就在版本兼容性上。

第三步:(可选)确认 JumpServer 侧的 Ansible 版本

如果你能登录到 JumpServer 服务器:

docker ps | grep -i core
docker exec <core容器名> ansible --version
docker exec <core容器名> pip show ansible-core

修复方案

方案对比速查表

三套修复方案对比

快速决策: - 🔴 现在就要修好方案 A(5 分钟)— 推荐用于紧急修复 - 🟡 需要长期规范方案 B(升级 ansible-core)— 推荐用于公司统一部署 - 🟢 JumpServer v3.x方案 C(配置变量)— 最规范的方式

方案 A:只改目标机 ⭐ 推荐

思路:装一个兼容的老版本 Python(3.9~3.11),让 Ansible 优先发现它,而不影响系统默认的 python3。

优点:改动最小,影响面最小 缺点:每台新机器都要做一遍

A1:用 uv 装独立 Python 3.11

# 安装 uv(不依赖系统包管理器)
curl -LsSf https://astral.sh/uv/install.sh | sh
source ~/.bashrc

# 装 Python 3.11
~/.local/bin/uv python install 3.11
~/.local/bin/uv python find 3.11

# 输出类似:/root/.local/share/uv/pythons/cpython-3.11.10-...

A2:改符号链接(让 Ansible 自动发现)

Ansible 的解释器自动发现有一个候选顺序,/usr/libexec/platform-python 在前面。我们把它改指向兼容版本:

# 备份原始链接
ls -la /usr/libexec/platform-python > ~/platform-python.backup.txt

# 改指向新装的 Python 3.11
ln -sfn /root/.local/share/uv/pythons/cpython-3.11.10-.../bin/python /usr/libexec/platform-python

# 验证
/usr/libexec/platform-python --version  # 应该输出 Python 3.11.x

⚠️ 注意:这个符号链接属于系统 RPM 包管理,未来 dnf update python3 时可能被重置,届时再跑一遍上面的 ln -sfn 即可。

A3:在 JumpServer 里明确指定(如果版本支持)

如果你的 JumpServer 是 v3.x 版本,资产配置页里通常有"Ansible 变量"选项,直接填:

ansible_python_interpreter=/root/.local/share/uv/pythons/cpython-3.11.10-.../bin/python

方案 B:升级 JumpServer 的 Ansible

升级 JumpServer 服务器上的 ansible-core 到新版本(支持 Python 3.12+)。

优点:一次修复,对所有新机器生效 缺点:改动范围大,需在测试环境先验证,可能影响已有的自动化任务

方案 C:用官方的解释器配置

JumpServer v3.x+ 在平台管理页面原生支持 ansible_python_interpreter 配置,这是最"正规"的做法。

修复效果对比

修复前后的符号链接指向对比

修复的关键是改变符号链接的指向: - 修复前/usr/libexec/platform-python/usr/bin/python3.12(系统默认,不兼容) - 修复后/usr/libexec/platform-python~/.local/bin/python3.11(独立版本,兼容)

这个改变会直接影响 Ansible 的解释器选择,从而使 six.moves 可用。

验证修复

改完之后,回到 JumpServer 资产详情页,点"测试资产可连接性",确认没有报 six.moves 错误即可。

为什么会踩这个坑

这个坑的本质是:新旧两个生态的边界问题

  • 你的 JumpServer 版本可能部署已久,没有跟进最新的 Python 支持
  • 新发行版激进地升级了 Python 版本并删除了老兼容库
  • 这两个"决策"在时间上碰在了一起

避坑方法: 1. 优先看报错栈,不要直接猜"可能是网络" 2. 用一台老系统(Python < 3.12)试一遍连接,对比行为 3. 查源码或文档确认自动发现机制,带着依据去改


常见问题

Q: 改了符号链接后,系统更新会不会覆盖?
A: 会。但你可以写个 cron 任务定期检查,或者改用方案 A1 的明确指定方式。

Q: 为什么不直接升级 JumpServer?
A: 可以,但风险更大。如果你的 JumpServer 版本太老,升级可能涉及数据库迁移、插件兼容性等。方案 A 风险最小。

Q: Python 3.13 会不会也有问题?
A: 会。所以最好升级 JumpServer 的 Ansible 版本,一劳永逸。


相关阅读 - Ansible 官方兼容性矩阵 - JumpServer 官方文档