文章目录
- 一、什么是存储卷
- 二、为什么需要存储卷
- 三、存储卷分类
- [四、管理卷 Volume](#四、管理卷 Volume)
-
- [方式一:Volume 命令操作](#方式一:Volume 命令操作)
- [方式二:使用 `-v` 或 `--mount` 参数指定卷](#方式二:使用
-v
或--mount
参数指定卷) - [方式三:Dockerfile 匿名卷](#方式三:Dockerfile 匿名卷)
- 五、操作案例
-
- [Docker 命令创建管理卷](#Docker 命令创建管理卷)
- [Docker -v 创建管理卷](#Docker -v 创建管理卷)
- [Docker 卷生命周期](#Docker 卷生命周期)
- [Docker 卷共享](#Docker 卷共享)
- [六、绑定卷 bind mount](#六、绑定卷 bind mount)
- [七、临时卷 `tmpfs`](#七、临时卷
tmpfs
) - [八、实战练习:Mysql 灾难恢复](#八、实战练习:Mysql 灾难恢复)
- [九、关于 Volume 的一些问题](#九、关于 Volume 的一些问题)
-
- [什么时候使用 Volume、Bind/Mount 和 tmpfs?](#什么时候使用 Volume、Bind/Mount 和 tmpfs?)
- 存储卷在实际研发中的问题
一、什么是存储卷
存储卷是指将宿主机的本地文件系统中的某个目录与容器内部文件系统中的目录建立绑定关系。
具体来说,当我们在容器中的某个目录下写入数据时,容器会将数据直接写入宿主机上与该容器绑定的目录。
宿主机上与容器形成绑定关系的 目录 被称为存储卷。存储卷的本质是文件或目录,它绕过默认的联合文件系统,直接以文件或目录的形式存在于宿主机上。
例如,宿主机的 /data/web
目录与容器的 /container/data/web
目录形成绑定关系。当容器中的进程向该目录写入数据时,数据会直接写入宿主机的 /data/web
目录。通过这种方式,容器文件系统和宿主机文件系统建立了关联,实现了容器和宿主机之间的数据共享。
这种机制使得容器可以直接访问宿主机中的内容,同时宿主机也可以向容器写入内容。容器和宿主机的数据读写是同步的。

二、为什么需要存储卷
- 数据丢失问题
容器按照业务类型,总体可以分为两类:
- 无状态的(数据不需要被持久化)
- 有状态的(数据需要被持久化
容器更擅长无状态应用。因为未持久化数据的容器根目录的生命周期与容器的
生命周期一样,容器文件系统的本质是在镜像层上面创建的读写层,运行中的容器对
任何文件的修改都存在于该读写层,当容器被删除时,容器中的读写层也会随之消失
虽然容器希望所有的业务都尽量保持无状态,这样容器就可以开箱即用,并且可以任
意调度,但实际业务总是有各种需要数据持久化的场景,比如 MySQL、Kafka 等有状
态的业务。因此为了解决有状态业务的需求,Docker 提出了卷(Volume)的概念。
- 性能问题
UnionFS 在处理修改、删除等操作时,效率较低。对于 I/O 要求较高的应用,如 Redis,在实现持久化存储时,底层存储的性能要求非常高,UnionFS 可能无法满足这些需求。
- 宿主机和容器互访不方便
宿主机访问容器,或容器访问宿主机时,通常需要通过 docker cp
等命令来完成,这使得操作变得复杂且不便于应用程序直接访问文件系统。
- 容器和容器共享不方便
容器之间共享数据时,也存在不便之处。由于每个容器都有独立的文件系统,容器间的直接共享和数据交换变得复杂且不容易操作。
三、存储卷分类
Docker 提供了三种方式将数据从宿主机挂载到容器中:
-
Volume(卷)
由 Docker 管理的卷,默认映射到宿主机的
/var/lib/docker/volumes
目录下。在容器内,只需指定挂载点,容器引擎会自动创建一个空目录,或使用已存在的目录与宿主机建立存储关系。这种方式解耦了用户与存储目录的关系,适合临时存储。缺点是用户无法指定具体使用的目录。 -
Bind Mount(绑定数据卷)
绑定数据卷将宿主机的指定路径映射到容器。宿主机上的路径需要用户手动指定,并且容器中也需要指定对应路径。两个已知的路径建立关联关系。
-
Tmpfs Mount(临时数据卷)
将数据映射到宿主机的内存中。一旦容器停止运行,
tmpfs
卷会被移除,数据丢失。此方式用于高性能的临时数据存储。

四、管理卷 Volume
存储卷可以通过命令创建,也可以在创建容器时通过 -v
或 --mount
进行指定。
方式一:Volume 命令操作
命令清单如下:
命令 | 别名 | 功能 | 备注 |
---|---|---|---|
docker volume create |
--- | 创建存储卷 | --- |
docker volume inspect |
--- | 显示存储卷详细信息 | 显示卷的元数据和配置 |
docker volume ls |
docker volume list |
列出存储卷 | --- |
docker volume prune |
--- | 清理所有无用数据卷 | 删除未使用的卷 |
docker volume rm |
--- | 删除存储卷 | 无法删除正在使用中的卷 |
Docker Volume 操作解释
-
docker volume create
功能 :创建存储卷
语法:bashdocker volume create [OPTIONS] [VOLUME]
关键参数:
-d, --driver
:指定驱动,默认是local
--label
:指定元数据
示例:
bashdocker volume create my-vol
-
docker volume inspect
功能 :查看卷详细信息
语法:bashdocker volume inspect [OPTIONS] VOLUME [VOLUME...]
关键参数:
-f
:指定输出格式,如json
示例:
bashdocker volume inspect my-vol
-
docker volume ls
功能 :列出卷
语法:bashdocker volume ls [OPTIONS]
关键参数:
--format
:指定输出格式,如json
,table
--filter, -f
:过滤-q
:仅显示卷名称
示例:
bashdocker volume ls
-
docker volume rm
功能 :删除卷,容器不使用时才能删除
语法:bashdocker volume rm [OPTIONS] VOLUME [VOLUME...]
关键参数:
-f, --force
:强制删除
示例:
bashdocker volume rm hello
-
docker volume prune
功能 :删除未使用的本地卷
语法:bashdocker volume prune [OPTIONS]
关键参数:
--filter
:过滤-f, --force
:不提示确认删除
示例:
bashdocker volume prune
方式二:使用 -v
或 --mount
参数指定卷
-v
和 --mount
都可以用于创建和管理卷。以下是两种方式的详细说明:
-v
参数
功能 :完成目录映射
语法:
bash
docker run -v name:directory[:options] ...
参数说明:
- 第一个参数:卷名称
- 第二个参数:卷映射到容器中的目录
- 第三个参数 :选项,如
ro
表示只读模式
示例:
bash
docker run -d --name devtest -v myvol2:/app nginx:latest
通过 docker inspect
可以看到:
json
"Mounts": [
{
"Type": "volume",
"Name": "myvol2",
"Source": "/var/lib/docker/volumes/myvol2/_data",
"Destination": "/app",
"Driver": "local",
"Mode": "",
"RW": true,
"Propagation": ""
}
]
-mount
参数
功能 :完成目录映射
语法:
bash
--mount '<key>=<value>,<key>=<value>'
关键参数:
- type :指定类型,可选
bind
、volume
或tmpfs
- source (src):对于命名卷,这是卷的名称;对于匿名卷,省略此字段
- destination (dst, target):容器中挂载的文件或目录路径
- ro, readonly:以只读方式挂载
示例 :
采用 --mount
创建卷
bash
docker run -d --name devtest --mount source=myvol2,target=/app nginx:latest
通过 docker inspect
可以看到:
json
"Mounts": [
{
"Type": "volume",
"Name": "myvol2",
"Source": "/var/lib/docker/volumes/myvol2/_data",
"Destination": "/app",
"Driver": "local",
"Mode": "",
"RW": true,
"Propagation": ""
}
]
方式三:Dockerfile 匿名卷
通过 Dockerfile 中的 VOLUME
指令,可以创建 Docker 管理的卷。
使用 VOLUME
指令可以在镜像中创建数据卷,确保每个通过该镜像创建的容器都会自动包含挂载点。但需要注意的是,通过 VOLUME
指令创建的挂载点无法指定主机上的具体目录,而是由 Docker 随机生成。
五、操作案例
Docker 命令创建管理卷
-
创建 Docker 管理卷
使用以下命令创建一个名为
test_volume
的管理卷,并通过 Docker 容器挂载该卷:
bash
ubuntu@VM-8-2-ubuntu:~$ docker volume create test_volume
test_volume
ubuntu@VM-8-2-ubuntu:~$ docker container run --name nginx1 -d -p 8080:80 -v test_volume:/usr/share/nginx/html nginx:1.24.0
b2148264eed78ff979945bd7bec1ca14c9158c596f7e509261acdbda9ac358f4
-
查看管理卷
使用以下命令查看所有 Docker 管理卷:
bash
ubuntu@VM-8-2-ubuntu:~$ docker volume ls
DRIVER VOLUME NAME
local 1d853d8af50a81ef4f21a3984288cc19bb2beea754ebd997f6aab62e04bbc6a9
local ebf76ebf9a2b50aa1cadfb4d9d3610b2172f13fb33d3ed52db755900daf3c5e1
local myvol2
local test_volume
-
查看卷的详细信息
通过以下命令查看
test_volume
的详细信息,包括对应的宿主机目录:
bash
ubuntu@VM-8-2-ubuntu:~$ docker volume inspect test_volume
[
{
"CreatedAt": "2025-03-17T16:14:48+08:00",
"Driver": "local",
"Labels": null,
"Mountpoint": "/var/lib/docker/volumes/test_volume/_data",
"Name": "test_volume",
"Options": null,
"Scope": "local"
}
]
-
查看宿主机目录内容
进入宿主机目录,查看卷挂载的数据:
bash
ubuntu@VM-8-2-ubuntu:~$ sudo bash
root@VM-8-2-ubuntu:/home/ubuntu# cd /var/lib/docker/volumes/test_volume/_data
root@VM-8-2-ubuntu:/var/lib/docker/volumes/test_volume/_data# ls
50x.html index.html
-
查看容器的挂载信息
通过以下命令查看容器的挂载信息,确认卷挂载到容器:
bash
ubuntu@VM-8-2-ubuntu:~$ docker inspect nginx1
[
{
"Id": "b2148264eed78ff979945bd7bec1ca14c9158c596f7e509261acdbda9ac358f4",
"Created": "2025-03-17T08:27:12.908655805Z",
"Path": "/docker-entrypoint.sh",
"Args": [
"nginx",
"-g",
"daemon off;"
],
"State": {
.....
-
修改文件
修改
index.html
文件的内容:
bash
cd /data/var/lib/docker/volumes/test_volume/_data
echo "Hello Docker" > index.html
-
通过浏览器查看
访问容器的页面,通过浏览器查看内容,可以看到宿主机和容器中的数据是同步的。

-
停止容器并释放资源
停止并删除容器以释放占用的资源:
bashdocker stop nginx1 docker rm nginx1
结论:
宿主机和容器之间的数据是同步的,修改宿主机目录中的文件,会实时反映到容器中的挂载位置。
Docker -v 创建管理卷
-v
创建管理卷,并启动容器
bash
ubuntu@VM-8-2-ubuntu:~$ docker container run --name nginx2 -d -p 8080:80 -v test_volume2:/usr/share/nginx/html:ro nginx:1.24.0
b4e93a64a5f7f9c278bc5d7b6e26efb8932f2387406fe66ec4a22dc80df1be35
- 进入卷目录:
bash
ubuntu@VM-8-2-ubuntu:~$ docker inspect test_volume2
[
{
"CreatedAt": "2025-03-17T17:04:09+08:00",
"Driver": "local",
"Labels": null,
"Mountpoint": "/var/lib/docker/volumes/test_volume2/_data",
"Name": "test_volume2",
"Options": null,
"Scope": "local"
}
]
ubuntu@VM-8-2-ubuntu:~$ sudo bash
root@VM-8-2-ubuntu:/home/ubuntu# cd /var/lib/docker/volumes/test_volume/_data
root@VM-8-2-ubuntu:/var/lib/docker/volumes/test_volume/_data#
- 修改
index.html
,随后在浏览器进行查看:
bash
root@VM-8-2-ubuntu:/var/lib/docker/volumes/test_volume/_data# echo "Hello Docker from test_volume2" > index.html
- 在容器中尝试修改
index.html
,提示只读文件,无法修改:
bash
root@VM-8-2-ubuntu:/var/lib/docker/volumes/test_volume/_data# docker exec -it nginx2 bash
root@b4e93a64a5f7:/# ls
bin boot dev docker-entrypoint.d docker-entrypoint.sh etc home lib lib64 media mnt opt proc root run sbin srv sys tmp usr var
root@b4e93a64a5f7:/# cd /usr/share/nginx/html/
root@b4e93a64a5f7:/usr/share/nginx/html# rm index.html
rm: cannot remove 'index.html': Read-only file system
- 清理释放空间
bash
docker stop nginx2
docker rm nginx2
结论:
使用 :ro
的卷挂载选项会让容器的文件系统无法修改挂载卷中的内容,但是宿主机仍然能修改卷中的内容。
Docker 卷生命周期
Docker 卷的生命周期包括以下几个关键阶段:
-
创建
使用
docker volume create
命令创建一个新的 Docker 卷。例如,创建名为test_volume
的卷:bashdocker volume create test_volume
这会在 Docker 引擎的存储位置(如
/var/lib/docker/volumes/
)下创建一个新的卷。 -
挂载与使用
卷创建后,可以在容器中挂载并使用。例如,通过以下命令将
test_volume
卷挂载到容器的/usr/share/nginx/html
目录:bashdocker container run --name nginx1 -d -p 8080:80 -v test_volume:/usr/share/nginx/html nginx:1.24.0
这样,卷
test_volume
就会与容器共享数据,并能够被容器中的应用程序访问。 -
数据读写
在容器中对挂载的卷进行数据的读写操作,任何对卷中文件的修改都会实时反映到容器内的挂载位置。如果使用
:ro
(只读)选项挂载卷,容器内的进程将无法修改卷中的数据,但宿主机仍然可以进行修改。 -
删除卷
当不再需要卷时,可以使用
docker volume rm
命令删除卷:bashdocker volume rm test_volume
删除卷时,如果卷仍被某个容器使用,则无法删除,需先停止并删除相关容器。
-
自动清理
Docker 也提供了自动清理未使用卷的功能,通过以下命令删除所有未使用的卷:
bashdocker volume prune
Docker 卷共享
Docker 卷共享允许多个容器共享同一个卷中的数据。这对于不同容器之间的数据共享和持久化存储非常有用。
-
共享卷的创建
为了共享数据,可以在多个容器中使用同一个卷。例如,创建一个名为
shared_volume
的卷:bashdocker volume create shared_volume
-
挂载到多个容器
可以将同一个卷挂载到多个容器中,从而共享数据。例如,将
shared_volume
同时挂载到两个容器:bashdocker container run --name container1 -d -v shared_volume:/usr/share/nginx/html nginx:1.24.0 docker container run --name container2 -d -v shared_volume:/usr/share/nginx/html nginx:1.24.0
这两个容器都将访问到
shared_volume
卷中的数据,因此它们的数据会同步共享。 -
数据同步
如果容器中有应用程序修改了共享卷中的数据,这些更改会立即反映到所有使用该卷的容器中。例如,如果
container1
修改了shared_volume
中的文件,container2
也能看到该文件的更改,因为它们都共享同一个卷。 -
共享卷的用途
- 日志共享:多个容器可以共享卷来写入日志文件,便于集中管理。
- 持久化存储:某些容器需要保存数据,例如数据库容器,可以将数据存储在卷中,以便容器重启后仍能保持数据。
六、绑定卷 bind mount
-v
和 -mount
都可以完成绑定卷的创建
创建卷
使用 -v
参数创建卷
功能:完成卷的映射。
语法:
bash
docker run -v <宿主机目录>:<容器目录>[:options] ...
参数说明:
- 第一个参数:宿主机目录(与管理卷不同)。
- 第二个参数:卷映射到容器的目录。
- 第三个参数 (可选):挂载选项,例如
ro
表示只读模式。
示例:
bash
docker run -d \
-it \
--name devtest \
-v "$(pwd)"/target:/app \
nginx:latest
使用 --mount
参数创建绑定卷
功能:完成目录映射。
语法:
bash
docker run --mount '<key>=<value>,<key>=<value>' ...
关键参数:
- type :类型,可以是
bind
、volume
或tmpfs
。 - source 或 src:宿主机目录(与管理卷不同)。
- destination 、dst 或 target:容器中的文件或目录路径。
- ro 或 readonly:以只读方式挂载。
示例:
bash
docker run -d \
-it \
--name devtest \
--mount type=bind,source="$(pwd)"/target,target=/app \
nginx:latest
结论:
-v
参数用于创建并映射卷,通常用于指定宿主机目录与容器目录之间的关系。--mount
参数提供了更详细的挂载选项,适用于绑定卷等更灵活的场景。
操作案例
使用 --mount
方式创建容器
-
创建 nginx 容器并挂载宿主机目录
使用
--mount
方式创建一个 nginx 容器,并将宿主机的/webapp1
目录挂载到容器的/usr/share/nginx/html
目录。如果宿主机的/webapp1
目录不存在,容器启动时会报错。
bash
docker container run --name nginx_mount -d -p 8080:80 --mount type=bind,source=/data/myworkdir/fs/webapp1,target=/usr/share/nginx/html nginx:1.24.0
创建宿主机目录并启动容器:
bash
root@VM-8-2-ubuntu:/home/ubuntu# mkdir -p /data/myworkdir/fs/webapp1
root@VM-8-2-ubuntu:/home/ubuntu# docker container run --name nginx_mount -d -p 8080:80 --mount type=bind,source=/data/myworkdir/fs/webapp1,target=/usr/share/nginx/html nginx:1.24.0
创建新的宿主机目录并启动另一个容器:
bash
root@VM-8-2-ubuntu:/home/ubuntu# mkdir -p /data/myworkdir/fs/webapp1_new
root@VM-8-2-ubuntu:/home/ubuntu# docker run -d -p 8081:80 --name bind1 --mount type=bind,source=/data/myworkdir/fs/webapp1_new,target=/usr/share/nginx/html nginx:1.24.0
-
查看挂载信息
使用
docker inspect
查看容器的挂载信息。
bash
root@VM-8-2-ubuntu:/home/ubuntu# docker inspect bind1
[
{
"Id": "c184c9db30fe849ea7ed5f8cd945bb3e3c51ad55edf6176c3d22d23cc4ead5d9",
"Created": "2025-03-18T07:20:17.73803812Z",
"Path": "/docker-entrypoint.sh",
"Args": [
"nginx",
"-g",
"daemon off;"
],
"State": {
...
-
进入容器查看挂载目录
进入容器终端,查看挂载点
/usr/share/nginx/html
,并在宿主机上查看挂载目录,发现文件并不存在。
bash
root@VM-8-2-ubuntu:/home/ubuntu# docker exec -it bind1 ls /usr/share/nginx/html
root@VM-8-2-ubuntu:/data/myworkdir/fs# ls
webapp1 webapp1_new
root@VM-8-2-ubuntu:/data/myworkdir/fs# ll webapp1
total 8
drwxr-xr-x 2 root root 4096 Mar 18 15:12 ./
drwxr-xr-x 4 root root 4096 Mar 18 15:20 ../
可以发现容器中的文件已消失,这是 bind mount
和 volume
模式的主要区别。
-
宿主机文件更新,容器实时读取
在宿主机
/webapp1
目录添加index.html
文件,并通过浏览器访问容器,容器成功读取并显示宿主机的文件内容。
bash
root@VM-8-2-ubuntu:/data/myworkdir/fs/webapp1# echo "Hello Bind Mount." >> index.html
通过浏览器访问容器时,可以看到宿主机的文件内容:

-
删除容器后,宿主机文件依然存在
删除容器后,宿主机上的文件保持不变,说明
bind mount
的挂载不受容器生命周期的影响。
bash
root@VM-8-2-ubuntu:/data/myworkdir/fs/webapp1# docker stop bind1
bind1
root@VM-8-2-ubuntu:/data/myworkdir/fs/webapp1# docker rm bind1
bind1
root@VM-8-2-ubuntu:/data/myworkdir/fs/webapp1# ll
total 12
drwxr-xr-x 2 root root 4096 Mar 18 15:32 ./
drwxr-xr-x 4 root root 4096 Mar 18 15:20 ../
-rw-r--r-- 1 root root 18 Mar 18 15:32 index.html
总结:
- 使用
--mount
创建绑定卷时,容器会挂载宿主机的指定目录。 - 容器中的挂载目录会覆盖容器内的原有内容,这是与
volume
模式的主要区别。 - 宿主机文件的变化会立即反映到容器中,且容器删除后,宿主机文件仍然存在。
使用 -v
创建绑定卷
-
使用
-v
方式创建容器创建一个 nginx 容器,并将宿主机的
/webapp2
目录挂载到容器的/usr/share/nginx/html
目录。需要注意的是,如果宿主机的/webapp2
目录不存在,容器启动时不会报错。这是-v
和--mount
方式的区别:-v
会自动创建挂载目录,而--mount
需要宿主机目录存在。
bash
docker run -d -p 8080:80 --name bind2 -v /data/myworkdir/fs/webapp2:/usr/share/nginx/html nginx:1.22.1
-
查看挂载信息
使用
docker inspect
命令查看容器的挂载信息。
bash
root@VM-8-2-ubuntu:/data/myworkdir/fs# docker inspect bind2
[
{
"Id": "b9631fa3b8a122012d2c876ffdd5826d80dbdb9336b967017d96061f98e5434a",
"Created": "2025-03-18T10:20:52.43803812Z",
"Mounts": [
{
"Type": "bind",
"Source": "/data/myworkdir/fs/webapp2",
"Destination": "/usr/share/nginx/html",
"Mode": "",
"RW": true,
"Propagation": "rprivate"
}
],
...
}
]
-
进入容器终端查看挂载点
进入容器终端,查看
/usr/share/nginx/html
目录,发现容器中的文件为空。同样,在宿主机上查看/webapp2
目录,也没有文件。这是bind mount
模式和volume
模式的区别:容器内原有文件会被挂载目录的内容覆盖。
bash
root@VM-8-2-ubuntu:/data/myworkdir/fs# docker exec -it bind2 ls /usr/share/nginx/html
root@VM-8-2-ubuntu:/data/myworkdir/fs# ll webapp2
total 8
drwxr-xr-x 2 root root 4096 Mar 18 15:20 ./
drwxr-xr-x 4 root root 4096 Mar 18 15:20 ../
-
在宿主机添加
index.html
文件在宿主机
/webapp2
目录下创建一个index.html
文件,并写入内容。
bash
root@VM-8-2-ubuntu:/data/myworkdir/fs/webapp2# echo "Hello Bind Mount in bind2" > index.html
-
通过浏览器访问
通过浏览器访问容器时,能够看到容器已经读取并显示了宿主机的共享内容。
-
删除容器后,宿主机文件依然存在
删除容器后,宿主机上的文件仍然存在,证明容器删除并不影响
bind mount
映射。
bash
root@VM-8-2-ubuntu:/data/myworkdir/fs/webapp2# docker stop bind2
bind2
root@VM-8-2-ubuntu:/data/myworkdir/fs/webapp2# docker rm bind2
bind2
root@VM-8-2-ubuntu:/data/myworkdir/fs/webapp2# ll
total 12
drwxr-xr-x 2 root root 4096 Mar 18 15:32 ./
drwxr-xr-x 4 root root 4096 Mar 18 15:20 ../
-rw-r--r-- 1 root root 21 Mar 18 15:32 index.html
总结:
- 使用
-v
创建绑定卷时,如果宿主机目录不存在,容器启动时不会报错。 - 容器内的挂载目录内容会被宿主机目录内容覆盖,这是
bind mount
模式与volume
模式的主要区别。 - 宿主机文件的变化会实时反映到容器中,且容器删除后,宿主机的文件依然存在,
bind mount
映射不会受到容器生命周期的影响。
绑定卷共享
- 启动两个绑定卷,绑定到宿主机的同一个目录:
bash
ubuntu@VM-8-2-ubuntu:~$ docker run -d -p 8082:80 --name bind3 -v /data/myworkdir/fs/webapp1:/usr/share/nginx/html nginx:1.24.0
7e23e9cf779c21fa4c1cf1982953c79c13564876cd2bfce60fe1fefddb2d1ef5
ubuntu@VM-8-2-ubuntu:~$ docker run -d -p 8083:80 --name bind4 -v /data/myworkdir/fs/webapp1:/usr/share/nginx/html nginx:1.24.0
0794b4c804ca3ab938a5023756c59e515047821eaead81b69774a883e27b17a8
- 访问这两个页面可以查到相同的内容:
bash
ubuntu@VM-8-2-ubuntu:~$ curl 127.0.0.1:8082
Hello Bind Mount.
ubuntu@VM-8-2-ubuntu:~$ curl 127.0.0.1:8083
Hello Bind Mount.
- 修改 index.html 后,再次访问两界面,可以发现 其index.html 都进行了修改,即实现了容器间的共享:
bash
root@VM-8-2-ubuntu:/data/myworkdir/fs/webapp1# echo "changed bind mount" > index.html
root@VM-8-2-ubuntu:/data/myworkdir/fs/webapp1# curl 127.0.0.1:8082
changed bind mount
root@VM-8-2-ubuntu:/data/myworkdir/fs/webapp1# curl 127.0.0.1:8083
changed bind mount
- 最后不要忘记清理资源
bash
root@VM-8-2-ubuntu:/data/myworkdir/fs/webapp1# docker stop bind2
bind2
root@VM-8-2-ubuntu:/data/myworkdir/fs/webapp1# docker stop bind3
bind3
root@VM-8-2-ubuntu:/data/myworkdir/fs/webapp1# docker rm bind2
bind2
root@VM-8-2-ubuntu:/data/myworkdir/fs/webapp1# docker rm bind3
bind3
七、临时卷 tmpfs
tmpfs
临时卷是位于内存中的卷,存储在容器和宿主机的外部。
tmpfs
的局限性:
- 与卷和绑定挂载不同,
tmpfs
挂载不能在多个容器之间共享。 - 仅在 Linux 系统上运行 Docker 时支持此功能。
创建临时卷
方式一:使用 --tmpfs
参数创建
- 功能:将临时卷挂载到容器指定目录。
- 语法:
bash
--tmpfs /app
- 示例:
bash
docker run -d \
-it \
--name tmptest \
--tmpfs /app \
nginx:1.22.1
方式二:使用 --mount
参数创建
- 功能:完成目录的临时卷映射。
- 语法:
bash
--mount '<key>=<value>,<key>=<value>'
-
关键参数:
type
:指定挂载类型,支持bind
、volume
或tmpfs
。destination
、dst
、target
:容器中挂载的目标路径。tmpfs-size
:指定tmpfs
挂载的大小(以字节为单位),默认没有限制。tmpfs-mode
:指定tmpfs
的文件模式(以八进制表示),例如700
或0770
,默认1777
,全局可写。
-
示例:
bash
docker run -d \
-it \
--name tmptest \
--mount type=tmpfs,destination=/app \
nginx:latest
总结:
tmpfs
提供了一个高效的内存存储方式,适用于不需要持久化存储的临时数据。- 使用
--tmpfs
或--mount
都可以创建临时卷,选择方式依据需求而定。
操作案例
tmpfs
参数创建临时卷
- 创建临时卷并启动容器
bash
root@VM-8-2-ubuntu:/data/myworkdir/fs/webapp1# docker container run --name tmpfs1 -d -p 8080:80 --tmpfs /usr/share/nginx/html nginx:1.24.0
a350249d8cf5efce441dac30059c8313aef9b6426ad4b8c9bb55a588a704f41a
- 进入容器可以发现 nginx 中的文件被覆盖了,即 tmpfs 会覆盖容器里的文件
bash
root@VM-8-2-ubuntu:/data/myworkdir/fs/webapp1# docker exec -it tmpfs1 bash
root@a350249d8cf5:/# cd /usr/share/nginx/html/
root@a350249d8cf5:/usr/share/nginx/html# ls -l
total 0
- 添加首页并进行访问:
bash
root@a350249d8cf5:/usr/share/nginx/html# echo "Hello docker from tmpfs" > index.html
root@a350249d8cf5:/usr/share/nginx/html# curl 127.0.0.1 :8080
Hello docker from tmpfs
- 停止容器并再次启动,进行查询后会发现tmpfs的内容完全消失了,即 tmpfs 的内容是存在内存中的:
bash
root@VM-8-2-ubuntu:# docker stop tmpfs1
tmpfs1
root@VM-8-2-ubuntu:# docker start tmpfs1
tmpfs1
root@VM-8-2-ubuntu:# docker exec -it tmpfs1 bash
root@a350249d8cf5:/# ls -l /usr/share/nginx/html/
total 0
- 清理资源:
bash
root@VM-8-2-ubuntu:/# docker stop tmpfs1
tmpfs1
root@VM-8-2-ubuntu:/# docker rm tmpfs1
tmpfs1
mount
创建临时卷
- 创建临时卷并启动容器
bash
ubuntu@VM-8-2-ubuntu:~$ docker container run --name mtpfs1 -d -p 8080:80 --mount type=tmpfs,destination=/usr/share/nginx/html,tmpfs-size=1m nginx:1.24.0
817957a8e4bf52736f3e854efb3601a4b04f09be6d5247cff617ae6e14680d8e
- 进入容器可以发现nginx的内容被覆盖了,即
tmpfs
会覆盖容器
bash
ubuntu@VM-8-2-ubuntu:~$ docker exec -it mtpfs1 bash
root@817957a8e4bf:/# ls
bin dev docker-entrypoint.sh home lib64 mnt proc run srv tmp var
boot docker-entrypoint.d etc lib media opt root sbin sys usr
root@817957a8e4bf:/# ls -l /usr/share/nginx/html
total 0
- 添加首页并进行访问:
bash
root@817957a8e4bf:/usr/share/nginx/html# echo "hello mount from tmpfs" > index.html
root@817957a8e4bf:/usr/share/nginx/html# curl 127.0.0.1
hello mount from tmpfs
- 剩下的步骤和 tmpfs 创建临时卷一样,就不再赘述了
tmpfs
失踪
- 创建一个普通的
docker
容器
bash
ubuntu@VM-8-2-ubuntu:~$ docker run -d -it --name tmptest nginx:1.24.0
d4bb0cce7be8705d6e97d41e946b9c9e7d737416f6c8ff149e2d989f79ea7c2f
- 运行容器并写入文件:
bash
ubuntu@VM-8-2-ubuntu:~$ docker exec -it tmptest bash
root@d4bb0cce7be8:/# mkdir -p /app
root@d4bb0cce7be8:/# echo 114 > /app/mylabel.txt
root@d4bb0cce7be8:/# exit
exit
- 在宿主机查找文件,是可以找到的,因为文件在容器的可写层
bash
ubuntu@VM-8-2-ubuntu:~$ sudo find / -name mylabel.txt
/var/lib/docker/overlay2/c3cdefc3d23aa496e133c7a352028bcad779c1eebfa0f01e13097ae942bb24a7/merged/app/mylabel.txt
/var/lib/docker/overlay2/c3cdefc3d23aa496e133c7a352028bcad779c1eebfa0f01e13097ae942bb24a7/diff/app/mylabel.txt
- 创建一个临时卷并启动,
bash
ubuntu@VM-8-2-ubuntu:~$ docker run -d --name tmptest2 --tmpfs /app nginx:1.24.0
10ce055a39d480fcdc00f00947c3eab4f981057021e61d9d8bfefe13bf59b4bc
ubuntu@VM-8-2-ubuntu:~$ docker exec -it tmptest2 bash
- 进入容器在 /app 目录下创建新文件 mynewlabel.txt
bash
root@10ce055a39d4:/# ls app
root@10ce055a39d4:/# echo 514 > /app/mynewlabl.txt
root@10ce055a39d4:/# ls /app
mynewlabl.txt
root@10ce055a39d4:/# exit
exit
- 返回宿主机并查询 mynewlabel.txt ,发现找不到文件
bash
ubuntu@VM-8-2-ubuntu:~$ sudo find / -name mynewlabel.txt
ubuntu@VM-8-2-ubuntu:~$
- 所以 tmpfs 的内容不是存储在容器的可写层里面的,最后释放容器
bash
ubuntu@VM-8-2-ubuntu:~$ docker stop tmptest
tmptest
ubuntu@VM-8-2-ubuntu:~$ docker rm tmptest
tmptest
ubuntu@VM-8-2-ubuntu:~$ docker stop tmptest2
tmptest2
ubuntu@VM-8-2-ubuntu:~$ docker rm tmptest2
tmptest2
八、实战练习:Mysql 灾难恢复
练习目标:
本次练习旨在掌握挂载卷的使用,将 MySQL 的业务数据存储到外部。
操作步骤:
-
准备镜像
首先,我们拉取
mysql:5.7
镜像。bashubuntu@VM-8-2-ubuntu:~$ docker pull mysql:5.7 Using default tag: latest latest: Pulling from library/mysql 7b36eddb0393: Pull complete 9d96cf4d7877: Pull complete c06a2d8bb529: Pull complete 7d30ccf68828: Pull complete 169c64bfa9b4: Pull complete 03f81c404364: Pull complete Digest: sha256:0b536be69f66e91e56ac8dbf45d5f55e8f393c19c22c6747e1ac9f4fbd37c2e0 Status: Downloaded newer image for mysql:5.7
-
创建容器并挂载卷
创建一个名为
mysql_demo
的容器,设置 MySQL 密码并将数据卷挂载到/data/myworkdir/mysql-data
目录。bashubuntu@VM-8-2-ubuntu:~$ docker container run --name mysql_demo -e MYSQL_ROOT_PASSWORD=114514 -itd -v /data/myworkdir/mysql-data:/var/lib/mysql mysql:5.7
解释:
-e MYSQL_ROOT_PASSWORD=114514
:设置 MySQL 根用户密码为114514
。-v /data/myworkdir/mysql-data:/var/lib/mysql
:将宿主机的/data/myworkdir/mysql-data
目录挂载到容器的/var/lib/mysql
,保存数据库数据。
-
查看容器挂载信息
使用
docker container inspect
命令查看容器的挂载信息。bashubuntu@VM-8-2-ubuntu:~$ docker container inspect mysql_demo | grep "Mounts" -A 20 "Mounts": [ { "Type": "volume", "Name": "mysql-demo-mysql-data", "Source": "/data/myworkdir/mysql-data", "Destination": "/var/lib/mysql", "Mode": "rw", "RW": true, "Propagation": "rprivate" } ]
-
连接 MySQL 创建数据库和表
连接到容器并进入 MySQL Shell,使用之前设置的密码
114514
。bashubuntu@VM-8-2-ubuntu:~$ docker container exec -it mysql_demo /bin/bash root@mysql_demo:/# mysql -u root -p Enter password: 114514 Welcome to the MySQL monitor. Commands end with ; or \g. Your MySQL connection id is 1 Server version: 5.7.34 MySQL Community Server (GPL)
创建
user
数据库和student
表,并插入一些数据。bashmysql> show databases; +--------------------+ | Database | +--------------------+ | information_schema | | mysql | | performance_schema | | sys | +--------------------+ 4 rows in set (0.00 sec) mysql> create database user; Query OK, 1 row affected (0.01 sec) mysql> use user; Database changed mysql> create table student(sno char(3), sname varchar(20)); Query OK, 0 rows affected (0.01 sec) mysql> insert into student values('1', '田所'),('2', '德川'); Query OK, 2 rows affected (0.01 sec) mysql> select * from student; +-----+--------+ | sno | sname | +-----+--------+ | 1 | 田所 | | 2 | 德川 | +-----+--------+ 2 rows in set (0.00 sec) mysql> exit Bye
在上述步骤中,我们创建了一个
user
数据库,在其中创建了student
表,并插入了两条数据。 -
查看宿主机上的数据卷内容
查看挂载到宿主机的
mysql-data
目录,验证数据是否被持久化。bashubuntu@VM-8-2-ubuntu:~$ ll /data/myworkdir/mysql-data/ total 96 drwxr-xr-x 6 root root 4096 Mar 23 10:00 . drwxr-xr-x 3 root root 4096 Mar 23 09:50 .. drwxr-xr-x 2 root root 4096 Mar 23 10:00 3306 drwxr-xr-x 2 root root 4096 Mar 23 10:00 lost+found -rw-r----- 1 root root 368 Mar 23 10:00 ib_logfile0 -rw-r----- 1 root root 368 Mar 23 10:00 ib_logfile1
-
删除容器
删除容器
mysql_demo
,此时 MySQL 数据也会随容器一起清除。bashubuntu@VM-8-2-ubuntu:~$ docker stop mysql_demo mysql_demo ubuntu@VM-8-2-ubuntu:~$ docker rm mysql_demo mysql_demo
-
恢复数据
通过再次运行容器并挂载相同的数据卷,数据可以恢复。
bashubuntu@VM-8-2-ubuntu:~$ docker container run --name mysql_demo_new -e MYSQL_ROOT_PASSWORD=114514 -itd -v /data/myworkdir/mysql-data:/var/lib/mysql mysql:5.7
-
重新连接 MySQL 查看数据
登录 MySQL,查看数据是否恢复。
bashubuntu@VM-8-2-ubuntu:~$ docker exec -it mysql_demo_new mysql -u root -p Enter password: 114514 Welcome to the MySQL monitor. Commands end with ; or \g. Your MySQL connection id is 1 Server version: 5.7.34 MySQL Community Server (GPL) mysql> show databases; +--------------------+ | Database | +--------------------+ | information_schema | | mysql | | performance_schema | | sys | | user | +--------------------+ 5 rows in set (0.00 sec) mysql> use user; Database changed mysql> show tables; +-----------------+ | Tables_in_user | +-----------------+ | student | +-----------------+ 1 row in set (0.00 sec) mysql> select * from student; +-----+--------+ | sno | sname | +-----+--------+ | 1 | 田所 | | 2 | 德川 | +-----+--------+ 2 rows in set (0.00 sec) mysql> exit Bye
数据成功恢复,
student
表中的数据仍然存在。 -
释放空间
删除新的容器以释放资源。
bashubuntu@VM-8-2-ubuntu:~$ docker stop mysql_demo_new mysql_demo_new ubuntu@VM-8-2-ubuntu:~$ docker rm mysql_demo_new mysql_demo_new
九、关于 Volume 的一些问题
什么时候使用 Volume、Bind/Mount 和 tmpfs?
-
Volume
Volume 是 Docker 容器与宿主机之间独立的存储区域,它位于宿主机的文件系统中。使用 Volume 时,Docker 会自动管理存储位置,不需要用户指定具体目录,适合不需要提前规划目录结构的场景。通常用于持久化容器数据,如数据库存储、日志文件等。
-
Bind Mount
Bind Mount 完全依赖于宿主机的目录结构和操作系统。它将宿主机的指定目录直接挂载到容器中,因此需要提前规划好宿主机上的目录。适用于需要容器与宿主机之间共享数据,或需要直接访问宿主机文件系统的场景。例如,开发环境中容器需要访问主机代码目录,或者需要挂载特定配置文件时,使用 Bind Mount 是一个合适的选择。
-
tmpfs
tmpfs 是一种将数据存储在内存中的临时存储方式,适用于不希望数据持久化到宿主机或容器可写层中的场景。使用 tmpfs 时,数据仅存在于容器生命周期内,当容器停止或删除时,数据会丢失。它适用于敏感数据存储、临时缓存等,不需要长期保存的数据。
存储卷在实际研发中的问题
-
跨主机使用问题
Docker 存储卷依赖于宿主机的本地文件系统,每个宿主机上的存储卷只能在该主机上使用,无法跨主机共享。
因此,当容器停止或删除后,虽然可以重新创建,但无法调度到其他主机。这是 Docker 默认存储卷的一个局限性。
为了实现跨主机的存储共享,通常需要搭建共享存储(如 NFS),但这种解决方案对运维人员的能力有较高要求。为了解决存储和数据的分离问题,越来越多的分布式存储方案(如 S3、NFS 等)应运而生。
-
启动参数管理
容器的启动选项相较于传统进程启动更加复杂,且容器在重启时常常无法记住上次启动时的参数。
为了确保容器能够以相同的配置启动,通常需要将启动参数保存在配置文件中。这正是容器编排工具(如 Kubernetes)发挥作用的地方。
虽然可以通过命令启动容器,并在文件中读取相关参数,但这种方式仅适用于单个容器的管理。对于需要管理几十或上百个容器的复杂应用,使用专业的容器编排工具(如 Kubernetes 或各大云厂商的企业版编排软件)是必不可少的。
-
复杂场景依赖运维经验
对于有状态且需要持久化的集群化组件(如 MySQL 主从复制),其部署和维护往往需要丰富的运维知识和经验。
例如,在部署、扩展、缩容或修复故障时,必须清楚集群规模、主从节点数量以及每个节点的具体配置。
这种复杂场景的管理对运维人员提出了高要求,目前还没有完美的自动化工具能够完全替代人工干预。因此,在这些复杂场景中,运维经验仍然至关重要。