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 |
| Docker | Docker Engine 28.5.1 |
| Docker Compose | Compose 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 |
| 容器静态 IP | 198.51.100.122,示例值 |
| DNS | 198.51.100.111,示例值 |
| 访问端口 | 8888 |
上线时把示例 IP、网关、DNS、镜像仓库和 Token 替换为自己的网络规划和凭据。
1. 实例目标#
这个实例的核心目标很明确:给研发或算法同学提供一个浏览器可访问的 JupyterLab 工作台,把宿主机上的工作目录挂载到容器里,并在有 NVIDIA GPU 的情况下让 Notebook 可以直接使用 CUDA。
典型使用场景包括:
- 在 Notebook 中浏览、清洗和抽样数据集。
- 使用 Python 快速验证模型推理代码。
- 调试 AI 数据预处理脚本。
- 在容器内调用 GPU 做小规模训练、推理和参数调优。
- 使用 Magika 这类 AI 文件类型识别库,对数据集中的文件类型做批量识别和质量检查。
实际目录中已经存在 Magika 相关工程。下面只保留公开可描述的信息,内部 fork 和工具脚本仓库不写真实地址。
| 路径 | 来源 | 用途 |
|---|---|---|
/workspace/google-magika | https://github.com/google/magika | 官方 Magika 多语言实现,包含 Python、Rust、JS、Go 代码 |
/workspace/magika | 内部 fork | Go 版本实验工程 |
/workspace/setup-scripts | 内部工具脚本仓库 | Go 与 ONNX Runtime 安装脚本 |
/workspace/magika-test | 本地 Notebook | Magika 测试 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
通过标准:
| 字段 | 期望 |
|---|---|
Driver | macvlan |
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 runtime | docker info --format '{{json .Runtimes}}' | 包含 nvidia | Docker 无 NVIDIA runtime |
| macvlan 网络 | docker network inspect vlan1111_net | 网络存在且 parent/subnet 正确 | 网络不存在、网段错误、IP 被占用 |
| 端口 | `ss -lntup | grep ‘:8888’ | true` | |
| Token | test -f .env && grep '^JUPYTER_TOKEN=' .env | .env 存在且权限为 600 | Token 缺失或准备把真实值写入文章/工单 |
当前现场停止在 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 | /workspace | Notebook、代码仓库、实验脚本 |
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.devices | Compose GPU 设备请求 |
capabilities: [gpu] | Docker Compose GPU 配置必填项 |
ipv4_address | 给容器分配固定 macvlan IP |
--IdentityProvider.token | Jupyter 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 ps | jupyterlab-pytorch-gpu 为 Up |
| Jupyter 启动 | docker logs --tail 120 jupyterlab-pytorch-gpu | 出现 Jupyter Server is running at |
| 端口监听 | ss -lntup | grep ':8888' | 宿主机或容器访问路径可用 |
| macvlan IP | docker inspect jupyterlab-pytorch-gpu | 容器在 vlan1111_net 上拿到固定 IP |
| 浏览器访问 | http://198.51.100.122:8888/lab | 进入 JupyterLab 登录页 |
| GPU | docker 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 |
推荐使用方式:
- 把代码仓库放在
/workspace。 - 把数据集放在
/data/datasets。 - 把实验输出放在
/data/outputs。 - Notebook 只保留调试过程和关键结论,不把大文件写进 Git 仓库。
- 对 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 结果会包含 path、status、label、mime_type、score 等字段。自动化流程建议优先使用稳定的 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
判断顺序:
- 执行
lspci -nn | grep -Ei 'nvidia|vga|3d|display',确认是否存在 NVIDIA 硬件。 - 执行
nvidia-smi,确认驱动是否加载。 - 执行
lsmod | grep '^nvidia',确认内核模块是否加载。 - 执行
dkms status,确认 DKMS 模块是否匹配当前内核。 - 执行
docker info,确认nvidiaruntime 是否存在。
当前现场属于第一类:硬件上的 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: 1 或 device_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/lab。ports 在这种场景里不是必需项,但保留它可以作为部署意图的声明,也能在某些网络策略下保留宿主机端口访问路径。正式生产化时建议二选一:要么只用 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,怎么排查#
按三层排查:
- 宿主机:
nvidia-smi是否正常。 - 容器:
docker exec jupyterlab-pytorch-gpu nvidia-smi是否正常。 - 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. 验证矩阵#
| 阶段 | 验证项 | 方法 | 通过标准 |
|---|---|---|---|
| 部署前 | Docker | docker --version && docker compose version | 命令正常返回 |
| 部署前 | GPU 硬件 | lspci -nn | grep -Ei 'nvidia' | 能看到 NVIDIA GPU |
| 部署前 | GPU 驱动 | nvidia-smi | 正常显示 GPU |
| 部署前 | macvlan | docker network inspect vlan1111_net | Driver 为 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 CUDA | torch.cuda.is_available() | 返回 True |
| 使用中 | 数据目录 | Path('/data').exists() | 返回 True |
| 使用中 | Magika | m.identify_path(path) | 返回预期 label |
17. 可复用原则#
这类实例的关键不是把 JupyterLab 跑起来,而是把三件事固定下来:
- 网络入口固定:macvlan 静态 IP 让使用者用稳定地址访问。
- 数据路径固定:
/workspace放代码,/data放数据,减少 Notebook 里的路径混乱。 - GPU 验证固定:先验证宿主机
nvidia-smi,再验证容器nvidia-smi,再验证 Notebooktorch.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
