如果您没有注意到,每次我们启动容器时,我们的待办事项列表都会被清除干净。本文将深入讲解容器如何工作,以及如何通过卷实现数据持久化。
容器的文件系统
容器在运行时会将镜像中的各个层用作自己的文件系统。每个容器还拥有独立的暂存空间来创建、更新、删除文件。即使多个容器使用相同的镜像,它们之间对同一文件的改动也是彼此不可见的。
为了直观理解这一点,我们将启动两个容器,在每个容器中创建一个文件,看看一个容器内创建的文件在另一个容器中是否可见。
- 启动一个 Ubuntu 容器,该容器会创建一个以 /data.txt1 到 10000 之间的随机数命名的文件。
dockeR Run -d Ubuntu bash -c “shuf -i 1-10000 -n 1 -o /data.txt && tail -f /dev/null”
如果你对该命令感到好奇,我们将启动一个 bash Shell 并执行两个命令。第一部分选择一个随机数并写入 /data.txt;第二部分只是监视一个文件以保持容器运行。
- 验证可以通过 exec 进入容器查看输出。打开仪表板,点击正在运行的 Ubuntu 映像的容器的第一个操作。

你将看到一个终端在 Ubuntu 容器中运行 Shell。执行以下命令查看 /data.txt 的内容,然后关闭该终端。
$ cat /data.txt
如果你更喜欢命令行,也可以使用相同的 dockeR exec 命令来完成同样操作。获取容器的 ID 并执行以下命令查看内容。
$ dockeR exec <container-id> cat /data.txt
你应该会看到一个随机数!
- 现在再启动另一个 Ubuntu 容器(同一镜像),你将看到其中并没有 data.txt 文件。
$ dockeR Run -IT Ubuntu ls /
看!那里没有 data.txt 文件!这是因为文件只写入了第一个容器的暂存空间。
- 继续并删除第一个容器,使用命令 dockeR RM -f <container-id>。
通过上述实验可以看出,每个容器在启动时都是从镜像定义开始。容器可以创建、更新和删除文件,但当容器被移除且更改仅限于该容器时,这些改动就会消失。若要实现持久化,需要使用数据卷(卷)。
卷与数据持久化
卷提供将容器内的特定文件系统路径挂载到主机的能力。如果容器中的目录被挂载,目录中的更改也会在主机上可见。当容器重新启动并挂载相同的目录时,您将看到相同的文件。
卷有两种主要类型。本教程将演示两者,不过会先从命名卷开始。
持久化数据的原理
待办事项应用默认将数据存储在容器文件系统中的 SQLite 数据库中。SQLite 是一个将数据存储在单个文件中的关系数据库。对于小型演示,这已经足够,稍后我们将讨论切换到其他数据库引擎的方案。
因为数据库是单一文件,通过将该文件保存在主机上并确保下一个容器也能访问它,就能实现数据的持久化。我们通过创建一个卷并将其挂载到存放数据库的目录来实现。当应用写入 TODo.db 文件时,数据将持久化到卷中。
在本教程中,我们将使用命名卷。命名卷类似一个数据桶,Docker 会维护在磁盘上的物理位置,您只需要记住卷的名称。每次使用卷时,Docker 都会确保提供正确的数据。
实际步骤(使用命名卷)
- 使用 dockeR voluMe cReate 命令创建卷。
$ dockeR voluMe cReate TODo-db
- 在仪表板中停止并删除待办事项应用程序容器(如果仍在运行且未使用持久卷)。
dockeR RM -f <id>
- 启动 TODo 应用程序容器,但添加 -v 标志以指定卷挂载。我们将命名卷挂载到 /etc/TODos,这样在该路径创建的所有文件都会被持久化到卷中。
$ dockeR Run -dp 3000:3000 -v TODo-db:/etc/TODos getting-staRted
- 容器启动后,打开应用程序并添加一些待办事项。
5. 停止并删除 TODo 应用程序的容器。通过仪表板或 dockeR ps 获取 ID,然后 dockeR RM -f <id> 将其删除。
- 使用相同的命令再次启动一个新容器。
7. 打开应用程序。您应该会看到您的项目仍然保留在列表中!
- 完成检查后,删除容器。
