Docker — 视觉开发工程师完全指南

学习笔记 | 更新: 2026-06-05

1. Docker 核心概念

镜像 vs 容器 vs 仓库

概念 本质 类比
镜像 (Image) 只读模板,采用分层存储(Union FS),每层只读 类的定义
容器 (Container) 镜像的运行实例,在镜像层上叠加可写层 类的实例
仓库 (Registry) 存储和分发镜像的服务(Docker Hub、阿里云ACR) 代码仓库

分层存储原理:Docker 镜像由多个只读层叠加而成。当你 docker build 时,Dockerfile 中每条指令生成一层。多个镜像可以共享相同的底层,这就是为什么拉取一个新镜像时如果底层已存在就无需重复下载。

Docker 架构原理

宿主机
├── Docker Daemon (dockerd)        # 后台守护进程,管理容器生命周期
│   ├── containerd                 # 容器运行时管理
│   │   └── runc                   # OCI 容器运行时,实际创建容器
│   └── 镜像层 (overlay2)          # 分层文件系统
├── Docker CLI (docker)            # 命令行客户端
└── Docker Registry                # 远程镜像仓库

核心隔离机制:

  • Namespace:PID、Network、Mount、UTS、IPC、User 六种命名空间,实现进程、网络、文件系统等的隔离
  • Cgroup:限制和统计容器的 CPU、内存、IO 等资源使用

Docker vs 虚拟机

对比项 Docker 容器 虚拟机 (VM)
虚拟化层次 操作系统层(共享宿主机内核) 硬件层(独立内核)
启动速度 秒级 分钟级
资源占用 MB 级内存开销 GB 级内存开销
隔离性 进程级(较弱) 完全硬件级(强)
镜像大小 通常几十 MB ~ 几 GB 通常几 GB ~ 几十 GB
运行密度 单机可运行数百个容器 通常十几个 VM

对视觉开发的意义:Docker 的轻量级特性非常适合需要频繁切换环境(CUDA 版本、ROS 版本、Python 版本)的视觉开发场景。一个 CUDA 11.8 + ROS Noetic 的环境和一个 CUDA 12.2 + ROS Humble 的环境可以秒级切换。


2. 安装与配置

Ubuntu 安装 Docker

# 1. 卸载旧版本(如有)
sudo apt-get remove docker docker-engine docker.io containerd runc

# 2. 安装依赖
sudo apt-get update
sudo apt-get install -y ca-certificates curl gnupg

# 3. 添加 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

# 4. 添加 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

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

# 6. 验证安装
sudo docker run hello-world

安装 NVIDIA Container Toolkit(GPU 支持)

为什么需要:Docker 默认无法访问宿主机 GPU。NVIDIA Container Toolkit 让容器能使用宿主机的 NVIDIA 驱动和 GPU 硬件。

# 前提:宿主机已安装 NVIDIA 驱动
nvidia-smi  # 确认驱动正常

# 1. 配置仓库
distribution=$(. /etc/os-release; echo $ID$VERSION_ID)
curl -fsSL https://nvidia.github.io/libnvidia-container/gpgkey | \
  sudo gpg --dearmor -o /usr/share/keyrings/nvidia-container-toolkit-keyring.gpg

curl -s -L https://nvidia.github.io/libnvidia-container/$distribution/libnvidia-container.list | \
  sed 's#deb https://#deb [signed-by=/usr/share/keyrings/nvidia-container-toolkit-keyring.gpg] https://#g' | \
  sudo tee /etc/apt/sources.list.d/nvidia-container-toolkit.list

# 2. 安装
sudo apt-get update
sudo apt-get install -y nvidia-container-toolkit

# 3. 配置 Docker 运行时
sudo nvidia-ctk runtime configure --runtime=docker

# 4. 重启 Docker
sudo systemctl restart docker

# 5. 验证
docker run --rm --gpus all nvidia/cuda:12.2.0-base-ubuntu22.04 nvidia-smi

配置镜像加速器(国内镜像源)

// /etc/docker/daemon.json
{
  "registry-mirrors": [
    "https://mirror.ccs.tencentyun.com",
    "https://registry.docker-cn.com",
    "https://docker.mirrors.ustc.edu.cn"
  ]
}
# 修改后重启 Docker
sudo systemctl daemon-reload
sudo systemctl restart docker

# 验证配置
docker info | grep -A 5 "Registry Mirrors"

注意:国内镜像源可用性经常变化,如果某个源不可用请尝试其他源或使用代理。

用户组配置(免 sudo)

# 1. 创建 docker 组(通常安装时已创建)
sudo groupadd docker

# 2. 将当前用户加入 docker 组
sudo usermod -aG docker $USER

# 3. 激活组变更(或重新登录)
newgrp docker

# 4. 验证(无需 sudo)
docker run hello-world

3. Docker 基础命令

镜像操作

# 拉取镜像
docker pull ubuntu:22.04                         # 拉取指定版本
docker pull nvidia/cuda:12.2.0-cudnn8-devel-ubuntu22.04  # CUDA 开发镜像
docker pull ros:humble                            # ROS2 Humble

# 查看本地镜像
docker images                                    # 列出所有镜像
docker images -a                                 # 包含中间层镜像
docker images --filter "dangling=true"           # 查找悬空镜像

# 构建镜像
docker build -t my-vision-app:1.0 .              # 从当前目录 Dockerfile 构建
docker build -t my-app:v1 -f Dockerfile.gpu .    # 指定 Dockerfile
docker build --no-cache -t my-app:v2 .           # 不使用构建缓存

# 镜像信息
docker inspect <image>                           # 查看镜像详细信息
docker history <image>                           # 查看镜像构建历史(各层大小)

# 删除镜像
docker rmi <image>                               # 删除指定镜像
docker image prune                               # 删除悬空镜像
docker image prune -a                            # 删除所有未使用镜像

# 导入导出
docker save -o my-image.tar my-image:latest      # 导出镜像为 tar
docker load -i my-image.tar                      # 从 tar 导入镜像

容器操作

# 创建并运行容器
docker run -it --name dev ubuntu:22.04 bash      # 交互模式,命名容器
docker run -d --name web nginx                   # 后台运行(detach)
docker run --rm ubuntu echo "hello"              # 运行后自动删除

# 视觉开发常用 run 参数组合
docker run -it --gpus all \
  --name vision-dev \
  -e DISPLAY=$DISPLAY \
  -v /tmp/.X11-unix:/tmp/.X11-unix \
  -v $(pwd)/src:/app/src \
  --shm-size=4g \
  --net=host \
  nvidia/cuda:12.2.0-cudnn8-devel-ubuntu22.04 bash

# 查看容器
docker ps                                        # 运行中的容器
docker ps -a                                     # 所有容器(含已停止)
docker ps -s                                     # 显示大小

# 进入运行中的容器
docker exec -it <container> bash                 # 进入容器 shell
docker exec -it <container> python3              # 在容器中运行 Python

# 容器生命周期
docker start <container>                         # 启动已停止的容器
docker stop <container>                          # 停止容器(SIGTERM + SIGKILL)
docker restart <container>                       # 重启容器
docker pause <container>                         # 暂停容器
docker unpause <container>                       # 恢复容器

# 查看日志
docker logs <container>                          # 查看全部日志
docker logs -f <container>                       # 实时跟踪日志
docker logs --tail 100 <container>               # 最后 100 行

# 删除容器
docker rm <container>                            # 删除已停止的容器
docker rm -f <container>                         # 强制删除运行中的容器
docker container prune                           # 删除所有已停止的容器

# 容器信息
docker inspect <container>                       # 详细信息(JSON)
docker stats                                     # 实时资源使用
docker top <container>                           # 容器内进程

卷与数据持久化

Docker 容器的可写层在容器删除后会丢失。视觉开发中数据集、模型权重、训练日志必须持久化。

# 命名卷(Docker 管理,推荐用于数据库等)
docker volume create mydata
docker run -v mydata:/data ubuntu

# 绑定挂载(直接映射宿主机目录,推荐用于开发代码)
docker run -v /home/user/dataset:/data/dataset \
           -v $(pwd)/src:/app/src \
           ubuntu

# tmpfs 挂载(内存中,容器停止即丢失)
docker run --tmpfs /app/temp ubuntu

# 卷管理命令
docker volume ls                                 # 列出所有卷
docker volume inspect mydata                     # 查看卷详情
docker volume rm mydata                          # 删除卷
docker volume prune                              # 删除未使用的卷

视觉开发数据挂载策略

# 推荐挂载方式
-v $(pwd)/src:/app/src           # 源代码(实时同步修改)
-v ~/datasets:/data/datasets     # 数据集(只读挂载更安全:ro)
-v ~/checkpoints:/app/checkpoints # 模型权重(持久化训练结果)
-v /tmp/.X11-unix:/tmp/.X11-unix # X11 socket(GUI 显示)

网络管理

# 查看网络
docker network ls

# 创建自定义网络(推荐,容器间可通过服务名通信)
docker network create vision-net

# 使用自定义网络运行容器
docker run -it --network vision-net --name detector ubuntu
docker run -it --network vision-net --name tracker ubuntu
# detector 容器中可以直接 ping tracker

# 连接/断开网络
docker network connect vision-net my_container
docker network disconnect vision-net my_container

# 查看网络详情
docker network inspect vision-net
网络模式 说明 适用场景
bridge 默认,容器通过虚拟网桥通信 一般容器间通信
host 共享宿主机网络栈 ROS 多节点、低延迟需求
none 无网络 完全隔离的计算任务
overlay 跨主机通信 Swarm 集群

4. Dockerfile 详解

基本指令

# ========== FROM: 基础镜像 ==========
# 必须是第一条指令。选择原则:够用即可,不要用 ubuntu 装整个 OS
FROM nvidia/cuda:12.2.0-cudnn8-devel-ubuntu22.04
# 可用 ARG 动态指定基础镜像
ARG BASE_IMAGE=nvidia/cuda:12.2.0-cudnn8-devel-ubuntu22.04
FROM ${BASE_IMAGE}

# ========== ENV: 环境变量 ==========
# 构建时和运行时都生效
ENV DEBIAN_FRONTEND=noninteractive
ENV PYTHONUNBUFFERED=1
ENV PATH="/opt/venv/bin:$PATH"

# ========== ARG: 构建时变量 ==========
# 仅在构建时生效,运行时不存在
ARG CUDA_VERSION=12.2
ARG USERNAME=developer

# ========== WORKDIR: 工作目录 ==========
# 不存在会自动创建,后续指令基于此目录
WORKDIR /app

# ========== RUN: 执行命令 ==========
# 每条 RUN 生成一层,应合并相关命令减少层数
# 关键:apt-get update 和 install 必须在同一 RUN 中,否则可能用到过期缓存
RUN apt-get update && \
    apt-get install -y --no-install-recommends \
    python3 python3-pip python3-dev \
    libgl1-mesa-glx libglib2.0-0 \
    libsm6 libxext6 libxrender-dev \
    git wget curl vim \
    && rm -rf /var/lib/apt/lists/*
#     ^^^ 清理 apt 缓存,减小镜像体积

# ========== COPY: 复制文件 ==========
# 从构建上下文复制到镜像中
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY . .  # 最后复制代码,利用缓存(代码变化最频繁)

# ========== ADD: 类似 COPY 但更强大 ==========
# 支持 URL 下载和自动解压 tar,但行为不透明,一般推荐用 COPY
# ADD https://example.com/file.tar.gz /tmp/  # 下载并解压

# ========== EXPOSE: 声明端口 ==========
# 仅声明,不实际映射。实际映射在 docker run -p 时指定
EXPOSE 8080
EXPOSE 6080  # noVNC

# ========== CMD: 默认启动命令 ==========
# 可被 docker run 的参数覆盖
CMD ["python3", "app.py"]

# ========== ENTRYPOINT: 入口点 ==========
# 不会被 docker run 参数覆盖,CMD 会作为参数传给 ENTRYPOINT
ENTRYPOINT ["python3"]
CMD ["app.py"]  # docker run myimage test.py  → python3 test.py

# ========== USER: 运行用户 ==========
# 默认以 root 运行,安全场景建议切换用户
ARG UID=1000
ARG GID=1000
RUN groupadd -g $GID devuser && \
    useradd -m -u $UID -g $GID devuser
USER devuser

# ========== HEALTHCHECK: 健康检查 ==========
HEALTHCHECK --interval=30s --timeout=3s --retries=3 \
  CMD curl -f http://localhost:8080/health || exit 1

多阶段构建

为什么需要:视觉开发镜像经常包含 CUDA、cuDNN、编译工具链等,体积可达 10GB+。多阶段构建将编译阶段和运行阶段分离,最终镜像只包含运行时必需的文件。

# ====== 阶段1:编译 OpenCV(带 CUDA 支持)======
FROM nvidia/cuda:12.2.0-cudnn8-devel-ubuntu22.04 AS builder

RUN apt-get update && apt-get install -y --no-install-recommends \
    build-essential cmake git \
    python3-dev python3-numpy \
    libgtk-3-dev libavcodec-dev libavformat-dev libswscale-dev \
    && rm -rf /var/lib/apt/lists/*

# 编译 OpenCV(带 CUDA 支持)
RUN git clone --depth 1 --branch 4.8.1 https://github.com/opencv/opencv.git && \
    git clone --depth 1 --branch 4.8.1 https://github.com/opencv/opencv_contrib.git && \
    mkdir opencv/build && cd opencv/build && \
    cmake -D CMAKE_BUILD_TYPE=Release \
          -D CMAKE_INSTALL_PREFIX=/usr/local \
          -D OPENCV_EXTRA_MODULES_PATH=/opencv_contrib/modules \
          -D WITH_CUDA=ON \
          -D WITH_CUDNN=ON \
          -D OPENCV_DNN_CUDA=ON \
          -D BUILD_opencv_python3=ON \
          .. && \
    make -j$(nproc) && make install

# ====== 阶段2:运行环境 ======
FROM nvidia/cuda:12.2.0-cudnn8-runtime-ubuntu22.04

RUN apt-get update && apt-get install -y --no-install-recommends \
    python3 python3-pip libgl1-mesa-glx libglib2.0-0 \
    && rm -rf /var/lib/apt/lists/*

# 从构建阶段复制编译好的 OpenCV
COPY --from=builder /usr/local /usr/local

RUN pip install --no-cache-dir numpy torch torchvision

WORKDIR /app
COPY . .
CMD ["python3"]

镜像优化技巧

# 1. 使用 --no-install-recommends 避免安装推荐包
RUN apt-get install -y --no-install-recommends <packages>

# 2. 同一 RUN 中安装并清理
RUN apt-get update && \
    apt-get install -y --no-install-recommends pkg1 pkg2 && \
    rm -rf /var/lib/apt/lists/*

# 3. pip 使用 --no-cache-dir
RUN pip install --no-cache-dir -r requirements.txt

# 4. 固定版本号确保可重现
FROM nvidia/cuda:12.2.0-cudnn8-devel-ubuntu22.04  # 不要用 latest
RUN pip install opencv-python==4.8.1.78

# 5. 利用构建缓存:变化频率低的指令在前
COPY requirements.txt .        # 不常变 → 先 COPY
RUN pip install -r requirements.txt
COPY . .                       # 频繁变 → 后 COPY

# 6. 使用 BuildKit 并行构建
# DOCKER_BUILDKIT=1 docker build .

.dockerignore

# 版本控制
.git
.gitignore

# Python
__pycache__
*.pyc
*.pyo
.venv
venv

# 数据集和模型(体积巨大,用 volume 挂载)
datasets/
checkpoints/
weights/
*.onnx
*.pt
*.pth

# IDE
.vscode/
.idea/

# 文档
*.md
docs/

# 环境变量
.env
.env.local

# Docker 自身
Dockerfile*
docker-compose*.yml
.dockerignore

5. Docker Compose

基本语法

version: '3.8'

services:
  # 服务名即为容器网络中的主机名
  vision-app:
    build:
      context: .
      dockerfile: Dockerfile
      args:
        CUDA_VERSION: "12.2"
    image: my-vision-app:latest
    container_name: vision-app
    ports:
      - "8080:8080"
    environment:
      - DISPLAY=${DISPLAY}
      - NVIDIA_VISIBLE_DEVICES=all
    env_file:
      - .env
    volumes:
      - ./src:/app/src
      - ~/datasets:/data/datasets:ro    # ro = 只读
      - /tmp/.X11-unix:/tmp/.X11-unix
    networks:
      - vision-net
    depends_on:
      - database
    restart: unless-stopped              # 除非手动停止,否则自动重启
    shm_size: '4gb'                     # 共享内存大小
    ipc: host                           # 使用宿主机 IPC 命名空间
    deploy:
      resources:
        limits:
          cpus: '8'
          memory: 16G
        reservations:
          devices:
            - driver: nvidia
              count: 1
              capabilities: [gpu]

  database:
    image: postgres:15
    volumes:
      - db_data:/var/lib/postgresql/data
    environment:
      POSTGRES_PASSWORD: secret

volumes:
  db_data:

networks:
  vision-net:
    driver: bridge

常用命令

# 启动
docker compose up -d                    # 后台启动所有服务
docker compose up -d --build            # 重新构建并启动
docker compose up -d vision-app         # 只启动指定服务

# 停止
docker compose down                     # 停止并删除容器、网络
docker compose down -v                  # 同时删除卷

# 查看状态
docker compose ps                       # 服务状态
docker compose logs -f                  # 实时日志(所有服务)
docker compose logs -f vision-app       # 指定服务日志

# 进入容器
docker compose exec vision-app bash     # 进入运行中的服务容器

# 其他
docker compose build                    # 构建所有服务镜像
docker compose restart                  # 重启所有服务
docker compose pull                     # 拉取最新镜像

视觉开发的 compose 模板

version: '3.8'

services:
  # 视觉开发主容器
  dev:
    build: .
    image: vision-dev:latest
    container_name: vision-dev
    stdin_open: true
    tty: true
    environment:
      - DISPLAY=${DISPLAY}
      - QT_X11_NO_MITSHM=1
      - NVIDIA_VISIBLE_DEVICES=all
    volumes:
      - ./src:/workspace/src
      - ~/datasets:/workspace/datasets:ro
      - ~/checkpoints:/workspace/checkpoints
      - /tmp/.X11-unix:/tmp/.X11-unix
      - ${HOME}/.Xauthority:/root/.Xauthority
    network_mode: host
    ipc: host
    shm_size: '8gb'
    deploy:
      resources:
        reservations:
          devices:
            - driver: nvidia
              count: all
              capabilities: [gpu]

  # ROS2 系统
  ros-master:
    image: ros:humble
    command: ros2 daemon start && tail -f /dev/null
    environment:
      - ROS_DOMAIN_ID=0
    network_mode: host
    ipc: host

  rviz:
    image: ros:humble-desktop
    command: rviz2
    environment:
      - DISPLAY=${DISPLAY}
      - ROS_DOMAIN_ID=0
    volumes:
      - /tmp/.X11-unix:/tmp/.X11-unix
      - ${HOME}/.Xauthority:/root/.Xauthority
    network_mode: host
    ipc: host

6. 视觉开发专项

6.1 GUI 程序容器化

X11 转发方案(详细步骤)

原理:X11 采用客户端-服务器架构。宿主机运行 X Server(显示服务器),容器内的 GUI 程序作为 X Client 通过 Unix socket 连接到 X Server 进行绘图。

# ===== 步骤 1: 宿主机授权 =====
# 允许本地用户连接 X Server(安全风险可控)
xhost +local:docker
# 更安全的方式:仅允许当前用户
xhost +SI:localuser:$(whoami)

# ===== 步骤 2: 运行容器 =====
docker run -it --rm \
  -e DISPLAY=$DISPLAY \
  -v /tmp/.X11-unix:/tmp/.X11-unix \
  -v $HOME/.Xauthority:/root/.Xauthority \
  --net=host \
  vision-dev:latest bash

# ===== 步骤 3: 容器内验证 =====
apt-get update && apt-get install -y x11-apps
xeyes  # 如果弹出眼睛窗口,说明 X11 转发成功

# ===== 步骤 4: 用完后撤销授权 =====
xhost -local:docker

关键参数详解

参数 作用 说明
-e DISPLAY=$DISPLAY 传递显示地址 通常为 :0(本机)或 :1
-v /tmp/.X11-unix:/tmp/.X11-unix 挂载 X11 socket X Server 的 Unix socket 文件
-v $HOME/.Xauthority:/root/.Xauthority 挂载认证文件 X11 连接认证令牌
--net=host 共享宿主机网络 某些情况下 X11 需要
--ipc=host 共享 IPC 命名空间 共享内存相关

OpenCV imshow 在容器中显示

# test_gui.py
import cv2
import numpy as np

# 创建测试图像
img = np.zeros((400, 600, 3), dtype=np.uint8)
cv2.putText(img, 'Docker GUI Works!', (50, 200),
            cv2.FONT_HERSHEY_SIMPLEX, 2, (0, 255, 0), 3)
cv2.imshow('Test', img)
cv2.waitKey(0)
cv2.destroyAllWindows()

如果报错 libGL.so.1: cannot open shared object file,在 Dockerfile 中安装:

RUN apt-get install -y libgl1-mesa-glx libglib2.0-0
# 或者更完整的:
RUN apt-get install -y libgl1-mesa-glx libglib2.0-0 libsm6 libxext6 libxrender-dev
VNC/noVNC 方案

适用于 Windows/Mac 宿主机或远程开发场景(无需 X Server)。

FROM ubuntu:22.04

ENV DEBIAN_FRONTEND=noninteractive

# 安装 VNC 和桌面环境
RUN apt-get update && apt-get install -y --no-install-recommends \
    tigervnc-standalone-server \
    xfce4 xfce4-goodies \
    novnc websockify \
    python3 python3-pip \
    libgl1-mesa-glx \
    && rm -rf /var/lib/apt/lists/*

# 设置 VNC 密码
RUN mkdir -p ~/.vnc && \
    echo "password" | vncpasswd -f > ~/.vnc/passwd && \
    chmod 600 ~/.vnc/passwd

# 启动脚本
COPY start-vnc.sh /start-vnc.sh
RUN chmod +x /start-vnc.sh

EXPOSE 5901 6080
CMD ["/start-vnc.sh"]
# start-vnc.sh
#!/bin/bash
# 启动 VNC 服务器
vncserver :1 -geometry 1920x1080 -depth 24
# 启动 noVNC(浏览器访问)
websockify --web /usr/share/novnc 6080 localhost:5901 &
tail -f /dev/null
# 运行
docker run -d -p 5901:5901 -p 6080:6080 vnc-vision
# VNC 客户端连接: localhost:5901
# 浏览器访问: http://localhost:6080
RViz/Gazebo 在容器中运行
# 运行带 GUI 支持的 ROS2 容器
docker run -it --rm \
  --gpus all \
  -e DISPLAY=$DISPLAY \
  -v /tmp/.X11-unix:/tmp/.X11-unix \
  -v $HOME/.Xauthority:/root/.Xauthority \
  --net=host \
  --ipc=host \
  ros:humble-desktop bash

# 容器内启动 RViz
rviz2

# 容器内启动 Gazebo
gazebo --verbose

常见问题:Gazebo 黑屏或渲染异常,通常是因为缺少 GPU 加速。确保 --gpus all 和 NVIDIA Container Toolkit 已正确配置。

6.2 GPU 加速配置

NVIDIA Container Toolkit 安装验证
# 验证 1: 容器内 nvidia-smi
docker run --rm --gpus all nvidia/cuda:12.2.0-base-ubuntu22.04 nvidia-smi

# 验证 2: 容器内 CUDA 示例
docker run --rm --gpus all nvidia/cuda:12.2.0-base-ubuntu22.04 \
  bash -c "nvcc --version && python3 -c 'import subprocess; print(subprocess.check_output([\"nvidia-smi\"]).decode())'"

# 验证 3: PyTorch GPU
docker run --rm --gpus all pytorch/pytorch:2.1.0-cuda12.1-cudnn8-runtime \
  python3 -c "import torch; print(f'CUDA: {torch.cuda.is_available()}, GPU: {torch.cuda.get_device_name(0)}')"
Dockerfile 中配置 GPU
# 选择正确的 CUDA 基础镜像
# devel 版本:包含 nvcc 编译器,需要编译 CUDA 代码时使用
FROM nvidia/cuda:12.2.0-cudnn8-devel-ubuntu22.04

# runtime 版本:仅运行时,体积更小
# FROM nvidia/cuda:12.2.0-cudnn8-runtime-ubuntu22.04

# base 版本:最小,仅 CUDA 运行时
# FROM nvidia/cuda:12.2.0-base-ubuntu22.04

ENV NVIDIA_VISIBLE_DEVICES=all
ENV NVIDIA_DRIVER_CAPABILITIES=compute,utility,video,graphics
docker-compose 中配置 GPU
services:
  training:
    build: .
    deploy:
      resources:
        reservations:
          devices:
            - driver: nvidia
              count: 1            # 使用 1 块 GPU
              # count: all        # 使用所有 GPU
              # device_ids: ['0', '1']  # 指定 GPU ID
              capabilities: [gpu]
    environment:
      - NVIDIA_VISIBLE_DEVICES=all
      - NVIDIA_DRIVER_CAPABILITIES=compute,utility
CUDA/cuDNN 版本匹配
宿主机 NVIDIA 驱动版本  →  支持的最高 CUDA 版本
545.x                  →  CUDA 12.3
535.x                  →  CUDA 12.2
525.x                  →  CUDA 12.0
515.x                  →  CUDA 11.7

匹配规则:容器内 CUDA 版本 <= 宿主机驱动支持的最高 CUDA 版本

# 查看宿主机支持的 CUDA 版本
nvidia-smi | head -5
# 右上角显示 "CUDA Version: 12.2"

# PyTorch CUDA 版本对应关系
# PyTorch cu118 → 需要 CUDA >= 11.8
# PyTorch cu121 → 需要 CUDA >= 12.1
# PyTorch cu124 → 需要 CUDA >= 12.4

6.3 ROS/ROS2 容器开发

官方 ROS Docker 镜像
# ROS 1
docker pull ros:noetic               # Ubuntu 20.04, 最后一个 ROS1 版本

# ROS 2
docker pull ros:humble                # Ubuntu 22.04, LTS(推荐)
docker pull ros:humble-desktop        # 含 RViz2、rqt 等 GUI 工具
docker pull ros:jazzy                 # Ubuntu 24.04
ROS 容器的网络配置

ROS 2 使用 DDS(Data Distribution Service)进行节点发现和通信。容器的网络隔离会导致节点无法互相发现。

# 方案 1: host 网络模式(最简单,推荐开发环境)
docker run -it --net=host --ipc=host ros:humble bash
# 优点:ROS 节点与宿主机节点完全互通
# 缺点:牺牲了网络隔离

# 方案 2: bridge 网络 + ROS_DOMAIN_ID(需要隔离时)
docker run -it -e ROS_DOMAIN_ID=42 ros:humble bash
# 所有使用相同 ROS_DOMAIN_ID 的节点可以互相发现

# 方案 3: 自定义 DDS 配置(高级)
# 创建 cyclonedds.xml
cat > cyclonedds.xml << 'EOF'
<?xml version="1.0" encoding="UTF-8" ?>
<CycloneDDS xmlns="https://cdds.io/config"
            xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
            xsi:schemaLocation="https://cdds.io/config
            https://raw.githubusercontent.com/eclipse-cyclonedds/cyclonedds/master/etc/cyclonedds.xsd">
  <Domain>
    <General>
      <Interfaces>
        <NetworkInterface name="eth0"/>
      </Interfaces>
    </General>
  </Domain>
</CycloneDDS>
EOF
docker run -it -e CYCLONEDDS_URI=file:///cyclonedds.xml \
  -v $(pwd)/cyclonedds.xml:/cyclonedds.xml \
  --net=host ros:humble bash
ROS 主题/服务跨容器访问
# docker-compose.yml - ROS2 多容器通信
version: '3.8'

services:
  # 相机驱动节点
  camera:
    image: ros:humble
    command: >
      bash -c "source /opt/ros/humble/setup.bash &&
               ros2 run image_tools cam2image"
    environment:
      - ROS_DOMAIN_ID=0
      - DISPLAY=${DISPLAY}
    volumes:
      - /tmp/.X11-unix:/tmp/.X11-unix
    network_mode: host
    ipc: host

  # 视觉处理节点
  vision:
    build: .
    command: >
      bash -c "source /opt/ros/humble/setup.bash &&
               source /ros2_ws/install/setup.bash &&
               ros2 run my_vision detect_node"
    environment:
      - ROS_DOMAIN_ID=0
      - NVIDIA_VISIBLE_DEVICES=all
    volumes:
      - ./src:/ros2_ws/src
    network_mode: host
    ipc: host
    deploy:
      resources:
        reservations:
          devices:
            - driver: nvidia
              count: all
              capabilities: [gpu]
    depends_on:
      - camera

  # 可视化节点
  rviz:
    image: ros:humble-desktop
    command: rviz2
    environment:
      - ROS_DOMAIN_ID=0
      - DISPLAY=${DISPLAY}
    volumes:
      - /tmp/.X11-unix:/tmp/.X11-unix
    network_mode: host
    ipc: host
常用 ROS Docker 模板
# ROS2 + CUDA + 视觉开发
FROM ros:humble

ENV DEBIAN_FRONTEND=noninteractive

# 安装 ROS 视觉包
RUN apt-get update && apt-get install -y --no-install-recommends \
    ros-${ROS_DISTRO}-rviz2 \
    ros-${ROS_DISTRO}-cv-bridge \
    ros-${ROS_DISTRO}-image-transport \
    ros-${ROS_DISTRO}-vision-opencv \
    ros-${ROS_DISTRO}-tf2-ros \
    ros-${ROS_DISTRO}-pcl-ros \
    python3-colcon-common-extensions \
    python3-pip \
    libgl1-mesa-glx \
    && rm -rf /var/lib/apt/lists/*

# Python 依赖
RUN pip3 install --no-cache-dir \
    opencv-python-headless \
    numpy \
    ultralytics

# 创建 ROS 工作空间
RUN mkdir -p /ros2_ws/src
WORKDIR /ros2_ws

# 自动 source ROS 环境
RUN echo "source /opt/ros/${ROS_DISTRO}/setup.bash" >> /root/.bashrc && \
    echo "source /ros2_ws/install/setup.bash" >> /root/.bashrc

CMD ["bash"]

6.4 深度学习容器

PyTorch GPU 容器
FROM pytorch/pytorch:2.1.0-cuda12.1-cudnn8-runtime

ENV DEBIAN_FRONTEND=noninteractive

RUN apt-get update && apt-get install -y --no-install-recommends \
    libgl1-mesa-glx libglib2.0-0 git wget \
    && rm -rf /var/lib/apt/lists/*

RUN pip install --no-cache-dir \
    opencv-python-headless \
    ultralytics \
    tensorboard \
    albumentations \
    onnxruntime-gpu

WORKDIR /workspace
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY . .

# PyTorch 显存优化
ENV PYTORCH_CUDA_ALLOC_CONF=max_split_size_mb:512

CMD ["python3", "train.py"]
# 运行训练容器
docker run -d --gpus all \
  --name training \
  -v $(pwd)/datasets:/workspace/datasets \
  -v $(pwd)/checkpoints:/workspace/checkpoints \
  -v $(pwd)/runs:/workspace/runs \
  --shm-size=8g \
  pytorch-vision:latest
Jupyter Notebook 在容器中运行
FROM pytorch/pytorch:2.1.0-cuda12.1-cudnn8-runtime

RUN pip install --no-cache-dir \
    jupyterlab \
    opencv-python-headless \
    matplotlib \
    ultralytics

EXPOSE 8888

CMD ["jupyter", "lab", "--ip=0.0.0.0", "--port=8888", \
     "--no-browser", "--allow-root", "--NotebookApp.token=''"]
# 运行 Jupyter
docker run -d --gpus all \
  -p 8888:8888 \
  -v $(pwd)/notebooks:/workspace/notebooks \
  -v ~/datasets:/workspace/datasets \
  --shm-size=4g \
  jupyter-vision

# 浏览器访问 http://localhost:8888
模型训练容器最佳实践
# 1. 持久化训练数据和模型
-v ~/datasets:/data/datasets:ro          # 数据集只读挂载
-v ./checkpoints:/app/checkpoints        # 模型检查点
-v ./runs:/app/runs                      # TensorBoard 日志

# 2. 资源限制
--shm-size=8g                            # 共享内存(DataLoader 多进程需要)
--gpus '"device=0,1"'                    # 指定 GPU
--memory=32g                             # 内存限制

# 3. 后台训练 + 日志
docker run -d --gpus all \
  --name train \
  -v ./checkpoints:/app/checkpoints \
  --shm-size=8g \
  train-image \
  python3 train.py --epochs 100

# 查看训练日志
docker logs -f train

# 训练完成后进入容器查看结果
docker exec -it train bash

7. 最佳实践

镜像构建优化

  1. 选择合适的基础镜像

    • 开发环境:ubuntu:22.04ros:humble(功能完整)
    • 生产推理:nvidia/cuda:12.2.0-runtime-ubuntu22.04(体积小)
    • 纯 Python:python:3.11-slim(~150MB vs 完整版 ~900MB)
  2. 善用构建缓存

    # 好:依赖先装,代码后复制
    COPY requirements.txt .
    RUN pip install -r requirements.txt
    COPY . .  # 代码变化不会触发 pip install 重建
    
    # 不好:每次代码改动都重新安装依赖
    COPY . .
    RUN pip install -r requirements.txt
    
  3. 合并 RUN 指令减少层数,每层都有元数据开销

  4. 使用 .dockerignore 排除数据集、模型权重、.git 等大文件

  5. 固定版本号确保可重现构建,不要用 latest

安全最佳实践

# 1. 非 root 用户运行
ARG UID=1000
RUN useradd -m -u $UID devuser
USER devuser

# 2. 不要在镜像中硬编码密钥
# 错误:ENV API_KEY=sk-xxxx
# 正确:运行时传入 docker run -e API_KEY=$API_KEY

# 3. 使用只读文件系统
# docker run --read-only --tmpfs /tmp myimage
# 4. 限制容器资源
docker run --memory=4g --cpus=2 myimage

# 5. 定期扫描镜像漏洞
docker scout cves myimage:latest
# 或使用 trivy
trivy image myimage:latest

开发工作流(dev container 模式)

# 1. 源代码通过 volume 挂载,实时同步编辑
docker run -it -v $(pwd)/src:/app/src vision-dev bash

# 2. 使用 VS Code Remote Containers
# 安装 "Dev Containers" 扩展 → 打开项目 → "Reopen in Container"

# 3. 多终端:tmux 或多个 docker exec
# 终端1: docker exec -it dev bash -c "ros2 run camera camera_node"
# 终端2: docker exec -it dev bash -c "ros2 run vision detector"

CI/CD 中的 Docker

# GitHub Actions 示例
jobs:
  build-and-test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - name: Build Docker image
        run: docker build -t vision-app:${{ github.sha }} .
      - name: Run tests
        run: docker run --rm vision-app:${{ github.sha }} python3 -m pytest
      - name: Push to registry
        run: |
          docker tag vision-app:${{ github.sha }} registry.example.com/vision-app:latest
          docker push registry.example.com/vision-app:latest

8. 常见问题与陷阱

libGL.so 报错

错误信息

ImportError: libGL.so.1: cannot open shared object file: No such file or directory

原因:OpenCV(非 headless 版本)依赖 OpenGL 库,容器中未安装。

解决方案

# 方案 1:安装 OpenGL 库
RUN apt-get install -y libgl1-mesa-glx libglib2.0-0

# 方案 2(推荐):使用 headless 版本
pip install opencv-python-headless
# headless 版本不依赖 GUI 库,但无法使用 cv2.imshow()

X11 cannot open display

错误信息

cannot open display: :0
Error: Can't open display: (null)

排查步骤

# 1. 确认宿主机 DISPLAY 变量
echo $DISPLAY  # 应该是 :0 或 :1

# 2. 授权 X Server
xhost +local:docker

# 3. 确认容器参数正确
docker run -it \
  -e DISPLAY=$DISPLAY \
  -v /tmp/.X11-unix:/tmp/.X11-unix \
  -v $HOME/.Xauthority:/root/.Xauthority \
  --net=host \
  ubuntu xeyes

# 4. 如果是 SSH 远程连接,需要 X11Forwarding
# /etc/ssh/sshd_config: X11Forwarding yes
# ssh -X user@host

CUDA 版本不匹配

错误信息

CUDA error: no kernel image is available for execution on the device
RuntimeError: CUDA driver version is insufficient for CUDA runtime version

原因:容器内 CUDA Toolkit 版本高于宿主机 NVIDIA 驱动支持的版本。

解决方案

# 查看宿主机支持的最高 CUDA 版本
nvidia-smi  # 右上角 "CUDA Version: 12.2"

# 选择匹配的镜像
# 驱动 CUDA 12.2 → 可用 nvidia/cuda:12.2.x, 12.1.x, 11.x
# 不可用 nvidia/cuda:12.3.x 或更高

# PyTorch 版本对应
# pip install torch --index-url https://download.pytorch.org/whl/cu121  # CUDA 12.1
# pip install torch --index-url https://download.pytorch.org/whl/cu118  # CUDA 11.8

共享内存不足

错误信息

Bus error (core dumped)
RuntimeError: unable to open shared memory object </torch_xxx>
torch.multiprocessing: Worker (pid xxxx) raised RuntimeError

原因:PyTorch DataLoader 的 num_workers > 0 或 ROS 图像传输需要较大的共享内存。Docker 默认只给 64MB。

解决方案

# 方案 1:增加共享内存大小
docker run --shm-size=4g myimage

# 方案 2:使用宿主机 IPC 命名空间(推荐)
docker run --ipc=host myimage

# docker-compose
services:
  training:
    shm_size: '8gb'
    # 或
    ipc: host

容器内无法访问 GPU

错误信息

NVIDIA-SMI has failed because it couldn't communicate with the NVIDIA driver
docker: Error response from failed to create shim task

排查清单

# 1. 宿主机驱动是否正常
nvidia-smi  # 宿主机上执行

# 2. NVIDIA Container Toolkit 是否安装
nvidia-ctk --version

# 3. Docker 运行时是否配置
cat /etc/docker/daemon.json
# 应包含 "runtimes": { "nvidia": { ... } }

# 4. 是否传入 --gpus 参数
docker run --gpus all ...  # 不是 --runtime=nvidia(旧方式)

# 5. 重启 Docker
sudo systemctl restart docker

ROS 节点跨容器发现

问题:容器 A 的 ROS 节点看不到容器 B 的节点。

解决方案

# 方案 1:使用 host 网络(最简单)
docker run --net=host --ipc=host ros:humble

# 方案 2:设置相同的 ROS_DOMAIN_ID
docker run -e ROS_DOMAIN_ID=0 ros:humble  # 容器 A
docker run -e ROS_DOMAIN_ID=0 ros:humble  # 容器 B

# 方案 3:检查 DDS 配置
# 在容器内检查
ros2 doctor --report
ros2 topic list

镜像体积过大

问题:视觉开发镜像动辄 10GB+,拉取和推送极慢。

解决方案

# 1. 使用多阶段构建
FROM nvidia/cuda:12.2.0-devel-ubuntu22.04 AS builder
# ... 编译步骤 ...

FROM nvidia/cuda:12.2.0-runtime-ubuntu22.04  # runtime 比 devel 小很多
COPY --from=builder /output /app

# 2. 合并 RUN 并清理缓存
RUN apt-get update && \
    apt-get install -y --no-install-recommends pkg1 pkg2 && \
    rm -rf /var/lib/apt/lists/*

# 3. 使用 --no-cache-dir
RUN pip install --no-cache-dir -r requirements.txt

# 4. 分析镜像层
docker history myimage:latest
# 或使用 dive 工具
dive myimage:latest

网络连接问题

问题:容器内无法访问外网或容器间无法通信。

# 1. DNS 问题
docker run --rm ubuntu cat /etc/resolv.conf
# 如果 DNS 不对,在 daemon.json 中配置
# { "dns": ["8.8.8.8", "114.114.114.114"] }

# 2. 代理问题
docker build --build-arg HTTP_PROXY=http://proxy:port \
             --build-arg HTTPS_PROXY=http://proxy:port .
# 或在 daemon.json 中配置
# { "proxies": { "http-proxy": "http://proxy:port" } }

# 3. 容器间通信
# 使用自定义网络,容器间通过服务名访问
docker network create mynet
docker run --network mynet --name service_a ...
docker run --network mynet --name service_b ...
# service_b 中可以直接 curl http://service_a:8080

# 4. 端口映射
docker run -p 8080:80 ...  # 宿主机 8080 → 容器 80
docker run -p 127.0.0.1:8080:80 ...  # 仅本机可访问

权限问题

问题:容器内以 root 创建的文件,在宿主机上属于 root,普通用户无法编辑。

# 解决方案:构建时创建匹配宿主机 UID 的用户
ARG UID=1000
ARG GID=1000
RUN groupadd -g $GID devuser && \
    useradd -m -u $UID -g $GID devuser && \
    mkdir -p /home/devuser/.cache && \
    chown -R devuser:devuser /home/devuser

# 给用户必要的目录权限
RUN chown -R devuser:devuser /app

USER devuser
# 运行时传入宿主机 UID/GID
docker build --build-arg UID=$(id -u) --build-arg GID=$(id -g) -t myimage .

参考来源


相关笔记

  • [[技术笔记/Docker/Docker速查手册]] — Docker 速查
  • [[技术笔记/系统/WSL2]] — WSL2
  • [[技术笔记/视觉环境搭建/Ubuntu22.04.2视觉开发环境搭建全流程]] — 环境搭建
  • [[技术笔记/深度学习/PyTorch/神经网络/神经网络1(基础概念和简单搭建)]] — PyTorch Docker 镜像
Logo

免费领 150 小时云算力,进群参与显卡、AI PC 幸运抽奖

更多推荐