90DaysOfDevOps 第 45 天:深入剖析 Docker 镜像的内部结构与 Dockerfile 镜像构建实战

发布时间:2026/10/6 12:19:07

90DaysOfDevOps 第 45 天:深入剖析 Docker 镜像的内部结构与 Dockerfile 镜像构建实战 文档/教程【免费下载链接】90DaysOfDevOpsThis repository started out as a learning in public project for myself and has now become a structured learning map for many in the community. We have 3 years under our belt covering all things DevOps, including Principles, Processes, Tooling and Use Cases surrounding this vast topic.项目地址https://gitcode.com/gh_mirrors/90/90DaysOfDevOps点击查看免费下载本篇是 90DaysOfDevOps 容器学习路线中承上启下的一课。在第 44 天通过 Docker Desktop 与 DockerHub 体验了拉取并运行现成镜像之后本课带你钻进镜像内部理解只读分层Layer、容器可写层、父镜像Parent Image与 Manifest 清单的构成并掌握两条创建自有镜像的路径——docker commit快速方案与基于 Dockerfile 的可重复构建方案。读完本文你将具备从零编写 Dockerfile、本地构建、镜像检查到推送 DockerHub 的完整实战能力。Docker 镜像的本质只读模板与容器的起点先回顾一个贯穿始终的定义Docker 镜像Image是一个只读模板其中包含一组用于创建容器的指令容器可在 Docker 平台上运行。它提供了一种便捷方式将应用与预配置好的服务器环境打包在一起既可以供自己私有使用也可以公开分享给其他 Docker 用户。对于任何首次接触 Docker 的人来说镜像都是入门的起点。上一课我们验证过官方 Ubuntu 镜像本身不足 30MB启动后只有一个精简的 bash 环境。这说明镜像并非操作系统的完整拷贝而是一个精炼到只包含所需依赖的可执行模板。镜像只负责定义环境真正运行起来的、可变的那部分属于容器。为什么需要 Dockerfile解决不可重复的痛点第 44 天我们做过一个演示从 Ubuntu 镜像启动容器在里面apt安装软件得到装好软件的自定义容器。但问题随之而来——一旦容器被关闭或销毁所有软件更新与安装全部丢失我们得到的是一份不可重复的一次性结果。这在演示能力时没问题但在生产场景下是致命的无法在多个环境之间传输同一套软件配置无法保证每次运行容器时都安装完全一致的软件集合无法把镜像构建接入**持续集成/持续交付CI/CD**流程。而 Dockerfile 正是解决这一切的工具把手动执行的命令固化为文本文件由 Docker 自动读取并重建出完全一致的镜像实现可重复、可版本化、可审计的镜像构建。理解 Docker 镜像的分层结构层Layer镜像的最小组成单元构成 Docker 镜像的每个文件都被称为一个层Layer。这些层按阶段堆叠形成一系列相互叠加的镜像每一层都依赖其紧邻的下层层与层之间按顺序累积上层的变化建立在下层之上。层的顺序决定生命周期管理效率的关键变化最频繁的层应尽量放在栈顶是核心优化原则。原因在于当你修改镜像中的某一层时Docker 不仅要重建该层本身还要重建所有建立在它之上的层。因此修改顶部层→ 重建工作量最小整个镜像只需极少的重编译修改底部层→ 其上所有层全部失效并重建成本最高。这也是为什么成熟的 Dockerfile 习惯把最稳定、最少变动的基础依赖如系统包安装放在最前面把频繁变动的应用代码放在最后——充分利用构建缓存与最小化重建范围。容器层Container Layer运行时的唯一增量每次 Docker 从镜像启动容器时都会额外添加一个可写层即容器层Container Layer用于存储在容器整个运行周期内产生的所有变更安装的软件、生成的文件、进程状态等。这里有一个非常关键的性质容器层是正在运行的容器与源镜像之间的唯一差异。任意数量的同类容器都可以共享访问同一个底层镜像同时各自维护自己的状态互不干扰。回到 Ubuntu 的例子我们可以用同一镜像启动多个容器——第一个容器安装pinta图像编辑器体积约 200MB第二个容器安装figlet字符艺术工具两个容器应用不同、用途不同、体积不同但它们共享同一镜像却不共享状态删除容器后各自的可写状态也随之消失。上图正是这一机制的直观示意三个容器各自拥有独立的薄可写层其下是它们共同引用的只读镜像层。父镜像Parent Image一切层的基石上述 Ubuntu 镜像以及 DockerHub 和第三方仓库中大量现成的镜像通常被称为父镜像Parent Image。它是所有其他层赖以构建的地基为容器环境提供最基本的构建块。选择可信的父镜像尤其是 Docker 官方镜像与认证发布者提供的镜像是镜像安全与稳定性的第一道防线。Manifest 清单镜像的身份证除了若干独立的层文件一个 Docker 镜像还包含一个附加文件——Manifest清单。它本质上是镜像的 JSON 格式描述包含镜像标签image tags数字签名digital signature面向不同宿主平台配置容器的细节信息。也就是说镜像 一组只读层文件 描述这些层如何组装与运行的 Manifest。创建 Docker 镜像的两种方式方式一docker commit——最快但不可管理第一种方式延续上一课的边跑边改思路选定基础镜像并启动容器在容器内安装所有需要的软件与依赖执行docker commit container name将当前容器状态固化为一个新的本地镜像。这样新镜像会出现在docker images列表与 Docker Desktop 的 Images 标签页中。作者对此方法的评价非常明确除非你想理解这个过程否则不推荐。因为这种方式难以管理生命周期需要大量手动配置与反复调整。但它也有价值——最快、最直接非常适合测试、故障排查、验证依赖关系等场景。方式二Dockerfile——干净、紧凑、可重复正式项目应采用 Dockerfile 方式。它带来干净、紧凑、可重复的镜像构建更轻松的生命周期管理与**持续集成CI/持续交付CD**流程的天然集成。虽然上手难度略高于docker commit但这种方式更贴近真实世界、企业级的容器部署实践。构建过程本质上是三步流程创建 Dockerfile → 在其中编写组装镜像所需的指令 → 交给 Docker 构建引擎执行。Dockerfile 核心指令速查表以下是构建 Dockerfile 时最常用指令的完整说明原文表格逐条保留并补充细节指令用途FROM指定父镜像即所有后续层的构建基础。WORKDIR设置 Dockerfile 中后续命令的工作目录。RUN在构建阶段执行命令通常用于安装容器所需的应用和软件包。COPY从指定位置复制文件或目录到镜像中。ADD与COPY类似但额外支持远程 URL并可自动解压压缩文件。ENTRYPOINT容器启动时总是执行的命令若未指定默认值为/bin/sh -c。CMD传给入口点的参数若未设置ENTRYPOINT默认/bin/sh -c则CMD即为容器实际执行的命令。EXPOSE声明通过哪个端口访问容器应用仅为声明实际发布端口还需-p映射。LABEL为镜像添加元数据如维护者、版本、用途说明。补充说明COPY与ADD的取舍ADD功能更强支持远程 URL 与自动解压但隐式行为更多可预测性略差在仅需拷贝本地文件时优先COPY是业界普遍做法。动手实战构建你的第一个 Docker 镜像准备构建上下文与 .dockerignore本仓库已在 2022/Days/Containers 目录中放置了本次实战所需文件。建议效仿.gitignore先创建一个.dockerignore文件它用于列出那些会在 Docker 构建过程中被创建、但你不希望进入最终镜像的文件从源头控制镜像体积。仓库中的 2022/Days/Containers/.dockerignore 即为此用途当前为空文件作为占位模板。牢记容器设计的核心理念一切从紧凑出发尽可能快速、零臃肿no bloat。编写一个极简 Dockerfile原文给出的基础示例# Use the official Ubuntu 18.04 as base FROM ubuntu:18.04 # Install nginx and curl RUN apt-get update apt-get upgrade -y RUN apt-get install -y nginx curl RUN rm -rf /var/lib/apt/lists/*而仓库中的 2022/Days/Containers/Dockerfile 在此基础上做了演进——在完成系统更新后新增了创建非特权用户并以该用户身份运行的指令# Use the official Ubuntu 18.04 as base FROM ubuntu:18.04 # Install nginx and curl RUN apt-get update apt-get upgrade -y #RUN apt-get install -y nginx curl #RUN rm -rf /var/lib/apt/lists/* RUN groupadd -g 1000 basicuser useradd -r -u 1000 -g basicuser basicuser USER basicuser对照两版可以读出两个实战要点分层顺序FROM父镜像在最底部然后是系统级RUN指令这正对应前面最稳定的层放最底、变化最频繁的层放最顶的优化原则。安全基线USER basicuser让容器进程以非 root 身份运行groupadd/useradd指定固定 UID/GID1000是容器化部署中的常见安全加固手段。执行构建docker build在包含 Dockerfile 的目录下的终端中执行docker build -t 90daysofdevops:0.1 .参数说明-t为镜像指定名称与标签tag格式为name:tag此处为90daysofdevops:0.1.构建上下文build context为当前目录Docker 会将该目录内容发送给构建引擎并被.dockerignore过滤。构建完成后镜像出现在本地的docker images列表与 Docker Desktop 的 Images 标签页中。此时可以运行镜像例如通过 Docker Desktop 启动容器并在容器的 CLI 中验证软件是否就绪——按原文示例镜像中应包含curl可在容器命令行直接调用。使用 docker inspect 检查镜像在 Docker Desktop 的 Images 标签页中可以对镜像执行Inspect展开后能清晰看到镜像的构建历史——Dockerfile 中的每条指令都会对应一条历史记录包括每层创建的文件、执行的命令与镜像层元数据。docker inspect命令行版本同样可以查看镜像的 Manifest、标签、暴露端口、入口点等完整 JSON 信息是排查镜像构成的常用手段。推送镜像到 DockerHub命名规范是关键构建出的镜像目前只存在于本地。Docker Desktop 的镜像详情中提供了Pull与Push to hub两个入口Pull对本地未托管的镜像执行会失败镜像尚未托管在任意注册中心这是符合预期的行为Push to hub可将镜像推送至 DockerHub。这里有一个极易踩坑的细节此前使用的docker build -t 90daysofdevops:0.1 .无法直接推送。推送要求镜像名称以 DockerHub 用户名开头正确命令应为docker build -t {{username}}/{{imagename}}:{{version}} .例如docker build -t yourname/90daysofdevops:0.1 .其中{{username}}是 DockerHub 账号名{{imagename}}是仓库名{{version}}是版本标签。推送成功后在 DockerHub 的仓库页面中即可看到刚推送的新镜像回到 Docker Desktop 的该镜像详情页Pull 按钮便可用——无论在本机还是其他环境都可以通过拉取该镜像复现完全一致的部署。至此我们完成了从 Dockerfile 定义 → 本地构建 → 推送注册中心 → 再次拉取运行的完整闭环这正是第 44 天结尾所承诺的目标做出一个产物并上传到自己的 DockerHub 仓库并能够部署它。总结从定义到交付的完整链路把本课内容串成一条主线Dockerfile定义→docker build→ Docker Image只读层 Manifest→docker run→ Docker Container叠加可写容器层。整条链路上Dockerfile 负责可重复定义分层机制负责共享与高效变更最小化、多容器共享只读层容器层负责运行时状态隔离父镜像与 Manifest 负责构建基座与镜像身份描述-t username/imagename:tag命名规范负责注册中心互通。这套知识是后续容器网络、安全加固、编排Kubernetes等课程的前置基础。延伸资源与下一步本仓库中与本课直接相关的材料可继续深入实战目录与 Dockerfile2022/Days/Containers含 Dockerfile、.dockerignore多服务编排示例2022/Days/Containers/my_wordpress/docker-compose.yaml与2022/Days/Containers/elasticsearch-logstash-kibana/docker-compose.yml可观察真实项目中如何用 Compose 组合多个镜像上一课基础第 44 天 Docker 镜像与 Docker Desktop 实战下一课预告第 46 天继续容器主题的深入。赞分享文档/教程【免费下载链接】90DaysOfDevOpsThis repository started out as a learning in public project for myself and has now become a structured learning map for many in the community. We have 3 years under our belt covering all things DevOps, including Principles, Processes, Tooling and Use Cases surrounding this vast topic.项目地址https://gitcode.com/gh_mirrors/90/90DaysOfDevOps点击查看免费下载相关推荐90DaysOfDevOps 第 45 天深入剖析 Docker 镜像的结构与 Dockerfile 实战90DaysOfDevOps 第 45 天深入剖析 Docker 镜像的结构与 Dockerfile 实战 本文是 90DaysOfDevOps 系列第 45文档/教程90DaysOfDevOps 实战Docker 镜像解剖与 Dockerfile 构建指南Day 4590DaysOfDevOps 实战Docker 镜像解剖与 Dockerfile 构建指南Day 45 本篇是 90DaysOfDevOps 开源学习计划文档/教程90DaysOfDevOps深入剖析 Docker 镜像结构 —— 从分层原理到 Dockerfile 构建实战90DaysOfDevOps深入剖析 Docker 镜像结构 —— 从分层原理到 Dockerfile 构建实战 镜像Image是 Docker 一切操作文档/教程上一篇老旧Mac升级终极指南让您的2012-2015款设备免费升级到最新macOS系统下一篇AtlasOS深度解析让你的Windows系统焕发新生的终极优化方案创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
延伸阅读

更多相关文章

2026/10/6 12:19:07

大模型应用开发笔记(一)——初识GPT

基础概念 AI:让计算机系统有能力执行那些通常需要人类智慧的任务。ML:开发算法使系统能够通过实例自己学习。DL:深度学习算法专注于受大脑结构启发的算法——人工神经网络(ANN)。Transformer:关注句子或段落的不同部分&#xff0c…

2026/10/6 13:14:10

Git + 云端仓库实战:安装配置、SSH免密与分支合并全攻略

1. 项目安全同步,为什么非 Git 不可 1.1 你还在用文件夹命名来"管理版本"吗 先问你一个扎心的问题:你的项目文件里,是不是还有这种东西—— 项目最终版_v5 、 项目最终版_真的不改了 、 项目最终版_最终最终_0321 &#xff…

2026/10/6 13:14:10

Git入门到实战:版本管理、云端仓库与分支合并全攻略

前言:从“一个文件夹复制10个版本”到真正敢改代码当我第一次用Git,是在一个凌晨两点钟,项目眼看着少了一块核心代码,而我手头只有三天前的压缩包备份。当时心里就一个念头:如果早知道“版本管理”四个字这么值钱&…

2026/10/6 13:14:10

CSR-DCF视频目标跟踪实战:从环境搭建到参数调优全记录

CSR-DCF这个项目,我在实验室里前前后后折腾了快两周才把全套流程跑通。当时刚接触视频目标跟踪,论文看了好几遍,觉得原理好像懂了,一上手源码才发现全是细节坑。这篇文章就记录我从零开始编译、配置、运行CSR-DCF的完整过程&#…

2026/10/6 13:14:10

Redis分布式锁实战:setnx+UUID防并发重复与幂等

你是不是也遇到过这种诡异现场:同一笔订单的支付回调被第三方平台连推三次,库存居然被扣了两次;或者表单只是双击了一下提交按钮,数据库里就多出两条一模一样的记录。很多人第一反应是加锁,用 synchronized 锁自己进程…

2026/10/6 13:14:10

Java服务TIME_WAIT过多?原理排查治理全解析

这个标题我太熟了。有段时间我负责的Java服务一到业务高峰期, netstat 一查就是几万个TIME_WAIT状态,端口被占满,新连接报 address already in use ,那个焦头烂额的感觉现在还记得。后来翻内核文档、看TCP协议栈实现、调应用配…

2026/10/6 13:09:10

TCP与UDP原理与实战:握手、协议栈与iperf3排障指南

我调试网络问题最怕遇到一种情况:链路明明通着,但数据就是传得不对。要么客户端报 Address already in use ,要么抓包软件里刷出一排 TCP Dup ACK ,要么UDP打流时丢包率忽高忽低。很多刚入门的同事把锅甩给交换机或网卡&#…

2026/10/5 6:32:56

Jev+Agent接管浏览器:browser-use实战与jev-ultrafast性能优化

1. 从“Jev”说起:为什么我要把Agent接进浏览器“Jev”这个词最近在圈子里出现的频率越来越高,很多人第一次听到会以为是某个新模型的名字,其实它更像是一种思路——把Jev模型的能力当作底座,通过Agent的方式去接管浏览器&#xf…

2026/10/6 4:01:51

多智能体集群实战:DeepAgents编排、MCP与A2A协议及Skills体系

1. 从"单兵作战"到"集群协同":多智能体编排到底在解决什么问题如果你最近在折腾 Agent 相关的东西,大概率会有一种感觉:单个 Agent 能做的事情,其实很快就摸到天花板了。你给它一个提示词,挂几个工…

2026/10/5 17:38:27

无源低通滤波器设计实战:从RC到LC,手把手教你避开那些坑

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/6 0:03:23

MR25H40CDF+STM32F031C6工业级高可靠数据存储方案

1. 项目概述:为什么在工业现场非得用 MR25H40CDF 配 STM32F031C6 做数据存储?在工厂产线的 PLC 控制柜里、在风电变流器的散热片背面、在矿井监测终端的金属外壳下,你经常能看到一块指甲盖大小的黑色芯片——它既不是 Flash,也不是…

2026/10/6 0:03:23

MRAM+STM32工业断电数据保全实战指南

1. 项目概述:为什么在工业现场非得用 MR25H40CDF 配 STM32F031C6 做数据存储?在工厂产线的PLC柜里、在野外无人值守的环境监测终端里、在高速运转的包装机控制板上,你经常能看到一块指甲盖大小的黑色芯片,旁边贴着“MR25H40CDF”丝…

还想了解更多?直接咨询顾问

免费诊断 + 免费方案 + 透明报价。

全国咨询热线400-8866-253
免费获取方案
☎咨询二维码 ☎ ↑