mirror of
https://github.com/yeasy/docker_practice.git
synced 2026-03-11 04:14:38 +00:00
172 lines
3.6 KiB
Go
172 lines
3.6 KiB
Go
## 7.1 RUN 执行命令
|
||
|
||
### 7.1.1 基本语法
|
||
|
||
如下代码块所示,展示了相关示例:
|
||
|
||
```docker
|
||
RUN <command>
|
||
RUN ["executable", "param1", "param2"]
|
||
```
|
||
|
||
`RUN` 指令是 Dockerfile 中最常用的指令之一。它在 **当前镜像层** 之上创建一个新层,执行指定的命令,并提交结果。
|
||
|
||
---
|
||
|
||
### 7.1.2 两种格式对比
|
||
|
||
#### 1. Shell 格式
|
||
|
||
如下代码块所示,展示了相关示例:
|
||
|
||
```docker
|
||
RUN apt-get update
|
||
```
|
||
|
||
- **特点**:默认通过 `/bin/sh -c` 执行。
|
||
- **优势**:可以使用环境变量、管道、重定向等 Shell 特性。
|
||
- **示例**:
|
||
```docker
|
||
RUN echo "Hello" > /test.txt
|
||
```
|
||
|
||
#### 2. Exec 格式
|
||
|
||
如下代码块所示,展示了相关示例:
|
||
|
||
```docker
|
||
RUN ["apt-get", "update"]
|
||
```
|
||
|
||
- **特点**:直接调用可执行文件,不经过 Shell。
|
||
- **优势**:避免 Shell 字符串解析问题,适用于参数中包含特殊字符的情况。
|
||
- **注意**:无法使用 `$VAR` 环境变量替换 (除非显式调用 shell)。
|
||
|
||
---
|
||
|
||
### 7.1.3 常见最佳实践
|
||
|
||
#### 1. 组合命令 (减少层数)
|
||
|
||
每一个 `RUN` 指令都会新建一层镜像。为了减少镜像体积和层数,应使用 `&&` 连接命令。
|
||
|
||
**❌ 糟糕的写法** (创建 3 层):
|
||
|
||
```docker
|
||
RUN apt-get update
|
||
RUN apt-get install -y nginx
|
||
RUN rm -rf /var/lib/apt/lists/*
|
||
```
|
||
|
||
**✅ 推荐写法** (创建 1 层):
|
||
|
||
```docker
|
||
RUN apt-get update && \
|
||
apt-get install -y nginx && \
|
||
rm -rf /var/lib/apt/lists/*
|
||
```
|
||
|
||
#### 2. 清理缓存
|
||
|
||
在安装完软件后,立即清除缓存,可以显著减小镜像体积。
|
||
|
||
- **Debian/Ubuntu**:
|
||
```docker
|
||
RUN apt-get update && apt-get install -y package-bar \
|
||
&& rm -rf /var/lib/apt/lists/*
|
||
```
|
||
|
||
- **Alpine**:
|
||
```docker
|
||
RUN apk add --no-cache package-bar
|
||
```
|
||
|
||
#### 3. 使用 `set -e` 和 `pipefail`
|
||
|
||
默认情况下,管道命令 `cmd1 | cmd2` 只要 `cmd2` 成功,整个 `RUN` 就视为成功。
|
||
|
||
**❌ 隐蔽的错误**:
|
||
|
||
```docker
|
||
## 如果下载失败,gzip 可能会报错,但如果不影响后续,构建可能继续
|
||
|
||
RUN wget http://error-url | gzip -d > file
|
||
```
|
||
|
||
**✅ 推荐写法**:
|
||
|
||
```docker
|
||
SHELL ["/bin/bash", "-o", "pipefail", "-c"]
|
||
RUN wget http://url | gzip -d > file
|
||
```
|
||
|
||
---
|
||
|
||
### 7.1.4 常见问题
|
||
|
||
#### Q:为什么 `RUN cd /app` 不生效?
|
||
|
||
如下代码块所示,展示了相关示例:
|
||
|
||
```docker
|
||
RUN cd /app
|
||
RUN touch hello.txt
|
||
```
|
||
|
||
**结果**:`hello.txt` 会出现在根目录 `/`,而不是 `/app`。**原因**:每个 `RUN` 都在一个新的 Shell/容器环境中执行。`cd` 只影响当前 `RUN` 的环境。**解决**:使用 `WORKDIR` 指令。
|
||
|
||
```docker
|
||
WORKDIR /app
|
||
RUN touch hello.txt
|
||
```
|
||
|
||
#### Q:环境变量不生效?
|
||
|
||
如下代码块所示,展示了相关示例:
|
||
|
||
```docker
|
||
RUN export MY_VAR=hello
|
||
RUN echo $MY_VAR
|
||
```
|
||
|
||
**结果**:输出为空。**原因**:同上,环境变量只在当前 `RUN` 有效。**解决**:使用 `ENV` 指令,或在同一行 `RUN` 中导出。
|
||
|
||
```docker
|
||
ENV MY_VAR=hello
|
||
RUN echo $MY_VAR
|
||
```
|
||
|
||
---
|
||
|
||
### 7.1.5 高级技巧
|
||
|
||
#### 1. 使用 BuildKit 的挂载缓存
|
||
|
||
BuildKit 支持在 `RUN` 指令中使用 `--mount` 挂载缓存,加速构建。
|
||
|
||
```docker
|
||
## 缓存 apt 包
|
||
|
||
RUN --mount=type=cache,target=/var/cache/apt,sharing=locked \
|
||
--mount=type=cache,target=/var/lib/apt,sharing=locked \
|
||
apt-get update && apt-get install -y gcc
|
||
```
|
||
|
||
```docker
|
||
## 缓存 Go 模块
|
||
|
||
RUN --mount=type=cache,target=/go/pkg/mod \
|
||
go build -o app
|
||
```
|
||
|
||
#### 2. 挂载密钥
|
||
|
||
安全地使用 SSH 密钥或 Token,而不将其记录在镜像中。
|
||
|
||
```docker
|
||
RUN --mount=type=secret,id=mysecret \
|
||
cat /run/secrets/mysecret
|
||
```
|
||
|
||
---
|