文章目录
- 一、docker 存储
- 1.1 docker 、宿主机、卷
- 1.1.1 docker、宿主机区别
- 1.1.2 卷介绍
- 1.2 镜像/容器如何存储
- 1.2.1 探索
- 1.2.2 镜像和图层
- 1.2.2 容器和图层
- 1.2.3 写时复制(COW)策略
- 1.3 docker 挂载
- 1.3.1 Volumes
- 1.3.2 Bind Mounts
一、docker 存储
- 引入一个问题:
- 某个宿主机Docker里面运行了十个Nginx,假设一个Nginx运行需要完整的运行环境50M,那么 10 个Nginx合起来占用多少的磁盘空间? 500M 还是更多?
- 如果是一个都占用50M,那么运行很多容器时候,一个50M,越来越多的时候怎么办呢?
- 如果这样的话,为啥不直接在宿主机上部署呢?
1.1 docker 、宿主机、卷
1.1.1 docker、宿主机区别
- docker 相比于宿主机:
- 优点:
- docker 的移植性、便捷性高于在宿主机部署、进程隔离、很方便的资源限制;
- 缺点:
- docker 使用虚拟化技术,存在一定的性能损失;
1.1.2 卷介绍
- 卷实际就是宿主机上的目录或者是文件,可以职级 mount 到容器当中使用,实际生产环境中,需要根据不同类型的服务。不同类型的数据存储要求做出响应的规划,确保服务的可扩展性、稳定性以及数据的安全性;
- 特点:
- 数据卷是目录或者文件,并且可以在多个容器间共同使用;
- 对数据卷的修改会立即更新到容器中;
- 数据卷的数据可以持久保存,即使删除使用该容器卷的容器也没啥影响;
- 在容器里面的写入数据,不会影响镜像本身;
- 适用场景:
- 日志输出;
- 静态WEB页面;
- 配置文件;
- 多容器间目录文件共享;
1.2 镜像/容器如何存储
- docker 使用
storage drivers
来存储镜像层,并将数据存储在容器的可写层(后面介绍)中。- 容器的可写层在容器被删除后不会持久化,但适用于存储运行时生成的临时数据;
storage drivers
针对空间效率进行了优化,但(取决于storage drivers
)写入速度地域本机文件系统性能,尤其是对于使用写时复制文件系统的存储驱动程序;- 写入密集型应用程序(例如数据库存储)会受到性能开销的影响,尤其是在只读层(后面介绍)中存在预先存在的数据时;
1.2.1 探索
- 我们拉取一个Nginx镜像获取分层:
- 这个Nginx是怎么存储的?
docker inspect nginx
查看下,GraphDriver指示了镜像怎么存。
- LowerDir底层目录,diff(只是存储不同),包含小型Linux和装好的软件;
- /var/lib/docker/overlay2/0f0d819110f94ec106d9bd5854e413edfe6b64bee3f37b6f8e3b4339d1996d99/diff
- /var/lib/docker/overlay2/2c6d5a1da33b9c02d168fa482fdbb90833f21ed10a7bdccdc357554ade92f16a/diff
- /var/lib/docker/overlay2/8ba433ec6b745ffee6c5c5ca09809d98d0d4dac3ed21f0e9263e65524cd798ff/diff
- /var/lib/docker/overlay2/6c9e61b637d27e41257041a00b6ff3cc63757e350bdf099f589446dcce87fe0d/diff
- /var/lib/docker/overlay2/31ae5b465988bc70f3bea314e20b38c81dc3abb50ab40f958ae47727e5cefc30/diff
- 镜像分层存储,从下图可以看到最下面的 diff 是个小型的Linux 系统(在构建镜像时候From来的),在构建镜像时候Dockerfile的每一条指令都可能引起系统的修改,所以跟Git一样,只记录修改;
- 我们进入用这个镜像构建的容器里面,发现相同目录的iNode和镜像的iNode一样,说明容器的文件系统是用镜像的文件系统。
- 看下这个容器真正使用的文件系统大小
- 那么问题来了,如果对镜像底层文件的修改,会反映到容器上面么?
- 对容器的文件修改,会修改镜像文件么?如果修改了,再根据镜像启动容器那不是使用修改的镜像么,这合适么?
- 如果能修改,修改后的镜像是个新文件,哪如果很多修改,创建许多镜像,存储等等问题怎么解决?
- 且看下文介绍;
1.2.2 镜像和图层
- docker 镜像是由一系列层构建而成。每一层都代表镜像的Dockerfile中的一条指令,除了最后一层之外,每一层都是只读的;
例如:
# syntax=docker/dockerfile:1
FROM ubuntu:18.04
LABEL org.opencontainers.image.authors="org@example.com"
COPY . /app
RUN make /app
RUN rm -r $HOME/.cache
CMD python /app/app.py
- 上面Dockerfile 包含四个命令:
FROM
语句从ubuntu:18.04
镜像创建一个图层;LABEL
命令仅修改镜像的元数据,不会生成新图层;COPY
命令从Dockerfile 客户端的当前目录添加一些文件;- 第一个
RUN
命令使用make 命令构建应用程序,并将结果写入新的图层;- 第二个
RUN
命令删除缓存目录,并将结果写入图层;CMD
指令指定要在容器中运行的命令,该命令只修改镜像的元数据,而不会生成新的图层;
- 每一层只是它之前的层的一组差异,添加删除文件都会生产一个新的图层;
在创建新容器时,将在基础层的顶部添加一个新的可写层,该层通常成为容器层,对是正在运行的容器所做的所有更改(例如写入新文件、修改现有文件和删除文件)都会写入容器层
;
1.2.2 容器和图层
容器和镜像之间的主要区别在于顶部的可写层
;- 添加新数据或修改现有数据的所有写入容器都存储在此可写层中;
- 当容器被删除时,可写层也被删除,底层的镜像保持不变;
每一个容器都会有自己的可写容器层,所有的变化都存储在这个容器层中,所以多个容器可以共享对同一个底层镜像的访问,同时又拥有自己的数据状态
;- docker 使用存储驱动程序来管理镜像层和可写容器层的内容,不同的存储驱动程序以不同的方式处理实现,但
所有的驱动程序都使用可堆叠的图像层和写时复制(COW)策略
;
- 这些都是一样的,说明容器是用的镜像的文件系统,我们可以看到容器使用宿主机文件系统大小,可以看到使用了很少的存储;
1.2.3 写时复制(COW)策略
- 当使用
docker pull
从镜像仓库拉取一个镜像或者从一个本地还不存在的镜像创建一个容器时,每一层都会被单独拉下来,并存储在Docker本地存储区中,通常Linux主机上在/var/lib/docker
下面;
- 写时复制是一种共享和复制文件以获得最大效率的策略。
- 如果文件或者目录存在于图像的下层,而另一层(包括可写层)需要对其进行读访问,则它仅使用现有文件。
- 第一次另一个层需要修改文件时(在构建镜像或者运行容器时),文件被复制到该层并修改。
- 我们验证下:
- 看下镜像Nginx 配置文件的 INode和容器中配置文件的INode;
- 给容器中nginx 配置文件追加内容,并查看INode,发现没有改变,那么修改保存在哪里了?
- 我们
docker inspect nginx
,看下 UpperDir,发现修改后的文件;
容器最终的完整工作目录全内容会在合并层
,数据卷产生在容器层,所有的增删改在容器层;
- 删除容器后,容器目录还会存在吗?
1.3 docker 挂载
- Docker 为容器提供了,用于在主机中存储文件,以便即使在容器停止后也能持久保存文件的选项:
volumes
- docker 管理宿主机文件系统的一部分,默认位于
/var/lib/docker/volumes 目录中
,(最常用的方式),如果指定则是指定的名字,否则是一串很长的字符串,下面说;
bind mounts
- 可以存储在宿主机系统的任何位置,但是在不同的宿主机系统时不可移植,(比较常用的方式);
tmpfs mount(Linux)
- 主机系统的内存中,而不会写入宿主机的文件系统;(一般都不会用的方式)
1.3.1 Volumes
- 匿名卷
- -v 不以
/
开头的路径 : Docker容器内部绝对路径:docker会自动管理;- 会给创建一个很长字符串为名字的卷,并把容器内绝对路径下文件复制到宿主机docker管理的区域的对应卷目录下;
- 例如:
docker run --name nginx1 -dP -v :/usr/share/nginx/html/ nginx
docker run --name nginx1 -dP -v /usr/share/nginx/html/ nginx
- 具名卷
- -v 不以
/
开头的路径宿主机卷名称(自定义) : Docker容器内部绝对路径:docker会自动管理;- 会给创建一个很长字符串为名字的卷,并把容器内绝对路径下文件复制到宿主机docker管理的区域的对应卷目录下;
- 例如:
docker run --name nginx1 -dP -v nginxhtml:/usr/share/nginx/html/ nginx
1.3.2 Bind Mounts
所有以/开始的都认为是 bind mounts;
- 如果将绑定安装或非空卷安装到存在某些文件或目录的容器中的目录中,则这些文件或目录会被
安装遮盖。- 总结:
外部目录覆盖内部容器目录内容,但不是修改。所以谨慎,外部空文件夹挂载方式也会导 致容器内部是空文件夹。
- 例如:
docker run --name nginx -dP -v /root/html:/usr/share/nginx/html/ nginx
- 这个会在宿主机创建目录,并把目录中的文件覆盖容器中对应的目录,访问会出现 Forbidden;
- 哪怎么解决这个问题呢?
- 提前准备好相应的文件,然后进行挂载或者复制其他容器文件到改挂载目录后修改文件或者其他;