Docker 容器化
Docker 将应用及其依赖打包成镜像(image),运行时实例化为容器(container),实现”一次构建,到处运行”。对 AI 工程而言,Docker 解决了”在我机器上能跑”的经典难题——CUDA 版本、驱动、Python 库版本差异都能封装在镜像内。
本页涵盖镜像分层、Dockerfile 指令、多阶段构建、GPU 容器、网络、数据卷、镜像仓库和安全实践。多容器编排见 Docker Compose 编排。
镜像分层原理
Section titled “镜像分层原理”Docker 镜像由多个**只读层(layer)**组成,每条 Dockerfile 指令生成一层。容器运行时在最上层叠加一个可写层。
💡 分层缓存:如果某层指令没变,Docker 构建时直接复用缓存。因此 Dockerfile 中应把变化频率低的指令放前面(如安装系统依赖),把频繁变化的放后面(如 COPY 源码),以加速构建。
Dockerfile 常用指令
Section titled “Dockerfile 常用指令”| 指令 | 作用 | 示例 |
|---|---|---|
FROM | 指定基础镜像 | FROM nvidia/cuda:12.1.0-runtime-ubuntu22.04 |
WORKDIR | 设置工作目录 | WORKDIR /app |
COPY | 复制本地文件到镜像 | COPY requirements.txt . |
RUN | 构建时执行命令(生成新层) | RUN pip install -r requirements.txt |
ENV | 设置环境变量 | ENV PYTHONUNBUFFERED=1 |
EXPOSE | 声明容器端口 | EXPOSE 8000 |
CMD | 容器默认启动命令(可被覆盖) | CMD ["python", "server.py"] |
ENTRYPOINT | 固定入口命令(不易覆盖) | ENTRYPOINT ["python"] |
ARG | 构建时变量(仅构建阶段可用) | ARG PY_VERSION=3.11 |
HEALTHCHECK | 容器健康检查 | HEALTHCHECK CMD curl -f http://localhost/ |
USER | 指定运行用户 | USER appuser |
COPY vs ADD
Section titled “COPY vs ADD”# COPY:纯复制(推荐,行为可预测)COPY requirements.txt .COPY ./src /app/src
# ADD:额外支持自动解压 tar、远程 URL(不推荐,行为隐晦)ADD archive.tar.gz /opt/ # 自动解压ADD https://example.com/file.txt /tmp/ # 下载远程文件💡 优先用
COPY,只有需要自动解压本地 tar 包时才考虑ADD。
CMD vs ENTRYPOINT
Section titled “CMD vs ENTRYPOINT”# 方式 1:CMD 可被 docker run 后的参数覆盖CMD ["python", "server.py"]# docker run myimage python debug.py ← 覆盖了 CMD
# 方式 2:ENTRYPOINT + CMD 组合(推荐)ENTRYPOINT ["python"]CMD ["server.py"]# docker run myimage ← 执行 python server.py# docker run myimage debug.py ← 执行 python debug.py多阶段构建(Multi-stage Build)
Section titled “多阶段构建(Multi-stage Build)”多阶段构建用一个 Dockerfile 中的多个 FROM 阶段,把编译环境和运行环境分离,大幅减小最终镜像体积。
# ===== 阶段 1:构建阶段(安装编译依赖、编译产物)=====FROM python:3.11-slim AS builder
WORKDIR /buildCOPY requirements.txt .# 安装到指定目录,便于后续拷贝RUN pip install --no-cache-dir --target=/build/libs -r requirements.txt
# ===== 阶段 2:运行阶段(只保留运行所需文件)=====FROM python:3.11-slim AS runtime
WORKDIR /app
# 从构建阶段拷贝已安装的库COPY --from=builder /build/libs /usr/local/lib/python3.11/site-packages/
# 拷贝应用源码COPY ./src /app/src
ENV PYTHONPATH=/app/srcEXPOSE 8000
# 非 root 用户运行(安全最佳实践)RUN useradd -m -u 1000 appuserUSER appuser
CMD ["python", "-m", "src.server"]💡 多阶段构建可以把镜像从 2GB+ 压缩到 300MB 以下,对 CI 推送、服务器拉取速度提升明显。
BuildKit 缓存挂载
Section titled “BuildKit 缓存挂载”Docker 18.09+ 的 BuildKit 支持 RUN --mount=type=cache,在多次构建间共享包管理器缓存:
# syntax=docker/dockerfile:1FROM python:3.11-slim
# pip 缓存跨构建复用(不写入镜像层)RUN --mount=type=cache,target=/root/.cache/pip \ pip install -r requirements.txt
# 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 --no-install-recommends \ curl git && rm -rf /var/lib/apt/lists/*AI 推理服务 Dockerfile 示例
Section titled “AI 推理服务 Dockerfile 示例”以下是一个完整的 GPU 推理服务 Dockerfile,基于 NVIDIA 官方 CUDA 镜像:
# 基于 CUDA 12.1 + cuDNN 的运行时镜像(比 devel 镜像小很多)FROM nvidia/cuda:12.1.1-cudnn8-runtime-ubuntu22.04
# 设置时区、禁止交互式安装ENV DEBIAN_FRONTEND=noninteractive \ TZ=Asia/Shanghai \ PYTHONUNBUFFERED=1 \ PIP_NO_CACHE_DIR=1
# 安装系统依赖(Python 3.11 + 基础工具)RUN apt-get update && apt-get install -y --no-install-recommends \ python3.11 python3-pip python3.11-venv \ curl ca-certificates \ && rm -rf /var/lib/apt/lists/* \ && ln -sf /usr/bin/python3.11 /usr/bin/python
WORKDIR /app
# 先拷贝依赖文件(利用 layer cache)COPY requirements.txt .RUN pip install --no-cache-dir -r requirements.txt
# 再拷贝源码(源码变化频繁,放后面)COPY ./src /app/srcCOPY ./models /app/models
# 声明端口和启动命令EXPOSE 8000HEALTHCHECK --interval=30s --timeout=10s --retries=3 \ CMD curl -f http://localhost:8000/health || exit 1
CMD ["python", "-m", "src.server", "--host", "0.0.0.0", "--port", "8000"]构建与运行:
# 构建镜像(-t 命名,. 表示 Dockerfile 所在目录)docker build -t my-inference:latest .
# 启动 GPU 容器(--gpus all 挂载 GPU)docker run --gpus all -p 8000:8000 -v /data:/data my-inference:latest
# 进入运行中的容器调试docker exec -it <container_id> bashGPU 容器详解
Section titled “GPU 容器详解”NVIDIA Container Toolkit
Section titled “NVIDIA Container Toolkit”GPU 容器依赖 NVIDIA Container Toolkit(原名 nvidia-docker2),它让容器能访问宿主机的 GPU 驱动:
# Ubuntu 安装sudo apt-get install -y nvidia-container-toolkitsudo systemctl restart docker
# 验证 GPU 可用docker run --rm --gpus all nvidia/cuda:12.1.1-base-ubuntu22.04 nvidia-smi--gpus 选项
Section titled “--gpus 选项”# 挂载所有 GPUdocker run --gpus all my-inference
# 挂载指定 GPU(第 0、1 块)docker run --gpus '"device=0,1"' my-inference
# 挂载 2 块 GPU(自动分配)docker run --gpus 2 my-inferenceCUDA 镜像变体
Section titled “CUDA 镜像变体”| 变体 | 大小 | 说明 |
|---|---|---|
base | ~1GB | 最小,只有 CUDA 运行时 |
runtime | ~2GB | base + cuDNN,推荐用于推理 |
devel | ~4GB | runtime + nvcc 编译器,用于需要编译 CUDA 扩展的场景 |
# 推理服务用 runtime(体积小)FROM nvidia/cuda:12.1.1-cudnn8-runtime-ubuntu22.04
# 需要编译 flash-attention 等扩展时用 develFROM nvidia/cuda:12.1.1-cudnn8-devel-ubuntu22.04💡 宿主机只需安装 NVIDIA 驱动(如 535.x),不需要安装完整 CUDA Toolkit。CUDA 库由镜像提供,容器和宿主机通过驱动通信。
Docker 网络
Section titled “Docker 网络”容器默认通过 Docker 创建的虚拟网络通信。理解网络模式是排查容器间连接问题的关键。
| 驱动 | 说明 | 典型场景 |
|---|---|---|
bridge(默认) | 虚拟网桥,容器间隔离,通过端口映射访问宿主机 | 单机多容器 |
host | 容器直接使用宿主机网络(无隔离) | 对网络性能要求高的场景 |
none | 无网络 | 安全隔离、离线计算 |
overlay | 跨主机的虚拟网络 | Docker Swarm / 多主机 |
常用网络操作
Section titled “常用网络操作”# 创建自定义网络(容器间通过容器名互相访问)docker network create mynet
# 在自定义网络中启动容器docker run -d --name api --network mynet my-apidocker run -d --name db --network mynet postgres:16
# api 容器内可以用 "db" 作为主机名连接数据库# psql -h db -U postgres
# 查看网络详情docker network inspect mynet
# 端口映射(宿主机:容器)docker run -p 8000:8000 my-inference # 宿主机 8000 → 容器 8000docker run -p 127.0.0.1:8000:8000 ... # 只绑定 localhost(安全)容器文件系统是临时的,容器删除后数据丢失。数据卷(volume)和绑定挂载(bind mount)用于持久化数据。
Volume vs Bind Mount
Section titled “Volume vs Bind Mount”# 命名卷(Docker 管理,推荐生产使用)docker run -v model_data:/app/models my-inference# 数据存在 Docker 管理的区域,容器删除后卷保留
# 绑定挂载(指定宿主机路径,适合开发)docker run -v /home/alice/code:/app my-inference# 宿主机目录直接映射到容器,修改实时同步
# 只读挂载(防止容器修改数据)docker run -v /data/datasets:/data:ro my-inference
# 推荐的 --mount 语法(更清晰)docker run --mount type=volume,src=model_data,dst=/app/models my-inferencedocker run --mount type=bind,src=/home/alice/code,dst=/app my-inferencedocker volume ls # 列出所有卷docker volume inspect model_data # 查看卷详情docker volume rm model_data # 删除卷(需先移除使用它的容器)docker volume prune # 清理未使用的卷⚠️ 绑定挂载的文件权限问题:容器内进程的 UID/GID 与宿主机不一致时,可能无法读写挂载目录。在 Dockerfile 中创建固定 UID 的用户,或用
--user $(id -u):$(id -g)指定。
镜像仓库与推送
Section titled “镜像仓库与推送”Docker Hub
Section titled “Docker Hub”# 登录 Docker Hubdocker login
# 给镜像打标签(用户名/镜像名:标签)docker tag my-inference:latest alice/my-inference:v1.0
# 推送docker push alice/my-inference:v1.0
# 拉取docker pull alice/my-inference:v1.0私有镜像仓库
Section titled “私有镜像仓库”# 阿里云 ACR、AWS ECR、Harbor 等私有仓库docker login registry.cn-hangzhou.aliyuncs.com -u <user> -p <password>docker tag my-inference:latest registry.cn-hangzhou.aliyuncs.com/myorg/my-inference:v1.0docker push registry.cn-hangzhou.aliyuncs.com/myorg/my-inference:v1.0镜像导出与导入(离线场景)
Section titled “镜像导出与导入(离线场景)”# 导出为 tar 文件(无网络环境下传输)docker save my-inference:latest -o my-inference.tar
# 在目标机器导入docker load -i my-inference.tar常用命令速查
Section titled “常用命令速查”| 操作 | 命令 |
|---|---|
| 列出镜像 | docker images |
| 删除镜像 | docker rmi <image> |
| 清理悬空镜像 | docker image prune |
| 列出容器 | docker ps(运行中)/ docker ps -a(全部) |
| 查看容器日志 | docker logs -f <container> |
| 进入容器 | docker exec -it <container> bash |
| 查看容器资源占用 | docker stats |
| 查看容器详情 | docker inspect <container> |
| 拷贝文件 | docker cp <container>:/app/log.txt . |
| 查看镜像层 | docker history <image> |
安全最佳实践
Section titled “安全最佳实践”-
用非 root 用户运行:在 Dockerfile 末尾创建用户并
USER appuser,避免容器内进程拥有 root 权限。 -
使用
.dockerignore:排除不必要的文件,防止密钥、.git目录被打包进镜像。
.git.gitignore.venv/__pycache__/*.pyc.env*.mdtests/-
使用固定版本标签:
FROM python:3.11-slim而非python:latest;python:3.11.9-slim-bookworm更精确。 -
合并 RUN 指令:减少镜像层数,每条
RUN尽量合并多个命令。
# 好:一条 RUN,最后清理缓存RUN apt-get update && apt-get install -y --no-install-recommends \ curl git vim \ && rm -rf /var/lib/apt/lists/*
# 差:三条 RUN,产生三个层,中间层仍包含缓存RUN apt-get updateRUN apt-get install -y curl git vimRUN rm -rf /var/lib/apt/lists/* # 删了也没用,中间层还在- 敏感信息用 secrets:不要在 Dockerfile 中硬编码密钥,使用
--secret或构建参数 + 环境变量。
# BuildKit secret(不写入镜像层)docker build --secret id=api_key,src=./api_key.txt .
# Dockerfile 中引用# RUN --mount=type=secret,id=api_key \# pip install --index-url $(cat /run/secrets/api_key) private-pkg- 扫描镜像漏洞:用
docker scout(官方)或 Trivy 扫描镜像中的已知漏洞。
docker scout cves my-inference:latest- Dockerfile 分层优化:把
COPY requirements.txt+RUN pip install放在COPY src之前。源码频繁改动,放后面可以利用缓存跳过耗时的 pip 安装。 - 镜像尽量用
-slim或-alpine变体,再用多阶段构建剥离编译工具。生产镜像不需要 gcc、构建头文件。 - GPU 容器要用
--gpus all或 compose 的deploy.resources声明,否则容器内看不到 GPU。 - 敏感信息用环境变量或 secrets 管理,不要写死在 Dockerfile 或 compose 文件里。Docker Compose 支持
secrets字段或.env文件。 - 用
.dockerignore减小构建上下文,加速构建并避免泄露敏感文件。
| 术语 | 英文 | 解释 |
|---|---|---|
| 容器 | container | 镜像的运行实例,隔离的进程环境 |
| 镜像 | image | 容器的只读模板,由多层组成 |
| 多阶段构建 | multi-stage build | 用多个 FROM 指令分离构建和运行环境,减小镜像体积 |
| 层缓存 | layer cache | Docker 构建时复用未变化层,加速重复构建 |
| 命名卷 | named volume | Docker 管理的持久化存储,独立于容器生命周期 |
| 绑定挂载 | bind mount | 将宿主机目录直接映射到容器的挂载方式 |
| 健康检查 | healthcheck | 容器内定期执行的检查命令,判断服务是否正常 |
- 站内关联
- Docker Compose 编排 —— 多容器编排和开发环境管理
- 工程基础概览 —— 本分类的入口页
- AI 后端服务 —— 用 Docker 部署推理 API 的深入实践
- 推荐资源