在 Ubuntu 上创建 Python 虚拟环境,几乎每个刚接触的人都会撞上同一堵墙:输入python3 -m venv myenv,然后屏幕弹出一段以 The virtual environment was not created successfully because ensurepip 开头的报错。这句话看着吓人,但绝大多数情况根本不是你的 Python 装坏了,而是 Ubuntu 的包管理策略和 Python 标准库之间一个由来已久的摩擦点。这篇文章会把这个错误从报错文案到根因、再到各种衍生场景讲清楚,并给出可以直接照抄的修复命令,适合在 Ubuntu 上做 Python 开发、尤其是刚被这个报错卡住的读者。
1. 这个报错不是在骂你的 Python,是 Ubuntu 把 ensurepip 单独拆了包
1.1 ensurepip 是什么?为什么创建虚拟环境非要它不可
先把报错原文摆出来,后面所有分析都以它为准:
$ python3 -m venv myenv The virtual environment was not created successfully because ensurepip is not available. Debian/Ubuntu systems use the ensurepip component which is not available in this environment. You may need to install it with the command apt install python3-venv注意措辞:它说的是 "not created successfully",不是 "not created"。也就是说myenv这个目录可能已经生成了,甚至里面有bin/python,但缺少bin/pip,activate脚本生成得也不算完整。这种半成品环境不建议拿来用,直接删掉重建就好。
那 ensurepip 到底是个什么东西?它是 Python 标准库里的一个模块,作用是引导安装 pip。当你执行python3 -m venv myenv时,venv 模块先把一个空环境搭好,然后调用 ensurepip 把 pip 安装到这个新环境里。这个流程是 venv 命令的收尾动作:环境可以没有 numpy、没有 requests,但不能连 pip 都没有,否则它就是个只会跑解释器的空壳。
问题在于,Ubuntu 的python3基础包里,ensurepip 默认是不在的。Ubuntu 把"标准库是否带 ensurepip"这件事做了拆分,ensurepip 被单独放进了python3-venv这个软件包里。所以你的 python3 能跑、能 import,但偏偏-m venv时找不到 ensurepip,整个初始化流程就断在了最后一步。
1.2 完整报错文案逐句拆解
把错误信息切成三段来看,每一句都能对上后面的修复动作:
The virtual environment was not created successfully:venv 在调用 ensurepip 安装 pip 时失败了,于是整体创建流程被判定为失败。如果你用ls -la myenv查看,目录多半已经存在,但里面没有可用的pip命令。because ensurepip is not available:venv 模块去找 ensurepip,结果在sys.path里找不到。这不是权限问题,也不是磁盘满,而是发行版根本没把这个模块放进系统 Python。Debian/Ubuntu systems use the ensurepip component which is not available in this environment. You may need to install it with the command apt install python3-venv:这句是解释原因并直接给出修复入口。它明确告诉你,这类发行版把 ensurepip 拆到了独立包里,你要做的就是装回python3-venv。
1.3 Ubuntu 为什么非要拆包
这个拆法乍看起来多此一举,但站在发行版维护的角度很好理解。Ubuntu 的基础镜像和最小化安装要尽量小,ensurepip 自带一段引导用 pip 的代码以及对应的 wheel,体积不是零。更重要的是,Python 上游的 pip 版本迭代很快,而 Ubuntu 的 apt 安全更新节奏要稳得多,把 pip 版本和系统 Python 拆开维护,能各自走各自的更新渠道,避免基础镜像被一个 pip 版本拖累。
下面这张表能把三个组件的关系理清楚:
| 组件 | 职责 | 在 Ubuntu 里的存在形式 |
|---|---|---|
| venv | 创建隔离的 Python 环境 | 标准库自带,命令入口是python3 -m venv |
| ensurepip | 负责在新环境里安装 pip | 默认不在python3包里,单独在python3-venv包中 |
| pip | 安装第三方包 | 虚拟环境内由 ensurepip 种下;系统级由python3-pip提供 |
看明白这个关系,你就知道接下来该怎么做:不是绕开虚拟环境,而是把缺失的 ensurepip 组件装回它应该在的位置。
2. 标准解法:一条 apt 命令修好,但不是装完就算完
2.1 实际操作:安装并验证 ensurepip
最正统的解法就是错误信息里那句提示,直接安装python3-venv。我建议顺手把python3-pip也装上,避免后面"系统级 pip 不存在"的连锁问题:
sudo apt update sudo apt install -y python3-venv python3-pip装完后不要急着创建项目环境,先验证 ensurepip 是否真的进了当前 python3:
python3 --version python3 -m ensurepip --version正常情况下会输出一行 pip 的版本信息,比如pip 22.0.2 from /usr/lib/python3/dist-packages/ensurepip (python 3.10)。如果你看到的是No module named ensurepip,说明which python3指向的不是系统 apt 管理的那个 Python,这个坑放到下一节专门讲。
确认没问题后,用一个临时目录做完整验收:
rm -rf /tmp/venv_test python3 -m venv /tmp/venv_test source /tmp/venv_test/bin/activate pip --version deactivate rm -rf /tmp/venv_test这条链路走通,说明从"创建环境"到"激活环境"再到"pip 可用"全都没有问题,可以正常开始写你的项目了。
2.2 为什么要连着 python3-pip 一起装
很多教程只让你装python3-venv,我建议多装一个python3-pip,原因有两条。
第一,Python 生态里的很多脚本和工具会直接调用系统级pip或pip3。如果只装 venv,你新建的虚拟环境里有独立的 pip,但跳出虚拟环境后,在系统环境里执行pip3 --version很可能直接报 command not found。这在排查问题时会多一道坎。
第二,从 Ubuntu 23.04 开始,系统 Python 默认启用 PEP 668(externally-managed-environment),在系统环境里直接pip install会报error: externally-managed-environment,提示你应该改用虚拟环境。也就是说,系统级 pip 的作用越来越像一把"钥匙":它让你能查看版本、配环境、处理系统侧问题,但真正的依赖安装都要在 venv 里进行。既然发行版已经逼你走虚拟环境这条路,那先把 venv 和 pip 这两块地基一起铺好,后面会顺很多。
2.3 版本必须对齐,包名跟着 Python 小版本走
python3 -m venv使用的是 PATH 里排在最前面的 python3。Ubuntu 默认的python3元包版本是跟随系统软件源走的,比如 22.04 是 3.10,24.04 是 3.12,所以apt install python3-venv会自动带上对应小版本的python3.10-venv或python3.12-venv。
但如果你手动装了其他版本,比如 Python 3.11,或者用某个第三方源把默认版本换了,就需要手动对齐包名:
sudo apt install python3.11-venv python3.11-pip python3.11 -m venv venv311判断标准很简单:python3 --version显示什么版本,就装pythonX.Y-venv那个包。版本对不上,安装完依然会报一模一样的 ensurepip 错误,这不是玄学,是包和解释器之间没有关联上。
3. 装完还报错?按这三步从 PATH 和解释器层面揪出真凶
3.1 第一步:确认当前 python3 的真实身份
有些读者执行完sudo apt install python3-venv后,发现错误纹丝不动。这时候千万别觉得"apt 没用",十有八九是你敲的python3根本不是 apt 管的那个。
先把环境看清楚:
which python3 type -a python3 python3 -m ensurepip --version如果which python3显示/usr/bin/python3,那 apt 是管得到的。如果显示/usr/local/bin/python3,说明你用的是一份自编译或第三方安装的 Python,它和/usr/bin/python3是两套东西,apt 的python3-venv包装不到它头上。
更迷惑的情况是type -a python3同时输出多个路径:
$ type -a python3 python3 is /usr/local/bin/python3 python3 is /usr/bin/python3这时候 PATH 的顺序决定了python3到底是谁。如果/usr/local/bin排在/usr/bin前面,你敲的python3就是/usr/local/bin/python3,虽然系统 Python 有了 ensurepip,但你的命令根本不会走到它那儿去。
3.2 第二步:把多解释器的混战理清楚
确认存在多个解释器后,先看它们分别是什么版本:
/usr/bin/python3 --version /usr/local/bin/python3 --version python3 --version如果版本不一样,说明你同时有好几个 Python 在抢python3这个名字。这时至少有三个选择:
- 用确切的路径创建虚拟环境:
/usr/bin/python3 -m venv myenv,绕开 PATH 干扰。 - 调整 PATH 顺序,把你想用的解释器放前面。
- 清理掉多余的
python3链接,让系统中只保留一个默认解释器。
我自己的经验是,不要急着删二进制。先看项目到底需要哪个版本,再用显式路径或显式python3.10、python3.11这类命令来创建环境,风险最小。很多项目根目录里写的.python-version或 Dockerfile 里的FROM python:3.x都绑定了一个明确版本,跟着它走就好。
3.3 第三步:pyenv、自编译与容器场景
pyenv 默认编译的 Python 会启用 ensurepip,所以正常环境下用pyenv install得到的版本不会触发这个错误。但如果你用的是某个裁剪过的预编译包、或者构建参数里手动关掉了 ensurepip,就会出现同样的现象。排查命令:
pyenv which python3 pyenv exec python3 -m ensurepip --version确认缺失后,最省事的做法是重装这个版本:
pyenv uninstall 3.10.12 pyenv install 3.10.12要是想显式保证 ensurepip 不被裁剪,可以在安装时带参数:
PYTHON_CONFIGURE_OPTS="--with-ensurepip=install" pyenv install 3.10.12自编译 Python 的场景同理,配置阶段加上--with-ensurepip=install再make && make install,系统层面就不会缺这个模块。
容器和 CI 场景也要单独说一嘴。很多基于 Ubuntu 的 Dockerfile 只安装python3基础包,没有装python3-venv,在容器里跑python3 -m venv会遇到一模一样的报错。修复方式就是容器构建阶段加上:
RUN apt-get update && apt-get install -y python3-venv python3-pip如果这个镜像刻意不保留 apt 能力、或者处于离线环境,那就用下一节的手工方案绕开 ensurepip。
4. 不碰 apt 的自救方案:--without-pip 与 virtualenv 双保险
4.1 方案 A:先建一个不带 pip 的 venv,再用 get-pip.py 补上
这个思路其实很简洁:既然 venv 默认会调用 ensurepip,那我就让它别调用。venv 模块提供了一个--without-pip参数,用来创建不带 pip 的纯环境。创建成功后再用官方引导脚本 get-pip.py 把 pip 装进去:
python3 -m venv --without-pip myenv source myenv/bin/activate curl -sS https://bootstrap.pypa.io/get-pip.py -o get-pip.py python get-pip.py pip --version deactivate每一条命令的作用分别是:
--without-pip:跳过 ensurepip,venv 只负责搭建目录结构和解释器软链。source myenv/bin/activate:进入这个全新环境,让后续的 python 命令指向myenv/bin/python。curl -o下载 get-pip.py:这是 Python 官方的 pip 引导脚本,也是目前最通用的"无 ensurepip 也能装 pip"的方式。python get-pip.py:脚本会把 pip 安装到当前激活环境对应的 site-packages 里。
这里必须强调一个安全习惯:不要直接写curl -sS https://bootstrap.pypa.io/get-pip.py | python,虽然网上到处是这种管道执行写法,但它把远程内容直接交给解释器,风险不可控。先下载到本地,必要时人工看一眼内容,再决定是否执行。离线环境里也可以先在有网的机器上把 get-pip.py 下载好,再拷进目标环境运行。
4.2 方案 B:用 virtualenv 这个第三方工具初始化
如果你不想跟 get-pip.py 打交道,还有一个更省心的第三方工具:virtualenv。它和标准库的 venv 不是一回事,virtualenv 不依赖系统 ensurepip,它自带 pip 的 wheel,在应用数据目录里缓存一份,创建环境时直接写进新环境。
在 Ubuntu 上安装最简单的方式还是 apt:
sudo apt install -y python3-virtualenv python3 -m virtualenv myenv source myenv/bin/activate pip --version如果你的机器上已经存在某个可以正常工作的 pip(比如另一个 Python 版本里的 pip),也可以直接用 pip 安装 virtualenv。不过受 PEP 668 限制,Ubuntu 23.04 以后在系统 Python 里直接pip install virtualenv可能会被拒,这时候要么走 apt,要么给命令加--break-system-packages,我建议优先 apt,少碰系统解释器。
virtualenv 还有一个实用特性:可以用-p参数指定目标解释器,比如python3 -m virtualenv -p python3.8 myenv,这在多版本切换时比标准库 venv 更灵活。
4.3 两条路怎么选
面对这三种修复思路,我一般按下面的优先级来:
- 能正常用 apt,就装
python3-venv,这是最贴近发行版原生逻辑的方案,后续升级维护都省心。 - 环境里没有 apt、或者不能安装系统包,就选
--without-pip加 get-pip.py,不改动任何系统级依赖。 - virtualenv 作为兜底,适合那种系统 Python 被改得很乱、标准库 venv 已经不太可信的场景。
三种方法产出的虚拟环境在使用上没有本质区别,你后续在该环境里pip install依赖的方式完全一致,选择哪种主要看你对当前机器控制力的判断。
5. 修好之后怎么验收,以及一劳永逸的避坑姿势
5.1 一张验收清单
修完不是看一眼报错消失就完事,建议按下面的清单完整过一遍:
python3 -m ensurepip --version有正常的版本输出。python3 -m venv /tmp/checkenv命令成功,退出码为 0。/tmp/checkenv/bin/pip --version能打印出 pip 的版本和路径。source /tmp/checkenv/bin/activate后执行pip list,结果正常显示。- 试验完成后删除
/tmp/checkenv,避免留下垃圾目录。
上面任何一步失败,都回到第三节的排查链路重新定位"当前 python3 的真实身份"。不要带着半成品环境继续开发,后面出现的各种ModuleNotFoundError会让你更难判断问题源头。
5.2 多版本并存时的正确姿势
如果你的机器上同时有 Python 3.10、3.11、3.12,创建虚拟环境时不要偷懒用裸的python3 -m venv,应该显式指定完整版本:
python3.11 -m venv venv311对应的系统包也要装齐:
sudo apt install python3.11-venv python3.11-pip如果是 pyenv 用户,创建前先用pyenv local 版本号锁住当前目录的 Python 版本,再执行python -m venv venv,确保 ensurepip 从正确的解释器里调用。
还有一个容易被忽略的点:虚拟环境里的bin/pip脚本头部会写死创建时那个 Python 的绝对路径,所以虚拟环境目录最好不要跨机器、跨目录搬家。换了位置后activate虽然能执行,但解释器路径可能就失效了,重建环境往往比修复快得多。
5.3 一个小脚本:创建 venv 前自动检查 ensurepip
为了避免以后每次手动检查,可以把这个脚本存成mkvenv.sh:
#!/usr/bin/env bash set -euo pipefail if [ -z "$1" ]; then echo "用法: $0 <虚拟环境目录>" exit 1 fi if ! python3 -m ensurepip --version >/dev/null 2>&1; then echo "当前 python3 缺少 ensurepip,准备安装 python3-venv" if command -v apt-get >/dev/null 2>&1; then sudo apt-get update sudo apt-get install -y python3-venv python3-pip else echo "没有 apt-get,改用 --without-pip 创建" python3 -m venv --without-pip "$1" echo "虚拟环境已创建(未安装 pip),可用 get-pip.py 手动补装" exit 0 fi fi python3 -m venv "$1" echo "虚拟环境已创建: $1"使用方式:
chmod +x mkvenv.sh ./mkvenv.sh myenv脚本的核心逻辑很简单:先确认当前解释器有没有 ensurepip,没有就先装系统包;装不了就退化成--without-pip模式。这种判断逻辑放进 CI 脚本里也适用,能避免每次换一台机器就踩一遍同样的坑。
我排查这类报错总结下来,90% 的情况停留在第二节就结束了;剩下 10%,是 PATH 里藏着第二个 python3。记住一句话:venv 启动的是你 PATH 里第一个 python3,ensurepip 必须属于那个 python3,而不是属于系统替你装的默认版本。这两点对齐了,The virtual environment was not created successfully because ensurepip 就基本不会再回来烦你。