本篇内容来源于官方文档:docker docs/Get started/Part 5: Persist the DB
不知你是否有注意到,每次当我们启动容器时,todo列表管理器中总是空的。这是为什么呢?在这篇文章中我们将深入了解容器是如何工作的。
容器的文件系统
每当容器启动时,它会用到镜像中的多个层来构建自己的文件系统。每个容器都在自己的空间中创建、更新和移除文件。就算它们都使用着同一个镜像,其中的任何改动也不会在另一个容器上看到。
动手实践看看
接下来我们将创建两个容器并各自创建一个文件,如前边提到的知识点所示,在其中一个容器中创建的文件将在其他容器中不可用。
-
启动一个
ubuntu
容器,该容器将创建一个以 1 到 10000 之间的随机数命名的文件/data.txt
。docker run -d ubuntu bash -c "shuf -i 1-10000 -n 1 -o /data.txt && tail -f /dev/null"
-
使用
docker exec
命令访问容器,查看我们刚刚创建的文件中的数字docker exec <container-id> cat /data.txt
-
启动另一个
ubuntu
容器,会发现该容器中没有我们创建的文件
-
使用
docker rm
命令删除刚刚创建的两个镜像docker rm -f <container-id>
容器数据卷
在先前的实践中,我们可以看到每个容器的启动时都从镜像的定义开始。虽然容器中可以创建、更新和删除文件,但一旦删除容器其中的更改都会丢失,因为Docker会隔绝容器中的所有操作。通过添加数据卷的方式,我们可以将这些操作保存下来。
数据卷Volumes
提供着一种特定的文件系统路径将容器和主机之间相连。如果你在容器中绑定了一个文件目录,那么在该目录下的任何改动,可以同样在主机中查看得到。在重新创建并运行的容器下绑定同一目录,就会在其中看到相同的文件。
保存todo数据
默认情况下,todo列表管理器保存它的数据于SQLite
数据库在容器文件系统的/etc/todos/todo.db
目录中。即使你对SQLite
数据库不熟悉也没有关系,我们这里并不会用到对它的操作,在稍后的学习中我们将会学到如何切换到不同的数据库引擎。
由于SQLite
数据库是将所有数据存储在单个文件中,如果我们能将该文件保留在主机中,那么就可以在记录上一个容器数据的同时供下一个容器继续使用。通过创建数据卷并将其绑定到存储数据的目录下,就能够将数据保留下来。
Docker实现了对数据卷的完全管理,包括在磁盘上的存储位置,我们要做的就只是记住卷的名称就好。
创建数据卷并启动容器
-
使用下述命令创建数据卷
docker volume create todo-db
-
删除之前运行的容器,因为该容器还未绑定数据卷
docker rm -f <container-id>
-
启动todo应用,通过添加
--mount
指定卷安装的选项,并将其安装到容器的/etc/todos
目录下,捕获该路径中创建的所有文件docker run -dp 127.0.0.1:3000:3000 --mount type=volume,src=todo-db,target=/etc/todos getting-started
-
在运行后的容器中,打开应用并添加一些项目
-
删除当前容器,并使用前面的步骤重新运行一个新容器,看我们添加的待办事项是否仍在列表中
docker rm -f <container-id> docker run -dp 127.0.0.1:3000:3000 --mount type=volume,src=todo-db,target=/etc/todos getting-started
-
可以看到我们添加的待办事项依然存在,现在我们了解了如何保存数据
深入了解数据卷
我们可以通过docker volume inspect
命令来查看Docker将我们的数据存储在何处。
数据实际存储在Docker管理的主机文件系统的一部分中,而并非在Windows环境下。数据卷是在Docker中持久化数据的最佳方式
docker volume insepct todo-db
[
{
"CreatedAt": "2023-11-01T09:42:03Z",
"Driver": "local",
"Labels": null,
"Mountpoint": "/var/lib/docker/volumes/todo-db/_data",
# ↑ ↑ 磁盘上数据的实际位置 ↑ ↑
"Name": "todo-db",
"Options": null,
"Scope": "local"
}
]
相关操作命令
本篇博客中我们学习到了几个新的执行命令,来简单回顾一下
docker exec <container-id> <command>
→ docker exec <container-id> cat /data.txt
# 在运行的容器中执行命令行
docker volume create <volume-name>
# 创建数据卷
docker volume inspect <volume-name>
# 显示数据卷的详细信息
docker volume ls
# 显示数据卷列表
docker volume prune
# 删除未被使用的数据卷
docker volume rm <volume-name>
# 删除指定数据卷
docker run --mount type=volume,src=<volume-name>,target=<当前容器数据的存储位置> <image-name>
# 运行一个新容器,将目标数据存储位置绑定在指定数据卷中