news 2026/9/26 12:01:58

Linux下使用Docker官方二进制包安装与运维实战

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Linux下使用Docker官方二进制包安装与运维实战

1. 对比包管理器与二进制通用包:什么环境才值得选后者

1.1 两种安装方式的分水岭

大多数人在 Linux 上装 Docker,第一反应就是 apt 或 yum 一把梭。这个思路本身没错,apt install docker.io或者yum install docker-ce在普通场景里确实省心,依赖自动拉、systemd 单元自动写、启动脚本自动配好,几乎零成本。但如果你在下面这几类环境里待过,就会明白包管理器方案其实非常别扭:

  • 内网离线服务器,软件源根本没有 Docker 的包,想临时挂载一个本地源又要折腾半天;
  • 发行版比较冷门,或者是一个阉割版系统,包仓库里没有 Docker,或者版本老得没法看;
  • 生产环境要求版本可预期、可锁定,不允许 apt upgrade 一冲动把 Docker 版本顺带换掉;
  • 需要在一批不同发行版上保持完全一致的 Docker 版本,方便排障和运维。

有这些需求时,官方发布的二进制通用包就是最合适的答案。它本质上是一个静态编译的 tarball,不依赖系统里有没有某版本 libc,不需要包管理器参与,把里面的工具铺好位置,自己写好 systemd 单元,Docker 就能像原生服务一样跑起来。

官方把这种安装方式称为 static binary。下面这张表是我在实际运维中反复对比后的结论:

对比项包管理器安装(apt/yum)二进制通用包安装
依赖处理自动拉取,但可能污染系统中的共享依赖基本零库依赖,全部内置在 tar 包中
版本控制受仓库源版本约束,容易被动升级自己下载指定版本,版本完全可控
离线安装需要完整的离线源,工作量大只需一个 tarball 和校验文件
systemd 集成自动完成手动编写 unit 文件,约 10 分钟
升级/回滚依赖包管理器的行为tar 包覆盖即升级,留旧版即回滚
学习成本低中等,但对排障能力很有帮助

一句话:包管理器适合"求快",二进制通用包适合"求稳、求可控、求可复现"。生产环境我后面基本一律二进制。

1.2 二进制包的适用边界与先决条件

需要说清楚,二进制包不是万能的,它也有自己的边界。首先是内核与系统基础组件,这恰恰是很多人忽略的点。Docker 的静态包虽然把用户态的东西都打包了,但底层仍然依赖 Linux 内核特性,比如 namespaces、cgroups、overlayfs、iptables/nftables。也就是说,你可以在一个非常精简的最小系统上装 Docker,但这个系统的内核要足够新,至少能支撑 overlay2 存储驱动和 cgroup v2(内核 4.9 以上基本没压力,5.x 内核是最舒服的)。内核太老的话,再高级的二进制包也白搭。

其次是进程托管方式。官方二进制包里的 dockerd 不带自带的服务管理逻辑,你希望它开机自启、崩溃自动拉起、和 systemd 日志系统无缝集成,就得自己写 systemd unit。如果目标机器是 SysV init 或纯容器化环境,连 systemd 都没有,那就要用dockerd &这类方式凑合,但这种跑法不适合长期生产,日志和进程管理都很脆弱。

最后是SELinux 和 AppArmor。在 RHEL/CentOS 系列上,如果用二进制包手动铺装,polkit 规则和 SELinux 上下文一般不会自动配好,遇上"服务起不来 / 容器无法访问文件"这类怪问题时,优先怀疑 SELinux 而不是 Docker 本身。好在大部分普通场景下把这层先 relax 掉就能跑通,具体怎么定位我放到后面故障排查那一节细说。

1.3 装之前先明确这套方案的"管理成本"

聊完优点,我也得把账算明白。二进制通用包的主要代价就是一切手动:

  • 不会自动升级,安全性补丁需要自己关注 release 动态;
  • 不会自动补 unit 文件,初始化时必须自己写;
  • 不会自动处理用户组,普通用户要访问 Docker 得手动加入 docker 组;
  • 不会自动处理镜像加速与日志轮转,这些都要写进/etc/docker/daemon.json。

这些成本加起来差不多也就是半小时的一次性投入,换来的是对整套运行时(dockerd、containerd、runc)的完全掌控。我见过不少团队被包管理器升级搞怕了:一次ubuntu unattended-upgrade把 Docker 从 20.10 升到 24.0,结果 K8s 集群直接不认存储驱动。用二进制包锁版本之后,这种事情再没发生过。接下来我就按完整流程走一遍。

2. 下载、校验与拆包:把官方仓库的 tarball 变成可控资产

2.1 确认架构与官方目录结构

动手之前先确认两件事:一是目标系统的 CPU 架构,二是目标系统能不能访问官方下载站点。架构直接决定下载路径,别想当然地以为都是 x86_64。

uname -m cat /etc/os-release

常规服务器输出一般是x86_64,树莓派之类的是aarch64或armv7l,还有s390x、ppc64le这类小众架构。Docker 官方下载目录把架构分得很清楚:

https://download.docker.com/linux/static/stable/x86_64/ https://download.docker.com/linux/static/stable/aarch64/ https://download.docker.com/linux/static/stable/armv7l/ https://download.docker.com/linux/static/stable/s390x/

注意stable和edge(有的版本叫test)的区别。stable目录下的才是经过完整验证的稳定版,edge或者test目录下的版本用于试验新功能,千万不要拿到生产环境去装。有的教程为了追新版本,直接从别处复制一个 URL 就下载,这很容易下到非 stable 分支的包,后面的行为都不可预期。

2.2 选版、下载与校验

确认好架构后,进目录看一下有哪些版本。我用 x86_64 举例,假设当前 stable 目录下的最新版是docker-27.3.1.tgz,实际以你在目录列表里看到的为准:

curl -sSL https://download.docker.com/linux/static/stable/x86_64/ | grep 'docker-27'

找个干净的目录,比如/opt/software,把包下载下来:

mkdir -p /opt/software cd /opt/software wget https://download.docker.com/linux/static/stable/x86_64/docker-27.3.1.tgz

下载完成后,务必做校验。这一步很多人会跳过,但我强烈建议不要省。静态包大多是压缩包,传输过程中有损坏,或者镜像源被替换,都可能导致后面装出一个行为诡异的 Docker。校验方法很简单:

sha256sum docker-27.3.1.tgz

然后把输出的哈希值和 Docker 官方在该版本 Release Notes 或 GitHub Release 页面公布的校验值对比。同一目录下也有文件大小的参考值,连大小都对不上的话基本可以判定文件坏了。

我踩过这样一个坑:某次在内网通过跳板机传输 tarball,中间环节的文件被截断了 200 字节,tar没有立刻报错,结果解压出来的 dockerd 一启动就 segmentation fault,查了大半天才发现是包坏了。从那以后,凡是从外部拿回来的安装包,一律先sha256sum再落地,这应该成为安装规范的一部分。

2.3 解包后先验货:tarball 里到底有什么

下载校验通过后,解压:

tar xzf docker-27.3.1.tgz ls -lh docker/

你会看到一个docker目录,里面是这套通用包的完整内容。我以某个较新的版本为例,典型的文件清单如下:

docker/ ├── containerd ├── containerd-shim-runc-v2 ├── ctr ├── docker ├── docker-init ├── docker-proxy ├── dockerd └── runc

这些文件各管一摊:

  • docker:客户端 CLI,就是你平时敲docker ps、docker run用的那个程序;
  • dockerd:守护进程端,真正管理镜像、容器、网络的那部分;
  • containerd:容器运行时管理器,负责拉镜像、管理容器生命周期,Docker 在现代版本里把它作为底层运行时的核心组件;
  • containerd-shim-runc-v2:容器和 containerd 之间的中间进程,负责容器退出后的兜底和 IO 转发;
  • runc:OCI 容器的真正创建者,负责和 Linux 内核打交道;
  • ctr:containerd 自带的调试客户端,平时用不到,但排查 containerd 问题时要靠它;
  • docker-init:容器内 PID 1 的轻量初始化程序,负责处理孤儿进程和信号转发;
  • docker-proxy:宿主机端口映射到容器时的辅助代理,负责 docker run -p 的端口转发。

先知道包里都有什么,后面排查问题时思路会清晰很多。比如容器里进程变僵尸、端口映射时灵时不灵,你就能联想到是 docker-init 或 docker-proxy 的问题,而不是瞎猜。

3. 铺装整机:二进制布局、权限与三段 systemd 单元

3.1 拷贝二进制、准备目录与用户组

验完货直接拷贝。我习惯把二进制放到/usr/bin,这样 systemd 单元里写ExecStart=/usr/bin/dockerd最自然,也符合大多数发行版 PATH 的默认范围。如果你想装到/usr/local/bin也完全可以,但 unit 文件里的路径必须同步修改,别配岔了。

cp docker/* /usr/bin/ chmod 755 /usr/bin/docker /usr/bin/dockerd /usr/bin/containerd /usr/bin/runc /usr/bin/ctr /usr/bin/docker-init /usr/bin/docker-proxy /usr/bin/containerd-shim-runc-v2

接着准备 Docker 自己的目录和用户组:

mkdir -p /etc/docker mkdir -p /var/lib/docker groupadd docker

/etc/docker是 daemon.json 配置所在地,/var/lib/docker是默认数据根目录,容器、镜像、卷全在这里。这里有一点值得展开:如果你打算把数据盘挂到独立分区,一定要在初始化阶段就把>getent group docker || groupadd docker

加组这步不会立刻对当前登录的 shell 生效,后面我会讲具体怎么让权限马上可用。

3.2 containerd 先行:为什么这套方案离不开鸡生蛋蛋生鸡的编排

老版本的 Docker 直接由 dockerd 自己管理容器运行时,新版本的架构则是 dockerd 与 containerd 协作。从 Docker 20.10 以后,containerd 必须作为独立服务存在,dockerd 启动时会去连 containerd 的 socket(默认在/run/containerd/containerd.sock)。如果你只把 dockerd 拉起来而 containerd 没运行,dockerd 会反复报 "failed to connect to containerd" 之类的错误。

所以这套方案的正确编排是:docker.socket先监听好 API socket,containerd.service先运行起来,docker.service再作为最终承载启动 dockerd。三个单元互相之间要靠Requires、After、Wants把顺序约束住,否则 systemd 并行启动时会出现竞态,偶尔成功偶尔失败。

这一段是我实际排障得来的教训。最早我图省事,只写了 docker.service 一个单元,把 containerd 直接忽略,结果服务 10 次里有 3 次起不来,起不来时 journald 里的错误信息还非常具有迷惑性。后来老老实实拆成三个单元,顺序理顺,这个问题彻底消失。

3.3 三段 unit 文件逐一拆解

下面是三个 unit 文件的具体内容,你可以直接复制参考,只要把路径和你的环境对齐就可以。

先创建/etc/systemd/system/docker.socket:

[Unit] Description=Docker Socket for the API [Socket] ListenStream=/var/run/docker.sock SocketMode=0660 SocketUser=root SocketGroup=docker [Install] WantedBy=sockets.target

这个 socket 单元的意义在于:systemd 会提前把/var/run/docker.sock创建好并监听。这样一来,即使 dockerd 还没完全就绪,客户端连这个 socket 时 systemd 也能通过 socket activation 机制把 dockerd 拉起来。SocketMode=0660和SocketGroup=docker就决定了普通 docker 组成员能不能访问——如果你省掉这个设置,后面保准会遇到权限 denied,这是权限问题的根源之一。

再创建/etc/systemd/system/containerd.service:

[Unit] Description=containerd container runtime Documentation=https://containerd.io After=network.target local-fs.target [Service] ExecStartPre=-/sbin/modprobe overlay ExecStart=/usr/bin/containerd Type=notify Delegate=yes KillMode=process Restart=always RestartSec=5 LimitNOFILE=1048576 LimitNPROC=infinity LimitCORE=infinity TasksMax=infinity [Install] WantedBy=multi-user.target

这个单元里有几个细节值得说明。ExecStartPre=-/sbin/modprobe overlay中的短横线表示这条命令即使失败也不要影响服务启动,因为 overlay 模块在很多新内核里已经内置了,加载失败不代表有错误。Type=notify表示 containerd 会通过 sd_notify 协议主动告诉 systemd "我已经准备好",这比盲目等 timeout 要可靠得多。Delegate=yes允许 containerd 完整接管 cgroup 子树的资源管理,配合TasksMax=infinity避免进程数限制导致容器启动报 "cgroup" 相关错误。

最后创建/etc/systemd/system/docker.service,这是最核心的一个:

[Unit] Description=Docker Application Container Engine Documentation=https://docs.docker.com After=network-online.target firewalld.service containerd.service docker.socket Wants=network-online.target Requires=network-online.target docker.socket containerd.service [Service] Type=notify ExecStart=/usr/bin/dockerd -H fd:// --containerd=/run/containerd/containerd.sock ExecReload=/bin/kill -s HUP $MAINPID TimeoutStartSec=0 RestartSec=2 Restart=always LimitNOFILE=infinity LimitNPROC=infinity LimitCORE=infinity TasksMax=infinity Delegate=yes KillMode=process [Install] WantedBy=multi-user.target

解释几个关键点:

  • ExecStart里的-H fd://表示 dockerd 通过 systemd 传入的 socket fd 来接收请求,而不是自己再开一个 socket 监听。这依赖 docker.socket 单元,如果 socket 单元没写对,这里就会出现fd://无法工作的怪问题。
  • --containerd=/run/containerd/containerd.sock明确指定 dockerd 要找的 containerd socket 路径。
  • TimeoutStartSec=0表示不限制 dockerd 启动等待时间。首次启动时 dockerd 要初始化网络、存储、底层运行时等,对于大型数据盘或者特殊存储驱动,启动可能超过默认的 90 秒,不设成 0 就会看到 "timed out" 但实际服务还在初始化。
  • Restart=always配合RestartSec=2,进程异常退出后 2 秒自动拉起,这在生产环境能大大减少人工干预。

三个文件写完后,执行:

systemctl daemon-reload

每次修改 unit 文件后都必须daemon-reload,否则 systemd 还记着旧配置,改了等于白改。

3.4 daemon.json 的初始化配置

还记得前面建好的/etc/docker目录吗?现在给/etc/docker/daemon.json写入一份初始配置。我建议哪怕是临时测试环境也写上,避免后面为日志撑爆磁盘而头疼:

{ "data-root": "/var/lib/docker", "exec-opts": ["native.cgroupdriver=systemd"], "log-driver": "json-file", "log-opts": { "max-size": "10m", "max-file": "3" }, "storage-driver": "overlay2", "registry-mirrors": ["https://your-mirror-address.example.com"] }

其中exec-opts里的native.cgroupdriver=systemd是我的强烈建议。当 Docker 在 systemd 下运行时,如果 cgroup 驱动保持默认的 cgroupfs,以后要是再叠上 Kubernetes 这类编排系统,就会出现 cgroup 驱动不一致的问题,K8s 会直接拒绝调度。storage-driver=overlay2是当前 Linux 主流内核环境下的最优选择,性能比 vfs 好一个数量级。registry-mirrors留了占位符,在实际使用中替换为你所在网络环境里可用且可信的镜像加速地址,能显著缓解拉镜像慢的问题。

如果写错了 daemon.json 导致 Docker 起不来,先别慌,把配置一项项读出来检查 JSON 格式(逗号、括号最容易错),改完再启动。

4. 首次启动与全链路验证:从 systemd 到拉镜像跑容器

4.1 按依赖顺序拉起服务

一切就绪,执行:

systemctl enable --now docker.socket containerd.service docker.service

用enable --now一次性完成开机自启和立即启动。如果你担心顺序问题,可以分步:

systemctl start docker.socket systemctl start containerd systemctl start docker

启动完先用systemctl status看一眼,这是最直观的状态反馈:

systemctl status docker --no-pager -l

看到Active: active (running)基本就成功了一大半。然后测试 socket 和进程:

ls -l /var/run/docker.sock ps aux | grep -E 'dockerd|containerd'

/var/run/docker.sock的属主应该是 root,属组是 docker,权限 0660。如果 socket 不存在,说明 docker.socket 单元没生效,检查systemctl status docker.socket。

4.2 docker version 与 docker info 该看哪些字段

服务起来之后立刻验证 CLI 能不能连上 daemon:

docker version

这个命令会分两段显示:Client段和Server段。Client来自/usr/bin/docker这个二进制,Server来自 dockerd。如果 Server 段正常打印出来,说明客户端和 daemon 之间的链路、containerd 的连接都通了。如果 Server 段报错,那问题多半在 dockerd 或 containerd,需要去 journal 里翻日志。

接着看docker info的关键字段:

docker info

重点确认这几项:

  • Server Version:是否和你下载的版本一致;
  • Storage Driver:应该是 overlay2,如果变成 vfs,说明 overlay 模块有问题;
  • Cgroup Driver:systemd 模式下应该显示 systemd;
  • Docker Root Dir:确认数据目录是你预期的路径,别不知不觉写到了系统盘;
  • Containers/Running/Images:初始状态为 0,这里能快速发现是否有历史数据残留。

4.3 hello-world 验证与普通用户免 sudo

然后跑最经典的验证:

docker run --rm hello-world

这条命令背后发生了一连串事情:CLI 通过 socket 通知 dockerd,dockerd 让 containerd 去拉取 hello-world 镜像,containerd 通过 runc 创建容器,容器里输出一段欢迎信息。整条链路任何一个环节断了都会在这条命令上暴露。如果镜像拉取很慢,先检查 daemon.json 里的 registry-mirrors 是否生效,docker info里会列出当前 registry mirrors。

验证 Docker 功能正常后,处理普通用户权限。用普通用户执行docker ps时会看到经典的报错:

permission denied while trying to connect to the Docker daemon socket at unix:///var/run/docker.sock

处理方式是把自己加入 docker 组:

sudo usermod -aG docker $USER

注意,加入 docker 组后不会立刻生效,因为当前会话的组信息还停留在登录时。要么重新登录,要么在当前 shell 里执行:

newgrp docker

newgrp这条命令很多人不知道,它能直接刷新当前终端进程的组身份,不用退出重登,省了很多事。之后再用docker ps就正常了。这里多说一句安全话题:docker 组等同于 root 权限,因为它可以挂载宿主机目录、操作特权容器,所以把哪些账号加进 docker 组要慎重,开发机无所谓,生产机上尽量只给运维账号。

到这里,一套由二进制通用包搭建的 Docker 已经完整跑起来了。但这只是开始,真正有价值的是后面遇到问题时怎么把故障拆解掉。

5. 启动失败与权限问题:一线排查的完整思路

5.1 第一步永远是看日志而不是瞎重启

服务起不来的时候,我见过太多人第一反应是systemctl restart docker,但这基本等于蒙着眼睛开车。正确的第一步是看日志:

journalctl -u docker -n 100 --no-pager journalctl -u containerd -n 100 --no-pager

如果日志里信息不够,dockerd 还可以用前台调试模式跑,这样所有报错都会打在终端上,信息密度比 journal 高很多:

systemctl stop docker dockerd --debug

前台模式下,你能看到 dockerd 一步步初始化:加载配置、初始化存储驱动、创建网桥、连接 containerd。卡在哪一步,错误就在哪一行。看到报错后按 Ctrl+C 停掉,再修复问题。这个方法对一切"启动失败但原因扑朔迷离"的场景都有效,建议每个人都练几遍,比背命令有用得多。

5.2 "Cannot connect to the Docker daemon" 的处理路径

客户端报Cannot connect to the Docker daemon at unix:///var/run/docker.sock. Is the docker daemon running?时,不要急着怀疑 daemon,先做三件事:

第一,确认 socket 文件是否存在:

ls -l /var/run/docker.sock

不存在说明 docker.socket 没起来,或者启用了但被 systemd 移除了。检查systemctl status docker.socket。

第二,确认 socket 是否挂在 systemd 手里:

systemctl list-sockets | grep docker

如果 socket 存在但 docker.service 没启动,所有连接会在 systemd 层面被 accept,然后触发 docker.service 启动。如果 docker.service 启动失败,连接就会报错。这时候看前面提到的 journal 日志,找 dockerd 的启动错误。

第三,确认 DOCKER_HOST 环境变量有没有被污染:

echo $DOCKER_HOST

有些教程或者脚本会把DOCKER_HOST设成远程 daemon 地址,一旦被设成了非本机地址,客户端就会去连一个不存在的地址。清掉这个变量或用docker -H unix:///var/run/docker.sock ps强制指定就是另一个世界。

5.3 权限 denied:docker 组与 socket 模式的细节

权限问题的典型报错就是 5.3 节开头说的permission denied while trying to connect to the Docker daemon socket。它的根源通常是用户不在 docker 组,或者 socket 权限设置不对。

先验证你的用户在哪个组:

id sudo -u $USER groups

如果不在 docker 组里,按前面 4.3 的方法加组。如果已经在组里还报权限错误,那就看 socket 权限:

ls -l /var/run/docker.sock

期望结果是srw-rw----且 root 用户、docker 组。如果你的 socket 权限是 0600 或者其他异常值,说明 docker.socket 单元里的SocketMode=0660没写,或者后来有别的进程改动过 socket 权限。修复方式有两种:改 unit 文件然后重启 docker.socket,或者手动chmod 0660 /var/run/docker.sock。前者是治本,后者是应急。

还有一个隐蔽坑:在某些精简系统上,docker 组可能不存在,但 unit 文件里写了SocketGroup=docker,systemd 在创建 socket 时发现组不存在,会把组设置成一个不正常的值。所以前面提到getent group docker || groupadd docker的预检查要在写 unit 文件之前就做掉。

5.4 网络与存储驱动:两个高频翻车点

网络问题主要集中在宿主机上看到 docker0 网桥没有创建,或者容器之间互通异常。dockerd 启动时会自动创建 docker0,并往 iptables 里写入 NAT 和 FORWARD 规则。常见故障场景:

  • 宿主机 netfilter 的 FORWARD 链策略被某些安全软件改成了 DROP,导致跨容器、容器外网流量全部被丢。解决办法是显式放行:
iptables -P FORWARD ACCEPT

这只是临时做法,更持久的做法是把规则写进防火墙配置里,或者研究清楚是谁改了默认策略。

  • 缺少br_netfilter内核模块时,Docker 网络会出现各种诡异问题:
modprobe br_netfilter sysctl -w net.bridge.bridge-nf-call-iptables=1
  • 云平台环境常见的坑是主网卡没有开启混杂模式或受云防 ARP 限制,docker0 通了但容器出不去,这时排查顺序是:宿主机ping 8.8.8.8是否通、容器里ping 宿主机是否通、iptables -t nat -L POSTROUTING里有没有容器网段的 MASQUERADE 规则。

存储驱动翻车则主要围绕 overlay2。如果你的内核开启了 module 加载限制,或者使用了一个不支持 overlay 的文件系统(比如把 Docker 数据目录放在某个云盘的只读挂载点上),dockerd 启动会退化成 vfs 驱动,性能大打折扣。验证方法:

cat /proc/filesystems | grep overlay

有 overlay 输出说明内核支持。如果不支持,可以尝试modprobe overlay;还不行,就需要检查内核版本,或者换一个数据目录位置。用vfs跑生产环境不是不行,但容器层文件变化时是完整拷贝,镜像体积和 IO 消耗会非常感人。

5.5 常见报错速查表

下面把我在一线见过的高频报错整理成速查表,遇到问题先对照:

报错特征可能原因处理方向
Cannot connect to the Docker daemondaemon 未启动、socket 不存在、DOCKER_HOST 错误查 socket、journal、环境变量
permission denied...docker.sock用户不在 docker 组、socket 权限被改usermod -aG,chmod 0660,newgrp
failed to dial gRPC: containerdcontainerd 未启动、socket 路径不一致启动 containerd,核对 dockerd 参数
error creating overlay mount内核无 overlay 模块、数据目录文件系统不支持modprobe overlay,换存储驱动
iptables failed/docker0 not foundFORWARD 策略、br_netfilter 缺失、firewalld 冲突放行 FORWARD、加载模块、处理防火墙
error saving image...operation not permitteddaemon.json 路径权限错误、磁盘只读检查 /var/lib/docker 权限与挂载
Device cgroup isn't mountedcgroup 挂载异常(老内核/容器内嵌套)检查 /sys/fs/cgroup 挂载状态
cgroup v2: systemd driver vs cgroupfs后续加 K8s 时驱动不一致daemon.json 里统一为 systemd

排查的核心思路永远是:先看日志、再查链路、最后动配置。别一上来就docker system prune或者重装系统,大部分问题都是配置层面的,日志里都写着答案。

6. 版本升级、回滚与日常维护:把二进制安装跑成长久方案

6.1 升级操作的标准动作

用二进制包方案的长期运行,绕不开升级和回滚。升级的标准动作其实很简单:

# 1. 停止 Docker 相关服务 systemctl stop docker.socket docker.service containerd.service # 2. 下载新版本 tarball 并校验(过程参考第二节) cd /opt/software wget https://download.docker.com/linux/static/stable/x86_64/docker-27.4.1.tgz sha256sum docker-27.4.1.tgz # 3. 解压并覆盖旧二进制 tar xzf docker-27.4.1.tgz cp docker/* /usr/bin/ # 4. 重新加载并启动 systemctl daemon-reload systemctl start containerd docker.socket docker.service

升级前重点检查两件事。一是兼容性:新版本 dockerd 一般能兼容旧版本数据目录,但跨大版本升级(比如 20.x 直接到 27.x)建议先看官方 release notes 里是否提及存储格式或配置变更。二是containerd 版本:二进制 tar 包里的 containerd 是和 dockerd 配好套的,升级时应该一起覆盖。最忌讳的是系统里原来有一个 apt 装的 containerd,你又拿 tar 包的 containerd 覆盖了一半,最后两套版本混着用,保准出怪问题。

6.2 回滚预案

升级回滚是二进制方案的天然优势。只要升级前备份了旧版本二进制,回滚就是几个命令的事:

# 假设升级前你把旧二进制都留在了 /opt/docker-backup systemctl stop docker.socket docker.service containerd.service cp /opt/docker-backup/* /usr/bin/ systemctl daemon-reload systemctl start containerd docker.socket docker.service

所以关键动作在升级前就要做:

mkdir -p /opt/docker-backup cp /usr/bin/docker /usr/bin/dockerd /usr/bin/containerd /usr/bin/runc /usr/bin/ctr /usr/bin/docker-init /usr/bin/docker-proxy /usr/bin/containerd-shim-runc-v2 /opt/docker-backup/

另外,升级前尽量做一次数据层面的快照。/var/lib/docker里存放着容器和镜像,数据卷也在这里。虽然版本回滚一般不影响已有数据,但万一升级过程中数据目录被新版本 daemon 做了迁移,没有备份就只能干瞪眼。数据目录的备份不需要停服务,用tar --exclude排除运行时文件也能凑合,最稳的还是配合存储的快照能力。

6.3 日志、磁盘与孤儿资源

长期运行的 Docker 宿主机,最容易被忽视的是日志和磁盘。daemon.json 里配置的日志轮转参数要尽早配好。一旦没配,容器里程序疯狂打日志,json-file 日志文件可能以 GB 计的速度增长,直接撑爆系统盘。检查当前各容器日志大小的命令:

du -sh /var/lib/docker/containers/*/*-json.log | sort -rh | head

如果已经积累了大量日志,可以即时清掉(容器还在运行,清完会继续写):

truncate -s 0 /var/lib/docker/containers/<container-id>/*-json.log

镜像和容器长期增删后,悬空镜像和停止的容器会占用不少磁盘。定期清理是必要的:

docker system prune -f

但注意-a参数会把所有未被容器使用的镜像都删掉,包括你想本地留档的版本。我自己一般只执行不带-a的docker system prune,版本镜像保留用docker image tag打上明确的版本号,避免误删。

6.4 几点长期经验

最后分享几条我用二进制方式维护 Docker 的长期经验:

一是保留一套固定的安装脚本。把下载、校验、铺装、unit 文件生成写成一个幂等 shell 脚本,放到服务器上,每次新机器处理直接跑一遍。这样能保证每台机器环境一致,出问题也好对比。

二是记录版本与校验值。我习惯在安装目录里放一个version.txt,记录安装/升级的时间、版本号、sha256 值。等到这台机器变成事故现场时,这些记录可能就是唯一能帮助你确认"当初到底装了什么"的依据。

三是关注 containerd 和 runc 的安全通告。二进制方案没有包管理器帮你跟踪 CVE,所以最好定期关注 Docker 官方 release 页面,确认当前稳定分支有没有重要的安全修复。这个成本不高,但收益很高。

四是别把 systemd 单元当一次性文件。它和 daemon.json 一样都是这台机器的配置资产。我通常会把这三个 unit 文件和 daemon.json 一起纳入配置管理仓库(Git 或 Ansible 都行),任何修改走版本化流程,而不是直接在机器上改。需要回滚配置时,比什么都好使。

我从第一台用二进制包方式部署 Docker 的机器算起,这套方案已经稳定跑了三四年,期间经历了多次大版本升级,既没有被包管理器的依赖绑架过,也没有因为某次自动升级导致集群不可用。每个方案都有自己的适用场景,但如果你和我一样要管理多个发行版混合的环境、又不想被系统包仓库牵着鼻子走,二进制通用包这条路,值得你认真走一遍。

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

连锁门店串口设备上云:网关数量与部署位置怎么算?

去年帮一个连锁烘焙品牌做设备改造&#xff0c;门店里的智能电表、后厨冷柜温控器、前场温湿度记录仪&#xff0c;清一色的串口设备。总部想远程统一监控&#xff0c;但设备本身没有网口&#xff0c;数据全靠店长每天拍照上传&#xff0c;数据真假且不说&#xff0c;光是整理就…

作者头像 李华
网站建设 2026/9/26 12:01:30

Bc_ChckenPrnce 配 TaoToken:settings.json 骨架与报错排查

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/26 12:01:19

UltraISO制作启动U盘全指南:从引导写入到BIOS设置与排错

1. 为什么都2025年了&#xff0c;我还是推荐UltraISO做启动盘先说个反直觉的事实&#xff1a;现在市面上做启动U盘的工具一大堆&#xff0c;Rufus、Ventoy、balenaEtcher各有拥趸&#xff0c;但如果你常年在帮人装机、维护老机器、或者折腾各种Linux发行版&#xff0c;UltraISO…

作者头像 李华
网站建设 2026/9/26 11:59:59

Spring Boot实现Java电子签章:数字签名与PDF落章完整指南

简介&#xff1a;面向Java开发者的电子合同电子签章实现项目&#xff0c;基于Spring Boot搭建&#xff0c;聚焦PDF合同数字签章生成与校验场景。压缩包为zip格式&#xff0c;约72KB&#xff0c;包内文件明细暂未提供&#xff0c;适合希望掌握电子签章集成、PDF盖章处理及数字签…

作者头像 李华
网站建设 2026/9/26 11:57:41

用Docker Compose部署OnlyOffice:架构、配置与避坑实践指南

在公司内网搭一套能在线打开、编辑、协同处理 Word、Excel、PPT 的文档服务&#xff0c;OnlyOffice 几乎是最绕不开的选择&#xff1b;只要你的机器上装了 Docker&#xff0c;用 Docker Compose 部署 OnlyOffice 又是目前公认最省心的一条路。官方镜像把 Nginx、Node.js、Postg…

作者头像 李华