1. 项目概述

在Ubuntu上安装Docker,对于很多开发者来说,几乎是环境搭建的“第一课”。但很多时候,我们需要的不仅仅是“能装上”,而是“装上指定版本”。比如,你接手了一个老项目,其Dockerfile里明确要求了 docker-ce=20.10.7 ;或者,你正在部署一个对Docker版本有严格依赖的CI/CD流水线,新版本可能引入了不兼容的改动。这时候,一句简单的 sudo apt install docker.io 或者跟着官方教程安装最新版,很可能就把你带进坑里。我遇到过不止一次,因为版本不匹配,导致镜像构建失败或者容器网络行为异常,排查起来非常耗时。所以,掌握在Ubuntu上精准安装指定版本Docker的方法,是一项非常实用的基本功。这篇文章,我会手把手带你走通两条最核心的路径:官方的标准安装流程,以及针对国内网络环境优化的国内镜像源安装方法,确保你能在任何情况下,都能把需要的Docker版本稳稳当当地装到系统里。

2. 核心思路与方案选型

为什么安装Docker会有“指定版本”这个需求?这背后其实涉及到软件部署的稳定性和可复现性。Docker作为一个活跃开发的开源项目,版本迭代很快,新版本固然带来了新特性和性能提升,但也可能伴随着Bug或对旧有使用习惯的破坏性变更。在生产环境或要求严格一致的开发环境中,锁定一个经过验证的稳定版本,是规避风险的最佳实践。

在Ubuntu上实现这个目标,主流方法就是通过APT包管理器来安装Docker官方维护的 docker-ce (社区版)包。整个流程的核心思路可以概括为: “添加正确的软件源 -> 获取可用的版本列表 -> 精确安装目标版本” 。这里的关键在于“正确的软件源”。我们将探讨两种源:

  1. 官方源 :直接从Docker官方和Ubuntu官方仓库获取,是最标准、最“纯净”的路径,适用于网络通畅的环境。
  2. 国内镜像源 :将官方源的地址替换为国内镜像站(如阿里云、腾讯云、中科大等)的地址,利用国内CDN加速下载,解决官方源在国内访问速度慢甚至无法连接的问题。这是在国内环境下的首选方案。

两种方案的底层操作(添加仓库、安装软件)是完全一致的,区别仅在于软件仓库的地址。因此,掌握了一种,另一种也就触类旁通了。下面,我们就从最基础的准备工作开始。

3. 安装前的系统准备

无论选择哪种源,在动手安装之前,都需要对Ubuntu系统做一些必要的检查和清理工作,这能避免很多后续的奇怪问题。

3.1 卸载旧版本Docker

如果你的系统上曾经通过其他方式安装过Docker(比如 docker.io docker-engine ),强烈建议先彻底卸载它们,以免和我们要安装的 docker-ce 产生冲突。

sudo apt-get remove docker docker-engine docker.io containerd runc

这条命令会移除这些旧的Docker相关包,但通常不会删除镜像、容器、卷等数据,它们默认存放在 /var/lib/docker/ 下。如果你需要全新开始,可以手动删除这个目录( 警告:这将永久删除所有Docker数据! ):

sudo rm -rf /var/lib/docker
sudo rm -rf /var/lib/containerd

3.2 安装基础依赖工具

我们需要一些工具来帮助添加和管理APT仓库。

sudo apt-get update
sudo apt-get install \
    ca-certificates \
    curl \
    gnupg \
    lsb-release
  • ca-certificates :用于验证HTTPS证书,确保从网络下载内容的安全性。
  • curl :命令行下载工具,用于获取Docker的GPG密钥。
  • gnupg :用于管理GPG密钥,验证软件包签名。
  • lsb-release :一个工具,可以方便地获取当前系统的LSB(Linux标准基础)信息,比如发行版代号,我们后面会用到。

3.3 信任Docker的APT仓库GPG密钥

APT包管理器通过GPG密钥来验证软件仓库的合法性,防止安装被篡改的软件包。我们需要先下载并信任Docker官方发布的密钥。

官方源方法:

sudo mkdir -p /etc/apt/keyrings
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg

这条命令做了两件事:1. 创建存放密钥的目录;2. 从Docker官网下载GPG公钥,并将其转换为APT可识别的格式,保存到指定文件。

国内源方法(以阿里云镜像为例): 原理相同,只是从镜像站下载密钥。但请注意,有些镜像站可能不直接提供GPG密钥文件,或者其密钥文件地址不同。一个更稳妥的方法是, 仍然从Docker官方下载密钥 ,因为密钥本身很小,且不需要频繁下载,直接使用官方地址通常没有问题。镜像源主要加速的是软件包本身的下载。所以这一步,通常可以沿用上面的官方命令。

注意 :确保 /etc/apt/keyrings/ 目录的权限正确,避免后续APT更新时出现权限警告:

sudo chmod a+r /etc/apt/keyrings/docker.gpg

4. 添加Docker的APT软件源

这是决定你从哪个服务器下载Docker安装包的关键步骤。我们需要根据系统版本,构造出正确的仓库地址字符串。

4.1 确定你的Ubuntu版本代号

使用 lsb_release 命令可以轻松获取:

lsb_release -cs

常见的输出结果有: jammy (22.04 LTS), focal (20.04 LTS), bionic (18.04 LTS)等。请记下这个代号,下面会用到。

4.2 添加仓库地址到APT源列表

我们需要将仓库地址写入 /etc/apt/sources.list.d/docker.list 文件。这里给出两种源的写法。

官方源地址:

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

让我们拆解一下这个命令:

  • deb : 表示这是一个二进制软件仓库。
  • [arch=$(dpkg --print-architecture) signed-by=...] : 这是APT仓库的选项。
    • arch=$(dpkg --print-architecture) : 自动获取当前系统的架构(如amd64, arm64),确保下载对应架构的包。
    • signed-by=/etc/apt/keyrings/docker.gpg : 指定用于验证这个仓库的GPG密钥文件路径。
  • https://download.docker.com/linux/ubuntu : Docker官方APT仓库的基地址。
  • $(lsb_release -cs) : 这里会替换成你系统的版本代号(如 jammy )。
  • stable : 使用稳定版仓库。Docker还提供 test (测试版)和 nightly (每日构建版)仓库,生产环境务必使用 stable

国内镜像源地址(以阿里云为例): 国内镜像站通常提供了对 download.docker.com 的镜像。以阿里云镜像站为例,其Docker CE镜像地址为: https://mirrors.aliyun.com/docker-ce/linux/ubuntu

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

可以看到,唯一的变化就是把仓库基地址从 https://download.docker.com/linux/ubuntu 换成了 https://mirrors.aliyun.com/docker-ce/linux/ubuntu

实操心得 :除了阿里云,腾讯云( mirrors.cloud.tencent.com/docker-ce )、中科大( mirrors.ustc.edu.cn/docker-ce )也都是很好的选择。选择离你网络地理位置近的镜像站,速度会更快。如果某个镜像站同步不及时,可以尝试换另一个。

4.3 更新APT软件包缓存

添加新仓库后,必须更新本地的软件包列表缓存,这样APT才知道从哪里、有哪些版本的软件可以安装。

sudo apt-get update

执行这个命令后,你应该能在输出信息中看到类似于“Get:XX https://mirrors.aliyun.com/docker-ce/linux/ubuntu jammy InRelease”这样的行,这说明新添加的Docker仓库已经成功被拉取。

5. 查询与安装指定版本Docker CE

仓库配置好,我们就可以开始寻找和安装特定版本的Docker了。

5.1 查询所有可安装的Docker CE版本

使用 apt-cache madison 命令可以列出仓库中某个包的所有可用版本。

apt-cache madison docker-ce | head -20

这条命令会列出 docker-ce 包的所有版本,格式如下:

docker-ce | 5:20.10.23~3-0~ubuntu-jammy | https://download.docker.com/linux/ubuntu jammy/stable amd64 Packages
docker-ce | 5:20.10.22~3-0~ubuntu-jammy | https://download.docker.com/linux/ubuntu jammy/stable amd64 Packages
docker-ce | 5:20.10.21~3-0~ubuntu-jammy | https://download.docker.com/linux/ubuntu jammy/stable amd64 Packages
...

输出分为三列:包名、版本字符串、所在的仓库。 我们需要关注的是第二列的完整版本字符串 ,例如 5:20.10.23~3-0~ubuntu-jammy 。这个字符串的格式通常是 epoch:upstream_version~debian_revision 。在安装时,我们需要指定这个完整的字符串,或者至少是 epoch:upstream_version 部分(如 5:20.10.23 )。

如果你想更精确地查找,比如找所有20.10.x的版本,可以用 grep 过滤:

apt-cache madison docker-ce | grep “20.10”

5.2 安装指定版本

假设我们通过查询,决定安装版本字符串为 5:20.10.23~3-0~ubuntu-jammy 的Docker CE。安装命令如下:

sudo apt-get install docker-ce=5:20.10.23~3-0~ubuntu-jammy docker-ce-cli=5:20.10.23~3-0~ubuntu-jammy containerd.io docker-compose-plugin

这里我们安装了四个包:

  1. docker-ce : Docker社区版引擎,核心。
  2. docker-ce-cli : Docker命令行工具。
  3. containerd.io : 容器运行时,Docker依赖它来管理容器生命周期。
  4. docker-compose-plugin : Docker Compose的插件版(V2),用于通过 docker compose 命令管理多容器应用。

为什么要把版本号写两遍? 因为 docker-ce docker-ce-cli 是两个独立的包,但为了兼容性,它们最好保持版本一致。 containerd.io docker-compose-plugin 通常不需要指定版本,APT会自动安装与 docker-ce 兼容的版本。

重要提示 :在安装过程中,APT可能会提示你,安装这个指定版本会导致不安装 docker-ce 的“候选版本”(即最新版)。这是正常的,直接确认即可。

5.3 安装后的基础验证

安装完成后,运行以下命令进行验证:

# 启动Docker服务,并设置为开机自启
sudo systemctl start docker
sudo systemctl enable docker

# 运行经典的hello-world镜像,测试Docker引擎是否正常工作
sudo docker run hello-world

如果看到“Hello from Docker!”等欢迎信息,说明Docker已经安装成功并且可以正常拉取和运行容器了。

6. 国内环境优化与配置

对于国内用户,安装完成只是第一步,后续的日常使用(尤其是拉取镜像)同样可能受网络问题困扰。进行一些优化配置可以极大提升体验。

6.1 配置Docker镜像加速器

Docker拉取镜像默认从Docker Hub( registry-1.docker.io )进行,国内访问速度很慢。我们可以配置一个国内的镜像加速器地址。

修改或创建Docker的守护进程配置文件 /etc/docker/daemon.json

sudo tee /etc/docker/daemon.json <<-‘EOF’
{
  “registry-mirrors”: [
    “https://registry.docker-cn.com“,
    “https://hub-mirror.c.163.com“,
    “https://mirror.baidubce.com“
  ]
}
EOF

这里我一次性添加了三个常用的国内镜像源。Docker会按顺序尝试。你可以根据网络情况选择其中一个或多个。

6.2 应用配置并重启服务

修改配置后,需要重启Docker服务使配置生效。

sudo systemctl daemon-reload
sudo systemctl restart docker

重启后,可以使用 docker info 命令查看配置是否生效,在输出中寻找 Registry Mirrors 部分。

6.3 为非root用户授权(可选但推荐)

默认情况下,执行 docker 命令需要 sudo 权限。为了方便,可以将当前用户加入 docker 用户组,这样就不用每次都加 sudo 了。

sudo usermod -aG docker $USER

重要 :执行此命令后,你需要 完全退出当前终端会话,并重新登录 ,或者新开一个终端窗口,用户组的变更才会生效。之后,你就可以直接运行 docker ps 等命令了。

安全须知 :将用户加入 docker 组等同于授予其root权限,因为Docker可以操作主机系统。请仅在个人开发环境或你信任的单用户系统上这样做。在生产服务器上,应严格管理 docker 组的成员。

7. 版本管理与后续维护

安装指定版本后,你可能会关心如何升级、降级或锁定版本。

7.1 查看当前安装的Docker版本

docker --version
docker version # 显示更详细的客户端和服务器端版本信息

7.2 升级Docker到新版本

如果你想升级到仓库中可用的另一个指定版本,步骤和安装时一样:

  1. sudo apt-get update 更新列表。
  2. apt-cache madison docker-ce 查看可用的新版本。
  3. sudo apt-get install docker-ce=<新版本号> docker-ce-cli=<新版本号> 执行升级。

如果你想升级到 当前仓库中的最新稳定版 ,可以运行:

sudo apt-get update
sudo apt-get install --only-upgrade docker-ce docker-ce-cli containerd.io docker-compose-plugin

--only-upgrade 参数会确保只升级已安装的包,不会安装新的包。

7.3 阻止Docker自动升级(锁定版本)

在Ubuntu上,如果你使用 apt 进行系统常规升级( sudo apt upgrade ),Docker可能会被一起升级到新版本。如果你想锁定当前版本,防止意外升级,可以使用 apt-mark hold 命令:

sudo apt-mark hold docker-ce docker-ce-cli containerd.io docker-compose-plugin

这样,这些包的状态就会被标记为“保留”,在常规升级中会被跳过。如果需要解除锁定,使用 sudo apt-mark unhold <包名>

8. 常见问题与故障排查实录

在实际操作中,你可能会遇到一些典型问题。这里记录了我踩过的一些坑和解决方法。

8.1 添加仓库后 apt-get update 报错“GPG error”或“NO_PUBKEY”

问题现象 :执行 sudo apt-get update 时,出现类似“The following signatures couldn‘t be verified because the public key is not available: NO_PUBKEY XXXXXXXXXXXXXXXX”或“GPG error: ... The following signatures were invalid: ...”的错误。

原因分析 :这通常是因为APT没有正确信任我们添加的Docker仓库GPG密钥。可能是密钥下载不完整、格式不对,或者权限有问题。

解决方案

  1. 重新下载并安装密钥 :删除旧的密钥文件,重新执行信任密钥的步骤(见3.3节)。
    sudo rm /etc/apt/keyrings/docker.gpg
    # 然后重新运行curl下载密钥的命令
    
  2. 检查密钥文件权限 :确保密钥文件对所有用户可读。
    sudo chmod a+r /etc/apt/keyrings/docker.gpg
    
  3. 检查 sources.list 文件格式 :确保 /etc/apt/sources.list.d/docker.list 文件中 signed-by 路径指向正确的密钥文件位置。

8.2 安装时找不到指定的版本号

问题现象 :执行 sudo apt-get install docker-ce=<版本号> 时,提示“E: Version ‘...’ for ‘docker-ce’ was not found”。

原因分析

  1. 最常见的可能: 你输入的版本字符串不完整或格式有误 。必须使用 apt-cache madison 命令列出的完整版本字符串,或者至少包含epoch和上游版本号(如 5:20.10.23 )。
  2. 可能的原因: 没有成功更新APT缓存 。添加仓库后忘了运行 sudo apt-get update
  3. 较罕见的原因: 你使用的镜像源同步延迟 。国内镜像站可能比官方源晚几个小时同步。如果你要安装一个刚发布的最新版本,可以尝试换回官方源,或者等待几小时后再试。

解决方案

  1. 再次运行 sudo apt-get update
  2. 仔细核对 apt-cache madison docker-ce 命令的输出,复制粘贴完整的版本字符串。
  3. 如果确认版本字符串正确且已更新缓存,可以尝试暂时将仓库地址换回官方源,查询和安装,完成后再改回镜像源。

8.3 Docker服务启动失败

问题现象 :执行 sudo systemctl start docker 后失败,使用 sudo systemctl status docker 查看状态显示异常。

原因分析 :原因多样,需要查看日志。

sudo journalctl -xu docker

常见原因及解决

  1. 与现有容器运行时冲突 :例如,之前安装的旧版Docker或Podman的残留。彻底卸载旧版本并清理 /var/lib/docker /var/lib/containerd 目录(见3.1节警告)。
  2. 存储驱动问题 :特别是Ubuntu某些版本默认的文件系统(如ZFS)可能与Docker的默认存储驱动 overlay2 不兼容。可以在 /etc/docker/daemon.json 中尝试更换存储驱动(如对于ZFS用 “storage-driver”: “vfs” ,但性能较差),或者更推荐的是将Docker的数据目录 /var/lib/docker 挂载到一个支持 overlay2 的文件系统(如ext4)上。
  3. 内核模块缺失或版本过低 :Docker需要一些特定的内核模块,如 overlay br_netfilter 等。确保系统已更新到较新的内核,并加载了所需模块。可以尝试更新系统: sudo apt update && sudo apt upgrade ,然后重启。

8.4 配置镜像加速器后拉取镜像依然很慢

问题现象 :配置了 registry-mirrors ,但 docker pull 速度没有明显改善。

原因分析

  1. 镜像加速器地址失效或网络不佳 :有些公共镜像源可能不稳定或已停止服务。
  2. 配置未生效 :修改 daemon.json 后没有重启Docker服务。
  3. 拉取的镜像不在加速器缓存中 :对于非常冷门的镜像,加速器可能没有缓存,首次拉取仍需从Docker Hub获取,速度会慢。后续拉取会快。
  4. 使用了私有仓库或第三方仓库 :镜像加速器通常只对Docker Hub ( docker.io ) 生效。如果你拉取的是 quay.io gcr.io 或自建仓库的镜像,加速器配置是不起作用的。

解决方案

  1. 确认Docker服务已重启,并用 docker info 确认镜像加速器地址已列出。
  2. 尝试更换 registry-mirrors 数组中的地址顺序,或将其他可用的加速器地址加入列表。
  3. 对于非Docker Hub的镜像,可以考虑使用代理,或者寻找该仓库对应的国内镜像(如果有的话)。

8.5 用户加入docker组后仍需sudo

问题现象 :执行了 sudo usermod -aG docker $USER 并重新登录后,运行 docker ps 仍提示权限拒绝。

原因分析 :用户组变更没有在新的会话中生效。最常见的原因是 没有完全退出当前会话并重新登录 。仅仅关闭终端窗口的某个标签页,或者在同一终端里执行 su - $USER ,有时不足以重新加载完整的用户登录会话环境。

解决方案

  1. 最可靠的方法 :完全注销当前图形界面登录(或SSH连接),然后重新登录。
  2. 如果不想重新登录,可以尝试在当前终端中启动一个新的登录shell:
    newgrp docker
    
    这个命令会为当前shell会话临时切换到 docker 组。但这只对当前终端有效。要永久生效,还是需要重新登录。

锁定版本这个习惯,是在经历了几次深夜紧急回滚后养成的。有一次,自动升级将Docker从20.10升级到了23.0,导致我们一个基于特定API版本编写的容器健康检查脚本全部失效,虽然问题不大,但批量修复也花了些时间。从那以后,非必要不升级,要升级也先在测试环境走一遍全流程。对于国内网络环境,镜像加速器是必选项,它能将镜像拉取时间从几十分钟缩短到几十秒,体验提升巨大。最后, /etc/docker/daemon.json 这个文件值得你多花点时间研究,除了镜像加速,日志驱动、存储驱动、默认网络等很多重要配置都在这里,根据你的实际需求调优一下,能让Docker跑得更顺畅。

更多推荐