[Docker#9] 存储卷 | Volume、Bind、Tmpfs | -v/mount | MySQL 灾难恢复 | 问题
目录
1. 什么是存储卷?
2. 生活案例
3. 为什么需要存储卷?
4. 存储卷分类
一. 管理卷 Volume
创建卷
通过 -v 或 --mount 指定
1. -v 语法
命令格式
参数说明
2. --mount 语法
命令格式
参数说明
验证
二. 绑定卷 (Bind Mount)
1. 绑定卷概述
2. 创建绑定卷
2.1 使用 -v 参数创建绑定卷
2.2 使用 --mount 参数创建绑定卷
3. 操作案例
使用 -v 创建绑定卷
4. 绑定卷共享
三. 临时卷 (tmpfs)
1. 临时卷概述
2. 创建临时卷
方式一:使用 --tmpfs 参数创建
方式二:使用 --mount 参数创建
3. 操作案例
使用 --tmpfs 创建临时卷
四. 运用
MySQL 灾难恢复
实战目的
实战步骤
五. 常见问题
Volume、Bind、Tmpfs 的使用时机
问题
1. 跨主机使用
2. 启动参数未知
3. 复杂场景仍需运维
1. 什么是存储卷?
- 存储卷是指将宿主机上的某个目录与容器内的某个目录建立绑定关系。
- 这意味着当容器在这个目录下写入数据时,实际上是直接写入宿主机上的对应目录。
- 存储卷的本质是一个文件或目录,它允许数据绕过容器的联合文件系统,直接存在于宿主机上,从而实现宿主机与容器之间的数据共享。
- 容器和宿主机的数据读写 是同步的。
2. 生活案例
存储卷就像租用了一个地下室,你可以通过这把“钥匙”(即映射关系)来使用房子外的空间。即使房子(容器)被毁,地下室(存储卷)中的数据仍然安全无损。
3. 为什么需要存储卷?
- 数据丢失问题:对于需要持久化的数据,容器销毁后其读写层的数据也会丢失。存储卷确保了即使容器被删除,数据也能被保存。
- 性能问题:UnionFS 对于频繁的读写操作效率较低,而存储卷可以提供更好的I/O性能。
- 宿主机与容器互访不便:通过存储卷,可以轻松地在宿主机与容器之间共享文件,无需使用
docker cp
命令。 - 容器间共享不便:存储卷也方便了不同容器间的文件共享。
4. 存储卷分类
Docker提供了三种主要的存储卷类型:
- volume:由Docker管理,默认映射到
/var/lib/docker/volumes/
下,适合临时存储。 - bind mount:映射到宿主机的指定路径,适用于需要精确控制宿主机目录的情况。
- tmpfs mount:映射到宿主机的内存中,适合需要高速度临时存储的场景.
对于 mount
的理解:
可以类比为将磁盘挂载到操作系统上,操作系统会提供一个访问接口。同样,Docker 的 mount
也是将一个资源(如存储卷)挂载到容器中,并提供一个访问路径。
一. 管理卷 Volume
创建卷
命令操作
docker volume create [OPTIONS] [VOLUME]
:创建存储卷。docker volume inspect [OPTIONS] VOLUME [VOLUME...]
:查看卷的详细信息。docker volume ls [OPTIONS]
:列出所有卷。docker volume rm [OPTIONS] VOLUME [VOLUME...]
:删除卷。docker volume prune [OPTIONS]
:清理所有未使用的卷。
实操:
通过 -v
或 --mount
指定
1. -v
语法
命令格式
docker run -v name:directory[:option]
参数说明
name
:绑定的现有卷的名称,可以省略,此时会创建一个新的匿名卷。directory
:数据卷在容器内部的路径。option
:可以指定ro
表示只读,此时只有宿主机可以修改数据卷,容器只读。
操作:
注意
- 即使删除容器,存储卷内的内容依然存在。
- 如果指定存储卷名时,存储卷不存在,Docker 会自动创建一个新的命名卷。新创建的:
2. --mount
语法
命令格式
docker run --mount key=value [key=value ...]
参数说明
type
:存储卷类型,指定volume
表示数据卷。src
:对于命名卷,表示卷名;对于匿名卷,此字段省略。dst
:文件在容器的挂载路径。ro
:只读方式挂载。
使用 --mount
创建命名卷
假设我们要创建一个命名卷 test_vm_3
并挂载到 Nginx 容器的 /usr/share/nginx/html
目录下,且设置为只读。
示例
docker run -d --name nginx-read-only --mount type=volume,src=test_vm_3,dst=/usr/share/nginx/html,ro nginx
验证
- 进入容器内部:
docker exec -it nginx-read-only /bin/bash
- 尝试修改
/usr/share/nginx/html
目录下的文件,会提示Read-only file system
,表示该目录是只读的。
对于管理卷 volume 默认路径的查看
总结:
-v
语法:简单直观,适用于快速创建和挂载数据卷。--mount
语法:更加灵活,支持更多选项,适用于复杂场景。- 数据卷:无论是命名卷还是匿名卷,都可以实现宿主机与容器之间的数据共享
- 数据卷在容器删除后依然保留。
二. 绑定卷 (Bind Mount)
1. 绑定卷概述
- 绑定卷(Bind Mount)是将宿主机上的某个目录直接挂载到容器内的某个目录。
- 这种方式允许容器和宿主机之间共享文件和目录,且数据直接存储在宿主机的文件系统中。
2. 创建绑定卷
2.1 使用 -v
参数创建绑定卷
- 功能:完成卷映射。
语法:
docker run -v host-dir:container-dir[:options] ...
参数:
host-dir
:宿主机目录,不同于管理卷。container-dir
:卷映射到容器的目录。options
:选项,如ro
表示只读。
样例:
docker run -d \
-it \
--name devtest \
-v "$(pwd)"/target:/app \
nginx:latest
2.2 使用 --mount
参数创建绑定卷
- 功能:完成目录映射。
- 语法:
docker run --mount 'type=bind,source=host-dir,target=container-dir,readonly'
关键参数:
type
:类型,表示bind
。source
,src
:宿主机目录,不同于管理卷。destination
,dst
,target
:文件或目录挂载在容器中的路径。ro
,readonly
:只读方式挂载。
样例:
docker run -d \
-it \
--name devtest \
--mount type=bind,source="$(pwd)"/target,target=/app \
nginx:latest
对比观察:
3. 操作案例
使用 -v
创建绑定卷
- 创建容器:
docker run -d -p 8080:80 --name bind2 -v /data/myworkdir/fs/webapp2:/usr/share/nginx/html nginx:1.22.1
注意:如果 webapp2
目录不存在,启动不会报错,这是 -v
和 --mount
方式的区别。
- 查看挂载信息:
docker inspect bind2
输出示例:
- 进入容器终端,查看挂载点目录:
docker exec -it bind2 ls /usr/share/nginx/html
宿主机上查看:
ll /data/myworkdir/fs/webapp2
容器中的 /usr/share/nginx/html 目录原本存在的文件消失了,这是因为 bind mount 模式会覆盖容器内的原有内容,而 volume 模式则不会。这是两者最大的不同点。
- 在宿主机上添加
index.html
:
echo "Hello bit bind2 mount" > /data/myworkdir/fs/webapp2/index.html
查看:
- 通过浏览器访问,可以看到容器已经读取到宿主机的共享内容。
- 删除容器,查看宿主机上的文件:
docker stop bind2
docker rm bind2
ll /data/myworkdir/fs/webapp2
宿主机上的文件依然存在,说明容器删除并不影响 bind
映射。
4. 绑定卷共享
- 启动两个绑定卷,都绑定到宿主机的同一个目录:
docker run -d -p 8770:80 --name bind3 -v /data/myworkdir/fs/webapp1:/usr/share/nginx/html nginx:1.22.1
docker run -d -p 7705:80 --name bind4 -v /data/myworkdir/fs/webapp1:/usr/share/nginx/html nginx:1.22.1
- 访问两个页面,可以看到相应内容一样:
curl 127.0.0.1:8770
curl 127.0.0.1:7705
查看
- 修改
index.html
:
echo "bind mount after edit" > /data/myworkdir/fs/webapp1/index.html
- 再次访问两个页面,可以看到我们实现了容器间的数据共享:
curl 127.0.0.1:8770
curl 127.0.0.1:7705
- 清理空间:
docker stop bind3
docker rm bind3
docker stop bind4
docker rm bind4
三. 临时卷 (tmpfs)
1. 临时卷概述
临时卷(tmpfs)的数据存储在内存中,不在容器和宿主机的文件系统中。因此,tmpfs 具有以下特点:
- 数据仅存在于内存中,容器停止后数据会丢失。
- 不能在容器之间共享 tmpfs 挂载。
- 仅在 Linux 上运行 Docker 时可用。
2. 创建临时卷
方式一:使用 --tmpfs
参数创建
- 功能:完成临时卷映射。
- 语法:
--tmpfs /app
- 样例:
docker run -d \
-it \
--name tmptest \
--tmpfs /app \
nginx:1.22.1
方式二:使用 --mount
参数创建
- 功能:完成目录映射。
- 语法:
--mount 'type=tmpfs,destination=/app,tmpfs-size=1m,tmpfs-mode=0770'
- 关键参数:
-
type
:类型,表示tmpfs
。destination
,dst
,target
:挂载在容器中的路径。tmpfs-size
:tmpfs 挂载的大小(以字节为单位)。默认无限制。tmpfs-mode
:tmpfs 的八进制文件模式。例如,700 或 0770。默认为 1777 或全局可写。
- 样例:
docker run -d \
-it \
--name tmptest \
--mount type=tmpfs,destination=/app \
nginx:latest
3. 操作案例
使用 --tmpfs
创建临时卷
- 创建临时卷并启动容器:
docker container run --name tmpfs1 -d -p 80:80 --tmpfs /usr/share/nginx/html nginx:1.22.1
进入容器查看挂载点目录:
docker exec -it tmpfs1 bash
cd /usr/share/nginx/html/
ls -l
发现容器中的 /usr/share/nginx/html
目录原本存在的文件被覆盖了。
添加一个首页:
echo "Hello bit from tmpfs" > index.html
cat index.html
浏览器查看:
访问 http://localhost
,可以看到页面显示 "Hello bit from tmpfs"。
说明:
- 重启容器后可以发现, tmpfs 的内容完全消失了,说明内容是存在内存中的。
- tmpfs 的内容不是存储在容器的可写层中。
总结
- 临时卷(tmpfs)是一种将数据存储在内存中的方式,适用于需要临时存储数据的场景。
- tmpfs 的数据在容器停止后会丢失,且不能在容器之间共享。
- 通过
--tmpfs
和--mount
参数可以创建 tmpfs 挂载,但需要注意 tmpfs 的大小限制和内存占用。
四. 运用
MySQL 灾难恢复
实战目的
掌握挂载卷的使用,将 MySQL 的业务数据存储到外部,以便在容器故障或服务器重启后能够快速恢复数据。
实战步骤
准备镜像
docker pull mysql:5.7
创建容器
docker container run --name mysql-demo -e MYSQL_ROOT_PASSWORD=bite -itd -v /data/myworkdir/mysql-data:/var/lib/mysql mysql:5.7
-e MYSQL_ROOT_PASSWORD=bite
:设置 MySQL 的 root 密码。-v /data/myworkdir/mysql-data:/var/lib/mysql
:将宿主机的/data/myworkdir/mysql-data
目录挂载到容器的/var/lib/mysql
目录。
查看容器挂载信息
docker container inspect mysql-demo | grep "Mounts" -A 10
输出示例:
连接 MySQL 并创建数据库和表
docker container exec -it mysql-demo /bin/bash
bash-4.2# mysql -u root -p
在 MySQL 中执行以下命令:
在宿主机中查看 volume
ll /data/myworkdir/mysql-data/
输出示例:
进入 user
目录查看:
cd /data/myworkdir/mysql-data/user
ll
输出示例:
模拟灾难情况
docker stop mysql-demo
docker rm mysql-demo
- 假设服务器突然断电,重启后 MySQL 无法启动。
- 为了节省磁盘空间,删除了所有停止的容器。
恢复数据
docker container run --name mysql-demo-new -e MYSQL_ROOT_PASSWORD=bite -itd -v /data/myworkdir/mysql-data:/var/lib/mysql mysql:5.7
- 重新启动容器,确保目录映射一致。
验证数据恢复
docker exec -it mysql-demo-new bash
mysql -u root -p
Enter password: bite
在 MySQL 中执行以下命令:
- 进入新容器并连接 MySQL,发现数据还在~
释放空间
docker stop mysql-demo-new
docker rm mysql-demo-new
五. 常见问题
Volume、Bind、Tmpfs 的使用时机
Volume:
- 适用场景:当不需要规划具体目录时使用,如日志文件、数据库数据等。
- 特点:是 Docker 宿主机文件系统的一部分。
Bind:
- 适用场景:当目录需要提前规划时使用,例如 MySQL 数据目录需要较大空间。
- mysql 的目录需要个空间大的,其他服务有不占用的时候,用 volume 就不太合 适了
- 特点:依赖于主机的目录结构和操作系统。
Tmpfs:
- 适用场景:用于存储敏感文件,文件不希望存储在宿主机的可写层或容器内。
问题
1. 跨主机使用
- 问题: Docker 存储卷默认使用宿主机的本地文件系统,不支持跨主机调度。
- 解决方案: 可以通过搭建共享存储(如 NFS)来实现跨主机存储,但依赖运维人员能力。
- 趋势: 应用存储和数据分离,分布式存储方案(如 S3、NFS)逐渐流行。
2. 启动参数未知
- 问题: 容器启动参数较多,容易忘记。
- 解决方案: 使用容器编排工具记录启动参数,通过文件读取启动配置。
- 工具: 如 Kubernetes(k8s)或云厂商的企业版编排软件。
3. 复杂场景仍需运维
- 问题: 对于有状态且需要持久化的集群化组件(如 MySQL 主从),部署和维护需要丰富的运维经验和知识。
- 挑战: 需要了解集群规模、节点数量、数据分布等,以便于故障修复和扩展。
- 现状: 复杂场景仍依赖人力,难以完全自动化。