1. 背景
在 Docker 环境中,我们通常会通过:
bash
docker info | grep "Docker Root Dir"
查看 Docker 的数据目录。
例如:
text
Docker Root Dir: /data/docker
Storage Driver: overlayfs
Logging Driver: json-file
看到这里很容易产生一个疑问:
Docker 的数据目录已经是
/data/docker,为什么/var/lib/containerd还会不断增长?
实际上,Docker Root Dir 和 containerd 的数据目录并不是同一个目录。
在当前 Docker + containerd 架构中,Docker 会使用 containerd 管理镜像内容、镜像层以及 snapshot 等数据。因此,即使 Docker Root Dir 已经迁移到 /data/docker,containerd 仍然可能默认使用:
text
/var/lib/containerd
如果镜像较大、容器较多或者存在大量构建缓存,/var/lib/containerd 很容易达到几十甚至上百 GB。
2. Docker Root Dir 与 containerd 数据目录
可以简单理解为:
text
Docker Engine
│
┌───────────┴───────────┐
│ │
▼ ▼
Docker Root Dir containerd
/data/docker │
▼
/var/lib/containerd
两者是相互关联但独立的存储位置。
Docker:
text
/data/docker
containerd:
text
/var/lib/containerd
因此:
修改 Docker Root Dir 为
/data/docker,不会自动修改 containerd 的 root 目录。
3. 查看 Docker 当前配置
首先确认 Docker Root Dir:
bash
docker info | grep -E "Docker Root Dir|Storage Driver|Logging Driver"
例如:
text
Storage Driver: overlayfs
Logging Driver: json-file
Docker Root Dir: /data/docker
这说明 Docker 自己的数据目录已经位于 /data/docker。
4. 查看 containerd 数据占用
使用:
bash
sudo du -xh --max-depth=2 /var/lib/containerd | sort -h | tail -30
例如某台服务器的结果:
text
5.8M /var/lib/containerd/io.containerd.metadata.v1.bolt
1.9G /var/lib/containerd/io.containerd.content.v1.content/ingest
42G /var/lib/containerd/io.containerd.content.v1.content
101G /var/lib/containerd/io.containerd.snapshotter.v1.overlayfs
101G /var/lib/containerd/io.containerd.snapshotter.v1.overlayfs/snapshots
142G /var/lib/containerd
从这里可以看到:
text
containerd 总占用:142G
content: 42G
overlayfs snapshots:101G
真正占空间的主要就是:
text
io.containerd.content.v1.content
io.containerd.snapshotter.v1.overlayfs
5. 如何确认 /var/lib/containerd 是 Docker 使用的?
可以查看 containerd namespace:
bash
sudo ctr namespaces list
如果看到:
text
NAME
moby
说明 Docker 正在使用 containerd 的 moby namespace。
进一步查看:
bash
sudo ctr -n moby images list
例如:
text
REF
docker.io/bitnami/kafka:3.5.0
docker.io/bitnami/kafka:latest
docker.io/library/ai_x86:v11.2
如果能够看到 Docker 中使用的镜像,就可以确认:
text
Docker
↓
containerd
↓
moby namespace
↓
/var/lib/containerd
6. 为什么 containerd 会有这么多数据?
containerd 主要涉及几个重要目录。
6.1 content
text
/var/lib/containerd/io.containerd.content.v1.content
这里主要保存镜像内容,例如:
text
blobs/
ingest/
其中:
text
blobs
保存镜像内容和层数据。
6.2 overlayfs snapshots
text
/var/lib/containerd/io.containerd.snapshotter.v1.overlayfs
这里保存 overlayfs snapshot。
如果 Docker 使用大量镜像层、容器以及构建缓存,这个目录可能会非常大。
例如:
text
101G /var/lib/containerd/io.containerd.snapshotter.v1.overlayfs
7. 当前环境的实际结构
例如:
text
/data
├── docker
│ ├── containers
│ ├── image
│ ├── overlay2
│ └── volumes
│
└── containerd
├── io.containerd.content.v1.content
├── io.containerd.metadata.v1.bolt
└── io.containerd.snapshotter.v1.overlayfs
最终希望达到:
text
Docker Root Dir
│
▼
/data/docker
containerd root
│
▼
/data/containerd
这样 Docker 和 containerd 的主要数据都不会继续占用系统盘 /var/lib。
8. 迁移前检查
首先检查 /data 空间:
bash
df -h /data
然后检查两个目录:
bash
sudo du -sh /var/lib/containerd
sudo du -sh /data/containerd
如果已经提前执行过:
bash
sudo rsync -aP /var/lib/containerd/ /data/containerd/
那么应该看到两边大小接近。
9. 为什么第一次 rsync 后还要再同步?
如果直接在 containerd 运行期间执行:
bash
sudo rsync -aP /var/lib/containerd/ /data/containerd/
同步过程中 containerd 仍然可能产生:
- 新镜像
- 新 snapshot
- metadata
- 临时文件
因此第一次同步完成后,建议停止 Docker 和 containerd,再做一次最终同步。
10. 停止 Docker 和 containerd
bash
sudo systemctl stop docker
sudo systemctl stop containerd
确认:
bash
sudo systemctl status docker containerd --no-pager
确保两个服务已经停止。
11. 最终同步 containerd 数据
执行:
bash
sudo rsync -aHAX --numeric-ids --delete \
/var/lib/containerd/ /data/containerd/
参数说明:
| 参数 | 作用 |
|---|---|
-a |
保留权限、时间、软链接等 |
-H |
保留硬链接 |
-A |
保留 ACL |
-X |
保留扩展属性 |
--numeric-ids |
保留 UID/GID 数字 |
--delete |
让目标目录与源目录保持一致 |
同步完成后:
bash
sudo du -sh /var/lib/containerd
sudo du -sh /data/containerd
两边应该基本一致。
12. /etc/containerd/config.toml 是空的怎么办?
有些系统安装 containerd 后:
bash
cat /etc/containerd/config.toml
可能没有任何内容。
这并不代表 containerd 没有配置。
containerd 没有配置文件时会使用默认配置。
通常默认:
text
root = /var/lib/containerd
state = /run/containerd
因此当前环境实际上可能是:
text
/etc/containerd/config.toml
│
└── 空
│
▼
使用默认配置
│
├── root
│ ↓
│ /var/lib/containerd
│
└── state
↓
/run/containerd
13. 生成 containerd 配置
建议先备份:
bash
sudo cp -a /etc/containerd/config.toml \
/etc/containerd/config.toml.bak
如果原文件为空,可以生成默认配置:
bash
sudo containerd config default \
| sudo tee /etc/containerd/config.toml >/dev/null
检查:
bash
grep -E '^(root|state)' /etc/containerd/config.toml
一般可以看到:
text
root = '/var/lib/containerd'
state = '/run/containerd'
14. 修改 containerd root
把:
text
root = '/var/lib/containerd'
修改为:
text
root = '/data/containerd'
例如:
bash
sudo sed -i \
"s#^root = .*#root = '/data/containerd'#" \
/etc/containerd/config.toml
确认:
bash
grep -E '^(root|state)' /etc/containerd/config.toml
最终:
text
root = '/data/containerd'
state = '/run/containerd'
这里特别注意:
只迁移
root,不要把state迁移到/data。
state 保持:
text
/run/containerd
即可。
15. 启动 containerd
bash
sudo systemctl daemon-reload
sudo systemctl start containerd
检查:
bash
sudo systemctl status containerd --no-pager
如果正常,再启动 Docker:
bash
sudo systemctl start docker
检查:
bash
sudo systemctl status docker --no-pager
16. 验证 Docker
首先:
bash
docker info | grep -E \
"Docker Root Dir|Storage Driver|Logging Driver"
例如:
text
Storage Driver: overlayfs
Logging Driver: json-file
Docker Root Dir: /data/docker
检查容器:
bash
docker ps -a
检查镜像:
bash
docker images
17. 验证 containerd
查看 namespace:
bash
sudo ctr namespaces list
应该看到:
text
NAME
moby
再查看 Docker 镜像:
bash
sudo ctr -n moby images list
如果原来的镜像仍然存在,例如:
text
docker.io/library/ai_x86:v11.2
docker.io/bitnami/kafka:latest
说明 containerd 数据已经被正确读取。
18. 检查数据是否真的使用 /data/containerd
可以查看:
bash
sudo du -sh /data/containerd
并观察:
bash
sudo du -sh /var/lib/containerd
此时 Docker/containerd 正常运行后,新的数据应该进入:
text
/data/containerd
而不是:
text
/var/lib/containerd
19. 不要立即删除旧目录
迁移成功后,建议不要马上执行:
bash
rm -rf /var/lib/containerd
更安全的方式是先改名:
bash
sudo mv /var/lib/containerd /var/lib/containerd.bak
然后重新启动:
bash
sudo systemctl restart containerd
sudo systemctl restart docker
再次检查:
bash
docker ps -a
docker images
sudo ctr -n moby images list
如果全部正常,再运行一段时间。
20. 最终删除旧数据
确认 Docker、containerd、容器和镜像都没有问题后:
bash
sudo rm -rf /var/lib/containerd.bak
删除之前可以再次确认:
bash
sudo du -sh /var/lib/containerd.bak
sudo du -sh /data/containerd
21. 一键迁移脚本
如果需要重复部署,可以使用下面的脚本:
bash
#!/bin/bash
set -euo pipefail
SRC="/var/lib/containerd"
DST="/data/containerd"
CONF="/etc/containerd/config.toml"
BACKUP="/etc/containerd/config.toml.bak"
echo "========================================"
echo " Docker containerd migration"
echo "========================================"
if [ "$(id -u)" -ne 0 ]; then
echo "ERROR: Please run as root."
exit 1
fi
if [ ! -d "$SRC" ]; then
echo "ERROR: $SRC does not exist."
exit 1
fi
mkdir -p "$DST"
echo
echo "[1] Current usage:"
du -sh "$SRC" "$DST" 2>/dev/null || true
echo
echo "[2] Stop Docker and containerd:"
systemctl stop docker
systemctl stop containerd
echo
echo "[3] Final synchronization:"
rsync -aHAX --numeric-ids --delete \
"$SRC/" "$DST/"
echo
echo "[4] Backup containerd configuration:"
if [ -f "$CONF" ]; then
cp -a "$CONF" "$BACKUP"
fi
echo
echo "[5] Generate default configuration if empty:"
if [ ! -s "$CONF" ]; then
containerd config default > "$CONF"
fi
echo
echo "[6] Change containerd root:"
sed -i \
"s#^[[:space:]]*root[[:space:]]*=.*#root = '/data/containerd'#" \
"$CONF"
echo
echo "[7] Configuration:"
grep -E \
"^[[:space:]]*(root|state)[[:space:]]*=" \
"$CONF"
echo
echo "[8] Start containerd:"
systemctl daemon-reload
systemctl start containerd
sleep 3
if ! systemctl is-active --quiet containerd; then
echo "ERROR: containerd failed to start."
journalctl -u containerd -n 50 --no-pager
exit 1
fi
echo
echo "[9] Start Docker:"
systemctl start docker
sleep 5
if ! systemctl is-active --quiet docker; then
echo "ERROR: Docker failed to start."
journalctl -u docker -n 50 --no-pager
exit 1
fi
echo
echo "[10] Verification:"
docker info | grep -E \
"Docker Root Dir|Storage Driver|Logging Driver"
docker ps -a
echo
echo "Migration completed."
echo
echo "Old directory:"
echo " $SRC"
echo
echo "New directory:"
echo " $DST"
echo
echo "Do NOT delete the old directory immediately."
22. 回滚方法
如果迁移后 Docker 无法正常启动,可以停止服务:
bash
sudo systemctl stop docker
sudo systemctl stop containerd
恢复配置:
bash
sudo cp -a \
/etc/containerd/config.toml.bak \
/etc/containerd/config.toml
如果已经把旧目录改成:
text
/var/lib/containerd.bak
则恢复:
bash
sudo mv \
/var/lib/containerd.bak \
/var/lib/containerd
然后:
bash
sudo systemctl start containerd
sudo systemctl start docker
23. 总结
Docker 环境中需要注意:
text
Docker Root Dir
≠
containerd root
例如:
text
Docker Root Dir
↓
/data/docker
containerd root
↓
/var/lib/containerd
因此,即使:
text
Docker Root Dir: /data/docker
仍然可能出现:
text
/var/lib/containerd
└── 142G
如果通过:
bash
sudo ctr namespaces list
发现:
text
moby
并且:
bash
sudo ctr -n moby images list
能够看到 Docker 使用的镜像,那么基本可以确认该 containerd 数据目录正在被 Docker 使用。
最终可以迁移为:
text
/data
├── docker
│
└── containerd
对应:
text
Docker Root Dir
→ /data/docker
containerd root
→ /data/containerd
containerd state
→ /run/containerd
这种方式可以将 Docker 和 containerd 的主要数据统一放到 /data,避免 /var/lib/containerd 持续占用系统盘空间。
迁移过程中最重要的原则:先同步、再停服务、最终同步、修改配置、启动验证,确认无误后再删除旧数据。