OpenCV 计算机视觉完全教程 / 第 16 章 — Docker 部署
第 16 章 — Docker 部署
16.1 为什么使用 Docker
| 问题 | Docker 解决方案 |
|---|
| “在我机器上能跑” | 完全一致的运行环境 |
| 依赖冲突 | 容器隔离 |
| 部署复杂 | 一条命令启动 |
| 扩展困难 | 容器编排 |
| 环境配置耗时 | 预构建镜像 |
16.2 基础 Dockerfile
16.2.1 最小 OpenCV 镜像
# Dockerfile.opencv
FROM python:3.11-slim
# 安装系统依赖(OpenCV 需要)
RUN apt-get update && apt-get install -y --no-install-recommends \
libgl1-mesa-glx \
libglib2.0-0 \
libsm6 \
libxrender1 \
libxext6 \
&& rm -rf /var/lib/apt/lists/*
# 安装 OpenCV
RUN pip install --no-cache-dir opencv-python-headless numpy
WORKDIR /app
COPY . .
CMD ["python", "main.py"]
16.2.2 生产级镜像
# Dockerfile.production
FROM python:3.11-slim AS base
# 安装系统依赖
RUN apt-get update && apt-get install -y --no-install-recommends \
libgl1-mesa-glx \
libglib2.0-0 \
libsm6 \
libxrender1 \
libxext6 \
libgstreamer1.0-0 \
libavcodec-dev \
libavformat-dev \
libswscale-dev \
&& rm -rf /var/lib/apt/lists/*
WORKDIR /app
# 先安装依赖(利用 Docker 缓存)
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
# 复制应用代码
COPY . .
# 健康检查
HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 \
CMD python -c "import cv2; assert cv2.__version__" || exit 1
# 非 root 用户
RUN useradd -m appuser
USER appuser
EXPOSE 8000
CMD ["python", "main.py"]
16.3 GPU Docker 镜像
16.3.1 CUDA 基础镜像
# Dockerfile.gpu
FROM nvidia/cuda:12.2.0-runtime-ubuntu22.04
# 安装 Python
RUN apt-get update && apt-get install -y \
python3 python3-pip \
libgl1-mesa-glx libglib2.0-0 \
&& rm -rf /var/lib/apt/lists/*
# 安装 OpenCV(含 CUDA)
RUN pip install --no-cache-dir \
opencv-contrib-python-headless \
numpy
# 验证 CUDA 支持
RUN python3 -c "import cv2; print(cv2.getBuildInformation())"
WORKDIR /app
COPY . .
CMD ["python3", "main.py"]
16.3.2 自定义编译 OpenCV + CUDA
# Dockerfile.opencv-cuda
FROM nvidia/cuda:12.2.0-devel-ubuntu22.04 AS builder
RUN apt-get update && apt-get install -y \
build-essential cmake git python3-dev python3-numpy \
libjpeg-dev libpng-dev libtiff-dev \
libavcodec-dev libavformat-dev libswscale-dev \
libgtk-3-dev libtbb-dev libeigen3-dev \
&& rm -rf /var/lib/apt/lists/*
# 下载 OpenCV 源码
RUN git clone --depth 1 --branch 4.10.0 \
https://github.com/opencv/opencv.git /opt/opencv && \
git clone --depth 1 --branch 4.10.0 \
https://github.com/opencv/opencv_contrib.git /opt/opencv_contrib
# 编译
RUN mkdir /opt/opencv/build && cd /opt/opencv/build && cmake \
-D CMAKE_BUILD_TYPE=Release \
-D CMAKE_INSTALL_PREFIX=/usr/local \
-D OPENCV_EXTRA_MODULES_PATH=/opt/opencv_contrib/modules \
-D WITH_CUDA=ON \
-D CUDA_ARCH_BIN="7.5,8.0,8.6,8.9" \
-D WITH_CUDNN=ON \
-D OPENCV_DNN_CUDA=ON \
-D BUILD_EXAMPLES=OFF \
-D BUILD_TESTS=OFF \
-D PYTHON3_EXECUTABLE=/usr/bin/python3 \
.. && make -j$(nproc) && make install && ldconfig
# 最终镜像(只包含运行时)
FROM nvidia/cuda:12.2.0-runtime-ubuntu22.04
RUN apt-get update && apt-get install -y \
python3 python3-numpy libgl1-mesa-glx libglib2.0-0 \
&& rm -rf /var/lib/apt/lists/*
COPY --from=builder /usr/local /usr/local
RUN ldconfig
WORKDIR /app
COPY . .
CMD ["python3", "main.py"]
16.3.3 运行 GPU 容器
# 运行 GPU 容器
docker run --gpus all -it --rm \
-v $(pwd):/app \
opencv-gpu:latest \
python3 main.py
# 指定 GPU
docker run --gpus '"device=0"' -it --rm opencv-gpu:latest
16.4 无头(Headless)渲染
服务器环境通常没有显示器,需要特殊处理 imshow。
16.4.1 使用 headless 包
# 使用 headless 版本(无 GUI 依赖)
RUN pip install opencv-python-headless
16.4.2 保存为文件替代显示
# 替代 imshow
# cv2.imshow("result", img) # 会报错
# 方法 1: 保存为文件
cv2.imwrite("/app/output/result.jpg", img)
# 方法 2: 生成 base64 用于 Web
import base64
def image_to_base64(image, fmt=".jpg"):
_, buffer = cv2.imencode(fmt, image)
return base64.b64encode(buffer).decode("utf-8")
# 方法 3: Matplotlib 保存
import matplotlib.pyplot as plt
plt.imshow(cv2.cvtColor(img, cv2.COLOR_BGR2RGB))
plt.savefig("output.png", dpi=150, bbox_inches="tight")
16.5 批量处理
"""
batch_processor.py — Docker 批量图像处理
"""
import cv2
import os
import glob
from pathlib import Path
def batch_process(input_dir, output_dir, process_func):
os.makedirs(output_dir, exist_ok=True)
files = glob.glob(os.path.join(input_dir, "*.*"))
supported = {".jpg", ".jpeg", ".png", ".bmp", ".tiff"}
total = 0
for filepath in files:
ext = Path(filepath).suffix.lower()
if ext not in supported:
continue
img = cv2.imread(filepath)
if img is None:
continue
result = process_func(img)
output_path = os.path.join(output_dir, Path(filepath).name)
cv2.imwrite(output_path, result)
total += 1
print(f"处理完成: {total} 个文件")
# 使用
# batch_process("/app/input", "/app/output", my_process_func)
16.6 微服务部署
FastAPI 图像处理服务
"""
api_server.py — OpenCV 微服务
"""
from fastapi import FastAPI, UploadFile, File
from fastapi.responses import StreamingResponse
import cv2
import numpy as np
import io
app = FastAPI(title="OpenCV 图像处理服务")
@app.post("/detect_edges")
async def detect_edges(file: UploadFile = File(...)):
# 读取上传文件
contents = await file.read()
nparr = np.frombuffer(contents, np.uint8)
img = cv2.imdecode(nparr, cv2.IMREAD_COLOR)
# 处理
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
edges = cv2.Canny(gray, 50, 150)
# 编码返回
_, buffer = cv2.imencode(".jpg", edges)
return StreamingResponse(
io.BytesIO(buffer.tobytes()),
media_type="image/jpeg"
)
@app.post("/detect_objects")
async def detect_objects(file: UploadFile = File(...)):
contents = await file.read()
nparr = np.frombuffer(contents, np.uint8)
img = cv2.imdecode(nparr, cv2.IMREAD_COLOR)
# YOLO 检测(示例)
# detections = yolo_detector.detect(img)
return {"status": "ok", "objects": []}
# docker run -p 8000:8000 opencv-api
# Dockerfile.api
FROM python:3.11-slim
RUN apt-get update && apt-get install -y \
libgl1-mesa-glx libglib2.0-0 \
&& rm -rf /var/lib/apt/lists/*
RUN pip install --no-cache-dir \
opencv-python-headless \
numpy \
fastapi \
uvicorn
WORKDIR /app
COPY . .
EXPOSE 8000
CMD ["uvicorn", "api_server:app", "--host", "0.0.0.0", "--port", "8000"]
16.7 Docker Compose
# docker-compose.yml
version: "3.8"
services:
opencv-api:
build:
context: .
dockerfile: Dockerfile.api
ports:
- "8000:8000"
volumes:
- ./data:/app/data
environment:
- MODEL_PATH=/app/models/yolov8n.onnx
- CONF_THRESHOLD=0.5
deploy:
resources:
reservations:
devices:
- driver: nvidia
count: 1
capabilities: [gpu]
restart: unless-stopped
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:8000/health"]
interval: 30s
timeout: 10s
retries: 3
worker:
build:
context: .
dockerfile: Dockerfile.gpu
volumes:
- ./data/input:/app/input
- ./data/output:/app/output
deploy:
resources:
reservations:
devices:
- driver: nvidia
count: 1
capabilities: [gpu]
16.8 镜像优化
| 技巧 | 效果 | 说明 |
|---|
| 使用 slim 基础镜像 | 减少 50%+ | python:3.11-slim |
| 多阶段构建 | 减少 70%+ | 编译层不进最终镜像 |
| 合并 RUN 指令 | 减少层数 | 减少镜像大小 |
| .dockerignore | 加速构建 | 排除不需要的文件 |
| pip –no-cache-dir | 减少 50MB+ | 不缓存 pip 下载 |
| headless 包 | 减少依赖 | 无 GUI 依赖 |
# .dockerignore
.git
__pycache__
*.pyc
*.pyo
public/
*.md
tests/
.env
16.9 常见 Docker 问题
| 问题 | 解决方案 |
|---|
libGL.so.1 not found | 安装 libgl1-mesa-glx 或使用 headless |
cannot connect to display | 使用 headless 版本 |
| GPU 不可用 | --gpus all + nvidia-docker |
| 容器内中文乱码 | 安装字体 fonts-noto-cjk |
| 图像颜色异常 | 检查 BGR/RGB 转换 |
| 内存溢出 | 限制输入图像大小 |
16.10 扩展阅读
本章小结: 掌握了 OpenCV 的 Docker 容器化部署,包括基础镜像、GPU 镜像、无头渲染、批量处理和微服务架构。