ARTICLE DETAIL

资讯详情

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

90DaysOfDevOps:深入剖析 Docker 镜像结构 —— 从分层原理到 Dockerfile 构建实战

90DaysOfDevOps:深入剖析 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点击查看免费下载镜像Image是 Docker 一切操作的起点也是容器编排、CI/CD 与云原生交付的基础单元。本文以 90DaysOfDevOps 第 45 天的内容为骨架对应 2022/ja/Days/day45.md系统讲解 Docker 镜像的分层结构、Dockerfile 的指令体系以及从编写 Dockerfile、docker build构建、运行容器到推送到 DockerHub 的完整实战链路并结合本仓库 Containers 目录下的真实示例文件进行源码级印证。读完本文你将能够独立设计并构建一个可复现、可分享的自定义 Docker 镜像。从镜像到容器先回顾 Docker 镜像是什么在动手构建之前先明确镜像的定义Docker 镜像是一个只读模板read-only template其中包含一组用于创建可在 Docker 平台上运行的容器的指令。它提供了一种便捷的方式将应用程序和预配置的服务器环境打包在一起既可以供个人私有使用也可以公开分享给其他 Docker 用户。对于任何 Docker 初学者而言镜像就是入门的起点。第 44 天2022/ja/Days/day44.md我们体验过如何借助 Docker Desktop 与 DockerHub 部署和运行经过验证的官方镜像例如docker run -it ubuntu bash拉起的 Ubuntu 容器、docker run hello-world这种运行即退出的示例容器。但那是消费镜像而不是制造镜像。如果只是拉取一个 Ubuntu 镜像手动在容器里安装软件问题马上就会出现一旦容器被关闭或销毁所有软件更新与安装都会随之消失不存在一份可重复使用的记录。这种方式适合演示 Docker 的能力却无法满足在多个环境中以同一套软件配置稳定交付镜像的需求。这正是 Dockerfile 存在的意义。什么是 Dockerfile镜像的施工图纸Dockerfile 是一个文本文件其中包含你平时手动执行、用于构建 Docker 镜像的命令。Docker 会读取 Dockerfile 中的指令自动完成镜像构建。也就是说Dockerfile 把人工操作变成了声明式脚本。理解 Dockerfile首先要理解镜像的分层模型构成 Docker 镜像的每一个文件单元被称为一个层layer这些层以堆叠的方式逐级构建形成一系列镜像每一层都依赖其正下方的那一层层的顺序直接决定镜像生命周期管理的效率。下图直观展示了只读镜像层 容器可写层的整体结构一个镜像由多层只读 Layer 堆叠而成多个容器可以共享同一镜像但各自拥有独立的薄可写层Thin Writeable Layer层的顺序为何如此关键原则很简单把最常变化的层尽量放在栈的顶部。原因是当你修改镜像中的某一层时Docker 不仅会重建该层还会重建所有基于它构建的上层。因此变更发生在底层 → 需要重建其上所有层工作量最大变更发生在顶层 → 只需重建最少的层整个镜像的重新构建开销最小。在实际工程中这一原则直接体现为 Dockerfile 的书写顺序稳定的基础镜像和系统级依赖放前面频繁变动的应用源码放最后从而最大化利用构建缓存、最小化重建代价。容器层镜像与运行中容器的唯一差异每当 Docker 从镜像启动一个容器它都会额外添加一个可写层writeable layer即容器层container layer。容器运行期间的所有变更写入文件、修改配置、安装软件都保存在这一层。因此容器层是正在运行的容器与源镜像之间唯一的差异任意数量的同类容器可以共享访问同一个底层镜像同时各自维护独立的运行状态。回到第 44 天的 Ubuntu 例子对同一个 Ubuntu 镜像反复执行docker run在第一个容器里安装 pinta约 200MB 的图像编辑器在第二个容器里安装 figlet——两个容器用途、大小完全不同但它们共享同一个底层镜像却互不共享状态删除容器后这些状态也随之消失。父镜像Parent Image与 Manifest上面的 Ubuntu 镜像以及 DockerHub 和其他第三方仓库中大量现成的镜像通常被称为父镜像parent image。它是所有其他层构建的地基为容器环境提供基础积木。除了各层文件本身Docker 镜像还包含一个额外的清单文件manifest它本质上是镜像的 JSON 格式描述包含镜像标签tag、数字签名以及针对不同宿主机平台如何配置容器的细节信息。这也是同一个镜像能够在多架构平台上运行的技术基础——拉取时 Docker 会根据宿主平台选择对应的 manifest 条目。创建 Docker 镜像的两种方式创建镜像有两条路径本仓库 Containers 目录下的文件正是围绕第二种方式准备的方式一docker commit临时快照法这是即兴做法承接第 44 天的流程选取基础镜像 → 启动容器 → 安装所需软件与依赖 → 然后执行docker commit 容器名执行后本地docker images列表以及 Docker Desktop 的 Images 标签页中就会出现这份镜像的本地副本。这个方法非常简单直接但作者明确不推荐用于正式场景它难以进行生命周期管理且伴随大量手动配置/重配置。它最适合测试、故障排查、验证依赖关系等临时需求是构建镜像最快、最简单的途径。方式二Dockerfile声明式构建这是本仓库推荐的正式方式通过 Dockerfile 获得干净、紧凑、可重复的镜像创建过程生命周期管理容易得多也能轻松接入持续集成CI与持续交付CD流水线。代价是比docker commit稍复杂但更符合真实世界、企业级的容器部署实践。一个典型的 Dockerfile 构建是三步走创建 Dockerfile → 编写组装镜像所需的指令 → 执行构建。下面的流程示意图清晰展示了 Dockerfile → 镜像 → 容器这条核心工作链路Dockerfile 核心指令速查下表汇总了本文以及日常实战中最常用的 Dockerfile 指令是编写 Dockerfile 时最直接的参考指令用途FROM指定父镜像parent image是每个 Dockerfile 的第一条有效指令。WORKDIR为后续指令设置工作目录路径不存在时会自动创建。RUN安装容器所需的应用程序和软件包是执行apt、yum、pip等安装命令的载体。COPY从特定位置将文件或目录复制进镜像。ADD功能同 COPY但额外支持远程 URL 和自动解压压缩文件。ENTRYPOINT容器启动时始终会执行的命令若未指定默认是/bin/sh -c。CMD传给 ENTRYPOINT 的参数若未设置 ENTRYPOINT默认为/bin/sh -cCMD 将成为容器启动时执行的命令。EXPOSE声明容器应用对外提供服务的端口用于定义访问容器应用的入口。LABEL为镜像添加元数据如维护者、版本、用途说明。补充几个原文未展开、但实战中高频使用的指令要点CMD 与 ENTRYPOINT 的区别ENTRYPOINT 固定入口不易被覆盖CMD 提供默认参数或默认命令docker run时追加的参数可覆盖 CMD从而灵活扩展容器行为EXPOSE 只是声明而非发布真正让端口对外可达需要docker run -p 宿主机端口:容器端口或 Docker Compose 中的ports映射见下文仓库示例USER 指令切换运行身份如非 root 用户是容器安全加固的常用手段本仓库的 Dockerfile 中就有实际使用。动手实践构建第一个 Docker 镜像准备目录与 .dockerignore在仓库的 Containers 目录中我们创建一个工作目录并在其中创建.dockerignore文件——它的作用与上一节讲到的.gitignore类似列出那些在 Docker 构建过程中产生、但你不希望进入最终镜像的文件。记住容器世界的核心信条一切从紧凑出发尽可能快、尽可能无臃肿no bloat。.dockerignore就是控制镜像体积的第一道闸门。编写一个极简 Dockerfile原文示例同样可在上述目录中找到如下# 使用官方 Ubuntu 18.04 作为基础镜像 FROM ubuntu:18.04 # 安装 nginx 和 curl RUN apt-get update apt-get upgrade -y RUN apt-get install -y nginx curl RUN rm -rf /var/lib/apt/lists/*指令解读FROM ubuntu:18.04指定父镜像与标签18.04 是当时 LTS 版本的 UbuntuRUN apt-get update apt-get upgrade -y先同步软件源索引再升级已有软件包RUN apt-get install -y nginx curl安装 nginx Web 服务器与 curl 客户端RUN rm -rf /var/lib/apt/lists/*清理 apt 缓存列表这是减小镜像体积的经典手法——每个 RUN 指令产生一个层层越少、层内无用文件越少镜像就越小。值得对照的是仓库中 Containers/Dockerfile 的实际文件做了进一步演进它注释掉了 nginx/curl 的安装行新增了RUN groupadd -g 1000 basicuser useradd -r -u 1000 -g basicuser basicuser和USER basicuser两条指令——创建一个固定 UID/GID 的普通用户并将运行身份切换到它。这正是前面提到的 USER 指令实战以非 root 身份运行容器降低安全风险。对比文档示例与仓库当前版本可以清晰看到能跑到跑得安全的工程演进过程。执行构建在终端中进入上述目录执行docker build -t 90daysofdevops:0.1 .其中-t用于设置镜像名称与标签tag.表示构建上下文为当前目录。构建过程会逐条执行 Dockerfile 指令、生成对应镜像层最终产出一个名为90daysofdevops、标签为0.1的本地镜像运行镜像并验证构建完成后既可以在 Docker Desktop 中启动容器也可以用命令行直接运行docker run -it 90daysofdevops:0.1 bash进入容器后执行curl会发现此前通过 RUN 指令安装的 curl 已经可用——这验证了 Dockerfile 中每条指令都被如实固化进了镜像而非停留在某个易失的容器状态里。这正是 Dockerfile 方法与docker commit的本质区别可复现、可交付。在 Docker Desktop 中管理镜像Inspect、Pull 与 Push镜像构建完成后Docker Desktop 的 UI 提供了针对该镜像的额外管理能力Inspect检查查看镜像的详细配置你能在其中看到 Dockerfile 中的指令与期望在容器内执行的命令即镜像的构建历史与元数据Pull拉取此时执行会失败并报错因为该镜像尚未托管在任何远端仓库中Push to hub推送到 DockerHub将镜像推送到 DockerHub 仓库推送成功后 Pull 选项即可正常使用。关于推送有一点必须注意如果此前构建命令只写了docker build -t 90daysofdevops:0.1 .直接推送是行不通的。推送到 DockerHub 要求镜像名必须携带你的 DockerHub 用户名正确写法为docker build -t {{username}}/{{imagename}}:{{version}}即镜像的完整引用格式为DockerHub用户名/镜像名:版本标签这与第 44 天从 DockerHub 拉取docker/getting-started、hello-world、ubuntu等官方镜像时的命名约定完全一致——官方镜像可以省略用户名而个人仓库必须显式携带。推送完成后登录 DockerHub 即可在个人仓库中看到刚刚推送的镜像此后在任何安装了 Docker 的环境里都能通过docker pull {{username}}/{{imagename}}:{{version}}重新拉取使用。仓库配套示例从单镜像到多服务编排本文讲解的 Dockerfile 只是起点。在 Containers 目录下还准备了两个多服务编排示例可以帮助你理解父镜像 端口映射 数据卷在实际项目中的组合方式my_wordpress/docker-compose.yaml以mysql:5.7和wordpress:latest两个官方镜像为父镜像通过ports: 8000:80发布 WordPress 端口并用命名卷db_data、wordpress_data持久化数据库与站点文件——数据卷对应第 44 天 Docker Desktop 中提到的 Volumes 概念elasticsearch-logstash-kibana/docker-compose.yml组合 elasticsearch、logstash、kibana 三个官方镜像ELK 日志分析栈通过ports发布 9200/9300/5601 等端口用healthcheck定义健康检查并用networks: driver: bridge组建服务间通信网络——这些正是 EXPOSE、端口映射与容器网络的工程化落地。这两个示例印证了本文的核心结论Dockerfile 定义镜像怎么做而镜像一旦构建好就可以像乐高积木一样被任意组合、共享与再发布。你在 Containers 目录中可以同时看到镜像构造层Dockerfile与镜像消费层docker-compose两类文件的完整配套。小结回顾第 45 天的核心脉络Docker 镜像是只读模板由多层只读 Layer 构成容器启动时叠加一层可写容器层同一镜像可被多容器共享层的排列顺序决定构建与生命周期管理的效率高频变化的内容应置于栈顶镜像由父镜像 各层文件 JSON 格式的 manifest 清单组成manifest 携带标签、签名与平台配置两种构建方式中docker commit适合快速验证Dockerfile 才是可重复、可审计、适合 CI/CD 的企业级方案一个规范的构建流程是准备.dockerignore→ 编写 DockerfileFROM/RUN/COPY/CMD/EXPOSE/LABEL 等指令→docker build -t user/image:tag .→ 运行验证 → 推送到 DockerHub。下一节Day 46将在此基础之上继续深入容器生态的更多主题。如果你想一边阅读一边练习直接参考本仓库 Containers 目录中的 Dockerfile 与两个 docker-compose 示例即可开始。赞分享文档/教程【免费下载链接】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 开源学习计划文档/教程docker-stacks镜像构建原理从Dockerfile到优化交付docker stacks镜像构建原理从Dockerfile到优化交付 一、镜像构建基础分层与继承架构 docker stacks采用分层构建策略所有镜像云原生开发工具数据科学创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表