本文使用「署名 4.0 国际 (CC BY 4.0)」许可协议,欢迎转载、或重新修改使用,但需要注明来源。 [署名 4.0 国际 (CC BY 4.0)](https://creativecommons.org/licenses/by/4.0/deed.zh) 本文作者: 苏洋 创建时间: 2025年02月05日 统计字数: 7846字 阅读时间: 16分钟阅读 本文链接: https://soulteary.com/2025/02/05/docker-stability-ubuntu-version-pinning-guide.html ----- # Docker 稳定性保障:Ubuntu 环境版本锁定方案 Docker 作为许多开发或生产环境中的基础设施的重要组成部分,其版本的稳定性直接影响着整个系统的可靠性。本文将介绍如何有效地管理和固定 Docker 的版本。 ## 写在前面 不知不觉,Docker 已经诞生十二年了。作为容器化技术的先驱,它早已成为了众多开发者和企业的必备工具。然而,随着 Docker 的不断发展,版本更新频繁带来的问题也日益凸显。 很多开发者可能都遇到过这样的困扰:明明之前运行得好好的容器,在更新 Docker 版本后突然就出现了各种奇怪的问题。有时是配置文件不兼容,有时是命令参数变化,有时甚至会影响到整个应用的稳定性。 更让人头疼的是,当你想要解决这些问题时,网上搜索到的许多文章要么已经过时,要么就是简单地复制粘贴一些错误的或者已经失效的命令。这不仅无法解决问题,反而会浪费大量的时间。(你在使用这些命令的时候,是否想过它们真的靠谱吗?) 正是基于这些原因,我决定写这篇文章,详细介绍如何在 Ubuntu 环境中有效地管理和固定 Docker 版本,帮助你避免版本更新带来的各种意外问题。 ## 准备工作:Docker 安装 如果你还没有安装 Docker,可以参考我之前写的《[搭建 Ubuntu 24.04 基础开发环境指南](https://soulteary.com/2025/01/17/guide-to-setting-up-ubuntu-24-04-basic-development-environment.html)》,文章详细介绍了 Docker 的安装和基础配置步骤。 ## 实战过程 首先,让我们来看看如何找出系统中已经安装的 Docker 相关包。 ### 查找 Docker 依赖软件包 我们可以使用 `dpkg-query` 命令来查询系统中已安装的软件包,并用 `grep` 筛选出 Docker 相关的内容: ```bash # dpkg-query -l | grep docker ii docker-buildx-plugin 0.20.0-1~ubuntu.24.04~noble arm64 Docker Buildx cli plugin. ii docker-ce 5:27.5.1-1~ubuntu.24.04~noble arm64 Docker: the open-source application container engine ii docker-ce-cli 5:27.5.1-1~ubuntu.24.04~noble arm64 Docker CLI: the open-source application container engine ii docker-ce-rootless-extras 5:27.5.1-1~ubuntu.24.04~noble arm64 Rootless support for Docker. ii docker-compose-plugin 2.32.4-1~ubuntu.24.04~noble arm64 Docker Compose (V2) plugin for the Docker CLI. ii python3-docker 5.0.3-1ubuntu1.1 all Python 3 wrapper to access docker.io's control socket ``` 为了避免手动复制粘贴可能带来的错误,我们可以使用 `awk` 命令来提取包名。使用命令获取软件包名还有一个好处,后续 `docker` 软件依赖发生变化后,相比较手动“复制粘贴”,使用命令也可以自适应解决问题。 ```bash # dpkg-query -l | grep docker | awk '{print $2}' docker-buildx-plugin docker-ce docker-ce-cli docker-ce-rootless-extras docker-compose-plugin python3-docker ``` 好了,日志中的结果就是 Docker 的主要程序组件依赖了。 不过,这只是处理了那些名字中明显包含 “docker” 关键词的包。接下来,我们还需要处理一些可能被遗漏的依赖包。 ### 深入了解 Docker 软件包依赖关系 在研究 Docker 的子依赖时,我们可以使用 `apt-cache depends` 结合 `xargs` 来进行分析。 首先,让我们看看如何获取相关软件包的依赖情况: ```bash # dpkg-query -l | grep docker | awk '{print $2}' | xargs -I {} apt-cache depends {} docker-buildx-plugin Replaces: docker-ce-cli Enhances: docker-ce-cli docker-ce Depends: containerd.io Depends: docker-ce-cli Depends: iptables Depends: libseccomp2 Depends: libc6 Depends: libsystemd0 Conflicts: Conflicts: Conflicts: docker.io Recommends: apparmor Recommends: ca-certificates Recommends: docker-ce-rootless-extras Recommends: git Recommends: kmod Recommends: libltdl7 Recommends: pigz Recommends: procps Recommends: xz-utils |Suggests: cgroupfs-mount Suggests: cgroup-lite Replaces: docker-ce-cli Depends: libc6 Conflicts: Conflicts: Conflicts: docker.io Breaks: docker-ce Recommends: docker-buildx-plugin Recommends: docker-compose-plugin Replaces: docker-ce docker-ce-rootless-extras Depends: dbus-user-session Depends: libc6 Conflicts: rootlesskit Breaks: rootlesskit Recommends: slirp4netns Replaces: rootlesskit Enhances: docker-ce docker-compose-plugin Enhances: docker-ce-cli python3-docker Depends: python3-requests Depends: python3-websocket Depends: python3 Depends: python3-packaging Depends: python3-urllib3 Breaks: python3-minimal ``` 这个命令会为我们展示详细的依赖信息,包括: - 必需依赖(Depends) - 推荐安装(Recommends) - 冲突包(Conflicts) - 替换包(Replaces)等 考虑到我们主要关注稳定环境下的版本控制和简化维护工作,我们本身不会强制安装古早的淘汰软件包,也不会强制安装冲突软件包。所以,重点应该放在 `Depends` 和 `Recommends` 这两类依赖上。我们可以这样继续筛选: ```bash #dpkg-query -l | grep docker | awk '{print $2}' | xargs -I {} apt-cache depends {} | grep -E "Depends|Recommends" Depends: containerd.io Depends: docker-ce-cli Depends: iptables Depends: libseccomp2 Depends: libc6 Depends: libsystemd0 Recommends: apparmor Recommends: ca-certificates Recommends: docker-ce-rootless-extras Recommends: git Recommends: kmod Recommends: libltdl7 Recommends: pigz Recommends: procps Recommends: xz-utils Depends: libc6 Recommends: docker-buildx-plugin Recommends: docker-compose-plugin Depends: dbus-user-session Depends: libc6 Recommends: slirp4netns Depends: python3-requests Depends: python3-websocket Depends: Depends: python3-packaging Depends: python3-urllib3 ``` 我们从输出结果中,能够看到非常清晰的依赖关系,其中主要包括: - 基础组件:containerd.io、iptables、libseccomp2 - 系统库:libc6、libsystemd0 - 工具包:docker-ce-cli、docker-buildx-plugin - 安全相关:apparmor、ca-certificates - 其他工具:git、kmod、pigz、procps 其中一个依赖支持“任意版本的 Python 软件包”,所以我们后续处理过程中,我们可以将这个依赖项去掉。 为了让结果更清晰易读,我们可以用 `sed` 来继续优化输出格式,只保留包名: ```bash dpkg-query -l | grep docker | awk '{print $2}' | xargs -I {} apt-cache depends {} | grep -E "Depends|Recommends" | grep -v ":any" | sed 's/^[[:space:]]*\(Depends\|Recommends\): //' containerd.io docker-ce-cli iptables libseccomp2 libc6 libsystemd0 apparmor ca-certificates docker-ce-rootless-extras git kmod libltdl7 pigz procps xz-utils libc6 docker-buildx-plugin docker-compose-plugin dbus-user-session libc6 slirp4netns python3-requests python3-websocket python3-packaging python3-urllib3 ``` 这样就得到了一个简洁的 Docker 核心依赖列表,对于我们理解当前版本的 Docker 的组件构成和进行环境配置都很有帮助。 ### 编写软件包获取脚本程序 有了前面的经验,我们可以写一个更完整的脚本,来获取所有软件包的依赖列表。这对我们后续锁定特定软件包会很有帮助。 ```bash # !/bin/bash # 创建临时文件存储所有依赖 temp_file=$(mktemp) # 获取所有 docker 相关包的依赖 dpkg-query -l | grep docker | awk '{print $2}' | while read pkg; do echo "=== Dependencies for $pkg ===" >> "$temp_file" apt-cache depends "$pkg" | grep -E "Depends:|Recommends:" | \ grep -v "<" | \ grep -v ":any" | \ sed 's/^[[:space:]]*\(Depends\|Recommends\): //' >> "$temp_file" echo >> "$temp_file" done # 显示所有依赖(带包名标题) cat "$temp_file" echo -e "\n=== Unique dependencies (sorted) ===" # 提取唯一的依赖项并排序 grep -v "^===" "$temp_file" | \ grep -v "^$" | \ sort -u # 清理临时文件 rm "$temp_file" ``` 我们将上面的内容保存为 `docker-deps.sh`,当我们使用 `bash docker-deps.sh` 运行这个脚本后,它会先按照每个包分类显示其依赖项,然后给出一个去重后的完整依赖列表。下面是运行结果的示例: ```bash === Dependencies for docker-buildx-plugin === === Dependencies for docker-ce === containerd.io docker-ce-cli iptables libseccomp2 libc6 libsystemd0 apparmor ca-certificates docker-ce-rootless-extras git kmod libltdl7 pigz procps xz-utils === Dependencies for docker-ce-cli === libc6 docker-buildx-plugin docker-compose-plugin === Dependencies for docker-ce-rootless-extras === dbus-user-session libc6 slirp4netns === Dependencies for docker-compose-plugin === === Dependencies for python3-docker === python3-requests python3-websocket python3-packaging python3-urllib3 === Unique dependencies (sorted) === apparmor ca-certificates containerd.io dbus-user-session docker-buildx-plugin docker-ce-cli docker-ce-rootless-extras docker-compose-plugin git iptables kmod libc6 libltdl7 libseccomp2 libsystemd0 pigz procps python3-packaging python3-requests python3-urllib3 python3-websocket slirp4netns xz-utils ``` 结果相比较之前更加清晰,并且我们可以从这个列表中清晰的对 Docker 进行全面的了解:Docker CE 本身依赖了一些基础组件,比如 containerd.io、iptables 等。而 Docker CLI 则需要 buildx 和 compose 插件的支持。对于 rootless 模式,还需要 dbus-user-session 和 slirp4netns。如果你用到了 Python SDK,python3-requests 等几个 Python 包也是依赖项之一。 ### 筛选需要关注的依赖 从前面的命令输出中,我们看到 Docker 依赖了不少系统组件。如果你追求极致的环境稳定性,并且不打算对宿主机做任何补丁更新,你当然可以把所有相关的软件包都锁定版本。 不过我建议你三思。将所有程序都锁定版本,可能会导致错过 Ubuntu 系统的重要安全更新,也可能错过一些系统稳定性的改进。所以我的建议是:**只锁定 Docker 直接相关的软件包,其他系统组件就交给系统的包管理器去维护。** 为了实现这个目标,我们可以改进一下脚本,筛选出真正需要锁定的 docker 相关包。在改进脚本之前,回忆下之前的文章中,我们的 Docker 安装命令是这样的(一共四个依赖): ```bash sudo apt update && sudo apt install -y docker-ce docker-ce-cli containerd.io docker-compose-plugin ``` 结合安装命令,我们最好也将 `containerd.io` 这个软件包进行锁定。 最终改进的脚本程序如下: ```bash # !/bin/bash # 创建临时文件存储所有依赖 temp_file=$(mktemp) # 获取所有 docker 相关包的依赖 dpkg-query -l | grep docker | awk '{print $2}' | while read pkg; do echo "=== Dependencies for $pkg ===" >> "$temp_file" apt-cache depends "$pkg" | grep -E "Depends:|Recommends:" | \ grep -v "<" | \ grep -v ":any" | \ sed 's/^[[:space:]]*\(Depends\|Recommends\): //' >> "$temp_file" echo >> "$temp_file" done temp_file2=$(mktemp) # 提取唯一的依赖项并排序 grep -v "^===" "$temp_file" | \ grep -v "^$" | \ sort -u > "$temp_file2" dpkg-query -l | grep docker | awk '{print $2}' >> "$temp_file2" cat "$temp_file2" | sort | uniq | grep -E "docker|container" # 清理临时文件 rm "$temp_file" rm "$temp_file2" ``` 执行之后,我们会得到一个需要锁定的软件包列表: ```bash # bash docker-deps.sh containerd.io docker-buildx-plugin docker-ce docker-ce-cli docker-ce-rootless-extras docker-compose-plugin python3-docker ``` 这就是我们真正需要锁定的软件包了,目前一共七个项目。 ### 进行软件包版本锁定 为了让系统能够自动对列表中的程序进行版本锁定,我们可以用下面这个命令: ```bash # echo $(bash docker-deps.sh) | tr ' ' '\n' | xargs -I {} sudo apt-mark hold {} containerd.io set on hold. docker-buildx-plugin set on hold. docker-ce set on hold. docker-ce-cli set on hold. docker-ce-rootless-extras set on hold. docker-compose-plugin set on hold. python3-docker set on hold. ``` ### 验证软件包是否锁定 命令执行完毕后,我们还可以执行命令验证软件包锁定是否成功: ```bash # apt-mark showhold containerd.io docker-buildx-plugin docker-ce docker-ce-cli docker-ce-rootless-extras docker-compose-plugin python3-docker ``` 从结果可以看到,系统中被锁定的软件包和我们之前获取的依赖列表一致。 ## 最后 好了,这篇文章就写到这里啦。 --EOF