Docker:Docker Volume存储卷-宿主机与容器的数据双向交流通道
- 一.什么是存储卷?意义?以及分类
- 二.熟悉三种存储卷的常用命令及简单实践
-
- 2.1管理卷volume
-
- 2.1.1通过命令操作
-
- [docker volume create](#docker volume create)
- [docker volume inspect](#docker volume inspect)
- [docker volume ls](#docker volume ls)
- [docker volume rm](#docker volume rm)
- [docker volume prune](#docker volume prune)
- 2.1.2通过-v或--mount指定
-
- [-v 参数](#-v 参数)
- --mount参数
- dockerfile匿名卷
- 2.2绑定卷
- 2.3临时卷
- 2.4使用样例-多个容器共享一个存储卷(除临时卷)
- 三.综合实践-Mysql灾难恢复
- 四.其他常见问题及扩展思考
-
- [什么时候用 Volume,什么时候用 bind、 tmpfs?](#什么时候用 Volume,什么时候用 bind、 tmpfs?)
- 扩展思考:存储卷在实际研发中带来了哪些问题
一.什么是存储卷?意义?以及分类
存储卷就是将宿主机的本地文件系统中存在的某个目录直接与容器内部的文件系统上的某一目录建立绑定关系。这就意味着,当我们在容器中的这个目录下写入数据时,容器会将其内容直接写入到宿主机上与此容器建立了绑定关系的目录。
在宿主机上的这个与容器形成绑定关系的目录被称作存储卷。卷的本质是文件或者目录,它可以绕过默认的联合文件系统,直接以文件或目录的形式存在于宿主机上。宿主机的/data/web 目录与容器中的/container/data/web 目录绑定关系,然后容器中的进程向这个目录中写数据时,是直接写在宿主机的目录上的,绕过容器文件系统与宿主机的文件系统建立关联关系,使得可以在宿主机和容器内共享数据库内容,让容器直接访问宿主机中的内容,也可以宿主机向容器写入内容, 容器和宿主机的数据读写是同步的。

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

二.熟悉三种存储卷的常用命令及简单实践
存储卷可以通过命令方式创建,也可以在创建容器的时候通过 -v and --mount 指定。
2.1管理卷volume
2.1.1通过命令操作
| 命令 | 别名 | 功能 | 备注 |
|---|---|---|---|
| docker volume create | 创建存储卷 | ||
| docker volume inspect | 显示存储卷详细信息 | ||
| docker volume ls | docker volume list | 列出存储卷 | |
| docker volume prune | 清理所有无用数据卷 | ||
| docker volume rm | 删除卷 | 使用中的卷无法删除 |
docker volume create
创建存储卷
语法
bash
docker volume create [OPTIONS] [VOLUME]
关键参数
- -d, --driver: 指定驱动,默认是 local
- --label: 指定元数据
一般我们不会去修改驱动,如果想要了解-d选项的读者可以查看官方文档,我们下面只演示下无参与加--label的情况:
比如我们直接执行此命令,docker会直接创建一个匿名卷:
bash
knd@NightCode:~$ sudo docker volume create
[sudo] password for knd:
fe6721d49d3e3fa4794db8f5fd06c0c487f439041433a62953a7b376217fffc8
我们可以使用docker inspect查看此存储卷的详情:
bash
knd@NightCode:~$ sudo docker inspect fe6721d49d3e3fa4794db8f5fd06c0c487f439041433a62953a7b376217fffc8
[
{
"CreatedAt": "2025-12-29T17:19:08+08:00",
"Driver": "local",
"Labels": {
"com.docker.volume.anonymous": ""
},
"Mountpoint": "/var/lib/docker/volumes/fe6721d49d3e3fa4794db8f5fd06c0c487f439041433a62953a7b376217fffc8/_data",
"Name": "fe6721d49d3e3fa4794db8f5fd06c0c487f439041433a62953a7b376217fffc8",
"Options": null,
"Scope": "local"
}
]
可以看到此存储卷默认情况下是在/var/lib/docker/volumes/目录下的。
如果我们指定其--label属性,其实就像我们浏览视频网站时,不同的视频被划分到了不同的分类标签下,主要是为了方便查找目标存储卷而存在的,毕竟元数据本就是对数据描述的数据。
我们来使用此选项去创建一个存储卷,至于如何使用此'标签',我们到ls那里在演示:
bash
knd@NightCode:~$ sudo docker volume create mytestvolume --label MYTEST=1
mytestvolume
knd@NightCode:~$ sudo docker volume inspect mytestvolume
[
{
"CreatedAt": "2025-12-29T17:26:10+08:00",
"Driver": "local",
"Labels": {
"MYTEST": "1"
},
"Mountpoint": "/var/lib/docker/volumes/mytestvolume/_data",
"Name": "mytestvolume",
"Options": null,
"Scope": "local"
}
]
docker volume inspect
查看卷详细信息
语法
bash
docker volume inspect [OPTIONS] VOLUME [VOLUME...]
关键参数
- -f: 指定相应个格式,如 json
emm,这里就不作演示了,详情可以看上面的create部分。
docker volume ls
功能
列出卷
语法
bash
docker volume ls [OPTIONS]
关键参数
- --format: 指定相应个格式,如 json,table
- --filter,-f: 过滤
- -q: 仅显示名称
比如我想要查询所有的存储卷信息,不带任何参数:
bash
knd@NightCode:~$ sudo docker volume ls
DRIVER VOLUME NAME
local fe6721d49d3e3fa4794db8f5fd06c0c487f439041433a62953a7b376217fffc8
local mytestvolume
想让其以json格式输出:
bash
knd@NightCode:~$ sudo docker volume ls --format json
{"Availability":"N/A","Driver":"local","Group":"N/A","Labels":"com.docker.volume.anonymous=","Links":"N/A","Mountpoint":"/var/lib/docker/volumes/fe6721d49d3e3fa4794db8f5fd06c0c487f439041433a62953a7b376217fffc8/_data","Name":"fe6721d49d3e3fa4794db8f5fd06c0c487f439041433a62953a7b376217fffc8","Scope":"local","Size":"N/A","Status":"N/A"}
{"Availability":"N/A","Driver":"local","Group":"N/A","Labels":"MYTEST=1","Links":"N/A","Mountpoint":"/var/lib/docker/volumes/mytestvolume/_data","Name":"mytestvolume","Scope":"local","Size":"N/A","Status":"N/A"}
想通过一定条件的过滤查询对应存储卷,比如我们使用label过滤:
bash
knd@NightCode:~$ sudo docker volume ls -f label=MYTEST
DRIVER VOLUME NAME
local mytestvolume
knd@NightCode:~$ sudo docker volume ls -f label=MYTEST=1
DRIVER VOLUME NAME
local mytestvolume
knd@NightCode:~$ sudo docker volume ls -f label=MYTEST=0
DRIVER VOLUME NAME
docker volume rm
功能
删除卷,需要容器不使用。
语法
bash
docker volume rm [OPTIONS] VOLUME [VOLUME...]
关键参数
- -f,--force:强制删除
这个命令不再多说,毕竟比较简单就不演示了。
docker volume prune
功能
删除不使用的本地卷
语法
bash
docker volume prune [OPTIONS]
关键参数
- --filter:过滤
- -f, --force :不提示是否删除
常规的prune不带任何参数的使用就不展示了,加-f则是不弹出任何删除提醒,并不会删除正在使用的存储卷 。我们这里展示下标签过滤删除的情况:
需要注意的一个问题是,默认的 prune 命令不处理未被使用的具名卷!!!
此时则需要加上--all或-a选项,此选项的作用是:在api:1.42+中官网文档中描述如下:Remove all unused volumes, not just anonymous ones
bash
knd@NightCode:~$ sudo docker volume prune -f -a --filter label=MYTEST=1
Deleted Volumes:
mytestvolume
Total reclaimed space: 0B
2.1.2通过-v或--mount指定
-v 和-mount 都可以完成管理卷的创建
-v 参数
功能
完成目录映射
语法
bash
docker run -v name:directory[:options] .........
参数
- 第一个参数:卷名称
- 第二个参数:卷映射到容器的目录
- 第三个参数:选项,如 ro 表示 readonly
这里需要说明的东西是,name位置如果给的是一个单纯的名字的话,它创建的是一个管理卷,但如果给的是宿主机上一个实际存在的文件目录的话,它创建的是一个绑定卷。
我们还是使用nginx来做下演示,比如我们让管理卷去映射其首页html文件所在的目录:
bash
knd@NightCode:~$ sudo docker run -d --name nginx -v mytestvolume:/usr/share/nginx/html -p 8080:80 nginx:1.29.4
eaf2c4533235c8f267fc877a6baaca669e43c5091a1126d635076e941f622b84
knd@NightCode:~$ sudo docker inspect mytestvolume
[
{
"CreatedAt": "2025-12-29T18:31:12+08:00",
"Driver": "local",
"Labels": null,
"Mountpoint": "/var/lib/docker/volumes/mytestvolume/_data",
"Name": "mytestvolume",
"Options": null,
"Scope": "local"
}
]
knd@NightCode:~$ sudo docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
eaf2c4533235 nginx:1.29.4 "/docker-entrypoint...." 22 seconds ago Up 21 seconds 0.0.0.0:8080->80/tcp, [::]:8080->80/tcp nginx
knd@NightCode:~$ sudo ls /var/lib/docker/volumes/mytestvolume/_data
50x.html index.html
可以看到此时nginx容器的首页目录下的所有文件都映射到了Mountpoint后的目录下了。我们再看下容器的详细信息:
bash
"Mounts": [
{
"Type": "volume",
"Name": "mytestvolume",
"Source": "/var/lib/docker/volumes/mytestvolume/_data",
"Destination": "/usr/share/nginx/html",
"Driver": "local",
"Mode": "z",
"RW": true,
"Propagation": ""
}
],
它立面也有一个Mounts,记录了对应的映射目录。
如果我们直接从宿主机上修改此index.html文件,则访问此容器的监听端口时内容也会发生改变:
bash
knd@NightCode:~$ sudo vim /var/lib/docker/volumes/mytestvolume/_data/index.html
knd@NightCode:~$ curl 127.0.0.1:8080
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
html { color-scheme: light dark; }
body { width: 35em; margin: 0 auto;
font-family: Tahoma, Verdana, Arial, sans-serif; }
</style>
</head>
<body>
<h1>Welcome to nginx change not in container!</h1>
<p>If you see this page, the nginx web server is successfully installed and
working. Further configuration is required.</p>
<p>For online documentation and support please refer to
<a href="http://nginx.org/">nginx.org</a>.<br/>
Commercial support is available at
<a href="http://nginx.com/">nginx.com</a>.</p>
<p><em>Thank you for using nginx.</em></p>
</body>
</html>
所以等于说容器内我们指定的那个目录实际上往立面修改或新增删除数据时其实就是对宿主机中创建存储卷时所指定的目录进行响应操作。
如果我们在启动容器-v绑定存储卷时加上了ro/readonly,那么我们从容器内部对绑定目录下的文件进行修改时会爆如下错误,但是此时仍可以在宿主机上对相应文件进行修改:
bash
root@de712d158b67:/# echo "hello world" > /usr/share/nginx/html/index.html
bash: /usr/share/nginx/html/index.html: Read-only file system
这里再额外说一个点,如果指定的目录不存在,那么-v选项会自动在容器内部创建对应目录。下面的--mount则是直接报错。
--mount参数
功能
完成目录映射
语法
bash
--mount <key>=<value>,<key>=<value>
关键参数
- type : 类型表示 bind, volume, or tmpfs
- source , src :对于命名卷,这是卷的名称。对于匿名卷,省略此字段。
- destination, dst,target:文件或目录挂载在容器中的路径
- ro,readonly: 只读方式挂载
我们这里演示下使用--mount参数对容器绑定一个管理卷:
bash
knd@NightCode:~$ sudo docker run --name=nginx -d -p 8080:80 --mount src=mytestvolume,dst=/usr/share/nginx/html,ro nginx:1.29.4
1e2101ec3f1ff17d6da3234212a030966aa495baf7a1386586e83fbcfe2cbf1f
当然这是个只读管理卷,所以情况和上面-v选项的最后部分说的一样,容器内无法进行修改。
dockerfile匿名卷
通过 Dockerfile 的 VOLUME 可以创建 docker 管理卷。这个我们后续在 Dockerfile中详细讲解。
我们也可以通过 dockerfile 的 VOLUME 指令在镜像中创建 Data Volume,这样只要通过该镜像创建的容器都会存在挂载点,但值得注意的是通过 VOLUME 指令创建的挂载点,无法指定主机上对应的目录,而是由 docker 随机生成的。
2.2绑定卷
-v参数创建
选项与语法与管理卷处展示一致。
需要提前说明一点的是,如果指定的宿主机路径不存在,-v则是会直接创建对应目录,而--mount不会,会直接报错。和上面dst指定不存在时一致。
创建绑定卷很简单,只要把name的位置输入为宿主机上的一个目录即可:
bash
knd@NightCode:~/dockertest$ ls
namespacets
knd@NightCode:~/dockertest$ sudo docker run -d -p 8080:80 --name=nginx -v ./test:/usr/share/nginx/html:ro nginx:1.29.4
fe04ea05bdb21a128092d84c326a8387002a6701a8b3f60b8d60d49aa79b7f28
knd@NightCode:~/dockertest$ ls
namespacets test
knd@NightCode:~/dockertest$ sudo docker volume ls
DRIVER VOLUME NAME
local mytestvolume
knd@NightCode:~/dockertest$ sudo docker inspect nginx
...
"Mounts": [
{
"Type": "bind",
"Source": "/home/knd/dockertest/test",
"Destination": "/usr/share/nginx/html",
"Mode": "ro",
"RW": false,
"Propagation": "rprivate"
}
],
...
可以看到此时的Mounts中的type类型为bind。
--mount参数创建
选项与语法与管理卷处展示一致。
也很简单,我们这里使用--mount创建一个只读绑定卷出来,但是需要注意,创建管理卷时必须显示指定type为bind,否则docker会认为你创建的是一个volume管理卷,而且命名会直接报错:
bash
knd@NightCode:~/dockertest$ sudo docker run -p 8080:80 --mount src=/home/knd/dockertest/test,dst=/usr/share/nginx/html,ro --name=nginx -d nginx:1.29.4
docker: Error response from daemon: create /home/knd/dockertest/test: "/home/knd/dockertest/test" includes invalid characters for a local volume name, only "[a-zA-Z0-9][a-zA-Z0-9_.-]" are allowed. If you intended to pass a host directory, use absolute path
Run 'docker run --help' for more information
knd@NightCode:~/dockertest$ sudo docker run -p 8080:80 --mount type=bind,src=./test,dst=/usr/share/nginx/html,ro --name=nginx -d nginx:1.29.4
403cc6195b8ff5687bbf0aacd87cd6486c6f6d0c1458abdb1b0df6a320766bb1
knd@NightCode:~/dockertest$ sudo docker inspect nginx
...
"Mounts": [
{
"Type": "bind",
"Source": "/home/knd/dockertest/test",
"Target": "/usr/share/nginx/html",
"ReadOnly": true
}
],
...
"Mounts": [
{
"Type": "bind",
"Source": "/home/knd/dockertest/test",
"Destination": "/usr/share/nginx/html",
"Mode": "",
"RW": false,
"Propagation": "rprivate"
}
],
...
2.3临时卷
我们上面说过,临时卷实际上是存储在内存中的。而内存我们是无法轻松的访问到我们指定的内容的除非通过相应程序。也确保了数据的安全性。不过需要注意的是,创建临时卷时会清空容器内对应目录下的所有文件。
--tmpfs创建
功能
完成临时卷映射
语法
bash
--tmpfs /app
我们使用run加--tmpfs为nginx首页目录指定一个临时卷:
bash
knd@NightCode:~/dockertest$ sudo docker run --name nginx -d -p 8080:80 --tmpfs /usr/share/nginx/html nginx:1.29.4
dc2fb625d39233c528145e076cff21e651b6d8c70fa638d47eeeec0e8992a4ff
knd@NightCode:~/dockertest$ sudo docker exec -it nginx ls /usr/share/nginx/html
knd@NightCode:~/dockertest$
如果我们在宿主机中查找对应目录下的文件也是查找不到的:
bash
knd@NightCode:~/dockertest$ sudo docker exec -it nginx bash
root@dc2fb625d392:/# cd /usr/share/nginx/html/
root@dc2fb625d392:/usr/share/nginx/html# touch mytest114514.txt
root@dc2fb625d392:/usr/share/nginx/html# echo 1 > mytest114514.txt
root@dc2fb625d392:/usr/share/nginx/html# exit
exit
knd@NightCode:~/dockertest$ sudo find / -name mytest114514.txt
knd@NightCode:~/dockertest$
--mount创建
功能
完成目录映射
语法
bash
--mount <key>=<value>,<key>=<value>
关键参数
- type : 类型表示 bind, volume, or tmpfs
- destination, dst,target:挂载在容器中的路径
- tmpfs-size: tmpfs 挂载的大小(以字节为单位)。默认无限制。
- tmpfs-mode: tmpfs 的八进制文件模式。例如, 700 或 0770。默认为 1777或全局可写。
它当然也会覆盖容器内对应目录下的所有文件,这里我们就演示下tmpfs-size选项,如果我们放入了一个超出其限制的文件会怎么样?:
bash
knd@NightCode:~/dockertest$ sudo docker cp ./card_normal_11.png nginx:/usr/share/nginx/html
Successfully copied 2.56MB to nginx:/usr/share/nginx/html
knd@NightCode:~/dockertest$ sudo docker exec -it nginx ls /usr/share/nginx/html
knd@NightCode:~/dockertest$
实际上并没有拷过去,我们来看下具体原因,换种方式拷贝,先将此文件拷贝到容器内的一个临时目录下,然后再到容器内部进行拷贝:
bash
knd@NightCode:~/dockertest$ sudo docker exec -it nginx mkdir /mytest
knd@NightCode:~/dockertest$ sudo docker cp ./card_normal_11.png nginx:/mytest
Successfully copied 2.56MB to nginx:/mytest
knd@NightCode:~/dockertest$ sudo docker exec -it nginx bash
root@860bdfa81188:/# cd /usr/share/nginx/html/
root@860bdfa81188:/usr/share/nginx/html# cp /mytest/card_normal_11.png ./
cp: error writing './card_normal_11.png': No space left on device
他说没有足够的空间,便足以说明我们上面的设置起效果了。
2.4使用样例-多个容器共享一个存储卷(除临时卷)
我们可以让多个容器去使用同一个绑定/管理卷实现不同容器下的数据同步:
这里我们以比较复杂些的--mount及较为常用的绑定卷做一个示例:
bash
knd@NightCode:~/dockertest$ sudo docker run --name nginx1 -d -p 8080:80 --mount type=bind,src=./test,dst=/usr/share/nginx/html nginx:1.29.4
e7ed6e95bf5c6b5d292d68d408ea2f6001465f5d7e60228f9916758ff0f93a8f
knd@NightCode:~/dockertest$ sudo docker run --name nginx2 -d -p 8081:80 --mount type=bind,src=./test,dst=/usr/share/nginx/html nginx:1.29.4
dfb5c9da06c61899d6590340ed6b632ef10fa1ecd5477e4fdb8453759f1221ef
knd@NightCode:~/dockertest$ sudo docker run --name nginx3 -d -p 8082:80 --mount type=bind,src=./test,dst=/usr/share/nginx/html nginx:1.29.4
eee3e7179def413e41ee2b722e5f846d66d68587872ac1dc777ad77d0a2e2aa7
knd@NightCode:~/dockertest$ ls ./test
knd@NightCode:~/dockertest$ echo "hello world" > ./test/index.html
knd@NightCode:~/dockertest$ curl 127.0.0.1:8080
hello world
knd@NightCode:~/dockertest$ curl 127.0.0.1:8081
hello world
knd@NightCode:~/dockertest$ curl 127.0.0.1:8082
hello world
可以看到不同的容器访问的结果却是一样的。
三.综合实践-Mysql灾难恢复
如果我们的mysql容器的数据库数据存放在容器内部,一旦不小心删除了容器,之前的文件就没有了,所以这里我们可以将mysql中的数据库数据在创建始就放到宿主机中实际存在的一个目录下最好:
我们根据官网给的提示进行存储卷绑定:

bash
knd@NightCode:~/dockertest$ sudo docker run --name mysql -p 8080:3306 -e MYSQL_ROOT_PASSWORD=114514 -d -v ./mysql:/var/lib/mysql mysql:8.0.44
305b13110d9b3d135ade72443e00bf4806e31daab1817d111fea5ab20ccb4e5a
这时候我们进入容器随便给他创建一个表插入几条数据,我直接让ai给我生成了:
bash
-- 1. 创建数据库
CREATE DATABASE demo_db
DEFAULT CHARACTER SET utf8mb4
COLLATE utf8mb4_general_ci;
-- 2. 使用数据库
USE demo_db;
-- 3. 创建表
CREATE TABLE users (
id INT PRIMARY KEY AUTO_INCREMENT,
username VARCHAR(50) NOT NULL,
email VARCHAR(100),
age INT,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
-- 4. 插入 3 条数据
INSERT INTO users (username, email, age) VALUES
('alice', 'alice@example.com', 23),
('bob', 'bob@example.com', 30),
('charlie', 'charlie@example.com', 28);
bash
knd@NightCode:~/dockertest$ ls
mysql namespacets test test.sql
knd@NightCode:~/dockertest$ sudo docker cp ./test.sql mysql:/test.sql
Successfully copied 2.56kB to mysql:/test.sql
knd@NightCode:~/dockertest$ sudo docker exec -it mysql ls /
afs bin boot dev docker-entrypoint-initdb.d entrypoint.sh etc home lib lib64 media mnt opt proc root run sbin srv sys test.sql tmp usr var
knd@NightCode:~/dockertest$ sudo docker exec -it mysql bash
bash-5.1# mysql -uroot -p < test.sql
Enter password:
bash-5.1# exit
exit
可以在navicat中看到对应数据库与数据:

然后我们把这个容器删除了,再新启一个此容器:
bash
knd@NightCode:~/dockertest$ sudo docker stop mysql
mysql
knd@NightCode:~/dockertest$ sudo docker rm mysql
mysql
knd@NightCode:~/dockertest$ sudo docker run --name mysql -v ./mysql:/var/lib/mysql -e MYSQL_ROOT_PASSWORD=114514 -d -p 8080:3306 mysql:8.0.44
a466d85bdec005497e2f9bd13400fd4b8b8e4dd930cd09d65c6bb49869198f18
此时我们发现我们之前的数据仍然存在!!!

四.其他常见问题及扩展思考
什么时候用 Volume,什么时候用 bind、 tmpfs?
volume : volume 是 docker 的宿主机文件系统一部分,用于不需要规划具体目录的场景
bind : bind mount 完全是依赖于主机的目录结构和操作系统,用于目录需要提前规划,比如 mysql 的目录需要个空间大的,其他服务有不占用的时候,用 volume 就不太合适了
tmpfs:用于敏感文件存储,文件不想存储的宿主机和容器的可写层之中
扩展思考:存储卷在实际研发中带来了哪些问题
- 跨主机使用
docker 存储卷是使用其所在的宿主机上的本地文件系统目录,也就是宿主机有一块磁盘,这块磁盘并没有共享给其他的 docker 主机,容器在这宿主机上停止或删除,是可以重新再创建的,但是不能调度到其他的主机上,这也是 docker 本身没有解决的问题,所以 docker 存储卷默认就是 docker 所在主机的本地,但是自己搭建一个共享的 NFS来存储 docker 存储的数据,也可以实现, 但是这个过程强依赖于运维人员的能力。所以未来应用的存储和数据往往分离,越来越多的分布式存储方案出现,如 s3 系列,nfs 等。 - 启动参数未知
容器有一个问题,一般与进程的启动不太一样,就是容器启动时选项比较多,如果下次再启动时,很容器会忘记它启动时的选项,所以最好有一个文件来保存容器的启动,这就是容器编排工具的作用。一般情况下,是使用命令来启动操作 docker,但是可以通过文件来读,也就读文件来启动,读所需要的存储卷等,但是它也只是操作一个容器,如果要几十上百个容器操作,就需要专业的容器编排工具这种一般像开源的 k8s,各个云厂商也有自己的企业版编排软件。 - 复杂场景仍然需要运维对于有状态要持久的集群化组件,如 mysql 的主从。部署维护一个 Mysql 主从需要运维知识、经验整合进去才能实现所谓的部署,扩展或缩容,出现问题后修复,必须要了解集群的规模有多大,有多少个主节点,有多少个从节点,主节点上有多少个库,这些都要一清二楚,才能修复故障,这些就强依赖于运维经验这种复杂的场景往往还是需要人力,很难有完美的工具出现。