Ubuntu 20.04安装Python 2.7:源码编译、虚拟环境与Docker容器化部署指南
1. 项目概述:为什么在2024年还要折腾Python 2.7?
最近在整理一个老旧的自动化运维脚本仓库时,我遇到了一个经典问题:几个核心的资产盘点和服务探活脚本,是七八年前用Python 2.7写的,里面大量使用了print语句(不带括号)、urllib2、以及老式的字符串处理逻辑。直接迁移到Python 3固然是终极方案,但时间紧,任务重,我需要先让这些脚本在全新的Ubuntu 20.04.3 LTS服务器上跑起来,保障现有业务流程不中断。Ubuntu 20.04本身已经不再预装Python 2,这成了第一个拦路虎。这个需求听起来很“复古”,但在企业运维、遗留系统维护,甚至是一些特定科学计算和工业控制场景下,依然真实存在。如果你也面临类似处境——需要为一个新部署的系统临时兼容一个老项目,或者维护一个短期内无法重写的核心遗产代码——那么这篇在Ubuntu 20.04.3 LTS上安装和配置Python 2.7的完整指南,就是为你准备的。我将不仅告诉你如何安装,更会分享如何安全、隔离地使用它,以及最终如何规划向Python 3的过渡。
2. 环境准备与方案选型:并非只有apt install一条路
在开始敲命令之前,我们先理清思路。在Ubuntu 20.04上获取Python 2.7,主要有三种路径,每种都有其适用的场景和需要权衡的利弊。
2.1 方案对比:源码编译、第三方仓库与容器化
方案一:从源码编译安装这是最经典、最可控的方式。直接从Python官网下载2.7系列的最终版本(如2.7.18)源码包,在本地编译。它的最大优势是纯净和高度可定制化。你可以指定安装路径(例如/opt/python27),与系统自带的Python 3完全隔离,避免污染系统环境。同时,你可以根据需要开启或关闭某些模块(如SSL支持、sqlite3模块),这对于构建最小化运行环境或解决特定依赖问题非常有用。缺点是步骤稍多,需要手动解决开发工具链和部分库的依赖,并且后续的包管理(pip)也需要单独安装和配置。
方案二:使用第三方PPA(Personal Package Archive)仓库Ubuntu社区有一些维护者提供了包含Python 2.7的PPA,例如知名的deadsnakes团队。通过添加他们的仓库,你可以直接用apt命令安装,就像安装任何其他软件包一样便捷。这种方法省去了编译的麻烦,安装速度快,并且通常能较好地集成到系统的包管理体系中。但它的风险在于,你需要信任第三方仓库的维护者。此外,通过系统包管理器安装的Python 2.7,其二进制文件通常会被放在/usr/bin/python2.7,可能会与某些期望/usr/bin/python就是Python 2的系统工具或脚本产生预期外的交互。
方案三:使用Docker容器这是目前最推荐用于处理遗留环境的方法,尤其是在生产环境中。你可以拉取一个包含Python 2.7的官方镜像(如python:2.7-slim),或者基于Ubuntu 18.04等仍支持Python 2的系统创建自定义镜像。所有依赖都被封装在容器内,与宿主机环境彻底隔离,绝对安全。部署和清理都极其方便。缺点是需要你具备一定的Docker使用知识,并且对于需要直接与宿主机硬件或特定目录交互的脚本,需要配置卷挂载和权限,稍显复杂。
注意:对于绝大多数寻求快速、稳定解决方案的运维场景,我强烈推荐方案一(源码编译)或方案三(Docker)。方案二(PPA)在可控性上稍弱,本文将以方案一作为主线进行详细演示,因为它最具教学意义,能让你理解整个底层过程,之后无论采用哪种方案都能心中有数。同时,我会在关键节点穿插对Docker方案的说明。
2.2 基础系统准备
无论选择哪种方案,我们都需要确保系统具备编译软件或管理容器的基础能力。首先,更新软件包列表并升级现有软件包是一个好习惯。
sudo apt update sudo apt upgrade -y接下来,安装编译Python源码所必需的开发工具和库。这些包包括编译器、链接器、标准库头文件,以及Python某些核心模块(如zlib,sqlite3,ssl)所依赖的开发文件。
sudo apt install -y build-essential sudo apt install -y zlib1g-dev libncurses5-dev libgdbm-dev libnss3-dev libssl-dev libreadline-dev libffi-dev libsqlite3-dev libbz2-devbuild-essential是一个元数据包,它包含了gcc,g++,make等核心编译工具。后面那一长串*-dev包,则是为了确保编译出的Python支持常见的功能模块。例如,缺少libssl-dev,ssl模块就无法编译,会导致使用pip安装某些需要HTTPS连接的包时失败;缺少zlib1g-dev,则无法处理压缩文件。
3. 源码编译安装Python 2.7.18全流程
我们选择Python 2.7系列的最终版本2.7.18进行安装,这是一个包含最终安全修复的版本。
3.1 下载源码与解压
首先,找一个合适的目录,例如/usr/src,用于存放源码。使用wget从Python官网下载源码包。
cd /usr/src sudo wget https://www.python.org/ftp/python/2.7.18/Python-2.7.18.tgz下载完成后,解压源码包并进入解压后的目录。
sudo tar -xzf Python-2.7.18.tgz cd Python-2.7.183.2 配置编译选项
运行configure脚本。这一步会检查系统环境,生成适合当前系统的Makefile。这里有几个关键参数需要指定:
--prefix=/opt/python27:这是最重要的参数。它指定了Python的安装目录。我们将其安装到/opt目录下,这是一个用于存放第三方独立软件的常规位置,与/usr下的系统软件隔离。--enable-optimizations:这个选项会运行一些测试,并基于测试结果优化生成的二进制文件,使其运行速度提升约10%。但这会显著增加编译时间(可能翻倍)。对于生产环境,建议开启;如果只是想快速验证,可以省略。--enable-shared:生成共享库(libpython2.7.so)。有些第三方C扩展模块可能需要链接这个共享库。通常建议加上。
sudo ./configure --prefix=/opt/python27 --enable-optimizations --enable-shared配置过程会输出一系列检查结果,只要没有出现明显的“not found”错误(尤其是前面安装的那些*-dev库),就可以继续。
3.3 编译与安装
接下来,使用make命令进行编译。-j参数后面跟的数字表示并行编译的作业数,通常设置为CPU核心数,可以加快编译速度。你可以用nproc命令查看核心数。
sudo make -j$(nproc)编译完成后,执行安装。
sudo make install至此,Python 2.7.18就已经被安装到了/opt/python27目录下。你可以验证一下:
/opt/python27/bin/python2.7 --version应该输出Python 2.7.18。
3.4 解决共享库路径问题
由于我们使用了--enable-shared并安装到了非标准路径,系统可能找不到Python的动态链接库。直接运行上述命令可能没问题,但当你尝试导入某些模块或第三方C扩展时,可能会遇到类似“error while loading shared libraries: libpython2.7.so.1.0: cannot open shared object file”的错误。
我们需要告诉系统到哪里去找这个库。有两种方法:
方法一:临时生效(推荐用于测试)在运行Python前,设置LD_LIBRARY_PATH环境变量。
export LD_LIBRARY_PATH=/opt/python27/lib:$LD_LIBRARY_PATH /opt/python27/bin/python2.7 -c "import ssl; print(ssl.OPENSSL_VERSION)"方法二:永久生效创建一个配置文件,让系统在启动时自动加载该路径。
echo '/opt/python27/lib' | sudo tee /etc/ld.so.conf.d/python27.conf sudo ldconfig执行sudo ldconfig命令后,系统会重新加载库文件缓存,之后就可以直接运行Python而无需设置环境变量了。
3.5 安装并配置pip
Python 2.7.18的安装包可能没有自带pip,或者自带的版本很旧。我们需要手动安装它。
首先,下载get-pip.py安装脚本。注意,pip官方已经停止了对Python 2的支持,但我们可以使用一个最终兼容的版本。
wget https://bootstrap.pypa.io/pip/2.7/get-pip.py然后,使用我们刚安装的Python 2.7来运行这个脚本。--user参数会将pip安装到当前用户的目录下(~/.local/bin),避免需要sudo权限。但为了全局使用,我们通常直接安装到Python的目录中。
sudo /opt/python27/bin/python2.7 get-pip.py安装完成后,pip和pip2命令会被安装在/opt/python27/bin/目录下。为了方便使用,我们可以创建一个软链接到/usr/local/bin/,这个目录通常在普通用户的PATH环境变量中。
sudo ln -s /opt/python27/bin/pip2 /usr/local/bin/pip2现在,你可以在终端中直接使用pip2命令来为这个Python 2.7环境安装包了,例如pip2 install requests。
实操心得:在编译安装后,务必测试核心模块是否能正常导入,特别是
ssl和sqlite3。我曾经遇到过因为漏装libsqlite3-dev,导致Django的数据库后端无法工作的问题。一个快速的测试命令是:/opt/python27/bin/python2.7 -c “import sqlite3, ssl, zlib; print(‘All core modules imported successfully.’)”。
4. 虚拟环境与依赖管理:隔离是王道
直接在系统级Python 2.7环境下安装项目依赖是危险的,容易引发包版本冲突。对于Python 2.7项目,我强烈建议使用virtualenv来创建独立的虚拟环境。
4.1 安装virtualenv
首先,确保pip2已经就绪,然后用它安装virtualenv。
sudo pip2 install virtualenv4.2 为项目创建虚拟环境
假设你的老项目代码在/home/user/old_project目录下。
cd /home/user/old_project virtualenv -p /opt/python27/bin/python2.7 venv这个命令做了两件事:
-p参数指定了使用我们自定义安装的Python 2.7解释器。- 在当前目录下创建了一个名为
venv的文件夹,里面包含了一个独立的Python环境。
4.3 使用虚拟环境
激活虚拟环境后,所有Python和pip操作都会被限制在这个“沙箱”内。
source venv/bin/activate激活后,终端提示符通常会发生变化,前面会多出(venv)字样。此时,python命令指向的是虚拟环境内的2.7解释器,pip命令也指向虚拟环境内的pip。
(venv) user@server:~/old_project$ python --version Python 2.7.18 (venv) user@server:~/old_project$ which python /home/user/old_project/venv/bin/python接下来,你就可以在虚拟环境中安全地安装项目依赖了。如果项目有requirements.txt文件,直接使用:
(venv) pip install -r requirements.txt如果没有,则需要根据项目文档或代码中的import语句手动安装。
完成工作后,使用deactivate命令退出虚拟环境。
(venv) deactivate5. 常见问题与故障排查实录
在实际操作中,你几乎一定会遇到下面这些问题。这里我把自己踩过的坑和解决方案整理出来。
5.1 编译阶段错误
问题1:ModuleNotFoundError: No module named ‘_ctypes’或在配置阶段报错。
- 原因:缺少
libffi的开发文件。 - 解决:安装
libffi-dev包,然后重新执行configure和make。sudo apt install -y libffi-dev
问题2:pip install时SSL证书验证失败。
- 现象:
pip is configured with locations that require TLS/SSL, however the ssl module in Python is not available. - 原因:Python编译时没有正确链接到系统的OpenSSL库。可能是在
configure阶段缺少libssl-dev,或者OpenSSL库的路径不标准。 - 解决:
- 确保已安装
libssl-dev。 - 在编译前,可以尝试找到OpenSSL的路径:
ls -l /usr/lib/x86_64-linux-gnu/libssl*。 - 有时需要显式指定OpenSSL路径进行重编译:
sudo ./configure --prefix=/opt/python27 --with-openssl=/usr --enable-optimizations sudo make clean sudo make -j$(nproc) sudo make install
- 确保已安装
5.2 运行时问题
问题3:运行Python脚本时提示/usr/bin/python: bad interpreter: No such file or directory。
- 原因:脚本的shebang行(第一行)指向了
#!/usr/bin/python,而这个路径在Ubuntu 20.04上默认不存在(可能是python3)。 - 解决:修改脚本的shebang行,指向我们安装的Python 2.7解释器绝对路径,例如
#!/opt/python27/bin/python2.7。或者,在虚拟环境中运行脚本,因为激活虚拟环境后,python命令会指向正确的解释器。
问题4:使用pip2安装包时速度极慢或连接超时。
- 原因:PyPI的官方源在国外,且对老旧pip版本的支持可能不佳。
- 解决:为pip配置国内镜像源。创建或编辑pip配置文件
~/.pip/pip.conf(对于全局pip是/etc/pip.conf):
对于虚拟环境中的pip,可以在激活环境后使用[global] index-url = https://pypi.tuna.tsinghua.edu.cn/simple trusted-host = pypi.tuna.tsinghua.edu.cnpip install -i https://pypi.tuna.tsinghua.edu.cn/simple some-package临时指定。
5.3 与系统Python 3的共存
问题5:如何区分python、python2、python3命令?
- 这是我们自定义安装的Python 2.7,位于
/opt/python27/bin/python2.7。我们为其pip创建了软链接pip2。 - 系统自带的Python 3位于
/usr/bin/python3,其包管理命令是pip3。 /usr/bin/python这个符号链接,在Ubuntu 20.04上默认指向python3。强烈建议不要修改它,因为很多系统工具(如apt)依赖于此。我们应该始终使用完整路径或我们创建的别名/虚拟环境来调用Python 2.7。
一个清晰的管理习惯是:永远不在系统级(/usr/bin/)的python或pip命令下安装Python 2的包。所有操作都通过/opt/python27/bin/python2.7、pip2或在虚拟环境中进行。
6. 备选方案:使用Docker容器运行Python 2.7环境
如果你觉得编译安装和配置环境变量太麻烦,或者需要在多台机器上快速部署一致的环境,Docker是最佳选择。
6.1 快速启动一个Python 2.7交互环境
docker run -it --rm python:2.7-slim bash这条命令会:
docker run:运行一个容器。-it:以交互模式运行并分配一个伪终端。--rm:容器退出后自动删除(用于临时测试)。python:2.7-slim:使用官方的、基于Debian的轻量级Python 2.7镜像。bash:在容器内启动bash shell。
进入容器后,你会发现Python 2.7和pip已经安装好了,可以直接使用。
6.2 使用Dockerfile构建项目专属镜像
对于正式项目,你应该创建一个Dockerfile来固化环境。
# Dockerfile FROM python:2.7-slim WORKDIR /app # 将宿主机的项目依赖文件复制到容器内 COPY requirements.txt . # 安装依赖,使用国内镜像加速 RUN pip install --no-cache-dir -i https://pypi.tuna.tsinghua.edu.cn/simple -r requirements.txt # 复制项目代码 COPY . . # 指定容器启动时默认运行的命令(例如你的主脚本) CMD ["python", "main.py"]然后,在项目根目录构建镜像并运行:
# 构建镜像 docker build -t my-old-python-app . # 运行容器,并将宿主机的当前目录挂载到容器的/app目录(方便开发调试) docker run -it --rm -v $(pwd):/app my-old-python-app # 或者,以守护进程模式运行 docker run -d --name my-app -v $(pwd):/app my-old-python-appDocker方案将环境隔离做到了极致,宿主机系统保持绝对干净。当你不再需要这个老项目时,只需要删除镜像和容器即可,没有任何残留。
7. 从Python 2到Python 3的迁移思考与工具
安装Python 2.7只是权宜之计。长期来看,将代码迁移到Python 3是必须完成的任务。这里分享一些启动迁移的思路和工具。
第一步:使用2to3进行初步语法转换Python自带了一个名为2to3的工具,可以自动修复许多常见的语法差异。务必在代码备份上进行操作!
# 查看2to3会建议哪些修改(不实际修改文件) 2to3 your_script.py # 将修改写回原文件(使用 -w 参数) 2to3 -w your_script.py2to3能处理诸如print语句、xrange()、unicode类型等基础问题,但对于更复杂的逻辑差异(如迭代器行为、标准库模块重组)则无能为力。
第二步:使用python-future和six库对于需要同时维护Python 2和Python 3兼容性一段时间的项目,可以使用future和six这两个兼容层库。它们提供了统一的API,让你可以用一种写法同时支持两个版本。
第三步:全面测试迁移后,测试是重中之重。你需要覆盖所有功能分支,特别是涉及字符串(文本/二进制)、网络I/O、文件操作和外部API调用的部分。编写或完善单元测试是保障迁移质量最有效的手段。
第四步:利用现代工具辅助像pylint、mypy这样的静态分析工具,以及caniusepython3这样的包,可以帮助你识别代码中不兼容Python 3的部分和依赖项。
最后,我个人在实际操作中的体会是,处理遗留的Python 2代码,心态比技术更重要。不要试图一次性完美迁移一个庞大的项目。可以采取“分而治之”的策略:先通过本文的方法让它在隔离环境中运行起来,保证业务连续性。然后,将项目拆分成模块,逐个模块进行迁移、测试和替换。对于最核心、最复杂的部分,甚至可以将其重写为一个小型的独立服务。记住,最终的目标不是“安装好Python 2.7”,而是“安全地告别Python 2.7”。
