Docker - 关于容器数据存储

    技术2025-11-20  53

    容器结构

    Docker容器启动结构:最上层是容器层,可读写层,之下就是镜像层,只读层

    为什么有多个镜像层?

    还记得Docker - 常用命令:帮助、镜像、容器下载镜像时,会发现需要下载多个镜像

    每个容器都类似与简化的独立的Linux,自然需要底层支持,如tomcat肯定是需要jdk的支持的,在后续的DockerFile会介绍

    那容器是如何获得镜像文件?

    Docker运行容器会在镜像栈顶部添加一个读写层,如果运行中的容器修改现有文件时,该文件会从读写层下面的只读层复制到读写层(从上到下一层层找),只读版本是不会改变的,只是我们只能看到读写层的副本,这就是写时复制机制

    这也是为什么不论我们怎么动容器,只要不提交镜像,通过镜像启动其他容器又是初始状态

    存在的问题

    Docker的这种结构,存在一些问题

    关闭或重启容器,该容器的数据不会被影响,但是删除该容器,数据就会丢失,因为容器的数据是不会影响到镜像存在于联合文件系统中,不易于宿主机的访问容器间数据共享不方便

    就现在而言,能够想到保存数据的方法是docker cp从容器内拷贝数据到主机

    什么是联合文件系统?

    也就是上面描述的docker的容器-镜像结构:

    联合文件系统(UnionFS)是一种分层、轻量级并且高性能的文件系统,它支持对文件系统的修改作为一次提交来一层层的叠加,同时可以将不同目录挂载到同一个虚拟文件系统下 联合文件系统是 Docker 镜像的基础。镜像可以通过分层来进行继承,基于基础镜像(没有父镜像),可以制作各种具体的应用镜像。 不同Docker 容器就可以共享一些基础的文件系统层,同时再加上自己独有的改动层,大大提高了存储的效率。


    容器数据卷

    为了解决上述问题,提出“卷”的概念:

    “卷”是容器上的一个或多个“目录”,通过这种目录可以绕过联合文件系统,与宿主机的某目录绑定,可以理解为容器挂载一个虚拟数据卷然后映射到一个主机目录中

    类似与Linux虚拟机与宿主机的共享文件夹


    如何添加数据卷

    说的再多不如操作一遍

    添加容器数据卷有两种方法

    run命令通过-v设置通过DockerFile添加

    run -v添加

    命令:docker run -v 宿主机目录路径:容器目录路径 镜像名

    还是使用centos进行实验

    运行容器:docker run -it -v /RootData:/ContainerData --name mycentos01 centos

    可以看到容器mycentos01创建了数据卷,目录ContainerData

    同样在宿主机创建了RootData文件夹

    我们可以通过创建文件测试:创建container01.txt文件,写一句container add data01

    在宿主机可以查看到:

    通过数据卷,容器与宿主机可以完成数据共享及数据持久化

    我们通过docker inspect(查看容器内部细节)也可以看到相关的信息:

    docker inspect 5838bc2db5a9

    其中有一条Binds属性

    DockerFile创建

    DockerFile是什么? Dockerfile: 用于描述镜像的生成规则 Dockerfile中的每一条命令,都在Docker镜像中以一个独立镜像层的形式存在

    DockerFile就类似与Linux的shell脚本,只不过DockerFile是用来构建镜像的 可以在gitHub上查看这些镜像

    例如这个tomcat镜像的Dockerfile

    因为DockerFile有自己的一套语法,比较复杂,后续介绍

    总之,DockerFile类似与shell脚本一样,用于创建镜像

    我们可以写一个简单的DockerFile:在根目录创建文件夹/mydocker 创建文件DockerFile

    # volume test FROM centos VOLUME ["/dataVolumeContainer","/dataVolumeContainer2"] CMD echo "finished,-----success" CMD /bin/bash

    意思就是,这个新镜像来自centos镜像,类似上面的结构centos为底层镜像;创建数据卷,两个目录路径分别是/dataVolumeContainer,/dataVolumeContainer2,打印finished,-----success,/bin/bash启动

    出于可移植的考虑,不能用 -v 主机目录:容器目录的方法在DockerFile创建,毕竟不是所有宿主机都存在设置的目录

    运行DockerFile创建新镜像

    运行docker build -f /mydocker/DockerFile -t test/centos .

    一层层的运行上面的脚本:

    这样就有了自带数据卷的镜像

    我们可以运行查看:

    那我们宿主机对应的目录在哪呢?我们并没有定义,由Docker设置为默认路径

    根目录下并没有

    通过docker inspect查看

    并不在原来的位置:

    而是在Mounts:Source:

    /dataVolumeContainer对应: "Source": "/var/lib/docker/volumes/709f2595d2af0e95c305f34671a81253c6ff930313b9c85c5eaedcc108a7eb76/_data" /dataVolumeContainer2对应: "Source": "/var/lib/docker/volumes/8a856ecc1707a0612bf8193c09652bae12b86969414c404143afc9d70277197a/_data"

    在dataVolumeContainer创建一个文件

    在宿主机可以查看到:


    数据卷容器

    为了数据持久化、共享,每次启动一个容器都要设置数据卷,太繁琐了

    能不能让数据卷“独立”出来,不同的容器只需要挂载这个数据卷就可以完成数据共享?

    用专门的容器来挂载数据卷,其他容器通过挂载这个父容器来实现数据共享,这个专门挂载数据卷的容器称为数据卷容器

    在docker通过--volumes-from在容器间传递共享

    例如:

    根据上面test/centos镜像,创建容器centos01,docker run -it --name centos01 test/centos,在dataVolumeContainer目录下创建文件container01.txt

    传递共享,以centos01为数据卷容器,创建容器centos02 docker run -it --name centos02 --volumes-from centos01 test/centos

    可以看到数据卷存在文件container01.txt,我们创建一个新文件container02.txt

    继续以centos01为数据卷容器创建新容器centos03

    同样的操作: 确实,所有容器都可以共享文件

    可以看到运行了3个容器:

    我们知道centos01是数据卷容器,centos2、centos3都是继承centos1 如果删除它,其他容器还能共享文件吗?

    删除: 重新连接centos02,创建文件update02.txt,重新连接centos03,可以确定依旧可以共享文件

    得到结论: 容器间配置信息的传递,数据卷的生命周期一直持续到没有容器使用它为止

    Processed: 0.011, SQL: 9