前言

最近在基于全爱科技 HOUYI-300U1 (搭载华为 Ascend 310P3 推理卡) 的边缘设备上部署视频追踪算法。由于宿主机环境比较杂乱,为了保证算法的可移植性和环境隔离,我决定使用 Docker 进行部署。

在这个过程中,遇到了宿主机驱动与容器 CANN 版本匹配动态库路径 (libascend_hal.so) 缺失等典型坑点。本文记录了从环境摸排到 Docker 成功跑通全流程,希望能帮助到同样在昇腾平台上折腾容器化的朋友。

1. 摸排宿主机环境 (最关键的一步)

在构建镜像前,必须搞清楚宿主机到底装了什么版本的驱动和固件,容器内的 CANN 版本必须与宿主机驱动兼容

1.1 查看 NPU 状态与驱动版本

使用 npu-smi info 查看:

npu-smi info
# 输出显示:
# Version: 23.0.1
# Chip: 310P3

驱动版本为 23.0.1

1.2 确认宿主机 CANN 版本

这一步极具迷惑性。查看 version.cfg 时,我看到了一串 7.6.0...:8.0.0 的数字:

cat /usr/local/Ascend/ascend-toolkit/latest/version.cfg
# runtime_running_version=[7.6.0.1.220:8.0.0]
# ...

解读: 不要被冒号左边的内部版本号干扰,冒号右边的 8.0.0 才是真正的对外发布版本

为了双重确认,查看安装信息文件:

cat /usr/local/Ascend/ascend-toolkit/latest/x86_64-linux/ascend_toolkit_install.info
# version=8.0.0

结论: 宿主机环境为 Driver 23.0.1 + CANN 8.0.0。为了由内而外的最大兼容性,决定直接提取宿主机上的 .run 安装包用于镜像构建。

1.3 提取安装包

直接在宿主机搜索现成的安装包,避免去官网下载的版本不一致:

find / -name "*Ascend-cann-toolkit*.run" 2>/dev/null
# 找到:/home/HwHiAiUser/Ascend-cann-toolkit_8.0.0_linux-x86_64.run

将此文件复制到 Docker 构建目录。


2. 构建 Docker 镜像

为了保证系统级库(glibc 等)与宿主机内核(EulerOS)最完美匹配,基础镜像选择了 openEuler 22.03 LTS

Dockerfile 编写思路

  1. 换源:openEuler 默认源在国内可能较慢,替换为华为云源。
  2. 分层构建:将系统依赖、Python 编译依赖、CANN 安装分层,利用缓存加速。
  3. 环境变量 (核心):这是最容易踩坑的地方,特别是 LD_LIBRARY_PATH

完整 Dockerfile

# Dockerfile for CANN 8.0.0 on 310P
# Base Image: openEuler 22.03 LTS
FROM openeuler/openeuler:22.03-lts

USER root

# 1. 基础环境配置:换源与更新
RUN sed -i "s@http://repo.openeuler.org@http://repo.huaweicloud.com/openeuler@g" /etc/yum.repos.d/openEuler.repo && \
    dnf clean all && \
    dnf makecache && \
    dnf update -y

# 2. 安装编译工具链 (gcc, cmake, git)
RUN dnf install -y gcc gcc-c++ make cmake git

# 3. 安装 Python 编译依赖
RUN dnf install -y zlib-devel bzip2-devel openssl-devel xz-devel libffi-devel python3-pip python3-devel

# 4. 安装图形处理依赖 (OpenCV/Pillow 需要)
RUN dnf install -y mesa-libGL glib2 libjpeg-turbo-devel

# 5. 安装常用工具
RUN dnf install -y wget curl net-tools pciutils which tar && dnf clean all

# 6. Python 环境软链
RUN ln -sf /usr/bin/python3 /usr/bin/python

# 7. 安装 CANN 前置 Python 依赖
RUN pip3 install --no-cache-dir \
    numpy attrs decorator sympy cffi pyyaml pathlib2 psutil protobuf scipy requests absl-py \
    -i https://pypi.tuna.tsinghua.edu.cn/simple/

# 8. 安装 CANN Toolkit 8.0.0 (使用宿主机同款包)
COPY Ascend-cann-toolkit_8.0.0_linux-x86_64.run /tmp/
RUN chmod +x /tmp/Ascend-cann-toolkit_8.0.0_linux-x86_64.run && \
    /tmp/Ascend-cann-toolkit_8.0.0_linux-x86_64.run --install --install-for-all --install-path=/usr/local/Ascend --quiet && \
    rm /tmp/Ascend-cann-toolkit_8.0.0_linux-x86_64.run

# 9. 配置环境变量 (重点!)
ENV ASCEND_HOME=/usr/local/Ascend
ENV ASCEND_TOOLKIT_HOME=${ASCEND_HOME}/ascend-toolkit/latest
ENV PATH=${ASCEND_TOOLKIT_HOME}/bin:${ASCEND_TOOLKIT_HOME}/compiler/ccec_compiler/bin:${PATH}
ENV PYTHONPATH=${ASCEND_TOOLKIT_HOME}/python/site-packages:${ASCEND_TOOLKIT_HOME}/opp/built-in/op_impl/ai_core/tbe:${PYTHONPATH}
ENV ASCEND_OPP_PATH=${ASCEND_TOOLKIT_HOME}/opp
ENV ASCEND_AICPU_PATH=${ASCEND_TOOLKIT_HOME}

# ⚠️ 填坑指南:必须手动包含宿主机挂载进来的 driver 路径,否则会报 libascend_hal.so 找不到
ENV LD_LIBRARY_PATH=/usr/local/Ascend/driver/lib64/driver:/usr/local/Ascend/driver/lib64/common:/usr/local/Ascend/driver/lib64:${ASCEND_TOOLKIT_HOME}/lib64:${ASCEND_TOOLKIT_HOME}/lib64/plugin/opskernel:${ASCEND_TOOLKIT_HOME}/lib64/plugin/nnengine:${ASCEND_TOOLKIT_HOME}/opp/built-in/op_impl/ai_core/tbe/op_tiling:${LD_LIBRARY_PATH}

# 10. 业务层构建 (使用 uv 加速)
WORKDIR /app
RUN pip3 install uv -i https://pypi.tuna.tsinghua.edu.cn/simple/
COPY pyproject.toml uv.lock README.md ./
ENV UV_HTTP_TIMEOUT=1800 UV_PYTHON_PREFERENCE=system UV_COMPILE_BYTECODE=1
RUN uv sync -v --index-url https://pypi.tuna.tsinghua.edu.cn/simple/

COPY . .
ENV PYTHONPATH=/app:$PYTHONPATH
RUN chmod +x lth_tools/run_video_config.sh

CMD ["/bin/bash", "-c", "source /usr/local/Ascend/ascend-toolkit/set_env.sh && ./lth_tools/run_video_config.sh"]

3. 编排运行 (Docker Compose)

在运行阶段,有两个核心概念:

  1. 特权模式与设备挂载:容器需要访问 /dev/davinciX 设备。
  2. 驱动映射:容器内只有 Toolkit(软件),内核态的 Driver 必须从宿主机挂载进去

docker-compose.yml

version: '3.8'

services:
  tracking-algo:
    container_name: tracking-algo
    image: cann:8.0.0-euler
    env_file:
      - .env
    # 开启特权模式,让容器能识别所有硬件设备
    privileged: true
    
    # 显式声明设备 (双重保险)
    devices:
      - /dev/davinci0:/dev/davinci0             # 310P3 NPU 核心
      - /dev/davinci_manager:/dev/davinci_manager # 管理控制设备
      - /dev/hisi_hdc:/dev/hisi_hdc             # 高速通道
      - /dev/devmm_svm:/dev/devmm_svm           # 内存管理
      
    volumes:
      # ⚠️ 核心:将宿主机的驱动目录只读挂载到容器
      - /usr/local/Ascend/driver:/usr/local/Ascend/driver:ro
      # 挂载代码目录,方便开发调试
      - .:/app
      # 避免本地 .venv 覆盖容器内的环境
      - /app/.venv
    
    # 视频处理通常需要较大的共享内存
    shm_size: '8gb'
    tty: true
    
    # 启动命令:激活环境 -> 加载CANN变量 -> 运行脚本
    command:
      - sh
      - -c
      - |
        source .venv/bin/activate && \
        source /usr/local/Ascend/ascend-toolkit/set_env.sh && \
        chmod +x ./lth_tools/run_video_config.sh && \
        ./lth_tools/run_video_config.sh

4. 踩坑与验证

4.1 遇到的主要报错:libascend_hal.so: No such file

起初运行时,Python 报错找不到 libascend_hal.so
原因:宿主机的驱动文件并未直接放在 /usr/local/Ascend/driver/lib64 下,而是藏在更深一层的 lib64/driver 目录中。
解决:在 Dockerfile 的 LD_LIBRARY_PATH 中显式添加了 /usr/local/Ascend/driver/lib64/driver

4.2 运行成功

启动容器后,查看日志:

docker logs -f tracking-algo

看到如下日志,说明 ACL 初始化成功,模型加载完毕,NPU 正在全速工作!

[INFO] acl init success
[INFO] open device 0 success
[INFO] create new context
[INFO] load model ./track_weights/detection/yolov8n-pose-simplify-nms.om success
...
Processing frames:   1% 350/28826 [00:14<19:27, 24.39frame/s]

5. 总结

在昇腾设备上进行 Docker 开发,核心在于**“软硬分离”**的理解:

  1. Image 里装软件:CANN Toolkit (用户态库,负责编译和接口调用)。
  2. Host 提供硬件和驱动:通过 volumes 把 Driver (.so) 挂载进去,通过 devices 把 NPU 挂载进去。
  3. 胶水配置:通过 LD_LIBRARY_PATH 让 Image 里的软件能找到 Host 挂进来的驱动文件。

希望这篇记录能帮你少踩几个坑,丝滑部署!

参考资料

Logo

鲲鹏昇腾开发者社区是面向全社会开放的“联接全球计算开发者,聚合华为+生态”的社区,内容涵盖鲲鹏、昇腾资源,帮助开发者快速获取所需的知识、经验、软件、工具、算力,支撑开发者易学、好用、成功,成为核心开发者。

更多推荐