你的 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
1.新发行版自带最新的 Python 3.12+
• Rocky Linux 10、RHEL 10、CentOS 10 等默认 python3 是 3.12 或更高
• 老版 ansible-core 内置的那份 six 兼容层,在 Python 3.12 上加载不起来
这里的措辞值得较真一下:网上不少说法是"Python 3.12 删除了 six",但 six 是 PyPI 上的第三方包,从来就不在 Python 标准库里,谈不上被谁删除。真正失效的是 ansible 自带的那份 vendored 副本,它依赖了 3.12 中被移除的旧导入机制(比如 imp)。结论和修复方法不受影响,但搞清楚这一点,你就知道为什么"装个 six 包"解决不了问题。
1.两者碰在一起就出问题
• 老版 ansible-core 的自动发现机制会尝试多个 Python 解释器候选路径
• 最终命中新系统的 Python 3.12,加载老版 Ansible 代码 → 找不到 six.moves → 报错

Ansible 解释器自动发现流程
老版 ansible-core(2.10.x)会按预设顺序挨个试探 Python 解释器,一旦找到就停止。问题就出在这个顺序上:
• 排在最前面的 /usr/bin/python 是 Python 2 时代的遗物,新发行版已经不提供了
• Ansible 于是跳过它,落到下一个候选 /usr/libexec/platform-python
• 而这个路径在新系统上恰好指向 Python 3.12
• 老代码配上新解释器,six.moves 就加载不起来,报出那行 ModuleNotFoundError
换句话说,Ansible 并没有挑错路径,是这条路径在新系统上换了指向。这也正是后面方案 A 的着力点。
登录目标服务器运行:
cat /etc/os-releasepython3 --versionls -la /usr/libexec/platform-python 2>&1如果三条同时成立——系统是 2024 年之后的新发行版、python3 版本在 3.12 及以上、/usr/libexec/platform-python 也指向这个新版本——基本可以确诊就是本文这个问题。
rpm -qa | grep -i ansiblewhich ansible ansible-playbook 2>/dev/nullpython3 -c "import ansible; print(ansible.__file__)" 2>&1如果都返回"未找到",说明不是本地 Ansible 冲突,问题就在版本兼容性上。
▎第三步:(可选)确认 JumpServer 侧的 Ansible 版本如果你能登录到 JumpServer 服务器:
docker ps | grep -i coredocker exec <core容器名> ansible --versiondocker exec <core容器名> pip show ansible-core
三套修复方案对比
怎么选,取决于你眼下最在意什么:
•现在就要能连上,先止血——用方案 A,只动目标机,五分钟见效
•想一次管长远,不介意动服务端——用方案 B,升级 ansible-core
•JumpServer 是 v3.x 及以上——用方案 C,平台自带的官方配置项,最规范
急事用 A 没问题,但记得把 B 或 C 排进后续计划,否则每台新机器都要重来一遍。
思路:装一个兼容的老版本 Python(3.9~3.11),让 Ansible 优先发现它,而不影响系统默认的 python3。
优点:改动最小,影响面最小 缺点:每台新机器都要做一遍
# 安装 uv(不依赖系统包管理器)curl -LsSf https://astral.sh/uv/install.sh | shsource ~/.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.11ln -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 -qf /usr/libexec/platform-python # 看它属于哪个包rpm -q --whatrequires platform-python # 看有没有别的包依赖它如果查出来"没有软件包依赖它",说明它只是系统 Python 包自带的历史遗留兼容路径,改掉不影响其他功能。
一个必须知道的注意事项:这个符号链接归 RPM 包管理,将来执行 dnf update python3 之类的操作时有可能被重置回系统默认版本。届时重新跑一遍上面那条 ln -sfn 即可,但要记得有这么回事,否则过几个月问题复发会很困惑。
▎方案 B:升级 JumpServer 的 ansible-core把 JumpServer 服务端的 ansible / ansible-core 升到支持 Python 3.12 的版本。
好处是一次修复,之后所有新纳管的新发行版资产都不用再管;代价是改动范围大得多,升级前建议先在测试环境验证,避免影响已有的自动化任务。
较新版本的 JumpServer(v3.x 及以上)在平台管理里原生支持配置 Ansible 变量,直接填上兼容解释器的路径即可:
ansible_python_interpreter=/root/.local/share/uv/pythons/cpython-3.11.10-.../bin/python这是三个方案里最"正规"的做法,优先于方案 A 那种改符号链接的取巧手段——前提是你的 JumpServer 版本有这个入口。v2.x 的界面上根本没有这一项,那是后来的版本才加的,具体以你所用版本的官方文档[1]为准。

修复前后的符号链接指向对比
修复的关键是改变符号链接的指向:
•修复前:/usr/libexec/platform-python → /usr/bin/python3.12(系统默认,不兼容)
•修复后:/usr/libexec/platform-python → ~/.local/bin/python3.11(独立版本,兼容)
这个改变会直接影响 Ansible 的解释器选择,从而使 six.moves 可用。
改完之后,回到 JumpServer 资产详情页,点"测试资产可连接性",确认没有报 six.moves 错误即可。
这个坑的本质,是两个各自都合理的决定,在时间上撞到了一起。
你的 JumpServer 大概率部署有些年头了,内置的 ansible-core 停在当年的版本,这没什么不对;新发行版把默认 Python 推进到 3.12、顺手清掉 /usr/bin/python 这类历史包袱,也是正常演进。问题只在于:谁都没料到对方会以这个组合出现在同一台机器上。
这类问题的排查思路可以复用:
1.先看报错栈落在哪一层。是连接超时、认证失败,还是 Python 模块内部的 ImportError / ModuleNotFoundError?后者基本可以排除网络与免密,直接往版本兼容性上想
2.找一台老系统做对照。同样的连接性测试,在 Python 3.12 之前的资产上如果一切正常,就基本坐实了是版本组合的问题
3.去查机制本身,别靠试。像 Ansible 解释器发现顺序这种东西,是写死在源码配置里的;官方也维护着一份Python 版本支持矩阵[2],写明了各版本 ansible-core 支持到哪个 Python。查出来再动手,比盲目试各种参数快得多
改了符号链接,系统更新会不会把它覆盖回去?
会。它归 RPM 包管理,dnf update python3 就可能重置。要么在运维手册里记一笔、复发时重跑一遍,要么升级到支持方案 C 的 JumpServer 版本,从根上不依赖这条链接。
为什么不干脆升级 JumpServer?
可以,但风险大得多。版本跨度大时可能涉及数据库迁移、插件兼容性、已有自动化任务受影响。如果眼下只是要把资产连上,方案 A 的影响面最小;升级值得做,但适合排期做,不适合救火时做。
以后出了 Python 3.13、3.14 会不会再犯?
大概率会——只要控制端的 ansible-core 不动,被控端的 Python 一路往前走,这个组合迟早再次错位。所以方案 A 是止血,方案 B 才是治本。
1免密正常却连不上,先看报错栈落在哪一层——落在 Python 模块层的 ModuleNotFoundError,基本就排除了网络与认证
2真凶是老版 ansible-core 撞上新发行版的 Python 3.12,而不是"Python 删了 six"这类流传说法
3急事改目标机的符号链接五分钟见效,但要知道它会被系统更新重置,治本仍是升级 ansible-core
— 每个结论都有出处、可回溯 | 飞说 AGI —
💬 你手上还有多少台跑在老版本 JumpServer 上的资产?评论区说说你踩过的版本错位坑,点赞最高的下一篇就拆它。
[1] https://jumpserver.readthedocs.io/
[2] https://docs.ansible.com/ansible/latest/reference_appendices/python_3_support.html
END飞说|feitalks
往期文章可检索归档|www.feitalks.cn