Docker 用于将应用及其运行环境封装为容器,实现环境隔离和一致部署。
Docker Compose 用于定义和管理由多个容器组成的应用。
1. 核心概念
Dockerfile
↓ docker build
Image(镜像)
↓ docker run
Container(容器)
| 概念 | 作用 |
|---|---|
| Dockerfile | 描述镜像如何构建 |
| Image | 只读镜像模板,包含应用和依赖 |
| Container | 镜像的运行实例,具有独立可写层 |
| Volume | 持久化数据 |
| Network | 容器间通信 |
| Registry | 镜像存储与分发服务 |
| Docker Compose | 管理多容器应用 |
一个镜像可以创建多个容器。容器之间默认不共享自己的可写文件层。
容器停止不等于数据消失;但删除容器时,其可写层通常会一起删除,因此数据库需要持久化存储。
2. 构建镜像
docker build -t sorivel-backend:1.0.0 .
参数:
| 参数 | 作用 |
|---|---|
-t | 指定镜像名称及 Tag |
. | Build Context,当前目录 |
-f Dockerfile.prod | 指定 Dockerfile |
--build-arg KEY=value | 传递构建参数 |
--no-cache | 不使用已有构建缓存 |
--pull | 尝试拉取较新的基础镜像 |
--platform linux/amd64 | 指定目标平台 |
--target prod | 指定多阶段构建目标 |
例如:
docker build \
-f Dockerfile.prod \
-t sorivel-backend:1.2.0 \
--build-arg APP_VERSION=1.2.0 \
.
Build Context
docker build . 会把当前目录作为构建上下文。
Dockerfile 的 COPY 通常只能读取构建上下文中未被忽略的文件。
例如:
COPY ./src /app/src
./src 相对于 Build Context,而不是必然相对于 Dockerfile 所在目录。
.dockerignore
非常建议配置:
.git
node_modules
.venv
__pycache__
.env
.env.*
dist
*.log
作用:
- 减少构建上下文。
- 加快构建。
- 减少不必要的缓存失效。
- 降低敏感文件意外进入镜像的风险。
.dockerignore 不等于安全密钥管理工具,仍然需要正确管理 Secrets。
3. 创建和运行容器
docker run -d \
--name myapp \
-p 8000:8000 \
--env-file .env \
myapp:1.0
| 参数 | 作用 |
|---|---|
--name | 容器名称 |
-p 8000:8000 | 宿主机端口映射到容器端口 |
-d | 后台运行 |
--rm | 容器退出后自动删除 |
-e | 设置环境变量 |
--env-file | 加载环境变量文件 |
-v | 挂载 Volume 或 Bind Mount |
--network | 指定容器网络 |
--restart | 重启策略 |
端口映射
-p 8000:8000
格式:
Host Port : Container Port
如果只允许宿主机本地访问:
-p 127.0.0.1:8000:8000
比直接绑定所有宿主机网络接口更适合不需要公开访问的开发服务。
覆盖启动命令
docker run --rm myapp:1.0 python manage.py shell
这里的命令会覆盖镜像的 CMD;如果镜像配置了 ENTRYPOINT,参数组合行为需要一并考虑。
4. Dockerfile 常见指令
| 指令 | 作用 |
|---|---|
FROM | 指定基础镜像 |
WORKDIR | 指定工作目录 |
COPY | 复制文件 |
ADD | 增强型复制,可处理部分归档和远程资源 |
RUN | 构建镜像时执行命令 |
CMD | 容器默认命令或参数 |
ENTRYPOINT | 容器默认入口程序 |
ENV | 设置环境变量 |
ARG | 构建参数 |
EXPOSE | 声明容器监听端口 |
USER | 指定执行用户 |
VOLUME | 声明挂载目录 |
LABEL | 镜像元数据 |
HEALTHCHECK | 容器健康检查 |
SHELL | 修改 Shell 形式指令使用的默认 Shell |
STOPSIGNAL | 设置容器停止信号 |
Dockerfile 示例
FROM python:3.12-slim
WORKDIR /app
ENV PYTHONDONTWRITEBYTECODE=1 \
PYTHONUNBUFFERED=1
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY . .
EXPOSE 8000
CMD [
"gunicorn",
"config.asgi:application",
"-k",
"uvicorn_worker.UvicornWorker",
"--bind",
"0.0.0.0:8000"
]
需要在依赖中安装 Gunicorn、Uvicorn 和对应 Worker 包。
CMD 与 ENTRYPOINT
ENTRYPOINT ["python"]
CMD ["manage.py", "runserver"]
组合后的默认命令:
python manage.py runserver
ENTRYPOINT:定义主程序。CMD:提供默认命令或参数。
两者都可以被运行参数覆盖,但覆盖方式不同。
一般推荐使用 JSON 数组形式(Exec Form),以便正确处理进程信号。
ARG 与 ENV
ARG APP_VERSION=dev
ENV APP_ENV=production
区别:
ARG:构建阶段使用,默认不作为容器环境变量保留。ENV:构建阶段和容器运行阶段都可以使用,并保留在镜像配置中。
不要使用 ARG 或 ENV 保存构建密钥。
5. Docker Image Layers 与 Build Cache
Docker 镜像由多个层组成,BuildKit 可以缓存构建步骤的结果。
例如:
COPY requirements.txt .
RUN pip install -r requirements.txt
COPY . .
如果仅修改 Python 业务代码,且依赖文件不变,Docker 通常可以复用之前安装依赖的构建缓存。
而:
COPY . .
RUN pip install -r requirements.txt
代码变化可能使依赖安装步骤的缓存更容易失效。
缓存优化原则
先复制变化频率低的依赖描述文件,再安装依赖,最后复制业务代码。
BuildKit Cache Mount
可以为包管理器提供构建缓存:
RUN --mount=type=cache,target=/root/.cache/pip \
pip install -r requirements.txt
这种方式允许复用 Pip 下载缓存,不等于将缓存目录永久写入最终镜像。
6. 多阶段构建(Multi-stage Build)
多阶段构建用于将构建依赖与最终运行环境分开。
典型 React + Vite:
FROM node:24-alpine AS builder
WORKDIR /app
RUN corepack enable
COPY package.json pnpm-lock.yaml ./
RUN pnpm install --frozen-lockfile
COPY . .
RUN pnpm build
FROM nginx:alpine
COPY --from=builder /app/dist /usr/share/nginx/html
Node Builder
↓
pnpm build
↓
dist/
↓ COPY --from
Nginx Image
最终镜像不需要包含 Node.js 项目的全部构建依赖。
COPY --chown
COPY --chown=appuser:appuser . /app
可在复制文件时设置所有者,适合非 Root 用户运行的容器。
7. Volume 与 Bind Mount
Named Volume
Docker 管理的数据卷:
docker volume create postgres_data
挂载:
docker run \
-v postgres_data:/var/lib/postgresql/data \
postgres:17
这里的路径适用于 PostgreSQL 17 官方镜像。
Volume 不会因为容器正常删除而自动删除,适合数据库持久化。
Bind Mount
把宿主机目录挂载进容器:
docker run \
-v ./src:/app/src \
myapp:1.0
Host ./src
↕
Container /app/src
适合本地开发和配置文件挂载。
区别
| 类型 | 管理者 | 典型用途 |
|---|---|---|
| Named Volume | Docker | PostgreSQL 数据 |
| Bind Mount | 宿主机文件系统 | 源码、配置文件 |
| tmpfs | 内存 | 临时敏感或高频数据 |
注意:生产环境中不要随意将整个源码目录 Bind Mount 到 /app,否则可能覆盖镜像构建时写入的文件。
8. Docker Network
容器可以通过 Docker 网络通信。
Docker Compose 默认会为项目创建网络,同一网络内的容器可以通过服务名访问彼此。
例如:
backend
↓ db:5432
PostgreSQL
backend
↓ redis:6379
Redis
Django 数据库配置通常使用:
DB_HOST=db
DB_PORT=5432
而不是:
DB_HOST=localhost
因为在容器中,localhost 通常指容器本身。
ports 与 expose
ports:
- "8000:8000"
发布到宿主机。
expose:
- "8000"
声明容器端口,但不发布到宿主机。
对于同一 Compose 网络的服务通信,通常即使不写 expose,也可以直接访问容器实际监听的端口。
9. Docker Compose
Compose 将多个服务定义在一个 YAML 文件中。
例如:
services:
backend:
build:
context: ./backend
dockerfile: Dockerfile
target: prod
args:
APP_VERSION: "1.2.0"
image: sorivel-backend:1.2.0
command: >
gunicorn config.asgi:application
-k uvicorn_worker.UvicornWorker
--bind 0.0.0.0:8000
env_file:
- ./backend/.env
depends_on:
db:
condition: service_healthy
redis:
condition: service_started
restart: unless-stopped
networks:
- backend_network
- frontend_network
db:
image: postgres:17
environment:
POSTGRES_DB: app
POSTGRES_USER: app
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:?Password required}
volumes:
- postgres_data:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U app"]
interval: 5s
timeout: 5s
retries: 5
start_period: 20s
networks:
- backend_network
redis:
image: redis:7
networks:
- backend_network
volumes:
postgres_data:
networks:
backend_network:
frontend_network:
这里是结构示例,实际项目还需要按需配置 Nginx、静态资源、Redis 持久化和密钥管理。
重要字段
| 字段 | 作用 |
|---|---|
services | 定义服务 |
image | 指定镜像名称 |
build | 指定构建方式 |
command | 覆盖 Dockerfile CMD |
entrypoint | 覆盖 Dockerfile ENTRYPOINT |
environment | 注入容器环境变量 |
env_file | 从文件注入容器环境变量 |
ports | 发布宿主机端口 |
volumes | 配置挂载 |
depends_on | 定义依赖启动顺序及条件 |
healthcheck | 定义健康检查 |
restart | 容器重启策略 |
networks | 指定容器网络 |
depends_on
depends_on:
db:
condition: service_healthy
表示启动 Backend 前先等待 DB 的 Health Check 通过。
但不会自动保证数据库在整个应用运行期间始终可用。Django 仍然需要合理处理数据库连接失败和重连。
10. Environment Variables
这里需要区分两种机制。
Compose 插值变量
environment:
APP_PORT: ${APP_PORT:-8000}
Compose 从 Shell、项目 .env 等来源解析变量。
其中:
${APP_PORT:-8000}:没有设置或为空时使用 8000。${VAR:?Required}:未设置或为空时报错。
env_file
env_file:
- ./backend/.env
将文件中的变量注入 Backend 容器。
重要区别
Compose .env
↓
Compose 配置变量插值
services.backend.env_file
↓
Backend 容器环境变量
两者不是同一回事。
同名变量同时在 environment 和 env_file 指定时,显式的 environment 具有更高优先级。
可以检查:
docker compose config
但这个命令可能输出解析后的敏感信息,不应随意把完整结果提交到日志或公开分享。
11. 容器生命周期
Created
↓
Running
↓
Stopped / Exited
↓
Removed
容器主进程退出,容器通常也会退出。
例如:
CMD ["gunicorn", "..."]
Gunicorn 是主进程;它退出后,容器随之结束。
Restart Policy
restart: unless-stopped
表示容器退出后由 Docker 按相应策略尝试重启,除非已被手动停止。
它不是 Health Check 失败后必然自动重启容器的保证。
PID 1 与信号
容器停止时通常向主进程发送终止信号,然后等待一段时间。
因此建议:
- 使用 Exec Form 启动应用。
- 确保应用支持优雅退出。
- 不要让不必要的 Shell 阻挡终止信号。
- 必要时使用
init: true处理子进程回收。
12. Docker Compose 常用命令
状态与日志
docker compose ps
docker compose logs
docker compose logs -f backend
docker stats
启动与停止
# 启动
docker compose up -d
# 停止但保留容器
docker compose stop
# 停止并删除服务容器和网络
docker compose down
# 同时删除 Compose 管理的相关 Volume
docker compose down -v
生产数据库所在的 Named Volume 不应随意执行 down -v。
构建与更新
# 只构建 Backend 镜像
docker compose build backend
# 不使用构建缓存
docker compose build --no-cache backend
# 构建并启动
docker compose up -d --build
# 只更新 Backend 服务
docker compose up -d --build backend
# 强制重建容器
docker compose up -d --force-recreate backend
如果修改了 Dockerfile 或镜像内部的源码,应先构建新镜像再启动。
如果只修改了通过 env_file 注入的环境变量,可以重新创建容器:
docker compose up -d --force-recreate backend
如果修改的是 Compose 配置:
docker compose up -d
通常会识别配置变化并按需重新创建对应服务。
进入容器
docker compose exec backend sh
docker compose exec backend python manage.py shell
docker compose exec backend python manage.py migrate
并非所有容器镜像都包含 Bash,特别是 Alpine 或精简镜像,因此 sh 通常更通用。
exec 与 run
docker compose exec backend python manage.py shell
在已有的运行中容器执行命令。
docker compose run --rm backend python manage.py shell
创建一个新的临时服务容器执行命令。
后者适合一次性管理任务,但其网络、端口及生命周期行为与 exec 不完全相同。
13. 镜像仓库(Registry)
常见镜像仓库:
- Docker Hub。
- GitHub Container Registry(GHCR)。
- AWS ECR。
镜像名称示例:
ghcr.io/username/sorivel-backend:1.0.0
构建与推送:
docker build \
-t ghcr.io/username/sorivel-backend:1.0.0 \
.
docker push ghcr.io/username/sorivel-backend:1.0.0
拉取:
docker pull ghcr.io/username/sorivel-backend:1.0.0
多架构构建
docker buildx build \
--platform linux/amd64,linux/arm64 \
-t ghcr.io/username/sorivel-backend:1.0.0 \
--push \
.
可以构建并推送多个 CPU 架构对应的镜像。
需要使用支持目标平台构建的 Buildx Builder,必要时通过 QEMU 等机制执行跨架构构建。
14. Health Check
Dockerfile:
HEALTHCHECK --interval=30s --timeout=5s \
CMD python -c \
"import urllib.request; urllib.request.urlopen('http://localhost:8000/healthz')"
也可以在 Compose 中定义:
healthcheck:
test:
- CMD
- python
- -c
- "import urllib.request; urllib.request.urlopen('http://localhost:8000/healthz')"
interval: 30s
timeout: 5s
retries: 3
Health Check 用于判断服务是否能够正常响应。
常见状态:
starting
healthy
unhealthy
注意:容器处于 Running 状态,不代表应用一定 Healthy。
15. Secrets 与安全
不要把密码直接写入 Dockerfile 或提交到 Git 仓库。
尤其避免:
ARG DATABASE_PASSWORD=secret
ENV DATABASE_PASSWORD=secret
构建阶段如果需要访问私有资源,可以使用 BuildKit Secret Mount。
例如:
RUN --mount=type=secret,id=pip_token \
...
对于运行时密钥,可以使用部署平台的 Secret 管理能力,或通过适当权限保护的配置文件注入。
其他建议:
- 尽量使用非 Root 用户运行应用。
- 不向公网暴露 PostgreSQL、Redis 等内部服务端口。
- 使用明确的镜像版本,而不是无条件依赖
latest。 - 定期更新基础镜像。
- 为 PostgreSQL Volume 建立独立备份机制。
16. 数据库操作与备份
执行 Migration:
docker compose exec backend python manage.py migrate
查看 Volume:
docker volume ls
PostgreSQL 导出
docker compose exec -T db \
pg_dump -U app -d app > backup.sql
PostgreSQL 导入
docker compose exec -T db \
psql -U app -d app < backup.sql
这里假设 Compose 服务名为 db,数据库和用户均为 app。
执行导入之前,需要确认目标数据库状态及备份格式。SQL 导入不等于安全、完整的数据库迁移流程。
Volume 不等于 Backup
Volume 负责持久化,但无法防止:
- 错误删除数据。
- 数据库逻辑损坏。
- 磁盘故障。
- 错误 Migration。
- 宿主机丢失。
生产数据库仍然需要定期备份,并验证恢复流程。
17. 清理命令
# 查看镜像
docker images
# 查看容器
docker ps -a
# 查看 Volume
docker volume ls
# 删除指定镜像
docker rmi image_name
# 清理不再使用的构建缓存
docker builder prune
# 清理全部未使用的构建缓存
docker builder prune -a
开发环境完全重置:
docker compose down -v --rmi all --remove-orphans
docker compose up -d --build
该操作具有破坏性,尤其是 -v 会删除 Compose 管理的相关 Volume。不要在包含生产数据的环境中直接执行。
总结
Docker 核心内容可以分为五部分:
| 分类 | 必须掌握的内容 |
|---|---|
| 基础模型 | Dockerfile、Image、Container |
| 构建 | Build Context、Cache、Multi-stage、Buildx |
| 运行环境 | ENV、ARG、CMD、ENTRYPOINT、Health Check |
| 基础设施 | Volume、Bind Mount、Network、Ports |
| 运维 | Docker Compose、Logs、Exec、Backup、Registry |
日常部署的典型流程:
Write Dockerfile
↓
docker compose build
↓
Docker Images
↓
docker compose up -d
↓
Running Containers
↓
Health Check / Logs
↓
Application
最重要的实践原则:
- 镜像负责打包应用,容器负责运行。
- 数据库数据必须持久化,并且要有备份。
- 生产部署尽量避免直接挂载整个源码目录。
- 通过 Docker Network 和服务名进行容器间通信。
- 修改镜像内容需要重新构建并更新容器。
- 不要把敏感信息写入镜像或版本库。