"Ice in my vein"
Docker Volume(存储卷)
什么是存储卷?
存储卷就是: "将宿主机的本地文件系统中存在的某个目录,与容器内部的文件系统上的某一目录建立绑定关系"。
存储卷与容器本身的联合文件系统?
在宿主机上的这个与容器形成绑定关系的目录被称作"存储卷"。而这个卷的本质就是文件或者目录,它可以绕过默认的联合文件系统,直接以文件或目录的形式存在于宿主机上。
使得**可以在宿主机和容器内共享数据库内容****。**让容器直接访问宿主机中的内容,也可以宿主机向容器写入内容,容器和宿主机的数据读写是同步的。
为什么需要存储卷?
存储卷技术的产生,主要来源于解决四个问题的需求。
✨ 数据丢失
容器按照业务类型,总体可以分为两类:
• 无状态(数据不需要被持久化)
• 有状态(数据需要被持久化)
显然,容器更擅长无状态应用。因为,容器根目录的生命周期,同容器的生命周期一样。容器文件系统的本质是在镜像层上面创建的读写层,运行中的容器对任何文件的修改都存在于该读写层,当容器被删除时,容器中的读写层也会随之消失。
但实际业务总是有各种需要数据持久化的场景: 比如 MySQL、Kafka 等有状态的业务。所以,Docker 提出了卷(volume)的概念
✨ 性能问题
UnionFS 对于修改删除等,一般效率非常低。
✨ 宿主机和容器互访不方便
宿主机访问容器,或者容器访问要通过 docker cp 来完成,应用很难操作。
✨ 容器和容器共享不方便
两个容器内部不同的目录,同时绑定宿主机上的某一目录,就可以实现数据共享。
存储卷分类
目前 Docker 提供了三种方式将数据从宿主机挂载到容器中:
🩰 volume docker 管理卷
🩰 bind mount 绑定数据卷
🩰 tmpfs mount 临时数据卷
下图表示三类存储卷,在Docker存储卷中的结构:
存储卷详解
管理卷 Volume
docker管理卷默认映射到宿主机的 /var/lib/docker/volumes(如果修改了默认目录,就不是这条路径)目录下,只需要在容器内指定要挂在点是什么。容器引擎daemon会自行创建一个空的目录在被绑定的宿主机目录下。或者使用一个已经存在的目录,与存储卷建立存储关系。
这种方式极大解脱用户在使用卷时的耦合关系,缺陷是用户无法指定那些使用目录,临时存储比较适合。
创建卷:
存储卷可以通过命令方式创建,也可以通过在创建容器的时候通过 -v and --mount 指定。
方法一:Volume 命令操作
操作docker管理卷的命令清单如下:
|-----------------------|--------------------|------------|
| 命令 | 别名 | 功能 |
| 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]
OPTIONS:
-d, --driver:指定驱动,默认是 local
--label:指定元数据
🎱 docker volume inspect
语法:
bash
# 查看卷详细信息
docker volume inspect [OPTIONS] VOLUME [VOLUME...]
OPTIONS:
-f:指定相应个格式
🎱 docker volume ls
语法:
bash
# 列出卷
docker volume ls [OPTIONS]
OPTIONS:
--format:指定相应个格式
--filter,-f: 过滤
-q: 仅显示名称
🎱 docker volume rm
语法:
bash
# 删除卷,需要容器不使用
ocker volume rm [OPTIONS] VOLUME [VOLUME...]
OPTIONS:
-f,--force:强制删除
🎱 docker volume prune
语法:
bash
# 删除不使用的本地卷
docker volume prune [OPTIONS]
OPTIONS:
--filter:过滤
-f, --force :不提示是否删除
方法二: -v 或者 --mount 指定
bash
# -v参数 完成目录映射
docker run -v name:directory[:options]
ARGS:
name:卷名称
directory:卷映射到容器的目录
options:如 ro 表示 readonly
bash
# --mount 参数
--mount '<key>=<value>,<key>=<value>'
ARGS:
type:类型表示 bind, volume, or tmpfs
source\src :对于命名卷,这是卷的名称。[对于匿名卷,省略此字段]
destination\dst\target:文件或目录挂载在容器中的路径
ro,readonly: 只读方式挂载
方法三: Dockerfile 匿名卷
通过Dockerfile的VOLUME可以创建docker管理卷。但不在本篇细说。
操作案例:
Docker 命令创建管理卷
使用 "docker create" 创建一个名为 "test_volume"的存储卷:
输入 "docker volume ls" 查看我们创建的存储卷:
检查存储卷的内部信息"docker volume inspect":
我们现在访问这个目录:
此时可以发现,该目录是被自动创建的空目录。
我们给该存储卷分配给一个nginx容器时,就会发现:
宿主机和容器之间数据是同步的
Docker -v 创建管理卷
-v 创建管理卷,并且启动容器:
bash
docker run --name nginx1 -d -p 80:80
-vtest_volume:/usr/share/nginx/html:ro nginx:1.24.0
进入卷的目录:
我们看到,创建docker管理卷后,宿主机的目录同容器目录文件实际是共享的~我们可以借此修改index.html文件:
这是修改前的原Nginx首页:
修改后(注:我们是在宿主机的目录,对这个首页文件进行修改):
我们再次进入docker容器中,看看修改容器内的文件后,宿主机会不会更新:
我们会发现,在容器内部是不能对该关联目录内的文件进行修改的!答案是,我们在创建管理卷的时候,设置了"ro"参数 ------ 所以,这个ro参数针对的对象是,谨防从容器内部对文件进行修改!
最后,我们再清理释放空间。
指定 ro 的话宿主机可以修改,但是容器里面无法修改
Docker mount 创建管理卷
mount 创建管理卷,并且启动容器:
bash
docker run -d -p 80:80 --name nginx
--mount
type=volume,source=test_volume,target=/usr/share/nginx/html nginx:latest
ARGS:
type有很多: volume表示管理存储卷 bind表示绑定存储卷 tmpfs表示临时存储卷
查看卷、容器创建成功:
查看卷挂载点:
Docker 卷生命周期
我们继续上接前文,为一个nginx容器创建了一个存储卷,我们可以在容器内、宿主机上找到这个存储卷同步的内容:
当我们将容器删除时:
存储卷的内容还是存在的,如果此时我们使用命令删除存储卷时,存储卷的内容就会被彻底清空:
Docker 卷共享
-v 创建管理卷,并且启动 2 容器,指定同一个存储卷。
bash
docker container run --name nginx1 -d -p 80:80
-v mutual_volume:/usr/share/nginx/html nginx:1.24.0
docker container run --name nginx2 -d -p 81:80
-v mutual_volume:/usr/share/nginx/html nginx:1.23.0
观察存储卷信息,修改 index.html:
观察容器内部的文件,发现都是同一份:
绑定卷 bind mount
绑定数据卷,需要人工在宿主机上指定一个特定路径,在容器中指定一个特定路径,再建立这两个特定路径的关联。
与管理卷不同,绑定卷的有些参数是不一样的。
bash
docker run -v name:directory[:options]
ARGS:
name:宿主机目录,这个和管理卷是不一样的
bash
--mount '<key>=<value>,<key>=<value>'
ARGS:
source\src: 宿主机目录,这个和管理卷是不一样的
其余参数都大差不差,咱们在这儿就不多费舌,直接上操作案例。
操作案例:
--mount 创建管理卷
使用-mount 方式创建容器:
创建 nginx 容器,并将宿主机 "/webapp" 目录挂载至容器"/usr/share/nginx/html"目录。
注:如果这个目录不存在,就会报错
bash
docker run -d -p 80:80 --name bind
--mount
type=bind,source=/data/webapp,target=/usr/share/nginx/html nginx:1.23.0
# source:人为指定路径
查看挂载信息:
bash
docker inspect bind
进入容器的终端,查看挂载点目录,和在宿主机上查看里面都是没有文件:
这是 bind mount 模式和 volume 模式最大的不同点!!
我们在宿主机上添加 index.html,通过浏览器,我们可以访问到这个文件内容:
删除容器,然后查看宿主机上的文件 , 还是存在,说明容器删除并不影响 bind 映射。
-v 创建绑定卷
使用-v 方式创建容器: 创建 nginx 容器:
并将宿主机/webapp2 目录挂载至容器 /usr/share/nginx/html 目录。注:当目录不存在时,仍然会报错。
bash
docker run -d -p 80:80 --name bind
-v /data/webapp2:/usr/share/nginx/html nginx:1.23.0
查看挂在信息:
后面的演示情况可以参照 使用 --mount方式创建的存储卷。
绑定卷共享
我们启动两个绑定卷,都绑定到宿主机的同一个目录:
我们使用本机回环,这两个容器看到的内容是一样的。
临时卷 tmpfs
临时卷数据位于内存中,在容器和宿主机之外。映射到于宿主机内存中,一旦容器停止运行,tmpfs mounts就会被移除,数据就会丢失,用于高性能的临时数据存储。
tmpfs 局限性:
🪀 不同于卷和绑定挂载,不能在容器之间共享 tmpfs 挂载
🪀 这个功能只有在 Linux 上运行 Docker 时才可用
创建卷:
方式一: 指定--tmpfs 创建
bash
--tmpfs /path
方式二:--mount 指定参数创建
bash
--mount '<key>=<value>,<key>=<value>'
OPTIONS:
destination,dst,target:挂载在容器中的路径
tmpfs-size:tmpfs 挂载的大小(以字节为单位)。默认无限制
tmpfs-mode:tmpfs 的八进制文件模式。例如,700 或 0770。默认为 1777或全局可写
操作案例:
tmpfs 参数创建临时卷
创建临时卷并启动容器
bash
docker container run --name tmpfs -d -p 80:80
--tmpfs /usr/share/nginx/html/ nginx:latest
进入容器可以看到 nginx 里面的文件被覆盖了,也就是说 **tmpfs也会覆盖容器里面的文件。**绑定卷也是如此。
添加一个首页:
我们可以在浏览器中查看:
我们现在重启容器,发现之前的文件不存在了!
mount 创建临时卷
创建临时卷并启动容器:
bash
docker container run --name tmpfs -d -p 80:80
--mount
type=tmpfs,target=/usr/share/nginx/html,tmpfs-size=1m nginx:1.23.0
添加一个首页:
tmp-size:
拷贝一个大文件到容器里面:
拷贝文件到我们的卷目录,超过了限制(tmpfs-size),空间限制为了 1m,会提示没有空间。
tmpfs 失踪了
创建一个普通的容器:
bash
docker run -d -it --name tmpfs nginx:1.23.0
在容器里面写入一个文件 mylabel.txt:
我们在宿主机上查找文件,文件被找到了,是因为他在容器的可写层:
我们再创建个临时卷:
bash
docker run -d --name tmpfs2 --tmpfs /app nginx:1.24.0
进入容器,在/app 目录下创建 mynewlabel.txt。
在宿主机上查找 mynewlabel.txt,可以发现,文件找不到。
所以 tmpfs 的内容不是存储在我们的容器的可写层里面的。更加进一步佐证其存储的位置是在内存上。
本篇到此结束,感谢你的阅读。