Docker Volume

"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 的内容不是存储在我们的容器的可写层里面的。更加进一步佐证其存储的位置是在内存上。


本篇到此结束,感谢你的阅读。

相关推荐
zeruns80236 分钟前
如何搭建自己的域名邮箱服务器?Poste.io邮箱服务器搭建教程,Linux+Docker搭建邮件服务器的教程
linux·运维·服务器·docker·网站
爱跑步的程序员~36 分钟前
Docker
docker·容器
福大大架构师每日一题1 小时前
23.1 k8s监控中标签relabel的应用和原理
java·容器·kubernetes
程序那点事儿1 小时前
k8s 之动态创建pv失败(踩坑)
云原生·容器·kubernetes
疯狂的大狗1 小时前
docker进入正在运行的容器,exit后的比较
运维·docker·容器
长天一色1 小时前
【Docker从入门到进阶】01.介绍 & 02.基础使用
运维·docker·容器
伊玛目的门徒1 小时前
docker 搭建minimalist-web-notepad
运维·docker·notepad
theo.wu4 小时前
使用Buildpacks构建Docker镜像
运维·docker·容器
唐大爹13 小时前
项目实战:k8s部署考试系统
云原生·容器·kubernetes
wusam13 小时前
螺蛳壳里做道场:老破机搭建的私人数据中心---Centos下Docker学习04(环境准备)
学习·docker·centos