mirror of
https://github.com/yeasy/docker_practice.git
synced 2026-08-10 08:27:25 +00:00
fix(content): harden Docker practice guide
This commit is contained in:
@@ -59,7 +59,7 @@ LABEL maintainer="user@example.com" \
|
||||
|
||||
```docker
|
||||
LABEL org.opencontainers.image.authors="yeasy" \
|
||||
org.opencontainers.image.documentation="https://yeasy.gitbooks.io" \
|
||||
org.opencontainers.image.documentation="https://yeasy.gitbook.io/docker_practice/" \
|
||||
org.opencontainers.image.source="https://github.com/yeasy/docker_practice" \
|
||||
org.opencontainers.image.licenses="MIT"
|
||||
```
|
||||
|
||||
@@ -2,9 +2,9 @@
|
||||
|
||||
### 官方文档
|
||||
|
||||
* `Dockerfile` 官方参考手册:https://docs.docker.com/engine/reference/builder/
|
||||
* `Dockerfile` 官方参考手册:https://docs.docker.com/reference/dockerfile/
|
||||
|
||||
* `Dockerfile` 最佳实践指南:https://docs.docker.com/develop/develop-images/dockerfile_best-practices/
|
||||
* `Dockerfile` 最佳实践指南:https://docs.docker.com/build/building/best-practices/
|
||||
|
||||
* `Docker` 官方镜像 `Dockerfile` 库:https://github.com/docker-library/docs
|
||||
|
||||
|
||||
+29
-28
@@ -4,12 +4,12 @@
|
||||
|
||||
在开始前,让我们直言不讳:**在大多数情况下,你应该使用 COPY,而不是 ADD**。
|
||||
|
||||
`ADD` 在 `COPY` 基础上增加了两个额外功能,但这些功能往往引入复杂性而非便利:
|
||||
`ADD` 在 `COPY` 基础上增加了两个额外功能。它不是 `COPY` 的通用替代品,但在少数场景中更合适:
|
||||
|
||||
1. 自动解压 tar 压缩包(有时你想复制一个 .tar.gz 本身,而 ADD 会意外地解压它)
|
||||
2. 支持从 URL 下载文件(这个功能由于网络不稳定已被广泛认为是反模式)
|
||||
2. 支持从 URL 下载公开远程文件,并可配合 `--checksum` 做校验
|
||||
|
||||
**实践中的建议**:除非你明确需要自动解压功能(比如官方基础镜像构建根文件系统),否则始终使用 COPY。原因很简单——显式优于隐式。你的 Dockerfile 在 6 个月后被接手维护时,清晰的意图会让团队少走很多弯路。
|
||||
**实践中的建议**:本地普通文件默认用 COPY;本地 tar 自动解压或公开远程 artifact 下载并校验时用 ADD;需要认证、请求头、重试或自定义解压流程时用 `RUN curl/wget`。
|
||||
|
||||
### 7.3.1 基本语法
|
||||
|
||||
@@ -20,7 +20,7 @@ ADD [选项] ["<源路径>", ... "<目标路径>"]
|
||||
`ADD` 在 `COPY` 基础上增加了两个功能:
|
||||
|
||||
1. 自动解压 tar 压缩包
|
||||
2. 支持从 URL 下载文件 (不推荐)
|
||||
2. 支持从 URL 下载文件
|
||||
|
||||
---
|
||||
|
||||
@@ -30,11 +30,11 @@ ADD [选项] ["<源路径>", ... "<目标路径>"]
|
||||
|------|------|-----|
|
||||
| 复制本地文件 | ✅ | ✅ |
|
||||
| 自动解压 tar | ❌ | ✅ |
|
||||
| 支持 URL | ❌ | ✅ (不推荐)|
|
||||
| 支持 URL | ❌ | ✅ (公开 artifact 可配合校验使用)|
|
||||
| 行为可预测性 | ✅ 高 | ⚠️ 低 |
|
||||
| 推荐程度 | ✅ **优先使用** | 仅解压场景 |
|
||||
| 推荐程度 | ✅ **普通复制优先使用** | 解压、本地 Git/公开远程 artifact |
|
||||
|
||||
> **笔者建议**:除非需要自动解压 tar 文件,否则始终使用 COPY。明确的行为比隐式的魔法更好。
|
||||
> **笔者建议**:普通复制始终优先 COPY;只有当你明确需要 ADD 的额外语义时再使用 ADD,并把意图写清楚。
|
||||
|
||||
---
|
||||
|
||||
@@ -79,7 +79,7 @@ app.tar.gz 包含: /app/ 目录结果:
|
||||
```
|
||||
---
|
||||
|
||||
### 7.3.4 URL 下载功能:不推荐
|
||||
### 7.3.4 URL 下载功能:谨慎使用
|
||||
|
||||
#### 基本用法
|
||||
|
||||
@@ -89,32 +89,29 @@ app.tar.gz 包含: /app/ 目录结果:
|
||||
ADD https://example.com/app.zip /app/app.zip
|
||||
```
|
||||
|
||||
#### 为什么不推荐
|
||||
#### 使用边界
|
||||
|
||||
| 问题 | 说明 |
|
||||
| 场景 | 建议 |
|
||||
|------|------|
|
||||
| 权限固定 | 下载的文件权限为 600,通常需要额外 RUN 修改 |
|
||||
| 不会解压 | URL 下载的压缩包不会自动解压 |
|
||||
| 缓存问题 | URL 内容变化时不会重新下载 |
|
||||
| 层数增加 | 需要额外 RUN 清理 |
|
||||
| 公开、版本固定的远程 artifact | 使用 `ADD --checksum=sha256:... URL dest` |
|
||||
| 需要认证、请求头或复杂重试 | 使用 `RUN curl/wget` |
|
||||
| 下载后需要复杂解压、校验或清理 | 使用 `RUN curl/wget`,把流程显式写出 |
|
||||
| URL 内容可变但无校验 | 不建议直接写入 Dockerfile |
|
||||
|
||||
#### 推荐替代方案
|
||||
#### 推荐写法
|
||||
|
||||
```docker
|
||||
## ❌ 不推荐:使用 ADD 下载
|
||||
## ✅ 公开 artifact:使用 ADD 并固定校验值
|
||||
|
||||
ADD https://example.com/app.tar.gz /tmp/
|
||||
ADD --checksum=sha256:<digest> https://example.com/app.tar.gz /tmp/app.tar.gz
|
||||
RUN tar -xzf /tmp/app.tar.gz -C /app && rm /tmp/app.tar.gz
|
||||
|
||||
## ✅ 推荐:使用 RUN + curl
|
||||
## ✅ 需要认证、请求头或特殊处理:使用 RUN + curl
|
||||
|
||||
RUN curl -fsSL https://example.com/app.tar.gz | tar -xz -C /app
|
||||
```
|
||||
优势:
|
||||
`ADD --checksum` 的优势是缓存更精确,并且校验值直接绑定到 Dockerfile。`RUN curl/wget` 的优势是控制力更强,适合企业内网、认证下载或复杂处理。
|
||||
|
||||
- 一条 RUN 完成下载、解压、清理
|
||||
- 减少镜像层数
|
||||
- 更清晰的构建意图
|
||||
|
||||
---
|
||||
|
||||
@@ -139,6 +136,10 @@ ADD rootfs.tar.gz /
|
||||
## 解压应用包
|
||||
|
||||
ADD dist.tar.gz /app/
|
||||
|
||||
## 下载公开 artifact 并校验
|
||||
|
||||
ADD --checksum=sha256:<digest> https://example.com/app.tar.gz /tmp/app.tar.gz
|
||||
```
|
||||
|
||||
#### ❌ 不适合使用 ADD
|
||||
@@ -149,9 +150,9 @@ ADD dist.tar.gz /app/
|
||||
ADD package.json /app/ # ❌
|
||||
COPY package.json /app/ # ✅
|
||||
|
||||
## 下载文件(用 RUN + curl)
|
||||
## 需要认证或复杂下载逻辑(用 RUN + curl/wget)
|
||||
|
||||
ADD https://example.com/file / # ❌
|
||||
ADD https://example.com/file / # ❌ 无法传认证信息,也没有显式处理
|
||||
RUN curl -fsSL ... -o /file # ✅
|
||||
|
||||
## 需要保留 tar 不解压(用 COPY)
|
||||
@@ -203,14 +204,14 @@ COPY . /app/
|
||||
ADD app.tar.gz /app/
|
||||
```
|
||||
|
||||
#### 3. 不要用 ADD 下载文件
|
||||
#### 3. 远程 artifact 使用 ADD 时必须固定校验值
|
||||
|
||||
```docker
|
||||
## ❌ 避免
|
||||
## ✅ 公开 artifact
|
||||
|
||||
ADD https://example.com/file.tar.gz /tmp/
|
||||
ADD --checksum=sha256:<digest> https://example.com/file.tar.gz /tmp/file.tar.gz
|
||||
|
||||
## ✅ 推荐
|
||||
## ✅ 认证下载或复杂处理
|
||||
|
||||
RUN curl -fsSL https://example.com/file.tar.gz | tar -xz -C /app
|
||||
```
|
||||
|
||||
@@ -65,7 +65,7 @@ CMD echo $HOME
|
||||
|
||||
CMD ["sh", "-c", "echo $HOME"]
|
||||
```
|
||||
**优点**:可以使用环境变量、管道等 shell 特性
|
||||
**优点**:可以使用 `$HOME` 这类 shell 变量展开、管道等 shell 特性
|
||||
|
||||
**缺点**:主进程是 sh,信号无法正确传递给应用
|
||||
|
||||
@@ -77,7 +77,7 @@ CMD ["sh", "-c", "echo $HOME"]
|
||||
|------|----------|-----------|
|
||||
| 主进程 | 指定的程序 | `/bin/sh` |
|
||||
| 信号传递 | ✅ 正确 | ❌ 无法传递 |
|
||||
| 环境变量 | ❌ 需要 shell 包装 | ✅ 自动解析 |
|
||||
| `$VAR` shell 展开 | ❌ 不自动展开;环境变量仍会传入进程 | ✅ 自动展开 |
|
||||
| 推荐使用 | ✅ 大多数场景 | 需要 shell 特性时 |
|
||||
|
||||
#### 信号传递问题示例
|
||||
|
||||
@@ -212,7 +212,7 @@ ENV HOST=localhost \
|
||||
|
||||
#### Q:环境变量在 CMD 中不展开
|
||||
|
||||
exec 格式不会自动展开环境变量:
|
||||
exec 格式不会自动执行 shell 展开,因此命令参数里的 `$PORT` 会按字面值传给进程;但环境变量本身仍会注入进程环境,应用可以通过语言运行时读取。
|
||||
|
||||
```docker
|
||||
## ❌ 不会展开 $PORT
|
||||
|
||||
@@ -88,17 +88,17 @@ $ docker run -v /my/data:/var/lib/mysql mysql:8.4
|
||||
|
||||
### 7.8.5 VOLUME 在构建时的特殊行为
|
||||
|
||||
> ⚠️ **重要**:VOLUME 之后对该目录的修改会被丢弃!
|
||||
> ⚠️ **重要**:`VOLUME` 之后再写入该目录的构建语义取决于 builder。legacy builder 会丢弃这些修改;BuildKit 会保留。但运行容器时,一旦该路径挂载了卷,卷会遮蔽镜像内同路径的内容。
|
||||
|
||||
```docker
|
||||
FROM ubuntu
|
||||
VOLUME /data
|
||||
|
||||
## ❌ 这个文件不会出现在镜像中!
|
||||
## ⚠️ legacy builder 会丢弃;BuildKit 会保留,但运行时挂载卷会遮蔽它
|
||||
|
||||
RUN echo "hello" > /data/test.txt
|
||||
```
|
||||
**原因**:在构建过程中,VOLUME 指令会为该目录创建一个临时的匿名卷。后续 RUN 指令对该目录的写入实际发生在这个临时卷中,而非镜像层。当该 RUN 指令结束后,临时卷被丢弃,因此写入的内容不会保存到最终镜像中。注意:这与容器运行时创建的匿名卷是不同的——运行时创建的卷会在容器生命周期内持续存在。
|
||||
**原因**:旧 builder 会在构建过程中为该目录创建临时匿名卷,后续写入发生在临时卷中;BuildKit 则会把修改保留在镜像层。为了避免不同 builder 下出现不同结果,也为了避免运行时卷遮蔽镜像内初始化数据,不要把必须存在的初始化文件写在 `VOLUME` 之后。
|
||||
|
||||
#### 正确做法
|
||||
|
||||
|
||||
@@ -7,12 +7,12 @@
|
||||
| **FROM** | 指定基础镜像 | 必须是第一条指令 |
|
||||
| **RUN** | 在新层执行命令 | 合并命令、清理缓存以减小体积 |
|
||||
| **COPY** | 复制文件 | 优先使用,支持 `--from` |
|
||||
| **ADD** | 更高级的复制 | 自动解压 tar,不推荐用于下载 |
|
||||
| **ADD** | 更高级的复制 | 自动解压 tar;公开远程 artifact 应配合 `--checksum` |
|
||||
| **CMD** | 容器启动默认命令 | 可被 `docker run` 参数覆盖 |
|
||||
| **ENTRYPOINT** | 容器入口点 | 固定启动命令,CMD 作为默认参数 |
|
||||
| **ENV** | 设置环境变量 | 构建时 + 运行时均生效 |
|
||||
| **ARG** | 构建参数 | 仅构建时生效,FROM 后需重新声明 |
|
||||
| **VOLUME** | 定义匿名卷 | VOLUME 之后的修改会丢失 |
|
||||
| **VOLUME** | 定义匿名卷 | 运行时挂载会遮蔽镜像内目录;构建后续写入语义依赖 builder |
|
||||
| **EXPOSE** | 声明端口 | 仅文档作用,不自动映射 |
|
||||
| **WORKDIR** | 指定工作目录 | 替代 `RUN cd`,目录不存在会自动创建 |
|
||||
| **USER** | 指定运行用户 | 用户必须已存在,推荐 gosu |
|
||||
|
||||
Reference in New Issue
Block a user