Docker 学习篇(五)
共 4776字,需浏览 10分钟
·
2021-09-26 12:16
Docker 容器数据卷
1、为什么需要有容器数据卷?
当我们把docker容器关闭了,其上的所有数据也就都消失了,但是如果有一些重要的数据需要保存下来,应该怎么办?
这个时候就可以使用 Docker 容器数据卷,把 Docker 容器运行时产生的数据保存到宿主机上,也即持久化,Docker 容器数据卷,有点类似于 Redis 里面的 RDB 和 AOF 文件。
2、容器数据卷的作用?
卷是目录或者文件,存在于一个或多个容器中,由 Docker 挂载到容器上,但不属于 UnionFS,因此能够绕过 UnionFS 提供一些用于持续存储和共享数据的特性。
卷的设计目的就是数据的持久化,完全独立于容器的生存周期,因此 Docker 不会在容器删除时删除其挂载的数据卷。
数据卷的特性如下所示:
1、数据卷可在容器之间共享或重用数据。
2、卷中的更改可以直接生效。
3、数据卷中的更改不会包含在镜像的更新中。
4、数据卷的生命周期一直持续到没有容器使用它为止。
综上所述,容器数据卷有如下几个作用:
1、将容器中的数据进行持久化,容器数据卷和主机上的某个目录对应,数据卷发生变化,主机这个目录都能感知到并作出相应的改变,通过这个特性,容器中的数据就持久化到主机的这个目录中了。
2、容器与容器间的数据共享。
3、容器和主机之间的数据共享。从容器内拷贝文件到主机上:docker cp 容器ID:容器内路径 主机路径,主机到容器的数据拷贝:docker cp 主机路径 容器ID:容器路径。
3、在容器内添加容器卷
第一种方式:直接命令添加
docker run -it -v /主机绝对路径目录:/容器内目录 镜像名,v 是 volume 的缩写,volume 是“卷”的意思。
下面我们来演示一下使用命令,在容器内添加容器卷:
1、docker run -it -v /myDataVolume:/dataVolumeContainer centos,在主机的根目录下创建一个名为 myDataVolume 的文件夹,在 centos 容器中创建一个名为 dataVolumeContainer 的文件夹。
2、docker inspect 容器ID,在返回的 json 串中,"Volume":{"/dataVolumeContainer":"/myDataVolume"} "HostConfig":{"Binds":["/myDataVolume:/dataVolumeContainer"]},出现这句话说明主机的 myDataVolume 文件夹和容器的 dataVolumeContainer 文件夹二者绑定成功,这样的话,这两个文件夹就可以实现数据的共享了,其中 dataVolumeContainer 文件夹就叫容器的数据卷。
3、在主机的 myDataVolume 文件夹中创建一个文件,host.txt。
4、进到 centos 容器中,发现容器里面 /dataVolumeContainer 文件夹下也自动创建了一个 host.txt 文件。
5、在 centos 容器内,修改 host.txt,vi host.txt,在里面写入 container update 01,并且创建一个新的文件,container.txt。
6、回到主机中,cd myDataVolume,我们发现里面也自动创建了一个 container.txt 文件,并且 host.txt 里面自动写上了 container update 01。
7、以上这些情况说明,容器和主机之间实现了数据共享,任何一方把数据改了,另一方都能感知到,并且做出同样的改变。
第二种方式:DockerFile 添加
下面我们来演示一下使用 DockerFile,在容器内添加容器卷:
1、在宿主机的根目录下创建 mydocker 文件夹并进入,在 mydocker 文件夹中创建 DockerFile 文件。
mkdir /mydocker
cd /mydocker
touch Dockerfile
2、编写 Dockerfile,在 Dockerfile 中使用 VOLUME 指令给镜像添加一个或多个数据卷,Dockerfile 内容如下:
#volume test
FROM centos #当前这个镜像是以centos镜像为基础制作的,即base镜像是centos镜像
VOLUME ["/dataVolumeContainer1","/dataVolumeContainer2"] #在当前制作的这个镜像生成的容器的根目录下生成两个容器数据卷,分别是 /dataVolumeContainer1 和 /dataVolumeContainer2
CMD echo "finished,--------success1" #打印这句话
CMD /bin/bash
这个 Dockerfile 等价于 “docker run -it -v /主机目录1:/dataVolumeContainer1 -v /主机目录2:/dataVolumeContainer2 centos /bin/bash”
此处需要注意一点,VOLUME ["/dataVolumeContainer","/dataVolumeContainer2","/dataVolumeContainer3"],在这条命令中,只指定了 Docker 容器中的目录,而没有指定宿主机目录,与上面讲过的 “docker run -it -v 宿主机目录:容器目录 容器名”不同,这条指令即制定容器目录,又指定了宿主机的目录。
那么为什么 Dockerfile 要这么设计呢,即只指定容器目录,不指定宿主机目录.这是出于DockerFile可移植性的考虑,因为如果指定了宿主机的目录,DockerFile就依赖特定宿主机,如果想要在其他机器上跑这个DockerFile,其他机器必须也得有这个目录,否则就跑不了,DokerFile的移植性变差。只指定docker目录,那么在所有机器上都可以跑。
cd /mydocker
vi Dockerfile
// 之后将上面的 Dockerfile 内容写入 Dockerfile 文件中。
3、build 我们创建好的 Dockerfile文件,生成一个新的镜像,新镜像的镜像名为 zhouxuejiao/centos。docker build -f /mydocker/Dockerfile -t zhouxuejiao/centos .。记住最后这个点一定不要忘记,“.”表示当前路径,忘记写了,运行这段命令就会报错。
4、运行 zhouxuejiao/centos 镜像,docker run -it zhouxuejiao/centos,生成的容器里面会自动创建 /dataVolumeContainer1 和 /dataVolumeContainer2 这两个文件夹,即容器的容器数据卷。
5、Dockerfile 只指定了容器内目录地址,没有指定其对应的主机目录地址,那么执行完上述步骤后,容器内的目录怎么知道其对应的主机目录在哪呢?
Docker 替我们想到这一点了,它会在宿主机上为我们指定一个默认的目录,对应我们创建的容器数据卷。
这个默认目录怎么找呢,返回到宿主机,执行 docker inspect 容器ID,在返回的 json 串中,"Volumes":{"/dataVolumeContainer1":"主机目录1","/dataVolumeContainer2":"主机目录2"},主机目录1是容器数据卷/dataVolumeContainer1对应的主机目录,主机目录2是/dataVolumeContainer2对应的主机目录。
怎么验证呢?我们在容器内的 /dataVolumeContainer1 目录下创建一个文件 conatiner1.txt,然后我们回到主机,cd 主机目录1,能看到conatiner1.txt这个文件,说明 /dataVolumeContainer1 和 主机目录1 是互通的。
4、使用容器数据卷时的一些补充
(1)如果进入到容器卷后,不能执行写操作,即访问出现 cannot open directory:Permission denied 错误。解决办法:在挂载容器卷的指令后面多加一个--privileged=true(赋予权限,权限设置为true)参数即可。docker run -it -v /主机目录1:/dataVolumeContainer1 -v /主机目录2:/dataVolumeContainer2 centos --privileged=true /bin/bash。
(2)容器停止并退出,此时主机修改和容器数据卷对应的目录,然后容器重新启动,问:容器中会不会有主机的修改呢?答案是会的,因为在容器停止的这段时间,主机的修改也会被同步到容器中。
首先,我们将容器停止并退出(exit)。
然后,回到宿主机,新建一个 host02.txt 文件,并且修改 host.txt,增加一行 host update 01。
(3)我们想对容器数据卷做一个保护,让其只可以读不可以写(不可以写,指不可以人为写,但是主机的修改还是会同步到容器数据卷上的),那应该怎么做呢?
只需要让命令带权限即可,在命令中加:ro(即read only),docker run -it -v /宿主机绝对路径目录:/容器内目录:ro 镜像名。
如果是不带权限的命令, docker inspect 容器ID,返回的 json 串中,"VolumeRW":{"/dataVolumeContainer":true},如果是带权限的命令,返回的json串中,"VolumeRW":{"/dataVolumeContainer":false},RW 即 read write。
我们来演示一下带权限命令的使用:
1、运行 docker run -it -v /myDataVolume:/dataVolumeContainer:ro centos。
2、回到主机(Ctrl+P+Q)。进入到主机目录,增加一个文件 host.txt,并且在 host.txt 中增加一行 host update。
3、 重新进入到容器中,发现 dataVolumeContainer 目录下自动生成 host.txt 文件,且文件中有 host update,说明带权限的命令,主机仍然可以对数据卷进行写操作。
4、但是当我们想在容器卷 dataVolumeCantainer 中创建一个文件时,就会报错 Read-only file。。。,说明带权限的命令可以阻止人为修改数据卷,人为只可以读,不可以写,见图16。同理修改文件 vi host.txt,想要加上一行 container update,仍然报错 read only,见图17、图18。