最近在测试环境搭一套基于容器的新服务,需要用到最新的 Docker 引擎。我习惯性地去 apt 源里找,结果发现软件源里还是 24.x 的老版本。Docker 官方其实很早就调整了策略,docker-ce.repo 里如果没主动 enabled 最新版本,默认情况下拿到的也未必是最新的 26.x。与其等着源更新,不如直接用最稳妥的二进制安装方式,指定版本安装 Docker 26.1.4。
选择 Docker 26.1.4 而不是盲目追新,是因为这个版本属于 26.x 系列里比较稳定的一个迭代。26.0 刚出来的时候 containerd 1.7.x 的集成还有些小问题,到了 26.1.x 已经把这些坑基本填平了。这篇文章就完整记录一下我在干净系统上从零安装 Docker 26.1.4 的全过程,包括环境准备、二进制安装、systemd 配置、镜像加速、常见故障排查这几个关键环节。不管你是 Ubuntu、Debian 还是 CentOS,只要内核满足要求,这套流程都能用。
1. 为什么选择二进制方式手动安装 Docker 26.1.4
1.1 包管理器安装和官方二进制安装的差异
大多数人在 Linux 上装 Docker,第一反应都是 apt install docker.io 或者 yum install docker-ce。这两种方式的确方便,但有各自的限制。
apt 源里自带的 docker.io 版本严重滞后,Ubuntu 24.04 的官方源里现在还是 Docker 24.x,很多新特性根本用不上。而 docker-ce.repo 虽然是官方源,但 DNF/YUM 的缓存机制会导致你明明已经添加了官方源,安装时却拿不到最新版本,尤其是国内服务器访问官方源经常超时,版本同步也是个大问题。
直接下载 Docker 官方编译好的二进制压缩包是最可控的方式。这种方式有几个好处:
- 版本完全可控,想装 26.1.4 就装 26.1.4,不会因为源的问题被强制升级或降级
- 不依赖系统包管理器的依赖解析,避免和系统自带的 containerd 或 runc 产生冲突
- 安装路径固定,卸载的时候直接删目录就行,不留垃圾
- 适用于离线环境,把 tar 包下载好拷到内网就能装
1.2 Docker 26.x 系列版本特性回顾
既然要装 26.1.4,就值得了解一下这个版本处于什么位置。Docker Engine 26.0 是 2024 年 4 月发布的,属于比较大的版本跳变,包含了一些 API 层面的更新和 containerd 集成的调整。26.1.x 是它的修复版本,主要解决了 26.0 中 docker build 在某些场景下缓存异常、IPv6 端口映射的一些边界问题,以及 docker compose 和 engine 之间的兼容性细节。
26.1.4 这个具体版本,修复了 docker run 在特定 cgroup v2 环境下的资源限制警告问题。这个坑你在 26.0 上跑容器用 --memory 参数限内存时可能会遇到,日志里会莫名出现 MemoryLimit 相关的 warning,虽然不影响运行,但看着很烦。到了 26.1.4 就干净了。
另外一个值得注意的点是,Docker 26.1.4 开始对 --iptables 的默认行为做了更严格的校验。如果你在宿主机上开启了 firewalld 或者 ufw,Docker 启动时可能会提示 iptables 规则冲突。这个后面在故障排查部分我会详细说。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 环境准备:内核要求、依赖清理和网络检查
2.1 内核版本要求:为什么必须 3.10 以上
Docker 的所有核心功能,包括容器隔离、资源限制、网络命名空间,都依赖 Linux 内核的 namespace 和 cgroup 特性。老内核虽然在功能上能用,但在 cgroup v2、overlay2 存储驱动、iptables 新语法这些关键模块上会有兼容性问题。
Docker 官方要求内核最低 3.10,但那是针对 RHEL/CentOS 6 时代的说法了。实际使用中,我建议内核至少 4.18 以上,因为 Docker 26.x 默认推荐使用 overlay2 存储驱动,而 overlay2 在 4.x 早期版本上还有些性能问题,到 4.18 才基本成熟。
检查内核版本:
bash复制uname -r
如果内核版本太低,比如 CentOS 7 默认的 3.10,你虽然能装上 Docker 26.1.4,但建议升级内核,不然运行高并发容器时,文件系统 I/O 会成为瓶颈。
2.2 移除旧版本 Docker 组件,避免环境冲突
这一步很多人会跳过,但我觉得必须做。因为旧版本的 Docker 会留下 docker0 网桥配置、残留的 iptables 规则、/var/lib/docker 目录里旧格式的数据,以及 /etc/docker/daemon.json 里不兼容的配置项。
如果你之前用过包管理器安装的 Docker:
bash复制# Ubuntu / Debian
sudo apt remove docker docker-engine docker.io containerd runc
# CentOS / RHEL
sudo yum remove docker docker-client docker-common docker-engine docker-ce
注意,这个操作不会删除 /var/lib/docker 目录,也就是说镜像、容器、卷都还保留着。如果你确定不要这些数据了,可以手动删掉:
bash复制sudo rm -rf /var/lib/docker
sudo rm -rf /var/lib/containerd
如果你不确定,就暂时别删,装好新版 Docker 后它会在启动时自动做数据格式的迁移。
2.3 网络连通性和 DNS 检查
二进制安装 Docker 本身不需要网络,但下载那个压缩包需要网络。检查一下你能否访问 Docker 官方的下载地址(需要能正常访问外网),如果服务器在国内,我建议直接使用国内镜像站下载压缩包,速度会快很多。下面是几个可用的下载地址:
- Docker 官方 GitHub Releases(需要能访问外网)
- 国内一些云厂商的开源镜像站提供的 docker 二进制包
检查 DNS 解析是否正常:
bash复制getent hosts download.docker.com
如果 DNS 解析超时,先检查 /etc/resolv.conf 里的 nameserver 配置,改成 223.5.5.5(阿里 DNS)或者 119.29.29.29(腾讯 DNS)再试。
注意:安装 Docker 需要的文件和依赖都来自官方发布渠道,务必从可信源获取安装文件。安装完成后如需拉取镜像,建议配置镜像加速,减少因网络问题导致的超时。
3. Docker 26.1.4 二进制安装完整步骤
3.1 下载并解压 Docker 二进制包
我这里以 Linux x86_64 架构为例。先确认自己的系统架构:
bash复制uname -m
正常服务器输出应该是 x86_64,如果是 ARM 架构的服务器(比如华为鲲鹏、AWS Graviton),下载地址里的架构名要换成 aarch64。
下载 Docker 26.1.4 的二进制压缩包:
bash复制cd /tmp
curl -fsSL https://download.docker.com/linux/static/stable/x86_64/docker-26.1.4.tgz -o docker-26.1.4.tgz
如果你服务器访问 download.docker.com 速度很慢,或者直接超时,可以用国内的镜像源。比如用阿里云的镜像路径下载(路径结构与 Docker 官方保持一致):
bash复制wget https://mirrors.aliyun.com/docker-toolbox/linux/x86_64/docker-26.1.4.tgz
下载完成后,先校验一下文件完整性。虽然 tgz 包没有官方校验和文件(Docker 官方偷懒,不提供单独的 sha256 文件),但你可以对比下载大小是否和官方标示的一致,或者用解压后的二进制文件跑一下版本命令来间接验证。
解压:
bash复制tar -zxvf docker-26.1.4.tgz
解压后得到一个 docker 目录,里面有这些文件:
text复制docker/docker
docker/dockerd
docker/containerd
docker/containerd-shim-runc-v2
docker/crictl
docker/ctr
docker/docker-init
docker/docker-proxy
docker/runc
这里解释一下每个组件的用途:
dockerd:Docker 守护进程,也就是常说的 Docker Engine,所有容器生命周期管理都靠它docker:命令行客户端,你敲的所有docker命令都是和它交互containerd:容器运行时,负责真正启动和停止容器runc:底层容器运行时,根据 OCI 规范创建容器进程docker-init:容器内 1 号进程的初始化工具,负责回收孤儿进程docker-proxy:端口映射代理,负责容器端口到宿主机的转发
把二进制文件拷贝到 /usr/bin 目录:
bash复制cd docker
sudo cp docker dockerd containerd containerd-shim-runc-v2 crictl ctr docker-init docker-proxy runc /usr/bin/
注意 containerd-shim-runc-v2 这个文件在较老版本的安装教程里是没有的,26.x 版本引入的 containerd 1.7.x 需要它。如果你只拷贝了 containerd 没拷贝 shim,后面启动容器会报 failed to start shim 的错误。
验证安装是否成功:
bash复制docker --version
dockerd --version
正常输出应该是:
text复制Docker version 26.1.4, build 5650f9b
containerd containerd.io 1.7.16
3.2 创建 Docker 用户组和运行用户
安全起见,Docker 的守护进程和客户端不建议直接用 root 运行。虽然 install 脚本会自动创建 docker 用户组,但二进制安装方式不会,需要手动创建。
bash复制sudo groupadd docker
sudo useradd -r -g docker -s /sbin/nologin docker
这里的 -r 表示创建系统用户,-s /sbin/nologin 表示禁止该用户登录系统。这个 docker 用户不是让你用来敲命令的,而是 dockerd 守护进程的运行身份。虽然实际上 Docker 官方推荐的是让 root 来运行 dockerd,然后通过 docker 组来授权普通用户访问,但我更倾向于给守护进程单独建用户,这样即使容器爆发逃逸漏洞,攻击者拿到的也只是低权限用户,而不是 root。
不过这里有个矛盾点:如果你用非 root 用户运行 dockerd,那么容器运行时的权限管理会变复杂。实际大多数服务器场景下,大家还是习惯让 dockerd 以 root 身份运行,靠 docker 组来控制客户端访问权限。所以上面的 useradd 步骤可以改成只创建组:
bash复制sudo groupadd docker
然后等 Docker 跑起来后,把需要执行 docker 命令的普通用户加入 docker 组:
bash复制sudo usermod -aG docker $USER
注意:把用户加入 docker 组等于赋予该用户 root 权限,因为 docker 组用户可以通过挂载宿主机目录等方式拿到宿主机文件系统的控制权。生产环境请谨慎使用这种方式授权。
3.3 配置 systemd 管理 dockerd 和 containerd
Docker 的二进制包不包含 systemd service 文件,需要手动编写。创建 /etc/systemd/system/docker.service:
写这个服务文件时,有几个细节需要特别注意:
ExecStart路径要和前面拷贝的二进制路径一致,否则服务启动失败-H unix:///var/run/docker.sock指定 Docker socket 位置,这是客户端和守护进程通信的默认通道- 不要加
-g /data/docker之类的自定义参数,这一步放到 daemon.json 里管理更规范
3.4 配置 daemon.json
创建 /etc/docker/daemon.json,这是 Docker 守护进程的核心配置文件。对于 26.1.4,我推荐这个配置:
json复制{
"exec-opts": ["native.cgroupdriver=systemd"],
"log-driver": "json-file",
"log-opts": {
"max-size": "100m"
},
"storage-driver": "overlay2",
"data-root": "/var/lib/docker",
"ip-forward": true,
"iptables": true,
"ip-masq": true,
"registry-mirrors": ["https://docker.m.daocloud.io"]
}
逐项解释一下为什么要这么配:
exec-opts: ["native.cgroupdriver=systemd"]:这行极其关键。Docker 默认使用cgroupfs作为 cgroup 驱动,但如果宿主机上同时跑着 systemd(几乎所有主流 Linux 发行版都默认用 systemd),就会出现双 cgroup 管理器的问题。当你在 Kubernetes 里使用 Docker 作为运行时(虽然现在 K8s 1.24+ 已经默认用 containerd 了,但仍有大量存量集群在用 Docker),这会导致节点上 Pod 的资源限制不生效。改成 systemd 驱动后,cgroup 的创建和管理就统一交给 systemd,避免很多诡异问题。log-driver: json-file+max-size: 100m:默认的 json-file 日志驱动不做任何轮转,容器写多少日志就占多少磁盘。如果你跑了个容器忘记限制日志,几天下来/var/lib/docker/containers目录可能塞满几十 GB 的日志文件。加上max-size: 100m后,每个容器日志达到 100MB 就会自动轮转。这一步对于磁盘空间紧张的环境是救命级别的配置。storage-driver: overlay2:overlay2 是 Docker 26 的默认存储驱动,但显式声明可以避免某些特殊环境下自动检测机制误判。比如在 XFS 文件系统上,如果不显式声明,某些旧版内核会回退到 vfs 驱动,性能会差很多。iptables: true和ip-masq: true:保持默认即可。如果宿主机上有防火墙,想要完全接管 Docker 的网络规则可以关闭这两个选项,但默认开启对绝大多数场景是安全的。
3.5 启动服务并设置开机自启
bash复制sudo systemctl daemon-reload
sudo systemctl enable docker
sudo systemctl start docker
查看服务状态:
bash复制sudo systemctl status docker
正常状态应该是 active (running)。再确认一下 docker0 网桥是否创建成功:
bash复制ip addr show docker0
如果你看到 docker0 接口存在且状态为 UP,说明网络模块初始化成功。
4. 镜像加速配置:解决镜像下载慢的痛点
4.1 为什么镜像下载会慢
Docker Hub 官方仓库的服务器在海外,国内直连下载镜像时,经常遇到几 KB/s 的龟速,甚至直接超时。这不是你网络的问题,而是国际带宽出口拥塞导致的。
镜像加速的原理是:通过在境内维护 Docker Hub 的缓存节点,让用户从就近的节点拉取镜像,大幅缩短传输路径。目前主流的公共加速器有:
| 加速器地址 | 提供方 | 备注 |
|---|---|---|
| 由你的云服务商提供 | 阿里云/腾讯云/华为云 | 需要登录控制台获取专属地址 |
| 开源社区公共加速器 | DaoCloud | 无需登录,即开即用 |
需要说明的是,公共加速器随时可能因运营调整而不可用,如果你用的是云服务器,我更推荐在云厂商的控制台里找到容器镜像服务,使用他们提供的专属加速地址,速度和稳定性都更好。
4.2 在 daemon.json 中配置 registry-mirrors
在刚才的 daemon.json 基础上,加上 registry-mirrors 字段:
json复制{
"registry-mirrors": ["https://docker.m.daocloud.io"]
}
修改完成后,重启 Docker:
bash复制sudo systemctl daemon-reload
sudo systemctl restart docker
验证加速器是否生效:
bash复制docker info | grep -A 10 "Registry Mirrors"
如果看到你配置的镜像地址出现在 Registry Mirrors 下面,说明配置成功。
提示:加速器只对 Docker Hub 官方镜像生效,如果你拉取的是 quay.io、gcr.io 等其他仓库的镜像,需要配置对应仓库的加速或代理方案。
4.3 实测拉取镜像速度
加速器配置完成后,拉个 nginx 镜像测试一下:
bash复制time docker pull nginx:alpine
有加速器的情况下,国内服务器拉 nginx:alpine 通常在 5~10 秒内完成。如果没有加速器,同样的镜像可能要等好几分钟。
5. 安装后的系统配置与验证
5.1 kernel 参数调优
Docker 正常运行需要一些内核参数配合。虽然默认值在大多数情况下够用,但在高并发场景下需要调整为更合适的值。编辑 /etc/sysctl.conf:
bash复制net.bridge.bridge-nf-call-iptables = 1
net.ipv4.ip_forward = 1
net.ipv4.conf.all.rp_filter = 1
net.ipv4.ip_forward 是整个 Docker 网络的核心。Docker 容器之间的通信、容器访问外网、端口映射,全部依赖这个参数。如果不开启,容器即使启动了也无法访问外部网络。
net.bridge.bridge-nf-call-iptables 控制 iptables 规则是否能作用于 Linux 网桥上的数据包。Docker 的容器间网络隔离依赖 iptables 规则,如果不开启,同一台宿主机上不同 Docker 网络之间的隔离就形同虚设。
使配置生效:
bash复制sudo sysctl -p /etc/sysctl.conf
5.2 运行测试容器验证整体功能
装好之后,最直观的验证方式是跑一个容器试试。
bash复制sudo docker run --rm -d -p 8080:80 --name nginx-test nginx:alpine
然后访问宿主机的 8080 端口看看有没有返回 nginx 的默认页面:
bash复制curl localhost:8080
看到 Welcome to nginx! 的 HTML 页面,说明从内核到网络到镜像拉取,整个链路都是通的。
测试完清理容器:
bash复制sudo docker rm -f nginx-test
5.3 常用运维命令速查
安装好 Docker 26.1.4 之后,有几个日常运维时高频用到的命令,列出来供参考:
- 查看容器运行状态:
docker ps -a - 进入运行中的容器:
docker exec -it <container_id> /bin/bash - 查看容器日志:
docker logs -f --tail 100 <container_id> - 一键清理所有停止的容器:
docker container prune - 清理所有悬空镜像:
docker image prune -a - 实时查看资源占用:
docker stats
6. 常见问题排查与解决方案
6.1 启动时报 cgroup 相关错误怎么办
如果你在启动 dockerd 时看到类似 failed to initialize kubelet cgroup driver 或者 cgroup parent 设置冲突 的报错,大概率是 cgroup 驱动不一致导致的。
解决方法:
-
检查当前系统的 cgroup 版本:
bash复制stat -fc %T /sys/fs/cgroup/输出是
cgroup2fs说明系统用的是 cgroup v2,如果输出是tmpfs说明是 cgroup v1。 -
检查 Docker 当前使用的 cgroup 驱动:
bash复制
docker info | grep -i cgroup -
确保执行 Docker 启动命令的进程和 dockerd 的驱动一致。Kubernetes 场景下,kubelet 使用 systemd 驱动,Docker 也必须是 systemd 驱动;反之都用 cgroupfs 也行。
6.2 端口映射不生效:IPVS 和 iptables 的冲突
有一种比较隐蔽的问题:宿主机上开启了 IPVS 模式的服务(比如 kube-proxy),导致 Docker 的端口映射失效,外部访问不到容器。
排查思路如下:
先看 dockerd 启动日志里有没有这样一行:
text复制Failed to load iptables module "ip_tables"
如果有,说明内核的 iptables 模块没有被正确加载。你可以尝试手动加载:
bash复制sudo modprobe ip_tables
sudo modprobe iptable_nat
sudo modprobe br_netfilter
然后重新启动 Docker。
如果你发现系统里已经启用了 IPVS,而且不想改 kube-proxy 的配置,那就在 daemon.json 里把 iptables 设为 false,告诉 Docker 不要尝试管理 iptables 规则。但这样做的代价是 Docker 的网络隔离和端口映射全部失效,你需要自己用其他方式管理,所以一般情况下我更推荐回退到 iptables 模式。
6.3 overlay2 存储驱动初始化失败
报错信息通常是 failed to mount overlay: no such file or directory。这个排查思路是检查文件系统类型。
bash复制df -T /var/lib/docker
如果输出显示 /var/lib/docker 所在分区是 xfs,且 ftype=0,那就找到根因了。XFS 文件系统在格式化时如果没开启 ftype 支持(老内核默认可能没开),就无法承载 overlay2 的 overlayfs 挂载。
解决办法有两个:
- 如果数据盘还没写入重要数据,直接重新格式化:
bash复制
mkfs.xfs -f -n ftype=1 /dev/sdb1 - 或者换一个存储驱动,把 daemon.json 里的
storage-driver改成vfs,但性能会明显下降,不推荐。
6.4 容器内部 DNS 解析失败
这个坑很经典。容器能 ping 通 IP,但 ping www.baidu.com 报 Temporary failure in name resolution。
原因通常是 Docker 自带的嵌入式 DNS 服务(127.0.0.11)无法代理容器内的解析请求。最简单的排查方法:
bash复制docker exec <container_id> cat /etc/resolv.conf
如果看到 nameserver 127.0.0.11 说明用的 Docker 内置 DNS。此时可以在 docker run 时加上 --dns 223.5.5.5 手动指定 DNS,或者修改 daemon.json 加上:
json复制{
"dns": ["223.5.5.5", "119.29.29.29"]
}
这种方式绕过了 Docker 的嵌入式 DNS,直接把请求发给公网 DNS。代价是容器内服务发现能力会受限,比如用 docker-compose 部署的多容器应用可能无法通过服务名互相访问。生产环境建议结合自己的业务情况权衡。
6.5 磁盘空间被容器日志占满
虽然前面配置了 max-size: 100m 的日志轮转,但如果你之前用的是默认配置,日志文件已经很大了,清理方式是:
bash复制sudo sh -c "truncate -s 0 /var/lib/docker/containers/*/*-json.log"
这个命令会把所有容器日志文件截断为空,但不会删除文件本身,所以不影响 Docker 的正常读写。如果你想让日志轮转立即生效,需要重建容器,因为日志轮转配置是在容器创建时读取的。
注意:千万不要直接
rm容器日志文件,因为 Docker 持有该文件的文件句柄,删除后即使容器还在写,你也看不到日志了,而且磁盘空间也不会立即释放。truncate才是正确操作。
7. Docker Compose 配套安装
7.1 docker-compose-plugin 的安装
现在新的趋势是使用插件形式的 docker compose(没有横杠),旧版的 docker-compose(带横杠)虽然还能用,但官方已经不再推荐了。26.1.4 的二进制包自带了 compose 插件支持,只需要把插件放到指定目录即可。
在 Makefile 里或者官方 Linux 包中,compose 插件以独立二进制提供。安装方式:
把 docker-compose 插件二进制放到:
bash复制sudo mkdir -p /usr/lib/docker/cli-plugins
sudo cp docker-compose-plugin /usr/lib/docker/cli-plugins/docker-compose
sudo chmod +x /usr/lib/docker/cli-plugins/docker-compose
验证:
bash复制docker compose version
正常会输出类似 Docker Compose version v2.27.1 的信息。
7.2 docker compose 的 basic 使用
有了 compose 后,日常搭建多容器环境会方便很多。比如快速部署一个 MySQL + Redis 的开发环境:
yaml复制version: '3.8'
services:
mysql:
image: mysql:8.0
container_name: mysql-dev
environment:
MYSQL_ROOT_PASSWORD: root123
MYSQL_DATABASE: testdb
ports:
- "3306:3306"
volumes:
- mysql_data:/var/lib/mysql
restart: always
redis:
image: redis:7
container_name: redis-dev
ports:
- "6379:6379"
restart: always
volumes:
mysql_data:
启动所有服务:
bash复制docker compose up -d
查看服务状态:
bash复制docker compose ps
这个过程中你会发现,compose 插件和 Docker 引擎的配合在 26.1.4 上非常丝滑,服务名的 DNS 解析、网络隔离、依赖启动顺序都自动处理好了,不需要像以前那样手动 docker network create 再手动链接。
8. 本次安装踩过的坑和几点经验总结
这套安装流程我前前后后在不同系统上跑过不下十遍,每次都会踩到一些意想不到的问题。挑几个印象最深的分享一下:
第一个是 systemd 服务文件里的 ExecStartPost 配置。我一开始写完 docker.service 后,就想当然地往里面加了一段 ExecStartPost 去设置内核参数,结果导致服务反复重启失败。后来才知道 ExecStartPost 里如果写的是会阻塞的命令,systemd 会认为服务启动超时。正确的做法是把内核参数调优全部放到 sysctl.conf 里,不要和 systemd 服务启动混在一起。
第二个是 containerd 的 socket 路径冲突。如果你系统里之前装过 containerd(比如作为 Kubernetes 组件装的),它的 socket 文件在 /run/containerd/containerd.sock,Docker 自带的 containerd 默认也用这个路径。两者共存时,后起的那个会报 address already in use。解决方式是在 docker.service 的 ExecStart 里给 containerd 指定一个独立的 socket 路径,或者干脆卸载掉系统自带的 containerd。
第三个是关于 docker info 里显示 WARNING: No blkio throttle support 的老问题。这个警告在内核开启了 cgroup v2 但未开启 blkio 相关的控制器时会看到,不影响正常使用,但多少让人心里不踏实。检查方式是看 /sys/fs/cgroup/cgroup.controllers 文件里有没有 io 字样。没有的话,在内核启动参数里加 cgroup_enable=io 重启机器就能解决。
最后再分享一个运维层面的习惯:装好 Docker 后,第一时间把 /etc/docker/daemon.json、/etc/systemd/system/docker.service 和 docker version 的输出归档到你们的配置管理仓库里。这样哪台机器出了问题需要重建,十分钟就能照着重现一模一样的 Docker 环境。这也是二进制安装相比包管理器安装一个隐形的好处——所有配置都在这几个文件里,没有散落在各种 /etc/default/ 和 /etc/sysconfig/ 里的隐藏项,迁移和克隆都特别干净。
