Docker — 视觉开发工程师完全指南
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. 最佳实践
镜像构建优化
-
选择合适的基础镜像
- 开发环境:
ubuntu:22.04或ros:humble(功能完整) - 生产推理:
nvidia/cuda:12.2.0-runtime-ubuntu22.04(体积小) - 纯 Python:
python:3.11-slim(~150MB vs 完整版 ~900MB)
- 开发环境:
-
善用构建缓存
# 好:依赖先装,代码后复制 COPY requirements.txt . RUN pip install -r requirements.txt COPY . . # 代码变化不会触发 pip install 重建 # 不好:每次代码改动都重新安装依赖 COPY . . RUN pip install -r requirements.txt -
合并 RUN 指令减少层数,每层都有元数据开销
-
使用 .dockerignore 排除数据集、模型权重、
.git等大文件 -
固定版本号确保可重现构建,不要用
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 Overview
- Docker 官方文档 - 多阶段构建
- Docker 官方文档 - Dockerfile 最佳实践
- Docker 官方文档 - 网络
- Docker 官方文档 - 存储
- NVIDIA Container Toolkit
- NVIDIA Container Toolkit 安装指南
- ROS 官方 Docker 镜像
- Docker Slim - 镜像瘦身工具
- CSDN 搜索: Docker 视觉开发
相关笔记
- [[技术笔记/Docker/Docker速查手册]] — Docker 速查
- [[技术笔记/系统/WSL2]] — WSL2
- [[技术笔记/视觉环境搭建/Ubuntu22.04.2视觉开发环境搭建全流程]] — 环境搭建
- [[技术笔记/深度学习/PyTorch/神经网络/神经网络1(基础概念和简单搭建)]] — PyTorch Docker 镜像
更多推荐



所有评论(0)