昇腾CANN cann-spack-package 环境隔离实战:Spack 多版本 CANN 并行安装与自定义 spec 编写
CANN 开发场景:同一台服务器上要同时维护三个版本的 CANN——8.0.RC2(主力训练集群)、8.0.3(新算子验证)、8.1.beta(下一代架构评测)。传统方式:手动解压 run 包到 /usr/local/Ascend/ascend-toolkit/latest → 换版本 = 卸载 + 重装 = 30 分钟。三个版本同时存在?符号链接地狱。
cann-spack-package 基于 Spack 包管理器——每个 CANN 版本是一个独立的 Spack package,安装到独立的 prefix(/opt/spack/opt/linux-ubuntu20.04-x86_64/.../ascend-toolkit-8.0.3-xxx),通过 spack load 切换版本。Spack 的 concretizer 自动解决依赖冲突(bisheng 版本、Python 版本、系统库版本),每个 CANN 版本拥有完整的独立环境。
Spack 基础:cann-spack-package 的目录结构
cann-spack-package/
├── repo.yaml # Spack repo 注册文件
├── packages/ # CANN 组件的 Spack package 定义
│ ├── ascend-toolkit/
│ │ └── package.py # CANN 主工具链(CANN 8.0.3)
│ ├── ascend-cann/
│ │ └── package.py # CANN 社区版(开源算子库 + 编译工具链)
│ ├── ascend-driver/
│ │ └── package.py # NPU 驱动(dkms 内核模块)
│ ├── ascend-firmware/
│ │ └── package.py # 昇腾 MCU 固件
│ ├── ascend-hccl/
│ │ └── package.py # HCCL 通信库(独立打包)
│ ├── ascend-toolkit-kernels/
│ │ └── package.py # 算子二进制包(ops-* 预编译)
│ ├── bisheng/
│ │ └── package.py # 毕昇编译器(CANN 依赖)
│ ├── pyasc/
│ │ └── package.py # pyasc Python 包
│ └── ascend-mindspore/
│ └── package.py # MindSpore + CANN backend
├── configs/
│ ├── packages.yaml # 全局包配置(版本偏好、编译器选择)
│ ├── compilers.yaml # 编译器配置(gcc/bisheng/clang)
│ └── repos.yaml # 外部 repo 配置(builtin + cann-spack-package)
├── environments/ # 预定义的 spack 环境
│ ├── cann-8.0.3/
│ │ ├── spack.yaml # CANN 8.0.3 完整环境定义
│ │ └── spack.lock # concretizer 锁文件(精确版本快照)
│ ├── cann-8.0.rc2/
│ │ ├── spack.yaml # CANN 8.0.RC2 环境定义
│ │ └── spack.lock
│ └── cann-edge/ # 边缘计算精简版
│ ├── spack.yaml
│ └── spack.lock
├── scripts/
│ ├── setup.sh # 一键初始化 Spack + 注册 CANN repo
│ ├── install-env.sh # 按环境名安装所有包
│ └── activate.sh # 激活特定 CANN 环境
└── README.md
核心 package.py:ascend-toolkit 的多版本定义
# cann-spack-package/packages/ascend-toolkit/package.py
#
# CANN 工具链的 Spack package 定义
# 支持 8.0.RC1 / 8.0.RC2 / 8.0.3 / 8.1.beta 四个版本并行安装
from spack.package import *
from spack.pkg.builtin.ascend_cann import AscendCannPackage
class AscendToolkit(AscendCannPackage):
"""CANN (Compute Architecture for Neural Networks) - Ascend Toolkit
CANN is Huawei's heterogeneous compute architecture for Ascend NPUs.
This package provides the full development toolkit including:
- AscendCL (Application & operator development)
- AOL (Ascend Operator Library)
- AOE (Ascend Optimization Engine)
- Graph Engine (GE)
- Runtime + Driver
- HCCL (Huawei Collective Communication Library)
- BiSheng Compiler
"""
homepage = "https://www.hiascend.com/software/cann"
# ====== 多版本并行定义 ======
# 每个 version() 指定一个可安装的 CANN 版本
# sha256 用于校验 run 包完整性
# CANN 8.0.3 (stable, recommended)
version("8.0.3",
sha256="a3f2c8b9d1e4f5a6b7c8d9e0f1a2b3c4d5e6f7a8b9c0d1e2f3a4b5c6d7e8f9a0",
preferred=True) # preferred=True → concretizer 默认选这个
# CANN 8.0.RC2 (legacy, still used in production)
version("8.0.rc2",
sha256="b4c3d2e1f0a9b8c7d6e5f4a3b2c1d0e9f8a7b6c5d4e3f2a1b0c9d8e7f6a5",
deprecated=True) # deprecated=True → 可用但推荐迁移到 8.0.3
# CANN 8.1.beta (preview, next-gen)
version("8.1.beta",
sha256="c5d4e3f2a1b0c9d8e7f6a5b4c3d2e1f0a9b8c7d6e5f4a3b2c1d0e9f8a7b6",
preferred=False)
# ====== 依赖声明 ======
# 所有 CANN 版本的共同依赖
# 编译器依赖:毕昇编译器 >= 3.3.0(CANN 8.0+ 要求)
depends_on("bisheng@3.3.0:", type="build")
# 运行时依赖:NPU 驱动(内核模块,系统级)
depends_on("ascend-driver@1.0.firmware", type="run", when="@8.0:")
# 算子库依赖(可选):
# 如果安装 ascend-toolkit-kernels,自动注册到 CANN 算子路径
depends_on("ascend-toolkit-kernels", type="run", when="+kernels")
depends_on("ascend-toolkit-kernels@8.0.3", type="run", when="@8.0.3+kernels")
# HCCL 通信库(独立打包,不同版本 CANN 可能用不同 HCCL)
depends_on("ascend-hccl@8.0.3", type="run", when="@8.0.3")
depends_on("ascend-hccl@8.0.rc2", type="run", when="@8.0.rc2")
# Python 绑定(AscendCL Python API, pyasc)
depends_on("python@3.8:3.10", type=("build", "run"))
depends_on("py-numpy", type="run")
# ====== Variant(可选特性) ======
variant("kernels", default=True,
description="Install pre-built operator kernels")
variant("torch", default=True,
description="Install PyTorch CANN backend (torch-npu)")
variant("mindspore", default=False,
description="Install MindSpore CANN backend")
variant("debug", default=False,
description="Build with debug symbols")
# ====== 下载源(多个 mirror,自动 fallback) ======
def url_for_version(self, version):
"""根据版本号构造下载 URL"""
base = "https://ascend-repo.obs.cn-north-4.myhuaweicloud.com"
if version >= Version("8.1"):
base = "https://ascend-repo-beta.obs.cn-north-4.myhuaweicloud.com"
return (
f"{base}/CANN/{version}/"
f"Ascend-cann-toolkit_{version}_linux-{self.arch}.run"
)
# ====== 安装步骤 ======
def install(self, spec, prefix):
"""
CANN 安装分三步:
1. 自解压 .run 包
2. 运行自带安装脚本(指定 prefix)
3. 配置环境变量 + 符号链接
"""
# Step 1: 自解压 run 包
run_file = self.stage.archive_file # 下载的 .run 文件
chmod = which("chmod")
chmod("+x", run_file)
# 解压到临时目录
extract_dir = join_path(self.stage.source_path, "extracted")
mkdirp(extract_dir)
# .run 包支持 --extract 参数
Executable(run_file)(
"--extract=" + extract_dir,
"--quiet"
)
# Step 2: 运行自带安装脚本(指定 prefix)
# CANN run 包内含 install.sh
with working_dir(extract_dir):
install_script = Executable("./install.sh")
install_script(
"--prefix=" + prefix, # Spack 管理的安装路径
"--install-type=full", # 全量安装(非 minimal)
"--install-for-all", # 所有用户可访问
"--quiet"
)
# Step 3: 环境变量 + 符号链接
# Spack 通过 setup_run_environment() 自动设置
# 这里只需确保安装目录结构正确
# 验证关键文件存在
for check_file in [
"compiler/bin/bisheng",
"runtime/lib64/libascendcl.so",
"opp/built-in/op_impl/ai_core/tbe/op_info_cfg",
]:
if not os.path.exists(join_path(prefix, check_file)):
raise InstallError(
f"Missing critical file: {check_file}. "
f"CANN installation may be incomplete."
)
# 可选:安装 PyTorch CANN backend
if spec.satisfies("+torch"):
pip = which("pip")
pip("install", f"torch-npu=={spec.version}")
def setup_run_environment(self, env):
"""
运行时环境变量(用户执行 spack load ascend-toolkit 时自动设置)
"""
prefix = self.prefix
# 基础路径
env.set("ASCEND_HOME_PATH", prefix)
env.set("ASCEND_TOOLKIT_HOME", prefix)
# 编译器
env.prepend_path("PATH", join_path(prefix, "compiler", "bin"))
env.prepend_path("LD_LIBRARY_PATH", join_path(prefix, "compiler", "lib64"))
# 运行时
env.prepend_path("LD_LIBRARY_PATH", join_path(prefix, "runtime", "lib64"))
env.prepend_path("PATH", join_path(prefix, "runtime", "bin"))
# 算子库
env.set("ASCEND_OPP_PATH", join_path(prefix, "opp"))
# Python 绑定
python_ver = f"python{self.spec['python'].version.up_to(2)}"
env.prepend_path("PYTHONPATH",
join_path(prefix, "python", "site-packages"))
def setup_dependent_build_environment(self, env, dependent_spec):
"""
下游包的构建环境(如 torch-npu、mindspore 需要 CANN 头文件和库)
"""
env.set("ASCEND_TOOLKIT_HOME", self.prefix)
env.prepend_path("CMAKE_PREFIX_PATH", self.prefix)
多版本并行安装:spack.yaml 环境定义
# cann-spack-package/environments/cann-8.0.3/spack.yaml
#
# CANN 8.0.3 完整环境定义
# spack install → 安装所有 12 个包 + 依赖 → spack load → 激活环境
spack:
# 环境元数据
specs:
# 核心:CANN 工具链 + 算子库 + 通信库
- ascend-toolkit@8.0.3 +kernels +torch
# 可选:MindSpore(如不需要可注释掉)
# - ascend-mindspore@2.2.0
# 开发工具
- pyasc@8.0.3 ~mindspore +torch
- cmake@3.27
# 推理引擎
- ascend-toolkit-kernels@8.0.3
# Concretizer 配置
concretizer:
unify: true # 所有包统一 concretize(避免冲突)
reuse: true # 复用已安装包
# 包配置(版本偏好、变体、编译器)
packages:
# 全局编译器:使用系统 gcc 12.3.0 或毕昇编译器
all:
compiler: [gcc@12.3.0, bisheng@3.3.0]
providers:
mpi: [openmpi@4.1]
blas: [openblas]
# ascend-toolkit 特定配置
ascend-toolkit:
variants: +kernels +torch ~mindspore ~debug
version: [8.0.3] # 优先 8.0.3
# Python 版本固定
python:
version: [3.9.18]
variants: +optimizations +ssl
# 使用外部预装包(避免编译)
cmake:
buildable: false # 使用系统 cmake
externals:
- spec: cmake@3.27.4
prefix: /usr
# 视图(View):将所有包链接到统一目录
view: true # spack view → $SPACK_ENV/.spack-env/view/
# 模块文件生成
modules:
default:
enable: [tcl, lmod]
roots:
lmod: /opt/modules/cann
tcl: /opt/modules/cann-tcl
一键初始化 + 安装脚本(scripts/setup.sh)
#!/bin/bash
# cann-spack-package/scripts/setup.sh
# 一键初始化 CANN Spack 环境
set -e
SPACK_ROOT="${SPACK_ROOT:-/opt/spack}"
CANN_REPO_DIR="$(cd "$(dirname "$0")/.." && pwd)"
echo "============================================"
echo "CANN Spack Package Manager Setup"
echo "============================================"
echo ""
# Step 1: 安装 Spack(如未安装)
if [ ! -d "$SPACK_ROOT" ]; then
echo "[1/5] Installing Spack..."
git clone -c feature.manyFiles=true \
https://github.com/spack/spack.git "$SPACK_ROOT"
cd "$SPACK_ROOT"
git checkout v0.21.0 # 锁定 Spack 版本
fi
# Step 2: 激活 Spack
echo "[2/5] Activating Spack..."
source "$SPACK_ROOT/share/spack/setup-env.sh"
# Step 3: 注册 cann-spack-package 为 Spack repo
echo "[3/5] Registering CANN Spack repo..."
spack repo add "$CANN_REPO_DIR"
# 验证注册成功
if spack repo list | grep -q "cann-spack-package"; then
echo " ✓ CANN Spack repo registered"
else
echo " ✗ Failed to register CANN Spack repo"
exit 1
fi
# Step 4: 配置系统编译器
echo "[4/5] Configuring compilers..."
spack compiler find # 自动检测 gcc / clang
# 手动注册毕昇编译器(如果已安装)
if [ -f "/opt/bisheng/3.3.0/bin/clang" ]; then
spack compiler add --scope site /opt/bisheng/3.3.0
echo " ✓ BiSheng 3.3.0 registered"
fi
# Step 5: Mirror 配置(国内镜像加速)
echo "[5/5] Configuring mirrors..."
spack mirror add cann-mirror \
https://mirrors.huaweicloud.com/ascend/cann-spack/
echo ""
echo "============================================"
echo "Setup complete! Next steps:"
echo ""
echo " Install CANN 8.0.3:"
echo " cd $CANN_REPO_DIR/environments/cann-8.0.3"
echo " spack env activate ."
echo " spack install"
echo ""
echo " Load CANN environment:"
echo " spack load ascend-toolkit@8.0.3"
echo ""
echo " Switch to different version:"
echo " spack unload ascend-toolkit@8.0.3"
echo " spack load ascend-toolkit@8.0.rc2"
echo "============================================"
实际操作:三版本并存
# === CANN 三版本并行安装与切换 ===
# 1. 安装三个版本(并行,互不干扰)
cd cann-spack-package/environments/cann-8.0.rc2/
spack env activate . && spack install # → /opt/spack/opt/.../ascend-toolkit-8.0.rc2-xxx/
cd cann-spack-package/environments/cann-8.0.3/
spack env activate . && spack install # → /opt/spack/opt/.../ascend-toolkit-8.0.3-xxx/
cd cann-spack-package/environments/cann-8.1.beta/
spack env activate . && spack install # → /opt/spack/opt/.../ascend-toolkit-8.1.beta-xxx/
# 2. 查看已安装版本
spack find ascend-toolkit
# ────────────────────────────────────────────────────
# ascend-toolkit@8.0.rc2 /opt/spack/opt/.../xxxx ← 训练集群
# ascend-toolkit@8.0.3 /opt/spack/opt/.../yyyy ← 新算子验证
# ascend-toolkit@8.1.beta /opt/spack/opt/.../zzzz ← 下一代评测
# 3. 动态切换版本(秒级)
# 当前使用 8.0.rc2(训练任务)
spack load ascend-toolkit@8.0.rc2
python -c "import torch_npu; print(torch_npu.__version__)" # → 8.0.RC2
# 切换到 8.0.3(测试新算子)
spack unload ascend-toolkit
spack load ascend-toolkit@8.0.3
python -c "import torch_npu; print(torch_npu.__version__)" # → 8.0.3
# 切换到 8.1.beta(性能评测)
spack unload ascend-toolkit
spack load ascend-toolkit@8.1.beta
自定义 package:第三方库的 CANN 适配
# 为团队内部项目定义 Spack package
# myproject/packages/my-cann-app/package.py
class MyCannApp(CMakePackage):
"""Custom CANN application built against ascend-toolkit"""
version("1.2.0", sha256="abc...")
# 依赖 CANN 8.0.3(version 约束)
depends_on("ascend-toolkit@8.0.3", type=("build", "run"))
# 依赖 CANN 提供的算子库
depends_on("ascend-toolkit-kernels@8.0.3", type="run")
def cmake_args(self):
cann = self.spec["ascend-toolkit"]
return [
f"-DASCEND_HOME={cann.prefix}",
f"-DASCEND_OPP_PATH={cann.prefix}/opp",
"-DCMAKE_BUILD_TYPE=Release",
]
def setup_run_environment(self, env):
env.prepend_path("PATH", join_path(self.prefix, "bin"))
env.prepend_path("LD_LIBRARY_PATH", join_path(self.prefix, "lib"))
踩坑一:Spack concretizer 超时——55 个 warehouse 触发指数爆炸
# ❌ concretizer 同时求解 ascend-toolkit + 55 个算子库的依赖图
# → SAT solver 在 45 分钟内未找到解(组合爆炸)
# → CI timeout
# ✅ 分层 concretize:先求解核心包,再按组添加
# Step 1: 只 concretize 核心
cat > /tmp/core.yaml <<EOF
spack:
specs: [ascend-toolkit@8.0.3, ascend-hccl@8.0.3]
concretizer: {unify: true, reuse: true}
EOF
spack -e /tmp/core install
# Step 2: 复用核心 concrete hash → 添加算子库
spack install ascend-toolkit-kernels@8.0.3 ^/hash-of-core-toolkit
# → concretizer 只需求解算子库自身依赖(<1 秒)
踩坑二:spack build 中途失败——30 分钟编译后 COPTFLAGS 冲突
# ❌ ascend-toolkit build 进行到 30 分钟时失败
# Error: COPTFLAGS='-O3 -march=native -mtune=generic' conflicts with bisheng
# → 因为 Spack 全局编译 flag 注入了 -march=native,但毕昇编译器不认识这个 flag
# ✅ package.py 中 filter 掉不兼容的 compiler flag
class AscendToolkit(AscendCannPackage):
def flag_handler(self, name, flags):
"""
过滤掉毕昇编译器不兼容的 flag
"""
if name == "cflags" or name == "cxxflags":
# 毕昇(基于 LLVM)不认识 -march=native
# 默认用 -mcpu=tsv110(Ascend 910 的 ARM 核心)
if self.spec.satisfies("%bisheng"):
flags = [f for f in flags if "march" not in f]
flags.append("-mcpu=tsv110")
return (flags, None, None) # (cflags, cxxflags, fflags)
踩坑三:同机器上两个 CANN 版本的 Python 绑定冲突——import torch_npu 加载错误的 .so
# ❌ PATH 中残留旧版本的 PYTHONPATH
# export PYTHONPATH=/opt/spack/opt/.../ascend-toolkit-8.0.rc2-xxx/python:$PYTHONPATH
# spack load ascend-toolkit@8.0.3
# → 同时有 8.0.rc2 和 8.0.3 的 python 目录在 PYTHONPATH → 错误加载旧 .so
# ✅ 使用 Spack modulefiles(自动隔离版本)
# module load 比 spack load 更严格的路径管理
spack install --add ascend-toolkit@8.0.3
module use /opt/modules/cann
module load ascend-toolkit-8.0.3
# 验证:所有路径指向同一个 version
which python # → /opt/spack/.../ascend-toolkit-8.0.3-xxx/python/bin/python
ldd $(python -c "import torch_npu; print(torch_npu.__file__)") \
| grep ascend
# → libascendcl.so.8.0.3 ← 全部指向 8.0.3,没有 8.0.rc2 残留
cann-spack-package 用 Spack 包管理器实现 CANN 多版本并行安装。package.py 定义版本 + 依赖 + variant + 安装步骤 → spack.yaml 定义完整环境(12 个包 + concretizer 策略)→ spack install 安装到独立 prefix → spack load/unload 秒级切换版本(三版本并行共存:8.0.RC2/8.0.3/8.1.beta)。三个踩坑:concretizer SAT 55 包 45 分钟→分层 concretize 核心+算子库、毕昇编译器不认识 -march=native→filter 改为 -mcpu=tsv110、两版本 PYTHONPATH 残留→modulefiles 代替 script load。
鲲鹏昇腾开发者社区是面向全社会开放的“联接全球计算开发者,聚合华为+生态”的社区,内容涵盖鲲鹏、昇腾资源,帮助开发者快速获取所需的知识、经验、软件、工具、算力,支撑开发者易学、好用、成功,成为核心开发者。
更多推荐


所有评论(0)