15.3.1 镜像概念与分层原理
镜像是容器运行所需的只读模板,包含应用程序、依赖库、运行时及默认配置。镜像与容器的关系类似于类与实例:镜像提供静态文件与元数据,容器是在镜像基础上叠加可写层后的运行态。
镜像的核心概念#
- 只读模板:镜像本身不可变,任何修改都发生在容器可写层。
- 可移植与一致性:镜像打包了运行环境,使开发、测试、生产环境一致。
- 不可变发布:镜像版本化交付,便于回滚和审计。
分层原理与联合文件系统#
Docker 镜像采用分层结构,每一层是对前一层的增量变更,通常由 Dockerfile 的一条指令生成一层。运行容器时,镜像的多层只读层通过联合文件系统(UnionFS,如 Overlay2)组合成统一视图,并在最上层叠加容器可写层。
分层机制的关键点:
- 层的复用:相同基础层可被多个镜像共享,节省存储与拉取时间。
- 写时复制(Copy-on-Write):当容器修改文件时,会在可写层创建副本,不会影响底层镜像层。
- 层缓存:构建镜像时按层缓存,未变更的层可复用,提高构建效率。
原理草图(镜像层与容器层叠加):
镜像与容器层的差异#
- 镜像层:只读、可共享、由构建过程生成。
- 容器层:可写、不可共享、随容器生命周期创建与销毁。
- 性能影响:大量小文件写入会产生较多写时复制开销,应合理规划数据持久化与分离写入路径。
镜像元数据与配置#
镜像包含元数据(如入口命令、环境变量、暴露端口、工作目录等),这些配置决定容器启动行为。元数据也按层叠加,后续层的配置可覆盖前层。
示例与命令解析#
1) 检查存储驱动(安装验证与环境检查)#
# 查看Docker信息,确认存储驱动(如 overlay2)
docker info | grep -i 'Storage Driver'
预期效果:输出 Storage Driver: overlay2,表示支持分层与联合文件系统。
2) 观察镜像层与大小#
# 拉取镜像并查看层信息
docker pull nginx:1.25
docker history nginx:1.25
命令说明:
- docker pull:下载镜像到本地。
- docker history:按层展示镜像构建历史和每层大小。
预期效果:输出多层记录,FROM、RUN、COPY 等对应不同层。
3) 查看镜像元数据(入口命令、环境变量等)#
# 查看镜像配置与元数据
docker image inspect nginx:1.25 --format \
'Entrypoint={{.Config.Entrypoint}} Cmd={{.Config.Cmd}} Env={{.Config.Env}}'
预期效果:展示 ENTRYPOINT、CMD 与 ENV 等信息。
4) 写时复制示例(验证可写层)#
# 启动容器并在可写层修改文件
docker run -d --name web1 nginx:1.25
docker exec web1 sh -c 'echo "hi from container" > /usr/share/nginx/html/hello.txt'
docker exec web1 cat /usr/share/nginx/html/hello.txt
预期效果:容器内可读到 hi from container,但镜像层不变。
常见排错#
1) 镜像层损坏或拉取失败#
现象:unexpected EOF、layer does not exist
排查与处理:
# 清理可能损坏的缓存并重试
docker image prune -f
docker pull nginx:1.25
说明:清理未使用镜像与缓存后重新拉取。
2) 存储驱动不一致导致容器异常#
现象:升级Docker后容器启动失败、层无法挂载
排查:
docker info | grep -i 'Storage Driver'
处理建议:确保升级前后使用相同存储驱动;必要时迁移或备份 /var/lib/docker。
实践提示#
- 选择稳定、体积小的基础镜像(如 alpine、debian-slim)可降低层数与体积。
- 合并 Dockerfile 指令减少层数,提升构建与分发效率。
- 应用与数据分离,避免写入镜像层导致不必要的膨胀。
练习#
1) 拉取 alpine:3.19,使用 docker history 观察层数与大小,并解释为什么比 ubuntu 更小。
2) 用 docker image inspect 找到 nginx:1.25 的 Cmd 和 ExposedPorts。
3) 启动容器修改文件后删除容器,再次启动新容器验证该文件是否存在,解释写时复制机制。