> ## Content Index
> Fetch the complete content index at: https://blog.vercanti.com/llms.txt
> Use this file to discover other available public pages before exploring further.

# Docker 初级指南
- URL: https://blog.vercanti.com/docker-chu-ji-zhi-nan/
- Published: 2026-08-28T14:34:22.000Z
- Updated: 2026-08-28T14:56:22.000Z
- Description: Docker 是一个开源的容器化平台，允许开发者将应用及其依赖打包到一个轻量级、可移植的容器中，在任何环境中一致地运行。 Image（镜像） 镜像是一个只读的模板，用于创建容器。它包含了运行应用所需的所有文件：操作系统文件、运行时、库、配置等。镜像采用分层结构，每一层都是只读的。 Container（容器） 容器是镜像的运行实例。在只读的镜像层之上，容器有一个可读写层。容器之间相互隔离，但共享宿主机的操作系统内核。容器停止后其可写层数据默认消失（需要卷来持久化）。 Registry（仓库） Registry 是存储和分发镜像的服务。常见的 Registr
- Author: yellowdog
- Tags: DevOps, Docker

> 官方文档：<https://docs.docker.com/>  
> 适用版本：Docker Engine 24+（2026-05-07 核实）

Docker 是一个开源的容器化平台，允许开发者将应用及其依赖打包到一个轻量级、可移植的容器中，在任何环境中一致地运行。

---

## 1\. Docker 是什么

### 容器 vs 虚拟机

| 对比项  | 容器（Container）           | 虚拟机（VM）           |
| ---- | ----------------------- | ----------------- |
| 启动速度 | 秒级（毫秒级）                 | 分钟级               |
| 资源占用 | 极低，共享宿主机内核              | 较高，每个 VM 独立内核     |
| 隔离级别 | 进程级隔离（namespace/cgroup） | 硬件级隔离（Hypervisor） |
| 镜像大小 | 通常几十 MB 到数百 MB          | 通常几 GB 到几十 GB     |
| 可移植性 | 极高，跨平台一致运行              | 较高，但文件体积大         |
| 密度   | 单机可运行数百个容器              | 单机通常运行数十个 VM      |
| 安全性  | 相对较低（共享内核）              | 较高（完全隔离）          |

### 核心概念

**Image（镜像）**

镜像是一个只读的模板，用于创建容器。它包含了运行应用所需的所有文件：操作系统文件、运行时、库、配置等。镜像采用分层结构，每一层都是只读的。

**Container（容器）**

容器是镜像的运行实例。在只读的镜像层之上，容器有一个可读写层。容器之间相互隔离，但共享宿主机的操作系统内核。容器停止后其可写层数据默认消失（需要卷来持久化）。

**Registry（仓库）**

Registry 是存储和分发镜像的服务。常见的 Registry：

- Docker Hub（`hub.docker.com`）：官方公共仓库
- 阿里云容器镜像服务：国内加速
- GitHub Container Registry（`ghcr.io`）
- 私有 Registry：自建的 Harbor 等

**Dockerfile**

Dockerfile 是一个文本文件，包含一系列指令，描述如何构建一个镜像。每条指令对应镜像中的一层。

**Docker Compose**

Docker Compose 是用于定义和运行多容器 Docker 应用的工具。通过一个 `docker-compose.yml` 文件配置所有服务，然后用一条命令启动整个应用栈。

### 为什么使用 Docker

- **环境一致性**：开发、测试、生产环境完全一致，消除"在我机器上能跑"的问题
- **快速部署**：镜像一旦构建，几秒内即可启动容器，大幅缩短部署时间
- **资源隔离**：每个容器拥有独立的文件系统、网络、进程空间，互不干扰
- **版本管理**：镜像版本化，可以随时回滚到任意历史版本
- **微服务友好**：天然适合微服务架构，每个服务独立打包、独立扩展

---

## 2\. 安装

### Linux（Ubuntu / Debian）

```bash
# 卸载旧版本
sudo apt-get remove docker docker-engine docker.io containerd runc

# 更新包索引并安装依赖
sudo apt-get update
sudo apt-get install -y ca-certificates curl gnupg

# 添加 Docker 官方 GPG 密钥
sudo install -m 0755 -d /etc/apt/keyrings
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg
sudo chmod a+r /etc/apt/keyrings/docker.gpg

# 添加 Docker 仓库
echo \
  "deb [arch="$(dpkg --print-architecture)" signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu \
  "$(. /etc/os-release && echo "$VERSION_CODENAME")" stable" | \
  sudo tee /etc/apt/sources.list.d/docker.list > /dev/null

# 安装 Docker Engine
sudo apt-get update
sudo apt-get install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin

# 将当前用户加入 docker 组（避免每次 sudo）
sudo usermod -aG docker $USER
newgrp docker

# 启动并设置开机启动
sudo systemctl enable docker
sudo systemctl start docker

```

### macOS（Docker Desktop）

1. 访问 <https://www.docker.com/products/docker-desktop>
2. 下载 macOS 版本（区分 Intel 和 Apple Silicon）
3. 安装 `.dmg` 文件，拖拽到 Applications
4. 启动 Docker Desktop，等待鲸鱼图标稳定

也可使用 Homebrew：

```bash
brew install --cask docker

```

### Windows（Docker Desktop + WSL2）

**前置条件：**

```powershell
# 以管理员身份在 PowerShell 中执行
# 启用 WSL
wsl --install

# 安装完成后重启，然后安装 Ubuntu 发行版
wsl --install -d Ubuntu

```

然后：

1. 访问 <https://www.docker.com/products/docker-desktop>
2. 下载 Windows 版 Docker Desktop
3. 安装时勾选"Use WSL 2 instead of Hyper-V"
4. 安装完成后重启
5. 在 Docker Desktop 设置中，确认 WSL Integration 已开启

### 验证安装

```bash
# 查看版本
docker --version
# 输出示例：Docker version 24.0.5, build ced0996

# 查看详细信息（守护进程是否运行）
docker info

# 运行 hello-world 测试
docker run hello-world

```

`hello-world` 输出 "Hello from Docker!" 表示安装成功，它完整演示了 Docker 从拉取镜像到运行容器的全流程。

---

## 3\. 核心概念与架构

### Docker 架构

Docker 采用客户端-服务端架构：

```
Docker Client (docker CLI)
       |
       | REST API / Unix socket
       v
Docker Daemon (dockerd)  <----->  Registry (Docker Hub 等)
       |
       +-- Images（本地镜像存储）
       +-- Containers（运行中的容器）
       +-- Networks（网络）
       +-- Volumes（卷）

```

- **Docker Client**：用户与 Docker 交互的命令行工具，发送命令给 Daemon
- **Docker Daemon（dockerd）**：后台运行的服务，负责构建、运行、管理容器
- **Registry**：镜像仓库，Daemon 从这里拉取（pull）和推送（push）镜像

### 镜像分层机制（Layer）

Docker 镜像由多个只读层叠加而成。以下 Dockerfile 会产生多层：

```dockerfile
FROM ubuntu:22.04        # 第1层：基础镜像
RUN apt-get update       # 第2层：更新包索引
RUN apt-get install -y nginx  # 第3层：安装 nginx
COPY index.html /var/www/html/ # 第4层：复制文件

```

层的核心特性：

- 每条 `RUN`、`COPY`、`ADD` 指令产生一个新层
- 层是只读且可复用的：多个镜像共享相同的层，节省磁盘空间
- 拉取镜像时，已有的层不会重复下载
- 构建时，未变化的层从缓存读取，加速构建

### Union File System（联合文件系统）

Docker 使用联合文件系统（如 overlay2）将多个只读层和一个可写层合并成一个统一的文件系统视图。容器对文件的修改写入最顶层的可写层（容器层），不影响下方的只读镜像层。容器删除后，可写层随之消失。

---

## 4\. 常用镜像操作命令

### docker pull

从 Registry 拉取镜像。

```
docker pull [OPTIONS] NAME[:TAG|@DIGEST]

```

| 参数                       | 类型     | 默认值   | 说明                               |
| ------------------------ | ------ | ----- | -------------------------------- |
| \-a, \--all-tags         | flag   | false | 拉取仓库中所有 tag 的镜像                  |
| \--platform              | string | 当前平台  | 指定目标平台，如 linux/amd64、linux/arm64 |
| \-q, \--quiet            | flag   | false | 静默模式，不显示进度条                      |
| \--disable-content-trust | flag   | true  | 跳过镜像签名验证                         |

```bash
# 拉取最新版 nginx（等同于 nginx:latest）
docker pull nginx

# 拉取指定 tag
docker pull nginx:alpine
docker pull ubuntu:22.04

# 拉取指定 digest（精确版本，不可变）
docker pull nginx@sha256:abc123...

# 为 ARM 设备拉取 AMD64 镜像
docker pull --platform linux/amd64 nginx

```

### docker images / docker image ls

列出本地镜像。

```
docker images [OPTIONS] [REPOSITORY[:TAG]]

```

| 参数             | 类型     | 默认值   | 说明                   |
| -------------- | ------ | ----- | -------------------- |
| \-a, \--all    | flag   | false | 显示所有镜像，含构建中间层        |
| \-q, \--quiet  | flag   | false | 只输出镜像 ID             |
| \--format      | string | \-    | 自定义输出格式（Go 模板）       |
| \-f, \--filter | string | \-    | 过滤条件，如 dangling=true |
| \--no-trunc    | flag   | false | 显示完整的镜像 ID           |
| \--digests     | flag   | false | 显示 digest 信息         |

```bash
# 列出所有本地镜像
docker images

# 只输出 ID（用于批量操作）
docker images -q

# 过滤悬空镜像（没有 tag 的镜像，通常是构建遗留）
docker images -f dangling=true

# 自定义格式输出
docker images --format "table {{.Repository}}\t{{.Tag}}\t{{.Size}}"

# 只显示 nginx 相关镜像
docker images nginx

```

### docker rmi / docker image rm

删除本地镜像。

```
docker rmi [OPTIONS] IMAGE [IMAGE...]

```

| 参数            | 类型   | 默认值   | 说明              |
| ------------- | ---- | ----- | --------------- |
| \-f, \--force | flag | false | 强制删除，即使有容器依赖此镜像 |
| \--no-prune   | flag | false | 不删除未被 tag 的父镜像  |

```bash
# 删除指定镜像
docker rmi nginx:alpine

# 强制删除（有容器在用时）
docker rmi -f nginx:alpine

# 删除多个镜像
docker rmi nginx ubuntu:22.04

# 通过 ID 删除
docker rmi abc123def456

# 批量删除所有悬空镜像
docker image prune

# 清理所有未被任何容器使用的镜像（谨慎）
docker image prune -a

# 删除所有镜像（极度谨慎）
docker rmi $(docker images -q)

```

### docker search

在 Docker Hub 中搜索镜像。

```
docker search [OPTIONS] TERM

```

| 参数             | 类型     | 默认值   | 说明      |
| -------------- | ------ | ----- | ------- |
| \--limit       | int    | 25    | 最大返回结果数 |
| \-f, \--filter | string | \-    | 过滤条件    |
| \--format      | string | \-    | 自定义输出格式 |
| \--no-trunc    | flag   | false | 显示完整描述  |

常用过滤条件：

| 过滤器               | 示例                | 说明          |
| ----------------- | ----------------- | ----------- |
| stars=N           | stars=100         | 最少 N 个 star |
| is-official=true  | is-official=true  | 只显示官方镜像     |
| is-automated=true | is-automated=true | 只显示自动构建镜像   |

```bash
# 搜索 nginx
docker search nginx

# 只看官方镜像且 star 数超过 1000
docker search --filter is-official=true --filter stars=1000 nginx

# 限制结果数
docker search --limit 5 python

```

### docker tag

为镜像打标签，常用于推送前重命名。

```
docker tag SOURCE_IMAGE[:TAG] TARGET_IMAGE[:TAG]

```

```bash
# 为本地镜像打 tag，准备推送到私有仓库
docker tag myapp:1.0 registry.example.com/myteam/myapp:1.0

# 打 latest tag
docker tag myapp:1.0 myapp:latest

# 推送到 Docker Hub（需先 docker login）
docker tag myapp:1.0 username/myapp:1.0
docker push username/myapp:1.0

```

---

## 5\. 容器操作命令

### docker run

创建并启动一个新容器，是最核心的命令。

```
docker run [OPTIONS] IMAGE [COMMAND] [ARG...]

```

| 参数                  | 类型     | 默认值    | 说明                                                                  |
| ------------------- | ------ | ------ | ------------------------------------------------------------------- |
| \-d, \--detach      | flag   | false  | 后台运行容器，返回容器 ID                                                      |
| \-i, \--interactive | flag   | false  | 保持 stdin 打开                                                         |
| \-t, \--tty         | flag   | false  | 分配伪终端（TTY）                                                          |
| \--name             | string | 随机生成   | 指定容器名称，便于引用                                                         |
| \-p, \--publish     | string | \-     | 端口映射，格式：HOST\_PORT:CONTAINER\_PORT 或 HOST\_PORT:CONTAINER\_PORT/tcp |
| \-P, \--publish-all | flag   | false  | 将所有 EXPOSE 的端口映射到宿主机随机端口                                            |
| \-v, \--volume      | string | \-     | 挂载卷，格式：HOST\_PATH:CONTAINER\_PATH\[:ro\]                            |
| \--mount            | string | \-     | 更清晰的挂载语法，推荐用于复杂挂载                                                   |
| \-e, \--env         | string | \-     | 设置环境变量，如 \-e KEY=VALUE                                              |
| \--env-file         | string | \-     | 从文件批量读取环境变量                                                         |
| \--network          | string | bridge | 网络模式：bridge、host、none、自定义网络名                                        |
| \--rm               | flag   | false  | 容器退出后自动删除容器（不删除卷）                                                   |
| \--restart          | string | no     | 重启策略                                                                |
| \-w, \--workdir     | string | \-     | 容器内的工作目录                                                            |
| \-u, \--user        | string | \-     | 运行命令的用户，格式：user 或 user:group                                        |
| \-m, \--memory      | string | \-     | 内存限制，如 512m、1g                                                      |
| \--memory-swap      | string | \-     | 内存+Swap 总限制                                                         |
| \--cpus             | float  | \-     | CPU 核数限制，如 0.5、2                                                    |
| \--cpu-shares       | int    | 1024   | CPU 相对权重                                                            |
| \--platform         | string | 当前平台   | 指定镜像平台                                                              |
| \--entrypoint       | string | \-     | 覆盖镜像的 ENTRYPOINT                                                    |
| \-l, \--label       | string | \-     | 添加元数据标签                                                             |
| \--hostname         | string | 容器ID   | 容器主机名                                                               |
| \--add-host         | string | \-     | 添加 hosts 记录，格式 host:ip                                              |
| \--privileged       | flag   | false  | 给予容器扩展权限（谨慎使用）                                                      |
| \--read-only        | flag   | false  | 将容器根文件系统设为只读                                                        |

重启策略说明：

| 策略               | 说明                     |
| ---------------- | ---------------------- |
| no               | 不自动重启（默认）              |
| always           | 总是重启，包括 Docker 守护进程重启后 |
| on-failure\[:N\] | 仅在非零退出码时重启，可限制次数       |
| unless-stopped   | 总是重启，除非手动 stop         |

```bash
# 后台运行 nginx，将宿主机 80 端口映射到容器 80 端口
docker run -d -p 80:80 --name my-nginx nginx

# 交互式运行 ubuntu，退出后自动删除容器
docker run -it --rm ubuntu:22.04 bash

# 挂载当前目录到容器 /app，设置工作目录，安装依赖
docker run -v $(pwd):/app -w /app node:18 npm install

# 传入环境变量运行 PostgreSQL
docker run -d \
  --name my-postgres \
  -e POSTGRES_USER=admin \
  -e POSTGRES_PASSWORD=secret \
  -e POSTGRES_DB=mydb \
  -p 5432:5432 \
  -v pgdata:/var/lib/postgresql/data \
  --restart unless-stopped \
  postgres:15

# 限制资源运行
docker run -d --memory 512m --cpus 1.0 --name limited-app myapp

# 从环境变量文件启动
docker run --env-file .env myapp

# 只读根文件系统（安全加固）
docker run --read-only --tmpfs /tmp myapp

# 覆盖 ENTRYPOINT，进入 shell 调试
docker run -it --entrypoint bash nginx

```

### docker start / stop / restart / kill

对已有容器进行生命周期管理。

```bash
# docker start：启动已停止的容器
docker start [OPTIONS] CONTAINER [CONTAINER...]

```

| 参数                  | 类型   | 默认值   | 说明                   |
| ------------------- | ---- | ----- | -------------------- |
| \-a, \--attach      | flag | false | 附加到容器的 stdout/stderr |
| \-i, \--interactive | flag | false | 附加容器的 stdin          |

```bash
# docker stop：优雅停止容器（发送 SIGTERM，等待后发送 SIGKILL）
docker stop [OPTIONS] CONTAINER [CONTAINER...]

```

| 参数             | 类型     | 默认值     | 说明          |
| -------------- | ------ | ------- | ----------- |
| \-t, \--time   | int    | 10      | 等待容器停止的超时秒数 |
| \-s, \--signal | string | SIGTERM | 发送的停止信号     |

```bash
# 示例
docker start my-nginx
docker stop my-nginx
docker stop -t 30 my-nginx    # 等待最多 30 秒

docker restart my-nginx
docker restart -t 5 my-nginx  # 5 秒后强制重启

# docker kill：立即发送信号（默认 SIGKILL）
docker kill [OPTIONS] CONTAINER [CONTAINER...]

```

| 参数             | 类型     | 默认值  | 说明        |
| -------------- | ------ | ---- | --------- |
| \-s, \--signal | string | KILL | 发送的信号名或编号 |

```bash
docker kill my-nginx
docker kill -s SIGINT my-nginx

```

### docker ps / docker container ls

列出容器。

```
docker ps [OPTIONS]

```

| 参数             | 类型     | 默认值   | 说明            |
| -------------- | ------ | ----- | ------------- |
| \-a, \--all    | flag   | false | 显示所有容器，含已停止的  |
| \-q, \--quiet  | flag   | false | 只输出容器 ID      |
| \-f, \--filter | string | \-    | 过滤条件          |
| \--format      | string | \-    | 自定义输出格式       |
| \-n, \--last   | int    | \-1   | 显示最近创建的 N 个容器 |
| \-l, \--latest | flag   | false | 显示最近创建的容器     |
| \-s, \--size   | flag   | false | 显示容器文件大小      |
| \--no-trunc    | flag   | false | 不截断输出         |

常用过滤条件：

| 过滤器      | 示例             | 说明                                       |
| -------- | -------------- | ---------------------------------------- |
| status   | status=running | 按状态过滤：created/running/paused/exited/dead |
| name     | name=my-nginx  | 按名称过滤（支持正则）                              |
| label    | label=env=prod | 按标签过滤                                    |
| ancestor | ancestor=nginx | 基于某镜像创建的容器                               |

```bash
# 查看运行中的容器
docker ps

# 查看所有容器（含已停止）
docker ps -a

# 只看 ID（用于批量操作）
docker ps -q

# 查看已停止的容器
docker ps -f status=exited

# 自定义输出格式
docker ps --format "table {{.Names}}\t{{.Status}}\t{{.Ports}}"

# 停止所有运行中的容器
docker stop $(docker ps -q)

```

### docker exec

在运行中的容器内执行命令。

```
docker exec [OPTIONS] CONTAINER COMMAND [ARG...]

```

| 参数                  | 类型     | 默认值   | 说明          |
| ------------------- | ------ | ----- | ----------- |
| \-i, \--interactive | flag   | false | 保持 stdin 打开 |
| \-t, \--tty         | flag   | false | 分配伪终端       |
| \-d, \--detach      | flag   | false | 在后台执行命令     |
| \-e, \--env         | string | \-    | 设置环境变量      |
| \-u, \--user        | string | \-    | 指定执行用户      |
| \-w, \--workdir     | string | \-    | 指定工作目录      |
| \--privileged       | flag   | false | 给予命令扩展权限    |

```bash
# 进入容器 bash（最常用）
docker exec -it my-nginx bash

# 进入容器 sh（当容器没有 bash 时）
docker exec -it my-nginx sh

# 在容器内执行单条命令
docker exec my-nginx nginx -t

# 以 root 身份进入容器
docker exec -it -u root my-app bash

# 在指定工作目录执行
docker exec -w /app my-app ls -la

# 后台执行命令
docker exec -d my-nginx touch /tmp/testfile

```

### docker logs

获取容器的日志输出。

```
docker logs [OPTIONS] CONTAINER

```

| 参数                 | 类型     | 默认值   | 说明                           |
| ------------------ | ------ | ----- | ---------------------------- |
| \-f, \--follow     | flag   | false | 实时跟踪日志输出                     |
| \--tail            | string | all   | 只显示最后 N 行                    |
| \--since           | string | \-    | 显示指定时间点之后的日志，如 2024-01-01、1h |
| \--until           | string | \-    | 显示指定时间点之前的日志                 |
| \-t, \--timestamps | flag   | false | 显示时间戳                        |
| \--details         | flag   | false | 显示额外的日志详情                    |

```bash
# 查看容器日志
docker logs my-nginx

# 实时跟踪（类似 tail -f）
docker logs -f my-nginx

# 只看最后 100 行
docker logs --tail 100 my-nginx

# 实时跟踪最后 50 行开始
docker logs -f --tail 50 my-nginx

# 显示时间戳
docker logs -t my-nginx

# 查看最近 1 小时的日志
docker logs --since 1h my-nginx

# 查看指定时间范围
docker logs --since "2024-01-01T10:00:00" --until "2024-01-01T11:00:00" my-nginx

```

### docker cp

在容器与宿主机之间复制文件。

```
docker cp [OPTIONS] CONTAINER:SRC_PATH DEST_PATH
docker cp [OPTIONS] SRC_PATH CONTAINER:DEST_PATH

```

| 参数                  | 类型   | 默认值   | 说明                     |
| ------------------- | ---- | ----- | ---------------------- |
| \-a, \--archive     | flag | false | 保留文件所有者和权限（Archive 模式） |
| \-L, \--follow-link | flag | false | 跟随 SRC\_PATH 中的符号链接    |
| \-q, \--quiet       | flag | false | 静默模式                   |

```bash
# 从容器复制文件到宿主机
docker cp my-nginx:/etc/nginx/nginx.conf ./nginx.conf

# 从宿主机复制文件到容器
docker cp ./index.html my-nginx:/usr/share/nginx/html/index.html

# 复制整个目录
docker cp my-app:/app/logs ./logs

# 复制到容器时保留权限
docker cp -a ./config.json my-app:/etc/app/config.json

```

### docker rm

删除已停止的容器。

```
docker rm [OPTIONS] CONTAINER [CONTAINER...]

```

| 参数              | 类型   | 默认值   | 说明                     |
| --------------- | ---- | ----- | ---------------------- |
| \-f, \--force   | flag | false | 强制删除运行中的容器（发送 SIGKILL） |
| \-v, \--volumes | flag | false | 同时删除容器关联的匿名卷           |
| \-l, \--link    | flag | false | 删除容器间的网络连接（不删除容器）      |

```bash
# 删除已停止的容器
docker rm my-nginx

# 强制删除运行中的容器
docker rm -f my-nginx

# 删除容器及其匿名卷
docker rm -v my-nginx

# 清理所有已停止的容器
docker container prune

# 删除所有容器（包括运行中的，谨慎）
docker rm -f $(docker ps -aq)

```

### docker inspect

获取容器或镜像的详细配置信息（JSON 格式）。

```
docker inspect [OPTIONS] NAME|ID [NAME|ID...]

```

| 参数             | 类型     | 默认值   | 说明                                  |
| -------------- | ------ | ----- | ----------------------------------- |
| \-f, \--format | string | \-    | 使用 Go 模板格式化输出                       |
| \--type        | string | \-    | 指定类型：container、image、network、volume |
| \-s, \--size   | flag   | false | 显示容器文件系统大小（仅容器）                     |

```bash
# 查看容器全部信息
docker inspect my-nginx

# 只获取容器 IP 地址
docker inspect -f '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' my-nginx

# 获取挂载点信息
docker inspect -f '{{json .Mounts}}' my-nginx | python3 -m json.tool

# 获取环境变量
docker inspect -f '{{range .Config.Env}}{{println .}}{{end}}' my-nginx

# 获取容器重启策略
docker inspect -f '{{.HostConfig.RestartPolicy.Name}}' my-nginx

```

### docker stats

实时显示容器资源使用情况。

```
docker stats [OPTIONS] [CONTAINER...]

```

| 参数           | 类型     | 默认值   | 说明           |
| ------------ | ------ | ----- | ------------ |
| \-a, \--all  | flag   | false | 显示所有容器（含停止的） |
| \--no-stream | flag   | false | 只输出一次，不持续刷新  |
| \--no-trunc  | flag   | false | 不截断容器 ID     |
| \--format    | string | \-    | 自定义输出格式      |

```bash
# 实时查看所有运行容器的资源使用
docker stats

# 查看指定容器
docker stats my-nginx my-postgres

# 只输出一次（用于脚本）
docker stats --no-stream

# 自定义格式
docker stats --format "table {{.Name}}\t{{.CPUPerc}}\t{{.MemUsage}}"

```

---

## 6\. Dockerfile 基础

### 基本结构示例

以下是一个完整的 Node.js 应用 Dockerfile：

```dockerfile
# 指定基础镜像和版本（使用具体版本而非 latest）
FROM node:18-alpine

# 设置维护者信息（元数据）
LABEL maintainer="your-email@example.com"
LABEL version="1.0"

# 设置工作目录（不存在会自动创建）
WORKDIR /app

# 先复制 package 文件，利用层缓存
# 只有 package.json 变化时才重新 npm install
COPY package*.json ./

# 安装生产依赖（alpine 镜像已有 npm）
RUN npm ci --only=production

# 再复制其余源代码
COPY . .

# 声明应用监听的端口（文档作用，不自动映射）
EXPOSE 3000

# 创建非 root 用户提升安全性
RUN addgroup -S appgroup && adduser -S appuser -G appgroup
USER appuser

# 设置健康检查
HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 \
  CMD wget -qO- http://localhost:3000/health || exit 1

# 容器启动时执行的命令（exec 形式，推荐）
CMD ["node", "server.js"]

```

### 常用指令完整参考

| 指令          | 语法示例                                        | 说明                          |
| ----------- | ------------------------------------------- | --------------------------- |
| FROM        | FROM ubuntu:22.04                           | 指定基础镜像，必须是第一条非注释指令          |
| RUN         | RUN apt-get install -y curl                 | 执行命令并提交结果为新层                |
| COPY        | COPY src/ /app/src/                         | 从构建上下文复制文件，推荐使用             |
| ADD         | ADD archive.tar.gz /app/                    | 类似 COPY，额外支持 URL 和自动解压 tar  |
| WORKDIR     | WORKDIR /app                                | 设置后续指令的工作目录                 |
| ENV         | ENV NODE\_ENV=production                    | 设置环境变量，运行时可见                |
| ARG         | ARG BUILD\_VERSION=1.0                      | 构建参数，仅构建阶段可见                |
| EXPOSE      | EXPOSE 8080                                 | 声明容器监听端口（仅文档作用）             |
| CMD         | CMD \["node", "app.js"\]                    | 默认启动命令，可被 docker run 末尾参数覆盖 |
| ENTRYPOINT  | ENTRYPOINT \["nginx", "-g", "daemon off;"\] | 入口点，不易被覆盖                   |
| LABEL       | LABEL version="1.0"                         | 添加键值元数据                     |
| USER        | USER 1001                                   | 切换运行用户                      |
| VOLUME      | VOLUME \["/data"\]                          | 声明挂载点                       |
| HEALTHCHECK | HEALTHCHECK CMD curl -f http://localhost/   | 定义健康检查命令                    |
| ONBUILD     | ONBUILD COPY . /app                         | 仅当此镜像作为基础镜像时触发              |
| STOPSIGNAL  | STOPSIGNAL SIGTERM                          | 设置停止容器时发送的信号                |

#### FROM

```dockerfile
# 基本用法
FROM ubuntu:22.04

# 使用摘要锁定版本
FROM ubuntu@sha256:abc123...

# 多阶段构建中命名阶段
FROM node:18 AS builder
FROM nginx:alpine AS final

```

#### RUN

RUN 有两种形式：

```dockerfile
# Shell 形式（通过 /bin/sh -c 执行，支持 shell 特性）
RUN apt-get update && apt-get install -y \
    curl \
    wget \
    && rm -rf /var/lib/apt/lists/*

# Exec 形式（直接执行，不通过 shell，推荐用于明确路径）
RUN ["apt-get", "install", "-y", "curl"]

```

建议在一条 RUN 中链接多条命令，减少层数：

```dockerfile
# 不推荐：产生多个层
RUN apt-get update
RUN apt-get install -y curl
RUN rm -rf /var/lib/apt/lists/*

# 推荐：一层完成，且清理缓存
RUN apt-get update \
    && apt-get install -y curl \
    && rm -rf /var/lib/apt/lists/*

```

#### COPY vs ADD

| 特性        | COPY | ADD         |
| --------- | ---- | ----------- |
| 复制本地文件    | 支持   | 支持          |
| 支持 URL 下载 | 不支持  | 支持          |
| 自动解压 tar  | 不支持  | 支持（本地 tar）  |
| 可预测性      | 高    | 低（行为复杂）     |
| 推荐程度      | 推荐   | 不推荐（除非需要解压） |

```dockerfile
# COPY 用法
COPY package.json /app/
COPY src/ /app/src/
COPY --chown=appuser:appgroup . /app/   # 指定所有者

# ADD 用法（仅在需要解压时使用）
ADD https://example.com/file.tar.gz /tmp/   # 下载并解压
ADD archive.tar.gz /app/                    # 自动解压

```

#### ENV 与 ARG

```dockerfile
# ENV：运行时环境变量，容器运行后也可见
ENV NODE_ENV=production
ENV PORT=3000 HOST=0.0.0.0   # 一行设置多个

# ARG：构建参数，仅在 docker build 时可用
ARG BUILD_DATE
ARG VERSION=1.0.0

# ARG 和 ENV 结合使用
ARG APP_VERSION=latest
ENV APP_VERSION=${APP_VERSION}   # 将构建参数转为环境变量

```

```bash
# 构建时传入 ARG 值
docker build --build-arg BUILD_DATE=$(date -u +%Y-%m-%dT%H:%M:%SZ) \
             --build-arg VERSION=2.0.0 \
             -t myapp:2.0.0 .

```

#### HEALTHCHECK

| 参数              | 类型       | 默认值 | 说明                    |
| --------------- | -------- | --- | --------------------- |
| \--interval     | duration | 30s | 两次检查的间隔时间             |
| \--timeout      | duration | 30s | 单次检查的超时时间             |
| \--start-period | duration | 0s  | 容器启动后等待多久才开始检查        |
| \--retries      | int      | 3   | 连续失败多少次才判定为 unhealthy |

```dockerfile
# HTTP 健康检查
HEALTHCHECK --interval=30s --timeout=10s --start-period=10s --retries=3 \
  CMD curl -f http://localhost:3000/health || exit 1

# 禁用基础镜像的健康检查
HEALTHCHECK NONE

```

### CMD 与 ENTRYPOINT 的关系

| 组合方式                    | ENTRYPOINT | CMD                  | 实际执行命令               |
| ----------------------- | ---------- | -------------------- | -------------------- |
| 只有 CMD                  | 无          | \["node", "app.js"\] | node app.js          |
| 只有 ENTRYPOINT           | \["node"\] | 无                    | node                 |
| 两者都有                    | \["node"\] | \["app.js"\]         | node app.js          |
| docker run 附加参数         | \["node"\] | \["app.js"\]         | node <你的参数>（CMD 被覆盖） |
| docker run --entrypoint | <被覆盖>      | \["app.js"\]         | <新entrypoint> app.js |

最佳实践：

- ENTRYPOINT 定义固定的可执行程序
- CMD 提供默认参数，允许用户在 `docker run` 时覆盖
- 两者都应使用 exec 形式（JSON 数组），避免 shell 形式（PID 1 问题）

```dockerfile
# 推荐写法：exec 形式
ENTRYPOINT ["nginx"]
CMD ["-g", "daemon off;"]

# 不推荐：shell 形式（nginx 不是 PID 1，无法接收信号）
ENTRYPOINT nginx -g "daemon off;"

```

### .dockerignore 文件

类似 `.gitignore`，防止不必要的文件进入构建上下文，加快构建速度：

```
# .dockerignore

# 版本控制
.git
.gitignore

# Node.js
node_modules
npm-debug.log

# 构建产物
dist
build
*.log

# 环境配置
.env
.env.*
*.env

# IDE 配置
.idea
.vscode
*.swp

# 文档
README.md
docs/

# 测试
__tests__
*.test.js
coverage/

# Docker 相关（避免递归）
Dockerfile*
docker-compose*
.dockerignore

```

### 构建命令 docker build

```
docker build [OPTIONS] PATH | URL | -

```

| 参数            | 类型     | 默认值          | 说明                  |
| ------------- | ------ | ------------ | ------------------- |
| \-t, \--tag   | string | \-           | 镜像名称和标签，格式 name:tag |
| \-f, \--file  | string | ./Dockerfile | 指定 Dockerfile 文件路径  |
| \--no-cache   | flag   | false        | 不使用缓存，全量重建所有层       |
| \--build-arg  | string | \-           | 传入 ARG 变量值          |
| \--platform   | string | 当前平台         | 目标平台，如 linux/amd64  |
| \--target     | string | \-           | 多阶段构建中指定目标阶段名称      |
| \--progress   | string | auto         | 输出格式：auto、plain、tty |
| \--pull       | flag   | false        | 总是拉取最新基础镜像          |
| \--label      | string | \-           | 为镜像设置元数据            |
| \-q, \--quiet | flag   | false        | 静默模式，只输出镜像 ID       |

```bash
# 基本构建
docker build -t myapp:1.0 .

# 指定 Dockerfile
docker build -f docker/Dockerfile.prod -t myapp:prod .

# 不使用缓存（排查问题时使用）
docker build --no-cache -t myapp:1.0 .

# 传入构建参数
docker build --build-arg VERSION=2.0 --build-arg ENV=prod -t myapp:2.0 .

# 多阶段构建，只构建到 builder 阶段
docker build --target builder -t myapp:builder .

# 跨平台构建（需要 buildx）
docker buildx build --platform linux/amd64,linux/arm64 -t myapp:1.0 --push .

```

---

## 7\. Docker Compose 基础

### 安装

Docker Compose 有两个版本：

| 版本         | 命令             | 说明                                   |
| ---------- | -------------- | ------------------------------------ |
| V2 插件版（推荐） | docker compose | 集成在 Docker CLI 中，Docker Desktop 默认包含 |
| V1 独立工具（旧） | docker-compose | 独立安装的 Python 脚本，已停止维护                |

```bash
# 验证 Compose 插件版本
docker compose version

# Linux 手动安装 Compose 插件（如果没有）
sudo apt-get install docker-compose-plugin

# 查看 V1 版本（如果已安装）
docker-compose --version

```

### 基本 docker-compose.yml 结构

以下是一个包含 Nginx、Node.js 应用、PostgreSQL 的完整示例：

```yaml
# docker-compose.yml

version: "3.9"   # Compose 文件规范版本

services:
  # Nginx 反向代理
  nginx:
    image: nginx:alpine
    container_name: proxy
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - ./nginx/nginx.conf:/etc/nginx/nginx.conf:ro
      - ./nginx/ssl:/etc/nginx/ssl:ro
    depends_on:
      - app
    networks:
      - frontend
    restart: unless-stopped

  # Node.js 应用
  app:
    build:
      context: .
      dockerfile: Dockerfile
      args:
        NODE_ENV: production
    container_name: node-app
    environment:
      - NODE_ENV=production
      - DATABASE_URL=postgresql://admin:secret@db:5432/mydb
      - PORT=3000
    env_file:
      - .env
    volumes:
      - app-logs:/app/logs
    depends_on:
      db:
        condition: service_healthy
    networks:
      - frontend
      - backend
    restart: unless-stopped
    healthcheck:
      test: ["CMD", "wget", "-qO-", "http://localhost:3000/health"]
      interval: 30s
      timeout: 10s
      retries: 3
      start_period: 10s

  # PostgreSQL 数据库
  db:
    image: postgres:15-alpine
    container_name: postgres
    environment:
      POSTGRES_USER: admin
      POSTGRES_PASSWORD: secret
      POSTGRES_DB: mydb
    volumes:
      - pgdata:/var/lib/postgresql/data
      - ./db/init.sql:/docker-entrypoint-initdb.d/init.sql:ro
    ports:
      - "5432:5432"   # 开发环境暴露，生产环境可移除
    networks:
      - backend
    restart: unless-stopped
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U admin -d mydb"]
      interval: 10s
      timeout: 5s
      retries: 5

# 具名卷（由 Docker 管理，数据持久化）
volumes:
  pgdata:
    driver: local
  app-logs:
    driver: local

# 自定义网络
networks:
  frontend:
    driver: bridge
  backend:
    driver: bridge
    internal: true   # 内部网络，不能访问外网

```

### 常用 Compose 命令

| 命令                              | 说明              |
| ------------------------------- | --------------- |
| docker compose up               | 创建并启动所有服务（前台运行） |
| docker compose up -d            | 创建并启动所有服务（后台运行） |
| docker compose up --build       | 构建镜像后启动（有代码变更时） |
| docker compose up service\_name | 只启动指定服务         |
| docker compose down             | 停止并删除容器、网络      |
| docker compose down -v          | 停止并删除容器、网络、卷    |
| docker compose down --rmi all   | 停止并删除容器、镜像      |
| docker compose ps               | 查看服务状态          |
| docker compose logs             | 查看所有服务日志        |
| docker compose logs -f app      | 跟踪指定服务日志        |
| docker compose exec app bash    | 进入指定服务容器        |
| docker compose build            | 构建所有服务的镜像       |
| docker compose build app        | 只构建指定服务         |
| docker compose pull             | 拉取所有服务镜像        |
| docker compose restart          | 重启所有服务          |
| docker compose restart app      | 重启指定服务          |
| docker compose stop             | 停止所有服务（不删除容器）   |
| docker compose start            | 启动已停止的服务        |
| docker compose rm               | 删除已停止的服务容器      |
| docker compose config           | 验证并输出最终配置       |
| docker compose run app npm test | 一次性运行命令（新建容器）   |
| docker compose scale app=3      | 扩展服务实例数量        |

```bash
# 启动所有服务（后台）
docker compose up -d

# 有代码变更时重新构建并启动
docker compose up -d --build

# 查看实时日志
docker compose logs -f

# 进入 app 服务容器
docker compose exec app bash

# 在 app 容器中运行迁移（临时容器，结束自动删除）
docker compose run --rm app npm run migrate

# 停止所有服务但保留容器和卷
docker compose stop

# 彻底清理（删除容器、网络、卷）
docker compose down -v

```

---

## 8\. 数据管理基础

### 三种挂载方式对比

| 类型          | 命令行语法                          | 数据位置        | 适用场景          |
| ----------- | ------------------------------ | ----------- | ------------- |
| Volume（具名卷） | \-v myvolume:/app/data         | Docker 管理目录 | 生产数据持久化（推荐）   |
| Bind Mount  | \-v /host/path:/container/path | 宿主机任意路径     | 开发时同步代码、配置    |
| tmpfs       | \--tmpfs /app/tmp              | 宿主机内存       | 临时数据，不持久化，高性能 |

具名卷 vs 匿名卷 vs Bind Mount 详细对比：

| 特性   | 具名卷                            | 匿名卷                 | Bind Mount                     |
| ---- | ------------------------------ | ------------------- | ------------------------------ |
| 名称   | 自定义，如 pgdata                   | Docker 自动生成 UUID    | 宿主机路径                          |
| 创建方式 | docker volume create 或 Compose | docker run -v /path | docker run -v /host:/container |
| 生命周期 | 独立于容器，容器删除后保留                  | 容器删除时默认保留（\-v 删除）   | 由宿主机管理                         |
| 备份难度 | 简单                             | 困难（难以找到路径）          | 直接操作宿主机路径                      |
| 推荐程度 | 生产环境推荐                         | 不推荐                 | 开发环境推荐                         |

### Volume 管理命令

```bash
# 创建具名卷
docker volume create mydata

# 列出所有卷
docker volume ls

# 查看卷详细信息
docker volume inspect mydata

# 删除指定卷
docker volume rm mydata

# 清理未使用的卷（谨慎）
docker volume prune

# 在 docker run 中使用具名卷
docker run -v mydata:/app/data myapp

# 只读挂载
docker run -v mydata:/app/data:ro myapp

```

### \--mount 语法（更清晰的挂载方式）

```bash
# Volume 挂载
docker run --mount type=volume,source=mydata,target=/app/data myapp

# Bind Mount
docker run --mount type=bind,source=/host/path,target=/container/path myapp

# 只读 Bind Mount
docker run --mount type=bind,source=/host/config,target=/etc/app,readonly myapp

# tmpfs
docker run --mount type=tmpfs,target=/app/tmp,tmpfs-size=100m myapp

```

### Compose 中声明卷

```yaml
services:
  app:
    volumes:
      - pgdata:/var/lib/postgresql/data   # 具名卷
      - ./src:/app/src                    # Bind Mount
      - /tmp/cache:/app/cache             # 绑定到宿主机临时目录

volumes:
  pgdata:              # 由 Docker 管理
  external-vol:
    external: true     # 使用已存在的外部卷（不由 Compose 创建）

```

---

## 9\. 网络基础

### 默认网络模式对比

| 模式             | 说明                         | 使用场景                              |
| -------------- | -------------------------- | --------------------------------- |
| bridge         | 默认模式，容器有独立网络栈，通过虚拟网桥与宿主机通信 | 单机多容器通信                           |
| host           | 容器共享宿主机网络命名空间，无端口映射，性能最高   | 高性能网络，Linux 专用（macOS/Windows 不支持） |
| none           | 容器无网络接口，完全隔离               | 安全敏感的无网络场景                        |
| container:NAME | 共享另一个容器的网络命名空间             | sidecar 模式                        |
| 自定义 bridge     | 用户创建的桥接网络，支持容器名 DNS 解析     | 多容器应用（推荐）                         |

注意：默认 bridge 网络不支持通过容器名互访（需 IP），自定义 bridge 网络支持容器名 DNS 解析。

### 常用网络命令

```bash
# 创建自定义网络
docker network create mynet

# 创建指定子网的网络
docker network create --subnet 172.20.0.0/16 mynet

# 列出所有网络
docker network ls

# 查看网络详细信息
docker network inspect mynet

# 删除网络
docker network rm mynet

# 清理未使用的网络
docker network prune

# 将运行中的容器加入网络
docker network connect mynet my-nginx

# 将容器从网络移除
docker network disconnect mynet my-nginx

# 运行时指定网络
docker run --network mynet --name app1 myapp

# 同一自定义网络中，容器可通过容器名互访
docker run --network mynet --name db postgres
docker run --network mynet --name app myapp
# app 容器内可通过 db:5432 访问数据库

```

---

## 10\. 常见问题与踩坑

### 问题 1：docker run 没加 -d 导致终端卡住

**现象**：运行 `docker run nginx` 后终端被占用，Ctrl+C 会停止容器。

**解决**：后台运行加 `-d` 参数。

```bash
# 错误方式
docker run nginx

# 正确方式
docker run -d nginx

# 如果需要查看日志
docker logs -f <container_id>

```

### 问题 2：端口冲突

**现象**：`docker run -p 80:80 nginx` 报错 `Bind for 0.0.0.0:80 failed: port is already allocated`。

**解决**：

```bash
# 查找占用端口的进程（Linux/macOS）
lsof -i :80

# Windows
netstat -ano | findstr :80

# 方案一：停止占用端口的进程
# 方案二：映射到其他端口
docker run -d -p 8080:80 nginx

```

### 问题 3：容器内无法连接宿主机服务

**现象**：容器内访问 `localhost:3000` 连接失败（如容器内应用访问宿主机上的数据库）。

**原因**：容器有独立网络命名空间，`localhost` 是容器自身，不是宿主机。

**解决**：

```bash
# macOS 和 Windows Docker Desktop：使用特殊域名
host.docker.internal

# Linux：使用 docker0 网关 IP（通常是 172.17.0.1）
# 或者运行时加 --add-host
docker run --add-host=host.docker.internal:host-gateway myapp

# 在代码中
# 错误：localhost:5432
# 正确：host.docker.internal:5432

```

### 问题 4：容器删除后数据丢失

**现象**：删除容器后，容器内写入的数据全部消失。

**原因**：容器层是临时可写层，容器删除时一并删除。

**解决**：必须使用卷或 Bind Mount 持久化数据。

```bash
# 数据库必须挂载卷
docker run -d \
  -v pgdata:/var/lib/postgresql/data \
  postgres:15

# 验证数据持久化
docker rm postgres-container
docker run -d \
  -v pgdata:/var/lib/postgresql/data \
  postgres:15
# 数据仍然存在

```

### 问题 5：镜像层缓存导致全量重建

**现象**：修改一行代码后，`docker build` 需要重新安装所有依赖，耗时很长。

**原因**：COPY 的顺序不当，源代码与依赖文件一起复制，任何改动都使缓存失效。

**解决**：利用层缓存，先复制依赖描述文件，安装依赖，再复制源代码。

```dockerfile
# 错误顺序：每次代码变更都重新 npm install
COPY . .
RUN npm install

# 正确顺序：只有 package.json 变更才重新 npm install
COPY package*.json ./
RUN npm ci
COPY . .

```

### 问题 6：时区问题

**现象**：容器内日志时间比实际时间少 8 小时，应用输出 UTC 时间。

**原因**：Docker 容器默认使用 UTC 时区。

**解决**：

```bash
# 方式一：运行时设置环境变量
docker run -e TZ=Asia/Shanghai myapp

# 方式二：Dockerfile 中设置
ENV TZ=Asia/Shanghai
RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone

# 方式三：挂载宿主机时区文件（Linux）
docker run -v /etc/localtime:/etc/localtime:ro myapp

```

### 问题 7：Linux bind mount 权限不匹配

**现象**：容器无法读写 bind mount 的文件，报 Permission denied。

**原因**：容器内进程的 UID/GID 与宿主机文件所有者不匹配。

**解决**：

```dockerfile
# 方式一：在 Dockerfile 中创建与宿主机 UID 匹配的用户
ARG UID=1000
RUN adduser --uid ${UID} --disabled-password appuser
USER appuser

```

```bash
# 方式二：运行时指定用户（匹配宿主机当前用户 UID）
docker run -u $(id -u):$(id -g) -v $(pwd):/app myapp

# 方式三：修改宿主机目录权限（不推荐，影响安全）
chmod -R 777 ./data

```

### 常用清理命令汇总

```bash
# 清理停止的容器
docker container prune

# 清理未使用的镜像
docker image prune

# 清理未使用的镜像（包括有 tag 的）
docker image prune -a

# 清理未使用的卷（谨慎！）
docker volume prune

# 清理未使用的网络
docker network prune

# 一键清理所有未使用的资源（容器、镜像、网络、构建缓存）
docker system prune

# 包括卷的全面清理（极度谨慎）
docker system prune --volumes

# 查看 Docker 磁盘占用
docker system df

```

---

---

## 最佳实践

**Dockerfile 指令按变化频率从低到高排列**：变化少的指令（`FROM`、`RUN apt install`）放在前面，变化多的（`COPY . .`、`RUN pip install -r requirements.txt`）放在后面，充分利用构建缓存，避免每次修改代码都重新安装依赖。

```dockerfile
FROM python:3.12-slim
# 先安装依赖（很少变化）
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
# 再复制代码（经常变化）
COPY . .
CMD ["python", "app.py"]

```

**使用 `.dockerignore` 减小构建上下文大小**：`COPY . .` 会把整个构建目录发送给 Docker daemon，`.git`、`node_modules`、`__pycache__`、`.env` 等无需进入镜像的内容应写入 `.dockerignore`，避免镜像体积膨胀和构建变慢。

**容器内不要以 root 用户运行**：默认 root 用户在容器逃逸时具有主机 root 权限，存在安全风险。在 Dockerfile 末尾创建非 root 用户并切换。

```dockerfile
RUN useradd -m appuser
USER appuser

```

**一个容器只跑一个进程**：不要在单个容器中同时运行 Web 服务 + 数据库 + Nginx，违反单一职责使容器难以扩展、监控和重启。多进程应拆分为多个容器，用 `docker compose` 协调。

**用多阶段构建减小生产镜像体积**：编译/构建阶段只在第一个 `FROM` 中完成，最终镜像只包含运行所需的产物。Go / Node / Java 项目镜像体积可减小 10 倍以上。

```dockerfile
FROM node:20 AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build

FROM nginx:alpine
COPY --from=builder /app/dist /usr/share/nginx/html

```

---

## 常见陷阱

### 陷阱：每次修改代码都触发依赖重新安装

**现象：** `docker build` 时，即使只改了一行业务代码，`npm install` 或 `pip install` 也重新执行，构建速度极慢。

**原因：** `COPY . .` 放在安装依赖之前，任何文件变化都会使缓存失效，导致后续所有层重新执行。

**解决：** 先只 COPY 依赖文件（`package.json`、`requirements.txt`），安装完成后再 COPY 其余代码。

### 陷阱：容器内修改文件后重启丢失

**现象：** 在运行中的容器内创建了文件或修改了数据库，容器重启后数据消失。

**原因：** 容器的可写层（writable layer）在容器删除或重新创建时被丢弃，容器内的文件系统不持久化。

**解决：** 使用 `docker volume` 将需要持久化的目录挂载为卷，或用 `-v /host/path:/container/path` 绑定挂载。

```bash
docker run -v db_data:/var/lib/postgresql/data postgres

```

### 陷阱：镜像中包含敏感信息（密码、密钥）

**现象：** 将密钥写入 Dockerfile 的 `ENV` 或 `RUN` 层，构建后任何人 `docker history` 都能看到明文。

**原因：** Dockerfile 中每一层的内容都保存在镜像的 layer 元数据中，即使在后续层删除了该 ENV，仍可通过历史层读取。

**解决：** 绝不在 Dockerfile 中硬编码密钥；运行时通过环境变量（`docker run -e`）或 Docker Secrets 注入，配合 `.dockerignore` 排除 `.env` 文件。

---

## 参见

- [Docker中级指南](https://blog.vercanti.com/docker-zhong-ji-zhi-nan/)
- [Docker高级指南](https://blog.vercanti.com/docker-gao-ji-zhi-nan/)
- [Docker Compose完全指南](https://blog.vercanti.com/docker-compose-wan-quan-zhi-nan/)
- [Kubernetes基础](https://blog.vercanti.com/kubernetes-ji-chu/)