mirror of
https://github.com/yeasy/docker_practice.git
synced 2026-03-25 19:25:27 +00:00
Remove blank lines after code block markers
This commit is contained in:
@@ -5,7 +5,6 @@
|
||||
```docker
|
||||
WORKDIR <工作目录路径>
|
||||
```
|
||||
|
||||
`WORKDIR` 指定后续指令的工作目录。如果目录不存在,Docker 会自动创建。
|
||||
|
||||
---
|
||||
@@ -19,7 +18,6 @@ RUN pwd # 输出 /app
|
||||
RUN echo "hello" > world.txt # 创建 /app/world.txt
|
||||
COPY . . # 复制到 /app/
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 7.10.3 为什么需要 WORKDIR
|
||||
@@ -27,13 +25,11 @@ COPY . . # 复制到 /app/
|
||||
#### 常见错误
|
||||
|
||||
```docker
|
||||
|
||||
## ❌ 错误:cd 在下一个 RUN 中无效
|
||||
|
||||
RUN cd /app
|
||||
RUN echo "hello" > world.txt # 文件在根目录!
|
||||
```
|
||||
|
||||
#### 原因分析
|
||||
|
||||
```dockerfile
|
||||
@@ -46,19 +42,16 @@ RUN echo "hello" > world.txt
|
||||
↓
|
||||
启动新容器(工作目录在 /)→ 创建 /world.txt
|
||||
```
|
||||
|
||||
每个 RUN 都在新容器中执行,**前一个 RUN 的内存状态 (包括工作目录) 不会保留**。
|
||||
|
||||
#### 正确做法
|
||||
|
||||
```docker
|
||||
|
||||
## ✅ 正确:使用 WORKDIR
|
||||
|
||||
WORKDIR /app
|
||||
RUN echo "hello" > world.txt # 创建 /app/world.txt
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 7.10.4 相对路径
|
||||
@@ -72,7 +65,6 @@ WORKDIR c
|
||||
|
||||
RUN pwd # 输出 /a/b/c
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 7.10.5 使用环境变量
|
||||
@@ -83,13 +75,11 @@ WORKDIR $APP_HOME
|
||||
|
||||
RUN pwd # 输出 /app
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 7.10.6 多阶段构建中的 WORKDIR
|
||||
|
||||
```docker
|
||||
|
||||
## 构建阶段
|
||||
|
||||
FROM node:20 AS builder
|
||||
@@ -105,7 +95,6 @@ FROM nginx:alpine
|
||||
WORKDIR /usr/share/nginx/html
|
||||
COPY --from=builder /build/dist .
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 7.10.7 最佳实践
|
||||
@@ -121,11 +110,9 @@ RUN npm install
|
||||
COPY . .
|
||||
CMD ["node", "server.js"]
|
||||
```
|
||||
|
||||
#### 2. 使用绝对路径
|
||||
|
||||
```docker
|
||||
|
||||
## ✅ 推荐:绝对路径,意图明确
|
||||
|
||||
WORKDIR /app
|
||||
@@ -134,11 +121,9 @@ WORKDIR /app
|
||||
|
||||
WORKDIR app
|
||||
```
|
||||
|
||||
#### 3. 不要用 RUN cd
|
||||
|
||||
```docker
|
||||
|
||||
## ❌ 避免
|
||||
|
||||
RUN cd /app && echo "hello" > world.txt
|
||||
@@ -148,7 +133,6 @@ RUN cd /app && echo "hello" > world.txt
|
||||
WORKDIR /app
|
||||
RUN echo "hello" > world.txt
|
||||
```
|
||||
|
||||
#### 4. 适时重置 WORKDIR
|
||||
|
||||
```docker
|
||||
@@ -162,7 +146,6 @@ WORKDIR /data
|
||||
|
||||
...
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 7.10.8 与其他指令的关系
|
||||
@@ -182,7 +165,6 @@ RUN pwd # /app
|
||||
COPY . . # 复制到 /app
|
||||
CMD ["./start.sh"] # /app/start.sh
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 7.10.9 运行时覆盖
|
||||
@@ -193,5 +175,4 @@ CMD ["./start.sh"] # /app/start.sh
|
||||
$ docker run -w /tmp myimage pwd
|
||||
/tmp
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
@@ -6,7 +6,6 @@
|
||||
USER <用户名>[:<用户组>]
|
||||
USER <UID>[:<GID>]
|
||||
```
|
||||
|
||||
`USER` 指令切换后续指令 (RUN、CMD、ENTRYPOINT) 的执行用户。
|
||||
|
||||
---
|
||||
@@ -29,7 +28,6 @@ flowchart LR
|
||||
NR_C -- 权限受限,危害降低 --> NR_Safe["无法控制系统"]
|
||||
end
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 7.11.3 基本用法
|
||||
@@ -57,16 +55,13 @@ USER appuser
|
||||
|
||||
CMD ["node", "server.js"]
|
||||
```
|
||||
|
||||
#### 使用 UID/GID
|
||||
|
||||
```docker
|
||||
|
||||
## 也可以使用数字
|
||||
|
||||
USER 1001:1001
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 7.11.4 用户必须已存在
|
||||
@@ -74,7 +69,6 @@ USER 1001:1001
|
||||
`USER` 指令只能切换到 **已存在** 的用户:
|
||||
|
||||
```docker
|
||||
|
||||
## ❌ 错误:用户不存在
|
||||
|
||||
USER nonexistent
|
||||
@@ -86,7 +80,6 @@ USER nonexistent
|
||||
RUN useradd -r -s /bin/false appuser
|
||||
USER appuser
|
||||
```
|
||||
|
||||
#### 创建用户的方式
|
||||
|
||||
**Debian/Ubuntu**:
|
||||
@@ -95,14 +88,12 @@ USER appuser
|
||||
RUN groupadd -r appgroup && \
|
||||
useradd -r -g appgroup appuser
|
||||
```
|
||||
|
||||
**Alpine**:
|
||||
|
||||
```docker
|
||||
RUN addgroup -g 1001 -S appgroup && \
|
||||
adduser -u 1001 -S -G appgroup appuser
|
||||
```
|
||||
|
||||
| 选项 | 说明 |
|
||||
|------|------|
|
||||
| `-r` (useradd) / `-S` (adduser) | 创建系统用户 |
|
||||
@@ -134,7 +125,6 @@ COPY docker-entrypoint.sh /usr/local/bin/
|
||||
ENTRYPOINT ["docker-entrypoint.sh"]
|
||||
CMD ["redis-server"]
|
||||
```
|
||||
|
||||
**docker-entrypoint.sh**:
|
||||
|
||||
```bash
|
||||
@@ -149,7 +139,6 @@ chown -R redis:redis /data
|
||||
|
||||
exec gosu redis "$@"
|
||||
```
|
||||
|
||||
#### 为什么不用 su/sudo
|
||||
|
||||
| 问题 | su/sudo | gosu |
|
||||
@@ -166,7 +155,6 @@ exec gosu redis "$@"
|
||||
使用 `-u` 或 `--user` 参数:
|
||||
|
||||
```bash
|
||||
|
||||
## 以指定用户运行
|
||||
|
||||
$ docker run -u 1001:1001 myimage
|
||||
@@ -175,7 +163,6 @@ $ docker run -u 1001:1001 myimage
|
||||
|
||||
$ docker run -u root myimage
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 7.11.7 文件权限处理
|
||||
@@ -204,7 +191,6 @@ COPY --chown=appuser:appuser . .
|
||||
USER appuser
|
||||
CMD ["node", "server.js"]
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 7.11.8 最佳实践
|
||||
@@ -212,7 +198,6 @@ CMD ["node", "server.js"]
|
||||
#### 1. 始终使用非 root 用户
|
||||
|
||||
```docker
|
||||
|
||||
## ✅ 推荐
|
||||
|
||||
RUN adduser -D appuser
|
||||
@@ -223,24 +208,20 @@ CMD ["myapp"]
|
||||
|
||||
CMD ["myapp"] # 以 root 运行
|
||||
```
|
||||
|
||||
#### 2. 使用固定 UID/GID
|
||||
|
||||
便于在宿主机和容器间共享文件:
|
||||
|
||||
```docker
|
||||
|
||||
## 使用常见的非 root UID
|
||||
|
||||
RUN addgroup -g 1000 -S appgroup && \
|
||||
adduser -u 1000 -S -G appgroup appuser
|
||||
USER 1000:1000
|
||||
```
|
||||
|
||||
#### 3. 多阶段构建中的 USER
|
||||
|
||||
```docker
|
||||
|
||||
## 构建阶段可以用 root
|
||||
|
||||
FROM node:20 AS builder
|
||||
@@ -257,7 +238,6 @@ COPY --from=builder --chown=appuser:appuser /app/dist .
|
||||
USER appuser
|
||||
CMD ["node", "server.js"]
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 7.11.9 常见问题
|
||||
@@ -267,13 +247,11 @@ CMD ["node", "server.js"]
|
||||
```bash
|
||||
permission denied: '/app/data.log'
|
||||
```
|
||||
|
||||
**解决**:确保目录权限正确
|
||||
|
||||
```docker
|
||||
RUN mkdir -p /app/data && chown appuser:appuser /app/data
|
||||
```
|
||||
|
||||
#### Q:无法绑定低于 1024 的端口
|
||||
|
||||
非 root 用户无法绑定 80、443 等端口。
|
||||
|
||||
@@ -6,7 +6,6 @@
|
||||
HEALTHCHECK [选项] CMD <命令>
|
||||
HEALTHCHECK NONE
|
||||
```
|
||||
|
||||
`HEALTHCHECK` 指令告诉 Docker 如何判断容器状态是否正常。这是保障服务高可用的重要机制。
|
||||
|
||||
---
|
||||
@@ -28,7 +27,6 @@ Starting ──成功──> Healthy ──失败N次──> Unhealthy
|
||||
▲ │
|
||||
└──────成功──────┘
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 7.12.3 基本用法
|
||||
@@ -42,7 +40,6 @@ RUN apt-get update && apt-get install -y curl && rm -rf /var/lib/apt/lists/*
|
||||
HEALTHCHECK --interval=30s --timeout=3s --retries=3 \
|
||||
CMD curl -fs http://localhost/ || exit 1
|
||||
```
|
||||
|
||||
#### 命令返回值
|
||||
|
||||
- `0`:成功 (healthy)
|
||||
@@ -68,7 +65,6 @@ HEALTHCHECK --interval=30s --timeout=3s --retries=3 \
|
||||
FROM my-base-image
|
||||
HEALTHCHECK NONE
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 7.12.5 常见检查脚本
|
||||
@@ -78,7 +74,6 @@ HEALTHCHECK NONE
|
||||
使用 `curl` 或 `wget`:
|
||||
|
||||
```docker
|
||||
|
||||
## 使用 curl
|
||||
|
||||
HEALTHCHECK CMD curl -f http://localhost/ || exit 1
|
||||
@@ -87,11 +82,9 @@ HEALTHCHECK CMD curl -f http://localhost/ || exit 1
|
||||
|
||||
HEALTHCHECK CMD wget -q --spider http://localhost/ || exit 1
|
||||
```
|
||||
|
||||
#### 数据库
|
||||
|
||||
```docker
|
||||
|
||||
## MySQL
|
||||
|
||||
HEALTHCHECK CMD mysqladmin ping -h localhost || exit 1
|
||||
@@ -100,14 +93,12 @@ HEALTHCHECK CMD mysqladmin ping -h localhost || exit 1
|
||||
|
||||
HEALTHCHECK CMD redis-cli ping || exit 1
|
||||
```
|
||||
|
||||
#### 自定义脚本
|
||||
|
||||
```docker
|
||||
COPY healthcheck.sh /usr/local/bin/
|
||||
HEALTHCHECK CMD ["healthcheck.sh"]
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 7.12.6 在 Compose 中使用
|
||||
@@ -125,7 +116,6 @@ services:
|
||||
retries: 3
|
||||
start_period: 40s
|
||||
```
|
||||
|
||||
带健康检查的依赖启动:
|
||||
|
||||
```yaml
|
||||
@@ -139,13 +129,11 @@ services:
|
||||
healthcheck:
|
||||
test: ["CMD", "mysqladmin", "ping", "-h", "localhost"]
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 7.12.7 查看健康状态
|
||||
|
||||
```bash
|
||||
|
||||
## 查看容器状态(包含健康信息)
|
||||
|
||||
$ docker ps
|
||||
@@ -169,7 +157,6 @@ $ docker inspect --format '{{json .State.Health}}' mycontainer | jq
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 7.12.8 最佳实践
|
||||
@@ -187,12 +174,10 @@ $ docker inspect --format '{{json .State.Health}}' mycontainer | jq
|
||||
应用启动可能需要时间 (如 Java 应用)。设置 `--start-period` 可以防止在启动阶段因检查失败而误判。
|
||||
|
||||
```docker
|
||||
|
||||
## 给应用 1 分钟启动时间
|
||||
|
||||
HEALTHCHECK --start-period=60s CMD curl -f http://localhost/ || exit 1
|
||||
```
|
||||
|
||||
#### 4. 只检查核心依赖
|
||||
|
||||
健康检查应主要关注 **当前服务** 是否可用,而不是检查其下游依赖 (数据库等)。下游依赖的检查应由应用逻辑处理。
|
||||
|
||||
@@ -5,7 +5,6 @@
|
||||
```docker
|
||||
ONBUILD <其它指令>
|
||||
```
|
||||
|
||||
`ONBUILD` 是一个特殊的指令,它后面跟的是其它指令 (如 `RUN`,`COPY` 等),这些指令 **在当前镜像构建时不会执行**,只有当以当前镜像为基础镜像去构建下一级镜像时才会被执行。
|
||||
|
||||
---
|
||||
@@ -42,7 +41,6 @@ ONBUILD COPY . .
|
||||
|
||||
CMD ["npm", "start"]
|
||||
```
|
||||
|
||||
**子项目 Dockerfile**:
|
||||
|
||||
```docker
|
||||
@@ -54,7 +52,6 @@ FROM my-node-base
|
||||
|
||||
...
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 7.13.3 执行机制
|
||||
@@ -67,7 +64,6 @@ Dockerfile (含 ONBUILD) ──build──> 基础镜像 (记录了 ONBUILD 触
|
||||
子镜像构建:
|
||||
FROM 基础镜像 ──build──> 读取基础镜像触发器 ──> 执行触发器指令 ──> 继续执行子 Dockerfile
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 7.13.4 常见使用场景
|
||||
@@ -75,32 +71,26 @@ FROM 基础镜像 ──build──> 读取基础镜像触发器 ──> 执行
|
||||
#### 1. 自动处理依赖安装
|
||||
|
||||
```docker
|
||||
|
||||
## Python 基础镜像
|
||||
|
||||
ONBUILD COPY requirements.txt ./
|
||||
ONBUILD RUN pip install -r requirements.txt
|
||||
```
|
||||
|
||||
#### 2. 自动编译代码
|
||||
|
||||
```docker
|
||||
|
||||
## Go 基础镜像
|
||||
|
||||
ONBUILD COPY . .
|
||||
ONBUILD RUN go build -o app main.go
|
||||
```
|
||||
|
||||
#### 3. 处理静态资源
|
||||
|
||||
```docker
|
||||
|
||||
## Nginx 静态网站基础镜像
|
||||
|
||||
ONBUILD COPY dist/ /usr/share/nginx/html/
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 7.13.5 注意事项
|
||||
@@ -137,7 +127,6 @@ ONBUILD COPY dist/ /usr/share/nginx/html/
|
||||
node:20-onbuild
|
||||
python:3.12-onbuild
|
||||
```
|
||||
|
||||
#### 2. 避免执行耗时操作
|
||||
|
||||
尽量不要在 `ONBUILD` 中执行过于耗时或不确定的操作 (如更新系统软件),这会让子镜像构建变得缓慢且不可控。
|
||||
|
||||
@@ -5,7 +5,6 @@
|
||||
```docker
|
||||
LABEL <key>=<value> <key>=<value> ...
|
||||
```
|
||||
|
||||
`LABEL` 指令以键值对的形式给镜像添加元数据。这些数据不会影响镜像的功能,但可以帮助用户理解镜像,或被自动化工具使用。
|
||||
|
||||
---
|
||||
@@ -27,7 +26,6 @@ LABEL <key>=<value> <key>=<value> ...
|
||||
LABEL version="1.0"
|
||||
LABEL description="这是一个 Web 应用服务器"
|
||||
```
|
||||
|
||||
#### 定义多个标签:推荐
|
||||
|
||||
```docker
|
||||
@@ -36,7 +34,6 @@ LABEL maintainer="user@example.com" \
|
||||
description="My App Description" \
|
||||
org.opencontainers.image.authors="Yeasy"
|
||||
```
|
||||
|
||||
> 💡 包含空格的值需要用引号括起来。
|
||||
|
||||
---
|
||||
@@ -65,7 +62,6 @@ LABEL org.opencontainers.image.authors="yeasy" \
|
||||
org.opencontainers.image.source="https://github.com/yeasy/docker_practice" \
|
||||
org.opencontainers.image.licenses="MIT"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 7.14.5 MAINTAINER 指令:已废弃
|
||||
@@ -73,16 +69,13 @@ LABEL org.opencontainers.image.authors="yeasy" \
|
||||
旧版本的 Dockerfile 中常看到 `MAINTAINER` 指令:
|
||||
|
||||
```docker
|
||||
|
||||
## ❌ 已弃用
|
||||
|
||||
MAINTAINER user@example.com
|
||||
```
|
||||
|
||||
现在推荐使用 `LABEL`:
|
||||
|
||||
```docker
|
||||
|
||||
## ✅ 推荐
|
||||
|
||||
LABEL maintainer="user@example.com"
|
||||
@@ -91,7 +84,6 @@ LABEL maintainer="user@example.com"
|
||||
|
||||
LABEL org.opencontainers.image.authors="user@example.com"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 7.14.6 动态标签
|
||||
@@ -105,7 +97,6 @@ ARG VCS_REF
|
||||
LABEL org.opencontainers.image.created=$BUILD_DATE \
|
||||
org.opencontainers.image.revision=$VCS_REF
|
||||
```
|
||||
|
||||
构建命令:
|
||||
|
||||
```bash
|
||||
@@ -114,7 +105,6 @@ $ docker build \
|
||||
--build-arg VCS_REF=$(git rev-parse --short HEAD) \
|
||||
.
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 7.14.7 查看标签
|
||||
@@ -129,13 +119,11 @@ $ docker inspect nginx --format '{{json .Config.Labels}}' | jq
|
||||
"maintainer": "NGINX Docker Maintainers <docker-maint@nginx.com>"
|
||||
}
|
||||
```
|
||||
|
||||
#### 过滤器
|
||||
|
||||
可以使用标签过滤镜像:
|
||||
|
||||
```bash
|
||||
|
||||
## 列出作者是 yeasy 的所有镜像
|
||||
|
||||
$ docker images --filter "label=org.opencontainers.image.authors=yeasy"
|
||||
@@ -144,5 +132,4 @@ $ docker images --filter "label=org.opencontainers.image.authors=yeasy"
|
||||
|
||||
$ docker rmi $(docker images -q --filter "label=stage=builder")
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
@@ -5,7 +5,6 @@
|
||||
```docker
|
||||
SHELL ["executable", "parameters"]
|
||||
```
|
||||
|
||||
`SHELL` 指令允许覆盖 Docker 默认的 shell。
|
||||
|
||||
- **Linux 默认**:`["/bin/sh", "-c"]`
|
||||
@@ -32,22 +31,18 @@ SHELL ["/bin/bash", "-c"]
|
||||
|
||||
RUN echo {a..z}
|
||||
```
|
||||
|
||||
#### 2. 增强错误处理
|
||||
|
||||
默认情况下,管道命令 `cmd1 | cmd2` 只要 `cmd2` 成功,整个指令就视为成功。这可能掩盖构建错误。
|
||||
|
||||
```docker
|
||||
|
||||
## ❌ 这里的 wget 失败了,但构建继续(因为 tar 成功了)
|
||||
|
||||
RUN wget -O - https://invalid-url | tar xz
|
||||
```
|
||||
|
||||
使用 `SHELL` 启用 `pipefail`:
|
||||
|
||||
```docker
|
||||
|
||||
## ✅ 启用 pipefail
|
||||
|
||||
SHELL ["/bin/bash", "-o", "pipefail", "-c"]
|
||||
@@ -56,7 +51,6 @@ SHELL ["/bin/bash", "-o", "pipefail", "-c"]
|
||||
|
||||
RUN wget -O - https://invalid-url | tar xz
|
||||
```
|
||||
|
||||
#### 3. Windows 环境
|
||||
|
||||
在 Windows 容器中,经常需要在 `cmd` 和 `powershell` 之间切换。
|
||||
@@ -77,7 +71,6 @@ RUN Write-Host "Hello from PowerShell"
|
||||
|
||||
SHELL ["cmd", "/S", "/C"]
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 7.15.3 作用范围
|
||||
@@ -103,7 +96,6 @@ SHELL ["/bin/sh", "-c"]
|
||||
|
||||
RUN echo "Using sh again"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 7.15.4 对其他指令的影响
|
||||
@@ -130,7 +122,6 @@ RUN echo "Using sh again"
|
||||
```docker
|
||||
SHELL ["/bin/bash", "-o", "pipefail", "-c"]
|
||||
```
|
||||
|
||||
#### 2. 明确意图
|
||||
|
||||
如果由于脚本需求必须更改 shell,最好在 Dockerfile 中显式声明,而不是依赖默认行为。
|
||||
|
||||
@@ -21,7 +21,6 @@ func main(){
|
||||
fmt.Printf("Hello World!");
|
||||
}
|
||||
```
|
||||
|
||||
编写 `Dockerfile.one` 文件
|
||||
|
||||
```docker
|
||||
@@ -42,13 +41,11 @@ WORKDIR /root/
|
||||
|
||||
CMD ["./app"]
|
||||
```
|
||||
|
||||
构建镜像
|
||||
|
||||
```bash
|
||||
$ docker build -t go/helloworld:1 -f Dockerfile.one .
|
||||
```
|
||||
|
||||
### 7.17.2 分散到多个 Dockerfile
|
||||
|
||||
另一种方式,就是我们事先在一个 `Dockerfile` 将项目及其依赖库编译测试打包好后,再将其拷贝到运行环境中,这种方式需要我们编写两个 `Dockerfile` 和一些编译脚本才能将其两个阶段自动整合起来,这种方式虽然可以很好地规避第一种方式存在的风险,但明显部署过程较复杂。
|
||||
@@ -67,7 +64,6 @@ COPY app.go .
|
||||
RUN go get -d -v github.com/go-sql-driver/mysql \
|
||||
&& CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o app .
|
||||
```
|
||||
|
||||
编写 `Dockerfile.copy` 文件
|
||||
|
||||
```docker
|
||||
@@ -81,7 +77,6 @@ COPY app .
|
||||
|
||||
CMD ["./app"]
|
||||
```
|
||||
|
||||
新建 `build.sh`
|
||||
|
||||
```bash
|
||||
@@ -99,7 +94,6 @@ echo Building go/helloworld:2
|
||||
docker build --no-cache -t go/helloworld:2 . -f Dockerfile.copy
|
||||
rm ./app
|
||||
```
|
||||
|
||||
现在运行脚本即可构建镜像
|
||||
|
||||
```bash
|
||||
@@ -107,7 +101,6 @@ $ chmod +x build.sh
|
||||
|
||||
$ ./build.sh
|
||||
```
|
||||
|
||||
对比两种方式生成的镜像大小
|
||||
|
||||
```bash
|
||||
@@ -117,7 +110,6 @@ REPOSITORY TAG IMAGE ID CREATED SIZE
|
||||
go/helloworld 2 f7cf3465432c 22 seconds ago 6.47MB
|
||||
go/helloworld 1 f55d3e16affc 2 minutes ago 295MB
|
||||
```
|
||||
|
||||
### 7.17.3 使用多阶段构建
|
||||
|
||||
为解决以上问题,Docker v17.05 开始支持多阶段构建 (`multistage builds`)。使用多阶段构建我们就可以很容易解决前面提到的问题,并且只需要编写一个 `Dockerfile`:
|
||||
@@ -147,13 +139,11 @@ COPY --from=0 /go/src/github.com/go/helloworld/app .
|
||||
|
||||
CMD ["./app"]
|
||||
```
|
||||
|
||||
构建镜像
|
||||
|
||||
```bash
|
||||
$ docker build -t go/helloworld:3 .
|
||||
```
|
||||
|
||||
对比三个镜像大小
|
||||
|
||||
```bash
|
||||
@@ -164,7 +154,6 @@ go/helloworld 3 d6911ed9c846 7 seconds ago 6.47MB
|
||||
go/helloworld 2 f7cf3465432c 22 seconds ago 6.47MB
|
||||
go/helloworld 1 f55d3e16affc 2 minutes ago 295MB
|
||||
```
|
||||
|
||||
很明显使用多阶段构建的镜像体积小,同时也完美解决了上边提到的问题。
|
||||
|
||||
### 7.17.4 只构建某一阶段的镜像
|
||||
@@ -174,13 +163,11 @@ go/helloworld 1 f55d3e16affc 2 minutes ago 295MB
|
||||
```docker
|
||||
FROM golang:alpine as builder
|
||||
```
|
||||
|
||||
例如当我们只想构建 `builder` 阶段的镜像时,增加 `--target=builder` 参数即可
|
||||
|
||||
```bash
|
||||
$ docker build --target builder -t username/imagename:tag .
|
||||
```
|
||||
|
||||
### 7.17.5 构建时从其他镜像复制文件
|
||||
|
||||
上面例子中我们使用 `COPY --from=0 /go/src/github.com/go/helloworld/app .` 从上一阶段的镜像中复制文件,我们也可以复制任意镜像中的文件。
|
||||
|
||||
@@ -29,7 +29,6 @@ storage/
|
||||
|
||||
...
|
||||
```
|
||||
|
||||
在 `laravel.conf` 文件中写入 nginx 配置。
|
||||
|
||||
```nginx
|
||||
@@ -55,7 +54,6 @@ server {
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 7.18.2 前端构建
|
||||
|
||||
第一阶段进行前端构建。
|
||||
@@ -76,7 +74,6 @@ RUN set -x ; cd /app \
|
||||
&& mkdir -p public \
|
||||
&& npm run production
|
||||
```
|
||||
|
||||
### 7.18.3 安装 Composer 依赖
|
||||
|
||||
第二阶段安装 Composer 依赖。
|
||||
@@ -96,7 +93,6 @@ RUN set -x ; cd /app \
|
||||
--no-scripts \
|
||||
--prefer-dist
|
||||
```
|
||||
|
||||
### 7.18.4 整合以上阶段所生成的文件
|
||||
|
||||
第三阶段对以上阶段生成的文件进行整合。
|
||||
@@ -122,7 +118,6 @@ RUN set -x ; cd ${LARAVEL_PATH} \
|
||||
&& chmod -R 777 storage \
|
||||
&& php artisan package:discover
|
||||
```
|
||||
|
||||
### 7.18.5 最后一个阶段构建 NGINX 镜像
|
||||
|
||||
```docker
|
||||
@@ -133,7 +128,6 @@ ARG LARAVEL_PATH=/app/laravel
|
||||
COPY laravel.conf /etc/nginx/conf.d/
|
||||
COPY --from=laravel ${LARAVEL_PATH}/public ${LARAVEL_PATH}/public
|
||||
```
|
||||
|
||||
### 7.18.6 构建 Laravel 及 Nginx 镜像
|
||||
|
||||
使用 `docker build` 命令构建镜像。
|
||||
@@ -143,7 +137,6 @@ $ docker build -t my/laravel --target=laravel .
|
||||
|
||||
$ docker build -t my/nginx --target=nginx .
|
||||
```
|
||||
|
||||
### 7.18.7 启动容器并测试
|
||||
|
||||
新建 Docker 网络
|
||||
@@ -151,19 +144,16 @@ $ docker build -t my/nginx --target=nginx .
|
||||
```bash
|
||||
$ docker network create laravel
|
||||
```
|
||||
|
||||
启动 laravel 容器,`--name=laravel` 参数设定的名字必须与 `nginx` 配置文件中的 `fastcgi_pass laravel:9000;` 一致
|
||||
|
||||
```bash
|
||||
$ docker run -dit --rm --name=laravel --network=laravel my/laravel
|
||||
```
|
||||
|
||||
启动 nginx 容器
|
||||
|
||||
```bash
|
||||
$ docker run -dit --rm --network=laravel -p 8080:80 my/nginx
|
||||
```
|
||||
|
||||
浏览器访问 `127.0.0.1:8080` 可以看到 Laravel 项目首页。
|
||||
|
||||
> 也许 Laravel 项目依赖其他外部服务,例如 redis、MySQL,请自行启动这些服务之后再进行测试,本小节不再赘述。
|
||||
|
||||
@@ -6,7 +6,6 @@
|
||||
RUN <command>
|
||||
RUN ["executable", "param1", "param2"]
|
||||
```
|
||||
|
||||
`RUN` 指令是 Dockerfile 中最常用的指令之一。它在 **当前镜像层** 之上创建一个新层,执行指定的命令,并提交结果。
|
||||
|
||||
---
|
||||
@@ -18,7 +17,6 @@ RUN ["executable", "param1", "param2"]
|
||||
```docker
|
||||
RUN apt-get update
|
||||
```
|
||||
|
||||
- **特点**:默认通过 `/bin/sh -c` 执行。
|
||||
- **优势**:可以使用环境变量、管道、重定向等 Shell 特性。
|
||||
- **示例**:
|
||||
@@ -31,7 +29,6 @@ RUN apt-get update
|
||||
```docker
|
||||
RUN ["apt-get", "update"]
|
||||
```
|
||||
|
||||
- **特点**:直接调用可执行文件,不经过 Shell。
|
||||
- **优势**:避免 Shell 字符串解析问题,适用于参数中包含特殊字符的情况。
|
||||
- **注意**:无法使用 `$VAR` 环境变量替换 (除非显式调用 shell)。
|
||||
@@ -51,7 +48,6 @@ RUN apt-get update
|
||||
RUN apt-get install -y nginx
|
||||
RUN rm -rf /var/lib/apt/lists/*
|
||||
```
|
||||
|
||||
**✅ 推荐写法** (创建 1 层):
|
||||
|
||||
```docker
|
||||
@@ -59,7 +55,6 @@ RUN apt-get update && \
|
||||
apt-get install -y nginx && \
|
||||
rm -rf /var/lib/apt/lists/*
|
||||
```
|
||||
|
||||
#### 2. 清理缓存
|
||||
|
||||
在安装完软件后,立即清除缓存,可以显著减小镜像体积。
|
||||
@@ -82,19 +77,16 @@ RUN apt-get update && \
|
||||
**❌ 隐蔽的错误**:
|
||||
|
||||
```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 常见问题
|
||||
@@ -105,28 +97,24 @@ RUN wget http://url | gzip -d > file
|
||||
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 高级技巧
|
||||
@@ -136,22 +124,18 @@ RUN echo $MY_VAR
|
||||
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,而不将其记录在镜像中。
|
||||
@@ -160,5 +144,4 @@ RUN --mount=type=cache,target=/go/pkg/mod \
|
||||
RUN --mount=type=secret,id=mysecret \
|
||||
cat /run/secrets/mysecret
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
@@ -6,7 +6,6 @@
|
||||
COPY [选项] <源路径>... <目标路径>
|
||||
COPY [选项] ["<源路径1>", "<源路径2>", ... "<目标路径>"]
|
||||
```
|
||||
|
||||
`COPY` 指令将构建上下文中的文件或目录复制到镜像内。
|
||||
|
||||
---
|
||||
@@ -16,7 +15,6 @@ COPY [选项] ["<源路径1>", "<源路径2>", ... "<目标路径>"]
|
||||
#### 复制单个文件
|
||||
|
||||
```docker
|
||||
|
||||
## 复制文件到指定目录
|
||||
|
||||
COPY package.json /app/
|
||||
@@ -25,11 +23,9 @@ COPY package.json /app/
|
||||
|
||||
COPY config.json /app/settings.json
|
||||
```
|
||||
|
||||
#### 复制多个文件
|
||||
|
||||
```docker
|
||||
|
||||
## 复制多个指定文件
|
||||
|
||||
COPY package.json package-lock.json /app/
|
||||
@@ -39,16 +35,13 @@ COPY package.json package-lock.json /app/
|
||||
COPY *.json /app/
|
||||
COPY src/*.js /app/src/
|
||||
```
|
||||
|
||||
#### 复制目录
|
||||
|
||||
```docker
|
||||
|
||||
## 复制整个目录的内容(不是目录本身)
|
||||
|
||||
COPY src/ /app/src/
|
||||
```
|
||||
|
||||
> ⚠️ **注意**:复制目录时,复制的是目录的 **内容**,不包含目录本身。
|
||||
|
||||
```bash
|
||||
@@ -57,7 +50,6 @@ src/ /app/src/
|
||||
├── index.js → ├── index.js
|
||||
└── utils.js └── utils.js
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 7.2.3 通配符规则
|
||||
@@ -76,7 +68,6 @@ COPY hom* /mydir/ # home.txt, homework.md 等
|
||||
COPY hom?.txt /mydir/ # home.txt, homy.txt 等
|
||||
COPY app[0-9].js /app/ # app0.js ~ app9.js
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 7.2.4 目标路径
|
||||
@@ -86,7 +77,6 @@ COPY app[0-9].js /app/ # app0.js ~ app9.js
|
||||
```docker
|
||||
COPY app.js /usr/src/app/
|
||||
```
|
||||
|
||||
#### 相对路径:基于 WORKDIR
|
||||
|
||||
```docker
|
||||
@@ -94,18 +84,15 @@ WORKDIR /app
|
||||
COPY package.json ./ # 复制到 /app/package.json
|
||||
COPY src/ ./src/ # 复制到 /app/src/
|
||||
```
|
||||
|
||||
#### 自动创建目录
|
||||
|
||||
如果目标目录不存在,Docker 会自动创建:
|
||||
|
||||
```docker
|
||||
|
||||
## /app/config/ 不存在也会自动创建
|
||||
|
||||
COPY settings.json /app/config/
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 7.2.5 修改文件所有者
|
||||
@@ -113,7 +100,6 @@ COPY settings.json /app/config/
|
||||
使用 `--chown` 选项设置文件的用户和组:
|
||||
|
||||
```docker
|
||||
|
||||
## 使用用户名和组名
|
||||
|
||||
COPY --chown=node:node package.json /app/
|
||||
@@ -126,7 +112,6 @@ COPY --chown=1000:1000 . /app/
|
||||
|
||||
COPY --chown=node . /app/
|
||||
```
|
||||
|
||||
> 💡 结合 `USER` 指令使用,确保应用以非 root 用户运行。
|
||||
|
||||
---
|
||||
@@ -141,12 +126,10 @@ COPY 会保留源文件的元数据:
|
||||
这对于脚本文件特别重要:
|
||||
|
||||
```docker
|
||||
|
||||
## start.sh 的可执行权限会被保留
|
||||
|
||||
COPY start.sh /app/
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 7.2.7 COPY vs ADD
|
||||
@@ -159,7 +142,6 @@ COPY start.sh /app/
|
||||
| 推荐程度 | ✅ **推荐** | ⚠️ 特殊场景使用 |
|
||||
|
||||
```docker
|
||||
|
||||
## 推荐:使用 COPY
|
||||
|
||||
COPY app.tar.gz /app/
|
||||
@@ -169,7 +151,6 @@ RUN tar -xzf /app/app.tar.gz
|
||||
|
||||
ADD app.tar.gz /app/
|
||||
```
|
||||
|
||||
> 笔者建议:除非需要自动解压 tar 文件,否则始终使用 COPY。明确的行为比隐式的魔法更好。
|
||||
|
||||
---
|
||||
@@ -179,7 +160,6 @@ ADD app.tar.gz /app/
|
||||
#### 从其他构建阶段复制
|
||||
|
||||
```docker
|
||||
|
||||
## 构建阶段
|
||||
|
||||
FROM node:20 AS builder
|
||||
@@ -194,16 +174,13 @@ RUN npm run build
|
||||
FROM nginx:alpine
|
||||
COPY --from=builder /app/dist /usr/share/nginx/html
|
||||
```
|
||||
|
||||
#### 使用 --link 优化缓存
|
||||
|
||||
```docker
|
||||
|
||||
## 使用 --link 后,文件以独立层添加,不依赖前序指令
|
||||
|
||||
COPY --link --from=builder /app/dist /usr/share/nginx/html
|
||||
```
|
||||
|
||||
`--link` 的优势:
|
||||
|
||||
- 更高效利用构建缓存
|
||||
@@ -217,7 +194,6 @@ COPY --link --from=builder /app/dist /usr/share/nginx/html
|
||||
使用 `.dockerignore` 排除不需要复制的文件:
|
||||
|
||||
```text
|
||||
|
||||
## .dockerignore
|
||||
|
||||
node_modules
|
||||
@@ -227,7 +203,6 @@ node_modules
|
||||
Dockerfile
|
||||
.dockerignore
|
||||
```
|
||||
|
||||
这可以:
|
||||
|
||||
- 减小构建上下文大小
|
||||
@@ -241,7 +216,6 @@ Dockerfile
|
||||
#### 1. 利用缓存,先复制依赖文件
|
||||
|
||||
```docker
|
||||
|
||||
## ✅ 好:先复制依赖定义,再安装,最后复制代码
|
||||
|
||||
COPY package.json package-lock.json ./
|
||||
@@ -253,11 +227,9 @@ COPY . .
|
||||
COPY . .
|
||||
RUN npm install
|
||||
```
|
||||
|
||||
#### 2. 使用 .dockerignore
|
||||
|
||||
```docker
|
||||
|
||||
## 确保 node_modules 不被复制
|
||||
|
||||
COPY . .
|
||||
@@ -266,11 +238,9 @@ COPY . .
|
||||
|
||||
...
|
||||
```
|
||||
|
||||
#### 3. 明确复制路径
|
||||
|
||||
```docker
|
||||
|
||||
## ✅ 好:明确的路径
|
||||
|
||||
COPY src/ /app/src/
|
||||
@@ -280,7 +250,6 @@ COPY package.json /app/
|
||||
|
||||
COPY . .
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
> **🔥 踩坑实录**
|
||||
|
||||
@@ -6,7 +6,6 @@
|
||||
ADD [选项] <源路径>... <目标路径>
|
||||
ADD [选项] ["<源路径>", ... "<目标路径>"]
|
||||
```
|
||||
|
||||
`ADD` 在 `COPY` 基础上增加了两个功能:
|
||||
|
||||
1. 自动解压 tar 压缩包
|
||||
@@ -33,12 +32,10 @@ ADD [选项] ["<源路径>", ... "<目标路径>"]
|
||||
#### 基本用法:自动解压本地 tar
|
||||
|
||||
```docker
|
||||
|
||||
## 自动解压 tar.gz 到目标目录
|
||||
|
||||
ADD app.tar.gz /app/
|
||||
```
|
||||
|
||||
ADD 会识别并解压以下格式:
|
||||
|
||||
- `.tar`
|
||||
@@ -54,7 +51,6 @@ ADD 会识别并解压以下格式:
|
||||
FROM scratch
|
||||
ADD ubuntu-noble-core-cloudimg-amd64-root.tar.gz /
|
||||
```
|
||||
|
||||
#### 解压过程
|
||||
|
||||
```bash
|
||||
@@ -69,7 +65,6 @@ app.tar.gz 包含: /app/ 目录结果:
|
||||
│ └── main.py │ └── main.py
|
||||
└── config.json └── config.json
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 7.3.4 URL 下载功能:不推荐
|
||||
@@ -77,12 +72,10 @@ app.tar.gz 包含: /app/ 目录结果:
|
||||
#### 基本用法
|
||||
|
||||
```docker
|
||||
|
||||
## 从 URL 下载文件
|
||||
|
||||
ADD https://example.com/app.zip /app/app.zip
|
||||
```
|
||||
|
||||
#### 为什么不推荐
|
||||
|
||||
| 问题 | 说明 |
|
||||
@@ -95,7 +88,6 @@ ADD https://example.com/app.zip /app/app.zip
|
||||
#### 推荐替代方案
|
||||
|
||||
```docker
|
||||
|
||||
## ❌ 不推荐:使用 ADD 下载
|
||||
|
||||
ADD https://example.com/app.tar.gz /tmp/
|
||||
@@ -105,7 +97,6 @@ RUN tar -xzf /tmp/app.tar.gz -C /app && rm /tmp/app.tar.gz
|
||||
|
||||
RUN curl -fsSL https://example.com/app.tar.gz | tar -xz -C /app
|
||||
```
|
||||
|
||||
优势:
|
||||
|
||||
- 一条 RUN 完成下载、解压、清理
|
||||
@@ -120,7 +111,6 @@ RUN curl -fsSL https://example.com/app.tar.gz | tar -xz -C /app
|
||||
ADD --chown=node:node app.tar.gz /app/
|
||||
ADD --chown=1000:1000 files/ /app/
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 7.3.6 何时使用 ADD
|
||||
@@ -128,7 +118,6 @@ ADD --chown=1000:1000 files/ /app/
|
||||
#### ✅ 适合使用 ADD
|
||||
|
||||
```docker
|
||||
|
||||
## 解压本地 tar 文件
|
||||
|
||||
FROM scratch
|
||||
@@ -138,11 +127,9 @@ ADD rootfs.tar.gz /
|
||||
|
||||
ADD dist.tar.gz /app/
|
||||
```
|
||||
|
||||
#### ❌ 不适合使用 ADD
|
||||
|
||||
```docker
|
||||
|
||||
## 复制普通文件(用 COPY)
|
||||
|
||||
ADD package.json /app/ # ❌
|
||||
@@ -158,7 +145,6 @@ RUN curl -fsSL ... -o /file # ✅
|
||||
ADD archive.tar.gz /archives/ # ❌ 会解压
|
||||
COPY archive.tar.gz /archives/ # ✅ 保持原样
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 7.3.7 缓存行为
|
||||
@@ -166,17 +152,14 @@ COPY archive.tar.gz /archives/ # ✅ 保持原样
|
||||
ADD 可能导致构建缓存失效:
|
||||
|
||||
```docker
|
||||
|
||||
## 如果 app.tar.gz 内容变化,此层及后续层都需重建
|
||||
|
||||
ADD app.tar.gz /app/
|
||||
RUN npm install
|
||||
```
|
||||
|
||||
**优化建议**:
|
||||
|
||||
```docker
|
||||
|
||||
## 先复制依赖文件
|
||||
|
||||
COPY package*.json /app/
|
||||
@@ -186,7 +169,6 @@ RUN npm install
|
||||
|
||||
ADD app.tar.gz /app/
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 7.3.8 最佳实践
|
||||
@@ -194,25 +176,20 @@ ADD app.tar.gz /app/
|
||||
#### 1. 默认使用 COPY
|
||||
|
||||
```docker
|
||||
|
||||
## ✅ 大多数场景使用 COPY
|
||||
|
||||
COPY . /app/
|
||||
```
|
||||
|
||||
#### 2. 仅在需要解压时使用 ADD
|
||||
|
||||
```docker
|
||||
|
||||
## ✅ 自动解压场景
|
||||
|
||||
ADD app.tar.gz /app/
|
||||
```
|
||||
|
||||
#### 3. 不要用 ADD 下载文件
|
||||
|
||||
```docker
|
||||
|
||||
## ❌ 避免
|
||||
|
||||
ADD https://example.com/file.tar.gz /tmp/
|
||||
@@ -221,16 +198,13 @@ ADD https://example.com/file.tar.gz /tmp/
|
||||
|
||||
RUN curl -fsSL https://example.com/file.tar.gz | tar -xz -C /app
|
||||
```
|
||||
|
||||
#### 4. 解压后清理
|
||||
|
||||
```docker
|
||||
|
||||
## 如果需要控制解压过程
|
||||
|
||||
COPY app.tar.gz /tmp/
|
||||
RUN tar -xzf /tmp/app.tar.gz -C /app && \
|
||||
rm /tmp/app.tar.gz
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
@@ -25,7 +25,6 @@ CMD ["nginx", "-g", "daemon off;"]
|
||||
CMD ["python", "app.py"]
|
||||
CMD ["node", "server.js"]
|
||||
```
|
||||
|
||||
**优点**:
|
||||
|
||||
- 直接执行指定程序,是容器的 PID 1
|
||||
@@ -38,11 +37,9 @@ CMD ["node", "server.js"]
|
||||
CMD echo "Hello World"
|
||||
CMD nginx -g "daemon off;"
|
||||
```
|
||||
|
||||
**实际执行**:会被包装为 `sh -c`
|
||||
|
||||
```docker
|
||||
|
||||
## 你写的
|
||||
|
||||
CMD echo $HOME
|
||||
@@ -51,7 +48,6 @@ CMD echo $HOME
|
||||
|
||||
CMD ["sh", "-c", "echo $HOME"]
|
||||
```
|
||||
|
||||
**优点**:可以使用环境变量、管道等 shell 特性 **缺点**:主进程是 sh,信号无法正确传递给应用
|
||||
|
||||
---
|
||||
@@ -68,7 +64,6 @@ CMD ["sh", "-c", "echo $HOME"]
|
||||
#### 信号传递问题示例
|
||||
|
||||
```docker
|
||||
|
||||
## ❌ shell 格式:docker stop 会超时
|
||||
|
||||
CMD node server.js
|
||||
@@ -85,7 +80,6 @@ CMD ["node", "server.js"]
|
||||
|
||||
...
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 7.4.4 运行时覆盖 CMD
|
||||
@@ -93,13 +87,11 @@ CMD ["node", "server.js"]
|
||||
`docker run` 后的命令会覆盖 Dockerfile 中的 CMD:
|
||||
|
||||
```bash
|
||||
|
||||
## ubuntu 默认 CMD 是 /bin/bash
|
||||
|
||||
$ docker run -it ubuntu # 进入 bash
|
||||
$ docker run ubuntu cat /etc/os-release # 覆盖为 cat 命令
|
||||
```
|
||||
|
||||
```bash
|
||||
Dockerfile: docker run 命令:
|
||||
CMD ["/bin/bash"] + cat /etc/os-release
|
||||
@@ -108,7 +100,6 @@ CMD ["/bin/bash"] + cat /etc/os-release
|
||||
↓
|
||||
执行: cat /etc/os-release
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 7.4.5 经典错误:容器立即退出
|
||||
@@ -116,12 +107,10 @@ CMD ["/bin/bash"] + cat /etc/os-release
|
||||
#### 错误示例
|
||||
|
||||
```docker
|
||||
|
||||
## ❌ 容器启动后立即退出
|
||||
|
||||
CMD service nginx start
|
||||
```
|
||||
|
||||
#### 原因分析
|
||||
|
||||
```bash
|
||||
@@ -137,16 +126,13 @@ CMD service nginx start
|
||||
↓
|
||||
6. 容器主进程(sh)退出 → 容器停止
|
||||
```
|
||||
|
||||
#### 正确做法
|
||||
|
||||
```docker
|
||||
|
||||
## ✅ 让 nginx 在前台运行
|
||||
|
||||
CMD ["nginx", "-g", "daemon off;"]
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 7.4.6 CMD vs ENTRYPOINT
|
||||
@@ -159,32 +145,26 @@ CMD ["nginx", "-g", "daemon off;"]
|
||||
#### 单独使用 CMD
|
||||
|
||||
```docker
|
||||
|
||||
## Dockerfile
|
||||
|
||||
CMD ["curl", "-s", "http://example.com"]
|
||||
```
|
||||
|
||||
```bash
|
||||
$ docker run myimage # 执行默认命令
|
||||
$ docker run myimage curl -v ... # 完全覆盖
|
||||
```
|
||||
|
||||
#### 搭配 ENTRYPOINT
|
||||
|
||||
```docker
|
||||
|
||||
## Dockerfile
|
||||
|
||||
ENTRYPOINT ["curl", "-s"]
|
||||
CMD ["http://example.com"]
|
||||
```
|
||||
|
||||
```bash
|
||||
$ docker run myimage # curl -s http://example.com
|
||||
$ docker run myimage http://other.com # curl -s http://other.com(参数覆盖)
|
||||
```
|
||||
|
||||
详见 [ENTRYPOINT 入口点](7.5_entrypoint.md)章节。
|
||||
|
||||
---
|
||||
@@ -194,7 +174,6 @@ $ docker run myimage http://other.com # curl -s http://other.com(参数覆盖
|
||||
#### 1. 优先使用 exec 格式
|
||||
|
||||
```docker
|
||||
|
||||
## ✅ 推荐
|
||||
|
||||
CMD ["python", "app.py"]
|
||||
@@ -203,11 +182,9 @@ CMD ["python", "app.py"]
|
||||
|
||||
CMD ["sh", "-c", "echo $PATH && python app.py"]
|
||||
```
|
||||
|
||||
#### 2. 确保应用在前台运行
|
||||
|
||||
```docker
|
||||
|
||||
## ✅ 前台运行
|
||||
|
||||
CMD ["nginx", "-g", "daemon off;"]
|
||||
@@ -219,11 +196,9 @@ CMD ["java", "-jar", "app.jar"]
|
||||
CMD service nginx start
|
||||
CMD systemctl start nginx
|
||||
```
|
||||
|
||||
#### 3. 使用双引号
|
||||
|
||||
```docker
|
||||
|
||||
## ✅ 正确:双引号
|
||||
|
||||
CMD ["node", "server.js"]
|
||||
@@ -232,11 +207,9 @@ CMD ["node", "server.js"]
|
||||
|
||||
CMD ['node', 'server.js']
|
||||
```
|
||||
|
||||
#### 4. 配合 ENTRYPOINT 使用
|
||||
|
||||
```docker
|
||||
|
||||
## 用于可配置参数的场景
|
||||
|
||||
ENTRYPOINT ["python", "app.py"]
|
||||
@@ -248,7 +221,6 @@ $ docker run myapp --port 9000
|
||||
|
||||
...
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 7.4.8 常见问题
|
||||
@@ -261,11 +233,9 @@ $ docker run myapp --port 9000
|
||||
CMD ["echo", "first"]
|
||||
CMD ["echo", "second"] # 只有这个生效
|
||||
```
|
||||
|
||||
#### Q:如何在 CMD 中使用环境变量?
|
||||
|
||||
```docker
|
||||
|
||||
## 方法1:使用 shell 格式
|
||||
|
||||
CMD echo "Port is $PORT"
|
||||
@@ -274,13 +244,11 @@ CMD echo "Port is $PORT"
|
||||
|
||||
CMD ["sh", "-c", "echo Port is $PORT"]
|
||||
```
|
||||
|
||||
#### Q:为什么我的容器不响应 Ctrl+C?
|
||||
|
||||
可能是使用了 shell 格式,信号被 sh 吃掉了:
|
||||
|
||||
```docker
|
||||
|
||||
## ❌ 信号无法传递
|
||||
|
||||
CMD python app.py
|
||||
@@ -289,5 +257,4 @@ CMD python app.py
|
||||
|
||||
CMD ["python", "app.py"]
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
@@ -16,7 +16,6 @@
|
||||
| **shell 格式** | `ENTRYPOINT 命令 参数` | ⚠️ 不推荐 |
|
||||
|
||||
```docker
|
||||
|
||||
## exec 格式(推荐)
|
||||
|
||||
ENTRYPOINT ["nginx", "-g", "daemon off;"]
|
||||
@@ -25,7 +24,6 @@ ENTRYPOINT ["nginx", "-g", "daemon off;"]
|
||||
|
||||
ENTRYPOINT nginx -g "daemon off;"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 7.5.3 ENTRYPOINT vs CMD
|
||||
@@ -42,44 +40,35 @@ ENTRYPOINT nginx -g "daemon off;"
|
||||
#### 行为对比
|
||||
|
||||
```docker
|
||||
|
||||
## 只用 CMD
|
||||
|
||||
CMD ["curl", "-s", "http://example.com"]
|
||||
```
|
||||
|
||||
```bash
|
||||
$ docker run myimage # curl -s http://example.com
|
||||
$ docker run myimage -v # 执行 -v(错误!)
|
||||
$ docker run myimage curl -v ... # curl -v ...(完全替换)
|
||||
```
|
||||
|
||||
```docker
|
||||
|
||||
## 只用 ENTRYPOINT
|
||||
|
||||
ENTRYPOINT ["curl", "-s"]
|
||||
```
|
||||
|
||||
```bash
|
||||
$ docker run myimage # curl -s(缺参数)
|
||||
$ docker run myimage http://example.com # curl -s http://example.com ✓
|
||||
```
|
||||
|
||||
```docker
|
||||
|
||||
## ENTRYPOINT + CMD 组合(推荐)
|
||||
|
||||
ENTRYPOINT ["curl", "-s"]
|
||||
CMD ["http://example.com"]
|
||||
```
|
||||
|
||||
```bash
|
||||
$ docker run myimage # curl -s http://example.com(默认)
|
||||
$ docker run myimage http://other.com # curl -s http://other.com ✓
|
||||
$ docker run myimage -v http://other.com # curl -s -v http://other.com ✓
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 7.5.4 场景一:让镜像像命令一样使用
|
||||
@@ -95,7 +84,6 @@ FROM ubuntu:24.04
|
||||
RUN apt-get update && apt-get install -y curl && rm -rf /var/lib/apt/lists/*
|
||||
CMD ["curl", "-s", "http://myip.ipip.net"]
|
||||
```
|
||||
|
||||
```bash
|
||||
$ docker run myip # ✓ 正常工作
|
||||
当前 IP:61.148.226.66
|
||||
@@ -107,7 +95,6 @@ exec: "-i": executable file not found
|
||||
|
||||
...
|
||||
```
|
||||
|
||||
#### 使用 ENTRYPOINT 解决
|
||||
|
||||
```docker
|
||||
@@ -115,7 +102,6 @@ FROM ubuntu:24.04
|
||||
RUN apt-get update && apt-get install -y curl && rm -rf /var/lib/apt/lists/*
|
||||
ENTRYPOINT ["curl", "-s", "http://myip.ipip.net"]
|
||||
```
|
||||
|
||||
```bash
|
||||
$ docker run myip # ✓ 正常工作
|
||||
当前 IP:61.148.226.66
|
||||
@@ -125,7 +111,6 @@ HTTP/1.1 200 OK
|
||||
...
|
||||
当前 IP:61.148.226.66
|
||||
```
|
||||
|
||||
#### 交互图示
|
||||
|
||||
```bash
|
||||
@@ -138,7 +123,6 @@ curl -s http://myip.ipip.net -i
|
||||
└─────────────────────────────┘
|
||||
ENTRYPOINT + docker run 参数
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 7.5.5 场景二:启动前的准备工作
|
||||
@@ -155,7 +139,6 @@ COPY docker-entrypoint.sh /usr/local/bin/
|
||||
ENTRYPOINT ["docker-entrypoint.sh"]
|
||||
CMD ["redis-server"]
|
||||
```
|
||||
|
||||
**docker-entrypoint.sh**:
|
||||
|
||||
```bash
|
||||
@@ -177,7 +160,6 @@ fi
|
||||
|
||||
exec "$@"
|
||||
```
|
||||
|
||||
#### 工作流程
|
||||
|
||||
```bash
|
||||
@@ -191,7 +173,6 @@ docker-entrypoint.sh redis-server docker-entrypoint.sh bash
|
||||
└─ exec gosu redis redis-server └─ exec bash
|
||||
(以 redis 用户运行) (以 root 用户运行)
|
||||
```
|
||||
|
||||
#### 关键点
|
||||
|
||||
1. **exec “$@”**:用传入的参数替换当前进程,确保信号正确传递
|
||||
@@ -211,9 +192,7 @@ RUN pip install -r requirements.txt
|
||||
ENTRYPOINT ["python", "app.py"]
|
||||
CMD ["--host", "0.0.0.0", "--port", "8080"]
|
||||
```
|
||||
|
||||
```bash
|
||||
|
||||
## 使用默认参数
|
||||
|
||||
$ docker run myapp
|
||||
@@ -234,7 +213,6 @@ $ docker run myapp --help
|
||||
|
||||
...
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 7.5.7 覆盖 ENTRYPOINT
|
||||
@@ -242,7 +220,6 @@ $ docker run myapp --help
|
||||
使用 `--entrypoint` 参数覆盖:
|
||||
|
||||
```bash
|
||||
|
||||
## 正常运行
|
||||
|
||||
$ docker run myimage
|
||||
@@ -255,7 +232,6 @@ $ docker run --entrypoint /bin/sh myimage
|
||||
|
||||
$ docker run --entrypoint /bin/cat myimage /etc/os-release
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 7.5.8 ENTRYPOINT 与 CMD 组合表
|
||||
@@ -277,7 +253,6 @@ $ docker run --entrypoint /bin/cat myimage /etc/os-release
|
||||
#### 1. 使用 exec 格式
|
||||
|
||||
```docker
|
||||
|
||||
## ✅ 推荐
|
||||
|
||||
ENTRYPOINT ["python", "app.py"]
|
||||
@@ -286,14 +261,12 @@ ENTRYPOINT ["python", "app.py"]
|
||||
|
||||
ENTRYPOINT python app.py
|
||||
```
|
||||
|
||||
#### 2. 提供有意义的默认参数
|
||||
|
||||
```docker
|
||||
ENTRYPOINT ["nginx"]
|
||||
CMD ["-g", "daemon off;"]
|
||||
```
|
||||
|
||||
#### 3. 入口脚本使用 exec
|
||||
|
||||
```bash
|
||||
@@ -305,7 +278,6 @@ CMD ["-g", "daemon off;"]
|
||||
|
||||
exec "$@"
|
||||
```
|
||||
|
||||
#### 4. 处理信号
|
||||
|
||||
确保 ENTRYPOINT 脚本能正确传递信号:
|
||||
@@ -323,5 +295,4 @@ PID=$!
|
||||
|
||||
wait $PID
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
### 7.6.1 基本语法
|
||||
|
||||
```docker
|
||||
|
||||
## 格式一:单个变量
|
||||
|
||||
ENV <key> <value>
|
||||
@@ -12,7 +11,6 @@ ENV <key> <value>
|
||||
|
||||
ENV <key1>=<value1> <key2>=<value2> ...
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 7.6.2 基本用法
|
||||
@@ -23,7 +21,6 @@ ENV <key1>=<value1> <key2>=<value2> ...
|
||||
ENV NODE_VERSION 20.10.0
|
||||
ENV APP_ENV production
|
||||
```
|
||||
|
||||
#### 设置多个变量
|
||||
|
||||
```docker
|
||||
@@ -31,7 +28,6 @@ ENV NODE_VERSION=20.10.0 \
|
||||
APP_ENV=production \
|
||||
APP_NAME="My Application"
|
||||
```
|
||||
|
||||
> 💡 包含空格的值用双引号括起来。
|
||||
|
||||
---
|
||||
@@ -57,24 +53,20 @@ WORKDIR $APP_HOME
|
||||
|
||||
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 支持环境变量的指令
|
||||
@@ -102,7 +94,6 @@ const dbUrl = process.env.DATABASE_URL;
|
||||
使用 `-e` 或 `--env` 覆盖 Dockerfile 中定义的环境变量:
|
||||
|
||||
```bash
|
||||
|
||||
## 覆盖单个变量
|
||||
|
||||
$ docker run -e APP_ENV=development myimage
|
||||
@@ -115,18 +106,15 @@ $ 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
|
||||
@@ -141,7 +129,6 @@ DATABASE_URL=postgres://localhost/mydb
|
||||
#### 组合使用
|
||||
|
||||
```docker
|
||||
|
||||
## ARG 接收构建时参数
|
||||
|
||||
ARG NODE_VERSION=20
|
||||
@@ -154,14 +141,11 @@ 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 最佳实践
|
||||
@@ -169,7 +153,6 @@ $ docker build --build-arg NODE_VERSION=18 -t myapp .
|
||||
#### 1. 统一管理版本号
|
||||
|
||||
```docker
|
||||
|
||||
## ✅ 好:版本集中管理
|
||||
|
||||
ENV NGINX_VERSION=1.25.0 \
|
||||
@@ -182,11 +165,9 @@ RUN apt-get install nginx=${NGINX_VERSION}
|
||||
|
||||
RUN apt-get install nginx=1.25.0
|
||||
```
|
||||
|
||||
#### 2. 不要存储敏感信息
|
||||
|
||||
```docker
|
||||
|
||||
## ❌ 错误:密码写入镜像
|
||||
|
||||
ENV DB_PASSWORD=secret123
|
||||
@@ -197,7 +178,6 @@ ENV DB_PASSWORD=secret123
|
||||
|
||||
...
|
||||
```
|
||||
|
||||
#### 3. 为应用提供合理默认值
|
||||
|
||||
```docker
|
||||
@@ -205,11 +185,9 @@ ENV APP_ENV=production \
|
||||
APP_PORT=8080 \
|
||||
LOG_LEVEL=info
|
||||
```
|
||||
|
||||
#### 4. 使用有意义的变量名
|
||||
|
||||
```docker
|
||||
|
||||
## ✅ 好:清晰的命名
|
||||
|
||||
ENV REDIS_HOST=localhost \
|
||||
@@ -220,7 +198,6 @@ ENV REDIS_HOST=localhost \
|
||||
ENV HOST=localhost \
|
||||
PORT=6379
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 7.6.8 常见问题
|
||||
@@ -230,7 +207,6 @@ ENV HOST=localhost \
|
||||
exec 格式不会自动展开环境变量:
|
||||
|
||||
```docker
|
||||
|
||||
## ❌ 不会展开 $PORT
|
||||
|
||||
CMD ["python", "app.py", "--port", "$PORT"]
|
||||
@@ -239,18 +215,15 @@ CMD ["python", "app.py", "--port", "$PORT"]
|
||||
|
||||
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 \
|
||||
@@ -263,5 +236,4 @@ ENV VAR1=value1
|
||||
ENV VAR2=value2
|
||||
ENV VAR3=value3
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
@@ -5,7 +5,6 @@
|
||||
```docker
|
||||
ARG <参数名>[=<默认值>]
|
||||
```
|
||||
|
||||
`ARG` 指令定义构建时的变量,可以在 `docker build` 时通过 `--build-arg` 传入。
|
||||
|
||||
---
|
||||
@@ -26,7 +25,6 @@ ARG <参数名>[=<默认值>]
|
||||
├─ ENV APP_ENV=prod │ APP_ENV=prod(仍存在)
|
||||
└─ RUN echo $VERSION │
|
||||
```
|
||||
|
||||
> ⚠️ **安全提示**:不要用 ARG 传递密码等敏感信息,`docker history` 可以查看所有 ARG 值。
|
||||
|
||||
---
|
||||
@@ -36,7 +34,6 @@ ARG <参数名>[=<默认值>]
|
||||
#### 定义和使用
|
||||
|
||||
```docker
|
||||
|
||||
## 定义有默认值的 ARG
|
||||
|
||||
ARG NODE_VERSION=20
|
||||
@@ -46,11 +43,9 @@ ARG NODE_VERSION=20
|
||||
FROM node:${NODE_VERSION}-alpine
|
||||
RUN echo "Using Node.js $NODE_VERSION"
|
||||
```
|
||||
|
||||
#### 构建时覆盖
|
||||
|
||||
```bash
|
||||
|
||||
## 使用默认值
|
||||
|
||||
$ docker build -t myapp .
|
||||
@@ -59,7 +54,6 @@ $ docker build -t myapp .
|
||||
|
||||
$ docker build --build-arg NODE_VERSION=18 -t myapp .
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 7.7.4 ARG 的作用域
|
||||
@@ -67,7 +61,6 @@ $ docker build --build-arg NODE_VERSION=18 -t myapp .
|
||||
#### FROM 之前的 ARG
|
||||
|
||||
```docker
|
||||
|
||||
## FROM 之前的 ARG 只能用于 FROM 指令
|
||||
|
||||
ARG REGISTRY=docker.io
|
||||
@@ -79,7 +72,6 @@ FROM ${REGISTRY}/${IMAGE_NAME}:20
|
||||
|
||||
RUN echo $REGISTRY # 输出空
|
||||
```
|
||||
|
||||
#### FROM 之后重新声明
|
||||
|
||||
```docker
|
||||
@@ -92,7 +84,6 @@ FROM node:${NODE_VERSION}-alpine
|
||||
ARG NODE_VERSION
|
||||
RUN echo "Node version: $NODE_VERSION"
|
||||
```
|
||||
|
||||
#### 多阶段构建中的 ARG
|
||||
|
||||
```docker
|
||||
@@ -112,7 +103,6 @@ FROM node:20-${BASE_VERSION}
|
||||
ARG NODE_VERSION=20
|
||||
RUN echo "Running with Node $NODE_VERSION"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 7.7.5 常见使用场景
|
||||
@@ -123,11 +113,9 @@ RUN echo "Running with Node $NODE_VERSION"
|
||||
ARG ALPINE_VERSION=3.19
|
||||
FROM alpine:${ALPINE_VERSION}
|
||||
```
|
||||
|
||||
```bash
|
||||
$ docker build --build-arg ALPINE_VERSION=3.18 .
|
||||
```
|
||||
|
||||
#### 2. 设置软件版本
|
||||
|
||||
```docker
|
||||
@@ -135,7 +123,6 @@ ARG NGINX_VERSION=1.25.0
|
||||
|
||||
RUN curl -fsSL https://nginx.org/download/nginx-${NGINX_VERSION}.tar.gz | tar -xz
|
||||
```
|
||||
|
||||
#### 3. 配置构建环境
|
||||
|
||||
```docker
|
||||
@@ -148,7 +135,6 @@ RUN if [ "$ENABLE_DEBUG" = "true" ]; then \
|
||||
npm install --production; \
|
||||
fi
|
||||
```
|
||||
|
||||
#### 4. 配置私有仓库
|
||||
|
||||
```docker
|
||||
@@ -158,14 +144,11 @@ RUN echo "//registry.npmjs.org/:_authToken=${NPM_TOKEN}" > ~/.npmrc && \
|
||||
npm install && \
|
||||
rm ~/.npmrc
|
||||
```
|
||||
|
||||
```bash
|
||||
|
||||
## 构建时传入 token
|
||||
|
||||
$ docker build --build-arg NPM_TOKEN=xxx .
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 7.7.6 将 ARG 传递给 ENV
|
||||
@@ -183,7 +166,6 @@ ENV APP_VERSION=$VERSION
|
||||
|
||||
CMD echo "App version: $APP_VERSION"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 7.7.7 预定义 ARG
|
||||
@@ -198,12 +180,10 @@ Docker 提供了一些预定义的 ARG,无需声明即可使用:
|
||||
| `FTP_PROXY` | FTP 代理 |
|
||||
|
||||
```bash
|
||||
|
||||
## 构建时使用代理
|
||||
|
||||
$ docker build --build-arg HTTP_PROXY=http://proxy:8080 .
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 7.7.8 最佳实践
|
||||
@@ -211,7 +191,6 @@ $ docker build --build-arg HTTP_PROXY=http://proxy:8080 .
|
||||
#### 1. 为 ARG 提供合理默认值
|
||||
|
||||
```docker
|
||||
|
||||
## ✅ 好:有默认值
|
||||
|
||||
ARG NODE_VERSION=20
|
||||
@@ -220,11 +199,9 @@ ARG NODE_VERSION=20
|
||||
|
||||
ARG NODE_VERSION
|
||||
```
|
||||
|
||||
#### 2. 不要用 ARG 存储敏感信息
|
||||
|
||||
```docker
|
||||
|
||||
## ❌ 错误:密码会被记录在镜像历史中
|
||||
|
||||
ARG DB_PASSWORD
|
||||
@@ -234,7 +211,6 @@ RUN echo "password=$DB_PASSWORD" > /app/.env
|
||||
|
||||
...
|
||||
```
|
||||
|
||||
#### 3. 使用 ARG 提高构建灵活性
|
||||
|
||||
```docker
|
||||
@@ -247,5 +223,4 @@ FROM ${BASE_IMAGE}
|
||||
|
||||
...
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
@@ -6,7 +6,6 @@
|
||||
VOLUME ["/路径1", "/路径2"]
|
||||
VOLUME /路径
|
||||
```
|
||||
|
||||
`VOLUME` 指令创建挂载点,并标记为外部挂载的卷。
|
||||
|
||||
---
|
||||
@@ -38,7 +37,6 @@ flowchart LR
|
||||
Volume ~~~ Result2
|
||||
end
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 7.8.3 基本用法
|
||||
@@ -49,14 +47,12 @@ flowchart LR
|
||||
FROM mysql:8.0
|
||||
VOLUME /var/lib/mysql
|
||||
```
|
||||
|
||||
#### 定义多个卷
|
||||
|
||||
```docker
|
||||
FROM myapp
|
||||
VOLUME ["/data", "/logs", "/config"]
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 7.8.4 VOLUME 的行为
|
||||
@@ -71,25 +67,20 @@ $ docker volume ls
|
||||
DRIVER VOLUME NAME
|
||||
local a1b2c3d4e5f6... # 自动创建的匿名卷
|
||||
```
|
||||
|
||||
#### 2. 可被命名卷覆盖
|
||||
|
||||
```bash
|
||||
|
||||
## 使用命名卷替代匿名卷
|
||||
|
||||
$ docker run -v mysql_data:/var/lib/mysql mysql:8.0
|
||||
```
|
||||
|
||||
#### 3. 可被 Bind Mount 覆盖
|
||||
|
||||
```bash
|
||||
|
||||
## 使用宿主机目录替代
|
||||
|
||||
$ docker run -v /my/data:/var/lib/mysql mysql:8.0
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 7.8.5 VOLUME 在构建时的特殊行为
|
||||
@@ -104,7 +95,6 @@ VOLUME /data
|
||||
|
||||
RUN echo "hello" > /data/test.txt
|
||||
```
|
||||
|
||||
**原因**:在构建过程中,VOLUME 指令会为该目录创建一个临时的匿名卷。后续 RUN 指令对该目录的写入实际发生在这个临时卷中,而非镜像层。当该 RUN 指令结束后,临时卷被丢弃,因此写入的内容不会保存到最终镜像中。注意:这与容器运行时创建的匿名卷是不同的——运行时创建的卷会在容器生命周期内持续存在。
|
||||
|
||||
#### 正确做法
|
||||
@@ -120,7 +110,6 @@ RUN mkdir -p /data && echo "hello" > /data/test.txt
|
||||
|
||||
VOLUME /data
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 7.8.6 常见使用场景
|
||||
@@ -131,27 +120,23 @@ VOLUME /data
|
||||
FROM postgres:16
|
||||
VOLUME /var/lib/postgresql/data
|
||||
```
|
||||
|
||||
#### 日志目录
|
||||
|
||||
```docker
|
||||
FROM nginx
|
||||
VOLUME /var/log/nginx
|
||||
```
|
||||
|
||||
#### 上传文件目录
|
||||
|
||||
```docker
|
||||
FROM myapp
|
||||
VOLUME /app/uploads
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 7.8.7 查看 VOLUME 定义
|
||||
|
||||
```bash
|
||||
|
||||
## 查看镜像定义的 VOLUME
|
||||
|
||||
$ docker inspect mysql:8.0 --format '{{json .Config.Volumes}}' | jq
|
||||
@@ -163,7 +148,6 @@ $ docker inspect mysql:8.0 --format '{{json .Config.Volumes}}' | jq
|
||||
|
||||
$ docker inspect mycontainer --format '{{json .Mounts}}' | jq
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 7.8.8 VOLUME vs docker run -v
|
||||
@@ -196,7 +180,6 @@ services:
|
||||
volumes:
|
||||
postgres_data: # 声明命名卷
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 7.8.10 安全注意事项
|
||||
@@ -204,7 +187,6 @@ volumes:
|
||||
#### 匿名卷可能导致数据丢失
|
||||
|
||||
```bash
|
||||
|
||||
## 使用 --rm 运行的容器,匿名卷会在容器删除时一起删除
|
||||
|
||||
$ docker run --rm mysql:8.0
|
||||
@@ -213,13 +195,11 @@ $ docker run --rm mysql:8.0
|
||||
|
||||
...
|
||||
```
|
||||
|
||||
**解决**:始终使用命名卷
|
||||
|
||||
```bash
|
||||
$ docker run -v mysql_data:/var/lib/mysql mysql:8.0
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 7.8.11 最佳实践
|
||||
@@ -227,17 +207,14 @@ $ docker run -v mysql_data:/var/lib/mysql mysql:8.0
|
||||
#### 1. 定义必须持久化的路径
|
||||
|
||||
```docker
|
||||
|
||||
## 数据库必须使用卷
|
||||
|
||||
FROM postgres:16
|
||||
VOLUME /var/lib/postgresql/data
|
||||
```
|
||||
|
||||
#### 2. 不要在 VOLUME 后修改目录
|
||||
|
||||
```docker
|
||||
|
||||
## ❌ 避免
|
||||
|
||||
VOLUME /app/data
|
||||
@@ -248,11 +225,9 @@ RUN cp init-data.json /app/data/
|
||||
RUN mkdir -p /app/data && cp init-data.json /app/data/
|
||||
VOLUME /app/data
|
||||
```
|
||||
|
||||
#### 3. 文档中说明 VOLUME 用途
|
||||
|
||||
```docker
|
||||
|
||||
## 持久化用户上传的文件
|
||||
|
||||
VOLUME /app/uploads
|
||||
@@ -261,5 +236,4 @@ VOLUME /app/uploads
|
||||
|
||||
VOLUME /var/lib/mysql
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
@@ -5,7 +5,6 @@
|
||||
```docker
|
||||
EXPOSE <端口> [<端口>/<协议>...]
|
||||
```
|
||||
|
||||
`EXPOSE` 声明容器运行时提供服务的端口。这是一个 **文档性质的声明**,告诉使用者容器会监听哪些端口。
|
||||
|
||||
---
|
||||
@@ -13,7 +12,6 @@ EXPOSE <端口> [<端口>/<协议>...]
|
||||
### 7.9.2 基本用法
|
||||
|
||||
```docker
|
||||
|
||||
## 声明单个端口
|
||||
|
||||
EXPOSE 80
|
||||
@@ -27,7 +25,6 @@ EXPOSE 80 443
|
||||
EXPOSE 80/tcp
|
||||
EXPOSE 53/udp
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 7.9.3 EXPOSE 的作用
|
||||
@@ -37,37 +34,30 @@ EXPOSE 53/udp
|
||||
告诉镜像使用者,容器将在哪些端口提供服务:
|
||||
|
||||
```docker
|
||||
|
||||
## 使用者一看就知道这是 web 应用
|
||||
|
||||
EXPOSE 80 443
|
||||
```
|
||||
|
||||
```bash
|
||||
|
||||
## 查看镜像暴露的端口
|
||||
|
||||
$ docker inspect nginx --format '{{.Config.ExposedPorts}}'
|
||||
map[80/tcp:{}]
|
||||
```
|
||||
|
||||
#### 2. 配合 -P 使用
|
||||
|
||||
使用 `docker run -P` 时,Docker 会自动映射 EXPOSE 的端口到宿主机随机端口:
|
||||
|
||||
```docker
|
||||
|
||||
## Dockerfile
|
||||
|
||||
EXPOSE 80
|
||||
```
|
||||
|
||||
```bash
|
||||
$ docker run -P nginx
|
||||
$ docker port $(docker ps -q)
|
||||
80/tcp -> 0.0.0.0:32768
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 7.9.4 EXPOSE vs -p
|
||||
@@ -85,11 +75,9 @@ flowchart TD
|
||||
Run["docker run -p<br/>实际端口映射<br/>宿主机 ←→ 容器"]
|
||||
Expose ~~~ Run
|
||||
```
|
||||
|
||||
#### 没有 EXPOSE 也能 -p
|
||||
|
||||
```docker
|
||||
|
||||
## 即使没有 EXPOSE,也可以使用 -p
|
||||
|
||||
FROM nginx
|
||||
@@ -98,14 +86,11 @@ FROM nginx
|
||||
|
||||
...
|
||||
```
|
||||
|
||||
```bash
|
||||
|
||||
## 仍然可以映射端口
|
||||
|
||||
$ docker run -p 8080:80 mynginx
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 7.9.5 常见误解
|
||||
@@ -113,12 +98,10 @@ $ docker run -p 8080:80 mynginx
|
||||
#### 误解:EXPOSE 会打开端口
|
||||
|
||||
```docker
|
||||
|
||||
## ❌ 错误理解:这不会让容器可从外部访问
|
||||
|
||||
EXPOSE 80
|
||||
```
|
||||
|
||||
EXPOSE 不会:
|
||||
|
||||
- 自动进行端口映射
|
||||
@@ -130,20 +113,16 @@ EXPOSE 只是元数据声明。容器是否实际监听该端口,取决于容
|
||||
#### 正确理解
|
||||
|
||||
```docker
|
||||
|
||||
## Dockerfile
|
||||
|
||||
FROM nginx
|
||||
EXPOSE 80 # 1. 声明:这个容器会在 80 端口提供服务
|
||||
```
|
||||
|
||||
```bash
|
||||
|
||||
## 运行:需要 -p 才能从外部访问
|
||||
|
||||
$ docker run -p 8080:80 nginx # 2. 映射:宿主机 8080 → 容器 80
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 7.9.6 最佳实践
|
||||
@@ -151,7 +130,6 @@ $ docker run -p 8080:80 nginx # 2. 映射:宿主机 8080 → 容器 80
|
||||
#### 1. 总是声明应用使用的端口
|
||||
|
||||
```docker
|
||||
|
||||
## Web 服务
|
||||
|
||||
FROM nginx
|
||||
@@ -167,11 +145,9 @@ EXPOSE 5432
|
||||
FROM redis
|
||||
EXPOSE 6379
|
||||
```
|
||||
|
||||
#### 2. 使用明确的协议
|
||||
|
||||
```docker
|
||||
|
||||
## 默认是 TCP
|
||||
|
||||
EXPOSE 80
|
||||
@@ -184,11 +160,9 @@ EXPOSE 53/udp
|
||||
|
||||
EXPOSE 53/tcp 53/udp
|
||||
```
|
||||
|
||||
#### 3. 与应用实际端口保持一致
|
||||
|
||||
```docker
|
||||
|
||||
## ✅ 好:EXPOSE 与应用端口一致
|
||||
|
||||
ENV PORT=3000
|
||||
@@ -200,7 +174,6 @@ CMD ["node", "server.js"]
|
||||
EXPOSE 80
|
||||
CMD ["node", "server.js"] # 实际监听 3000
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 7.9.7 使用环境变量
|
||||
@@ -209,7 +182,6 @@ CMD ["node", "server.js"] # 实际监听 3000
|
||||
ARG PORT=80
|
||||
EXPOSE $PORT
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 7.9.8 在 Compose 中
|
||||
@@ -225,7 +197,6 @@ services:
|
||||
expose:
|
||||
- "80" # 仅声明(类似 EXPOSE)
|
||||
```
|
||||
|
||||
`expose` 在 Compose 中仅用于容器间通信的文档说明,不进行端口映射。
|
||||
|
||||
---
|
||||
|
||||
@@ -57,11 +57,9 @@ Dockerfile 一般分为四部分:基础镜像信息、维护者信息、镜像
|
||||
```bash
|
||||
docker build [选项] <上下文路径/URL/->
|
||||
```
|
||||
|
||||
例如,在 Dockerfile 所在目录执行:
|
||||
|
||||
```bash
|
||||
docker build -t my-image:v1 .
|
||||
```
|
||||
|
||||
更多关于 `docker build` 的用法,我们在实战中会结合具体指令进行演示。
|
||||
|
||||
Reference in New Issue
Block a user