Files
docker_practice/07_dockerfile/7.6_env.md
T
yeasy 08e981018e fix(content): 修正四处书内自相矛盾的技术断言
1. FROM「必须是第一条指令」(4.5、7.16、summary 三处)与本书 7.7.4「FROM 之前的
   ARG」整节直接冲突。官方 Dockerfile 参考写的是 "A Dockerfile must begin with a
   FROM instruction. This may be after parser directives, comments, and globally
   scoped ARGs",且 "ARG is the only instruction that may precede FROM"。三处
   一并改成「第一条构建指令」并点明例外。

2. 12 章小结把 USER Namespace 列进「默认启用」的一档,并写「容器 root ≠ 宿主机
   root」;而同章 12.2.2 明写 USER Namespace 默认**不**启用、需 userns-remap 显式
   开启,18.1 更直说「在默认情况下,容器内的 root 用户(UID=0)就是宿主机上的
   root 用户」。小结按正文口径改回——这条读反了会直接误判容器逃逸的风险面。

3. 11.5「目前支持三种日志驱动类型」是 Compose 早期文本的残留。官方支持 json-file、
   local、syslog、journald、gelf、fluentd、awslogs、splunk、etwlogs、gcplogs、
   none 共十余种,本书 19 章小结自己就列了六种。改为给出常见取值并链到官方清单。

4. 7.8 与 7.5 的示例注释建议 postgres:latest / redis:latest,而 4.1、7.10、
   7.16、7 章小结、4.5 全都要求避免 latest(7.8 同一文件第 177 行也写「避免
   latest」)。按全书口径改掉这两处。

另:ENV 的空格分隔旧写法(7.6 的「格式一」、附录四的 PG_MAJOR/PATH 示例)改为等号
形式并加注。BuildKit 的 LegacyKeyValueFormat 检查会报
"ENV key=value" should be used instead of legacy "ENV key value" format,
而本书 10.2 与 07 章 README 正是在推荐 docker buildx build --check。
2026-08-07 23:26:12 -07:00

254 lines
4.9 KiB
Go
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
## 7.6 ENV 设置环境变量
### 7.6.1 基本语法
```docker
## 格式一:单个变量
ENV <key>=<value>
## 格式二:多个变量
ENV <key1>=<value1> <key2>=<value2> ...
```
> 用空格分隔的旧写法 `ENV <key> <value>``ARG` 同理虽然仍能构建但已被官方弃用BuildKit
> [LegacyKeyValueFormat](https://docs.docker.com/reference/build-checks/legacy-key-value-format/) 构建检查会报
> `"ENV key=value" should be used instead of legacy "ENV key value" format``docker buildx build --check` [10.2](../10_buildx/10.2_buildx.md)会直接把它标出来一律用等号
---
### 7.6.2 基本用法
#### 设置单个变量
```docker
ENV NODE_VERSION=20
ENV APP_ENV=production
```
#### 设置多个变量
```docker
ENV NODE_VERSION=20 \
APP_ENV=production \
APP_NAME="My Application"
```
> 💡 包含空格的值用双引号括起来
---
### 7.6.3 环境变量的作用
#### 1. 后续指令中使用
```docker
ENV NODE_VERSION=20.10.0
## 在 RUN 中使用
RUN curl -fsSL https://nodejs.org/dist/v${NODE_VERSION}/node-v${NODE_VERSION}-linux-x64.tar.xz \
| tar -xJ -C /usr/local --strip-components=1
## 在 WORKDIR 中使用
ENV APP_HOME=/app
WORKDIR $APP_HOME
## 在 COPY 中使用
COPY . $APP_HOME
```
#### 2. 容器运行时使用
```docker
ENV DATABASE_URL=postgres://localhost/mydb
```
应用代码中可以读取
```python
import os
db_url = os.environ.get('DATABASE_URL')
```
```javascript
const dbUrl = process.env.DATABASE_URL;
```
---
### 7.6.4 支持环境变量的指令
以下指令可以使用 `$变量名` `${变量名}` 格式
| 指令 | 示例 |
|------|------|
| `RUN` | `RUN echo $VERSION` |
| `CMD` | `CMD ["sh", "-c", "echo $HOME"]` |
| `ENTRYPOINT` | 同上 |
| `COPY` | `COPY . $APP_HOME` |
| `ADD` | `ADD app.tar.gz $APP_HOME` |
| `WORKDIR` | `WORKDIR $APP_HOME` |
| `EXPOSE` | `EXPOSE $PORT` |
| `VOLUME` | `VOLUME $DATA_DIR` |
| `USER` | `USER $USERNAME` |
| `LABEL` | `LABEL version=$VERSION` |
| `FROM` | `FROM node:$NODE_VERSION` |
> **注意**`FROM` 是上表中的特例——它只能引用在第一条 `FROM` 之前用 `ARG` 声明的变量**无法**使用 `ENV` 定义的环境变量`ENV` 要进入构建阶段后才生效)。其余指令才能引用 `ENV` 环境变量
---
### 7.6.5 运行时覆盖
使用 `-e` `--env` 覆盖 Dockerfile 中定义的环境变量
```bash
## 覆盖单个变量
$ docker run -e APP_ENV=development myimage
## 覆盖多个变量
$ docker run -e APP_ENV=development -e DEBUG=true myimage
## 从环境变量文件读取
$ docker run --env-file .env myimage
```
#### .env 文件格式
```bash
## .env
APP_ENV=development
DEBUG=true
DATABASE_URL=postgres://localhost/mydb
```
---
### 7.6.6 ENV vs ARG
| 特性 | ENV | ARG |
|------|-----|-----|
| **生效时间** | 构建时 + 运行时 | 仅构建时 |
| **持久性** | 写入镜像运行时可用 | 构建后消失 |
| **覆盖方式** | `docker run -e` | `docker build --build-arg` |
| **适用场景** | 应用配置 | 构建参数 (如版本号)|
#### 组合使用
```docker
## ARG 接收构建时参数
ARG NODE_VERSION=20
## ENV 保存到运行时
ENV NODE_VERSION=$NODE_VERSION
## 后续指令使用
RUN curl -fsSL https://nodejs.org/dist/v${NODE_VERSION}/...
```
```bash
## 构建时指定版本
$ docker build --build-arg NODE_VERSION=18 -t myapp .
```
---
### 7.6.7 最佳实践
#### 1. 统一管理版本号
```docker
## ✅ 好:版本集中管理
ENV NGINX_VERSION=1.30 \
NODE_VERSION=22 \
PYTHON_VERSION=3.12
RUN apt-get install nginx=${NGINX_VERSION}
## ❌ 差:版本分散在各处
RUN apt-get install nginx=1.30
```
#### 2. 不要存储敏感信息
```docker
## ❌ 错误:密码写入镜像
ENV DB_PASSWORD=secret123
## ✅ 正确:运行时传入
## docker run -e DB_PASSWORD=xxx myimage
...
```
#### 3. 为应用提供合理默认值
```docker
ENV APP_ENV=production \
APP_PORT=8080 \
LOG_LEVEL=info
```
#### 4. 使用有意义的变量名
```docker
## ✅ 好:清晰的命名
ENV REDIS_HOST=localhost \
REDIS_PORT=6379
## ❌ 差:模糊的命名
ENV HOST=localhost \
PORT=6379
```
---
### 7.6.8 常见问题
#### Q环境变量在 CMD 中不展开
exec 格式不会自动执行 shell 展开因此命令参数里的 `$PORT` 会按字面值传给进程但环境变量本身仍会注入进程环境应用可以通过语言运行时读取
```docker
## ❌ 不会展开 $PORT
CMD ["python", "app.py", "--port", "$PORT"]
## ✅ 使用 shell 格式或显式调用 sh
CMD ["sh", "-c", "python app.py --port $PORT"]
```
#### Q如何查看容器的环境变量
```bash
$ docker inspect mycontainer --format '{{json .Config.Env}}'
$ docker exec mycontainer env
```
#### Q多行 ENV 还是多个 ENV
```docker
## ✅ 推荐:减少层数
ENV VAR1=value1 \
VAR2=value2 \
VAR3=value3
## ⚠️ 多个 ENV 会创建多层
ENV VAR1=value1
ENV VAR2=value2
ENV VAR3=value3
```
---