GLM-Image WebUI部署案例:私有云K8s集群中GLM-Image服务编排实践

1. 为什么要在K8s里跑GLM-Image WebUI

你可能已经试过在本地笔记本上启动GLM-Image的Gradio界面——点几下命令,浏览器打开http://localhost:7860,输入“一只穿西装的柴犬在咖啡馆写代码”,十几秒后一张风格统一、细节丰富的图就出来了。这种体验很爽,但问题也跟着来了:模型34GB,显存要24GB起步,普通开发机根本扛不住;多人同时访问会卡死;重启服务得手动敲命令;想换台机器部署?又得重装一遍环境。

这些不是小问题,而是从单机玩具走向生产可用的必经门槛。而私有云K8s集群,就是那个能真正把GLM-Image变成团队共享AI绘图服务的底座。

它不只解决“能不能跑”,更解决“稳不稳定”“好不好管”“扩不扩容”“安不安全”这四个核心问题。比如,当市场部同事下午三点集中提需求做海报,K8s能自动把WebUI副本从1个扩到3个;当某次生成任务意外占满GPU显存,K8s会立刻杀掉异常Pod,而不是让整个服务挂掉;所有模型缓存、输出图片都存在持久化卷里,哪怕节点宕机,数据也不丢。

这不是把单机脚本往容器里一塞就完事。它是一次面向工程落地的重新设计:把原来靠人肉维护的start.sh,变成可声明、可版本化、可审计的YAML;把临时下载的模型,变成预拉取、带校验的镜像层;把散落在/root/build/outputs/里的图片,变成按项目隔离、带生命周期管理的对象存储路径。

下面我们就从零开始,把GLM-Image WebUI真正“种”进你的私有云K8s里。

2. 镜像构建:从Python脚本到生产级容器

2.1 构建思路:轻量 + 确定性 + 可复现

直接用python:3.9-cuda基础镜像跑webui.py?不行。原因有三:第一,每次启动都要pip install一堆包,网络波动就失败;第二,Hugging Face模型默认缓存在~/.cache,容器重启就清空,下次还得重新下载34GB;第三,Gradio默认绑定0.0.0.0:7860,没做健康检查和优雅退出,K8s没法判断服务是否真就绪。

所以我们的Dockerfile必须做到三件事:

  • 把所有Python依赖、CUDA库、甚至模型权重(或至少是下载脚本)全打进镜像;
  • HF_ENDPOINTHF_HOME环境变量锁定模型源和缓存路径;
  • 加入livenessProbereadinessProbe探针,让K8s能真正“看懂”服务状态。

2.2 实战Dockerfile(精简版)

# 使用NVIDIA官方CUDA基础镜像,确保驱动兼容性
FROM nvidia/cuda:11.8.0-devel-ubuntu22.04

# 设置时区和语言,避免中文乱码
ENV TZ=Asia/Shanghai
RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone
ENV LANG=C.UTF-8 LC_ALL=C.UTF-8

# 创建非root用户,提升安全性
RUN groupadd -g 1001 -r user && useradd -S -u 1001 -r -g user -m -d /home/user user
USER user
WORKDIR /home/user

# 安装系统级依赖(ffmpeg用于视频相关扩展,libglib2.0-0是Gradio所需)
RUN apt-get update && apt-get install -y \
    ffmpeg \
    libglib2.0-0 \
    && rm -rf /var/lib/apt/lists/*

# 设置Hugging Face环境变量,所有缓存定向到容器内固定路径
ENV HF_HOME=/home/user/cache/huggingface
ENV HUGGINGFACE_HUB_CACHE=/home/user/cache/huggingface/hub
ENV TORCH_HOME=/home/user/cache/torch
ENV HF_ENDPOINT=https://hf-mirror.com

# 创建缓存目录结构
RUN mkdir -p $HF_HOME $HUGGINGFACE_HUB_CACHE $TORCH_HOME

# 复制项目代码(假设当前目录有webui.py、start.sh等)
COPY --chown=user:user . /home/user/

# 预下载模型(关键!避免Pod启动时网络卡顿)
# 此处使用huggingface-cli download,比运行时自动下载更可控
RUN pip install --no-cache-dir huggingface-hub && \
    huggingface-cli download --resume-download zai-org/GLM-Image --local-dir $HUGGINGFACE_HUB_CACHE/models--zai-org--GLM-Image

# 安装Python依赖(requirements.txt需提前准备好,含torch==2.1.0+cu118等精确版本)
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt

# 暴露端口,设置健康检查入口
EXPOSE 7860
HEALTHCHECK --interval=30s --timeout=3s --start-period=60s --retries=3 \
    CMD curl -f http://localhost:7860/gradio_api/health || exit 1

# 启动命令,用exec保证PID 1,支持信号传递
CMD ["bash", "start.sh", "--port", "7860"]

关键点说明

  • huggingface-cli download在构建阶段就拉取模型,镜像体积虽增大,但换来的是Pod秒级就绪;
  • HEALTHCHECK指向Gradio内置的/gradio_api/health端点,比简单curl首页更准确;
  • 所有路径都用环境变量定义,后续K8s配置可无缝替换。

2.3 构建与推送

# 构建镜像(注意tag带上日期和commit,便于回滚)
docker build -t registry.yourcompany.com/ai/glm-image-webui:v1.0.0-20240520 .

# 推送到私有仓库
docker push registry.yourcompany.com/ai/glm-image-webui:v1.0.0-20240520

3. K8s服务编排:不只是一个Deployment

3.1 核心资源清单设计

在K8s里,一个“服务”从来不止是Deployment。我们为GLM-Image WebUI准备了四类资源:

资源类型 作用 关键配置
Deployment 管理Pod副本、滚动更新、故障自愈 resources.limits.nvidia.com/gpu: 1liveness/readinessProbe
Service 提供集群内稳定访问入口 type: ClusterIP,暴露7860端口
PersistentVolumeClaim 持久化保存生成图片和模型缓存 storageClassName: ssdaccessModes: ReadWriteOnce
ConfigMap 解耦配置项(如端口、共享模式) --port 7860--share等参数外置

3.2 Deployment YAML(精简核心字段)

apiVersion: apps/v1
kind: Deployment
metadata:
  name: glm-image-webui
  labels:
    app: glm-image-webui
spec:
  replicas: 1
  selector:
    matchLabels:
      app: glm-image-webui
  template:
    metadata:
      labels:
        app: glm-image-webui
    spec:
      # 强制调度到有GPU的节点
      nodeSelector:
        nvidia.com/gpu.present: "true"
      # 请求1块A100 GPU,限制显存使用
      containers:
      - name: webui
        image: registry.yourcompany.com/ai/glm-image-webui:v1.0.0-20240520
        ports:
        - containerPort: 7860
          name: http
        envFrom:
        - configMapRef:
            name: glm-image-config
        # 挂载PVC,让outputs和cache落盘
        volumeMounts:
        - name: outputs
          mountPath: /home/user/outputs
        - name: cache
          mountPath: /home/user/cache
        # GPU资源请求
        resources:
          limits:
            nvidia.com/gpu: 1
            memory: 32Gi
            cpu: "8"
          requests:
            nvidia.com/gpu: 1
            memory: 24Gi
            cpu: "4"
        # 健康检查
        livenessProbe:
          httpGet:
            path: /gradio_api/health
            port: 7860
          initialDelaySeconds: 120
          periodSeconds: 30
        readinessProbe:
          httpGet:
            path: /gradio_api/health
            port: 7860
          initialDelaySeconds: 60
          periodSeconds: 10
      volumes:
      - name: outputs
        persistentVolumeClaim:
          claimName: glm-image-outputs-pvc
      - name: cache
        persistentVolumeClaim:
          claimName: glm-image-cache-pvc

为什么initialDelaySeconds设这么长?
因为首次加载34GB模型需要时间。我们给足2分钟缓冲,避免K8s误判为启动失败而反复重启Pod。

3.3 PVC与存储策略

# outputs PVC:保存用户生成的所有图片,需高IOPS
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: glm-image-outputs-pvc
spec:
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 100Gi
  storageClassName: ssd  # 对应高性能SSD存储类

# cache PVC:存放模型权重和PyTorch缓存,读多写少
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: glm-image-cache-pvc
spec:
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 50Gi
  storageClassName: hdd  # 对应大容量HDD存储类,降低成本

3.4 ConfigMap:让配置可管理

apiVersion: v1
kind: ConfigMap
metadata:
  name: glm-image-config
data:
  PORT: "7860"
  SHARE: "false"  # 生产环境默认不开启公网分享
  # 其他可配置项,如LOG_LEVEL, MODEL_PATH等

然后在Deployment的envFrom中引用,后续修改端口只需更新ConfigMap,无需重建Deployment。

4. 网络与访问:从集群内到企业内网

4.1 Service与Ingress分层设计

  • Service(ClusterIP):仅限K8s集群内部访问,供运维调试、Prometheus监控抓取指标;
  • Ingress:面向企业内网,通过公司统一域名(如glm-image.internal.company.com)提供HTTPS访问,自动注入SSL证书。
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: glm-image-ingress
  annotations:
    nginx.ingress.kubernetes.io/ssl-redirect: "true"
    nginx.ingress.kubernetes.io/proxy-body-size: "50m"  # 支持大尺寸图片上传
spec:
  ingressClassName: nginx
  tls:
  - hosts:
      - glm-image.internal.company.com
    secretName: company-tls-secret
  rules:
  - host: glm-image.internal.company.com
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: glm-image-service
            port:
              number: 7860

安全提示:禁用Gradio的--share参数(已在ConfigMap中设为false),防止生成公网可访问链接,杜绝数据泄露风险。

4.2 访问控制加固

在Ingress前加一层企业级API网关(如Kong或自研网关),实现:

  • IP白名单:仅允许市场部、设计部办公网段访问;
  • 请求频率限制:单IP每分钟最多10次生成请求,防滥用;
  • JWT鉴权:对接公司统一身份平台,未登录用户无法进入界面。

5. 运维与可观测性:让AI服务像数据库一样可靠

5.1 日志标准化

Gradio默认日志格式混乱。我们在启动脚本中统一重定向:

# start.sh 中添加
exec 1>>/home/user/logs/webui.log 2>&1
echo "$(date): Starting GLM-Image WebUI on port $PORT" >> /home/user/logs/webui.log
python webui.py --port $PORT --enable-xformers 2>&1

再通过Filebeat采集/home/user/logs/目录,打上app:glm-image-webui标签,接入ELK栈。这样查问题时,一句kibana: app:glm-image-webui AND "CUDA out of memory"就能定位OOM根源。

5.2 指标监控(Prometheus)

webui.py中嵌入Prometheus客户端,暴露关键指标:

指标名 类型 说明
glm_image_generate_duration_seconds Histogram 每次生成耗时(秒)
glm_image_generate_total Counter 总生成次数
glm_image_gpu_memory_used_bytes Gauge 当前GPU显存占用(字节)
glm_image_model_load_status Gauge 模型加载状态(1=成功,0=失败)

配合Grafana看板,运维人员一眼就能看到:过去1小时平均生成耗时是否突增?GPU显存是否持续95%以上?哪个时间段请求量最大?

5.3 故障自愈演练

我们定期执行混沌工程测试:

  • kubectl delete pod -l app=glm-image-webui:模拟Pod异常终止,验证K8s是否30秒内拉起新Pod;
  • kubectl patch pvc glm-image-outputs-pvc -p '{"spec":{"resources":{"requests":{"storage":"200Gi"}}}}':模拟磁盘空间不足,验证PVC扩容是否生效;
  • iptables -A OUTPUT -p tcp --dport 443 -j DROP:在Pod内切断外网,验证模型加载失败时是否有降级逻辑(如返回缓存图片)。

6. 总结:从Demo到Production的跨越

把GLM-Image WebUI跑在K8s上,表面看是换了个部署方式,实质是一次研发范式的升级:

  • 交付物变了:从一份README.md和几个shell脚本,变成可GitOps管理的YAML清单、带签名的Docker镜像、可审计的CI/CD流水线;
  • 责任边界清了:算法同学专注调参和提示词工程,运维同学负责资源水位和告警,前端同学优化界面交互,不再有人需要“啥都得懂一点”;
  • 成本更透明了:通过K8s metrics,我们清楚知道:每生成一张1024x1024图,消耗0.32个GPU-hour,对应电费X元,人力成本Y元,ROI一目了然;
  • 能力可扩展了:当业务需要支持文生视频,只需新增一个stable-video-diffusion的Deployment,复用同一套Ingress、监控、日志体系。

这条路没有银弹。你会遇到CUDA版本与PyTorch不兼容的深夜debug,会为PVC扩容失败翻遍K8s事件日志,也会在Ingress证书过期那天被全员@。但每一次踩坑,都在把AI能力从“能用”推向“敢用”——就像当年把MySQL从物理机迁到K8s,过程曲折,结果值得。

现在,你的私有云K8s集群里,已经准备好了一个随时待命的AI绘图引擎。它不声不响,却能在你需要时,把一行文字变成一张惊艳的视觉作品。


获取更多AI镜像

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

Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐