第 5 章 · 02 镜像构建与容器生命周期


文档摘要

第 5 章 · 02 镜像构建与容器生命周期 镜像的分层模型告诉我们"每一层来自一条指令",本节就把这些指令逐个讲透。Dockerfile 是构建镜像的配方文件,其中最微妙的是 RUN/CMD/ENTRYPOINT 三兄弟的分工与 Exec form/Shell form 的写法陷阱。后半节走出构建,看看容器从 run 到 stop 的一生,以及 docker run 背后那条 daemon → containerd → runc 的链路。 学习目标 掌握 Dockerfile 关键指令的作用与建层规则 说清 RUN/CMD/ENTRYPOINT 的区别与拼接逻辑 避开 Exec form 与 Shell form 混用陷阱,理解 ADD 与 COPY 的取舍 会用构建上下文、.

第 5 章 · 02 镜像构建与容器生命周期

镜像的分层模型告诉我们"每一层来自一条指令",本节就把这些指令逐个讲透。Dockerfile 是构建镜像的配方文件,其中最微妙的是 RUN/CMD/ENTRYPOINT 三兄弟的分工与 Exec form/Shell form 的写法陷阱。后半节走出构建,看看容器从 run 到 stop 的一生,以及 docker run 背后那条 daemon → containerd → runc 的链路。

学习目标

  • 掌握 Dockerfile 关键指令的作用与建层规则
  • 说清 RUN/CMD/ENTRYPOINT 的区别与拼接逻辑
  • 避开 Exec form 与 Shell form 混用陷阱,理解 ADD 与 COPY 的取舍
  • 会用构建上下文、.dockerignore 组织构建
  • 掌握容器生命周期命令与四种重启策略
  • 理解 docker run 的完整幕后链路

一、Dockerfile 指令地图

Dockerfile 是容器引擎可自动读取的文本文件,包含构建镜像的全部指令。按"是否创建新层"可分为两类:创建新层的指令(FROM、COPY、RUN 等)与只写元数据、不建层的指令(ENTRYPOINT、ENV、EXPOSE 等)。

指令 作用 建层
FROM 指定基础镜像(几乎总是第一条,可前置 ARG)
RUN 构建时执行命令,结果写入新层
COPY 从构建上下文复制文件/目录进镜像
ADD COPY 的增强版:支持 URL 源与 tar 自动解压
WORKDIR 设置工作目录,影响其后所有指令
EXPOSE 声明暴露端口(仅文档化元数据,不真正发布)
ENTRYPOINT 指定容器启动时运行的固定命令
CMD 指定容器启动时的默认命令/参数
ENV 设置环境变量
USER 设置运行镜像时使用的用户(可含组)
ARG 构建参数(仅构建期可见)
HEALTHCHECK 定义容器健康检查命令

两个最佳实践先立起来:FROM 必须显式指定 tag(否则永远拉 latest,镜像随时间漂移导致不可复现);EXPOSE 只是"说明书",真正发布端口靠运行时 -p 参数。

二、RUN vs CMD vs ENTRYPOINT:三兄弟的分工

这是容器面试的第一高频题,核心是分清"构建期"与"运行期":

指令 执行时机 行为
RUN 构建时 执行命令并写入镜像,成为新的一层
CMD 运行时 容器启动的默认命令,可被 run 命令行参数覆盖
ENTRYPOINT 运行时 容器启动的固定命令,run 参数不覆盖它

CMD 与 ENTRYPOINT 的关系是拼接:容器启动时,引擎把 ENTRYPOINT 与 CMD 拼在一起执行——ENTRYPOINT 固定命令,CMD 提供默认参数。docker run -it image ls 中的 ls 会覆盖 CMD(追加替换默认参数),但不会覆盖 ENTRYPOINT。规则还有三条:

  • Dockerfile 中可写多条 ENTRYPOINT/CMD,但只有最后一条生效;
  • 只定义 CMD 未定义 ENTRYPOINT 时,使用基础镜像的 ENTRYPOINT(若有);
  • 若镜像定义了 ENTRYPOINT,docker run image 某命令 传的命令会替换 CMD 部分而非 ENTRYPOINT。

典型设计:ENTRYPOINT 固定主程序(如 httpd),CMD 提供默认参数;需要覆盖启动参数时用 --entrypoint 显式指定。

三、Exec form 与 Shell form:两种写法与一个陷阱

每条指令都有两种写法:

# Exec form(推荐) ENTRYPOINT ["cmd", "param0", "param1"] CMD ["param0"] # Shell form ENTRYPOINT cmd param0 param1 CMD param0

区别在 Shell form 会把命令包装进 /bin/sh -c,因此会多创建一个 shell 进程;Exec form 直接以参数数组执行,不经过 shell(信号传递更干净,是官方推荐形式)。单独用任何一种都没问题,混用会出事故

ENTRYPOINT ["ls"] CMD /tmp

实际执行的是 ls /bin/sh -c /tmp——Exec form 下 CMD 的参数被原样追加,而 Shell form 的 CMD 变成了三个参数。结果 ls 把 "/bin/sh"、"-c"、"/tmp" 当参数列出三个不存在的文件。判断技巧:写出拼接后的完整命令行再核对。

四、COPY vs ADD:增强版与它的代价

COPY 与 ADD 都从构建上下文复制文件进镜像。区别是 ADD 额外支持两种来源:直接使用 URL 下载;从源位置自动解压 tar 文件到目标。听起来方便,但自动解压/远程拉取的隐式行为容易造成意外结果(比如本想复制一个 tar 却被解开了)。常规复制一律用 COPY,只有明确需要"下载"或"解压"语义时才用 ADD。

五、构建上下文与 .dockerignore

构建上下文(build context)是"位于指定 PATH 或 URL 中的文件集合"。默认情况下目录下所有文件都会被打包进上下文——包括 node_modules、.git 这类巨物,既拖慢传输又可能污染镜像。.dockerignore 的作用就是排除不需要的文件/目录(语义类似 .gitignore)。构建命令在 Dockerfile 所在目录执行:

docker image build -t some_app:latest .

六、容器生命周期命令

场景 命令
运行容器(前台) docker container run ubuntu
后台运行 docker container run -d httpd
附加终端运行 docker container run -it ubuntu /bin/bash
列出运行中容器 docker container ls(docker ps)
列出全部(含已退出) docker ps -a
进入运行中容器 docker container exec -it bash
停止容器 docker container stop
删除容器 docker container rm
删除全部已停止容器 docker rm $(docker ps -a -q)
一键清理 docker system prune

两个高频陷阱先排掉。为什么 docker run ubuntu 之后 ps 是空的:容器为"运行服务/应用"而设计,任务干完立即退出——ubuntu 没有前台任务,瞬间退出;用 docker ps -a 能看到已退出的它,想保持运行就加 -it /bin/bash 或让它执行 sleep。为什么不能直接删运行中的容器:必须 stop 再 rm,或者用 -f。

restart 的行为也要说清:docker restart 不是"杀掉主进程重跑",而是创建一个同 ID 的新容器、复用原容器的文件系统与状态。

七、docker run 的幕后链路

一条 docker run 命令背后是完整的分层架构:

  1. Docker client 把命令转换为 API payload;
  2. POST 到 Docker daemon(dockerd)暴露的 API 端点;
  3. daemon 检查镜像(本地没有则从仓库拉取),通过 gRPC 调用 containerd 创建容器;
  4. containerd 把镜像转换为 OCI bundle,指示 runc 用该 bundle 创建容器;
  5. runc 与内核协作,组合 namespaces、cgroups 构造容器;
  6. 容器进程作为 runc 的子进程启动,启动完成后 runc 退出;
  7. shim(垫片进程) 成为容器的新父进程:向 daemon 报告退出码、保持 stdin/stdout 打开——即使 daemon 重启,容器也不会终止。

这解释了几个经典判断题:杀掉 daemon 不会杀掉运行中的容器(运行时在 containerd/runc 手里);运行 12 个容器不会有 12 个常驻 runc(创建后即退出);daemon 做的是比 containerd 更高层的活(管理网络、卷、镜像,而非直接操作容器)。

Docker 技术栈分三层:Runtime(runc/containerd 负责启停容器)、Daemon(实现 Docker API,负责镜像/认证/安全/网络)、Orchestrator(编排)。containerd 由 Docker 捐给 CNCF,Docker 和 Kubernetes 都拿它当容器运行时。

八、重启策略:容器的自愈开关

容器崩溃了怎么办?重启策略(restart policy)在特定事件后自动重启容器,是生产环境的第一道自愈防线:

策略 行为
no 任何情况都不重启(默认)
always 容器停止时始终重启(手动 stop 除外)
unless-stopped 除非被手动停止,否则始终重启
on-failure 仅因错误退出(退出码非 0)时重启

小结

本节走完了"写配方 → 构建 → 运行"的主线:Dockerfile 指令中 RUN 管构建、CMD/ENTRYPOINT 管运行、COPY 管文件;Exec/Shell 两种形式不能混用;容器生命周期命令构成了日常操作的主干;docker run 链路揭示了三层运行时架构与 shim 的保命作用。镜像构建好了、容器跑起来了,还差最后一块拼图:容器之间如何通信、数据如何持久——下一节讲网络与存储。

下一节预告

第 5 章第 3 节《网络存储与生产实践》将解决容器化落地三问:容器怎么互相访问(CNM/CNI 与端口映射)、数据怎么不丢(volume/bind mount)、多容器应用怎么编排(Compose),最后给出安全五条铁律与多阶段构建。


发布者: 作者: 灏天文库 转发
评论区 (0)
U