使用数据缓存加速部署Stable Diffusion应用

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

背景信息

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

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

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

前提条件

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

说明

如果VPC没有绑定公网NAT网关,您需要在创建数据缓存和部署应用时绑定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服务的镜像

创建数据缓存

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

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

  2. 创建数据缓存。

    说明

    如果您的业务部署在中国内地地域,建议您在中国香港地域创建数据缓存,然后拷贝数据缓存到您需要的地域。

    调用CreateDataCache接口创建数据缓存所采用的参数示例如下,表示从HuggingFace拉取指定模型数据,保存到名为test的Bucket的/model/stable-diffusion/目录。数据缓存名为stable-diffusion,保留时长为1天。

    重要

    如果您通过SDK创建数据缓存,DataSource.Options中的各个参数前无需添加参数名的长度,例如#10#repoSource直接写成repoSource#6#repoId直接写成repoId即可。

    {
      "RegionId": "cn-beijing",
      "SecurityGroupId": "sg-2ze63v3jtm8e6syi****",
      "VSwitchId": "vsw-2ze94pjtfuj9vaymf****",
      "Bucket": "test",
      "Path": "/model/stable-diffusion/",
      "Name": "stable-diffusion",
      "DataSource": {
        "Type": "URL",
        "Options": {
          "#10#repoSource": "HuggingFace/Model",
          "#6#repoId": "stabilityai/stable-diffusion-2-1"
        }
      },
      "RetentionDays": 1
    }
  3. 查询数据缓存状态。

    根据返回的数据缓存ID调用DescribeDataCaches接口查询数据缓存信息,当数据缓存的状态(DataCaches.Status)为Available时,表示可以使用该数据缓存。

部署Stable Diffusion应用

  1. 使用数据缓存创建ECI实例,部署Stable Diffusion应用。

    调用CreateContainerGroup接口创建ECI实例所采用的参数示例如下,该ECI实例使用GPU规格,并挂载了Stable Diffusion v2-1模型数据。实例内容器使用GPU版镜像,容器启动后会运行python3 http-server.py,启动一个HTTP服务。

    {
      "RegionId": "cn-beijing",
      "SecurityGroupId": "sg-2ze63v3jtm8e6syi****",
      "VSwitchId": "vsw-2ze94pjtfuj9vaymf****",
      "ContainerGroupName": "stable-diffusion",
      "InstanceType": "ecs.gn6i-c16g1.4xlarge",
      "DataCacheBucket": "test",
      "Container": [
        {
          "Arg": [
            "python3 http-server.py"
          ],
          "VolumeMount": [
            {
              "MountPath": "/data/model/",
              "Name": "model"
            }
          ],
          "Command": [
            "/bin/sh",
            "-c"
          ],
          "Gpu": 1,
          "Name": "stable-diffusion",
          "Image": "registry.cn-hangzhou.aliyuncs.com/eci_open/ubuntu:cuda11.7.1-cudnn8-ubuntu20.04"
        }
      ],
      "Volume": [
        {
          "Type": "HostPathVolume",
          "HostPathVolume.Path": "/model/stable-diffusion/",
          "Name": "model"
        }
      ]
    }
  2. 确认应用部署状态。

    根据返回的实例ID调用DescribeContainerGroupStatus查询实例和容器状态。当实例状态(Status)和容器状态(ContainerStatuses.State)为Running,表示实例已经创建成功,容器正在运行。

  3. 对外开放应用。

    在ECI实例所属VPC已绑定NAT网关的前提下,您需要创建DNAT条目,使得ECI实例可以对外提供公网访问服务。具体操作,请参见创建和管理DNAT条目

    说明

    如果VPC没有绑定NAT网关,则需要您在创建ECI实例时为其绑定一个EIP,此时可以跳过本步骤。

    创建DNAT条目采用的配置项示例如下:

    • 公网IP地址:选择NAT网关绑定的EIP地址。

    • 私网IP地址:选择部署了Stable Diffusion应用的ECI实例。

    • 端口设置:使用具体端口,公网端口配置为80,私网端口配置为8888,协议为TCP。

测试模型效果

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

    1. 在ECI实例所属安全组中添加入方向规则,开放应用对外端口(本文以80端口为例)。

    2. 打开浏览器,访问Stable Diffusion应用,并传入文本描述。

      在配置了DNAT的情况下,请输入DNAT条目中配置的公网IP地址和公网端口,以及文本描述,例如47.94.XX.XX:80?input=xxx。其中,input=xxx表示要传入的文本描述,Stable Diffusion应用会基于该文本描述生成图片,并将图片保存到容器的/workspace/pic目录下。示例如下:

      StableDiffusion-api.png
  2. 查看生成的图片。

    1. 在容器内部启动一个新的HTTP服务,用于查看图片。

      连接容器后执行以下命令:

      python3 -m http.server 7777 --directory /workspace/pic/ &
    2. 在ECI实例所属安全组中添加入方向规则,开放用于查看图片的端口(本文以70端口为例)。

    3. 创建一条新的DNAT条目,以便外部可以访问新的HTTP服务。

      新的DNAT条目采用的端口示例为:公网端口配置为70,私网端口配置为7777,协议为TCP。

    4. 查看生成的图片。

      输入新的DNAT条目中配置的公网IP地址和公网端口,例如47.94.XX.XX:70

      示例如下:

      StableDiffusion-api-1.png

使用支持WebUI的镜像

创建数据缓存

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

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

  2. 创建数据缓存。

    说明

    如果您的业务部署在中国内地地域,建议您在中国香港地域创建数据缓存,然后拷贝数据缓存到您需要的地域。

    调用CreateDataCache接口创建数据缓存所采用的参数示例如下,表示从HuggingFace拉取指定模型数据,保存到名为test的Bucket的/model/BeautyProMix/目录。数据缓存名为beautypromix,保留时长为1天。

    {
      "RegionId": "cn-beijing",
      "SecurityGroupId": "sg-2ze63v3jtm8e6syi****",
      "VSwitchId": "vsw-2ze94pjtfuj9vaymf****",
      "Bucket": "test",
      "Path": "/model/BeautyProMix/",
      "Name": "beautypromix",
      "DataSource": {
        "Type": "URL",
        "Options": {
          "#10#repoSource": "HuggingFace/Model",
          "#6#repoId": "hanafuusen2001/BeautyProMix"
        }
      },
      "RetentionDays": 1
    }
  3. 查询数据缓存状态。

    根据返回的数据缓存ID调用DescribeDataCaches接口查询数据缓存信息,当数据缓存的状态(DataCaches.Status)为Available时,表示可以使用该数据缓存。

部署Stable Diffusion应用

  1. 使用数据缓存创建ECI实例,部署Stable Diffusion应用。

    调用CreateContainerGroup接口创建ECI实例所采用的参数示例如下,该ECI实例使用GPU规格,并挂载了BeautyProMix模型数据。实例内的容器使用GPU版镜像,容器启动后会运行python3 launch.py --listen --skip-torch-cuda-test --port 8888 --no-half,启动一个WebUI。

    {
      "RegionId": "cn-beijing",
      "SecurityGroupId": "sg-2ze63v3jtm8e6syi****",
      "VSwitchId": "vsw-2ze94pjtfuj9vaymf****",
      "ContainerGroupName": "stable-diffusion",
      "InstanceType": "ecs.gn6i-c16g1.4xlarge",
      "DataCacheBucket": "test",
      "Container": [
        {
          "Arg": [
            "python3 launch.py --listen --skip-torch-cuda-test --port 8888 --no-half"
          ],
          "VolumeMount": [
            {
              "MountPath": "/stable-diffusion-webui/models/Stable-diffusion/",
              "Name": "model"
            }
          ],
          "Command": [
            "/bin/sh",
            "-c"
          ],
          "Gpu": 1,
          "Name": "stable-diffusion",
          "Image": "registry.cn-hangzhou.aliyuncs.com/eci_open/stable-diffusion:1.0.0"
        }
      ],
      "Volume": [
        {
          "Type": "HostPathVolume",
          "HostPathVolume.Path": "/model/BeautyProMix/",
          "Name": "model"
        }
      ]
    }
  2. 确认应用部署状态。

    根据返回的实例ID调用DescribeContainerGroupStatus查询实例和容器状态。当实例状态(Status)和容器状态(ContainerStatuses.State)为Running,表示实例已经创建成功,容器正在运行。

  3. 对外开放应用。

    在ECI实例所属VPC已绑定NAT网关的前提下,您需要创建DNAT条目,使得ECI实例可以对外提供公网访问服务。具体操作,请参见创建和管理DNAT条目

    说明

    如果VPC没有绑定NAT网关,则需要您在创建ECI实例时为其绑定一个EIP,此时可以跳过本步骤。

    创建DNAT条目采用的配置项示例如下:

    • 公网IP地址:选择NAT网关绑定的EIP地址。

    • 私网IP地址:选择部署了Stable Diffusion应用的ECI实例。

    • 端口设置:使用具体端口,公网端口配置为80,私网端口配置为8888,协议为TCP。

测试模型效果

  1. 在ECI实例所属安全组中添加入方向规则,开放应用对外端口。

  2. 打开浏览器,访问Stable Diffusion应用。

    在配置了DNAT的情况下,请输入DNAT条目中配置的公网IP地址和公网端口,例如47.94.XX.XX:80

    说明

    如果ECI实例单独绑定了EIP,请输入EIP地址和容器开放的端口,例如47.94.XX.XX:8888

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

    示例如下:

    webui-api.png