跳到主要内容

L0.6 实操环境与安装基线

三维坐标 layer: L0(基础)level: Engineerpillar: 硬件架构

本文是 L0 新手村的收官篇,也是全站的「实操准入闸门」。目标不是讲透某个组件,而是把后续所有动手实验所需的环境、硬件、依赖一次性铺平——让你在进入 L1 微架构实战前,手里已经有一套可复现、可隔离、可观测的开发基线。

学习目标

  • 前置知识:读过 L0.1–L0.5;会用命令行、装过 pip 包、对 Docker 有耳闻即可。无需 K8s/运维经验。
  • 学完产出:① 看懂「驱动 → 容器运行时 → CUDA Toolkit → Python 环境」这条工具栈是如何分层咬合的,以及任一环错配会卡在哪;② 用「CPU 工作台 + 临时租卡」的思路,在本地硬核云端低成本两套方案间做成本最优编排;③ 掌握「按层装包」铁律,知道做哪一层只装哪一层;④ 亲手写一个最小 Dockerfile,在容器里跑通第一个 hello_infra.py,拿到「实操准入就绪」的凭证。
  • 阅读姿势:记住一句话——「能在容器里跑的,绝不污染宿主机;做哪一层实验,就只装哪一层的依赖。」 这是全站所有动手实验的环境契约。

背景与现状

做 AI Infra 最大的隐形成本,不是算法难度,而是 「环境地狱」:CUDA 版本与驱动错配、Python 包依赖打架、「在我机器上能跑」的不可复现性。一个成熟的 infra 工程师,第一项硬功夫就是把环境本身工程化——用容器锁定运行时、用版本管理器隔离 Python、用基础镜像统一 CUDA ABI。

从业界实践看,环境管理的演进同样是三句话:

  • 裸机时代:直接 pip install 到系统 Python,结果是「装一个新项目,废掉一个旧项目」。
  • 虚拟环境时代venv / conda 隔离 Python 依赖,但系统级的 CUDA / cuDNN / 驱动仍然全局共享,跨机器复现困难。
  • 容器化时代(当下):以 Docker + nvidia-container-toolkit 为标准,把「Python 包 + CUDA 运行时 + 系统库」整体打包成镜像,配合 kind(推荐;亦可用 minikube)在本地模拟 K8s 调度——这是云原生 AI 的事实基线。

业界信号:几乎所有主流框架(PyTorch、vLLM、Megatron-LM)都首推官方 Docker 镜像作为安装方式,正是因为「镜像即环境契约」。nvidia/cudapytorch/pytorch 系列镜像的海量拉取量,说明「容器化准入」已是大模型工程的默认起点。截至 2026 年,主流栈已是 CUDA 12.8(生产)~ 13.3(最新)+ PyTorch 2.9~2.13(2026-07 最新稳定 2.13)+ Python 3.11/3.12,但「锁版本、整体打包」的容器哲学始终不变。

本站的实操约定:能在容器里跑的,绝不污染宿主机做哪一层实验,就只装哪一层的依赖。本文给出这套约定的完整落地基线。

配套代码仓库github.com/blueyi/ai-infra-labs — 含 hello_infra.py、Dockerfile 与 scripts/run_all_labs.py 一键验证脚本。单卡 6GB 环境已验证通过。

原理与架构

理解实操基线,关键是看清两件事:工具栈是如何分层咬合的,以及两套硬件方案各自的取舍边界

2.1 工具栈分层关系图

自底向上读这张图:宿主机的 NVIDIA Driver 是一切 GPU 能力的内核态根基(它决定了你能用的 CUDA 最高版本)→ Docker Engine 提供隔离的运行时 → nvidia-container-toolkit 是把宿主 GPU「桥接」进容器的关键胶水(没有它,--gpus all 直接失败)→ 容器内的 CUDA Toolkit 提供 nvcc、cuBLAS 等编译与算子库 → 最上层是 Python 3.11 + uv/conda 隔离的包环境。旁路的 Ollama 用来快速验证「这台机器的显存到底能跑多大模型」,kind 则让你在单机上练习 K8s 调度而不必租真集群(详见 L1.7 K8s 核心对象)。

2.2 两套硬件方案对比

维度本地硬核玩家云端低成本玩家
典型配置RTX 3090 / 4090(24G)或 A100 / 昇腾 910B;内存 ≥ 32G无 GPU 云主机(编译 LLVM/MLIR、跑 CPU 实验)+ 按需租 GPU
GPU 来源自有显卡,常驻可用Colab / RunPod / Lambda 按小时租用
最适合做的层L1 微架构 profile、L3 多卡并行、L4 Serving 压测L0/L2 概念与编译实验、轻量算子验证、写代码
首付成本高(整机数万元)极低(按分钟计费,几元起)
复现性强(环境长期固定)中(每次开实例需重建环境,靠镜像/脚本固化)
核心痛点散热、功耗、驱动维护数据上传带宽、实例随时被回收、冷启动慢
建议策略重活(训练/压测)本地跑编译/写码在便宜 CPU 机,跑 GPU 算子时临时租卡

需要强调的是:两套方案不是二选一,而是 「CPU 机当工作台,GPU 卡当试验场」 的组合。绝大多数 L0–L2 的学习实验根本不需要 GPU(编译 MLIR、读源码、跑 CPU 版 PyTorch 验证逻辑),只在真正测算子性能时才租卡——这能把学习成本压到最低。

2.3 按层安装原则

全站遵循一条铁律:做哪一层,只装哪一层的包

  • L0/L1torch(CPU 或 CUDA 版)+ nvidia-ml-py(查显存)即可。
  • L2 编译:额外装 triton / LLVM / MLIR 工具链。
  • L3 训练:再加 deepspeed / megatron-core
  • L4 推理:再加 vllm / tensorrt-llm

这样每一层环境最小、冲突最少。当需要一键全量复现整站环境时,再用一份顶层 requirements.txt 锁版本(见下文动手实践一节)。

2.4 Docker 镜像分层与运行时隔离

理解 Dockerfile 不是「一串命令」,而是**分层文件系统(Union FS)**的叠加——每一行 RUN / COPY 产生一个新 layer,层与层之间可缓存复用。

实践原因
把不常变的放上面、常变的放下面COPY requirements.txt + pip installCOPY . 之前,改代码不 invalidate 依赖层
多阶段构建(multi-stage)build 阶段用 devel 镜像编译,runtime 阶段只 COPY 二进制,镜像从 8GB 降到 2GB
.dockerignore排除 .git__pycache__、大数据,避免 layer 膨胀与缓存失效
固定 digest 而非 :latestpython:3.11-slim@sha256:... 保证 CI 与本地一致
运行时隔离容器进程看不到宿主机其他进程;GPU 通过 nvidia-container-toolkit 选择性挂载设备
# 多阶段示例:编译与运行分离
FROM nvidia/cuda:12.6.0-devel-ubuntu22.04 AS builder
RUN pip install torch --index-url https://download.pytorch.org/whl/cu126
COPY build_ext.sh .
RUN ./build_ext.sh

FROM nvidia/cuda:12.6.0-runtime-ubuntu22.04
COPY --from=builder /opt/wheels /opt/wheels
RUN pip install /opt/wheels/*.whl
COPY app/ /app/
WORKDIR /app

查看镜像层:docker history hello-infra:gpu --no-trunc | head。层数过多会拖慢 push/pull——合并相邻 RUN apt 命令是常见优化。

动手实践:用 Docker 起最小可复现环境

实验目标:用一个最小 Dockerfile 从零构建一个隔离环境,在容器内跑通本站第一个 hello_infra.py——打印 PyTorch 版本、检测设备、跑一次 256×256 矩阵乘并计时。产出物:一段容器内的执行输出,证明你的「实操准入」已就绪。

双 venv 提示(ai-infra-labs 实测):32 个核心 lab 用默认 .venv 即可。AWQ / vLLM / 完整 datasets 链路建议另建 .venv-sysuv venv --python /usr/bin/python3),因为 pyenv 编译的 Python 若缺 _lzma 会导致 mlflow / datasets 无法 import。

3.1 Hello-Infra 脚本

# hello_infra.py —— 本站第一个 Infra 健康检查脚本
import time
import torch

print("=" * 48)
print(f"[Hello-Infra] torch version : {torch.__version__}")
cuda_ok = torch.cuda.is_available()
print(f"[Hello-Infra] CUDA available: {cuda_ok}")

dev = "cuda" if cuda_ok else "cpu"
if cuda_ok:
print(f"[Hello-Infra] GPU name : {torch.cuda.get_device_name(0)}")
print(f"[Hello-Infra] using device : {dev}")

# 一次小矩阵乘 + 计时(验证算子能落到设备上执行)
a = torch.randn(256, 256, device=dev)
b = torch.randn(256, 256, device=dev)

# 预热(首次会触发 kernel JIT / cuBLAS handle 初始化)
for _ in range(3):
_ = a @ b
if cuda_ok:
torch.cuda.synchronize()

t0 = time.perf_counter()
c = a @ b
if cuda_ok:
torch.cuda.synchronize() # 异步 kernel 必须同步后再停表
dt = (time.perf_counter() - t0) * 1e3

print(f"[Hello-Infra] 256x256 matmul: {dt:.3f} ms -> result sum={c.sum().item():.2f}")
print("=" * 48)
print(">>> 环境基线就绪,欢迎进入 AI Infra 实战。")

3.2 最小 Dockerfile —— CPU / Mac 路径(默认)

# Dockerfile.cpu —— 纯 CPU 最小镜像,适合 Mac / 无 GPU 云主机
FROM python:3.11-slim

WORKDIR /app

# 只装 CPU 版 torch,镜像最小化(不拉 CUDA 运行时)
RUN pip install --no-cache-dir \
torch --index-url https://download.pytorch.org/whl/cpu

COPY hello_infra.py /app/hello_infra.py

CMD ["python", "hello_infra.py"]

构建并运行:

# 构建镜像
docker build -f Dockerfile.cpu -t hello-infra:cpu .

# 运行(无需 GPU)
docker run --rm hello-infra:cpu
# 预期输出:CUDA available: False,using device: cpu,并打印 matmul 耗时

3.3 GPU 路径 —— NVIDIA CUDA 基础镜像

有 NVIDIA 显卡时,换用官方 CUDA 基础镜像,并在 docker run 时加 --gpus all 暴露设备:

# Dockerfile.gpu —— 基于 CUDA 运行时镜像
FROM nvidia/cuda:12.1.1-runtime-ubuntu22.04

WORKDIR /app

# 装 Python 3.11 与 pip
RUN apt-get update && apt-get install -y --no-install-recommends \
python3.11 python3-pip && \
rm -rf /var/lib/apt/lists/*

# 装匹配 CUDA 12.1 的 torch wheel(cu121)
RUN pip3 install --no-cache-dir \
torch --index-url https://download.pytorch.org/whl/cu121

COPY hello_infra.py /app/hello_infra.py

CMD ["python3", "hello_infra.py"]
# 前置:宿主机必须已装 NVIDIA Driver + nvidia-container-toolkit
docker build -f Dockerfile.gpu -t hello-infra:gpu .

# 关键:--gpus all 才能把宿主 GPU 暴露进容器
docker run --rm --gpus all hello-infra:gpu
# 预期输出:CUDA available: True,并打印 GPU 型号与更快的 matmul 耗时

3.4 全量复现:顶层 requirements.txt

当需要一键复现整站环境(跨层)时,用一份锁版本的清单:

# requirements.txt(示意,按需取消注释对应层;版本号截至 2026 年中,仅供参考)
# --- L0/L1 基线 ---
torch==2.9.0 # 锁定的稳定基线(2026-07 最新稳定线已到 2.13,装前以官方矩阵为准)
nvidia-ml-py
# --- L2 编译(可选)---
# triton # 已是 torch.compile/Inductor 在 GPU 上的默认 kernel 后端,随 torch 自带
# --- L3 训练(可选)---
# deepspeed # ZeRO;PyTorch 原生等价物是 FSDP2(torch 自带,无需额外装)
# --- L4 推理(可选)---
# vllm # V1 引擎;版本迭代极快,装前查 GitHub Releases

📅 2026 版本对齐提示:① CUDA 12.6 / 12.8 仍是生产主流;CUDA 13.0 + PyTorch cu130 适合 RTX 40 系 / 新驱动环境(本站 RTX 3060 实测 torch 2.12.1+cu130)。② PyTorch 2.9+ 为稳定基线(2026-07 最新稳定 2.13),FSDP2(fully_shardtorch.compile 已是新项目的默认选型。③ 版本号会持续滚动,装前务必以 PyTorch Get Started 矩阵为准——这正是「镜像即环境契约」要锁版本的原因。

pip install -r requirements.txt

踩坑预警 (Gotchas)

  • --gpus all 直接报错 could not select device driver:99% 是宿主机没装 nvidia-container-toolkit。CPU/Mac 路径用不到它,但 GPU 路径必须先 apt install nvidia-container-toolkit && nvidia-ctk runtime configure --runtime=docker && systemctl restart docker
  • CUDA 版本与驱动不匹配:镜像里的 CUDA 是 12.1,但宿主驱动太旧只支持到 11.x,容器内 torch.cuda.is_available() 会返回 False 或直接崩。原则:宿主驱动支持的 CUDA 版本 ≥ 镜像 CUDA 版本(用 nvidia-smi 右上角看驱动支持上限)。
  • 镜像动辄数 GBnvidia/cuda:*-devel 含完整编译工具链,体积巨大;只跑推理选 *-runtime 标签即可,能省一半以上。CPU 路径务必用 python:3.11-slim 而非完整 python:3.11
  • synchronize 就计时 = 测了个寂寞:CUDA kernel 异步下发,GPU 路径计时前后必须 torch.cuda.synchronize(),否则耗时接近 0。
  • Mac(Apple Silicon)跑不了 --gpus all:Docker Desktop 不直通 NVIDIA GPU,Mac 一律走 CPU 路径;想用 MPS 加速需在宿主机原生 Python(非容器)里用 torch.device("mps")

深入思考

下面三题每题先给题干,再用 <details> 折叠一份图文并茂的参考答案。建议先合上答案自己想 3 分钟,再展开对照。

思考题 1:复现性边界

你给同事一份 Dockerfile.gpurequirements.txt,他在另一台机器上 buildtorch.cuda.is_available() 却是 False。结合 2.1 的工具栈分层图,列出自底向上的排查清单——哪些环节属于宿主机(驱动/toolkit),哪些属于镜像(CUDA 版本/torch wheel),如何用一条命令快速定位断点?

展开参考答案(含自底向上排查决策树)

结论:is_available()=False 八成断在「宿主机层」而非镜像——镜像在你这能跑,说明镜像没问题,变量是对方的驱动 / nvidia-container-toolkit / --gpus 参数。

自底向上排查清单

检查项一条命令属于
宿主机·驱动驱动是否就绪 + 支持的 CUDA 上限nvidia-smi(看右上角 CUDA Version)宿主机
运行参数是否暴露 GPUdocker run --gpus all ...运行命令
宿主机·toolkitGPU 能否进容器docker run --rm --gpus all nvidia/cuda:12.x-base nvidia-smi宿主机
镜像·CUDA驱动 CUDA ≥ 镜像 CUDA对比 nvidia-smi 上限 vs 镜像 tag镜像
镜像·torchtorch 是否 CUDA 版容器内 python -c "import torch;print(torch.version.cuda)"镜像

最快定位断点的一条命令docker run --rm --gpus all nvidia/cuda:12.x-base nvidia-smi——

  • could not select device driver → 宿主机没装/没配 nvidia-container-toolkit(宿主机层)。
  • 能看到 GPU → 问题在镜像里的 torch wheel(多半装成了 CPU 版,或 CUDA 版本比驱动新),属镜像层。 这一刀就把「宿主机问题」和「镜像问题」切开了。

思考题 2:成本最优编排

你要完成一组实验:①读 MLIR 源码并编译(CPU 重)②跑一个算子的 GPU 性能 profile(GPU 重,仅 10 分钟)。用「CPU 工作台 + 临时租卡」的思路,设计一套让 GPU 计费时间最短的工作流——哪些步骤在便宜 CPU 机做,租卡后只做哪一步?为什么镜像化是这套流程的前提?

展开参考答案(含 CPU/GPU 计费时间线编排图)

结论:所有「不需要 GPU 的活」(编译、写码、调通逻辑、构建镜像)全在便宜 CPU 机做;只把「真正要 GPU 的 10 分钟 profile」留到租卡后做,让 GPU 计费窗口最短。

工作流

  1. CPU 机:读 + 编译 MLIR(耗时但 CPU 便宜,随便跑);
  2. CPU 机:写好 profile 脚本,用小 shape / CPU 后端把逻辑、依赖、命令行全部调通(这一步最耗人时,绝不能占用计费 GPU);
  3. CPU 机docker build 把 CUDA 运行时 + torch + 脚本打包成镜像并 push;
  4. 租 GPUdocker pull && docker run --gpus all只跑那 10 分钟 profile,拉回结果立刻销毁实例。

为什么镜像化是前提:租来的 GPU 实例是临时、易失的——开机即裸环境。如果上去现装 CUDA / torch / 依赖,光环境冷启动就可能耗掉半小时计费时间,比 profile 本身还贵,且极易「在我机器上能跑、租的机器上报错」。镜像把环境固化成契约,租卡后一条 docker run 直接进入就绪态,GPU 计费窗口压缩到只剩「真正算」的那 10 分钟。这正是 2.2「CPU 工作台 + GPU 试验场」组合的精髓。

思考题 3:隔离哲学

有人主张「直接 pip install 到系统 Python 最省事,何必容器」。请用「按层安装原则 + CUDA ABI 全局共享」两个角度,论证为什么容器隔离对 AI Infra 比对普通 Web 后端更不可省略——即 GPU 软件栈的哪些特性放大了环境冲突的代价?

展开参考答案(含「系统污染 vs 容器隔离」对比图)

结论:普通 Web 后端只有「Python 包」一层会冲突;AI Infra 多了「CUDA / cuDNN / 编译 ABI」这层全局共享的系统级依赖,冲突代价被放大到「装一个新项目废掉一个旧项目」,所以容器隔离不可省。

角度一:按层安装原则。AI Infra 按 L0–L4 分层装包(torch / triton / deepspeed / vllm),不同项目、不同层对版本要求往往互斥(项目 A 锁 vllm==0.6、项目 B 要新版)。裸装系统 Python 时,全局只能有一套,切项目就要反复卸装重装,极易「装一个废一个」。容器让每个项目带自己的一整套依赖,按层裁剪、互不影响。

角度二:CUDA ABI 全局共享(关键放大器)。普通 Web 后端冲突只发生在「Python 包」这一层,venv 就能隔离。但 AI 软件栈还依赖 CUDA Toolkit、cuDNN、NCCL、特定 glibc/C++ ABI(_GLIBCXX_USE_CXX11_ABI 这些系统级、全局共享的组件——venv 隔离不了它们。torch wheel 编译时绑死了某个 CUDA 版本和 ABI,一旦系统级 CUDA 被另一个项目换掉,已装好的 torch 直接 undefined symbol 崩掉。这层全局共享的系统依赖,正是 GPU 软件栈把环境冲突代价放大的根源——而容器恰恰能把「Python 包 + CUDA 运行时 + 系统库 + ABI」整体打包隔离,这是 venv 做不到、却又是 AI Infra 必须的。

归结起来:Web 后端的冲突止于 Python 包,venv 够用;AI Infra 的冲突下沉到 CUDA/ABI 系统层,只有容器能隔离——所以容器对 AI Infra 不是「省事」,而是「不隔离就反复自爆」。

延伸阅读

1. 核心文档与规范

  • NVIDIA Container Toolkit 官方安装文档 — 理解 --gpus all 背后的设备直通机制。
  • PyTorch Get Started Locally — 不同 CUDA 版本对应的 wheel 选择矩阵(cu118/cu121 等)。

2. 相关高 Star 仓库与必读路径

  • NVIDIA/nvidia-container-toolkit — 看 cmd/nvidia-ctk 如何配置 Docker runtime。
  • ollama/ollama — 本地快速验证显存与吞吐的最轻量入口,docs/gpu.md 讲清各平台 GPU 支持矩阵。
  • pytorch/pytorch 官方 Dockerfile — 学习生产级镜像如何分层组织 CUDA + Python 依赖。

3. 优质博客 / 实践指南

  • Docker 官方「Best practices for writing Dockerfiles」— 镜像瘦身与分层缓存。
  • RunPod / Lambda Labs 官方「快速起 GPU 实例」教程 — 云端低成本玩家的标准起步路径。
  • uv 文档(Astral)— 比 conda 更快的 Python 环境与依赖管理方案。

下一篇L1.1 单卡异构芯片微架构深挖:L0 新手村到此结束,从下一篇起放大「硬件层」,深入 GPU/NPU 单芯片内部的 SM、Tensor Core、HBM 与片上互联,开始真正的微架构实战。