1. 概述
在使用 Docker 容器时,我们经常需要创建各种持久对象,例如卷和镜像。默认情况下,这些对象占用启动磁盘的空间。这种默认配置可能会导致一些重大数据问题,例如其他应用程序的磁盘空间不足或硬件故障时的数据丢失。
本教程将讨论如何在 Docker 中配置数据根目录。这允许我们更改镜像安装目录并缓解上述提到的问题。
2. 示例设置
让我们创建几个持久的 Docker 对象作为示例。
首先,我们将拉取 NGINX 和 Redis 镜像:
$ docker image pull nginx
$ docker image pull redis
让我们验证它们是否已正确拉取:
$ docker image list
REPOSITORY TAG IMAGE ID CREATED SIZE
nginx latest 76c69feac34e 2 weeks ago 142MB
redis latest c2342258f8ca 2 weeks ago 117MB
接下来,我们将创建一个卷并找到其挂载点:
$ docker volume create dangling-volume
$ docker volume inspect dangling-volume -f '{{ .Mountpoint }}'
/var/lib/docker/volumes/dangling-volume/_data
最后,我们将在 /var/lib/docker/volumes/dangling-volume/_data 目录中创建一个示例文件:
$ echo "Dangling volume" | sudo tee /var/lib/docker/volumes/dangling-volume/_data/sample.txt
$ sudo cat /var/lib/docker/volumes/dangling-volume/_data/sample.txt
Dangling volume
在下一节中,我们将看到如何更改 Docker 根目录以将此类持久对象存储在不同的位置。
3. 更改镜像安装目录
在 Docker 中,镜像安装目录由 DockerRootDir 属性表示。我们可以使用 info 子命令查找其值:
$ docker info -f '{{ .DockerRootDir }}'
/var/lib/docker
在这个例子中,启动磁盘上位于 /var/lib/docker 的目录代表 Docker 根目录。
现在,让我们讨论几种方法来更改此默认根目录。
3.1. 使用守护进程配置文件
我们可以通过更新守护进程配置文件来更改默认根目录。Linux 上的默认配置文件位置是 /etc/docker/daemon.json。
因此,让我们创建一个新的目录并通过编辑守护进程配置文件将其配置为根目录:
$ mkdir -p /tmp/new-docker-root
$ sudo vi /etc/docker/daemon.json
然后我们编辑文件,使其具有指向新创建目录的 data-root。当我们保存它:
$ sudo cat /etc/docker/daemon.json
{
"data-root": "/tmp/new-docker-root"
}
最后,我们必须重新启动 docker 服务并使用 info 子命令检查更新后的根目录:
$ sudo systemctl restart docker
$ docker info -f '{{ .DockerRootDir}}'
/tmp/new-docker-root
现在,我们可以看到 Docker 根目录已设置为 /tmp/new-docker-root。
3.2. 恢复默认配置
在上一节中,我们通过更新守护进程配置文件更改了默认配置。但在进入下一节之前,我们必须恢复到之前的设置。
首先,通过删除守护进程配置文件和 /tmp/new-docker-root 目录,我们重置设置:
$ sudo rm /etc/docker/daemon.json
$ sudo rm -rf /tmp/new-docker-root/
接着,我们必须重新启动 docker 服务以使更改生效:
$ sudo systemctl restart docker
最后,我们可以验证 Docker 根目录已设置为其默认位置:
$ docker info -f '{{ .DockerRootDir}}'
/var/lib/docker
3.3. 使用 systemd 配置文件
同样,我们也可以通过修改 docker 服务的 systemd 配置来实现相同的结果。Docker 使用***/lib/systemd/system/docker.service* 单位文件**来存储其配置。让我们看看如何更新此文件。
首先,我们将创建一个新的目录并将 /lib/systemd/system/docker.service 文件中的 ExecStart 属性更改为:
$ mkdir -p /tmp/new-docker-root/
$ sudo vi /lib/systemd/system/docker.service
现在,更新后的文件看起来如下:
$ grep ExecStart /lib/systemd/system/docker.service
ExecStart=/usr/bin/dockerd --data-root /tmp/new-docker-root -H fd:// --containerd=/run/containerd/containerd.sock
在此示例中,我们使用 使用 –data-root /tmp/new-docker-root 属性配置了 Docker 根目录。
接下来,我们需要重新加载单位文件并重新启动 docker 服务以使更改生效:
$ sudo systemctl daemon-reload
$ sudo systemctl restart docker
最后,让我们使用 info 子命令检查更新后的数据根目录:
$ docker info -f '{{ .DockerRootDir}}'
/tmp/new-docker-root
在这里,我们可以看到数据根目录指向 /tmp/new-docker-root 位置。
3.4. 限制
在前面的几节中,我们看到了如何更改 Docker 数据根目录。但是,仅更改数据根目录并不足够,因为此配置 无法定位先前创建的持久对象。
为了理解这一点,让我们列出图像和卷:
$ docker image list
REPOSITORY TAG IMAGE ID CREATED SIZE
$ docker volume list
DRIVER VOLUME NAME
如您所见,Docker 无法识别来自先前数据根目录的图像和卷。
4. 迁移持久对象
为了完整地更改持久对象的位置,我们可能还需要迁移现有的对象。
4.1. 使用 rsync 命令复制数据
rsync 是一种命令行实用程序,可以高效地复制和同步文件和目录。我们可以使用此命令将 /var/lib/docker 目录的内容复制过来:
让我们使用 rsync 命令复制数据并重新启动 docker 服务:
$ sudo rsync -aqxP /var/lib/docker/ /tmp/new-docker-root
$ sudo systemctl restart docker
在此示例中,我们使用以下选项:
- –a 选项启用归档模式
- –q 选项抑制非错误消息
- –x 选项避免在复制递归目录时跨越文件系统边界
- –P 选项保留部分复制的文件/目录
现在,让我们列出图像和卷:
$ docker image list
REPOSITORY TAG IMAGE ID CREATED SIZE
nginx latest 76c69feac34e 3 weeks ago 142MB
redis latest c2342258f8ca 3 weeks ago 117MB
$ docker volume list
DRIVER VOLUME NAME
local dangling-volume
如您所见,Docker 现在能够识别先前创建的持久对象。
4.2. 恢复默认配置
首先,让我们停止 docker 服务,并从 /lib/systemd/system/docker.service 单位文件中删除 –data-root 属性:
$ sudo systemctl stop docker
$ sudo vi /lib/systemd/system/docker.service
修改后,单位文件如下:
$ grep ExecStart /lib/systemd/system/docker.service
ExecStart=/usr/bin/dockerd -H fd:// --containerd=/run/containerd/containerd.sock
现在,我们将重新加载单位文件、重新启动 docker 服务并检查更新后的数据根目录:
$ sudo systemctl daemon-reload
$ sudo systemctl restart docker
$ docker info -f '{{ .DockerRootDir}}'
/var/lib/docker
5. 结论
在这篇文章中,我们看到了如何通过配置 data-root 属性更改 Docker 中的镜像安装目录。
首先,我们使用守护进程配置文件更改了 Docker 根目录。接下来,我们讨论了如何使用 systemd 单位文件实现相同结果。然后,我们讨论了这些方法施加的限制。
最后,我们讨论了如何克服这些限制,通过迁移持久对象。