news 2026/9/18 23:29:41

Ubuntu 18.04 安装 Docker:官方 APT 源配置与排错实战

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Ubuntu 18.04 安装 Docker:官方 APT 源配置与排错实战

上周有个读者发来一段报错,说他在一台 Ubuntu 18.04 的机器上装 Docker,apt install docker.io装完之后docker info里一堆警告,跑docker run hello-world直接卡住不动。这类问题我这两年遇到过不下十次,几乎每一次的根因都不一样:有人是装到了发行版自带的老包里,有人是 systemd-resolved 和容器 DNS 打架,有人是 iptables 的 FORWARD 链被防火墙脚本清成了 DROP。Ubuntu 18.04(Bionic Beaver)这个版本很特殊——它已经过了标准维护期,但大量存量服务器、边缘设备、内网构建机还跑着它,而 Docker 官方早就停止为 bionic 构建新版本包了。这意味着在 18.04 上装 Docker,不能照抄官网首页那几行命令,得知道自己在装哪个版本、为什么装这个版本、装完之后哪些地方一定会出问题。

下面这篇是我自己反复装过几十台机器之后整理出来的完整流程,从"为什么不用 apt 里的 docker.io"一直讲到多架构镜像构建。不管你是第一次碰 Docker 的新手,还是只是想在老机器上补一套干净的环境,按这个顺序走一遍,基本不会再踩那些冤枉坑。

1. 为什么在 Ubuntu 18.04 上不该直接敲 apt install docker.io

1.1 Ubuntu 自带源的 docker.io 到底是什么东西

很多人装 Docker 的第一反应是sudo apt install docker.io,因为 Ubuntu 的官方源里确实有这么个包,而且它不用配任何第三方仓库,一条命令就完事。问题是这个包的性质和你想的不一样:它是 Debian/Ubuntu 社区从 Docker 上游源码重新打包的版本,跟着 Ubuntu 的发布节奏走,而不是跟着 Docker 的发布节奏走。Ubuntu 18.04 官方源里的 docker.io 大致停留在 19.03 甚至更早的补丁级别,之后基本只做安全补丁合并,不会升级功能版本。

这会带来三个具体后果。第一,docker命令的很多新参数用不了,比如docker buildxdocker scan、容器健康检查的部分语法,你在别人的教程里看到能跑,自己这里报"unknown flag"。第二,社区打包版本和上游在默认配置上有差异,比如默认存储驱动、默认日志驱动的取值可能不同,导致你照搬网上的 daemon.json 配置时出现奇怪的行为差异。第三,也是最麻烦的一点:当你后面想升级到上游新版本时,docker.iodocker-ce是两个不同的包,apt 会认为它们冲突或者共存,产生一堆依赖残留,清理起来非常恶心。

所以结论很直接:如果你想认真用 Docker,尤其是要跑生产容器或者做 CI 构建,别用发行版自带的包。这不代表 docker.io 一无是处,如果只是临时跑一个工具容器、对版本没有要求、也不想配任何外部源,那它确实最省事。但你得清楚自己在用什么。

1.2 四条安装路径的横向对比与选择依据

Ubuntu 上装 Docker 其实有四条路,我列个表把它们的差异摊开,你按场景对号入座。

安装路径版本新鲜度可控性适合场景主要缺点
Ubuntu 源 docker.io低,停在旧版本临时试用、内网无外网版本老、升级路径乱
Docker 官方 APT 仓库中高,bionic 有上限高,可锁版本服务器、生产环境需配源、需处理 GPG
官方 convenience script取到该仓库最新低,不好回滚一次性测试机不记录安装来源
snap 安装桌面体验与 apt 版并存会冲突

对绝大多数人来说,官方 APT 仓库是唯一合理的选择。它把安装来源、可用版本、依赖关系全部交给 apt 管理,你随时能apt-cache madison docker-ce看到有哪些版本,能精确指定装哪一个,后续升级和降级都在 apt 的框架里。官方那个get.docker.com脚本我也用过,图快是很爽,但它本质就是帮你做了配源和安装的动作,出问题时你连它到底改了哪些文件都不清楚,回滚成本比省下的时间高得多。

snap 版本要单独说一句,它在 Ubuntu 上是官方推荐的桌面软件分发方式,但 Docker 的 snap 包是严格 confinement 模式,对宿主机文件系统的访问受限,挂载卷的时候经常出权限问题。而且如果系统里同时存在 apt 装的 docker 和 snap 装的 docker,which docker的结果会随 PATH 顺序变化,调试起来能让人崩溃。桌面用户想用图形化,直接考虑 Docker Desktop,但注意 Docker Desktop for Linux 要求在 20.04 及以上,18.04 上根本装不了,这一点很多人不知道,白白折腾半天。18.04 只能用 Engine,没有别的选择。

1.3 动手之前先花两分钟确认三件事

在敲任何安装命令之前,有三项检查我建议你固定做一遍,能省掉后面大量的排查时间。

第一,确认系统版本和代号。lsb_release -a看是不是 Bionic Beaver,dpkg --print-architecture看是 amd64 还是 arm64。这两个信息决定了你后面 APT 源里写哪个代号、哪个架构字段,写错了 apt update 会直接报 404。第二,确认内核版本,uname -r。18.04 初始内核是 4.15,后续更新能到 5.4,overlay2 存储驱动需要 4.0 以上内核,4.15 完全够用,但如果你装过某些老旧的虚拟化环境内核只有 3.x,那就得考虑先升级内核或者退回 vfs/devicemapper,性能会差很多。第三,确认当前系统里有没有已经存在的容器运行时,dpkg -l | grep -Ei 'docker|containerd|runc'一条命令扫一遍,还会发现 snap 装的包也在这里露头。如果有残留,必须在安装前清干净,否则后面 apt 会告诉你 containerd.io 依赖冲突,而你完全看不懂冲突从哪来。

还有一个容易被忽略的点:/var/lib/docker默认在根分区。如果这台机器根分区只有 20G,你后面拉几个镜像就满了。安装前先df -h /看一眼,心里有数,必要时提前规划好数据目录迁移,这个后面第 4 章会讲。

2. 走官方 APT 仓库把 Docker Engine 装进系统

2.1 先把旧版本和冲突包清理干净

这一步看起来像例行公事,但它救过我很多次。命令本身很简单:

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

要注意的是,remove只删包不删数据,/var/lib/docker里的镜像、容器、卷都还在。如果你是有意做版本迁移,这是好事;如果是想彻底重装,那还得手动sudo rm -rf /var/lib/docker /var/lib/containerd。还有一个细节:这条 remove 命令在包不存在的时候会返回非零退出码,如果你把它写在脚本里并且开了set -e,脚本会直接中断。稳妥写法是加|| true

另外,如果你之前用 snap 装过 Docker,一定要单独处理:sudo snap remove docker。snap 的数据目录在/var/snap/docker,包删掉之后这个目录可能还在,里面塞着几十 G 的镜像层,占空间不说,还可能导致新装的 docker 因为 socket 路径冲突起不来。我遇到过一次,机器上/var/run/docker.sock被 snap 版本占着,apt 版本的 dockerd 启动时报 "address already in use",排查了半小时才发现是 snap 没清干净。

2.2 配源与 GPG 密钥的正确姿势

Ubuntu 18.04 的 apt 版本是 1.6.x,已经支持signed-by这种更规范的密钥引用方式。虽然apt-key add在这个版本上还能用,但它会把密钥加到全局信任库里,属于官方明确标记为废弃的做法,能不碰就不碰。我更推荐把密钥单独放在一个文件里,然后在源的配置中显式引用:

sudo apt-get update sudo apt-get install -y apt-transport-https ca-certificates curl gnupg lsb-release 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 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 sudo apt-get update

这里有几个踩过坑的细节。gpg --dearmor生成的必须是二进制格式的 keyring 文件,如果你直接用curl -o把 ASCII armored 的文本存进去,apt 更新时会报 "The following signatures couldn't be verified",这个报错信息不会告诉你真正原因是格式不对,只会说签名校验失败。chmod a+r那行也别省,apt 以_apt用户身份读取密钥文件,如果权限是 600 并且属主是 root,会报权限拒绝,同样是个很迷惑的报错。$(lsb_release -cs)在 18.04 上会展开为bionic,如果你嫌麻烦直接写死 bionic 也行,但如果哪天系统升级了,写死的代号就会导致源失效或者装到错误的包。

2.3 锁定版本安装,别一上来就装最新

配好源之后先别急着 install,先看看到底有哪些版本可选:

apt-cache madison docker-ce

Ubuntu 18.04 的情况比较特殊,Docker 官方虽然保留了 bionic 的仓库目录,但新版本已经不再为它构建,能拿到的稳定版本大致停在 20.10 这一代,具体以这条命令的实际输出为准。这其实不是坏事,20.10 是个成熟稳定的版本,配套的 containerd、runc 生态都很完善,比某些刚发布的新版本反而省心。

看好了版本号之后,精确安装:

sudo apt-get install -y \ docker-ce=5:20.10.24~3-0~ubuntu-bionic \ docker-ce-cli=5:20.10.24~3-0~ubuntu-bionic \ containerd.io

为什么一定要带上版本号?因为不带版本号的 install 会在每次apt upgrade时把 Docker 一起升级,而 Docker 的大版本升级经常会动默认配置、改 iptables 规则链名、调容器运行时接口,线上机器一个不经意的 apt upgrade 就可能把整套容器打挂。带版本号装完之后,再用 dpkg 的 hold 标记把它钉住,这部分第 5 章细说。至于containerd.io为什么不指定版本,因为它和 docker-ce 之间有依赖约束,apt 会自动挑一个兼容的版本,硬指定反而容易冲突。

2.4 装完之后的第一轮验证

装完立刻做三件事,别等到部署业务的时候才发现问题。

sudo systemctl status docker --no-pager docker version sudo docker info

systemctl status看的是服务有没有真的起来,输出里要有绿色的 active (running)。docker version会把 Client 和 Server 两段分开列出来,如果只显示 Client 而 Server 段报 "Cannot connect to the Docker daemon",说明 dockerd 没起来或者 socket 权限不对。docker info的信息量最大,重点看三行:Storage Driver 应该是 overlay2,Cgroup Driver 应该是 cgroupfs 或者 systemd(18.04 上默认 cgroupfs),Logging Driver 默认 json-file。如果 Storage Driver 显示的是 vfs,说明 overlay2 没被启用,性能会差好几倍,通常是因为/var/lib/docker所在的分区是某些特殊文件系统,比如 ZFS 或者某些网络存储,这种情况要么换分区要么接受性能损失。

3. 免 sudo 使用 docker:用户组背后的权限账

3.1 加组之后还要 sudo 是怎么回事

每次敲 docker 命令都要加 sudo,用不了多久就会烦。标准做法是把当前用户加进 docker 组:

sudo groupadd docker 2>/dev/null || true sudo usermod -aG docker $USER

关键点来了:执行完这两条,当前这个终端里 docker 命令依然需要 sudo。这不是没生效,而是因为用户的组成员关系是在登录时确定的,已经打开的 shell 会话里保存的是旧的组列表。很多人在这里以为自己操作失败了,反复执行 usermod,其实完全没必要。解决办法有三个:newgrp docker可以在当前 shell 里刷新组信息(会开一个子 shell),su - $USER重新登录一下,或者最省事的直接关掉终端重连。我最常用的是newgrp docker,但它有个副作用是当前目录不变而环境变量会重置,在特定场景下要留意。

加完组之后如果还是不行,可以用id命令确认一下 docker 组有没有出现在输出里,再用ls -l /var/run/docker.sock看 socket 文件的属主和权限,正常应该是root:docker且权限srw-rw----。如果 socket 的属组不是 docker,说明 dockerd 启动时的配置有问题,得去看 daemon.json 或者 systemd 的启动参数。

3.2 docker 组等于 root,这个账要算清楚

这里必须严肃说一下:把用户加进 docker 组,等价于给他 root 权限。原因是 docker 守护进程以 root 身份运行,任何能对 socket 发指令的人都可以让它以 root 身份跑一个容器,比如docker run -v /:/host --privileged ...就能拿到宿主机的完整文件系统。所以"加 docker 组"这个操作,在个人开发机上完全没问题,在多人共用的服务器上就要慎重了,尤其是那种多人登录的跳板机或者构建机。

我在实际项目里的做法是按机器角色区分。开发笔记本上,把自己加进 docker 组,享受便利。CI 构建机上,用专门的jenkins或者gitlab-runner账号跑容器,通过 socket 挂载或者 API 访问,不给真人账号开 docker 组。生产服务器上,除了运维之外不给任何人开 docker 组,业务进程如果需要访问容器能力,通过受控的接口或者专门的 agent 来做。这套划分听起来麻烦,但它把"谁能做危险操作"这件事变得清晰,出事故的时候责任边界也清楚。

3.3 rootless 模式在 18.04 上到底能不能用

既然 docker 组等于 root,那 rootless 模式自然是更安全的方案。Docker 的 rootless 模式允许以普通用户身份运行整个守护进程,靠的是 user namespace 做 UID 映射。它的原理是把容器里的 root 映射到宿主机上的普通用户 UID,这样即便容器里出现提权,攻击者实际拿到的也只是宿主机上一个没有特权的账号。

那 18.04 能不能跑 rootless?技术上可以,但有前提。需要内核开启 user namespace 支持,需要newuidmapnewgidmap这两个 setuid 工具(包名uidmap),还需要在/etc/subuid/etc/subgid里给用户分配一段可映射的 UID 范围。18.04 的内核 4.15 对这些的支持是完整的,但 rootless 模式在 18.04 上的体验明显不如新系统,主要痛点是网络性能有损耗(走 slirp4netns 或者 VPNKit 做用户态转发),以及低端口绑定、卷挂载权限这些问题会变多。

我的建议是:个人开发机、临时测试环境,用 docker 组就够了,简单直接;如果这台机器上有不受信任的代码要跑,比如接受外部提交的 CI 任务,那 rootless 或者干脆用一台隔离的虚拟机专门跑,值得花这个时间。别为了"更安全"三个字,在 18.04 上硬上 rootless,结果被网络问题折腾一整天。

4. daemon.json:镜像加速、日志轮转与 DNS 三件套

4.1 镜像加速配置的写法与生效验证

/etc/docker/daemon.json是 Docker 守护进程的主要配置文件,默认是不存在的,需要自己创建。最常配的第一项是镜像仓库的加速地址,尤其在国内网络环境下,不加这个拉一个基础镜像可能要好几分钟甚至直接超时。配置结构是这样:

{ "registry-mirrors": [ "https://<你的加速地址>.mirror.aliyuncs.com" ] }

加速地址的来源有几种:公有云厂商的容器镜像服务(阿里云、腾讯云等,登录控制台后能拿到专属地址)、企业自建的 Harbor 或 Nexus 仓库、公司内网搭建的缓存代理。写法上有个必须注意的点:registry-mirrors数组,而且里面的地址必须是 https(除非你显式把某个地址加进insecure-registries)。JSON 格式对新手很不友好,多一个逗号、少一个引号都会导致 dockerd 启动直接失败,而且失败信息只会说配置文件解析错误,不会告诉你是哪一行。我的习惯是每次改完先跑一遍python3 -m json.tool /etc/docker/daemon.json校验格式,通过了再systemctl restart docker

生效验证很简单:docker info输出的最后会有Registry Mirrors:一行,列出你配置的地址。如果没有这一行,说明配置没被读到,检查文件权限(应该是 644,属主 root)和路径拼写。另外镜像加速只对 Docker Hub 的官方镜像生效,对你自己指定的私有仓库地址无效,这点别搞混。

4.2 日志轮转:不配这个迟早把磁盘撑爆

这是我认为最容易被忽略、但后果最严重的一项配置。Docker 默认的日志驱动是 json-file,容器内标准输出的每一行都会写到宿主机上的一个 JSON 文件里。默认情况下这个文件没有大小上限,也不会自动轮转。一个持续输出日志的应用——比如一个每秒打几行访问日志的 Web 服务——跑上几周就能把/var/lib/docker/containers/塞满几十 G,然后整台机器的磁盘写满,容器全部挂掉,ssh 都可能登不上。

配置很简单,加到 daemon.json 里:

{ "log-driver": "json-file", "log-opts": { "max-size": "50m", "max-file": "5" } }

这组配置的意思是单文件最大 50MB,最多保留 5 个文件,也就是单个容器最多占 250MB 日志空间。数值怎么定?我一般按照"单容器峰值日志量 × 需要保留的时间"来估算。如果某个服务出问题的时候你要回溯三天前的日志,那就得保证轮转周期能覆盖三天。max-size设得太小会导致日志被频繁截断,排查问题时关键信息已经滚掉了,那配置就白做了。

还有一个重要限制:log-opts在 daemon.json 里配的只是新建容器的默认值,已经在跑的容器不会自动应用新配置。要么重建容器,要么在docker run时单独指定--log-opt。这一点很多人配完之后发现没效果,就是因为没意识到这个作用域。

4.3 容器内 DNS 解析失败与 systemd-resolved 的关系

这一条是 Ubuntu 18.04 上最经典的坑,没有之一。现象是:宿主机上curl一个外网域名完全正常,容器里apt update或者curl任何域名都报 "Temporary failure in name resolution"。原因是 18.04 默认启用 systemd-resolved,它把/etc/resolv.conf变成了一个指向127.0.0.53的软链接,而 Docker 启动容器时默认会复制宿主机的 resolv.conf 内容到容器里。容器内部看到nameserver 127.0.0.53,这个地址在容器自己的网络命名空间里当然是不可达的,DNS 解析自然全部失败。

解决方式是在 daemon.json 里显式指定容器使用的 DNS:

{ "dns": ["223.5.5.5", "119.29.29.29"] }

这里填公共 DNS 还是内网 DNS,取决于你的环境。如果容器需要解析公司内网的域名,那必须填内网的 DNS 服务器地址,否则内网服务全部解析不到。如果只是访问公网,公共 DNS 就够了。企业环境里还有一种做法是把内网 DNS 配成 forwarder,让它同时能解析内网和外网,这样容器只需要配一个地址。

配置完systemctl restart docker之后,用docker run --rm busybox nslookup <某域名>验证一下,能返回 IP 就说明生效了。注意这个dns配置同样只影响新建容器。如果你的环境里已经跑着几十个容器,重启 docker 守护进程是会影响它们的(默认 dockerd 重启会保留运行中的容器,但如果配置了live-restore才是真正无感知),生产环境操作前要评估。

4.4 存储驱动选择与数据目录迁移

前面提过,docker info里的 Storage Driver 应该是 overlay2。18.04 上默认就是它,不需要额外配置。唯一要留意的是如果你的/var/lib/docker落在了非 ext4/xfs 的文件系统上,overlay2 可能不可用,Docker 会退回 vfs 驱动,性能损失非常明显。遇到这种情况,检查df -T /var/lib/docker的输出确认文件系统类型。

数据目录迁移是另一个常见需求,原因基本只有一个:根分区不够大。做法是把整个 docker 数据目录搬到一块更大的盘上,然后在 daemon.json 里指定新位置:

{ "data-root": "/data/docker" }

操作步骤要小心:先用systemctl stop docker停服务,用rsync -aP /var/lib/docker/ /data/docker/同步数据(用 rsync 而不是 cp,因为要保留权限和硬链接关系,overlay2 的镜像层里有大量硬链接),确认同步完整之后再改配置、启服务,最后验证docker images里的镜像都还在。原来的目录别急着删,跑上一周稳定了再清理,这是基本的回滚保险。

5. 装完之后的验证清单与版本固定

5.1 让 hello-world 跑通,然后立刻删掉

sudo docker run hello-world是标准的冒烟测试。它做的事情是:从镜像仓库拉一个小镜像,创建容器,容器打印一段文字后退出。如果这一步能通,说明镜像拉取、存储驱动、容器创建、网络配置这几条链路都是通的。如果卡在 "Unable to find image locally" 之后不动,基本是网络问题,检查镜像加速配置;如果报 permission denied 或者 mount 相关错误,多半是存储驱动或者 SELinux/AppArmor 的问题。

跑通之后我建议立刻清理,docker rmi hello-world。不是因为占空间(它只有十几 KB),而是为了让这台机器的镜像列表保持干净,后续排查问题时看到不认识的镜像会分心。同理,docker run时养成加--rm的习惯,临时容器用完自动删除,能省掉大量docker ps -a里的一堆 Exited 状态垃圾。

5.2 开机自启与 systemd 的依赖关系

用 apt 安装的 Docker 会自动创建 systemd unit 并enable,也就是开机自启是默认开着的。你可以用systemctl is-enabled docker确认一下,输出应该是 enabled。如果输出是 disabled,用sudo systemctl enable docker打开。对于已经过了标准维护期的系统,systemctl is-enabled有时会返回enabled-runtime,那说明只做了临时启用,重启后会失效,需要重新 enable。

Docker 的 unit 文件里AfterWants声明了对 network-online.target 的依赖,意思是等网络就绪之后再启动。但在一些配置了静态 IP 或者有复杂网络初始化的机器上,dockerd 启动时网络还没完全就绪,会导致容器默认 bridge 创建失败。如果遇到开机后 docker 服务是 failed 状态、手动 restart 就能好,基本就是这个原因,可以在 unit 里加个Restart=always或者用 override 增加启动延迟。

5.3 用 dpkg hold 把版本钉死

前面说了要指定版本安装,但光这样还不够,因为后续的apt upgrade依然会把它们升级上去。真正锁住要用 dpkg 的 selections 机制:

echo "docker-ce hold" | sudo dpkg --set-selections echo "docker-ce-cli hold" | sudo dpkg --set-selections echo "containerd.io hold" | sudo dpkg --set-selections

验证用dpkg --get-selections | grep -E 'docker|containerd',输出里第二列应该是 hold。解除锁定就把 hold 换成 install 再执行一次。

这套机制的价值在于把"升级"变成一个明确的、需要人工决策的动作,而不是 apt upgrade 时顺手就做了。Docker 的大版本升级涉及容器运行时接口变化、iptables 链调整、默认配置变更,在生产机器上必须先在测试环境验证过再动。我见过太多"只是随手跑了个 apt upgrade,结果所有容器都起不来"的事故了。

6. Ubuntu 18.04 上六个高频翻车现场与排查链路

6.1 GPG 报错和源不可达时的排查顺序

配源阶段最常见的报错是NO_PUBKEY或者The following signatures couldn't be verified。排查顺序应该是这样:第一步,确认/etc/apt/keyrings/docker.gpg文件存在且非空,ls -l看一下大小,如果只有几百字节说明下的是错误页而不是密钥。第二步,file命令看一下文件类型,gpg --dearmor出来的应该是 "GPG key public ring" 或者二进制数据,如果是 ASCII text 说明 dearmor 没生效。第三步,检查源的配置行里signed-by=的路径和实际文件路径是否完全一致,有没有多余空格。第四步,sudo apt-get update看具体的报错行号,apt 会告诉你是哪个源的哪个阶段失败。

如果是网络层面的不可达,报错会是Could not resolve或者连接超时。这种情况下先curl -I一下源的地址看能不能通,通了说明是 apt 的代理配置问题,检查/etc/apt/apt.conf.d/下有没有代理设置,以及环境变量里有没有http_proxy之类的遗留配置。企业内网环境下,很多机器是通过代理访问外网的,apt 和 curl 用的是不同的代理配置,经常出现 curl 能通但 apt 不通的情况。

6.2 容器网络不通,从 iptables 的 FORWARD 链开始查

这是最典型的"看起来一切都正常但容器就是上不了网"的问题。排查链路我一般这么走:

先确认宿主机自身网络正常,ping或者curl一个外网地址。然后在容器里测试:docker run --rm busybox ping -c 2 8.8.8.8,注意这里用 IP 而不是域名,目的是把 DNS 问题排除掉。如果 IP 能通而域名不通,那就是前面 4.3 讲的 DNS 问题。如果 IP 也不通,问题就在转发链路上。

接下来看两个地方。第一个是sysctl net.ipv4.ip_forward,应该是 1,如果是 0,说明内核没有开启 IP 转发,Docker 理论上会自己开,但如果被其他脚本改回去了,容器就出不去。第二个是 iptables 的 FORWARD 链默认策略:

sudo iptables -L FORWARD -n --line-numbers | head -5

如果看到Chain FORWARD (policy DROP)且下面没有 Docker 相关的 ACCEPT 规则,那容器流量就被全部丢弃了。这种情况通常发生在机器上装了某些防火墙管理工具,或者用iptables -P FORWARD DROP做过加固脚本之后。Docker 在启动时会往 FORWARD 链插入自己的规则,但如果默认策略是 DROP 且规则顺序不对,依然会被拦。修法是重启 docker 服务让它重建规则链,同时检查有没有开机脚本在 docker 之后又把策略改回去。

6.3 磁盘满与 overlay2 的 inode 耗尽

磁盘写满有两种,一种是空间满了,一种是 inode 满了,后者更隐蔽。先用df -h /var/lib/docker看空间,再用df -i /var/lib/docker看 inode 数量。如果空间还有很多但 inode 显示 100%,那就是创建了太多小文件。overlay2 每启动一个容器都会创建一批目录结构,如果有个脚本在循环创建销毁容器,inode 会飞快消耗。

清理的手段按安全程度排序:docker system prune能清掉停止的容器、未被使用的网络和悬空镜像,这是最安全的;加-a参数会连未被任何容器引用的镜像一起清掉,要小心,可能把你有意保留的基础镜像删了;docker volume prune会清理未被挂载的卷,这个最危险,因为卷里可能是业务数据,执行前一定用docker volume ls确认一遍。我个人的习惯是先docker system df -v看看各项占用,心里有数了再决定清哪一层。

6.4 containerd 依赖冲突与残留包

containerd.io的依赖冲突是安装阶段最常见的中断点。报错通常长这样:docker-ce : Depends: containerd.io (>= 1.2.2-3) but it is not going to be installed。原因是系统里已经有其他来源的 containerd 包,apt 无法同时满足两边的依赖。

排查步骤:dpkg -l | grep containerd看装了什么版本,apt-cache policy containerd.io看可用版本和来源。如果发现系统里有来自 Ubuntu 源的containerd(不带 .io 后缀),那就是冲突源,先把它卸掉。如果卸载会牵连其他包,用apt-get remove --dry-run先模拟一遍看看影响范围。还有一种情况是 apt 的包缓存里有旧版本的索引,sudo apt-get clean && sudo apt-get update刷新一遍再试。

6.5 命令找不到与多个 docker 二进制并存

现象是docker: command not found,但dpkg -l | grep docker-ce明明显示已安装。这时候用which -a docker看 PATH 里能找到几个 docker 可执行文件,ls -l /usr/bin/docker看是不是软链接指向了不存在的目标。多版本并存的典型症状是 docker 命令能跑但行为诡异,比如docker version报客户端和服务端版本差得离谱,那就说明你调用的客户端不是你以为的那个。

彻底排查的方法是把所有可能的路径都扫一遍:/usr/bin/docker/usr/local/bin/docker/snap/bin/docker/var/lib/docker下的运行时文件。找到多余的版本,用对应的包管理器卸掉(apt 装的用 apt,snap 装的用 snap,手动下载二进制装的一般在/usr/local/bin,直接删文件)。清完之后hash -r刷新 shell 的命令缓存,再which docker确认。

6.6 服务起不来时先看 journalctl 的第一手日志

dockerd 启动失败的报错信息,systemctl status docker只显示最后一小段,往往不是根因。真正的第一手信息在 journal 里:

sudo journalctl -u docker.service -n 100 --no-pager

看日志有个技巧:从最后往前找第一条 ERROR 或者 FATAL,那通常才是原始错误,后面的报错都是它的连锁反应。常见的几类根因:daemon.json 格式错误(报 "unable to configure the Docker daemon")、端口或 socket 被占用(报 "address already in use")、cgroup 挂载点异常(报 "failed to mount cgroup")、磁盘满(报 "no space left on device")。定位到根因之后再去找对应的解法,比盲目重启服务高效得多。

7. 把这个环境用起来:从单机 Docker 到多架构构建

7.1 用一套 Redis 主从来验证容器网络模型

装好之后想验证环境是不是真的好用,跑一套主从最直观。准备一个docker-compose.yml

services: redis-master: image: redis:6.2-alpine container_name: redis-master ports: - "6379:6379" command: redis-server --appendonly yes volumes: - master-data:/data redis-replica: image: redis:6.2-alpine container_name: redis-replica depends_on: - redis-master command: redis-server --replicaof redis-master 6379 volumes: - replica-data:/data volumes: master-data: replica-data:

这里注意两点。第一,replicaof redis-master 6379里的主机名是 compose 的服务名,会被自动解析到容器 IP,这正好验证了自定义网络的 DNS 功能是好用的。如果你在第 4 章配的 DNS 设置有问题,这一步会失败。第二,两个服务在同一个自定义网络里,互相可以访问,但外部只能通过映射的 6379 端口访问 master,replica 没有映射端口,从宿主机访问不到,这是符合预期的隔离行为。

验证的话,进 master 容器docker exec -it redis-master redis-cli,然后SET foo bar,再进 replica 执行GET foo,能读到值说明主从同步正常。也可以用INFO replicationrole字段和slave_repl_offset,后者在持续增长说明同步链路是活的。

7.2 多架构镜像与跨平台构建的思路

如果你的环境里混着 x86 和 ARM 的机器——比如国产化平台上的构建需求——那就需要关注多架构镜像。核心工具是docker buildx,它能在一次构建中输出多个架构的镜像,并通过 manifest list 让docker pull根据当前平台自动选择对应的层。基础用法:

docker buildx create --name mybuilder --use docker buildx inspect --bootstrap docker buildx build --platform linux/amd64,linux/arm64 -t yourrepo/app:1.0 --push .

几个必须知道的限制。第一,--push是必需的,多架构构建结果不能直接--load到本地 docker 里,因为本地镜像存储只支持单一架构的 manifest。第二,构建过程中用了 QEMU 做跨架构模拟,速度会明显慢于本机构建,复杂的编译任务可能慢十倍以上,做好心理准备。第三,如果目标架构不在 Docker 官方支持列表里(比如某些国产 CPU 架构),需要自己准备对应的构建器镜像和工具链,这条路会比较长,建议先用官方支持的架构把流程跑通再说。

另外需要提醒的是,Ubuntu 18.04 上默认的 docker 版本对 buildx 的支持可能不完整,buildx 在较新的版本里才作为默认插件集成。如果docker buildx报 command not found,需要单独下载 buildx 的可执行文件放到~/.docker/cli-plugins/目录下。这类插件在 18.04 上手动安装是比较常见的操作。

7.3 后续升级与彻底卸载的顺序

如果哪天你决定把这台机器的 Docker 升级到更新的版本,正确的顺序是:先备份/etc/docker/daemon.json,记录当前版本号,然后dpkg --set-selections解除 hold,apt-get install指定新版本,重启服务,用docker infodocker ps确认现有容器状态。特别注意跨大版本升级时容器运行时从 runc 到 runc/containerd 的切换,以及 cgroup v1 到 v2 的变化(后者在 18.04 上默认还是 v1,一般不受影响)。

彻底卸载的步骤是:

sudo systemctl stop docker sudo apt-get purge -y docker-ce docker-ce-cli containerd.io sudo apt-get autoremove -y sudo rm -rf /var/lib/docker /var/lib/containerd sudo rm -rf /etc/docker /etc/apt/sources.list.d/docker.list /etc/apt/keyrings/docker.gpg

最后那几行删配置的动作千万别省,不然你下次重装的时候,一个残留的 daemon.json 会让你怀疑人生——新装的 docker 启动时读到了旧的配置,行为和你预期的完全不一样,而你又想不起来什么时候配过。我踩过一次,排查了两小时才发现是半年前留下的>

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

2026年靠谱的红木回收公司推荐,一诺红木家具回收上榜

红木回收市场现状近年来&#xff0c;随着红木家具市场的发展&#xff0c;红木回收行业也逐渐兴起。然而&#xff0c;市场上红木回收公司良莠不齐&#xff0c;消费者在选择时往往面临诸多困惑。如何找到一家靠谱的红木回收公司成为了许多人关心的问题。一诺红木家具回收的优势 专…

作者头像 李华
网站建设 2026/9/18 23:25:11

零基础用Godot做第一款小游戏:从选型到完整实操指南

开始正文做一款属于自己的游戏&#xff0c;这个念头很多人都有过。但真到动手的时候&#xff0c;第一个问题往往不是“我该学什么”&#xff0c;而是“我该从哪里开始”——引擎那么多、教程那么杂、知识点那么碎&#xff0c;光是把环境装好、把界面看懂&#xff0c;就已经劝退…

作者头像 李华
网站建设 2026/9/18 23:23:35

智慧机场数字化转型:从数据孤岛到智能运营中心

简介&#xff1a;智慧机场解决方案与应用以52页精炼篇幅&#xff0c;系统梳理民航机场数字化转型的顶层思路与落地路径。内容面向机场运控、地服、安检、信息中心等业务骨干&#xff0c;以及智慧城市/智慧交通方案规划人员&#xff0c;围绕“四型机场”政策、A-CDM到TAM演进、生…

作者头像 李华
网站建设 2026/9/18 23:22:38

降AI率全场景推荐:从毕业论文到SCI期刊,嘎嘎降AI都能搞定

降AI率全场景推荐&#xff1a;从毕业论文到SCI期刊&#xff0c;嘎嘎降AI都能搞定 降AI率的需求不只有毕业论文&#xff0c;也不只有知网。 本科毕业论文、硕士论文、博士论文、SCI期刊投稿——各场景的AI率要求不一样&#xff0c;检测平台不一样&#xff0c;需要的工具能力也…

作者头像 李华