SGLang-v0.5.6部署优化:结合Docker与Kubernetes,实现高可用服务

当你辛辛苦苦把一个LLM服务部署上线,以为可以高枕无忧时,现实往往会给你当头一棒:流量高峰时服务卡死、某个GPU挂了导致整个服务不可用、想扩缩容还得手动操作半天。这些问题,相信很多做过大模型部署的朋友都深有体会。

SGLang-v0.5.6作为一个高性能推理框架,本身已经解决了计算效率的问题,但要让它在生产环境中真正“扛得住”,还需要一套成熟的部署架构。今天,我们就来聊聊如何用Docker和Kubernetes,把SGLang服务打造成一个高可用、可弹性伸缩的生产级系统。

1. 为什么需要Docker和Kubernetes?

在深入技术细节之前,我们先搞清楚一个问题:为什么单机部署不够用?

1.1 单机部署的局限性

想象一下,你在一台服务器上直接运行SGLang服务,看起来一切正常。但很快你会发现几个头疼的问题:

  • 环境依赖复杂:每台新机器都要重新配环境,CUDA版本、Python包、系统库……任何一个环节出错,服务就起不来。
  • 资源隔离差:多个服务跑在同一台机器上,容易互相影响。SGLang把GPU内存吃满了,其他服务就没法用。
  • 扩缩容困难:流量来了想加机器?得从头开始配置,等配好了,流量高峰可能都过去了。
  • 故障恢复慢:服务器宕机了怎么办?手动重启,期间服务完全不可用。

1.2 Docker带来的标准化

Docker就像给应用打包了一个“集装箱”,里面包含了运行所需的一切:代码、运行时、系统工具、库。有了Docker,你的SGLang服务就变成了一个标准化的镜像,可以在任何支持Docker的机器上“开箱即用”。

好处很明显:

  • 环境一致性:开发、测试、生产环境完全一致,避免“在我机器上能跑”的问题
  • 快速部署:一行命令就能启动服务,无需复杂配置
  • 资源隔离:每个容器有独立的资源视图,互不干扰

1.3 Kubernetes提供的编排能力

如果说Docker解决了“单个服务怎么跑”的问题,Kubernetes(简称K8s)解决的就是“一堆服务怎么管”的问题。

K8s能帮你:

  • 自动扩缩容:根据CPU/内存使用率或自定义指标,自动增加或减少服务实例
  • 服务发现与负载均衡:流量自动分配到健康的实例上
  • 自我修复:实例挂了自动重启,节点挂了自动迁移
  • 滚动更新:不中断服务的情况下更新版本

2. 构建生产级Docker镜像

我们先从基础做起,构建一个适合生产环境的SGLang Docker镜像。

2.1 优化Dockerfile设计

直接安装所有依赖的Dockerfile虽然简单,但镜像体积大、构建慢。我们来优化一下:

# 使用多阶段构建,减小最终镜像体积
# 第一阶段:构建环境
FROM nvidia/cuda:12.1.1-cudnn8-runtime-ubuntu22.04 as builder

ENV DEBIAN_FRONTEND=noninteractive \
    PYTHONUNBUFFERED=1 \
    PYTHONDONTWRITEBYTECODE=1

# 安装系统依赖
RUN apt-get update && apt-get install -y \
    python3.10 \
    python3-pip \
    python3.10-venv \
    git \
    curl \
    && rm -rf /var/lib/apt/lists/*

# 创建虚拟环境
RUN python3.10 -m venv /opt/venv
ENV PATH="/opt/venv/bin:$PATH"

# 安装Python依赖
COPY requirements.txt .
RUN pip install --no-cache-dir --upgrade pip && \
    pip install --no-cache-dir -r requirements.txt

# 第二阶段:运行环境
FROM nvidia/cuda:12.1.1-cudnn8-runtime-ubuntu22.04

ENV DEBIAN_FRONTEND=noninteractive \
    PYTHONUNBUFFERED=1

# 只安装运行时必要的包
RUN apt-get update && apt-get install -y \
    python3.10 \
    python3.10-venv \
    && rm -rf /var/lib/apt/lists/*

# 从构建阶段复制虚拟环境
COPY --from=builder /opt/venv /opt/venv
ENV PATH="/opt/venv/bin:$PATH"

# 创建非root用户运行(安全最佳实践)
RUN useradd -m -u 1000 -s /bin/bash sglang && \
    chown -R sglang:sglang /opt/venv

USER sglang
WORKDIR /home/sglang/app

# 复制启动脚本和配置文件
COPY --chown=sglang:sglang entrypoint.sh .
COPY --chown=sglang:sglang config.yaml .

# 健康检查
HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 \
    CMD curl -f http://localhost:30000/health || exit 1

EXPOSE 30000

ENTRYPOINT ["./entrypoint.sh"]

2.2 优化requirements.txt

只安装必要的包,并固定版本以确保一致性:

# 核心依赖
sglang==0.5.6
torch==2.1.2
transformers==4.36.2
accelerate==0.25.0

# 性能优化相关
flash-attn==2.3.6  # 如果支持的话
vllm==0.2.7  # 可选,用于PagedAttention

# 监控和工具
prometheus-client==0.19.0  # 暴露监控指标
uvicorn[standard]==0.24.0  # ASGI服务器

2.3 创建启动脚本和配置文件

entrypoint.sh

#!/bin/bash
set -e

# 设置环境变量
export CUDA_VISIBLE_DEVICES=${CUDA_VISIBLE_DEVICES:-0}
export MODEL_PATH=${MODEL_PATH:-"/models/llama-3-8b-instruct"}
export PORT=${PORT:-30000}
export TENSOR_PARALLEL_SIZE=${TENSOR_PARALLEL_SIZE:-1}

# 检查模型是否存在
if [ ! -d "$MODEL_PATH" ]; then
    echo "Error: Model directory $MODEL_PATH does not exist"
    exit 1
fi

# 启动服务
exec python3 -m sglang.launch_server \
    --model-path "$MODEL_PATH" \
    --host 0.0.0.0 \
    --port "$PORT" \
    --tensor-parallel-size "$TENSOR_PARALLEL_SIZE" \
    --log-level warning

config.yaml(配置文件示例):

server:
  host: "0.0.0.0"
  port: 30000
  workers: 1
  
model:
  path: "/models/llama-3-8b-instruct"
  tensor_parallel_size: 1
  max_model_len: 8192
  
generation:
  max_tokens: 1024
  temperature: 0.7
  top_p: 0.9
  
monitoring:
  metrics_port: 9090
  enable_prometheus: true

2.4 构建和测试镜像

# 给启动脚本执行权限
chmod +x entrypoint.sh

# 构建镜像(使用BuildKit加速)
DOCKER_BUILDKIT=1 docker build -t sglang-prod:v0.5.6 .

# 测试运行
docker run --gpus all -d \
  -p 30000:30000 \
  -v /path/to/models:/models \
  -e CUDA_VISIBLE_DEVICES=0 \
  --name sglang-test \
  sglang-prod:v0.5.6

# 检查服务状态
curl http://localhost:30000/health

3. Kubernetes部署配置

有了Docker镜像,我们现在把它部署到Kubernetes集群中。

3.1 创建Namespace和ConfigMap

首先创建一个专门的命名空间:

# namespace.yaml
apiVersion: v1
kind: Namespace
metadata:
  name: sglang-prod

创建ConfigMap存储配置:

# configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: sglang-config
  namespace: sglang-prod
data:
  model-path: "/models/llama-3-8b-instruct"
  tensor-parallel-size: "2"
  max-model-len: "8192"

3.2 创建持久化存储

模型文件通常很大,我们需要持久化存储:

# pvc.yaml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: sglang-model-pvc
  namespace: sglang-prod
spec:
  accessModes:
    - ReadOnlyMany
  resources:
    requests:
      storage: 100Gi
  storageClassName: standard

3.3 创建Deployment

这是核心部分,定义如何运行我们的服务:

# deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: sglang-deployment
  namespace: sglang-prod
  labels:
    app: sglang
spec:
  replicas: 2  # 初始副本数
  selector:
    matchLabels:
      app: sglang
  strategy:
    type: RollingUpdate
    rollingUpdate:
      maxSurge: 1
      maxUnavailable: 0
  template:
    metadata:
      labels:
        app: sglang
    spec:
      # 节点选择器,确保调度到有GPU的节点
      nodeSelector:
        accelerator: nvidia-gpu
      containers:
      - name: sglang-server
        image: sglang-prod:v0.5.6
        imagePullPolicy: IfNotPresent
        ports:
        - containerPort: 30000
          name: http
        - containerPort: 9090
          name: metrics
        env:
        - name: MODEL_PATH
          valueFrom:
            configMapKeyRef:
              name: sglang-config
              key: model-path
        - name: TENSOR_PARALLEL_SIZE
          valueFrom:
            configMapKeyRef:
              name: sglang-config
              key: tensor-parallel-size
        - name: CUDA_VISIBLE_DEVICES
          value: "0,1"  # 使用前两个GPU
        resources:
          limits:
            nvidia.com/gpu: 2  # 申请2个GPU
            memory: "32Gi"
            cpu: "8"
          requests:
            nvidia.com/gpu: 2
            memory: "16Gi"
            cpu: "4"
        volumeMounts:
        - name: model-storage
          mountPath: /models
          readOnly: true
        livenessProbe:
          httpGet:
            path: /health
            port: 30000
          initialDelaySeconds: 60
          periodSeconds: 30
          timeoutSeconds: 10
        readinessProbe:
          httpGet:
            path: /health
            port: 30000
          initialDelaySeconds: 30
          periodSeconds: 10
          timeoutSeconds: 5
        # 监控指标暴露
        args: ["--enable-metrics", "--metrics-port", "9090"]
      volumes:
      - name: model-storage
        persistentVolumeClaim:
          claimName: sglang-model-pvc
      # 容忍度设置,允许调度到有污点的节点
      tolerations:
      - key: "nvidia.com/gpu"
        operator: "Exists"
        effect: "NoSchedule"

3.4 创建Service和Ingress

Service提供内部负载均衡,Ingress提供外部访问:

# service.yaml
apiVersion: v1
kind: Service
metadata:
  name: sglang-service
  namespace: sglang-prod
spec:
  selector:
    app: sglang
  ports:
  - port: 30000
    targetPort: 30000
    name: http
  - port: 9090
    targetPort: 9090
    name: metrics
  type: ClusterIP
# ingress.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: sglang-ingress
  namespace: sglang-prod
  annotations:
    nginx.ingress.kubernetes.io/proxy-body-size: "50m"
    nginx.ingress.kubernetes.io/proxy-read-timeout: "300"
    nginx.ingress.kubernetes.io/proxy-send-timeout: "300"
spec:
  ingressClassName: nginx
  rules:
  - host: sglang.yourdomain.com
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: sglang-service
            port:
              number: 30000

3.5 部署所有资源

# 应用所有配置
kubectl apply -f namespace.yaml
kubectl apply -f configmap.yaml
kubectl apply -f pvc.yaml
kubectl apply -f deployment.yaml
kubectl apply -f service.yaml
kubectl apply -f ingress.yaml

# 检查部署状态
kubectl get all -n sglang-prod

# 查看Pod日志
kubectl logs -f deployment/sglang-deployment -n sglang-prod

4. 高可用与自动扩缩容配置

部署完成只是第一步,我们还需要确保服务的高可用性。

4.1 配置Horizontal Pod Autoscaler(HPA)

根据CPU/内存使用率自动扩缩容:

# hpa.yaml
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
  name: sglang-hpa
  namespace: sglang-prod
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: sglang-deployment
  minReplicas: 2
  maxReplicas: 10
  metrics:
  - type: Resource
    resource:
      name: cpu
      target:
        type: Utilization
        averageUtilization: 70
  - type: Resource
    resource:
      name: memory
      target:
        type: Utilization
        averageUtilization: 80
  behavior:
    scaleDown:
      stabilizationWindowSeconds: 300
      policies:
      - type: Percent
        value: 10
        periodSeconds: 60
    scaleUp:
      stabilizationWindowSeconds: 60
      policies:
      - type: Percent
        value: 100
        periodSeconds: 60

4.2 配置PodDisruptionBudget(PDB)

确保在维护期间至少有一定数量的Pod可用:

# pdb.yaml
apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
  name: sglang-pdb
  namespace: sglang-prod
spec:
  minAvailable: 1
  selector:
    matchLabels:
      app: sglang

4.3 多可用区部署

对于生产环境,建议跨多个可用区部署:

# 在Deployment的spec中添加
spec:
  template:
    spec:
      affinity:
        podAntiAffinity:
          requiredDuringSchedulingIgnoredDuringExecution:
          - labelSelector:
              matchExpressions:
              - key: app
                operator: In
                values:
                - sglang
            topologyKey: topology.kubernetes.io/zone
        nodeAffinity:
          requiredDuringSchedulingIgnoredDuringExecution:
            nodeSelectorTerms:
            - matchExpressions:
              - key: topology.kubernetes.io/zone
                operator: In
                values:
                - zone-a
                - zone-b
                - zone-c

5. 监控与告警配置

没有监控的系统就像在黑暗中开车,不知道什么时候会撞墙。

5.1 配置Prometheus监控

首先确保Prometheus能抓取我们的指标:

# service-monitor.yaml
apiVersion: monitoring.coreos.com/v1
kind: ServiceMonitor
metadata:
  name: sglang-monitor
  namespace: sglang-prod
spec:
  selector:
    matchLabels:
      app: sglang
  endpoints:
  - port: metrics
    interval: 30s
    path: /metrics
  namespaceSelector:
    matchNames:
    - sglang-prod

5.2 创建Grafana仪表板

在Grafana中创建监控面板,关键指标包括:

  • 请求QPS(每秒查询数)
  • 平均响应延迟
  • GPU利用率
  • 显存使用情况
  • 错误率
  • Pod运行状态

5.3 配置告警规则

# prometheus-rules.yaml
apiVersion: monitoring.coreos.com/v1
kind: PrometheusRule
metadata:
  name: sglang-alerts
  namespace: sglang-prod
spec:
  groups:
  - name: sglang.rules
    rules:
    - alert: HighErrorRate
      expr: rate(sglang_http_requests_total{status=~"5.."}[5m]) / rate(sglang_http_requests_total[5m]) > 0.05
      for: 5m
      labels:
        severity: critical
      annotations:
        summary: "SGLang服务错误率过高"
        description: "错误率超过5%,当前值 {{ $value }}"
    
    - alert: HighLatency
      expr: histogram_quantile(0.95, rate(sglang_request_duration_seconds_bucket[5m])) > 5
      for: 5m
      labels:
        severity: warning
      annotations:
        summary: "SGLang服务延迟过高"
        description: "95分位延迟超过5秒,当前值 {{ $value }}s"
    
    - alert: GPUHighMemoryUsage
      expr: sglang_gpu_memory_usage_percent > 90
      for: 10m
      labels:
        severity: warning
      annotations:
        summary: "GPU显存使用率过高"
        description: "GPU显存使用率超过90%,当前值 {{ $value }}%"

6. 实战:蓝绿部署与金丝雀发布

生产环境更新服务时,我们需要确保平滑过渡,不影响用户体验。

6.1 蓝绿部署策略

蓝绿部署让你可以零停机更新:

# 1. 部署新版本(绿色环境)
kubectl apply -f deployment-v2.yaml -n sglang-prod

# 2. 等待新版本就绪
kubectl rollout status deployment/sglang-deployment-v2 -n sglang-prod

# 3. 切换流量(更新Ingress指向新服务)
kubectl apply -f ingress-v2.yaml -n sglang-prod

# 4. 验证新版本运行正常后,删除旧版本
kubectl delete deployment sglang-deployment-v1 -n sglang-prod

6.2 金丝雀发布配置

更精细的流量控制,先让少量用户试用新版本:

# 使用Istio进行流量分割
apiVersion: networking.istio.io/v1beta1
kind: VirtualService
metadata:
  name: sglang-vs
  namespace: sglang-prod
spec:
  hosts:
  - sglang.yourdomain.com
  http:
  - match:
    - headers:
        user-type:
          exact: internal  # 内部用户先体验新版本
    route:
    - destination:
        host: sglang-service-v2
        port:
          number: 30000
      weight: 100
  - route:
    - destination:
        host: sglang-service-v1
        port:
          number: 30000
      weight: 90
    - destination:
        host: sglang-service-v2
        port:
          number: 30000
      weight: 10  # 10%的流量到新版本

7. 总结

通过Docker和Kubernetes的组合,我们把一个单点的SGLang服务,变成了一个高可用、可弹性伸缩、易于管理的生产级系统。这套方案的核心价值在于:

标准化与一致性:Docker镜像确保了环境的一致性,无论在哪里运行,行为都是一样的。

弹性与自愈:Kubernetes让服务具备了自动扩缩容和自我修复的能力,流量来了自动扩容,节点挂了自动迁移。

可观测性:完善的监控告警体系,让你随时掌握服务状态,快速定位问题。

安全更新:蓝绿部署和金丝雀发布让你可以安全地更新服务,不影响用户体验。

当然,这套方案不是一成不变的。你可以根据实际需求调整:

  • 如果流量不大,可以先从简单的Deployment开始
  • 如果需要更高的可用性,可以考虑多集群部署
  • 如果对延迟极其敏感,可以优化网络配置和节点亲和性

最关键的是,你现在有了一个坚实的基础架构,可以在此基础上不断优化和扩展。大模型服务的部署从来不是一蹴而就的,而是一个持续迭代的过程。有了这套方案,你至少可以少踩很多坑,把更多精力放在业务逻辑和模型优化上。


获取更多AI镜像

想探索更多AI镜像和应用场景?访问 CSDN星图镜像广场,提供丰富的预置镜像,覆盖大模型推理、图像生成、视频生成、模型微调等多个领域,支持一键部署。

Logo

免费领 200 小时云算力,进群参与显卡、AI PC 幸运抽奖

更多推荐