1. 装Docker之前,先把环境里的“旧版本”清干净
最近在帮一台 Ubuntu 22.04 的机器配置 Docker,顺手把国内镜像加速也做了。这台机器是半路接手的,里面曾经装过 Ubuntu 自带源里的 docker.io,后来卸载得又不干净,导致我这次配置时踩了好几个隐藏的坑。这篇笔记按我实际操作顺序来写:为什么先清理环境、怎么安装 Docker、如何配置国内镜像加速、如何验证加速真的生效,以及几个最容易让 Docker “罢工”的故障现场。
如果你刚接触 Linux,或者之前只在 Windows/Mac 上用过 Docker Desktop,这篇笔记可以直接照抄;如果你已经装过 Docker 但拉镜像一直很慢,重点看第 4 章的 daemon.json 配置和第 5 章的验证方法。
先说一个反直觉的事:在 Ubuntu 22.04 上,直接执行 sudo apt install docker.io 表面上看最省事,实际后患很大。Ubuntu 仓库里的 docker.io 版本通常落后 Docker 官方版本好几个大版本,而且它自带的 containerd、runc 等组件版本也偏旧。如果你只是跑一两个简单容器,可能感觉不到差异;但一旦用到 Docker Compose 新语法、BuildKit 新特性,或者需要和 CI/CD 平台对齐版本,旧版本的兼容性问题就会接二连三地冒出来。
更麻烦的是,如果机器上之前用 apt 装过 docker.io,再切到 Docker 官方源安装时,两个包可能会产生文件冲突。旧版残留的 /etc/default/docker、/var/lib/docker 里的老数据,也会让新版 Docker 启动时一脸茫然。所以我的建议永远是:不要急着执行安装命令,先花两分钟确认机器当前状态。
清理命令如下:
bash复制# 查看是否已安装过 Docker 相关包
dpkg -l | grep -i docker
# 如果有输出,先卸载:docker.io / docker-ce / docker-engine 等等
sudo apt remove --purge docker.io docker-ce docker-ce-cli containerd runc -y
注意,这里 --purge 会把软件的配置文件也删掉。第一次执行时可能会提示某些包不存在,这很正常,因为每台机器装过的版本组合不一样。删完包之后,还需要手动清理几个残留目录:
bash复制sudo rm -rf /var/lib/docker
sudo rm -rf /var/lib/containerd
sudo rm -rf /etc/docker
sudo rm -rf /etc/apt/sources.list.d/docker.list
sudo rm -rf /usr/share/keyrings/docker-archive-keyring.gpg
这里我必须加一句重要提醒:如果你之前用 Docker 跑过数据库这类有持久化数据的容器,别急着 rm -rf /var/lib/docker。这个目录里保存着所有容器、镜像、数据卷,一旦删除就彻底找不回来了。正确做法是先 sudo docker ps -a 查看现有容器,把需要保留的数据用 docker cp 拷贝出来,或者直接备份整个 /var/lib/docker 目录到移动硬盘。我见过不止一次“因为清理残留把数据库一起带走”的事故,宁可多花几分钟备份,也别赌手速。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 换好 apt 镜像源再做系统更新:这一步很多人顺序反了
清理完旧版本后,接下来第一步其实是确认 Ubuntu 22.04 的 apt 源本身可用。这一步经常被忽略,因为用户总想着“先装 Docker”,结果 apt update 报出一堆“无法连接”或“404 Not Found”的错误,还会反过来怀疑 Docker 源配错了。其实问题出在系统源上。
国内服务器如果还在用 Ubuntu 默认的 archive.ubuntu.com 和 security.ubuntu.com,更新速度通常非常慢,有时候一个 apt update 都要卡好几分钟。所以在配置 Docker 源之前,我会先顺手把系统的 apt 源换成国内镜像。
这里注意一个细节:换源操作只影响后续的 apt update/install/upgrade,不会对 Docker 镜像拉取起任何加速作用。Docker 镜像拉取走的是 Docker Hub 或你配置的 registry mirror,跟 apt 仓库是两条独立通道。很多人以为“系统源换了,Docker 拉镜像也快了”,这是完全错误的理解。
Ubuntu 22.04 的代号是 jammy,所以源的格式长这样,以下以阿里源为例:
bash复制# 先备份原始源配置,这是一个好习惯
sudo cp /etc/apt/sources.list /etc/apt/sources.list.bak
# 把 jammy 相关源写入
sudo tee /etc/apt/sources.list > /dev/null <<'EOF'
deb http://mirrors.aliyun.com/ubuntu/ jammy main restricted universe multiverse
deb http://mirrors.aliyun.com/ubuntu/ jammy-security main restricted universe multiverse
deb http://mirrors.aliyun.com/ubuntu/ jammy-updates main restricted universe multiverse
deb http://mirrors.aliyun.com/ubuntu/ jammy-backports main restricted universe multiverse
EOF
sudo apt update
如果你用的是腾讯云服务器,可以换成 mirrors.cloud.tencent.com;如果是华为云,用 mirrors.huaweicloud.com。域名写成 mirrors.aliyun.com 也能覆盖大部分场景,因为阿里源在国内的连接速度和稳定性都不错。
做完这些之后,顺手安装基础工具,后面会用到:
bash复制sudo apt update
sudo apt install -y ca-certificates curl gnupg lsb-release apt-transport-https
这里面 ca-certificates 和 gnupg 是为了验证 Docker 官方 GPG Key,curl 用来下载密钥文件,lsb-release 用来获取系统版本代号。不要嫌它们多余,缺了任何一样,后续添加 Docker 源时都会卡在奇怪的报错上。
提示:如果是在新装好的 Ubuntu 22.04 上操作,且网络情况正常,也可以跳过系统源替换这一步直接进入 Docker 安装。但如果
apt update明显很慢,或者 pingarchive.ubuntu.com的延迟高得离谱,就老老实实先换源,否则后面的安装体验会非常难受。
3. 从 Docker 官方源安装 docker-ce:完整步骤与两个易错细节
3.1 添加 GPG Key 时的“apt-key 警告”陷阱
这是我每次重装 Docker 都会重点提醒自己的地方。网络上大量旧教程会这样写:
bash复制# 老教程的写法,新版系统不推荐
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add -
这条命令在 Ubuntu 22.04 上执行后,系统会弹出一行弃用警告:Warning: apt-key is deprecated. Manage keyring files in trusted.gpg.d instead。为什么弃用?因为 apt-key add 会把密钥加到系统全局信任区,意味着机器上任何配置了 apt 源的软件包都能用这个 Key 通过校验,权限边界非常模糊,安全性不理想。
现在的主流做法是把密钥文件单独保存,并通过源配置里的 signed-by 参数精确指定。这样 Docker 源只信任我们指定的那个 Key,逻辑清晰,排查也方便。
bash复制# 下载 GPG Key 到 keyrings 目录
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
这里解释一下几个细节:
--dearmor的作用是把 ASC 格式的密钥转为二进制 GPG 格式,apt 源的signed-by通常需要这种格式。- 文件放在
/etc/apt/keyrings/而不是自定义路径,是 Ubuntu 22.04 上的惯例;当然你也可以放到任意路径,只要源配置里写对即可。 chmod a+r是为了确保 apt 进程能读取密钥文件,权限不足时apt update会报错Key is stored in legacy trusted.gpg keyring或直接无法验证。
3.2 添加 Docker apt 源:download.docker.com 慢怎么办
Docker 官方 apt 源地址是:
bash复制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;$(lsb_release -cs) 输出 jammy。如果你服务器架构不是 x86_64,这里会自动适配,不用手动改,省心很多。
但问题来了:download.docker.com 虽然在国内大部分地区可以访问,速度却时快时慢,尤其在晚高峰执行 apt update 时经常卡在“等待下载”状态。遇到这种情况,不用死磕官方源,直接把 apt 源替换成清华或阿里维护的 Docker CE 镜像源即可。
以下是清华源的写法:
bash复制echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://mirrors.tuna.tsinghua.edu.cn/docker-ce/linux/ubuntu $(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
这里有一个很容易踩的坑:如果你前面用 curl https://download.docker.com/.../gpg 下载密钥,之后又把 apt 源换成了清华的地址,GPG Key 本身并不受域名影响,密钥和软件源两者是解耦的。任何人都可以从 Docker 官方下载 Key,然后把 Key 用于自己的镜像源。不过真正稳妥的方式是,Key 的下载地址和源地址保持一致。比如用清华源的话,密钥也可以从清华镜像站取:
bash复制curl -fsSL https://mirrors.tuna.tsinghua.edu.cn/docker-ce/linux/ubuntu/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg
这种“源和 Key 都在同一家镜像站”的做法,可以避免不同源之间同步延迟导致的签名验证失败。
配置完成后执行:
bash复制sudo apt update
如果输出末尾能看到 Get:... https://mirrors.tuna.tsinghua.edu.cn/docker-ce/linux/ubuntu jammy/stable amd64 Packages 之类的信息,说明 Docker 源添加成功。
3.3 选择具体版本并安装
Docker 官方现在把组件拆得很细,我建议直接安装这四件套:docker-ce、docker-ce-cli、containerd.io,以及 docker-compose-plugin。其中 docker-compose-plugin 提供的是 Docker Compose v2 插件,对应命令是 docker compose,注意中间有空格,不是老版本的 docker-compose 连字符命令。
先看一下源里有哪些版本可选:
bash复制apt-cache madison docker-ce
输出类似:
bash复制docker-ce | 5:26.1.3-1~ubuntu.22.04~jammy | https://download.docker.com/linux/ubuntu jammy/stable amd64 Packages
指定版本安装的好处是可控。生产环境我不建议直接装最新版,因为新版本偶尔会引入存储驱动或网络栈的变动,最好采用“先测试、后生产”的方式。但作为学习笔记,直接装最新稳定版即可:
bash复制sudo apt install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
这里有个常见疑问:为什么 Docker 版本号是 5:26.x 而不是 26.x?前面那个 5: 是 Debian/Ubuntu 的 upstream 版本前缀,由打包者定义,不影响实际软件版本。只要看 26.x 即可判断 Docker 引擎版本。
安装完成后,先把服务跑起来并设置开机自启:
bash复制sudo systemctl enable --now docker
sudo systemctl status docker
正常情况下 status 会显示 active (running)。如果这里看到 dead 或 failed,先不要急着往下走,用 journalctl -u docker 查看详细日志。绝大多数情况是 daemon.json 配置错误或 containerd 版本冲突,后面第 6 章我会详细展开。
3.4 把当前用户加入 docker 组:方便但注意安全边界
安装完 Docker 后,每次执行 docker 命令都要加 sudo,非常啰嗦。标准做法是把用户加入 docker 用户组:
bash复制sudo usermod -aG docker $USER
newgrp docker
我建议在真实操作时,不要只执行一次 usermod 就以为万事大吉。usermod -aG 修改的是用户组配置,但如果你的 SSH 会话是当前机器上开的,需要重新登录一次,组权限才会真正生效。执行 newgrp docker 可以临时切换当前会话的组身份,但新开的终端窗口不一定继承,所以最稳妥的还是 exit 后重新登录,然后用以下命令验证:
bash复制docker version
当输出中能看到:
bash复制Client:
Cloud integration: v1.0.35
Version: 26.1.3
以及 Server 段不是 ERROR: permission denied,就说明组权限已生效。
这里必须把安全问题说透:把用户加入 docker 组,相当于给了这个用户 root 级别的能力。因为 Docker 的容器技术本质上就是与内核交互,攻击者一旦能控制 Docker 守护进程,就能通过挂载宿主目录等方式拿到机器权限。所以:
- 只把可信的管理员账号加入 docker 组;
- 不要把普通开发机的每个用户都加进去;
- 如果机器是多租户共用,建议保持使用 sudo 的习惯。
4. 国内镜像加速配置:daemon.json 是全部重点
4.1 为什么拉镜像会慢到怀疑人生
Docker 默认从 Docker Hub(hub.docker.com)拉取镜像。Docker Hub 的服务器主要部署在海外和部分海外边缘节点,国内网络访问时经常出现丢包、限速、TLS 握手超时等问题。表现就是 docker pull 卡在:
bash复制Waiting 31.0s (retry after 1s)
或者进度条长时间停在某个百分比不动。
国内镜像加速配置的原理,是让 Docker daemon 在拉取官方镜像时,把请求转发到你配置的 registry mirror 节点。这些节点一般由国内云厂商或高校提供,服务器在国内,与 Docker Hub 之间有专用或高带宽通道,会把镜像缓存到本地,相当于在国内给你做了个代理缓存。
我这样解释可能更直白:Docker Hub 像是海外的一个大型仓库,国内镜像加速节点相当于你在国内小区门口开了一个同一家仓库的分仓。你下单时不用跨国去取货,直接在分仓拿,速度快很多。但如果分仓里没有你想要的镜像,它背后的系统还是会去海外仓库同步,所以第一次拉取某些冷门镜像时依然可能慢。
4.2 可用加速地址和选择建议
网上流传的加速地址更新频率很高,今天我写下来的时候还能用,不代表三个月后依然有效。所以更推荐你掌握方法而不是死记地址。
以下是我近期实测可用性较好的几个公共地址:
| 加速地址 | 说明 | 备注 |
|---|---|---|
https://docker.m.daocloud.io |
DaoCloud 公共镜像加速 | 目前连通性不错,适合拉取 Docker Hub 镜像 |
https://docker.nju.edu.cn |
南京大学镜像站 | 高校站,稳定性看出口带宽 |
https://mirrors.aliyun.com/docker-ce/ |
阿里云镜像 | 注意这不是 registry mirror,而是 Docker 安装源的地址,不要混淆 |
这里特别提醒一下:阿里云同时提供两类服务,一类是 Docker 安装的 apt 源(mirrors.aliyun.com/docker-ce),另一类是容器镜像加速器。后者需要登录阿里云容器镜像服务控制台,每个账号分配一个专属地址,形如 https://xxxxxxxx.mirror.aliyuncs.com。很多人抄教程时把两个地址搞混,结果改了之后拉镜像一点反应都没有。
此外还有一些高校或社区提供的地址,比如 docker.mirrors.sjtug.sjtu.edu.cn(上海交大),但因为负载和合规原因经常需要内网或教育网访问,公共场景下就不作为首选了。
4.3 daemon.json 文件写法与 JSON 格式坑
Docker daemon 的配置文件是 /etc/docker/daemon.json。如果系统里还没有这个文件,直接创建即可:
bash复制sudo mkdir -p /etc/docker
sudo tee /etc/docker/daemon.json > /dev/null <<'EOF'
{
"registry-mirrors": [
"https://docker.m.daocloud.io",
"https://docker.nju.edu.cn"
]
}
EOF
配置完成后,重启 Docker:
bash复制sudo systemctl daemon-reload
sudo systemctl restart docker
重启完成别急着拉镜像,先看配置有没有被正确加载:
bash复制docker info
在输出中找到这一段:
bash复制 Registry Mirrors:
https://docker.m.daocloud.io/
https://docker.nju.edu.cn/
看到 Registry Mirrors 非空,说明 daemon 已经读到了配置。
JSON 文件最坑的一点是:它不允许注释,不允许末尾多逗号,字段名必须用双引号。我第一次配置时手一抖在最后一个镜像地址后面加了个逗号:
json复制{
"registry-mirrors": [
"https://docker.m.daocloud.io",
"https://docker.nju.edu.cn",
]
}
然后 sudo systemctl restart docker 直接报 Job for docker.service failed。为什么会这样?因为 daemon.json 解析失败时,Docker 守护进程拒绝启动,而 systemd 会因为服务启动超时或退出而报 red。遇到这种情况,不要慌,按这个顺序排查:
bash复制# 第一步:看 Docker 服务状态
sudo systemctl status docker
# 第二步:看具体日志
sudo journalctl -u docker --no-pager | tail -n 30
# 第三步:验证 JSON 格式
python3 -m json.tool /etc/docker/daemon.json
如果输出:
bash复制Expecting ',' delimiter: line 5 column 5 (char 90)
那就是 JSON 结尾多了一个逗号。把逗号删掉再重启即可。
我建议每次修改完 daemon.json,先执行一遍 python3 -m json.tool 做语法校验,再重启 Docker,能省去很多无谓的排障时间。
4.4 镜像加速的边界:哪些镜像能加速,哪些不能
这里有个很容易误解的技术点:registry-mirrors 只对 Docker Hub 官方仓库生效。也就是说,你在 docker pull nginx、docker pull mysql:8.0 时会走加速节点;但如果要拉取 quay.io/prometheus/node-exporter、ghcr.io/... 这类第三方仓库的镜像,Docker daemon 不会把它们当 Docker Hub 镜像处理,依然直连原站,速度完全取决于你的网络到该站点的质量。
所以如果你在拉一个带仓库前缀的镜像(比如 docker pull gcr.io/...)时觉得慢,不要怀疑是 registry-mirrors 没配上,而是这些镜像根本不在加速范围内。对于 GitHub Container Registry,可以尝试用 ghcr.io 的前缀改写方式,但因为合规和稳定性原因我就不展开推荐了。
5. 用一次 MySQL 8.0 拉取实测,验证加速是否真正生效
5.1 拉镜像时看不到加速效果怎么办
配置完加速后,最直接感受差异的操作就是拉一个常用镜像。我习惯用 mysql:8.0 做测试,因为它体积大、层数多,比 hello-world 这种小镜像更能体现网络质量。
bash复制docker pull mysql:8.0
正常拉取时输出会按层显示,每层会有 Pulling fs layer、Waiting、Downloading 这样的状态。如果配置了有效加速,Downloading 的进度会很明显地快速前进;如果碰到卡住,先检查三个地方:
docker info中的 Registry Mirrors 是否真的配置上了;- 加速地址当前是否可访问:
curl -I https://docker.m.daocloud.io/v2/; - Docker daemon 日志有没有报 TLS 或连接错误。
第 2 步里很多人会忽略,因为配置成功不代表加速地址真的能用。公共加速地址偶尔会故障或对特定运营商线路不友好,如果你的服务器在某个云厂商内网,可能 ping 不通某些高校节点。这时候换一个加速地址再重启 Docker 即可。
5.2 跑一个 MySQL 容器并检查数据卷
镜像拉取完成后,跑起来验证一下整个链路:
bash复制docker run -d \
--name mysql8 \
-p 3306:3306 \
-e MYSQL_ROOT_PASSWORD=YourPassword123 \
-v /data/mysql:/var/lib/mysql \
mysql:8.0 \
--character-set-server=utf8mb4 \
--collation-server=utf8mb4_unicode_ci
解释一下这段命令的关键参数:
-d:后台运行容器;--name mysql8:给容器起名字;-p 3306:3306:把宿主机的 3306 端口映射到容器的 3306;宿主机端口可以改成别的,比如-p 33060:3306,但客户端连接时也要相应调整;-e MYSQL_ROOT_PASSWORD=...:MySQL 容器首次启动时会根据这个环境变量初始化 root 密码;-v /data/mysql:/var/lib/mysql:数据卷挂载。MySQL 的数据实际写在容器/var/lib/mysql里,如果不挂载到宿主机,容器删除后数据就全部消失了;- 最后的
--character-set-server=utf8mb4是 MySQL 服务端参数,用来指定默认字符集。
启动后执行:
bash复制docker ps
docker logs mysql8
docker exec -it mysql8 mysql -uroot -p
看到 mysql> 提示符说明容器正常。然后创建一个测试库:
sql复制CREATE DATABASE testdb DEFAULT CHARACTER SET utf8mb4;
SHOW DATABASES;
再用宿主机检查数据卷是否真正写入了文件:
bash复制sudo ls -lh /data/mysql
如果能看到 ibdata1、#innodb_redo 之类的文件,说明数据卷挂载成功。这一步对后面理解容器的数据生命周期很有帮助。
5.3 顺手延伸:Redis、Nginx 等常见镜像的实际使用姿势
同样配置下,拉取其他 Docker Hub 镜像速度都会改善。我最近一次测试,未配置加速时拉 redis:7.2 耗时接近 20 分钟且中途失败,配置 DaoCloud 镜像后大约 30 秒就完成了全部层的下载。
Redis 容器启动方式和 MySQL 类似:
bash复制docker run -d --name redis7 -p 6379:6379 -v /data/redis:/data redis:7.2 --appendonly yes
这里 --appendonly yes 是开启 AOF 持久化,开启后数据会写入容器内 /data,通过数据卷挂载到宿主机。不用 --appendonly 的 Redis 容器重启后内存数据直接清空,新手经常在这上面吃亏。
Nginx 更简单:
bash复制docker run -d --name nginx -p 8080:80 nginx:latest
然后浏览器访问 http://服务器IP:8080,能看到欢迎页就说明容器运行正常。
6. 那些让 Docker “罢工”的现场:常见故障复盘与快速恢复
6.1 daemon.json 非法内容导致服务启动失败
前面 4.3 节提到过 JSON 末尾多逗号的问题,那是非常典型的一类故障。这类故障最气人的地方在于:你不是命令敲错,系统也没有明显报错,只是 Docker 从那一刻开始拒绝启动。如果对 systemd 不太熟,很容易进入“反复 restart、反复失败”的死循环。
一个更隐蔽的坑是有人会在 daemon.json 里加注释:
json复制{
// 这里是镜像加速配置
"registry-mirrors": ["https://docker.m.daocloud.io"]
}
JSON 标准不允许 // 注释,哪怕你写的是合法注释内容,json.tool 一校验也会报错。Docker daemon 用的是标准 JSON 解析器,不吃注释,所有说明性文字请放在文件之外,或者使用 README 方式单独记录。
6.2 拉取时报 TLS 或 timeout:先排除网络,再怀疑源
镜像拉取时的报错多种多样,最常见的是:
bash复制Error response from daemon: Get "https://registry-1.docker.io/v2/": net/http: request canceled while waiting for connection (Client.Timeout exceeded while awaiting headers)
这个报错的意思直白地说就是:Docker daemon 访问 Docker Hub 时连接超时,连 HTTP 响应头都没等到。这时先查加速地址:
bash复制docker info --format '{{json .RegistryMirrors}}'
curl -I https://docker.m.daocloud.io/v2/
如果 curl 返回类似 200 OK,说明加速节点可达。之后检查是镜像本身不存在还是拉取名单问题:对于官方镜像,先确认标签是否存在,比如 docker pull mysql:8.0.99 这种不存在的版本,会在 Docker Hub 返回 manifest unknown。有些教程和网文会复制粘贴不存在的镜像标签,导致每次拉取都失败,而你的加速配置和网络其实完全没问题。
6.3 磁盘空间满:overlay2 目录悄然膨胀
容器运行一段时间后会遇到一个很实际的问题:磁盘满了。docker system df 可以查看占用的镜像、容器、数据卷、构建缓存分别多大:
bash复制docker system df
输出中:
bash复制TYPE TOTAL ACTIVE SIZE RECLAIMABLE
Images 25 6 12.34GB 8.2GB (66%)
Containers 4 2 1.2GB 0B (0%)
Local Volumes 3 2 4.5GB 512MB (11%)
Build Cache 18 0 5.6GB 5.6GB (100%)
Build Cache 那一项很容易被忽略,而且它会占据大量磁盘空间。清理方式:
bash复制# 清除悬空镜像、停止的容器、无用网络
docker system prune
# 清除 Build Cache
docker builder prune
# 更激进:把没在运行的容器、悬空镜像、缓存全清理
docker system prune -a
prune 命令很好用,但使用 -a 要非常谨慎,它会把所有未被运行容器使用的镜像全部删掉。下次拉取时可能又要等半天。我通常只在磁盘告急时执行 docker system prune,再加一句“构建缓存能清就清,镜像不到万不得已不要全清”。
6.4 systemd 开机自启失败的排查顺序
Docker 服务设置自启后,如果服务器重启发现 Docker 没起来,按这个顺序排查:
bash复制# 1. 确认服务有没有 enable
sudo systemctl is-enabled docker
# 2. 手动启动看报错
sudo systemctl start docker
# 3. 如果启动失败,查看最近 50 行日志
sudo journalctl -u docker --no-pager -n 50
注意排查顺序很重要,因为手动执行 systemctl start 能帮你判断是配置问题还是资源不足。如果你是 cloud-init 自动初始化服务器,有时镜像的 systemd 处于 masked 状态,表现为执行 enable 时提示:
bash复制Failed to enable unit: Unit file docker.service is masked.
原因通常是镜像已经预装了另一个容器运行时,把 Docker 服务 mask 掉了。执行:
bash复制sudo systemctl unmask docker
sudo systemctl enable --now docker
即可恢复。这类问题在云厂商提供的所谓“Docker 预装镜像”里尤其常见,别问我怎么知道的。
7. Docker 卸载重装的完整操作与数据保全方法
最后把卸载重装的方法完整写出来,因为“装错了想重来”这件事一定会发生。只要按正确步骤做,可以在不丢数据的情况下把 Docker 环境恢复干净。
首先,备份正在运行的容器数据。如果是数据库容器,最稳妥的方式是使用数据库自带的导出工具,而不是简单复制数据卷文件。以 MySQL 为例:
bash复制docker exec mysql8 sh -c 'exec mysqldump -uroot -p"$MYSQL_ROOT_PASSWORD" --all-databases' > backup.sql
导出后,再备份全部数据卷目录或 /var/lib/docker/volumes 目录。这个过程我在前面已经反复强调,不做展开。
然后卸载:
bash复制sudo systemctl stop docker
sudo apt purge -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin docker-ce-rootless-extras
docker-ce-rootless-extras 可能没有安装,如果 apt 提示“Package not installed”,不用管,继续往下执行。随后删除所有残留目录:
bash复制sudo rm -rf /var/lib/docker
sudo rm -rf /var/lib/containerd
sudo rm -rf /etc/docker
sudo rm -rf /etc/apt/sources.list.d/docker.list
sudo rm -rf /etc/apt/keyrings/docker.gpg
这里如果你之前备份过数据,删除 /var/lib/docker 就没有心理负担;如果没有备份,那就先恢复备份目录,或者直接把 /var/lib/docker 改名为 /var/lib/docker.bak 再做卸载,这样哪怕忘了备份也有后悔药。
重装时回到第 3 章继续执行即可。整体上我个人的体会是:Ubuntu 22.04 上安装 Docker 本身并不难,真正的难点集中在“旧环境清理、apt 源选择、daemon.json 格式、镜像加速地址可用性”这几个地方。每次到一个新环境配置 Docker,我都习惯把这几项当作固定检查清单过一遍,到现在已经很少遇到安装后才知道失败的状况了。最后再说一个小技巧:配置完镜像加速后不要急着大量拉镜像,先用一个小体积镜像比如 docker pull busybox:latest 验证,如果它正常,说明通道已经通了。等你有真实业务镜像再大规模拉取,那时候会更从容,也不会因为网络问题耽误正经事。
