文章目录
- Docker Volume
- 为什么需要存储卷
- 管理卷 Volume
- 绑定卷 Bind
- 临时卷 Tmpfs
- MySQL灾难恢复
- Docker Volume使用情况
- Docker Volume 带来的问题
Docker Volume
存储卷就是宿主机的本地文件系统中存在的某个目录直接与容器内部的文件系统上的某一目录建立绑定关系。容器直接在宿主机的目录上写数据,绕过容器的联合文件系统与宿主机文件系统建立关系,使得可以在宿主机和容器内共享数据库内容,让容器直接访问宿主机的内容,也可以宿主机向容器写入内容,容器和宿主机的数据读写是同步的
为什么需要存储卷
1、数据丢失问题,容器按照业务类型总体可以分成两类:有状态(数据不需要被持久化)有状态(数据需要被持久化)。显然容器更擅长无状态应用。因为未持久化数据的容器根目录的生命周期和容器的生命周期一样,这样容器就可以开箱即用,任意调度。但是实际业务中总是存在需要数据持久化的场景,如MySQL、Kafka等有状态业务,为了解决这部分有状态的需求,Docker提出了卷(Volume)的概念
2、性能问题,联合存文件储系统对于修改删除等,一般效率很低,如果对于I/O要求较高的应用,如redis在实现持化存储时,是在底层存储时的性能要求比较高
3、宿主机和容器互访不方便,宿主机访问容器,或容器访问需要通过docker cp来完成,应用很难操作
4、容器和容器共享不方便
管理卷 Volume
创建卷
命令 | 别名 | 功能 |
docker volume create | 创建存储卷 | |
docker volume inspect | 显示存储卷信息 | |
docker volume ls | docker volume list | 列出存储卷 |
docker volume prune | 清理所有无用数据卷 | |
docker volume rm | 删除卷,使用过程中无法删除 |
docker volume create 创建存储卷
docker volume create [options][volume]
-d --driver # 指定驱动,默认是local
--label # 指定元数据
# 创建一个匿名卷
[root@VM-20-6-centos ~]# docker volume create
cf86c1e3cf63373e4c4c65dfb9db64cbc109b1e8a300709a42e95660cc288dee
# 查看当前卷信息
[root@VM-20-6-centos ~]# docker volume ls
DRIVER VOLUME NAME
local 675a12200f8dab1acce8425e1ba85415af6b53219fc27def0583187fcb3d19cb
local 96224d842ebe28d79ae0c075de5888c8effd585ffb7abc9220f2670288ed9a42
local ac4eede8917817747f075577d13e516bcae6f0da0c713791d6db38ab34fb7c6b
local cf86c1e3cf63373e4c4c65dfb9db64cbc109b1e8a300709a42e95660cc288dee
local ec475dc1d244625b67ac6279c64fe1c456ed3e56dbfae5bb4768023bd43a8dba
[root@VM-20-6-centos ~]# docker volume inspect cf86c1e3cf63373e4c4c65dfb9db64cbc109b1e8a300709a42e95660cc288dee
[
{
"CreatedAt": "2023-06-01T20:26:52+08:00",
"Driver": "local",
"Labels": {
"com.docker.volume.anonymous": ""
},
"Mountpoint": "/data/var/lib/docker/volumes/cf86c1e3cf63373e4c4c65dfb9db64cbc109b1e8a300709a42e95660cc288dee/_data", # 挂载目录
"Name": "cf86c1e3cf63373e4c4c65dfb9db64cbc109b1e8a300709a42e95660cc288dee",
"Options": null,
"Scope": "local"
}
]
# 创建存储卷 并设置元数据
[root@VM-20-6-centos ~]# docker volume create --label MYTEST=1 myvoltest2
myvoltest2
[root@VM-20-6-centos ~]# docker volume inspect myvoltest2
[
{
"CreatedAt": "2023-06-01T20:32:41+08:00",
"Driver": "local",
"Labels": {
"MYTEST": "1"
},
"Mountpoint": "/data/var/lib/docker/volumes/myvoltest2/_data",
"Name": "myvoltest2",
"Options": null,
"Scope": "local"
}
]
docker volume inspect 查看卷的详细信息
docker volume inspect [options] volume [volume...]
-f # 指定响应格式
# 可以查看多个,可以返回一个json数组
docker volume ls 列出卷
docker volume ls [options]
--format # 指定响应格式,如json,table
--filter -f # 过滤
-q # 仅显示名称
[root@VM-20-6-centos ~]# docker volume ls -f label=MYTEST2
DRIVER VOLUME NAME
local mytestvol5
通过-v 创建管理卷
docker run -v name:directory[:options]
# 创建一个容器指定其存储卷为 volnginx1,其中内容对应容器内部 /usr/share/nginx/html/
[root@VM-20-6-centos ~]# docker run -d --name myvolnginx1 -v volnginx1:/usr/share/nginx/html/ nginx:1.24.0
# :readonly选项,则无法删除,修改该目录下的文件
[root@VM-20-6-centos ~]# docker run -d --name myvolnginx2 -v vlonginx2:/usr/share/nginx/html/:ro nginx:1.24.0
8821895b7d169a045e3931593c4ade9c74e76015154c1ab8c658ae11ceb97812
[root@VM-20-6-centos ~]# docker volume inspect volnginx1
[
{
"CreatedAt": "2023-06-01T21:44:03+08:00",
"Driver": "local",
"Labels": null,
"Mountpoint": "/data/var/lib/docker/volumes/volnginx1/_data", # 存储到这个路径下
"Name": "volnginx1",
"Options": null,
"Scope": "local"
}
]
# 可以看到容器内部数据被拷贝到本机上了
[root@VM-20-6-centos ~]# ls /data/var/lib/docker/volumes/volnginx1/_data
50x.html index.html
# 进入容器将存储卷挂接目录删除
[root@VM-20-6-centos ~]# docker exec -it myvolnginx1 bash
root@8821895b7d16:/# cd /usr/share/nginx/html/
root@8821895b7d16:/usr/share/nginx/html# rm ./*
root@8821895b7d16:/usr/share/nginx/html# ls
root@8821895b7d16:/usr/share/nginx/html# exit
exit
# 可以看到本机数据也同样被删除了
[root@VM-20-6-centos ~]# ls /data/var/lib/docker/volumes/volnginx1/_data
通过 --mount 创建管理卷
--mount '<key>=<value>, <key>=<value>'
# 关键参数
# type : bind (绑定卷),volume (管理卷), tmpfs (临时卷)
# source, src : 对于命名卷,这是卷的名称。对于匿名卷,则可以省略此字段
# destination, dst, target : 文件或目录挂载在容器中的路径
# ro, readonly : 以只读方式挂载
# 创建一个命名卷,--mount 内部键值对之间不可以有空格
[root@VM-20-6-centos ~]# docker run -d --name myvolnginx3 --mount 'src=volnginx3,dst=/usr/share/nginx/html' nginx:1.24.0
3374587cb8c876233805cf903b9448cb460bd97d7e73c685856ab81600b7e613
## 我们可以 docker container inspect container_name 输出中的 mount字段查看到存储卷信息
[root@VM-20-6-centos ~]# docker inspect volnginx3
"Mounts": [
{
"Type": "volume",
"Name": "volnginx3",
"Source": "/data/var/lib/docker/volumes/volnginx3/_data",
"Destination": "/usr/share/nginx/html",
"Driver": "local",
"Mode": "z",
"RW": true,
"Propagation": ""
}
],
通过Dockerfile 创建匿名卷
# 我们亦可以通过dockerfile的VOLUME指令在镜像中创建Data Volume,这样只要镜像创建容器就会存在挂载点,但通过Dockerfile 的VOLUME 可以创建docker管理卷,这个我们后续在Dockerfile中详细讲解通过VOLUE指令创建的挂载点,无法指定主机上对应目录,由docker随机生成
若容器内某个目录和存储卷进行映射,内部数据会自动同步,不管是在宿主机上操作或是在容器内操作,两个地方的数据都会同步。
若卷被设置了 read only 选项(限制容器),则在容器内部无法写入更新目录信息。但是在宿主机上我们任然能对卷进行操作,并且同步
Docker 卷的生命周期
卷就是来保护数据的,所以容器被删除,宿主机上的数据不会连带被删除。如果主动删除卷那么是可以清理宿主机上的备份数据的
Docker 卷共享
当多个容器绑定一个存储卷时,宿主机上的文件被修改会使多个绑定同一个卷的容器文件被修改
绑定卷 Bind
# 若宿主机上的绑定目录不存在 1、使用--mount方式会直接报错 2、使用-v方式会自动创建目录
# 若两者目录下都存在文件,以宿主机的文件为标准
# 1、-v 方式创建
# 创建一个绑定卷,不指定名称,指定宿主机上的目录。宿主机修改,容器会自动同步
[root@VM-20-6-centos testbind]# docker run -d --name mynginx027 -p 8827:80 -v /data/maxxin/testbind:/usr/share/nginx/html nginx:1.24.0
d78edbd2a13de510062aa36727dc9708e4f0ac2b6fd7538f8d71b9fde6c8ab9a
[root@VM-20-6-centos testbind]# docker container inspect mynginx027
"Mounts": [
{
"Type": "bind",
"Source": "/data/maxxin/testbind",
"Destination": "/usr/share/nginx/html",
"Mode": "",
"RW": true,
"Propagation": "rprivate"
}
],
# 可以看到nginx原有内部数据并没有被同步下来
[root@VM-20-6-centos testbind]# ls
[root@VM-20-6-centos testbind]# pwd
/data/maxxin/testbind
# 进入容器发现该目录下并没有数据
[root@VM-20-6-centos testbind]# docker exec -it mynginx027 bash
root@d78edbd2a13d:/# cd /usr/share/nginx/html
root@d78edbd2a13d:/usr/share/nginx/html# ls
# 2、--mount 方式创建
--mount '<key>=<value>, <key>=<value>'
# 关键参数
# type : bind (绑定卷),volume (管理卷), tmpfs (临时卷)
# source, src : 宿主机目录,就这个和管理卷有区分
# destination, dst, target : 文件或目录挂载在容器中的路径
# ro, readonly : 以只读方式挂载
[root@VM-20-6-centos testbind]# docker run -d --name mynginx028 -p 8828:80 --mount type=bind,src=/data/maxxin/testbind/,target=/usr/share/nginx/html/ nginx:1.24.0
251959010be6ab3d8863b1f7942f01c2638a0e45ce1f40c0ee682654e36dea52
临时卷 Tmpfs
# --tmpfs 方式创建
[root@VM-20-6-centos ~]# docker run -d --name mynginx029 -p 8829:80 --tmpfs /test1 nginx:1.24.0
84b8e9816800318daf35646b479d4951a53121ff3bfafd21900b0e68ae6acad2
# 查看容器信息
[root@VM-20-6-centos ~]# docker inspect mynginx029
# 可以观察到 Mounts 字段被置空,Tmpfs 字段被设置
"Mounts": [],
"Tmpfs": {
"/test1": ""
},
# 进入容器test1/目录
[root@VM-20-6-centos ~]# docker exec -it mynginx029 bash
root@84b8e9816800:/# cd test1/
# 发现目录为空,创建文件后退出
root@84b8e9816800:/test1# ls
root@84b8e9816800:/test1# echo "hello world" > index.html
root@84b8e9816800:/test1# exit
exit
# 重启容器返回原目录发现目录额为空,数据丢失
[root@VM-20-6-centos ~]# docker restart mynginx029
mynginx029
[root@VM-20-6-centos ~]# docker exec -it mynginx029 bash
root@84b8e9816800:/# cd test1
root@84b8e9816800:/test1# ls
root@84b8e9816800:/test1#
# --mount 指定参数创建
[root@VM-20-6-centos ~]# docker run -d --name mynginx030 --mount type=tmpfs,dst=/test2 nginx:1.24.0
5cfbe81d78304f89b3109649f0abccd9bb78bafda29c4f5f55481c897d8fecb3
# 我们发现使用--mount 方式创建的临时卷会在docker inspect中设置Mounts信息
[root@VM-20-6-centos ~]# docker inspect mynginx030
"Mounts": [
{
"Type": "tmpfs",
"Source": "",
"Destination": "/test2",
"Mode": "",
"RW": true,
"Propagation": ""
}
],
tmpfs 参数创建临时卷
[root@VM-20-6-centos ~]# docker run -d --tmpfs /usr/share/nginx/html/ --name nginx031 -p 8831:80 nginx:1.24.0
a93d147cd638f087094fcd5755d71708bf62c21173b68b667a4b8cbb13e3f16a
[root@VM-20-6-centos ~]# docker exec -it nginx031 bash
root@a93d147cd638:/# cd /usr/share/nginx/html/0
# 可以看到存储卷绑定的容器目录被清空
root@a93d147cd638:/usr/share/nginx/html# ls
root@a93d147cd638:/usr/share/nginx/html#
mount 创建临时卷
--mount '<key>=<value>,<key>=<value>'
type:类型表示bind,volume,tmpfs
destination,dst,target : 挂载在容器中的路径
tmpfs-size: tmpfs挂载的大小(以字节为单位)。默认无限制
tmpfs-mode: tmpfs的八进制文件模式,例如700 或0770。默认1777或全局可写
[root@VM-20-6-centos ~]# docker run -d --name mynginx032 -p 8832:80 --mount type=tmpfs,dst=/usr/share/nginx/html/,tmpfs-size=1m nginx:1.24.0
91fb515334fc1715fea7ee025d2b275fe535f8bbc5dc47730f3c8bfbf4dd2740
[root@VM-20-6-centos ~]# docker exec -it mynginx032 bash
root@91fb515334fc:/# cd /usr/share/nginx/html
root@91fb515334fc:/usr/share/nginx/html# ls
[root@VM-20-6-centos ~]# docker cp mybusybox.tar mynginx032:/usr/share/nginx/html/
root@91fb515334fc:/# ls
bin boot dev docker-entrypoint.d docker-entrypoint.sh etc home lib lib64 media mnt mybusybox.tar opt proc root run sbin srv sys tmp usr var
# 当拷贝到临时卷的数据大于临时卷的空间,则不允许拷贝
root@91fb515334fc:/# cp mybusybox.tar /usr/share/nginx/html/
cp: error writing '/usr/share/nginx/html/mybusybox.tar': No space left on device
tmpfs失踪了(宿主机不能看到临时卷的内容)
[root@VM-20-6-centos ~]# docker run -d --name mynginx033 nginx:1.24.0
6e33cc97bbccf9fcadc69019759b6e6aa3a8654906d8ae5ef9853ad2cb8c5a01
[root@VM-20-6-centos ~]# docker exec -it mynginx033 bash
# 在容器中创建一个文件
root@6e33cc97bbcc:/# echo "test safe for tmpfs" > tmplablebymaxxin.txt
root@6e33cc97bbcc:/# ls
bin boot dev docker-entrypoint.d docker-entrypoint.sh etc home lib lib64 media mnt opt proc root run sbin srv sys tmp tmplablebymaxxin.txt usr var
# 新建终端,在宿主机上查找上述新建文件
[root@VM-20-6-centos ~]# find / -name tmplablebymaxxin.txt
/data/var/lib/docker/overlay2/0cf0e7e5843424ffe27619969c6bcc047848ab3479393887051bfdcb89e2730e/diff/tmplablebymaxxin.txt
/data/var/lib/docker/overlay2/0cf0e7e5843424ffe27619969c6bcc047848ab3479393887051bfdcb89e2730e/merged/tmplablebymaxxin.txt
# 可以看到宿主机上不仅仅找到了,甚至还可以获取容器内部文件信息
[root@VM-20-6-centos ~]# cat /data/var/lib/docker/overlay2/0cf0e7e5843424ffe27619969c6bcc047848ab3479393887051bfdcb89e2730e/diff/tmplablebymaxxin.txt
test safe for tmpfs
# 使用临时卷存储的情况
[root@VM-20-6-centos ~]# docker run -d --name mynginx034 -p 8834:80 --tmpfs /tmp_test nginx:1.24.0
626003be340f82c34e6c4a427412fbe35592e73f0540584a1fddcfff1c5bbbb7
[root@VM-20-6-centos ~]# docker exec -it mynginx034 bash
root@626003be340f:/# ls
bin boot dev docker-entrypoint.d docker-entrypoint.sh etc home lib lib64 media mnt opt proc root run sbin srv sys tmp tmp_test usr var
root@626003be340f:/# cd tmp_test
# 在临时卷内创建一个文件
root@626003be340f:/tmp_test# echo "test safe for tmpfs" > tmplablebymaxxin2.txt
root@626003be340f:/tmp_test# ls
tmplablebymaxxin.txt
root@626003be340f:/tmp_test# exit
exit
# 新建终端进行查找,发现找不到
[root@VM-20-6-centos ~]# find / -name tmplabelbymaxxin2.txt
find: ‘/tmp/.mount_nvimoDA04M’: Permission denied
MySQL灾难恢复
目标:掌握挂在卷的使用,将MySQL业务数据存储到外部
创建一个mysql容器,并设置绑定卷
首先到docker hub官网上查看MySQL镜像使用概览,可以找到修改Store Data存储位置指令
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-u06KgpaZ-1686150220457)(C:\Users\Lenovo\AppData\Roaming\Typora\typora-user-images\image-20230604123755642.png)]
# 对指令进行填充,创建MySQL容器
[root@VM-20-6-centos ~]# docker run --name mysql001 -v /data/maxxin/mysql001/store_data:/var/lib/mysql -e MYSQL_ROOT_PASSWORD=2519375966 -d mysql:5.7
16759973395bdffc161c1541b905cc271bce8179a26c959f48836b394b59cd9a
[root@VM-20-6-centos ~]# docker ps | grep mysql
16759973395b mysql:5.7 "docker-entrypoint.s…" 16 seconds ago Up 14 seconds 3306/tcp, 33060/tcp mysql001
82ec53615120 mysql:5.7 "docker-entrypoint.s…" 3 days ago Up 3 days 33060/tcp, 0.0.0.0:8888->3306/tcp, :::8888->3306/tcp mysql
# 可以看到MySQL容器内部文件在主机绑定卷内部同样存储了一份
[root@VM-20-6-centos ~]# ls -l /data/maxxin/mysql001/store_data
total 188480
-rw-r----- 1 polkitd input 56 Jun 4 12:40 auto.cnf
-rw------- 1 polkitd input 1676 Jun 4 12:40 ca-key.pem
-rw-r--r-- 1 polkitd input 1112 Jun 4 12:40 ca.pem
-rw-r--r-- 1 polkitd input 1112 Jun 4 12:40 client-cert.pem
-rw------- 1 polkitd input 1680 Jun 4 12:40 client-key.pem
-rw-r----- 1 polkitd input 1318 Jun 4 12:40 ib_buffer_pool
-rw-r----- 1 polkitd input 79691776 Jun 4 12:40 ibdata1
-rw-r----- 1 polkitd input 50331648 Jun 4 12:40 ib_logfile0
-rw-r----- 1 polkitd input 50331648 Jun 4 12:40 ib_logfile1
-rw-r----- 1 polkitd input 12582912 Jun 4 12:40 ibtmp1
drwxr-x--- 2 polkitd input 4096 Jun 4 12:40 mysql
lrwxrwxrwx 1 polkitd input 27 Jun 4 12:40 mysql.sock -> /var/run/mysqld/mysqld.sock
drwxr-x--- 2 polkitd input 4096 Jun 4 12:40 performance_schema
-rw------- 1 polkitd input 1676 Jun 4 12:40 private_key.pem
-rw-r--r-- 1 polkitd input 452 Jun 4 12:40 public_key.pem
-rw-r--r-- 1 polkitd input 1112 Jun 4 12:40 server-cert.pem
-rw------- 1 polkitd input 1680 Jun 4 12:40 server-key.pem
drwxr-x--- 2 polkitd input 12288 Jun 4 12:40 sys
# 进入MySQL容器
[root@VM-20-6-centos ~]# docker exec -it mysql001 bash
bash-4.2# mysql -h 127.0.0.1 -u root -p
Enter password:
# 创建数据库表,并插入一条数据
mysql> create database clx_test;
Query OK, 1 row affected (0.00 sec)
mysql> use clx_test
Database changed
mysql> create table student(
-> sno int primary key auto_increment,
-> sname varchar(50) not null);
Query OK, 0 rows affected (0.13 sec)
mysql> show tables;
+--------------------+
| Tables_in_clx_test |
+--------------------+
| student |
+--------------------+
1 row in set (0.00 sec)
mysql> insert into student values (1, 'tony');
Query OK, 1 row affected (0.02 sec)
mysql> exit
Bye
bash-4.2# exit
exit
# 强制删除mysql001
[root@VM-20-6-centos ~]# docker rm -f mysql001
mysql001
# 重新启动一个容器,除了改变容器名,其它都不变
[root@VM-20-6-centos ~]# docker run --name mysql002 -v /data/maxxin/mysql001/store_data:/var/lib/mysql -e MYSQL_ROOT_PASSWORD=2519375966 -d mysql:5.7
d2e9ef7b6b83ab806f83d3530237e6baa5d82253f6ca5330cefee7b281024788
[root@VM-20-6-centos ~]# docker exec -it mysql002 bash
bash-4.2# mysql -h 127.0.0.1 -u root -p
Enter password:
Welcome to the MySQL monitor. Commands end with ; or \g.
Your MySQL connection id is 2
Server version: 5.7.42 MySQL Community Server (GPL)
Copyright (c) 2000, 2023, Oracle and/or its affiliates.
Oracle is a registered trademark of Oracle Corporation and/or its
affiliates. Other names may be trademarks of their respective
owners.
Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.
# 可以观察到上面被删除容器的数据被保存下来了
mysql> show databases;
+--------------------+
| Database |
+--------------------+
| information_schema |
| clx_test |
| mysql |
| performance_schema |
| sys |
+--------------------+
5 rows in set (0.00 sec)
mysql> use clx_test
Reading table information for completion of table and column names
You can turn off this feature to get a quicker startup with -A
Database changed
mysql> select * from student;
+-----+-------+
| sno | sname |
+-----+-------+
| 1 | tony |
+-----+-------+
1 row in set (0.00 sec)
Docker Volume使用情况
- volume:volume是docker的宿主文件系统一部分,用于不需要规划具体目录的场景
- bind:bind mount 完全依赖主机的目录结构和操作系统,用于目录需要提前规划,比如MySQL的目录需要空间大的,其它服务有不占用的时候,用volume就不太合适
- tmpfs:用于敏感文件存储,文件不想存储在宿主机和容器的可写层
Docker Volume 带来的问题
- 跨主机使用:docker存储卷使用的是宿主机上的本地文件系统目录,也就是使用的是宿主机的一块磁盘,而这块磁盘无法共享给其它docker主机,容器在这个宿主机停止或删除是可以重新使用这块磁盘的,但是其它主机并不能使用。所以docker 存储卷默认就是存在本地主机。若想要解决这个问题,需要运维人员自己搭建一个共享的NFS存储docker 内部数据,非常考验运维人员的能力。 伴随分布式存储方案出现,现有(s3系列,nfs等解决方案)
- 启动参数未知:容器启动选项非常多,运维人员很容易忘记容器启动选项,需要有一个文件来保存容器操作信息,要求运维人员学会容器编排工具的使用。如果要对几十甚至上百个容器操作,就需要专业的容器编排工具,现有(k8s 系列,各个云厂商还有自己的编排工具)
- 复杂场景仍然需要运维:对于状态需要持久化的集群化组件,如MySQL的主从,部署MySQL主从需要运维知识,扩展缩容,修复。对整体规模掌控,需要多少个主结点,有多少个从节点,主节点上有多少库,都需要一清二楚才可以修复故障,这些强依赖运维经验