☰
Amazon Linux 2023 源码编译 Python 3.12 与全局环境变量配置
2026/9/26 23:46:24 网站建设 项目流程

刚拿到一台Amazon Linux 2023实例时,最容易被卡住的需求就是装新版Python 3.12。我手头有个内部的数据清洗任务,代码用了tomllib和Self类型这类只在 3.11+ 里稳定的特性,系统默认的 Python 3.9 跑不动,当时我下意识执行dnf install python3.12,结果直接No match for argument。这一篇就把我在 Amazon Linux 2023 上编译安装 Python 3.12,并且把环境变量一次性配到位的完整过程拆开讲,包括每一步背后的逻辑、那些官方文档没写的坑,以及多版本共存时怎么保证安全和稳定。不管你是临时想跑个新脚本,还是要正经交付服务器环境,按我这套流程走下来都能落地。

1. 先搞清楚现状:AL2023 自带 Python 的版本与仓库限制

1.1 系统默认自带的情况

Amazon Linux 2023 默认的 Python 版本是 3.9,这个版本满足绝大多数系统工具的依赖,比如dnf、cloud-init、ec2-net-utils这些系统组件都在用它。你可以执行:

cat /etc/os-release python3 --version

看到类似Python 3.9.16的输出是正常的。这里有个很重要的认知:系统自带的 Python 3.9 不能动,因为很多系统级的 Python 脚本(尤其/usr/libexec和/usr/bin下的工具)都依赖它。如果你把它替换成 3.12,轻则dnf直接报错,重则整台实例的包管理器都无法工作。

1.2 默认软件源里搜不到 Python 3.12

在 AL2023 上执行下面这条命令:

dnf search python3.12

大概率得到的结果是No matches found,或者只有零散的子包但没有完整的运行时。这是因为 AL2023 的软件仓库策略相对保守,它主要维护 LTS 版本,Python 主版本的更新节奏远慢于社区。仓库里目前比较确定的是python3.11和相关模块,你可以试一下:

dnf info python3.11

如果对 3.11 可以接受,直接:

sudo dnf install -y python3.11 python3.11-pip

这种安装方式最简单,但既然这里的需求明确是Python 3.12+,而 3.12 在多数云厂商默认仓库里都没有,那就必须换路线。

1.3 "搜索不到"不等于"装不了"

我见过很多人在dnf search找不到包之后就放弃或者直接换系统。实际上这只是说明默认仓库没有预编译好的 RPM,并不代表 Python 3.12 用不了。常见的替代路径有三条:EPEL 扩展仓库、源码编译、自建 RPM 包。EPEL 对 AL2023 的支持目前还处于逐步完善阶段,Python 3.12 的包并不齐全,有时候装出来还会依赖一堆用不上的模块,反而拖慢机器。在云服务器上最稳妥、也最可控的思路,就是走一次源码编译。

2. 方案选型:为什么我选择源码编译而不是走 RPM 或者工具链

2.1 三种可行路线的对比

方案优点缺点适用场景
dnf直接装 3.11快、省心、系统原生管理当前仓库没有 3.12,版本可能滞后不挑版本,只想尽快跑通
EPEL 装 3.12比源码安装省事AL2023 下包不齐,可能拉入多余依赖恰好遇到仓库里有完整包的时候
源码编译 3.12版本完全可控、可指定 prefix、优化选项丰富编译耗时几分钟到十几分钟、需要装开发依赖生产环境、多版本共存、有定制需求

我自己在 AL2023 上测试,源码编译 3.12 整个过程大概 8~12 分钟(取决于实例规格),这点时间成本换来的是一个完全符合预期的独立 Python,很划算。

2.2 源码编译为什么可控

用源码编译可以自主决定:

  • 安装目录(--prefix),把它隔离在/usr/local/python3.12,不动系统路径;
  • 启用哪些编译优化(比如--enable-optimizations做 PGO);
  • 是否顺带装 pip(--with-ensurepip=install);
  • 编译时链接系统 OpenSSL 的方式。

这种隔离思路在多个 Python 版本共存时尤其重要。你不需要删除系统 Python,也不用改/usr/bin下的任何文件,就能拥有一个完全独立的 3.12 环境,后续维护也不影响系统稳定性。

2.3 编译安装的底层逻辑:configure 到底在干嘛

很多新手对./configure这步不太理解,可以把它类比成"装修前先量房"。它不会真正构建 Python,而是做一堆检查:当前系统有没有 C 编译器?OpenSSL 头文件在哪?libffi版本够不够?然后把这些答案写进Makefile。如果系统缺少某个库,configure阶段就会报错,告诉你缺了什么,这时候及时补依赖比等make到一半再中断要好处理得多。

所以记住这个链条:依赖检测 → configure 生成 Makefile → make 编译 → make install 安装。后续踩坑大部分都发生在第一步和第三步之间的衔接位置。

3. 完整实操:从零编译安装 Python 3.12 的手把手步骤

3.1 安装编译依赖:少装一个包后面都是泪

回到实例上,先升级索引并安装编译工具链和 Python 运行时依赖:

sudo dnf update -y sudo dnf groupinstall -y "Development Tools" sudo dnf install -y openssl-devel bzip2-devel libffi-devel zlib-devel readline-devel sqlite-devel xz-devel wget tar

Development Tools这个组包覆盖了 gcc、gcc-c++、make 等核心工具,不用单独一个个装。

重点说几个容易漏的包:

  • openssl-devel:如果不装,编译出来的 Python 大概率没有_ssl模块,pip 在 HTTPS 场景下直接废掉;
  • libffi-devel:缺少它会导致_ctypes构建失败,而ctypes又是很多底层库(比如cryptography)依赖的东西;
  • zlib-devel:缺少它会在make install阶段因为 zipimport 失败而中断安装;
  • readline-devel和sqlite-devel:分别是交互式命令行和sqlite3模块的基础。

这几项几乎是 Python 源码编译的标准前置条件,在 AL2023 上尤其要一次装齐,否则后续排查会非常痛苦。

3.2 下载源码并解压到指定目录

我习惯把源码放在/usr/src下,避免放在家目录里不小心被清理掉:

cd /usr/src sudo wget https://www.python.org/ftp/python/3.12.4/Python-3.12.4.tgz sudo tar -xzf Python-3.12.4.tgz cd Python-3.12.4

需要注意版本号。3.12.x 是一个持续迭代的系列,你去的时候可能已经有更新的补丁版本,比如 3.12.5、3.12.6。优先用 python.org 的 FTP 目录 里最新的 3.12.x,因为 patch 版本往往包含安全和兼容性修复。

3.3 configure 参数里藏着哪些门道

这是整个编译过程里最需要认真对待的一步。我使用的参数如下:

sudo ./configure --prefix=/usr/local/python3.12 --enable-optimizations --with-ensurepip=install

逐个拆开解释:

  • --prefix=/usr/local/python3.12:指定安装目录。把 Python 隔离到独立目录,这样卸载时直接删掉这个目录就干净了,也不会污染/usr/local/bin。
  • --enable-optimizations:开启性能优化,效果相当于用当前机器上已有的编译器做一次 Profile Guided Optimization。代价是编译时间变长,但对 Python 这种解释型语言,运行时性能提升是实打实的。
  • --with-ensurepip=install:安装结束后自动拉取并安装 pip。装完少一步手动引导 pip 的操作。

另外还有一个参数值得留意,如果你有特殊需求可以加,但默认场景没必要:

--enable-shared

共享库模式会让 Python 以.so动态库形式存在,方便像 mod_wsgi 这类扩展调用,但代价是多了LD_LIBRARY_PATH设置,环境变量配置复杂度更高。本地跑脚本、普通服务部署用静态模式就够了,不要为了"灵活"给自己找麻烦。

3.4 make 和 install:用 altinstall 保护系统 python3

编译:

sudo make -j$(nproc)

-j$(nproc)表示用机器的所有 CPU 核心并行编译。在 2C4G 的实例上大约 5~8 分钟,在 4C8G 上能压到 3 分钟左右。编译过程中如果看到个别 warning 不用慌,只要没中断都算正常。

安装时有一个关键动作:

sudo make altinstall

这里切记不要用make install,要用altinstall。install会把二进制安装成python3,直接覆盖掉/usr/bin/python3这个软链,而/usr/bin/python3是系统包管理工具和很多服务的命根子。altinstall只会生成python3.12和python3.12-config,不会碰python3,从源头杜绝了把系统环境搞坏的可能性。

进一步确认安装结果:

sudo ls /usr/local/python3.12/bin/

正常应该能看到python3.12和pip3.12。执行:

/usr/local/python3.12/bin/python3.12 --version

能输出Python 3.12.4就是好的开始。

3.5 顺手把 pip 升级到最新

装完自带一个匹配的 pip,建议立刻升级:

/usr/local/python3.12/bin/python3.12 -m pip install --upgrade pip

这一步看起来多余,但自带的ensurepip通常内置了一个略旧的 pip,升级能避免后续安装包时因为旧版 pip 导致依赖解析异常。

4. 环境变量配置:让 python3.12 在任意目录下都认你

4.1 为什么编译完还是要配环境变量

你可能会疑惑,/usr/local/python3.12/bin都已经有二进制文件了,直接输全路径也能调用,何必再配 PATH?原因有两个:

第一,日常使用不可能每次都敲全路径。把所有标志性目录加进 PATH 后,在任意工作目录直接输python3.12和pip3.12就能调用,这是命令行体验的基础。

第二,不少自动化工具(比如 cron 定时任务、systemd 服务脚本、IDE 的远程解释器)在非交互 shell 环境下加载的是系统级环境变量,而不是你手动敲source ~/.bashrc后的那些。如果不把这套配置写到全局生效的文件里,定时任务里就会出现"找不到 python3.12"的诡异现象。

4.2 全局配置 vs 用户配置:写在 /etc/profile.d 更省心

环境变量配置有两种常见位置:

  • 用户级:写在~/.bashrc或~/.bash_profile,只对当前用户生效;
  • 系统级:写在/etc/profile.d/xxx.sh,所有用户、所有登录会话都生效。

我强烈推荐写在/etc/profile.d/下,因为服务器上通常需要这个 Python 环境的不止一个服务账号。如果你把它写进root的~/.bashrc,后面切换到普通用户或者用sudo -u跑任务,又得排查一遍。

创建配置文件:

sudo tee /etc/profile.d/python312.sh <<'EOF' export PYTHON_HOME=/usr/local/python3.12 export PATH=$PYTHON_HOME/bin:$PATH EOF

然后让当前会话立即生效:

source /etc/profile.d/python312.sh

验证:

which python3.12 echo $PATH

which python3.12应该返回/usr/local/python3.12/bin/python3.12。

4.3 用软链做一个"急救入口"

如果不想让 PATH 里塞太多目录,也可以选择在/usr/local/bin下建软链,因为/usr/local/bin默认就在 PATH 里:

sudo ln -s /usr/local/python3.12/bin/python3.12 /usr/local/bin/python3.12 sudo ln -s /usr/local/python3.12/bin/pip3.12 /usr/local/bin/pip3.12

这样一来,不配置环境变量也能直接输python3.12。但软链方式不适合管理多个 Python 版本:切换版本时需要把软链指来指去,容易混乱。所以我的建议是:主要版本用 PATH 方式,临时备用的版本才用软链入口。

4.4 用 alternatives 管理多版本 Python 的优雅姿势

如果你的机器上同时存在 3.9、3.11、3.12 多个版本,想快速切换默认版本,可以使用系统自带的update-alternatives机制,它比手动改软链规范得多。

注册 Python 3.12 到 alternatives:

sudo update-alternatives --install /usr/local/bin/python3.12 python3.12 /usr/local/python3.12/bin/python3.12 100

如果后续装了 3.12.5、3.12.6 等多个补丁版本,可以通过:

sudo update-alternatives --config python3.12

交互式切换优先级,或者修改/etc/alternatives/python3.12软链的指向。

但要提醒一点:绝对不要通过 alternatives 把新的 python3.12 注册成系统的 python3,更不要注册成 /usr/bin/python3。你真正能安全管理的目标是/usr/local/bin/python3.12这类非系统路径的入口,系统的/usr/bin/python3永远不要动。

4.5 环境变量生效的验证方法

配置完之后要分两种 shell 验证,别只在当前窗口验证一次:

第一,重新登录一个新的 SSH 会话,执行:

python3.12 --version command -v python3.12

第二,用一个非交互式环境验证:

env -i /bin/bash -c 'source /etc/profile.d/python312.sh; python3.12 --version'

这里之所以要单独验证非交互场景,是因为 cron 和 systemd 的环境和普通登录 shell 不一样,很多"我明明配了环境变量但定时任务还是找不到命令"的故障,根源就在这里。

5. 实测中一定会遇到的坑和解决办法

5.1 configure 阶段提示无法找到 OpenSSL

如果你在./configure时看到类似这样的报错:

checking for libssl... no Could not find the OpenSSL library, please install its dev package.

基本原因就是没有装openssl-devel。在 AL2023 上安装:

sudo dnf install -y openssl-devel

装完之后重新从./configure开始跑,先删掉之前生成的Makefile和缓存:

sudo make distclean sudo ./configure ...

这里特别提醒一次:如果补齐依赖后直接make,可能会出现一些难以解释的链接错误,因为旧配置已经写了"找不到 OpenSSL"的结论。每次补完依赖,必须make distclean后再 configure。这个习惯能省下大量排查时间。

5.2 编译成功了,但 pip 报No module named '_ssl'

这是我见过最多的情况。明明编译过程没报错,结果执行:

python3.12 -c "import ssl"

提示ModuleNotFoundError: No module named '_ssl',或者 pip 在访问 HTTPS 源时卡住。

原因很简单:系统缺少 OpenSSL 开发头文件,而 Python 在编译_ssl扩展模块时会实时检测。如果检测不到头文件,它不会让整个编译失败,而是"静默跳过"这个模块,导致装出来的 Python 缺失 SSL 能力。要规避这个问题,最稳的办法是在configure阶段就显式指定 OpenSSL:

sudo ./configure --prefix=/usr/local/python3.12 --enable-optimizations --with-ensurepip=install --with-openssl=/usr

--with-openssl=/usr是明确告诉编译器"去 /usr 下找 openssl 的头文件和库"。AL2023 里 OpenSSL 的安装路径通常位于/usr/include/openssl和/usr/lib64,配好之后也要等到安装完成再验证:

python3.12 -c "import ssl; print(ssl.OPENSSL_VERSION)"

能输出 OpenSSL 版本号,说明 SSL 模块是真的可用的。

5.3 把系统 python3 强行替换的严重后果

网上有些教程会教你:

sudo ln -sf /usr/local/python3.12/bin/python3.12 /usr/bin/python3

这种操作在Amazon Linux 2023 上极其危险。系统里的dnf、cloud-init、rpm脚本、甚至aws命令行工具都依赖/usr/bin/python3。一旦把它换成 3.12,轻则出现ModuleNotFoundError,重则所有与包管理相关的命令全部瘫痪,最后只能从快照回滚。

正确思路是上面反复强调的altinstall加 PATH 环境变量,让新版本和旧版本井水不犯河水。如果确实需要系统级python3命令指向 3.12,必须经过充分测试且做好应急回滚方案,在核心生产环境里不要拍脑袋做。

5.4 虚拟环境用的不一定是你刚装的 Python

如果后面你用python3.12 -m venv创建虚拟环境,它默认会关联当前的解释器:

python3.12 -m venv myenv source myenv/bin/activate python --version

这个没问题。但有一种情况容易踩坑:系统其他脚本创建的 venv 很可能使用的是旧的系统 Python,比如某个项目文件里写死了:

#!/usr/bin/env python3

因为它寻找的是 PATH 里的python3,而你没有修改/usr/bin/python3时,它找到的仍然是 3.9。遇到这种情况,要么在项目虚拟环境里明确指定解释器路径,要么在 venv 之外用别名:

alias python=/usr/local/python3.12/bin/python3.12

或者更干净的做法:直接编辑项目的虚拟环境配置文件myenv/pyvenv.cfg,把home = /usr/local/python3.12/bin指到新环境目录。改完重启 shell 就生效。

5.5 权限设计:普通用户如何调用新装的 Python

源码编译需要sudo,但安装完成之后没必要让每个应用用户都拥有写/usr/local/python3.12的权限。我习惯这样分配:

sudo chown -R root:root /usr/local/python3.12

这样普通用户只拥有读和执行权限,能运行 Python 但不能往系统目录里随意装包。需要装包时要么用sudo,要么使用虚拟环境。这能防止团队成员误操作污染全局 Python 环境,尤其是多人共用的开发服务器。

最后分享一点我的实际体会

这套流程我在多台 AL2023 实例上跑过,从第一次踩了_ssl的坑到现在整条链路闭着眼都能装完,最大的经验就三个:一是编译依赖一次装齐,不要挤牙膏式地缺啥补啥;二是永远用altinstall保护系统 Python;三是环境变量放到/etc/profile.d/全局配置,并且要用非交互 shell 验证。把这个思路迁移到 Ubuntu、Rocky Linux 上也完全成立,只是包管理器的命令从dnf换成了apt或yum。希望这篇能帮你少绕几圈,在 Amazon Linux 2023 上顺利用上 Python 3.12。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询