1. 概述
Docker 容器本质上是在 Linux 系统中隔离运行的环境,它能分配预定义的资源。我们通常使用 Docker 容器来隔离运行应用程序。但默认情况下,容器一旦停止,其内部的所有修改都会丢失。如果我们希望保留这些数据,可以使用 Docker 卷(Volume)或绑定挂载(Bind Mount)来实现。
在本文中,我们将学习 Docker 卷的基本概念,以及如何管理和挂载它们到容器中。
2. Docker 文件系统原理
Docker 容器是基于镜像运行的。镜像由多个只读层构成,这些层通过 Union Filesystem 合并成一个文件系统。当容器启动时,Docker 会在这些只读层之上添加一个可读写的层,使容器看起来像是一个完整的 Linux 文件系统。
但是,当容器停止或被删除后,这个读写层也会被清除,所以容器内的文件修改不会保留。
我们可以用以下命令验证这一点:
$ docker run bash:latest \
bash -c "echo hello > file.txt && cat file.txt"
输出:
hello
但如果我们再次运行容器并尝试读取这个文件:
$ docker run bash:latest bash -c "cat file.txt"
cat: can't open 'file.txt': No such file or directory
文件已经不存在了。因此,如果我们希望数据在容器重启后仍然保留,就需要使用 Docker 卷。
3. 绑定挂载 vs Docker 卷
绑定挂载和 Docker 卷都可以用于实现持久化存储,但它们的实现机制和适用场景有所不同。
- 绑定挂载:将主机上的文件或目录挂载到容器中,适合需要直接访问主机文件的场景。性能高,但依赖主机的文件系统结构。
- Docker 卷:由 Docker 管理的存储目录,通常位于 Docker 的存储目录中。它不依赖主机文件系统,具有更好的可移植性和管理性。
✅ Docker 卷的优势:
- 更容易迁移和管理
- 提供专用命令进行操作(如
docker volume
) - 支持跨平台(Windows 和 Linux)
- 多容器共享更安全
❌ 绑定挂载的缺点:
- 依赖主机路径,移植性差
- 容易因为路径错误导致问题
- 安全性较低,容器可直接访问主机文件
⚠️ 总体建议:优先使用 Docker 卷,除非你有明确理由需要绑定挂载。
4. Docker 中的卷
Docker 卷是用于在容器重启之间持久化数据的一种机制。它既可以是 Docker 原生的卷,也可以是绑定挂载。
4.1 绑定挂载示例
绑定挂载用于将主机目录挂载到容器中。例如,我们可以将当前工作目录挂载到容器中,并在容器中创建文件:
$ docker run -v $(pwd):/var/opt/project bash:latest \
bash -c "echo Hello > /var/opt/project/file.txt"
-v
参数指定挂载关系:$(pwd)
:主机当前目录/var/opt/project
:容器内的挂载路径
执行后,你会在主机当前目录下看到 file.txt
文件。这说明容器和主机之间共享了该目录。
4.2 Docker 卷示例
Docker 卷是 Docker 自己管理的存储空间。它独立于主机文件系统,更适合在多个容器之间共享数据。
例如,我们先创建一个卷:
$ docker volume create data_volume
data_volume
然后启动一个容器并挂载该卷:
$ docker run -v data_volume:/var/opt/project bash:latest \
bash -c "echo Baeldung > /var/opt/project/Baeldung.txt"
再次运行容器读取文件:
$ docker run -v data_volume:/var/opt/project bash:latest \
bash -c "cat /var/opt/project/Baeldung.txt"
Baeldung
✅ 这说明即使容器重启,数据依然保留。
5. 卷的管理
Docker 提供了专门的命令来管理卷,包括创建、查看、删除等操作。
5.1 创建卷
$ docker volume create data_volume
data_volume
不指定名称时,Docker 会自动生成一个随机名称:
$ docker volume create
d7fb659f9b2f6c6fd7b2c796a47441fa77c8580a080e50fb0b1582c8f602ae2f
5.2 查看卷列表
$ docker volume ls
DRIVER VOLUME NAME
local data_volume
local d7fb659f9b2f6c6fd7b2c796a47441fa77c8580a080e50fb0b1582c8f602ae2f
可以使用过滤器查看特定卷:
$ docker volume ls -f name=data
DRIVER VOLUME NAME
local data_volume
5.3 查看卷详细信息
$ docker volume inspect data_volume
[
{
"CreatedAt": "2020-11-13T17:04:17Z",
"Driver": "local",
"Labels": null,
"Mountpoint": "/var/lib/docker/volumes/data_volume/_data",
"Name": "data_volume",
"Options": null,
"Scope": "local"
}
]
5.4 删除卷
$ docker volume rm data_volume
data_volume
⚠️ 注意:如果卷正在被某个容器使用,则无法删除。
5.5 清理未使用的卷
$ docker volume prune
WARNING! This will remove all local volumes not used by at least one container.
Are you sure you want to continue? [y/N] y
Deleted Volumes:
data_volume
6. 启动容器时挂载卷
6.1 使用 -v
参数挂载
$ docker run -v data_volume:/var/opt/project bash:latest \
bash -c "ls /var/opt/project"
如果卷为空,不会有任何输出。但我们可以写入文件并再次读取:
$ docker run -v data_volume:/var/opt/project bash:latest \
bash -c "echo Baeldung > /var/opt/project/Baeldung.txt"
$ docker run -v data_volume:/var/opt/project bash:latest \
bash -c "ls /var/opt/project"
Baeldung.txt
6.2 使用 --mount
参数挂载
--mount
提供了更清晰的语法:
$ docker run --mount \
'type=volume,src=data-volume,\
dst=/var/opt/project,volume-driver=local,\
readonly' \
bash -c "ls /var/opt/project"
参数说明:
type
: 挂载类型(volume)src
: 源卷名dst
: 容器内挂载路径volume-driver
: 存储驱动(默认 local)readonly
: 只读挂载(如需可写,使用rw
)
⚠️ 注意:--mount
会在卷不存在时自动创建。
6.3 使用 --volumes-from
共享卷
如果你有一个已经退出的容器,可以通过 --volumes-from
将其挂载的卷复制到新容器中:
$ docker run --volumes-from 4920 \
bash:latest \
bash -c "ls /var/opt/project"
Baeldung.txt
✅ 这在容器间共享数据时非常有用,例如 Jenkins 容器之间的数据共享。
7. 总结
Docker 默认不会持久化容器内的数据。为了实现数据持久化,我们可以使用绑定挂载或 Docker 卷。
- 绑定挂载:适合需要直接访问主机文件的场景,性能高但可移植性差
- Docker 卷:由 Docker 管理,更适合跨容器共享和生产环境使用
本文介绍了 Docker 卷的创建、管理和挂载方式,以及如何使用 --mount
和 --volumes-from
实现更灵活的数据共享。
掌握 Docker 卷的使用,对于构建持久化、可扩展的容器化应用至关重要。