跳过正文
  1. 博客文章/

基于 macvlan 的 JupyterLab GPU 调试实例部署复盘

·1801 字·9 分钟·
DevOps AI Engineering Jupyterlab GPU Docker Macvlan NVIDIA Magika DevOps
Zayn
作者
Zayn
专注 Kubernetes、CI/CD、可观测性等云原生技术栈,记录生产环境中的实战经验与踩坑复盘。
目录
生产变更复盘 - 这篇文章属于一个选集。
2: 本文
这个 JupyterLab 实例用于 AI 数据集调试:容器通过 Docker Compose 部署,通过 macvlan 获得独立内网 IP;在具备 NVIDIA GPU 的硬件上,Notebook 可以直接调用 CUDA 做数据集验证、模型推理和调优实验。
示例中的 IP、主机名、镜像仓库和 Token 只用于说明部署方法。实际环境要替换为自己的 VLAN、镜像地址和访问凭据。

0. 结论和使用边界
#

文中统一使用 macvlan 这个名称。当前实例里的工作目录按 AI 文件识别库和调试工程来梳理,重点覆盖 Magika 相关库的使用方法。

这不是一个从零讲 JupyterLab 的入门教程,而是一份可以复现同类实例的部署复盘。读者可以直接复用目录规划、macvlan 网络、Docker Compose、GPU 前置检查、JupyterLab 使用方式和故障判断流程。

这个实例有一个重要边界:当前硬件上的 NVIDIA 显卡已经被移除,nvidia-smi 无法和 NVIDIA 驱动通信,原 GPU 容器因此退出。配置里仍保留 GPU 部署和验证方法,因为这个实例的设计目标就是在 JupyterLab 中调用硬件 NVIDIA GPU,用于数据集调试和 AI 相关调优。

当前部署参数:

主机角色Docker 单机实验/调试节点
操作系统Debian GNU/Linux 12,内核 6.12.43+deb12-amd64
DockerDocker Engine 28.5.1
Docker ComposeCompose plugin v2.40.2
JupyterLab 镜像registry.example.internal/devops/pytorch:25.09-py3
JupyterLab 版本4.4.7
Jupyter Server 版本2.17.0
部署目录/nvme0/docker-compose/jupyterlab
工作目录挂载./work:/workspace
数据目录挂载./data:/data
macvlan 网络vlan1111_net
容器静态 IP198.51.100.122,示例值
DNS198.51.100.111,示例值
访问端口8888

上线时把示例 IP、网关、DNS、镜像仓库和 Token 替换为自己的网络规划和凭据。

1. 实例目标
#

这个实例的核心目标很明确:给研发或算法同学提供一个浏览器可访问的 JupyterLab 工作台,把宿主机上的工作目录挂载到容器里,并在有 NVIDIA GPU 的情况下让 Notebook 可以直接使用 CUDA。

典型使用场景包括:

  • 在 Notebook 中浏览、清洗和抽样数据集。
  • 使用 Python 快速验证模型推理代码。
  • 调试 AI 数据预处理脚本。
  • 在容器内调用 GPU 做小规模训练、推理和参数调优。
  • 使用 Magika 这类 AI 文件类型识别库,对数据集中的文件类型做批量识别和质量检查。

实际目录中已经存在 Magika 相关工程。下面只保留公开可描述的信息,内部 fork 和工具脚本仓库不写真实地址。

路径来源用途
/workspace/google-magikahttps://github.com/google/magika官方 Magika 多语言实现,包含 Python、Rust、JS、Go 代码
/workspace/magika内部 forkGo 版本实验工程
/workspace/setup-scripts内部工具脚本仓库Go 与 ONNX Runtime 安装脚本
/workspace/magika-test本地 NotebookMagika 测试 Notebook

2. 架构
#

实例架构如下:

flowchart TB
    browser["Client Browser
http://198.51.100.122:8888"] -->|VLAN / macvlan 独立 IP| lab["JupyterLab Container
/workspace /data
CUDA / PyTorch / JupyterLab"] lab -->|bind mount| host["Docker Host
/nvme0/docker-compose/...
NVIDIA GPU + Docker runtime"]

这里选择 macvlan 的原因是:容器获得一个独立内网 IP,访问者可以像访问普通主机一样访问 JupyterLab,而不必关心 Docker bridge 网络的端口映射细节。对于实验室、内网工具和临时调试环境,这种方式直观且便于网络侧做 ACL。

需要注意 macvlan 的一个常见限制:默认情况下,宿主机不一定能直接访问同一 macvlan 网络中的容器 IP。因此验证时应优先从同 VLAN 的其他机器访问 http://容器IP:8888

3. 前置条件
#

部署前先确认主机满足这些条件。本节的目的不是安装所有依赖,而是建立 Review Gate,避免在驱动、网络或目录不存在的情况下直接启动容器。

3.1 系统与 Docker
#

uname -a
docker --version
docker compose version

通过标准:

检查项通过标准
Docker Engine能正常执行 docker ps
Compose plugin能正常执行 docker compose version
数据盘部署目录所在磁盘容量充足
端口8888 未被其他服务占用

端口检查:

ss -lntup | grep ':8888' || true

3.2 NVIDIA GPU 与容器运行时
#

如果要使用 GPU,这一步必须通过。

lspci -nn | grep -Ei 'nvidia|vga|3d|display'
nvidia-smi
docker info --format 'Runtimes={{json .Runtimes}} DefaultRuntime={{.DefaultRuntime}}'

通过标准:

检查项通过标准
硬件lspci 能看到 NVIDIA 设备
驱动nvidia-smi 正常显示 GPU、驱动版本和显存
容器运行时docker info 中存在 nvidia runtime

当前现场的失败证据如下:

NVIDIA-SMI has failed because it couldn't communicate with the NVIDIA driver.

旧容器退出原因如下:

nvidia-container-cli: initialization error: nvml error: driver not loaded

这说明问题不在 JupyterLab 本身,而在 GPU 硬件/驱动前置条件。当前硬件已移除 NVIDIA 显卡,因此不再继续恢复 GPU 容器。

3.3 macvlan 网络
#

macvlan 需要一个父接口。现场使用的是 VLAN 子接口,示例写作 eth0.1111

检查接口:

ip -br addr

创建 macvlan 网络:

docker network create -d macvlan \
  --subnet=198.51.100.0/24 \
  --gateway=198.51.100.111 \
  -o parent=eth0.1111 \
  vlan1111_net

验证网络:

docker network inspect vlan1111_net

通过标准:

字段期望
Drivermacvlan
Subnet和现场 VLAN 网段一致
Gateway和现场网关一致
Options.parent指向正确物理接口或 VLAN 子接口

Docker 官方文档要求 macvlan 网络通过 --driver macvlan 创建,并指定流量实际经过的 parent 接口。Compose 中如果使用外部网络,只需要声明 external: true

3.4 Review Gate
#

真正执行部署前,建议先完成下面这组门禁。任意一项失败,都先停下来处理,不要直接 docker compose up -d

检查项命令继续条件停止条件
GPU 硬件lspci -nn | grep -Ei 'nvidia'能看到 NVIDIA GPU无 NVIDIA GPU,但仍使用 GPU Compose
GPU 驱动nvidia-smi能显示 GPU 和驱动版本driver not loaded 或无法通信
NVIDIA runtimedocker info --format '{{json .Runtimes}}'包含 nvidiaDocker 无 NVIDIA runtime
macvlan 网络docker network inspect vlan1111_net网络存在且 parent/subnet 正确网络不存在、网段错误、IP 被占用
端口`ss -lntup | grep ‘:8888’true`
Tokentest -f .env && grep '^JUPYTER_TOKEN=' .env.env 存在且权限为 600Token 缺失或准备把真实值写入文章/工单

当前现场停止在 GPU 门禁:硬件已移除,nvidia-smi 无法通信,所以不应继续强行启动 GPU 版容器。文档保留 GPU 版本,是为了后续硬件恢复后可以按同一套门禁复现。

4. 目录规划
#

推荐目录如下:

/nvme0/docker-compose/jupyterlab/
├── compose.yaml
├── .env
├── data/
└── work/
    ├── google-magika/
    ├── magika/
    ├── magika-test/
    └── setup-scripts/

创建目录:

mkdir -p /nvme0/docker-compose/jupyterlab/data
mkdir -p /nvme0/docker-compose/jupyterlab/work
cd /nvme0/docker-compose/jupyterlab

目录职责:

目录容器内路径职责
work/workspaceNotebook、代码仓库、实验脚本
data/data数据集、临时输入输出、模型文件

这个规划遵循 KISS:一个 Compose 项目只负责一个 JupyterLab 实例;代码和数据分开挂载,后续备份、清理和迁移都更直接。

5. Compose 配置
#

下面是可复现的 GPU 版 Compose。实际部署前需要替换镜像、IP、DNS 和 Token。

5.1 .env
#

不要把登录 Token 明文写死在 compose.yaml。使用 .env 管理,并限制文件权限:

JUPYTER_TOKEN=CHANGEME_GENERATE_WITH_OPENSSL_64_HEX
JUPYTER_IP=198.51.100.122
JUPYTER_DNS=198.51.100.111

生成随机 Token 的方式:

openssl rand -hex 32
chmod 600 /nvme0/docker-compose/jupyterlab/.env

5.2 compose.yaml
#

services:
  jupyterlab:
    image: registry.example.internal/devops/pytorch:25.09-py3
    container_name: jupyterlab-pytorch-gpu
    restart: always

    env_file:
      - .env

    ports:
      - "8888:8888"

    volumes:
      - ./work:/workspace
      - ./data:/data

    environment:
      LANG: C.UTF-8
      LC_ALL: C.UTF-8
      NVIDIA_VISIBLE_DEVICES: all
      NVIDIA_DRIVER_CAPABILITIES: compute,utility

    deploy:
      resources:
        reservations:
          devices:
            - driver: nvidia
              count: all
              capabilities: [gpu]

    command: >
      bash -c "
        jupyter lab
        --ip=0.0.0.0
        --port=8888
        --no-browser
        --allow-root
        --IdentityProvider.token=\"$${JUPYTER_TOKEN}\"
      "

    networks:
      vlan1111_net:
        ipv4_address: "${JUPYTER_IP}"

    dns:
      - "${JUPYTER_DNS}"

networks:
  vlan1111_net:
    external: true

配置说明:

配置作用
image使用带 PyTorch、CUDA、JupyterLab 的基础镜像
env_file把登录 Token 从 Compose 主体中拆出,降低误发配置时的泄露概率
volumes把宿主机工作目录和数据目录挂入容器
NVIDIA_VISIBLE_DEVICES=all允许容器看到所有 GPU
NVIDIA_DRIVER_CAPABILITIES=compute,utility开启计算和工具能力,够 Notebook 调试使用
deploy.resources.reservations.devicesCompose GPU 设备请求
capabilities: [gpu]Docker Compose GPU 配置必填项
ipv4_address给容器分配固定 macvlan IP
--IdentityProvider.tokenJupyter Server 2.x 推荐的 Token 配置方式

如果镜像内 Jupyter 版本较旧,可能还会看到 --NotebookApp.token--ServerApp.token。在 Jupyter Server 2.x 中,--ServerApp.token 已标记为 deprecated,应优先使用 --IdentityProvider.token

这里用 $${JUPYTER_TOKEN} 是有意的:双美元符号会让 Compose 把变量留给容器内 shell 展开,避免在 command 字段中直接渲染真实 Token。即便如此,也不要把 docker compose config 的完整输出粘贴到公开渠道。

6. 启动和验证
#

启动前先渲染 Compose,确认变量都被正确替换:

cd /nvme0/docker-compose/jupyterlab
docker compose config | sed -E 's/(JUPYTER_TOKEN: ).+/\1***REDACTED***/'

上面的命令只适合做人工检查。不要把完整的 docker compose config 输出发送到聊天、工单或博客,因为它可能展开 .env 中的真实 Token。

启动:

docker compose up -d

查看状态:

docker compose ps
docker logs --tail 120 jupyterlab-pytorch-gpu

如果容器无法启动,先保存证据,再决定是否回滚或切换 CPU fallback:

docker inspect jupyterlab-pytorch-gpu --format 'State={{json .State}}'
docker logs --tail 200 jupyterlab-pytorch-gpu

通过标准:

验证项命令通过标准
容器状态docker compose psjupyterlab-pytorch-gpuUp
Jupyter 启动docker logs --tail 120 jupyterlab-pytorch-gpu出现 Jupyter Server is running at
端口监听ss -lntup | grep ':8888'宿主机或容器访问路径可用
macvlan IPdocker inspect jupyterlab-pytorch-gpu容器在 vlan1111_net 上拿到固定 IP
浏览器访问http://198.51.100.122:8888/lab进入 JupyterLab 登录页
GPUdocker exec jupyterlab-pytorch-gpu nvidia-smi正常显示 GPU

Notebook 内验证 PyTorch CUDA:

import torch

print(torch.__version__)
print(torch.cuda.is_available())
print(torch.cuda.device_count())
print(torch.cuda.get_device_name(0) if torch.cuda.is_available() else "no cuda")

如果 torch.cuda.is_available() 返回 False,不要先怀疑 Notebook。按顺序检查硬件、驱动、NVIDIA Container Toolkit、Compose GPU 配置和容器日志。

7. JupyterLab 使用方法
#

访问入口:

http://198.51.100.122:8888/lab

登录时输入 .env 中的 JUPYTER_TOKEN

工作目录:

JupyterLab 路径宿主机路径
/workspace/nvme0/docker-compose/jupyterlab/work
/data/nvme0/docker-compose/jupyterlab/data

推荐使用方式:

  1. 把代码仓库放在 /workspace
  2. 把数据集放在 /data/datasets
  3. 把实验输出放在 /data/outputs
  4. Notebook 只保留调试过程和关键结论,不把大文件写进 Git 仓库。
  5. 对 GPU 实验,在 Notebook 第一格固定打印 CUDA 状态,避免误以为跑在 GPU 上。

示例:

from pathlib import Path

workspace = Path("/workspace")
data_dir = Path("/data/datasets")
output_dir = Path("/data/outputs")
output_dir.mkdir(parents=True, exist_ok=True)

print(workspace.exists(), data_dir.exists(), output_dir.exists())

8. 使用 Magika 调试数据集
#

Magika 是 Google 开源的 AI 文件类型识别工具,可用于识别数据集中的文件类型。它支持 CLI,也支持 Python API,适合在 JupyterLab 中做数据集抽样和质量检查。

8.1 安装
#

如果镜像内没有 Magika,可以在容器中安装:

pip install magika

如果只是使用命令行,也可以用 pipx install magika。在 Notebook 环境里,直接使用 pip install magika 更简单。

8.2 CLI 批量识别
#

递归扫描数据集:

magika -r /data/datasets

输出 JSON,便于后续处理:

magika /data/datasets/sample.py --json

Magika 官方示例中,JSON 结果会包含 pathstatuslabelmime_typescore 等字段。自动化流程建议优先使用稳定的 label 字段,而不是面向人的描述文本。

8.3 Notebook 中使用 Python API
#

识别字节内容:

from magika import Magika

m = Magika()
res = m.identify_bytes(b"function log(msg) { console.log(msg); }")
print(res.output.label)

识别文件路径:

from pathlib import Path
from magika import Magika

m = Magika()
path = Path("/workspace/google-magika/tests_data/basic/python/code.py")
res = m.identify_path(path)

print(res.output.label)
print(res.output.mime_type)
print(res.score)

识别二进制流:

from magika import Magika

m = Magika()

with open("/workspace/google-magika/tests_data/basic/ini/doc.ini", "rb") as f:
    res = m.identify_stream(f)

print(res.output.label)

8.4 数据集质量检查示例
#

下面的 Notebook 片段会扫描数据集目录,统计文件类型分布:

from collections import Counter
from pathlib import Path
from magika import Magika

dataset_dir = Path("/data/datasets")
m = Magika()
counter = Counter()
failed = []

for path in dataset_dir.rglob("*"):
    if not path.is_file():
        continue

    result = m.identify_path(path)
    if not result.ok:
        failed.append(str(path))
        continue

    counter[result.output.label] += 1

counter.most_common(30), failed[:20]

如果要导出结果:

import csv
from pathlib import Path
from magika import Magika

dataset_dir = Path("/data/datasets")
report_path = Path("/data/outputs/magika-file-types.csv")
m = Magika()

with report_path.open("w", newline="", encoding="utf-8") as f:
    writer = csv.DictWriter(
        f,
        fieldnames=["path", "label", "mime_type", "score"],
    )
    writer.writeheader()

    for path in dataset_dir.rglob("*"):
        if not path.is_file():
            continue
        result = m.identify_path(path)
        if not result.ok:
            continue
        writer.writerow(
            {
                "path": str(path),
                "label": result.output.label,
                "mime_type": result.output.mime_type,
                "score": result.score,
            }
        )

print(report_path)

这个流程适合用在数据集入库前:先识别文件类型,再过滤不符合预期的文件,避免训练或推理脚本在后续阶段才因为异常文件失败。

9. GPU 调优工作流
#

在 JupyterLab 中做 AI 调优时,建议把每次实验拆成四个 Notebook 区块。

9.1 环境确认
#

import os
import torch

print("CUDA_VISIBLE_DEVICES:", os.environ.get("CUDA_VISIBLE_DEVICES"))
print("CUDA available:", torch.cuda.is_available())
print("GPU count:", torch.cuda.device_count())
if torch.cuda.is_available():
    print("GPU name:", torch.cuda.get_device_name(0))

9.2 数据集抽样
#

from pathlib import Path

dataset_dir = Path("/data/datasets")
samples = [p for p in dataset_dir.rglob("*") if p.is_file()][:20]
samples

9.3 小批量推理或训练
#

先使用很小的 batch 和样本数,确认代码、数据、显存都正常,再扩大规模。

import torch

device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")
x = torch.randn(1024, 1024, device=device)
y = x @ x
print(y.shape, y.device)

9.4 资源观察
#

在终端里观察 GPU:

watch -n 1 nvidia-smi

如果显存持续增长,优先检查 Notebook 中是否保留了大对象引用。必要时执行:

import gc
import torch

gc.collect()
if torch.cuda.is_available():
    torch.cuda.empty_cache()

10. 故障判断
#

10.1 容器退出:nvml error: driver not loaded
#

症状:

nvidia-container-cli: initialization error: nvml error: driver not loaded

判断顺序:

  1. 执行 lspci -nn | grep -Ei 'nvidia|vga|3d|display',确认是否存在 NVIDIA 硬件。
  2. 执行 nvidia-smi,确认驱动是否加载。
  3. 执行 lsmod | grep '^nvidia',确认内核模块是否加载。
  4. 执行 dkms status,确认 DKMS 模块是否匹配当前内核。
  5. 执行 docker info,确认 nvidia runtime 是否存在。

当前现场属于第一类:硬件上的 NVIDIA 显卡已经移除,所以 GPU 容器无法启动是预期结果。

10.2 容器启动但访问不到
#

检查容器状态:

docker compose ps
docker logs --tail 120 jupyterlab-pytorch-gpu

检查网络:

docker network inspect vlan1111_net
docker inspect jupyterlab-pytorch-gpu --format '{{json .NetworkSettings.Networks}}'

从同 VLAN 机器访问:

curl -I http://198.51.100.122:8888/lab

macvlan 场景下,不要只从宿主机本机验证容器 IP。宿主机访问不到 macvlan 容器 IP 并不一定代表容器不可用。

10.3 Token 登录失败
#

检查 .env

grep '^JUPYTER_TOKEN=' /nvme0/docker-compose/jupyterlab/.env

检查容器实际命令:

docker inspect jupyterlab-pytorch-gpu --format '{{json .Config.Cmd}}'

不要在博客、PR、工单或聊天记录里粘贴真实 Token。如果怀疑 Token 泄露,立即更换 .env 中的值并重启容器。

11. 加固项
#

这个实例偏实验和调试,但只要 JupyterLab 暴露在内网,就需要做最小加固。这里不追求复杂方案,只保留对复现和日常使用最有价值的控制点。

11.1 访问控制
#

推荐策略:

  • 只在受控内网或 VPN 中暴露 8888
  • 不把 JupyterLab 直接暴露到公网。
  • Token 使用随机值,定期轮换。
  • 如果使用反向代理,优先在代理层增加认证、TLS 和访问日志。
  • 对 macvlan IP 做网络侧 ACL,只允许研发网段或跳板机访问。

11.2 文件和目录权限
#

cd /nvme0/docker-compose/jupyterlab
chmod 600 .env
chmod 755 work data

如果多人共享同一实例,应避免所有人共用 root 容器用户长期写文件。更稳妥的做法是为多人环境单独拆实例,或者改造成 JupyterHub/企业网关模式。

11.3 资源限制
#

单人调试实例可以不做严格限制,但共享机器建议增加内存和 CPU 限制,避免 Notebook 误操作拖垮宿主机。

services:
  jupyterlab:
    deploy:
      resources:
        limits:
          cpus: "8"
          memory: 32G

GPU 资源也建议从 count: all 收敛到 count: 1device_ids,尤其是同一台机器还运行其他推理或训练服务时。

12. CPU fallback
#

如果短期内没有 NVIDIA GPU,但仍希望保留 JupyterLab 的 Notebook 访问能力,可以临时使用 CPU fallback。核心变化是删除 GPU 设备请求和 NVIDIA 环境变量。

services:
  jupyterlab:
    image: registry.example.internal/devops/pytorch:25.09-py3
    container_name: jupyterlab-pytorch
    restart: always
    env_file:
      - .env
    ports:
      - "8888:8888"
    volumes:
      - ./work:/workspace
      - ./data:/data
    environment:
      LANG: C.UTF-8
      LC_ALL: C.UTF-8
    command: >
      bash -c "
        jupyter lab
        --ip=0.0.0.0
        --port=8888
        --no-browser
        --allow-root
        --IdentityProvider.token=\"$${JUPYTER_TOKEN}\"
      "
    networks:
      vlan1111_net:
        ipv4_address: "${JUPYTER_IP}"
    dns:
      - "${JUPYTER_DNS}"

networks:
  vlan1111_net:
    external: true

CPU fallback 适合继续做数据清洗、文件类型识别、Notebook 文档整理、小样本逻辑验证;不适合需要 CUDA 的训练和大模型推理。

13. 备份、升级和回滚
#

13.1 备份
#

变更前备份 Compose 和环境文件:

cd /nvme0/docker-compose/jupyterlab
cp compose.yaml "compose.yaml.$(date +%Y%m%d%H%M%S).bak"
cp .env ".env.$(date +%Y%m%d%H%M%S).bak"

数据目录备份按业务重要性处理:

tar -czf "/tmp/jupyterlab-work-$(date +%Y%m%d%H%M%S).tar.gz" -C /nvme0/docker-compose/jupyterlab work

13.2 升级
#

升级镜像前先记录当前镜像 ID:

docker image inspect registry.example.internal/devops/pytorch:25.09-py3 --format '{{.Id}}'

拉取并启动:

docker compose pull
docker compose up -d

13.3 回滚
#

如果新容器无法启动,先恢复 Compose:

cd /nvme0/docker-compose/jupyterlab
cp compose.yaml.20260608211500.bak compose.yaml
docker compose up -d

如果问题来自 GPU 驱动,不要通过反复重启容器解决。应先恢复驱动和 nvidia-smi,再启动容器。

14. 常见问题
#

14.1 为什么 Compose 里用了 macvlan,还保留 ports
#

macvlan 模式下,主要访问入口是容器独立 IP,例如 http://容器IP:8888/labports 在这种场景里不是必需项,但保留它可以作为部署意图的声明,也能在某些网络策略下保留宿主机端口访问路径。正式生产化时建议二选一:要么只用 macvlan IP,要么只用端口映射,避免排障时混淆。

14.2 为什么不在当前现场启动 CPU fallback
#

这次任务目标是整理和复盘,不是改变实例运行状态。当前旧容器已经退出,强行切换 CPU fallback 会改变远端实例配置。文档保留 CPU fallback,是为了在需要恢复 Notebook 访问能力时有一条低风险路径。

14.3 为什么不用 docker compose config 原样贴出来
#

因为 Compose 会展开 .env,可能把真实 JUPYTER_TOKEN 带出来。文章、工单和聊天记录只保留过滤后的配置,真实值留在服务器 .env 文件中。

14.4 GPU 已恢复但 Notebook 里仍然是 CPU,怎么排查
#

按三层排查:

  1. 宿主机:nvidia-smi 是否正常。
  2. 容器:docker exec jupyterlab-pytorch-gpu nvidia-smi 是否正常。
  3. Notebook:torch.cuda.is_available() 是否为 True

如果第 1 层失败,修驱动;第 2 层失败,查 NVIDIA Container Toolkit 和 Compose GPU 配置;第 3 层失败,查 PyTorch/CUDA 版本和 Notebook kernel 环境。

15. 遗留项
#

当前现场有几项没有继续处理,原因和后续动作如下:

遗留项当前状态未处理原因后续动作
NVIDIA GPU 运行态验证未通过硬件已移除,nvidia-smi 无法通信硬件恢复后从 Review Gate 重新验证
容器恢复启动未执行用户要求只记录使用方法和文档,不改远端实例如需恢复,先选择 GPU 版或 CPU fallback
Token 轮换未执行未改远端 .env实际上线前用 openssl rand -hex 32 生成新 Token
多用户隔离未实现当前设计是单人/小团队调试实例多人长期使用时评估 JupyterHub 或按人拆实例
HTTPS未配置当前按内网访问复盘如跨网段或长期使用,建议通过反向代理提供 TLS

16. 验证矩阵
#

阶段验证项方法通过标准
部署前Dockerdocker --version && docker compose version命令正常返回
部署前GPU 硬件lspci -nn | grep -Ei 'nvidia'能看到 NVIDIA GPU
部署前GPU 驱动nvidia-smi正常显示 GPU
部署前macvlandocker network inspect vlan1111_netDriver 为 macvlan
启动前Compose 渲染docker compose config无变量缺失
启动后容器状态docker compose ps状态为 Up
启动后Jupyter 日志docker logs --tail 120 jupyterlab-pytorch-gpu出现 Jupyter URL
启动后浏览器访问http://容器IP:8888/lab出现登录页
启动后GPU 容器docker exec jupyterlab-pytorch-gpu nvidia-smi容器内能看到 GPU
使用中Notebook CUDAtorch.cuda.is_available()返回 True
使用中数据目录Path('/data').exists()返回 True
使用中Magikam.identify_path(path)返回预期 label

17. 可复用原则
#

这类实例的关键不是把 JupyterLab 跑起来,而是把三件事固定下来:

  • 网络入口固定:macvlan 静态 IP 让使用者用稳定地址访问。
  • 数据路径固定:/workspace 放代码,/data 放数据,减少 Notebook 里的路径混乱。
  • GPU 验证固定:先验证宿主机 nvidia-smi,再验证容器 nvidia-smi,再验证 Notebook torch.cuda.is_available()

当硬件 GPU 被移除时,文档和 Compose 仍然有价值:它记录了恢复 GPU 版本所需的完整条件,也允许临时切换到 CPU fallback 继续做数据集检查和 Notebook 调试。

18. 参考资料
#

  • Docker Compose GPU support: https://docs.docker.com/compose/how-tos/gpu-support/
  • Docker macvlan network driver: https://docs.docker.com/engine/network/drivers/macvlan/
  • Jupyter command line options: https://docs.jupyter.org/en/latest/running.html
  • Magika: https://github.com/google/magika
生产变更复盘 - 这篇文章属于一个选集。
2: 本文

相关文章

生产 DNS 集群平滑升级复盘:dnsdist 与 Technitium 的滚动变更
·989 字·5 分钟
SRE DNS SRE DevOps Dnsdist +3
Docker 常用命令速查手册
·378 字·2 分钟
Docker DevOps Docker Container DevOps Cheatsheet
企业级 GitLab 平台部署与运维完整指南
·3964 字·19 分钟
Git CI/CD Docker DevOps GitLab +3