Docker 容器数据卷
容器数据卷(Volumes)
容器数据卷概述
- Docker 镜像是分层结构,联合挂载,镜像中各层的文件都是只读的,容器中所有文件对外可见的状态是多层叠加后的可见状态。因此对于IO性能要求较高的容器,如果使用Overlayfs会严重影响容器的性能。
- 另外,容器在生命周期结束的时候会被删除,对于有数据持久化要求的容器而言,容器运行中有价值的数据并需要存储在外部的逻辑卷中。如果由容器直接使用共享存储系统,那么每次容器启动后都需要挂载,而且还需要具备与外界通信的能力,不仅繁琐,而且损失性能。
- 针对这种需求,docker推出了卷管理系统,在容器创建时直接使用宿主机的存储系统,当容器结束运行时,不会删除卷中的数据。
- 如图中,宿主机将远程NFS服务器中的数据盘挂载到本地/data/目录下,创建httpd容器时,将本地的/data/盘映射到容器中的网站根目录:/data/html中。
- 当httpd容器挂掉后,无论在哪台服务器,只要满足宿主机挂载NFS存储,并且httpd容器映射网站根目录到/data/html即可。针对不需要持久化存储的应用,则可以不使用存储卷管理。
- 将宿主机上的目录挂载到容器中有两种方式:
- Bind mount volume
手动指定宿主机目录和容器中目录的映射关系,或使用容器编排工具(k8s或者docker-compose)指定 - Docker-managed volume
Docker自动管理的挂载关系,这种情况下宿主机目录不固定,后期维护成本高,仅用于存放一些临时数据
- Bind mount volume
使用数据卷
直接使用命令来挂载 -v
docker run -it -p 主机端口:容器内端口
docker run -it -v 主机目录:容器内目录
docker run -it -v /home/ceshi:/home centos /bin/bash
docker inspect 容器id 可以查看挂载信息
docker inspect 39f2fc482f42
...
"Mounts": [ 挂载
{
"Type": "bind",
"Source": "/home/ceshi", 主机内地址
"Destination": "/home", docker容器内的地址
"Mode": "",
"RW": true,
"Propagation": "rprivate"
}
],
...
测试,我们在主机的/home/ceshi下创建文件
[root@docker ceshi]# touch maomao.py
容器内数据同步
[root@39f2fc482f42 home]# ls
maomao.py
然后我们在容器内创建文件
[root@39f2fc482f42 home]# touch zhuzhu.py
主机内也同步了文件
[root@docker ceshi]# ls
maomao.py zhuzhu.py
再次测试
将容器关闭,然后在maomao.py里面写入代码,再启动容器验证数据是否同步
vim maomao.py
#!/usr/bin/python3
print('hello maomao')
[root@docker ceshi]# python3 maomao.py
hello maomao
验证成功
[root@39f2fc482f42 home]# cat maomao.py
#!/usr/bin/python3
print('hello maomao')
实现mysql数据同步
搜索镜像
docker search mysql --limit 2
下载镜像
docker pull mysql:5.7
开启mysql容器
docker run -d -p 3310:3306 -v /home/mysql/my.cnf:/etc/my.cnf -v /home/mysql/data:/var/lib/mysql -e MYSQL_ROOT_PASSWORD=123 --name mysql01 mysql:5.7
测试能否连接mysql
我们进入容器 连接数据库创建一个库
docker exec -it mysql01 /bin/bash
root@fecdbe5b8949:/# mysql -uroot -p
Enter password:
mysql> create database maomao;
查看主机挂载点
[root@docker data]# ls
auto.cnf client-cert.pem ibdata1 ibtmp1 performance_schema server-cert.pem
ca-key.pem client-key.pem ib_logfile0 maomao private_key.pem server-key.pem
ca.pem ib_buffer_pool ib_logfile1 mysql public_key.pem sys
里面有maomao这个文件夹 就是容器内创建的库
最后删除容器
docker stop mysql01
docker rm mysql01
发现,挂载到本地的数据卷依然没有丢失,这就实现了容器数据持久化功能
具名挂载和匿名挂载
匿名挂载
-v 容器内地址
-P 随机映射端口
docker run -d -P --name nginx01 -v /etc/nginx nginx
查看所有volume的情况
docker volume ls
DRIVER VOLUME NAME
local 9de966bc2f690dad1bc0aca3a374686d84f03e8e8bd32bfbefe5c663f52d1ed2
local 783326254c48003588c1633d52fd85579b07a0dd8fc3ef9e379cd9613d3ed0c7
local a762d81d21a44050086711739c06ebed8426c1b118951766b1ad04421575f557
这种就是匿名挂载,我们在-v后 只写了容器内的地址,没有写容器外的地址
具名挂载
docker run -d -P --name nginx02 -v juming-nginx:/etc/nginx nginx
发现Volume name 变成了我们自己命名的name
docker volume ls
DRIVER VOLUME NAME
local 9de966bc2f690dad1bc0aca3a374686d84f03e8e8bd32bfbefe5c663f52d1ed2
local 783326254c48003588c1633d52fd85579b07a0dd8fc3ef9e379cd9613d3ed0c7
local a762d81d21a44050086711739c06ebed8426c1b118951766b1ad04421575f557
local juming-nginx
查看挂载路径
docker volume inspect 挂载名字
docker volume inspect juming-nginx
[
{
"CreatedAt": "2021-04-22T10:52:20-04:00",
"Driver": "local",
"Labels": null,
"Mountpoint": "/var/lib/docker/volumes/juming-nginx/_data",
"Name": "juming-nginx",
"Options": null,
"Scope": "local"
}
]
Mountpoint 就是挂载点
[root@docker ~]# cd /var/lib/docker/volumes/juming-nginx/_data/
[root@docker _data]# ls
conf.d fastcgi_params koi-utf koi-win mime.types modules nginx.conf scgi_params uwsgi_params win-utf
-
所有的docker容器内的卷,没有指定目录的情况下都是在
/var/lib/docker/volumes/xxx/_data
下 -
我们通过具名挂载可以方便的找到我们的一个卷,大多数情况都使用具名挂载
-
如何确定具名挂载或者匿名挂载,还是指定路径挂载
-v 容器内地址 # 是匿名挂载 -v 卷名:容器内地址 # 是具名挂载 -v /宿主机路径:/容器内地址 # 是指定路径挂载
补充:
通过-v容器内路径:ro/rw改变读写权限
ro readonly 只读
rw readwrite 可读可写
一旦这个了设置了容器权限,容器对我们挂载出来的内容就有限定了!
docker run -d -p --name nginx02 -v juming-nginx:/etc/nginx:ro nginx
docker run -d -p --name nginx02 -v juming-nginx:/etc/nginx:rw nginx
只要看到ro就说明这个路径只能通过宿主机来操作,容器内部是无法操作!
DockerFile里写Volumes配置
Dockerfile就是用来构建docker镜像的构建文件!命令脚本!通过脚本可以生成镜像
创建一个写dockerfile的目录
mkdir docker-volume
cd !$
编写dockerfile
vim dockerfile1
FROM centos
VOLUME ["volume01","volume02"]
CMD echo "hello world"
CMD /bin/bash
通过dockerfile创建镜像
-f 是指定脚本文件
-t 是生成镜像 后面跟镜像名字
最后的 . 是之当前目录的意思 或者写绝对路径也可以如 ./home/docker-volume
docker build -f dockerfile1 -t maomao_centos:1.0 .
Sending build context to Docker daemon 2.048kB
Step 1/4 : FROM centos
---> 300e315adb2f
Step 2/4 : VOLUME ["volume01","volume02"]
---> Running in 04ae767b3fc3
Removing intermediate container 04ae767b3fc3
---> 90600bc216f4
Step 3/4 : CMD echo "hello world"
---> Running in 2488826ba14a
Removing intermediate container 2488826ba14a
---> 261164acfcf7
Step 4/4 : CMD /bin/bash
---> Running in 2df546baf492
Removing intermediate container 2df546baf492
---> f54c4d94f07b
Successfully built f54c4d94f07b
Successfully tagged maomao_centos:1.0
- 基础镜像 From centos
- 通过volumes挂载
- 执行内部命令
- 进入当前的/bin/bash
查看构建的镜像
docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
maomao_centos 1.0 f54c4d94f07b 3 minutes ago 209MB
开启容器
docker run -it --name niu f54c4d94f07b /bin/bash
ls -l
drwxr-xr-x 2 root root 6 Apr 23 03:55 volume01
drwxr-xr-x 2 root root 6 Apr 23 03:55 volume02
这两个目录就是我们生成镜像时自动挂载的,数据卷目录
这个卷和容器外部一定有一个同步的目录
并且dockerfile里面写的挂载是一个匿名挂载
cd volume01
在挂载点写一个文件
vim container.py
#! /usr/bin/python3
i = 0
num = int(input('high:'))
j = num
while i < num:
i += 1
j -= 1
print(j * ' ' + (2*i-1)*'*')
在外部查看容器信息
docker inspect 7b12b042fbd4
"Mounts": [
{
"Type": "volume",
"Name": "ccf9df5be200fb23d40c3e7d02ff7d7c77cfe52d82c8925b426465a018237ad0",
"Source": "/var/lib/docker/volumes/ccf9df5be200fb23d40c3e7d02ff7d7c77cfe52d82c8925b426465a018237ad0/_data",
"Destination": "volume02",
"Driver": "local",
"Mode": "",
"RW": true,
"Propagation": ""
},
{
"Type": "volume",
"Name": "cf59ff76f95694ce2a8fc571048cee5373dc76759ce534f2be6e94a5cb42690c",
"Source": "/var/lib/docker/volumes/cf59ff76f95694ce2a8fc571048cee5373dc76759ce534f2be6e94a5cb42690c/_data",
"Destination": "volume01",
"Driver": "local",
"Mode": "",
"RW": true,
"Propagation": ""
}
],
显示了挂载位置
cd /var/lib/docker/volumes/cf59ff76f95694ce2a8fc571048cee5373dc76759ce534f2be6e94a5cb42690c/_data
[root@docker _data]# ls
container.py
[root@docker _data]# python3 container.py
high:5
*
***
*****
*******
*********
多个数据卷容器挂载
实验一:
创建两个容器 子容器挂载到父容器上面 查看数据是否同步
启动父容器
docker run -it --name docker01 maomao_centos:1.0 /bin/bash
启动子容器 挂载到父容器
docker run -it --name docker02 --volumes-from docker01 maomao_centos:1.0 /bin/bash
在父容器的volume01里面创建文件
[root@8b03c7d68afd /]# cd volume01/
[root@8b03c7d68afd volume01]# ls
[root@8b03c7d68afd volume01]# touch docker01
在子容器的volume01里会同步文件
[root@ca2db151b708 /]# cd volume01/
docker01创建的内容同步到docker2上
[root@ca2db151b708 volume01]# ls
docker01
实验二:
创建第三个容器挂载到第二个容器上
创建docker03挂载到docker02上
docker run -it --name docker03 --volumes-from docker02 maomao_centos:1.0 /bin/bash
[root@424d9d12c37e /]# cd volume01/
[root@424d9d12c37e volume01]# ls
docker01
[root@424d9d12c37e volume01]# touch docker03
[root@ca2db151b708 volume01]# ls
docker01 docker03
将docker01删除
docker rm -f docker01
docker02和docker03数据依然存在
实验三:
使用具名挂载 然后实现两个容器的数据同步
docker run -it -P --name nginx01 -v nginx:/etc/nginx nginx /bin/bash
查看具名挂载目录
docker inspect e1dd0b5ff9e5
cd /var/lib/docker/volumes/nginx/_data
第二个容器挂载到第一个容器上
docker run -it -P --name nginx02 --volumes-from nginx01 nginx /bin/bash
在具名挂载的目录里面修改nginx.conf配置文件
vim nginx.conf
events {
worker_connections 4096;
}
查看两个容器的配置文件
root@e1dd0b5ff9e5:/etc/nginx# cat nginx.conf
events {
worker_connections 4096;
}
两个容器配置文件都被改变 说明实现两个容器的数据同步
多个mysql实现数据共享
开启第一个mysql容器 并挂载
docker run -d -p 3310:3306 -v conf:/etc/mysql/conf.d -v mysql:/var/lib/mysql -e MYSQL_ROOT_PASSWORD=123 --name mysql01 mysql:5.7
docker exec -it mysql01 /bin/bash
开启第二个mysql容器 并挂载到第一个mysql上
docker run -d -p 3311:3307 -e MYSQL_ROOT_PASSWORD=123456 --name mysql02 --volumes-from mysql01 mysql:5.7
- 容器之间配置信息的传递,数据卷容器的生命周期一直持续到没有容器为止
- 一旦持久化到本地,本地的数据不会因为容器被删除而删除