Remove blank lines after code block markers

This commit is contained in:
yeasy
2026-03-21 22:36:09 -07:00
parent 312f8fea42
commit 9ac19d79ee
132 changed files with 0 additions and 1517 deletions

View File

@@ -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
```
---

View File

@@ -6,7 +6,6 @@
USER <用户名>[:<用户组>]
USER <UID>[:<GID>]
```
`USER` 指令切换后续指令 (RUNCMDENTRYPOINT) 的执行用户
---
@@ -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 用户无法绑定 80443 等端口

View File

@@ -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. 只检查核心依赖
健康检查应主要关注 **当前服务** 是否可用而不是检查其下游依赖 (数据库等)下游依赖的检查应由应用逻辑处理

View File

@@ -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` 中执行过于耗时或不确定的操作 (如更新系统软件)这会让子镜像构建变得缓慢且不可控

View File

@@ -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")
```
---

View File

@@ -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 中显式声明而不是依赖默认行为

View File

@@ -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 .` 从上一阶段的镜像中复制文件我们也可以复制任意镜像中的文件

View File

@@ -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 项目依赖其他外部服务例如 redisMySQL请自行启动这些服务之后再进行测试本小节不再赘述

View File

@@ -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
```
---

View File

@@ -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 . .
```
---
> **🔥 踩坑实录**

View File

@@ -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
```
---

View File

@@ -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"]
```
---

View File

@@ -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 # ✓ 正常工作
当前 IP61.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 # ✓ 正常工作
当前 IP61.148.226.66
@@ -125,7 +111,6 @@ HTTP/1.1 200 OK
...
当前 IP61.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
```
---

View File

@@ -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
```
---

View File

@@ -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}
...
```
---

View File

@@ -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
```
---

View File

@@ -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 中仅用于容器间通信的文档说明不进行端口映射
---

View File

@@ -57,11 +57,9 @@ Dockerfile 一般分为四部分:基础镜像信息、维护者信息、镜像
```bash
docker build [选项] <上下文路径/URL/->
```
例如 Dockerfile 所在目录执行
```bash
docker build -t my-image:v1 .
```
更多关于 `docker build` 的用法我们在实战中会结合具体指令进行演示