ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

Docker 镜像的多层结构是如何实现的?

Docker 镜像的多层结构是如何实现的? 一、什么是镜像的多层结构1.1 直观理解# Dockerfile FROM ubuntu:20.04 # 层1: 基础系统 RUN apt-get update \ # 层2: 安装软件 apt-get install -y python3 COPY app.py /app/ # 层3: 复制文件 CMD [python3, /app/app.py] # 层4: 元数据每一行指令都会创建一个新层--------------------- | 容器层 (可写) | ← 容器启动时添加 --------------------- | 层4: CMD命令 | ← 镜像层4元数据 --------------------- | 层3: COPY app.py | ← 镜像层3文件 --------------------- | 层2: apt-get install| ← 镜像层2修改 --------------------- | 层1: ubuntu:20.04 | ← 基础镜像层 ---------------------1.2 生活中的类比想象一本漫画书每层是一张透明的胶片每张胶片上画了一部分内容叠加在一起所有胶片叠起来就是完整的画面可以复用多个漫画可以共享相同的背景胶片修改只影响上层如果要改某个细节,只需要覆盖新的胶片在上层二、多层结构的核心技术2.1 UnionFS联合文件系统UnionFS 是 Docker 镜像分层的基础,它允许将多个目录联合挂载成一个虚拟文件系统。# UnionFS 的工作原理# 目录A: /var/lib/docker/overlay2/layer1# 目录B: /var/lib/docker/overlay2/layer2# 目录C: /var/lib/docker/overlay2/layer3# 通过 UnionFS 挂载成一个目录mount-toverlay overlay\-olowerdir/layer3:/layer2:/layer1\/merged# 在 merged 目录看到的是三个层的合并视图2.2 OverlayFSDocker 现在使用的存储驱动Docker 主要使用OverlayFS实现多层结构# OverlayFS 结构# lowerdir: 只读层镜像层# upperdir: 可写层容器层# workdir: 工作目录# merged: 最终视图mount-toverlay overlay\-olowerdir/lower1:/lower2:/lower3,\upperdir/upper,\workdir/work\/merged实际例子# 查看容器的文件系统层dockerinspect容器|grep-A10GraphDriver{GraphDriver:{Data:{LowerDir:/var/lib/docker/overlay2/123.../diff,MergedDir:/var/lib/docker/overlay2/456.../merged,UpperDir:/var/lib/docker/overlay2/789.../diff,WorkDir:/var/lib/docker/overlay2/abc.../work},Name:overlay2}}三、多层结构的实现原理3.1 写时复制Copy-on-Write这是 Docker 最巧妙的设计之一# 1. 镜像层都是只读的# 2. 容器启动时在最上层加一个可写层# 3. 读文件从上往下找找到第一个就返回# 4. 写文件复制到可写层再修改# 读文件示例cat/etc/nginx/nginx.conf# 查找顺序: 可写层 → 层4 → 层3 → 层2 → 层1# 修改文件echonew config/etc/nginx/nginx.conf# 1. 在可写层复制一份 nginx.conf# 2. 修改可写层的副本# 3. 以后再读从可写层返回3.2 层的存储结构# Docker 存储目录/var/lib/docker/ ├── overlay2/ │ ├── l/# 符号链接│ ├──123.../# 层ID│ │ ├── diff/# 实际文件内容│ │ ├──link# 短链接名│ │ ├── lower# 下层信息│ │ └── work/# 工作目录│ ├──456.../ │ └──789.../ ├── image/ │ └── overlay2/ │ ├── repositories.json# 镜像仓库信息│ ├── layerdb/# 层数据库│ └── distribution/# 分发信息3.3 层的元数据# 查看镜像的层信息dockerhistorynginx:latest IMAGE CREATED CREATED BY SIZE abc123def4562hours ago /bin/sh-c#(nop) CMD [nginx -g daemon… 0Bdef456abc1232hours ago /bin/sh-c#(nop) STOPSIGNAL SIGQUIT 0B789abc123def2hours ago /bin/sh-c#(nop) EXPOSE 80 0B456def789abc2hours ago /bin/sh-c#(nop) ENTRYPOINT [/docker-entr… 0B123abc456def2weeks ago /bin/sh-c#(nop) COPY file:09... in / 1.2kB............每层包含实际文件这一层添加/修改的文件元数据指令信息、环境变量等层ID唯一标识基于内容哈希四、多层结构的优势4.1 存储效率# 多个镜像共享基础层 FROM ubuntu:20.04 # 层1120MB RUN apt-get install -y python3 # 层250MB FROM ubuntu:20.04 # 复用相同的层1 RUN apt-get install -y nodejs # 不同层240MB效果10 个基于 Ubuntu 的镜像,只存一份 Ubuntu 层磁盘空间节省 90% 以上4.2 传输效率# 推送镜像时只推送变化的层dockerpush myapp:1.0# 输出# Layer 1: 已存在,跳过# Layer 2: 已存在,跳过# Layer 3: 推送新层# 拉取镜像dockerpull myapp:1.0# 只下载本地没有的层4.3 构建缓存# 利用层缓存加速构建 FROM node:16 COPY package*.json ./ # 如果 package.json 没变,这层缓存 RUN npm install # 直接使用缓存的 node_modules COPY . . # 只有这层重新构建缓存命中效果第二次构建只需几秒而不是几分钟CI/CD 流程大幅加速五、层的存储和分发5.1 层的存储格式# 每一层压缩后是一个 tar 包# /var/lib/docker/overlay2/123.../# └── diff/# ├── etc/# ├── usr/# └── var/# 导出镜像查看层dockersave myapp:1.0-omyapp.tartar-xvfmyapp.tar# 看到多个 layer.tar 文件,每个对应一层5.2 层的分发过程是否是否构建镜像计算每层哈希层是否存在?复用现有层压缩新层上传到仓库其他节点拉取本地是否有?复用本地层下载缺失层六、实际操作验证6.1 查看镜像层# 查看镜像分层dockerinspect nginx:latest|jq.[0].RootFS{Type:layers,Layers:[sha256:2edcec3590a4ec7f40cf0743c15d78fb39d8326bc029073b41ef9727da6c851f,sha256:9df5e5c6c8f0bdfb0a7264f69d368af23c92b059a8ae82a9a622ac19b26f5a1a,...]}# 查看层大小dockerhistory--no-trunc nginx:latest6.2 查看 OverlayFS 挂载# 查看容器的 Overlay 挂载mount|grepoverlay# 查看各层内容ls/var/lib/docker/overlay2/l/ls/var/lib/docker/overlay2/层ID/diff/6.3 验证写时复制# 运行容器dockerrun-d--nametestnginx# 找到容器的 upper 目录UPPER$(dockerinspecttest|jq-r.[0].GraphDriver.Data.UpperDir)# 查看目录内容ls$UPPER# 修改容器内文件dockerexectestbash-cecho test /usr/share/nginx/html/test.html# 查看 upper 目录文件出现在这里ls$UPPER/usr/share/nginx/html/七、最佳实践7.1 优化层数# ❌ 太多层 FROM ubuntu RUN apt-get update RUN apt-get install -y curl RUN apt-get install -y nginx RUN rm -rf /var/lib/apt/lists/* # ✅ 合并 RUN FROM ubuntu RUN apt-get update \ apt-get install -y curl nginx \ rm -rf /var/lib/apt/lists/*7.2 利用缓存# 把变化少的放前面 FROM python:3.9 COPY requirements.txt . # 少变 RUN pip install -r requirements.txt # 少变 COPY . . # 多变放最后7.3 清理不必要的文件# 安装后清理缓存 RUN apt-get update \ apt-get install -y curl \ apt-get clean \ rm -rf /var/lib/apt/lists/*7.4 多阶段构建# 构建阶段大 FROM maven:3.8 AS builder COPY . . RUN mvn package # 运行阶段小 FROM openjdk:11-jre-slim COPY --frombuilder target/*.jar app.jar # 最终镜像只有最后一层八、常见问题Q1层数太多会影响性能吗# 理论上有影响文件查找要遍历所有层# 但实际影响很小内核有缓存# 建议合理合并层但不必过度优化Q2删除文件为什么镜像大小不变FROM ubuntu RUN apt-get install -y big-package # 层1添加100MB RUN rm -rf /big-package # 层2标记删除 # 最终镜像还是 100MB因为层1的文件还在 # 需要在同一层删除Q3如何查看每层的大小# 使用 dive 工具dive myapp:latest# 或使用 docker historydockerhistory--no-trunc myapp:latest九、总结概念说明多层结构镜像由多个只读层叠加而成UnionFS联合文件系统,合并多个目录OverlayFSDocker 使用的存储驱动写时复制修改文件时复制到可写层层缓存复用已有层加速构建层共享多个镜像共享相同基础层一句话理解Docker 镜像的多层结构通过UnionFS/OverlayFS实现,每层只记录相对于上一层的差异,利用写时复制技术,实现了高效的存储、传输和构建缓存。
返回列表