Ubuntu Docker 安装、镜像加速与配置排障

本文记录 Ubuntu 上安装 Docker Engine、配置 Docker Hub 镜像加速,以及排查 daemon.json 未按预期生效的方法。

先说明结论:排障时应当区分已经验证的事实和暂时的猜测。看到 dockerd 的启动参数中没有 --config-file,并不能说明 Docker 没有读取配置文件;普通 Linux 安装默认就会读取 /etc/docker/daemon.json。只有掌握日志、配置路径和实际运行方式后,才能判断问题出在哪里。

一、通过 Docker 官方软件源安装

以下步骤适用于 Docker 官方支持的 Ubuntu 版本。安装前应先确认系统版本、CPU 架构和防火墙策略符合官方要求。

1. 移除可能冲突的软件包

Ubuntu 软件源中的 docker.io、旧版 Compose、podman-docker,以及单独安装的 containerdrunc,可能与 Docker 官方软件包冲突。

1
2
3
sudo apt remove $(dpkg --get-selections \
docker.io docker-compose docker-compose-v2 docker-doc \
podman-docker containerd runc | cut -f1)

如果系统提示没有安装这些软件包,可以继续下一步。该命令不会自动删除已有的 Docker 镜像、容器和数据卷。

2. 添加 Docker 官方 GPG 密钥与软件源

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
sudo apt update
sudo apt install ca-certificates curl

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

sudo tee /etc/apt/sources.list.d/docker.sources <<EOF
Types: deb
URIs: https://download.docker.com/linux/ubuntu
Suites: $(. /etc/os-release && echo "${UBUNTU_CODENAME:-$VERSION_CODENAME}")
Components: stable
Architectures: $(dpkg --print-architecture)
Signed-By: /etc/apt/keyrings/docker.asc
EOF

sudo apt update

3. 安装并验证

1
2
3
4
5
sudo apt install docker-ce docker-ce-cli containerd.io \
docker-buildx-plugin docker-compose-plugin

sudo systemctl status docker
sudo docker run --rm hello-world

hello-world 能正常拉取并运行,才说明软件安装、守护进程和基础网络链路均可用。

二、配置 Docker Hub 镜像加速

当主机访问 Docker Hub 较慢或超时时,可以使用组织内部的缓存仓库,或自己有权使用的镜像服务。第三方镜像站的地址、可用性和使用规则可能变化,使用前需要以服务提供方的最新说明为准。

普通 Docker Engine 的默认配置文件为:

1
/etc/docker/daemon.json

创建或编辑该文件:

1
2
3
4
5
{
"registry-mirrors": [
"https://你的镜像服务地址"
]
}

修改后先校验 JSON 和 Docker 配置,再重启服务:

1
2
3
4
sudo dockerd --validate --config-file=/etc/docker/daemon.json
sudo systemctl restart docker
docker info
docker pull hello-world

只有在校验成功、docker info 显示了预期配置,并且实际拉取镜像成功后,才能认为配置已经生效。

三、一个需要纠正的判断

原排障记录曾根据下面的进程参数作出判断:

1
/usr/bin/dockerd -H fd:// --containerd=/run/containerd/containerd.sock

因为其中没有 --config-file /etc/docker/daemon.json,便认为 Docker 没有加载 daemon.json。这个判断不成立。

Docker Engine 在普通 Linux 环境中默认读取 /etc/docker/daemon.json--config-file 只是用于显式指定其他路径。换句话说,参数没有出现,不等于默认行为不存在。

此外,同一个配置项如果同时出现在启动参数和 daemon.json 中,Docker 可能因配置冲突而无法启动。因此,不应在没有确认原因时直接重写 ExecStart

四、daemon.json 不生效时的排查顺序

1. 确认运行方式和配置路径

不同运行方式使用的配置位置不同:

  • 普通 Linux Docker Engine:/etc/docker/daemon.json
  • Rootless 模式:~/.config/docker/daemon.json
  • 设置了 XDG_CONFIG_HOME 的 Rootless 模式:$XDG_CONFIG_HOME/docker/daemon.json
  • Docker Desktop:应通过 Docker Desktop 的设置界面修改 Docker Engine 配置

可以先检查当前上下文、进程和 systemd 服务:

1
2
3
4
5
docker context show
docker info
ps -ef | grep '[d]ockerd'
systemctl cat docker
systemctl show -p ExecStart docker

2. 校验配置文件

1
2
sudo cat /etc/docker/daemon.json
sudo dockerd --validate --config-file=/etc/docker/daemon.json

除了 JSON 语法,还要检查同一个选项是否同时出现在 ExecStart 参数和配置文件中。

3. 重启服务并立即查看日志

只修改 daemon.json 时,通常直接重启 Docker 即可:

1
2
3
sudo systemctl restart docker
sudo systemctl status docker --no-pager
sudo journalctl -xu docker.service --no-pager -n 100

systemctl daemon-reload 用于重新加载 systemd 单元或 drop-in 文件;仅修改 daemon.json 时,它不是关键步骤。

4. 验证配置和实际请求

1
2
docker info
docker pull hello-world

不要只依赖配置文件“看起来正确”。docker info 用于确认守护进程当前报告的配置,实际拉取则用于验证 DNS、TLS、代理、镜像服务和上游仓库是否共同可用。

5. 检查 DNS、TLS 和出口网络

1
2
getent hosts registry-1.docker.io
curl -v --connect-timeout 10 https://registry-1.docker.io/v2/

访问 /v2/ 返回 401 Unauthorized 并不一定是故障,它通常表示网络和 TLS 已经连通,只是当前请求没有携带认证信息。连接超时、DNS 解析失败和证书验证失败则需要分别排查。

6. 检查 Docker 守护进程的代理

终端里设置的代理不一定会自动传给由 systemd 启动的 Docker 守护进程。组织网络需要代理时,可以在 daemon.json 中配置:

1
2
3
4
5
6
7
{
"proxies": {
"http-proxy": "http://proxy.example.com:3128",
"https-proxy": "http://proxy.example.com:3128",
"no-proxy": "localhost,127.0.0.1,.example.com"
}
}

也可以通过 systemd drop-in 设置 HTTP_PROXYHTTPS_PROXYNO_PROXY。修改后要重启 Docker,并用下面的命令核对 systemd 实际加载的环境变量:

1
sudo systemctl show --property=Environment docker

五、什么时候才需要修改 systemd 启动命令

只有日志明确显示启动参数与 daemon.json 冲突,或者确实需要改变 Docker 的监听地址等启动行为时,才考虑创建 systemd drop-in。

例如,Ubuntu 的 systemd 单元可能通过 -H fd:// 指定监听方式;如果又在 daemon.json 中设置 hosts,相同配置会发生冲突。此时应根据日志和 Docker 官方文档消除重复项,而不是为了“强制读取配置”盲目加入 --config-file

修改 systemd 配置后才需要执行:

1
2
sudo systemctl daemon-reload
sudo systemctl restart docker

六、重装应当是最后手段

配置未生效不等于 Docker 安装已经损坏。重装之前,至少应完成以下检查:

  1. 确认安装的是 Docker Engine、Rootless Docker 还是 Docker Desktop。
  2. 确认修改了对应运行方式的配置文件。
  3. 使用 dockerd --validate 校验配置。
  4. 检查 journalctl 中的明确错误。
  5. 排除参数冲突、代理、DNS、TLS 和镜像服务自身故障。

如果已经确认软件包混装或系统状态确实无法恢复,可以卸载 Docker 软件包:

1
2
sudo apt purge docker-ce docker-ce-cli containerd.io \
docker-buildx-plugin docker-compose-plugin docker-ce-rootless-extras

卸载软件包不会自动删除 /var/lib/docker/var/lib/containerd 中的数据。下面的命令会永久删除本地镜像、容器、数据卷等内容,不能作为普通排障步骤:

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

只有在已完成必要备份、确认数据不再需要,并且确实要进行全新初始化时,才能执行删除。

七、wget 无法建立 SSL 连接

系统时间错误是证书校验失败的一种原因,但不是唯一原因。CA 根证书过期、代理或网关替换证书、DNS 异常、服务端 TLS 配置不兼容,也可能造成类似现象。

先检查时间和时区:

1
2
timedatectl status
date

需要时启用 NTP:

1
sudo timedatectl set-ntp true

如果时间正确,再检查证书包和详细连接日志:

1
2
3
4
sudo apt update
sudo apt install --reinstall ca-certificates
wget --debug https://目标地址
curl -v https://目标地址

不建议用 --no-check-certificate 作为长期解决方案,因为它绕过了证书身份校验,只会隐藏真正的问题。

八、总结

这次记录中最重要的经验不是“重装能解决问题”,而是排障结论必须由证据支撑:

  • 默认配置路径存在,不代表配置内容一定正确。
  • 启动参数中没有 --config-file,不代表 Docker 没有读取默认配置。
  • 服务成功重启,不代表镜像地址和网络链路一定可用。
  • 某次重装后问题消失,只能证明环境状态发生了变化,不能反推唯一根因。

按照“确认运行方式 → 校验配置 → 查看日志 → 验证网络 → 最后考虑重装”的顺序,更容易找到真正原因,也能减少不必要的数据风险。

参考资料