3、Docker基础回顾:镜像、容器、Dockerfile、常用命令、数据卷与网络基础

在进入 Docker Compose 编排边缘 AI 推理服务之前,必须先夯实 Docker 的核心知识体系。边缘 AI 场景通常涉及 GPU 加速、模型文件分发、推理服务热更新、多节点协同等复杂需求,而这些需求最终都要落实到镜像构建、容器生命周期管理、数据持久化和网络通信这四个基础能力上。本章将系统梳理 Docker 的六大核心主题,帮助你在后续学习 Compose 语法和编排策略时,能够准确理解每个配置项背后的运行时原理。

镜像层与构建 容器运行时 数据与网络 基础镜像 (Ubuntu/CUDA) 依赖层 (Python/PyTorch) 应用层 (推理代码) 可写容器层 (日志/临时文件) 进程隔离 (PID/Namespace) 资源限制 (cgroups) Volume 持久化存储 Bridge/Host 网络模式 端口映射与DNS Dockerfile 构建流程 FROM 基础镜像 COPY 模型/代码 RUN 安装依赖 CMD 启动命令 镜像只读层 + 容器可写层 = 完整运行时视图

3.1 镜像与容器:分层存储与运行时隔离

核心原理

Docker 镜像采用分层存储架构(UnionFS),每一层对应 Dockerfile 中的一条指令。基础镜像层(如 ubuntu:22.04、nvidia/cuda:12.1-runtime)位于最底部,应用层(Python 依赖、推理代码)叠加在上方。所有层均为只读,容器启动时在镜像顶部添加一个可写层。这种设计的核心优势在于:多个容器可以共享相同的镜像层,显著节省磁盘空间并加速镜像拉取。对于边缘 AI 场景,一个包含 CUDA 运行时的镜像往往超过 3GB,分层机制使得同一宿主机上运行多个推理容器时,只需存储一份基础层。

判断方法

使用 docker image inspect 可以查看镜像的层信息,重点关注 RootFS.Layers 字段。使用 docker history 可以查看镜像构建历史,确认每一层的大小和创建指令。当镜像体积异常膨胀时,应检查是否有大量中间层残留,或是否在单层中写入了不必要的缓存文件。

案例拆解:边缘网关上的模型热更新

某智慧园区部署了 8 个边缘节点,每个节点运行一个基于 YOLOv8 的推理容器。初始方案将所有模型文件直接 COPY 进镜像,导致每次模型迭代(每周约 2 次)都需要重新构建并分发约 2.8GB 的镜像。优化后,将模型文件挂载为数据卷,镜像仅包含推理框架和代码(约 1.2GB)。更新模型时只需替换宿主机上的模型文件并重启容器,分发时间从 15 分钟降至 30 秒。代价是需要在宿主机上维护模型文件的版本管理,但整体运维效率显著提升。

经验:在边缘 AI 场景中,将模型权重、配置文件等易变数据与代码分离,是降低带宽消耗和提升更新速度的关键策略。

3.2 Dockerfile 编写:从基础指令到多阶段构建

核心原理

Dockerfile 是镜像构建的蓝图,每条指令生成一个镜像层。常用指令包括 FROM(指定基础镜像)、COPY(复制文件)、RUN(执行命令)、ENV(设置环境变量)、EXPOSE(声明端口)、CMD(默认启动命令)。多阶段构建允许在一个 Dockerfile 中使用多个 FROM,第一阶段编译依赖,第二阶段仅复制编译产物,从而大幅减小最终镜像体积。

操作流程

  1. 选择合适的基础镜像:优先使用官方镜像或带 -slim、-alpine 标签的轻量版本;GPU 场景使用 nvidia/cuda 系列。
  2. 按变更频率排序指令:将不常变化的依赖安装放在前面,频繁修改的代码放在后面,充分利用层缓存。
  3. 合并 RUN 指令:使用 && 连接多个命令,减少镜像层数。
  4. 使用 .dockerignore 排除无关文件(如 .git、__pycache__)。

案例拆解:PyTorch 推理镜像优化

初始 Dockerfile 直接基于 pytorch/pytorch:2.0.1-cuda11.7-cudnn8-runtime,安装推理依赖后镜像体积达 6.2GB。优化后采用多阶段构建:第一阶段使用完整版 PyTorch 编译扩展模块,第二阶段仅复制编译产物和运行库,最终镜像体积降至 3.1GB。构建时间从 12 分钟降至 4 分钟(利用层缓存),推送至边缘节点的带宽消耗减少 50%。

# 第一阶段:编译
FROM pytorch/pytorch:2.0.1-cuda11.7-cudnn8-runtime AS builder
WORKDIR /app
COPY requirements.txt .
RUN pip install --user -r requirements.txt

# 第二阶段:运行
FROM pytorch/pytorch:2.0.1-cuda11.7-cudnn8-runtime
WORKDIR /app
COPY --from=builder /root/.local /root/.local
COPY inference.py .
ENV PATH=/root/.local/bin:$PATH
CMD ["python", "inference.py"]
注意:使用 --user 安装 Python 包时,需确保第二阶段镜像中存在相同的用户路径(通常为 /root/.local),否则运行时找不到依赖。

3.3 常用命令与容器生命周期管理

核心原理

Docker 命令围绕镜像、容器、网络、数据卷四大对象展开。容器生命周期包括创建(create)、启动(start)、停止(stop)、重启(restart)、删除(rm)等状态。边缘 AI 场景中,容器通常以 --restart=always 策略运行,确保设备重启后推理服务自动恢复。

操作流程

命令用途边缘 AI 场景示例
docker ps -a查看所有容器检查推理容器是否异常退出
docker logs -f跟踪日志查看模型加载是否完成
docker exec -it进入容器调试 Python 环境或检查 GPU 状态
docker stats实时资源监控确认容器内存/CPU 是否超限
docker cp文件复制从容器中导出推理结果日志

常见误区

  • 使用 docker attach 而非 docker exec 进入容器,导致无法退出。
  • 忘记设置 --restart 策略,边缘设备断电重启后容器无法自动恢复。
  • 在容器内手动安装调试工具,但未更新 Dockerfile,导致重启后丢失。

3.4 数据卷:持久化与共享

核心原理

数据卷(Volume)是 Docker 提供的一种持久化机制,独立于容器生命周期。命名卷由 Docker 管理,存储在宿主机 /var/lib/docker/volumes/ 下;绑定挂载(Bind Mount)直接将宿主机目录映射到容器内。边缘 AI 场景中,模型文件、日志、配置通常使用绑定挂载,而数据库或中间件数据使用命名卷。

判断方法

使用 docker volume ls 查看已有卷,使用 docker volume inspect 查看挂载点。当容器需要共享同一份模型缓存时,可让多个容器挂载同一个卷。

案例拆解:多模型推理服务的模型切换

某边缘盒子需要同时运行人脸检测和车辆识别两个推理服务,共享同一个模型存储卷。宿主机目录 /data/models 挂载到两个容器的 /models 路径。当模型版本更新时,运维人员只需替换 /data/models 下的文件,然后依次重启两个容器。若使用绑定挂载,需确保宿主机目录存在且权限正确;若使用命名卷,则需通过 docker cp 或辅助容器更新文件。

关键点:绑定挂载适合开发调试和模型热更新,命名卷适合生产环境的数据持久化。在 Compose 文件中,两者分别对应 volumes 下的路径映射和卷名声明。

3.5 网络基础:通信模式与端口映射

核心原理

Docker 提供多种网络模式:bridge(默认,容器间通过虚拟网桥通信)、host(容器直接使用宿主机网络栈)、none(无网络)。边缘 AI 场景中,推理服务通常需要对外提供 REST API,同时与边缘网关中的其他容器(如 MQTT Broker、数据清洗服务)通信。

操作流程

  1. 创建自定义 bridge 网络:docker network create edge-net。
  2. 启动容器时指定网络:docker run --network edge-net。
  3. 通过容器名互相访问:在 edge-net 中,容器可直接使用服务名作为主机名。
  4. 对外暴露端口:-p 8080:8000 将宿主机 8080 端口映射到容器 8000 端口。

常见误区

  • 使用默认 bridge 网络时,容器间只能通过 IP 访问,且 IP 会随容器重启变化。
  • 在 host 网络模式下,端口映射参数 -p 被忽略,容易造成端口冲突。
  • 未指定 --network-alias,导致服务发现失败。
建议:在 Compose 编排中,所有服务默认加入同一个自定义网络,可直接使用服务名作为域名访问,这是边缘 AI 服务间通信的最佳实践。

3.6 总结复盘:基础能力与边缘 AI 的映射