尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

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

Ubuntu 18.04 安装 Docker:官方 APT 源配置与排错实战 上周有个读者发来一段报错说他在一台 Ubuntu 18.04 的机器上装 Dockerapt install docker.io装完之后docker info里一堆警告跑docker run hello-world直接卡住不动。这类问题我这两年遇到过不下十次几乎每一次的根因都不一样有人是装到了发行版自带的老包里有人是 systemd-resolved 和容器 DNS 打架有人是 iptables 的 FORWARD 链被防火墙脚本清成了 DROP。Ubuntu 18.04Bionic Beaver这个版本很特殊——它已经过了标准维护期但大量存量服务器、边缘设备、内网构建机还跑着它而 Docker 官方早就停止为 bionic 构建新版本包了。这意味着在 18.04 上装 Docker不能照抄官网首页那几行命令得知道自己在装哪个版本、为什么装这个版本、装完之后哪些地方一定会出问题。下面这篇是我自己反复装过几十台机器之后整理出来的完整流程从为什么不用 apt 里的 docker.io一直讲到多架构镜像构建。不管你是第一次碰 Docker 的新手还是只是想在老机器上补一套干净的环境按这个顺序走一遍基本不会再踩那些冤枉坑。1. 为什么在 Ubuntu 18.04 上不该直接敲 apt install docker.io1.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 buildx、docker scan、容器健康检查的部分语法你在别人的教程里看到能跑自己这里报unknown flag。第二社区打包版本和上游在默认配置上有差异比如默认存储驱动、默认日志驱动的取值可能不同导致你照搬网上的 daemon.json 配置时出现奇怪的行为差异。第三也是最麻烦的一点当你后面想升级到上游新版本时docker.io和docker-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 装的 dockerwhich docker的结果会随 PATH 顺序变化调试起来能让人崩溃。桌面用户想用图形化直接考虑 Docker Desktop但注意 Docker Desktop for Linux 要求在 20.04 及以上18.04 上根本装不了这一点很多人不知道白白折腾半天。18.04 只能用 Engine没有别的选择。1.3 动手之前先花两分钟确认三件事在敲任何安装命令之前有三项检查我建议你固定做一遍能省掉后面大量的排查时间。第一确认系统版本和代号。lsb_release -a看是不是 Bionic Beaverdpkg --print-architecture看是 amd64 还是 arm64。这两个信息决定了你后面 APT 源里写哪个代号、哪个架构字段写错了 apt update 会直接报 404。第二确认内核版本uname -r。18.04 初始内核是 4.15后续更新能到 5.4overlay2 存储驱动需要 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 ar /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 couldnt be verified这个报错信息不会告诉你真正原因是格式不对只会说签名校验失败。chmod ar那行也别省apt 以_apt用户身份读取密钥文件如果权限是 600 并且属主是 root会报权限拒绝同样是个很迷惑的报错。$(lsb_release -cs)在 18.04 上会展开为bionic如果你嫌麻烦直接写死 bionic 也行但如果哪天系统升级了写死的代号就会导致源失效或者装到错误的包。2.3 锁定版本安装别一上来就装最新配好源之后先别急着 install先看看到底有哪些版本可选apt-cache madison docker-ceUbuntu 18.04 的情况比较特殊Docker 官方虽然保留了 bionic 的仓库目录但新版本已经不再为它构建能拿到的稳定版本大致停在 20.10 这一代具体以这条命令的实际输出为准。这其实不是坏事20.10 是个成熟稳定的版本配套的 containerd、runc 生态都很完善比某些刚发布的新版本反而省心。看好了版本号之后精确安装sudo apt-get install -y \ docker-ce5:20.10.24~3-0~ubuntu-bionic \ docker-ce-cli5: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 infosystemctl status看的是服务有没有真的起来输出里要有绿色的 active (running)。docker version会把 Client 和 Server 两段分开列出来如果只显示 Client 而 Server 段报 Cannot connect to the Docker daemon说明 dockerd 没起来或者 socket 权限不对。docker info的信息量最大重点看三行Storage Driver 应该是 overlay2Cgroup Driver 应该是 cgroupfs 或者 systemd18.04 上默认 cgroupfsLogging 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 里刷新组信息会开一个子 shellsu - $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 支持需要newuidmap和newgidmap这两个 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 文件里After和Wants声明了对 network-online.target 的依赖意思是等网络就绪之后再启动。但在一些配置了静态 IP 或者有复杂网络初始化的机器上dockerd 启动时网络还没完全就绪会导致容器默认 bridge 创建失败。如果遇到开机后 docker 服务是 failed 状态、手动 restart 就能好基本就是这个原因可以在 unit 里加个Restartalways或者用 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 couldnt 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 装的用 aptsnap 装的用 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.ymlservices: 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 端口访问 masterreplica 没有映射端口从宿主机访问不到这是符合预期的隔离行为。验证的话进 master 容器docker exec -it redis-master redis-cli然后SET foo bar再进 replica 执行GET foo能读到值说明主从同步正常。也可以用INFO replication看role字段和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解除 holdapt-get install指定新版本重启服务用docker info和docker 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 启动时读到了旧的配置行为和你预期的完全不一样而你又想不起来什么时候配过。我踩过一次排查了两小时才发现是半年前留下的>
返回列表