部署Stable Diffusion应用

本文以部署HuggingFace上的Stable Diffusion为例,演示如何使用DataCache。通过DataCache提前拉取Stable Diffusion相关模型数据,然后在创建Stable Diffusion应用Pod时直接挂载模型数据,可以免去在Pod中拉取模型数据的等待时间,加速Stable Diffusion应用部署。

背景信息

Stable Diffusion是一个可以根据文字描述生成和修改图像的模型。Stable Diffusion分为文字理解和图片生成两个部分。文字理解部分使用CLIP模型对文本进行Encoding,图片生成采用Diffusion模型。

重要
  • 阿里云不对第三方模型的合法性、安全性、准确性进行任何保证,阿里云不对由此引发的任何损害承担责任。

  • 您应自觉遵守第三方模型的用户协议、使用规范和相关法律法规,并就使用第三方模型的合法性、合规性自行承担相关责任。

前提条件

  • 集群中已部署DataCache CRD。具体操作,请参见部署DataCache CRD

  • 集群所属VPC已绑定公网NAT网关,并配置SNAT条目允许该VPC或下属交换机的资源可以访问公网。

    说明

    如果VPC没有绑定公网NAT网关,您需要在创建DataCache和部署应用时绑定EIP,以便可以拉取公网数据。

准备运行环境

部署Stable Diffusion应用需要准备好包含Stable Diffusion运行所需环境的容器镜像,环境需求包括CUDA、Diffusers库,以及其他的基础依赖等。ECI已经准备好了能运行大多数模型的稳定环境的容器镜像。如果您的应用没有特殊依赖,可以直接使用。

  • 启动HTTP服务的镜像

    • GPU版:registry.cn-hangzhou.aliyuncs.com/eci_open/ubuntu:cuda11.7.1-cudnn8-ubuntu20.04

    • CPU版:registry.cn-hangzhou.aliyuncs.com/eci_open/ubuntu:hf-ubuntu20.04

    镜像相关信息参考如下:

    展开查看构建镜像的完整Dockerfile

    以下Dockerfile可以构建一个基于Ubuntu和Python的开发环境,并预安装一些常用依赖。在启动容器后,可以使用命令python3 http-server.py启动一个HTTP服务。

    FROM nvidia/cuda:11.7.1-cudnn8-runtime-ubuntu20.04
    LABEL maintainer="Alibaba Cloud Serverless Container"
    
    ENV DEBIAN_FRONTEND=noninteractive
    
    RUN apt update && \
        apt install -y bash \
                       vim \
                       build-essential \
                       git \
                       git-lfs \
                       curl \
                       ca-certificates \
                       libsndfile1-dev \
                       libgl1 \
                       python3.8 \
                       python3-pip \
                       python3.8-venv && \
        rm -rf /var/lib/apt/lists
    
    # make sure to use venv
    RUN python3 -m venv /opt/venv
    ENV PATH="/opt/venv/bin:$PATH"
    RUN mkdir -p /workspace/pic/
    WORKDIR /workspace
    COPY http-server.py http-server.py
    
    # pre-install the heavy dependencies (these can later be overridden by the deps from setup.py)
    RUN python3 -m pip install --no-cache-dir --upgrade pip && \
        python3 -m pip install --no-cache-dir \
            torch \
            torchvision \
            torchaudio \
            invisible_watermark && \
        python3 -m pip install --no-cache-dir \
            accelerate \
            datasets \
            hf-doc-builder \
            huggingface-hub \
            Jinja2 \
            librosa \
            numpy \
            scipy \
            tensorboard \
            transformers \
            omegaconf \
            pytorch-lightning \
            xformers \
            safetensors \
            diffusers
            
    
    CMD ["/bin/bash"]

    展开查看镜像中用到的HTTP服务脚本

    http-server.py脚本是一个简单的HTTP服务,用于接收输入的文本,生成对应的图片,并返回图片路径。

    import os
    import hashlib
    import torch
    from diffusers import StableDiffusionPipeline, DPMSolverMultistepScheduler
    from http.server import BaseHTTPRequestHandler
    from http.server import HTTPServer
    from urllib.parse import urlparse, parse_qs
    
    MODEL_DIR_NEV = "MODEL_DIR"
    APP_PORT_ENV = "APP_PORT"
    
    
    def text2image(input):
        model_id = os.getenv(MODEL_DIR_NEV, default="/data/model/")
        pipe = StableDiffusionPipeline.from_pretrained(model_id, torch_dtype=torch.float16)
    
        pipe.scheduler = DPMSolverMultistepScheduler.from_config(pipe.scheduler.config)
        pipe = pipe.to("cuda")
    
        image = pipe(input).images[0]
        name = "/workspace/pic/" + hashlib.md5(input.encode('utf8')).hexdigest() + ".png"
        image.save(name)
        return name
    
    
    class GetHandler(BaseHTTPRequestHandler):
    
        def do_GET(self):
            query = parse_qs(urlparse(self.path).query)
            # 获取参数值
            input = query.get('input')[0]
            print("get user input:%s, try generate image" % input)
            picName = text2image(input)
            # 构造响应
            self.send_response(200)
            self.send_header('Content-type', 'text/html')
            self.end_headers()
            self.wfile.write(bytes("<html><head><title>Stable Diffusion</title></head>", "utf-8"))
            self.wfile.write(bytes("<body><p>Success generate image:%s</p>" % picName, "utf-8"))
            self.wfile.write(bytes("</body></html>", "utf-8"))
    
    
    if __name__ == '__main__':
        server = HTTPServer(('', int(os.getenv(APP_PORT_ENV, default="8888"))), GetHandler)
        server.serve_forever()
        print('Starting server')
    
  • 支持WebUI的镜像

    镜像地址:registry.cn-hangzhou.aliyuncs.com/eci_open/stable-diffusion:1.0.0

    说明

    该镜像对于Stable Diffusion模型是通用的,只需将创建好的模型数据缓存挂载到/stable-diffusion-webui/models/Stable-diffusion/目录即可使用不同的模型。

操作步骤

请根据使用的镜像选择对应的操作:

使用启动HTTP服务的镜像

创建DataCache

  1. 访问HuggingFace,获取模型ID。

    本示例使用stabilityai/stable-diffusion-2-1模型。在HuggingFace找到目标模型后,在模型详情页面顶部可以复制模型ID。

  2. 编写DataCache的YAML配置文件,然后使用该YAML文件创建DataCache拉取stable-diffusion-2-1模型数据。

    kubectl create -f datacache-test.yaml

    datacache-test.yaml的内容示例如下:

    apiVersion: eci.aliyun.com/v1alpha1
    kind: DataCache
    metadata:
      name: stable-diffusion
    spec:
      path: /model/stable-diffusion/                 # 设置模型数据的存储路径
      dataSource:
        type: URL 
        options:
          repoSource: HuggingFace/Model              # 指定数据源为HuggingFace的模型
          repoId: stabilityai/stable-diffusion-2-1   # 指定模型ID
      retentionDays: 1
      netConfig: 
        securityGroupId: sg-2ze63v3jtm8e6s******
        vSwitchId: vsw-2ze94pjtfuj9vay******         # 指定已配置SNAT的交换机
  3. 查看DataCache状态。

    kubectl get edc stable-diffusion

    当数据下载完成,DataCache的状态为Available时,表示可以使用DataCache。示例如下:

    StableDiffusion.png

部署Stable Diffusion应用

  1. 编写应用的YAML配置文件,然后使用该YAML文件部署Stable Diffusion应用。

    kubectl create -f stable-diffusion.yaml

    stable-diffusion.yaml的内容示例如下,可以创建一个包含1个Pod副本的Deployment,Pod使用GPU规格,并挂载了Stable Diffusion v2-1模型数据。Pod内容器使用GPU版镜像,容器启动后会运行python3 http-server.py,启动一个HTTP服务。

    说明

    以下示例使用的是GPU版镜像,创建ECI Pod时需要指定GPU规格并设置容器所需GPU数。您也可以使用CPU版镜像,CPU版应用启动会比GPU版快,但是推理会相对慢。

    apiVersion: apps/v1
    kind: Deployment
    metadata:
      name: stable-diffusion
      labels:
        app: stable-diffusion
    spec:
      replicas: 1
      selector:
        matchLabels:
          app: stable-diffusion
      template:
        metadata:
          name: stable-diffusion
          labels:
            app: stable-diffusion
            alibabacloud.com/eci: "true" 
          annotations:
            k8s.aliyun.com/eci-use-specs: ecs.gn7i-c16g1.4xlarge     # 指定GPU规格
            k8s.aliyun.com/eci-data-cache-bucket: "default"          # 指定DataCache Bucket
        spec:
          containers:
          - name: stable-diffusion
            image: registry.cn-hangzhou.aliyuncs.com/eci_open/ubuntu:cuda11.7.1-cudnn8-ubuntu20.04  # 使用GPU版镜像
            resources:
                limits:
                  nvidia.com/gpu: "1"           # 设置容器所需的GPU数
            command: ["/bin/sh"]
            args: ["-c","python3 http-server.py"]
            volumeMounts:
            - name: "model"
              mountPath: "/data/model/"         # 模型数据在容器内的挂载路径
          volumes: 
          - name: "model"
            hostPath:             
              path: "/model/stable-diffusion/"    # 挂载模型数据
  2. 确认应用部署状态。

    kubectl get deployment stable-diffusion
    kubectl get Pod

    示例如下,可以看到Stable Diffusion应用已经部署成功。

    StableDiffusion2.png
  3. 确认模型数据的挂载情况。

    kubectl exec -it <POD_NAME> -- bash
    ls /data/model

    示例如下,可以看到容器的/data/model目录下已挂载模型数据。

    StableDiffusion3.png
  4. 创建Service,以便外部可以访问Stable Diffusion应用。

    kubectl create -f stable-diffusion-svc.yaml

    stable-diffusion-svc.yaml的内容示例如下,可以创建一个公网类型、按使用流量计费的负载均衡服务(LoadBalancer Service),Service对外公开8888端口,可以将流量转发到带有app: stable-diffusion的Label的Pod(即Stable Diffusion应用Pod)的8888端口。

    apiVersion: v1
    kind: Service
    metadata:
      annotations:
        service.beta.kubernetes.io/alibaba-cloud-loadbalancer-address-type: internet
        service.beta.kubernetes.io/alibaba-cloud-loadbalancer-instance-charge-type: PayByCLCU
      name: stable-diffusion-svc
      namespace: default
    spec:
      externalTrafficPolicy: Local
      ports:
      - port: 8888
        protocol: TCP
        targetPort: 8888
      selector:
        app: stable-diffusion
      type: LoadBalancer
  5. 查看Service地址。

    kubectl get svc stable-diffusion-svc

    返回示例如下,Service对外地址(EXTERNAL-IP)为39.106.XX.XX。

    StableDiffusion4.png

测试模型效果

  1. 传入文本,测试是否可以生成图片。

    1. 在Pod所属安全组中添加入方向规则,开放8888端口。

    2. 打开浏览器,访问Service对外地址的8888端口,并传入文本描述。

      http://39.106.XX.XX:8888?input=xxx

      input=xxx表示要传入的文本描述,Stable Diffusion应用会基于该文本描述生成图片,并将图片保存到容器的/workspace/pic目录下。示例如下:

      StableDiffusion5.png
  2. 查看生成的图片。

    1. 在Pod所属安全组中添加入方向规则,开放查看图片服务的端口(本文使用7777作为示例)。

    2. 更新Service,添加查看图片服务所需的7777端口。

      kubectl patch service stable-diffusion-svc --type='json' -p '[{"op": "add", "path": "/spec/ports/-", "value": {"name":"image", "port": 7777, "targetPort": 7777}}]'
    3. 在容器内部启动一个新的HTTP服务(7777端口),用于查看图片。

      kubectl exec -it stable-diffusion-7fb849b98f-b9d9t -- bash
      python3 -m http.server 7777 --directory /workspace/pic/ &
    4. 打开浏览器,访问Service对外地址的7777端口,查看生成的图片。

      StableDiffusion6.png

使用支持WebUI的镜像

创建DataCache

  1. 访问HuggingFace,获取模型ID。

    本示例使用hanafuusen2001/BeautyProMix模型。在HuggingFace找到目标模型后,在模型详情页面顶部可以复制模型ID。

  2. 编写DataCache的YAML配置文件,然后使用该YAML文件创建DataCache拉取BeautyProMix模型数据。

    kubectl create -f datacache-test.yaml

    datacache-test.yaml的内容示例如下:

    apiVersion: eci.aliyun.com/v1alpha1
    kind: DataCache
    metadata:
      name: beautypromix
    spec:
      path: /model/BeautyProMix/                 # 设置模型数据的存储路径
      dataSource:
        type: URL 
        options:
          repoSource: HuggingFace/Model              # 指定数据源为HuggingFace的模型
          repoId: hanafuusen2001/BeautyProMix        # 指定模型ID
      retentionDays: 1
      netConfig: 
        securityGroupId: sg-2ze63v3jtm8e6s******
        vSwitchId: vsw-2ze94pjtfuj9vay******         # 指定已配置SNAT的交换机
  3. 查看DataCache状态。

    kubectl get edc beautypromix

    当数据下载完成,DataCache的状态为Available时,表示可以使用DataCache。示例如下:

    webui.png

部署Stable Diffusion应用

  1. 编写应用的YAML配置文件,然后使用该YAML文件部署Stable Diffusion应用。

    kubectl create -f stable-diffusion.yaml

    stable-diffusion.yaml的内容示例如下,可以创建一个包含1个Pod副本的Deployment,Pod使用GPU规格,并挂载了BeautyProMix模型数据。Pod内容器使用GPU版镜像,容器启动后会运行python3 launch.py --listen --skip-torch-cuda-test --port 8888 --no-half,启动一个WebUI。

    apiVersion: apps/v1
    kind: Deployment
    metadata:
      name: stable-diffusion
      labels:
        app: stable-diffusion
    spec:
      replicas: 1
      selector:
        matchLabels:
          app: stable-diffusion
      template:
        metadata:
          name: stable-diffusion
          labels:
            app: stable-diffusion
            alibabacloud.com/eci: "true" 
          annotations:
            k8s.aliyun.com/eci-use-specs: ecs.gn7i-c16g1.4xlarge     #指定GPU规格
            k8s.aliyun.com/eci-data-cache-bucket: "default"          #指定DataCache Bucket
        spec:
          containers:
          - name: stable-diffusion
            image: registry.cn-hangzhou.aliyuncs.com/eci_open/stable-diffusion:1.0.0  #使用支持WebUI的镜像
            resources:
                limits:
                  nvidia.com/gpu: "1"           #设置容器所需的GPU数
            command: ["/bin/sh"]
            args: ["-c","python3 launch.py --listen --skip-torch-cuda-test --port 8888 --no-half"]
            volumeMounts:
            - name: "model"
              mountPath: "/stable-diffusion-webui/models/Stable-diffusion/"         #模型数据在容器内的挂载路径
          volumes: 
          - name: "model"
            hostPath:             
              path: "/model/BeautyProMix/"    #挂载模型数据
  2. 确认应用部署状态。

    kubectl get deployment stable-diffusion
    kubectl get Pod

    示例如下,可以看到Stable Diffusion应用已经部署成功。

    webui2.png
  3. 确认模型数据的挂载情况。

    kubectl exec -it <POD_NAME> -- bash
    ls /stable-diffusion-webui/models/Stable-diffusion

    示例如下,可以看到容器的/stable-diffusion-webui/models/Stable-diffusion目录下已挂载模型数据。

    webui3.png
  4. 创建Service,以便外部可以访问Stable Diffusion应用。

    kubectl create -f stable-diffusion-svc.yaml

    stable-diffusion-svc.yaml的内容示例如下,可以创建一个公网类型、按使用流量计费的负载均衡服务(LoadBalancer Service),Service对外公开8888端口,可以将流量转发到带有app: stable-diffusion的Label的Pod(即Stable Diffusion应用Pod)的8888端口。

    apiVersion: v1
    kind: Service
    metadata:
      annotations:
        service.beta.kubernetes.io/alibaba-cloud-loadbalancer-address-type: internet
        service.beta.kubernetes.io/alibaba-cloud-loadbalancer-instance-charge-type: PayByCLCU
      name: stable-diffusion-svc
      namespace: default
    spec:
      externalTrafficPolicy: Local
      ports:
      - port: 8888
        protocol: TCP
        targetPort: 8888
      selector:
        app: stable-diffusion
      type: LoadBalancer
  5. 查看Service地址。

    kubectl get svc stable-diffusion-svc

    返回示例如下,Service对外地址(EXTERNAL-IP)为101.200.XX.XX。

    webui4.png

测试模型效果

  1. 在Pod所属安全组中添加入方向规则,开放8888端口。

  2. 打开浏览器,访问Service对外地址的8888端口。

  3. 输入文本描述,测试图片生成。

    示例如下:

    webui5.png