news 2026/10/13 3:44:37

Ubuntu旧版本安装Docker完整指南:20.04/18.04/16.04兼容性解析

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Ubuntu旧版本安装Docker完整指南:20.04/18.04/16.04兼容性解析

做运维时间长了,你会发现最怕的不是新系统上的新功能,而是那些还在跑着老系统的机器。明明业务一直没迁移,安全补丁还得打,新组件还得装,偏偏环境又是 Ubuntu 20.04、18.04 这样的中老年系统。折腾 Docker 的时候,网上教程一大把,但大部分都默认你用的是最新 LTS,一换到旧版本就开始报错,不是仓库不对就是依赖冲突。这篇是我运维系列里的参考记录,标了【仅供参考】,因为不同内核、不同系统代差、不同网络策略,真照着抄的时候还是得结合自己环境来调整。

1. 为什么要在 Ubuntu 22.04 以下版本折腾 Docker

1.1 旧版本环境依然广泛存在

很多人会觉得,新装系统直接用 24.04,谁还在 20.04、18.04 上折腾?但实际生产环境里,老版本 LTS 的存在感极强。业务系统跑得稳的时候没人愿意动底层系统,数据库、应用中间件、编译环境全都绑在上面,一升级就是大项目,风险远大于收益。于是这些机器常年保持着 Ubuntu 20.04、18.04,甚至 16.04,直到某天有新的交付需求,需要部署容器化应用,Docker 就不得不上了。

在我的实操里,遇到过两种典型场景。一种是新项目要落地,但资源有限,不想再拆新机器出来,就拿现有的旧系统复用;另一种是客户要求环境统一,不同开发机、测试机、生产机要保持同一套系统版本,其中一部分机器是老的 LTS,导致新装机那套 Docker 安装方法在老的上面完全跑不通。这两种场景都很现实,核心矛盾就是:系统版本偏老,但容器化需求不会因此降低。

1.2 Docker 在旧版本上的兼容性边界

Docker 引擎不是单个静态二进制跑起来就完事,它依赖内核特性,比如 namespace、cgroups、iptables、overlayfs,还依赖 init 系统如何管理守护进程。Ubuntu 22.04 以下版本跨度很大,我用表格拉一个参考,方便你快速判断自己机器在什么水平:

系统版本默认内核安装 Docker 时的注意点
Ubuntu 20.04 (focal)5.4官方仓库支持好,注意 iptables 与 nftables 的关系
Ubuntu 18.04 (bionic)4.15需要用 lsb-release 正确获取代号,HWE 内核更稳
Ubuntu 16.04 (xenial)4.4新版 Docker 部分特性受限,建议锁定版本安装
Ubuntu 14.04 (trusty)3.13官方仓库已放弃,基本不建议强行安装

Docker 的最低内核要求是 3.10,所以从内核门槛来讲,16.04 以上都没问题。但存储驱动 overlay2 对内核要求更高,老内核上可能遇到挂载 overlay 失败的问题,尤其 16.04 默认 4.4 内核,跑新版 Docker 时偶尔会有莫名其妙的文件系统错误。

另外还要注意 init 系统。Docker 官方 deb 包安装后会注册为 systemd 服务,Ubuntu 16.04 之后都已经切换到 systemd,这一块问题不大。真正需要花心思的是仓库源能不能对上号,以及网络能不能访问到 Docker 官方源。

2. 安装方式选型:别急着复制粘贴

2.1 官方 apt 仓库安装

这是我最推荐的生产环境安装方式。通过官方 apt 仓库安装有几个明确好处:版本可锁定、升级路径清晰、卸载也干净。安装之前,关键一步是正确添加仓库,里面有个陷阱:仓库地址里的系统代号如果写错,后面 apt update 会直接报 404,或者根本找不到对应软件包。

不同 Ubuntu 版本的代号如下:

  • 20.04 → focal
  • 18.04 → bionic
  • 16.04 → xenial

网上很多教程直接让你用$(lsb_release -cs)动态获取,这个命令在大多数情况下是对的,但前提是系统里装了 lsb-release,而且系统代号确实在 Docker 官方仓库支持范围内。如果机器是 14.04,lsb_release 会返回 trusty,但 Docker 官方仓库里已经没有 trusty 目录了,照样报错。

所以我建议在动手前先执行一条命令确认系统代号:

cat /etc/os-release

重点看VERSION_CODENAME,后续仓库地址要用它来核对。

2.2 离线 deb 包安装

不少生产环境是内网隔离的,无法访问外网,这时候不能直接往 /etc/apt/sources.list.d 里添加源,装到一半也会因为没有网络而失败。离线安装的思路是:找一台与目标机器同系统版本、同架构、能联网的机器,先把所需 deb 包都下下来,然后拷贝到目标机器上安装。

具体操作,联网机器上先添加官方 repo 并执行 apt update:

sudo apt update sudo apt-get install --downloadonly docker-ce docker-ce-cli containerd.io

下载好的 deb 包默认在/var/cache/apt/archives/目录里。把目录下相关 deb 包拷到目标机器,然后在目标机器上进入存放目录执行:

sudo dpkg -i *.deb

如果 dpkg 报依赖缺失,说明依赖包没有全部下载,需要回到联网机器上把依赖也一并拿下来。这里有个经验:离线安装时不要指望 apt 帮你自动补齐依赖,最好用apt depends docker-ce把依赖树列出来,逐一核对。更简单的方法是在联网机器上用一个干净目录做模拟安装,比如apt install --no-install-recommends --downloadonly并检查缓存目录里是否有全部依赖。

2.3 为什么我不建议生产环境用卷脚本

curl -fsSL https://get.docker.com | sh这种一键脚本确实方便,很多测试环境我本人也这么干过,但生产环境我坚决不用。原因有三点:脚本内容不可审计,每次执行拿到的东西可能不一致;安装版本不受控,没法精确锁定你需要的版本;一旦脚本执行一半失败,残留的源和包状态清理起来费劲,反而比从零开始装还麻烦。

还有一点很现实:很多内网环境根本访问不了 get.docker.com,脚本第一步就挂掉。与其到时候找各种奇奇怪怪的搬运方案,不如从一开始就选择 apt 仓库或离线 deb 包方案,至少整个安装过程可追溯、可复现。

3. 完整安装步骤拆解

3.1 环境检查与依赖安装

登录机器后,先确认系统版本和内核:

cat /etc/os-release uname -r

接着更新 apt 索引,安装基础依赖。以 Ubuntu 20.04/18.04 为例:

sudo apt update sudo apt install -y ca-certificates curl gnupg lsb-release

这几个依赖各司其职:ca-certificates 负责 HTTPS 证书验证,curl 用来下载 Docker 官方 GPG 密钥,gnupg 用于验证密钥格式,lsb-release 负责提供 lsb_release 命令从而获取系统代号。

在老版本系统上,你还要顺手确认系统里是否已经有 iptables,因为 Docker 网络功能高度依赖 iptables:

iptables -t nat -L -n

如果这条命令执行报错,说明 iptables 本身有问题,后面启动 Docker 大概率也会死在网络上。遇到这种情况应该先修复 iptables,而不是急着装 Docker。

3.2 添加 Docker 官方仓库和 GPG 密钥

先建立密钥目录,并下载 Docker 官方 GPG 密钥:

sudo install -m 0755 -d /etc/apt/keyrings curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg sudo chmod a+r /etc/apt/keyrings/docker.gpg

这里解释一下每个动作的意图:install -m 0755 -d是创建 /etc/apt/keyrings 目录并设置权限,确保后续 apt 能读取;管道后接gpg --dearmor是将下载的 ASCII 格式公钥转换成二进制格式,apt 处理二进制密钥文件更稳定;chmod a+r保证系统其他用户也能读,避免权限问题导致 apt 拒绝使用密钥。

添加 apt 仓库:

echo \ "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu \ $(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null

这段命令里有几个细节值得展开:dpkg --print-architecture会输出 amd64 或 arm64,确保架构正确;signed-by 参数指向刚刚存放的密钥文件;最后的$(lsb_release -cs)就是系统代号,这里需要确认输出的结果在官方仓库中存在。

如果你的网络访问 download.docker.com 很慢或经常超时,可以直接把仓库地址替换成国内镜像站,效果立竿见影。比如使用阿里云镜像站:

curl -fsSL https://mirrors.aliyun.com/docker-ce/linux/ubuntu/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg echo \ "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://mirrors.aliyun.com/docker-ce/linux/ubuntu \ $(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null

注意,启用新仓库后必须重新执行 apt update,否则 apt 不会感知新源:

sudo apt update

这一步如果报错,比如 404 或源里找不到路径,多半是系统代号不对,或者源地址不支持当前版本。

3.3 安装 Docker 引擎并配置开机自启

执行安装命令:

sudo apt install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin

如果是在 18.04、16.04 上,docker-buildx-plugin和docker-compose-plugin可能在对应源里不存在,导致整体安装失败。这时候就只装核心组件:

sudo apt install -y docker-ce docker-ce-cli containerd.io

装完之后确认版本:

docker --version

启动服务并设置开机自启:

sudo systemctl enable docker --now systemctl status docker --no-pager

看到 active (running) 说明守护进程已经起来了。接着跑一个 hello-world 验证整体链路:

sudo docker run hello-world

如果镜像拉取失败,先不要急,可能是网络问题,也可能是没有配置镜像加速,后面章节会讲。

3.4 用户权限与基础配置

默认情况下,只有 root 或者 docker 组成员才能操作 docker 命令。为避免每次敲命令都加 sudo,把当前用户加入 docker 组:

sudo usermod -aG docker $USER

然后必须重新登录或者执行:

newgrp docker

否则当前会话的组权限不生效,你以为命令不对,实际上只是还没建立新会话。这个细节我在不少机器上踩过,网上很多教程都没提。

此时可以执行docker info看配置概览,确认存储驱动是 overlay2。如果输出里显示 storage-driver 不是 overlay2,而是 vfs 或其他,后面容器运行性能会很差,需要重点关注。

4. 旧版本系统上最常见的问题排查

4.1 添加仓库连接超时或签名验证失败

在旧版本系统上,最容易遇到的是网络问题。常见表现有两个:一个是 curl 下载 GPG 密钥时卡住或超时,另一个是 apt update 时提示无法解析 download.docker.com。

排查思路和解决办法如下:

先确认基础网络连通性:

curl -I https://download.docker.com

如果直接失败,换成国内镜像站往往是更快的解决办法。参考方法我在 3.2 节里已经写了,把源地址换成阿里云或中科大等镜像即可。

如果是签名验证失败,提示信息通常类似The following signatures couldn't be verified because the public key is not available。这种情况多半是密钥文件没下载完整,或者路径指向不对。重新部署一遍密钥文件,并确认 apt 源里的 signed-by 路径与实际密钥文件路径一致,基本能解决。

还有一个小概率问题:老系统上的 curl 版本过低,导致 HTTPS 证书验证失败。可以先更新 ca-certificates 再重试:

sudo apt update && sudo apt install -y ca-certificates

4.2 安装后 Docker 服务启动不了

这是让人最头疼的反馈,命令行明明安装成功,systemctl start docker 却一直失败。第一步永远是看日志:

journalctl -u docker -n 50 --no-pager

日志里比较常见的有两类信息。

第一类是 daemon.json 配置错误。Docker 启动时如果发现 /etc/docker/daemon.json 解析失败,会直接拒绝启动。比如少写一个逗号、多写一个括号,都会造成 JSON 解析错误。遇到这种问题,可以用下面命令先检查 JSON 合法性:

python3 -m json.tool /etc/docker/daemon.json

如果没有 Python,就手动检查配置文件结构,确保键值对的引号和逗号都正确。

第二类是 iptables 初始化失败。日志中出现failed to create NAT chain或permission denied,说明 Docker 尝试操作 iptables 时被系统拦截。根本原因是 Ubuntu 20.04 之后系统默认使用 nftables,而 Docker 依赖的是传统 iptables 接口,两者切换和兼容问题经常搞人心态。

我的排查顺序是:先看iptables -t nat -L -n能不能正常输出;再查是否有 nft 规则冲突;必要时重新安装 iptables 或调整 alternatives:

sudo apt install -y iptables sudo update-alternatives --config iptables

这一步可以在 iptables-legacy 和 iptables-nft 之间切换,根据系统实际情况选择,改完再重启 Docker。

4.3 容器端口映射和 UFW 防火墙冲突

跑在生产上的老机器,很多都开着 UFW 防火墙。Docker 默认会直接操作 iptables,它发布的端口位于 DOCKER 链中,会绕过 UFW 的 deny 规则。明明 UFW 已经禁用某端口,但从外部照样能访问到容器端口,这就是经典冲突现场。

这个问题最稳妥的解决方式不是关掉 UFW,而是在/etc/ufw/after.rules中编写规则,让 Docker 发布的端口也要经过 filter 校验。更极端的做法是在 daemon.json 里设置"iptables": false,让 Docker 放弃管理 iptables,但这样端口映射和跨主机容器网络都受影响,需要自己维护规则,新手不建议轻易尝试。

我个人在 18.04 上的处理方式是:保留 UFW,在 after.rules 里增加针对 Docker 网桥的过滤,再重启 UFW。整个过程比较复杂,但胜在生产环境安全可控。

4.4 旧系统内核导致的存储驱动问题

在 16.04 或更老的系统上,启动容器时可能遇到类似failed to mount overlay的报错。overlay2 需要内核支持 overlayfs,4.4 内核虽然理论上可以,但某些配置下会出现兼容性问题。

我提供的备选方案有三个:

第一,升级到 HWE 内核。Ubuntu 16.04 可以安装 HWE 内核,比如:

sudo apt install -y linux-generic-hwe-16.04

装完重启,uname -r 看到新版本后,overlay2 通常就能用了。

第二,修改 daemon.json 使用 vfs 存储驱动。vfs 不依赖 overlayfs,兼容性最好,但性能很差,镜像和容器占用的磁盘空间也大,只适合临时验证。

第三,建议直接迁移系统或换新版本。老平台硬撑不是长久之计。

5. 安装完成后的优化建议

5.1 用 daemon.json 统一配置

安装完只是开始。Docker 默认配置不会管日志大小,也没配置镜像加速,跑一段时间后宿主机磁盘可能被容器日志占满。我建议安装完成后第一时间创建 /etc/docker/daemon.json:

{ "storage-driver": "overlay2", "log-driver": "json-file", "log-opts": { "max-size": "10m", "max-file": "3" }, "registry-mirrors": ["https://你的加速地址.mirror.aliyuncs.com"] }

各字段含义说明:storage-driver 指定存储驱动,生产环境强烈建议 overlay2;log-driver 和 log-opts 限制单个容器日志文件大小和数量,防止日志撑爆磁盘;registry-mirrors 配置国内镜像加速器地址,不同云平台提供的地址不同,阿里云需要登录容器镜像服务控制台获取专属地址。

配置完成后重启 Docker:

sudo systemctl restart docker

重启后执行docker info确认配置生效,重点看 Storage Driver、Logging Driver 和 Registry Mirrors 三行。

注意:daemon.json 语法错误会导致 Docker 启动失败。每次修改文件后,我都先跑一次dockerd --validate或者用 json.tool 校验,确认无误再重启服务。

5.2 容器资源限制与监控

多容器部署在同一台老机器上时,资源隔离很关键。如果某个容器写死 CPU 或内存,可能拖垮整个宿主机。通过 daemon.json 也可以设置默认资源约束,比如:

{ "default-ulimits": { "nofile": { "Name": "nofile", "Hard": 65535, "Soft": 65535 } }, "pids-limit": 512 }

pids-limit 限制每个容器可创建的进程状态数,防止 Fork 炸弹把系统打挂。default-ulimits 设置文件描述符上限,避免运行高并发应用时出现 too many open files。

还可以引入监控命令,快速查看容器资源占用:

docker stats --no-stream

这个命令输出所有运行中容器的 CPU、内存、网络和磁盘 I/O 情况。老系统资源本来就有限,监控要尽早配置,等出问题再补救就很被动。

结尾

最后说点自己折腾老版本 Docker 的真实体会。最深的感受是:顺序比命令重要,环境比版本重要。先花几分钟确认系统代号、内核版本、iptables 状态和网络策略,远比装到一半再翻日志省力。网上很多教程默认你用的是新版 LTS,遇到老系统时仓库源、依赖包、存储驱动这些细节全都会变成拦路虎。还有一个我的习惯:装之前会把关键命令提前写成脚本保存到本地,方便批量或重复部署时直接复用,同时每一次安装都留一份操作记录,出问题能迅速定位是环境差异还是人为失误。

如果你手里的系统真的很老,比如连 Docker 官方仓库都已经放弃支持了,那我劝你认真考虑系统升级或迁移业务,老环境里强行堆新组件,后续维护成本会高到让你怀疑人生。这篇【仅供参考】的运维记录希望能让你在旧系统的 Docker 安装道路上少走几步。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/13 3:43:54

鸿蒙Flutter混合开发指南:从环境搭建到跑通首个页面

前阵子公司要启动鸿蒙版本,我翻了一圈资料,发现大部分教程都在讲 ArkTS 原生开发,可我们产品线过去两年一直用 Flutter 写跨端,代码和组件全在 Flutter 里。要是完全另起炉灶,用鸿蒙原生重写一遍,周期和人力…

作者头像 李华
网站建设 2026/10/13 3:43:54

用NodeXL做动态网络分析:从时间线到演化指标全流程

很多人做社会网络分析,第一步就是把边列表导入软件,出一张五颜六色的网络图,然后开始数节点、算密度、找社群。说实话,静态分析确实入门友好,但我自己在用 NodeXL 的过程中越来越觉得,如果数据里带着时间戳…

作者头像 李华
网站建设 2026/10/13 3:43:54

RouterOS WebFig汉化补丁:v7.9–v7.13内核级本地化方案

简介:本资源是专为中文网络管理员与RouterOS初学者设计的Web界面汉化补丁包,解决英文WebFig界面带来的理解与操作障碍,显著提升中文用户在企业级路由配置中的效率与体验。压缩包共4个文件(257KB),含1个核心…

作者头像 李华
网站建设 2026/10/13 3:43:31

进程管理与计划任务:从基础命令到系统化运维

1. 进程和计划任务,运维手里最常被低估的两张牌如果你在服务器上排查过"CPU突然飙高但找不到元凶"的问题,或者遇到过"明明设置了定时任务却总在凌晨三点悄悄失效"的怪事,就一定明白我在说什么。进程管理管的是"现在…

作者头像 李华
网站建设 2026/10/13 3:43:16

WPF DataGrid指定列可编辑:三种实现方案与避坑指南

做WPF桌面应用的人,迟早都要和 DataGrid 打交道。刚上手那会儿,我以为绑上数据、能显示出来就完事了,直到被安排了一个很常见的需求:列表里有一列可以填写实际数量,其他列全部只读,不能误改。这个功能听起来…

作者头像 李华