构建镜像:从手动提交到 Dockerfile 革命
Source: Dev.to
1. 手动方式:使用 docker commit 构建镜像
A. docker commit 的工作原理
-
启动一个基础容器 – 运行一个带交互式 shell 的镜像。
docker run -it --name my_sandbox ubuntu:latest bash -
手动进行更改 – 在容器内部安装或配置软件,例如:
apt-get update && apt-get install -y nginx -
退出容器 – 输入
exit停止容器,同时保留可写层中的更改。 -
提交更改 – 将容器当前状态保存为一个新的、不可变的镜像。
docker commit :
示例
docker commit my_sandbox my_custom_nginx:v1.0
B. 升级已提交的镜像
要“升级”镜像,重复以下过程:
- 从
my_custom_nginx:v1.0启动一个新容器。 - 进行进一步的手动更改(例如,更新 Nginx 配置)。
- 再次提交容器,并为新镜像打标签。
docker commit my_custom_nginx:v2.0
2. docker commit 的局限性
| 局限性 | 描述 | 影响 |
|---|---|---|
| 不可追溯 | 容器内部执行的命令没有记录,无法看到文件为何存在或软件包是如何安装的。 | 审计、调试和安全检查几乎不可能。 |
| 不可复现 | 删除镜像后必须手动重复相同的 shell 命令且顺序必须完全一致才能重新构建。 | 在不同环境之间导致开发和部署不一致。 |
| 镜像体积大 | docker commit 常会把不必要的文件(如临时安装缓存)一起捕获进镜像层。 | 镜像臃肿,拉取慢,占用更多磁盘空间。 |
| 安全风险 | 难以验证镜像层的内容或历史。 | 隐蔽漏洞的风险增加。 |
3. Dockerfile 革命
Dockerfile 是一个纯文本文件,包含一系列指令,Docker 按顺序执行这些指令来构建镜像。
为什么要使用 Dockerfile?
- 自动化 – 整个构建过程全部自动化。
- 可追溯 – 每条命令都显式列出,提供透明、可审计的历史。
- 可复现 – 任何拥有 Dockerfile 的人都能一致地重新构建完全相同的镜像。
Dockerfile 成为镜像的源代码,使得构建能够保持小巧、安全并受版本控制的最佳实践。
4. 基本 Dockerfile 指令(第 1 部分)
| 指令 | 用途 | 是否创建层? | 示例 |
|---|---|---|---|
FROM | 指定 构建的基础镜像(必须是第一行)。 | 是 | FROM node:18-alpine |
RUN | 在新层中执行命令(例如安装软件包)。 | 是 | RUN apk add --no-cache git |
WORKDIR | 为后续的 RUN、CMD、ENTRYPOINT、COPY 或 ADD 设置工作目录。 | 是 | WORKDIR /app |
COPY | 将主机上的文件/目录复制到镜像文件系统中。 | 是 | COPY package.json /app |
CMD | 为 运行中的容器 提供默认命令;通常在启动时被覆盖。只能有一个 CMD。 | 是 | CMD ["node", "server.js"] |
EXPOSE | 记录容器运行时监听的端口(不实际发布端口)。 | 否 | EXPOSE 8080 |
理解 CMD 与 RUN 的区别
RUN– 在构建镜像时执行(例如安装软件)。CMD– 在容器启动时执行(例如启动应用)。
对未涉及主题的简要说明
网络
EXPOSE 指令仅记录应用使用的端口。实际的端口映射(如 -p 8080:80)是在 docker run 时或通过编排工具完成的,而不是在 Dockerfile 中。
数据卷
数据卷用于持久化数据,通常通过 docker run -v 或 Docker Compose 定义。可选的 VOLUME 指令可以标记挂载点,但一般更倾向于在镜像外部管理卷。
接下来会讲什么?
现在你已经了解了 Dockerfile 为什么必不可少。第 2 部分我们将深入 docker build 的完整构建流程,探讨 ENTRYPOINT 等高级指令,介绍多阶段构建,并分享将镜像发布到仓库的最佳实践。