升级 Ubuntu 20.04 LTS (Focal Fossa) 是个大动作,尤其是对于还在用 18.04 或者更老版本的用户来说。虽然 20.04 是长期支持版,稳定性不错,但从底层库到内核都有不少变动,踩坑的概率确实不低。
作为在云服务器和开发环境里摸爬滚打多年的老兵,我总结了几个最常见、也最让人头大的兼容性问题,以及对应的“避坑”指南。
1. Python 环境的“断崖式”切换:从 3.6 到 3.8
这是开发者遇到最多的问题。Ubuntu 18.04 默认绑定的是 Python 3.6,而 20.04 直接跳到了 Python 3.8。
- 现象:
- 原本能跑的
pip install突然报错,找不到某些依赖包(因为很多旧包只支持到 3.7)。 - 系统级命令如
apt内部脚本如果硬编码了#!/usr/bin/python3,可能会因为路径或模块缺失而失效(虽然 Ubuntu 团队做了软链接处理,但仍有风险)。 - 虚拟环境(venv/virtualenv)重建后,部分 C 扩展库编译失败。
- 原本能跑的
- 对策:
- 不要直接依赖系统自带的 python3,尤其是生产环境。强烈建议使用
pyenv或者 Docker 来管理多版本 Python。 - 如果你必须用系统 Python,记得检查
/etc/apt/sources.list中是否有第三方 PPA 引入了冲突的 Python 包。 - 对于需要旧版本 Python 的项目,优先考虑迁移到容器化部署,而不是强行在宿主机上安装 Python 3.6/3.7(容易污染系统环境)。
- 不要直接依赖系统自带的 python3,尤其是生产环境。强烈建议使用
2. MySQL/MariaDB 的版本变更与字符集陷阱
Ubuntu 20.04 默认仓库中的 MySQL 版本通常是 8.0(社区版)或 MariaDB 10.3+。
- 现象:
- 认证插件变更:MySQL 8.0 默认使用
caching_sha2_password,而许多老旧的 Java 应用(如 Spring Boot < 2.2)、PHP 框架或数据库连接池(如 HikariCP 早期版本)不支持这种新的认证方式,导致连接超时或鉴权失败。 - 时区数据缺失:新安装的 MySQL 可能没有加载时区表,导致
NOW()函数返回的时间不对,或者出现Unknown timezone错误。 - SQL 模式严格化:MySQL 8.0 默认开启了更严格的 SQL 模式,以前能跑通的“宽松” SQL 语句现在会报错。
- 认证插件变更:MySQL 8.0 默认使用
- 对策:
- 如果是 Java/PHP 老项目,建议在 MySQL 配置中将用户密码认证改回
mysql_native_password(注意安全性权衡),或者升级驱动。 - 初始化时务必运行
mysql_tzinfo_to_sql /usr/share/zoneinfo | mysql -u root -p mysql导入时区数据。 - 备份!备份!备份!升级前导出所有数据库结构。
- 如果是 Java/PHP 老项目,建议在 MySQL 配置中将用户密码认证改回
3. NVIDIA 显卡驱动与 CUDA 生态的断裂
对于搞 AI、深度学习或图形渲染的用户,20.04 的内核更新对闭源驱动的影响很大。
- 现象:
- 升级后黑屏,无法进入 GUI。这是因为 Nouveau 开源驱动与 NVIDIA 闭源驱动冲突,或者内核头文件不匹配。
- CUDA 工具链版本不匹配:NVIDIA 官方提供的
.deb安装包有时滞后于 Ubuntu 的新内核,导致nvidia-smi正常但nvcc编译失败,或者 CUDA 运行时库找不到。
- 对策:
- 禁用 Nouveau:在 GRUB 启动参数中添加
nouveau.modeset=0。 - 使用 DKMS 机制:确保安装了
linux-headers-generic和dkms,这样内核更新后驱动能自动重新编译。 - 推荐方案:与其纠结于系统级的 NVIDIA 驱动,不如直接使用 Docker + nvidia-docker2。在容器内指定 CUDA 版本,避免宿主机的驱动版本锁定问题。这是目前最稳定的做法。
- 禁用 Nouveau:在 GRUB 启动参数中添加
4. Systemd 服务与 Init 脚本的混用
Ubuntu 20.04 完全基于 systemd,但一些老软件的安装包(特别是通过 .sh 脚本或旧版 apt 包安装的)可能还保留着 init.d 风格的启动脚本。
- 现象:
- 服务设置开机自启后,重启机器发现服务没起来。
systemctl start xxx成功,但status显示 inactive (dead),实际上是因为依赖的服务还没启动,或者环境变量未加载。
- 对策:
- 检查
/etc/systemd/system/下是否有对应的.service文件。如果没有,手动编写一个简单的 service 文件是最稳妥的。 - 特别注意
EnvironmentFile的路径是否正确,以及 ExecStart 的路径是否使用了绝对路径。
- 检查
5. 防火墙规则丢失:UFW vs iptables
很多人习惯直接用 iptables 操作防火墙,但在 Ubuntu 20.04 中,推荐使用 UFW(Uncomplicated Firewall)。
- 现象:
- 升级后,之前配置的 iptables 规则可能没有被正确持久化,或者被 UFW 的规则覆盖/冲突。
- 云厂商(如阿里云、腾讯云)的安全组已经放行了端口,但本地 UFW 拒绝了流量,导致排查困难。
- 对策:
- 统一防火墙管理工具。要么全用 UFW(简单直观),要么全用 iptables/nftables(高级定制)。
- 升级前先用
sudo ufw status verbose或sudo iptables -L -n记录当前规则。 - 升级后立即检查 UFW 状态:
sudo ufw enable并重新添加必要的规则。
6. Docker 容器网络问题
升级到 20.04 后,内核版本从 4.15 跳到了 5.4。
- 现象:
- Docker 容器内的网络连接不稳定,或者 DNS 解析失败。
- 某些依赖特定内核特性(如 cgroup v1 vs v2)的应用行为异常。Ubuntu 20.04 默认仍使用 cgroup v1,但部分新软件开始尝试适配 v2。
- 对策:
- 升级 Docker 到最新版本(>= 19.03),以更好地支持新内核。
- 如果遇到 DNS 问题,检查
/etc/resolv.conf是否被 NetworkManager 劫持,建议将 Docker 的 DNS 设置为8.8.8.8或114.114.114.114。
✅ 给升级前的终极建议
- 快照/备份是第一生产力:无论是物理机还是云服务器,升级前打个快照(Snapshot)或备份整个根分区。一旦翻车,一键恢复比修 bug 快得多。
- 阅读 Release Notes:去 Ubuntu 官网看看 20.04 的已知问题(Known Issues),特别是关于你正在使用的特定软件的部分。
- 最小化升级:如果只是个人桌面使用,可以接受折腾;如果是生产服务器,强烈建议在新服务器上安装 20.04,然后迁移数据和服务,而不是直接在旧服务器上执行
do-release-upgrade。后者在复杂环境下失败率极高,且排错成本巨大。
希望这些经验能帮你少掉几根头发。如果有具体的报错日志,欢迎贴出来一起分析。
云技术博客