定义
是什么 :存储卷就是将宿主机本地文件系统中存在的某个目录直接与容器内部的文件系统上的某一个目录建立绑定关系,当我们在容器的这个目录下写入数据的时候,容器会将其内容直接写入宿主机与此容器建立了绑定关系的目录上。
在宿主机上的这个与容器形成绑定关系的目录称为存储卷,卷的本质是文件或者目录,它可以绕过默认的联合文件系统,直接以文件或者目录的形式存在于宿主机上。
为什么:为什么需要存储卷呢?
- 数据丢失问题
容器按照业务类型分为两类,第一类是无状态的,数据不需要被持久化,第二类是有状态的,数据需要被持久化。
容器更擅长无状态应用,因为未持久化的容器根目录的生命周期和容器的生命周期一样,容器文件系统的本质是在镜像层上面创建的读写层,运行中的容器对任何文件的修改都只存在于读写层,当容器被删除的时候,容器中的读写层也会随之消失。但实际业务中总是需要各种有状态的场景,比如MySQL,Kafka等有状态的业务。 - 性能问题
UnionFS对于修改删除时,一般效率非常低,对于一个对IO要求比较高的应用,如redis在实现持久化存储时,对于底层存储的性能要求较高。 - 宿主机和容器互相访问不方便
宿主机访问容器,或者容器访问宿主机都要通过docker cp来完成,容器很难操作。 - 容器和容器之间互相访问不方便
分类
volume docker 管理卷
默认映射到宿主机的/var/lib/docker/volumes目录下,只需要在容器内指定容器的挂载点是什么,而被绑定宿主机下的那个目录,是由容器引擎daemon自行创建的一个空目录,或者使用一个已经存在的目录,这种方式降低了耦合关系,缺陷是用户无法指定那些使用目录,临时存储比较适合。
创建卷
volume命令操作
docker volume create
用于创建存储卷。
bash
docker volume create [options] [volume]
-d指定驱动,默认是local
-label指定元数据

docker volume inspect
用于查看卷详细信息。
bash
docker volume inspect [options] volume1 [volume2...]
-f指定相应个格式

docker volume ls
列出所有的卷。
bash
docker volume ls [options]
--format指定相应格式,如json,table等
--filter过滤
-q表示仅显示名称

docker volume rm
用于删除卷,需要在容器不使用的情况下使用。
bash
docker volume rm [options] volume1 [volume2...]
-f表示强制删除

docker volume prune
删除不使用的本地卷。
bash
docker volume prune [options]
--filter表示过滤
-f表示不提示是否删除

-v或者--mount指定
-v参数
bash
docker run -v name:directory[:options]
name表示卷的名称,directory表示卷映射到容器的目录,option表示选项。
当指定了一个-v参数创建容器以后,使用docker inspect就可以看到此存储卷。

--mount参数
用于完成目录映射。
bash
--mount '<key>=<value>,<key>=<value>'
type类型表示bind,volume或者tmpfs
source对于有命名的卷来说,这是卷的名称,对于匿名的卷省略此字段
destination/dst/target表示文件或者目录挂载在容器中的路径
ro表示以只读方式挂载

dockerfile匿名卷
通过dockerfile的volume可以创建docker管理卷,也可以通过dockerfile的volume指令在镜像中创建data volume,这样只要通过改镜像创建的容器都会存在挂载点,但通过volume指令创建的挂载点无法指定主机上对应的目录,而是由docker随机生成的。
操作
docker命令创建管理卷
- 命令创建管理卷
bash
docker volume create 卷名
- 查看docker管理卷
bash
docker volume ls
- 查看其中一个管理卷,可以看到对应的宿主机目录
bash
docker volume inspect 卷名

- 进入宿主机目录,可以看到容器里面的内容已经放到该目录了
bash
cd 目录
ls -l
- 使用docker inspect查看容器,可以看到对应的卷信息
- 修改index.html
bash
cd 目录

- 通过浏览器查看

docker卷生命周期
- -v创建管理卷,并且启动容器
bash
docker container run --name 容器名 -d -p 80:80 -v 卷名:/usr/share/nginx/html nginx
- 进入卷目录,当我们将一个空白的卷挂载到一个容器上以后,容器挂载目录里的文件会自动放到宿主机对应的文件夹里。
- 停止容器,清理容器
bash
docker stop 容器名
docker rm 容器名
- 进入卷目录查看对应的文件,发现没有删除,当删除卷以后,就看不见这个目录了

bind mount绑定数据卷
映射到宿主机指定路径下,在宿主机上的路径要人工指定一个特定的路径,在容器中也需要指定一个特定的路径,两个已知的路径建立联系。
创建卷
-v参数创建卷
bash
docker run -v name:directory[:options]...
name表示宿主机的目录,directory表示卷映射到容器的目录,option是选项,如ro表示readonly只读。
--mount创建卷
--mount用于完成目录映射。
bash
--mount '<key>=<value>,<key>=<value>'
type类型表示bind,volume或者tmpfs
source指的是宿主机的目录
destination/dst/target表示文件或者目录挂载在容器中的路径
ro表示以只读方式挂载
操作
mount创建绑定卷
- 创建绑定卷
bash
mkdir -p /data/mydir/fd/webapp1
docker run -d -p 80:80 --name 容器名 --mount type=bind,source=/data/mydir/fd/webapp1,target=/usr/share/nginx/html nginx
- 进入容器的终端,查看挂载点目录,和宿主机上查看,会发现都是没有文件,这也是存储卷和绑定卷最大的区别,绑定卷会将主机上的文件拷贝到容器中
bash
docker exec -it 容器名 ls /usr/share/nginx/html

- 在宿主机上添加index.html,通过浏览器访问,可以看到容器已经可以读取到宿主机的共享内容了


- 删除容器,查看宿主机上的文件,发现还是存在,说明容器的删除不影响bind映射

tmpfs mount临时数据卷
映射于宿主机内存中,不存在于主机的可写层中,一旦容器停止运行,tmpfs mounts会被移除,数据就会丢失,用于高性能的临时数据存储。
但tmpfs不同于卷和绑定挂载,不能在容器之间共享tmpfs挂载,这个功能也只有在Linux上运行docker时才可用。
创建卷
--tmpfs
用于完成临时卷的映射。
bash
--tmpfs /app
--mount
用于完成目录映射。
bash
--mount '<key>=<value>,<key>=<value>'
type类型表示bind,volume或者tmpfs
destination/dst/target表示文件或者目录挂载在容器中的路径
tmpfs-size:tmpfs挂载的大小,默认无限制
tmpfs-mode:设置tmpfs权限模式,默认为1777,表示全局可读可写有粘滞位
操作
tmpfs参数创建临时卷
- 创建临时卷启动容器
bash
docker run --name 容器名 -d -p 80:80 --tmpfs /usr/share/nginx/html nginx

- 进入容器可用看到nginx里的文件被覆盖了
bash
docker exec -it 容器名 bash
#修改文件
#载入数据
docker reload 容器名
-
添加一个index.html文件,修改文件内容


-
停止容器,再启动容器,再次进入,可以看到tmpfs已经消失,说明内容是存在内存里面的

tmpfs失踪
- 创建一个普通的容器,在容器里面写入一个文件load.txt,在宿主机上查找这个文件,文件是可以被找到的,因为此文件在容器的可写层
bash
docker run --name 容器名 -d -p 80:80 nginx
docker exec -it 容器名 bash
mkdir -p /app
echo 1 > /app/mylabel.txt
exit
find / -name mylabel.txt

- 创建一个临时卷,重复以上步骤,查找文件的时候发现文件是找不到的,因为tmpfs的内容不是存在容器的可写层中的
bash
docker run --name 容器名 --tmpfs /app -d -p 80:80 nginx
docker exec -it 容器名 bash
echo 1 > /app/mylabel.txt
exit
find / -name mylabel.txt

MySQL恢复数据
- 准备MySQL镜像
bash
docker pull mysql:5.7
- 创建容器
bash
docker run --name mysql-demo -e MYSQL_ROOT_PASSWORD=密码 -itd -v /data/mydir/mysql-data:/var/lib/mysql mysql:5.7

- 查看容器挂载信息
bash
docker inspect 容器名 | grep "Mounts" -A 10
- 连接MySQL的shell,创建数据库,输入密码
- 在MySQL里床关键一个数据库,并且在这个数据库里创建一个表,在主机中查看volume,可以看到容器中MySQL创建的数据库和表数据以及持久化到宿主机挂载的目录下了
bash
ls -l /data/mydir/mysql-data

- 关闭服务器,删除MySQL容器,可以看到数据还在,再次启动容器,确认目录映射一致就可以恢复数据了
bash
docker run --name mysql-demo-new -e MYSQL_ROOT_PASSWORD=密码 -d -v /data/mydir/mysql-data:/var/lib/mysql mysql5.7
- 从MySQL客户端进去,查看其中的数据
bash
docker exec -it mysql-demo-new bash
mysql -u root -p
