生产 1:Docker 部署
谁该读这一篇? 准备把 SGLang 跑进 Docker(生产 / CI / 多环境隔离)的运维 / SRE。 前置阅读:
01-overview/05-quickstart.md。 耗时: 25 分钟 学完能: 1. 用官方镜像起一个 SGLang 容器; 2. 自建镜像,控制每一层; 3. 知道 GPU 驱动 / CUDA / 镜像版本的兼容矩阵; 4. 处理模型缓存 / 多 GPU / 网络配置; 5. 上线 checklist。
1. 官方镜像
SGLang 官方镜像:
lmsysorg/sglang:latest # 最新稳定
lmsysorg/sglang:v0.x.y # 固定版本
lmsysorg/sglang:dev # 开发版
lmsysorg/sglang:v0.x.y-cu124 # 指定 CUDA 版本
镜像里包含:CUDA toolkit、PyTorch、FlashInfer、Triton、SGLang、常用依赖。
跑一下:
docker run --rm -it --gpus all --ipc=host \
-p 30000:30000 \
-v ~/.cache/huggingface:/root/.cache/huggingface \
lmsysorg/sglang:latest \
python -m sglang.launch_server \
--model-path Qwen/Qwen2.5-7B-Instruct \
--host 0.0.0.0 \
--port 30000
关键 flag:
--gpus all:把宿主机所有 GPU 暴露给容器。--ipc=host:让容器用宿主机 shared memory(PyTorch dataloader 需要)。-v ~/.cache/huggingface:/root/.cache/huggingface:模型缓存挂出来,重启不重新下。
2. 自建镜像
照抄官方 Dockerfile 一份,自己改:
# Dockerfile.sglang
ARG CUDA_VERSION=12.4.1
FROM nvidia/cuda:${CUDA_VERSION}-cudnn-devel-ubuntu22.04
ENV DEBIAN_FRONTEND=noninteractive \
PIP_NO_CACHE_DIR=1 \
PYTHONUNBUFFERED=1
# 系统依赖
RUN apt-get update && apt-get install -y \
python3.11 python3.11-venv python3-pip \
git curl ca-certificates \
libnuma-dev \
&& rm -rf /var/lib/apt/lists/*
RUN ln -sf /usr/bin/python3.11 /usr/bin/python
# 装 sglang
ARG SGLANG_VERSION=0.5.0
RUN pip install --upgrade pip && \
pip install "sglang[all]==${SGLANG_VERSION}"
# 校验
RUN python -m sglang.check_env || true
WORKDIR /workspace
EXPOSE 30000
ENTRYPOINT ["python", "-m", "sglang.launch_server"]
CMD ["--help"]
build:
docker build -t mycompany/sglang:0.5.0-cu124 -f Dockerfile.sglang .
3. 多阶段构建(减小镜像)
# Stage 1: 构建
FROM nvidia/cuda:12.4.1-cudnn-devel-ubuntu22.04 AS builder
RUN apt-get update && apt-get install -y python3.11 python3-pip
RUN pip install --user "sglang[all]==0.5.0"
# Stage 2: 运行(不带 devel)
FROM nvidia/cuda:12.4.1-cudnn-runtime-ubuntu22.04
RUN apt-get update && apt-get install -y python3.11
COPY --from=builder /root/.local /root/.local
ENV PATH=/root/.local/bin:$PATH
EXPOSE 30000
ENTRYPOINT ["python", "-m", "sglang.launch_server"]
效果:镜像从 ~20 GB → ~8 GB。
4. CUDA / 驱动兼容矩阵
| Host 驱动 | 容器 CUDA | 备注 |
|---|---|---|
| 535+ | 12.4 / 12.6 / 12.8 | ✓ |
| 525-534 | 最多 12.2 | 可能不支持新 GPU 特性 |
| 470+ | 11.8 | 老服务器,性能差 |
宿主机驱动版本必须 ≥ 容器 CUDA。nvidia-smi 第一行看驱动。
5. 模型缓存策略
模型 7-700 GB 不等,下载慢。三种方案:
5.1 挂宿主机目录(开发)
-v ~/.cache/huggingface:/root/.cache/huggingface
简单,但容器迁移要重下。
5.2 NFS / 共享存储
集群多节点共享一个 NFS:
-v /mnt/nfs/hf_cache:/root/.cache/huggingface
首次任一节点下载,后续所有节点直接用。
5.3 模型烤进镜像
RUN huggingface-cli download Qwen/Qwen2.5-7B-Instruct \
--local-dir /models/qwen2.5-7b
ENV MODEL_PATH=/models/qwen2.5-7b
CMD ["--model-path", "/models/qwen2.5-7b", ...]
启动最快,但镜像变大(10+ GB),并且模型版本固定。 适合:生产稳定版本、CI 测试镜像。
6. 多 GPU / TP
docker run --gpus all --ipc=host --shm-size=64g \
-p 30000:30000 \
-v ~/.cache/huggingface:/root/.cache/huggingface \
lmsysorg/sglang:latest \
python -m sglang.launch_server \
--model-path meta-llama/Llama-3.1-70B-Instruct \
--tp 4 \
--port 30000
--shm-size=64g:NCCL 之间共享内存通信,默认 64 MB 不够,要调大。
某些主机 P2P 需要:--privileged 或 --ulimit memlock=-1。
7. 网络配置
7.1 端口暴露
-p 30000:30000 # HTTP API
-p 31000:31000 # metrics(独立端口可选)
-p 5000:5000 # disagg bootstrap
7.2 跨容器(多副本)
用 docker-compose:
version: "3"
services:
sglang-1:
image: lmsysorg/sglang:latest
ports: ["30001:30000"]
deploy:
resources:
reservations:
devices: [{ driver: nvidia, device_ids: ["0","1"], capabilities: [gpu] }]
command:
- python
- -m
- sglang.launch_server
- --model-path
- Qwen/Qwen2.5-72B-Instruct
- --tp
- "2"
sglang-2:
image: lmsysorg/sglang:latest
ports: ["30002:30000"]
deploy:
resources:
reservations:
devices: [{ driver: nvidia, device_ids: ["2","3"], capabilities: [gpu] }]
command: [..., --tp, "2"]
router:
image: lmsysorg/sglang-router:latest
ports: ["30000:30000"]
depends_on: [sglang-1, sglang-2]
command:
- --worker-urls
- http://sglang-1:30000
- http://sglang-2:30000
- --port
- "30000"
8. 健康检查
# 容器内部
curl localhost:30000/health # 200 = OK
# 等 model ready:
curl localhost:30000/health_generate # 跑一个小请求验证
# Prometheus metrics:
curl localhost:30000/metrics
Docker healthcheck:
HEALTHCHECK --interval=30s --timeout=10s --start-period=5m --retries=3 \
CMD curl -f http://localhost:30000/health || exit 1
start-period=5m 给模型加载预留时间。
9. 资源限制
docker run \
--gpus all \
--memory=64g \
--cpus=8 \
--shm-size=32g \
--ipc=host \
...
注意:
- CPU / 内存限制对推理性能影响不大(GPU 是瓶颈)。
- shm-size 务必给够。
- 不要限制 GPU 显存(驱动层级做不到)。
10. 安全建议
| 实践 | 说明 |
|---|---|
| Non-root 运行 | USER 1000,避免容器 escape 是 root |
| 私有镜像仓库 | 别 push lmsysorg 到 docker hub 公仓 |
| 模型权重不打进公开镜像 | 私有模型走 secrets / NFS |
| API key 不写镜像 | 用环境变量 / secrets |
| HTTPS 终结在 ingress | 容器内 plain HTTP,外层 LB 加 TLS |
11. 上线 checklist
- [ ] 固定镜像版本(不用
:latest) - [ ] 模型路径稳定(NFS / 烤镜像)
- [ ]
--ipc=host+--shm-size=64g - [ ] 健康检查 / start-period 给够
- [ ] Metrics 端口暴露给监控
- [ ] 日志输出到 stdout(Docker 自动收)
- [ ] 优雅退出 hook(SIGTERM 等请求处理完)
- [ ] 资源 reservation(GPU device_ids 明确)
12. 小结
- 官方镜像 + 挂载模型 cache 是最快上手方式。
- 自建镜像可控版本和体积;多阶段构建省 60% 大小。
- 多 GPU 必须
--ipc=host --shm-size调大。 - 健康检查 start-period 给 5+ 分钟。
- 安全:non-root、私仓、不打权重进镜像。
13. 自检
--ipc=host干什么?不加会发生什么?
答案
让容器**共享宿主机的 IPC namespace**——主要是 `/dev/shm`(共享内存段)和 System V IPC。 PyTorch 用 shared memory 做进程间张量传递(DataLoader、TP NCCL 内部 buffer 等)。容器默认 `/dev/shm` 只有 64 MB,多 GPU TP 启动时 NCCL 分配 buffer 立刻就会爆,启动卡死或报 "Bus error" / "shared memory exhausted"。 `--ipc=host` 直接用宿主机的 `/dev/shm`(通常几十 GB),干净利落。 替代方案:`--shm-size=64g`(独立 namespace 但调大),更隔离但占机器内存。 生产推荐 `--shm-size`,开发用 `--ipc=host` 方便。- 怎么减小自建镜像体积一半?
答案
多阶段构建: ```dockerfile FROM nvidia/cuda:12.4.1-devel AS builder RUN pip install --user "sglang[all]" FROM nvidia/cuda:12.4.1-runtime COPY --from=builder /root/.local /root/.local ``` 差异: - `devel` 镜像含 CUDA toolchain(nvcc / 头文件 / cuDNN devel),20 GB。 - `runtime` 镜像只含运行时库,8 GB。 - 把 pip 装的依赖从 builder 拷过来,得 runtime 8 GB + 依赖 5 GB ≈ 13 GB(vs 单阶段 20+ GB)。 再砍: - 删 pip cache(`PIP_NO_CACHE_DIR=1`)。 - 不装 dev / docs 依赖。 - 用 distroless 或 alpine 基础(但 GPU 镜像兼容性差,慎用)。- 模型烤进镜像 vs NFS 各自的取舍?
答案
**烤进镜像**: - 优势:启动最快(无网络下载);版本严格绑定;不可变;适合 CI 测试 / 私有模型上线。 - 劣势:镜像巨大(70B fp8 ~70 GB),镜像仓库 / pull 慢;切换模型要重新 build;难以多模型共用。 **NFS / 共享存储**: - 优势:所有节点共享一份;切模型只改 path;镜像本身小。 - 劣势:NFS 故障是全集群单点;首次冷启读 NFS 慢(如果不预热);权限管理复杂。 规则: - 单一稳定模型 + 严格版本 → 烤镜像。 - 多模型 / 频繁切换 / 大集群 → NFS + local SSD 二级缓存。 - K8s 上常用 PV + initContainer 预热到 local SSD。- 多 GPU TP 启动卡住,可能跟 docker 哪个参数有关?
答案
关键参数排查: (1) **`--shm-size` / `--ipc=host`**:NCCL 共享内存 buffer 不够,最常见。 (2) **`--gpus all` 或 `--gpus '"device=0,1,2,3"'`**:GPU device 没正确暴露,nvidia-smi 在容器里看不到 4 张卡。 (3) **`--ulimit memlock=-1`**:NCCL 用 pinned memory,被 ulimit 限制时分配失败。 (4) **`--privileged` 或 cap_add SYS_PTRACE**:某些 P2P 拓扑探测需要权限。 (5) **network=host** 或 IB 设备暴露:跨节点 TP 需要 NCCL 走 IB,容器默认 bridge 网络 IB 看不到。 定位:进容器跑 `nvidia-smi`、`ls /dev/nvidia*`、`ipcs -m`,看缺哪样。- Healthcheck 没设 start-period 会怎样?
答案
K8s / Docker healthcheck 默认从容器启动那一刻就开始 ping `/health`,连续 N 次失败就标记 unhealthy 重启容器。 SGLang 模型加载 + CUDA Graph capture 可能要 1-5 分钟,期间 `/health` 还没就绪。没 start-period 时 healthcheck 一直失败,容器在永远启动 → 永远健康检查失败 → 永远重启的循环。 `start-period 300s`(5 分钟宽限期):这段时间内的 healthcheck 失败不计入失败次数,等服务就绪后再开始严格检查。 建议:start-period 设到 模型加载时间 × 2;70B 给 5 分钟;large MoE 给 10 分钟。 配合 `readinessProbe` 用 `/health_generate`(真跑一次请求)比 `/health`(只 ping)更准。14. 下一步
02-kubernetes.md— K8s 上集群部署。03-monitoring.md— 监控接入。07-router.md— Router 配合。- 源码:
sglang/docker/。
上游源码:
sglang/docker/。