当前位置:   article > 正文

优化 Stable Diffusion 在 GKE 上的启动体验

stable diffuion 谷歌启动器

以下文章来源于谷歌云服务,作者 Google Cloud

背景

现如今随着 AIGC 这个话题越来越热,越来越多优秀的开源项目基于文生图的 AI 模型如 MidJourney,Stable Diffusion 等应运而生。Stable Diffusion 是一个文字生成图像的 Diffusion 模型,它能够根据给定任何文本输入生成逼真的图像。我们在 GitHub Repo 中提供了三种不同的解决方案 (可参考https://github.com/nonokangwei/Stable-Diffusion-on-GCP),可以快速地分别在 GCP Vertex AI,GKE,和基于 Agones 的平台上部署 Stable Diffusion,以提供弹性的基础设施保证 Stable Diffusion 提供稳定的服务。本文将重点讨论 Stable Diffusion 模型在 GKE 上的实践。

提出问题

在实践中,我们也遇到了一些问题,例如 Stable Diffusion 的容器镜像较大,大约达到 10-20GB,导致容器在启动过程中拉取镜像的速度变慢,从而影响了启动时间。在需要快速扩容的场景下,启动新的容器副本需要超过 10 分钟的时间,严重影响了用户体验。

31b9264e831ad465d65ddca299d4aff2.png

我们看到容器的启动过程,按时序排列:

●触发 Cluster Autoscaler 扩容 + Node 启动并调度 Pod: 225s

●启动 Pull Image: 4s

●拉取镜像: 5m 23s

●启动 Pod: 1s

●能够提供 sd-webui 的服务 (大约): > 2m 

在这段时序分析中,我们可以看到,在 Stable Diffusion WebUI 运行在容器上启动慢主要面临的问题是由于整个 runtime 依赖较多,导致容器镜像太大从而花费了很长时间拉取下载、也造成了 pod 启动初始化加载时间过长。于是,我们考虑优化启动时间从以下三个方面入手:

●优化 Dockerfile,选择正确的 base image,精简 runtime 的依赖安装,减小镜像大小。

●借助基础环境与 runtime 依赖分离方式,通过磁盘复制方式加速运行环境的创建。

●通过 GKE Image Streaming 优化镜像加载时间,利用 Cluster Autoscaler 提升弹性扩缩容速度。

本文着重为大家介绍通过基础环境与 runtime 依赖分离方式,借助磁盘复制的高性能来优化 Stable Diffusion WebUI 容器启动时间的方案。

优化 Dockerfile

首先,我们可以参考官方 Stable Diffusion WebUI 安装说明,生成其 Dockerfile。在这里给大家一个参考: https://github.com/nonokangwei/Stable-Diffusion-on-GCP/blob/main/Stable-Diffusion-UI-Agones/sd-webui/Dockerfile 

在初始构建的 Stable Diffusion 的容器镜像中,我们发现除了基础镜像 nvidia runtime 之外,还安装了大量的库和扩展等。

5d1c41124c866f13682ba52a503c9878.png

▲ 调优之前容器镜像大小为 16.3GB

在 Dockerfile 优化方面,我们对 Dockerfile 进行分析后,发现 nvidia runtime 约占 2GB,而 PyTorch 库是一个非常大的包,约占 5GB。另外 Stable Diffusion 及其扩展等也占据了一定的空间。因此,我们按照最小可用环境为原则,去除环境中不必要的依赖。将 nvidia runtime 作为基础镜像,然后把 PyTorch、Stable Diffusion 的库和扩展等从原始镜像中分离出来,单独存放在文件系统中。

以下是初始的 Dockerfile 的片段。

# Base image

FROM nvidia/cuda:11.8.0-runtime-ubuntu22.04

RUN set -ex && \

      apt update && \

      apt install -y wget git python3 python3-venv python3-pip libglib2.0-0 pkg-config libcairo2-dev && \

      rm -rf /var/lib/apt/lists/*

# Pytorch  

RUN   python3 -m pip install torch==1.13.1+cu117 torchvision==0.14.1+cu117   --extra-index-url https://download.pytorch.org/whl/cu117

# Stable   Diffusion

RUN git   clone https://github.com/AUTOMATIC1111/stable-diffusion-webui.git

RUN git   clone https://github.com/Stability-AI/stablediffusion.git   /stable-diffusion-webui/repositories/stable-diffusion-stability-ai

RUN git   -C /stable-diffusion-webui/repositories/stable-diffusion-stability-ai   checkout cf1d67a6fd5ea1aa600c4df58e5b47da45f6bdbf

# Stable   Diffusion extensions

RUN set   -ex && cd stable-diffusion-webui \

    && git clone   https://gitcode.net/ranting8323/sd-webui-additional-networks.git   extensions/sd-webui-additional-networks \

    && git clone   https://gitcode.net/ranting8323/sd-webui-cutoff extensions/sd-webui-cutoff \

    && git clone   https://ghproxy.com/https://github.com/toshiaki1729/stable-diffusion-webui-dataset-tag-editor.git   extensions/stable-diffusion-webui-dataset-tag-editor

我们在移除 Pytorch 的库和 Stable Diffusion 之后,我们只保留了基础镜像 nvidia runtime 在新的 Dockerfile 中。

FROM   nvidia/cuda:11.8.0-runtime-ubuntu22.04

RUN set -ex && \

    apt update && \

    apt install -y wget   git python3 python3-venv   python3-pip   libglib2.0-0 && \

    rm -rf /var/lib/apt/lists/*

30fe0994d03028daf06bb0514800dfa2.png

▲ 基础镜像变成了 2GB

其余的运行时类库和 extension 等存放在磁盘镜像中,磁盘镜像的大小为 6.77GB。采用磁盘镜像的好处是,它可以最多支持同时恢复 1,000 块磁盘,完全能满足大规模扩缩容的使用场景。

19ab0f3109f6fb7b2bfc6ef5acf260bf.png

挂载磁盘到 GKE 节点

然而,问题来了,如何将这个单独的文件系统挂载到容器运行时中呢?一种想法是使用 Persistent VolumeClaim (PVC) 进行挂载,但由于 Stable Diffusion WebUI 在运行时既需要读取又需要写入磁盘,而 GKE 的 PD CSI 驱动程序目前不支持多写入 ReadWriteMany,只有像 Filestore 这样的 NFS 文件系统才能支持,但是通过网络挂载的 Filestore 就延迟来说仍然无法达到快速启动的效果。同时,由于 GKE 目前不支持在创建或更新 Nodepool 时挂载磁盘,所以我们考虑使用 DaemonSet 在 GKE 节点启动时挂载磁盘。具体做法如下:

144e8388c3454003fc86352f7ef0c471.png

那么如何将磁盘挂载到 GKE 的节点上呢?可以直接调用 Cloud SDK,创建基于磁盘镜像的磁盘。

gcloud   compute disks create sd-lib-disk-$NOW --type=pd-balanced --size=30GB --zone=$ZONE --image=$IMAGE_NAME

gcloud   compute instances attach-disk ${MY_NODE_NAME} --disk=projects/$PROJECT_ID/zones/$ZONE/disks/sd-lib-disk-$NOW --zone=$ZONE

利用 GKE Image Streaming

和 Cluster Autoscaler

另外,正如我们前面提到的那样,在优化镜像下载和加载时间方面,我们还启用了 GKE Image Streaming 来加速镜像的拉取速度。它的工作原理是使用网络挂载将容器数据层挂载到 containerd 中,并在网络、内存和磁盘上使用多个缓存层对其进行支持。一旦我们准备好 Image Streaming 挂载,您的容器就会在几秒钟内从 ImagePulling 状态转换为 Running (无论容器大小);这有效地将应用程序启动与容器映像中所需数据的数据传输并行化。因此,您可以看到更快的容器启动时间和更快速的自动缩放。

我们开启了 Cluster Autoscaler 功能,让有更多的请求到来时,GKE 节点自动进行弹性扩展。通过 Cluster Autoscaler 触发并决定扩展到多少个节点来接收新增的请求。当 CA 触发了新的一轮扩容,新的 GKE 节点注册到集群以后,Daemonset 就会开始工作,帮助挂载存储了 runtime 依赖的磁盘镜像,而 Stable Diffusion Deployment 则会通过 HostPath 来访问这个挂载在节点上的磁盘。

我们还使用了 Cluster Autoscaler 的 Optimization Utilization Profile 来缩短扩缩容时间、节省成本并提高机器利用率。

最后的启动效果如下:

20ff2f8fba7fc525ce23f20ac307fd3c.png

按时序排列

●触发 Cluster Autoscaler 扩容: 38s

●Node 启动并调度 Pod: 89s

●挂载 PVC: 4s

●启动 Pull Image: 10s

●拉取镜像: 1s

●启动 Pod: 1s

●能够提供 sd-webui 的服务 (大约): 65s

总共经历了大约 3 分钟的时间,就完成了启动一个新的 Stale Diffusion 容器实例,并在一个新的 GKE 节点上进行正常服务的过程。相比于之前的 12 分钟,可以看见,明显的提升启动速度改善了用户体验。

完整代码: https://github.com/nonokangwei/Stable-Diffusion-on-GCP/tree/main/Stable-Diffusion-UI-Agones/optimizated-init

通过 VolumeSnapshot

除了挂载 Disk 到 GKE 节点上,还有一种尝试,我们可以使用 StatefulSet 来挂载 PVC。

具体做法如下: 先定义一个 storageclass,注意我们使用 DiskImageType: images 来指定 PVC从 Disk Image 来恢复,而不是 Snapshot。

●Snapshot 每 10 分钟只能恢复一次,一小时以内恢复 6 次 Disk 的限制。

●而 Image 可以支持每 30 秒恢复一次,最多 1,000 个 Disk。

apiVersion: snapshot.storage.k8s.io/v1

kind: VolumeSnapshotClass

metadata:

    name: image-class

driver: pd.csi.storage.gke.io

deletionPolicy: Delete

parameters:

DiskImageType: images

再定义一个 VoluemSnapShotContent,它指定了 source 为一个 Disk Image sd-image。

apiVersion: snapshot.storage.k8s.io/v1

kind: VolumeSnapshotContent

metadata:

    name: test-snapshotcontent-from-image

spec:

    deletionPolicy: Retain

    driver: pd.csi.storage.gke.io

    volumeSnapshotClassName:   image-class

    source:

      snapshotHandle:projects/flius-vpc-2/global/images/sd-image

    volumeSnapshotRef:

      name: test-snapshot

      namespace: default

接下来,我们再创建一个 VolumeSnapShot,指定它的 source 是刚刚定义的VoluemSnapShotContent。

apiVersion: snapshot.storage.k8s.io/v1

kind: VolumeSnapshot

metadata:

    name: test-snapshot

namespace: default

spec:

    source:

  volumeSnapshotContentName:test-snapshotcontent-from-image

最后,我们创建一个 StatefulSet 来挂载这个 VolumeSnapShot。

apiVersion: apps/v1

kind: StatefulSet

metadata:

    name: stable-diffusion-statefulset-image

    labels:

      app: stable-diffusion

spec:

    podManagementPolicy: "Parallel"

    replicas: 1

    selector:

      matchLabels:

      app: stable-diffusion

    template:

      metadata:

      labels:

        app: stable-diffusion

      spec:

      containers:

      - name: stable-diffusion-webui

        image: us-central1-docker.pkg.dev/flius-vpc-2/stable-diffusion-repo/sd-webui-final:0.1

     command: ["/bin/bash"]

     args: ["-c", "source /runtime-lib/bin/activate; cp/user-watch.py /runtime-lib/stable-diffusion-webui/user-watch.py;cp/start.sh /runtime-lib/stable-diffusion-webui/start.sh; cd /runtime-lib/stable-diffusion-webui; python3 launch.py --listen --xformers   --enable-insecure-extension-access--no-gradio-queue" ]

        volumeMounts:

          - mountPath: "/runtime-lib"

            name: runtime-lib

        resources:

          limits:

            nvidia.com/gpu: 1

        ports:

          - containerPort: 7860

    volumeClaimTemplates:

    - metadata:

      name: runtime-lib

      spec:

      dataSource:

        name: test-snapshot

        kind: VolumeSnapshot

        apiGroup: snapshot.storage.k8s.io

      accessModes: [ "ReadWriteOnce" ]

      storageClassName: "standard-rwo"

      resources:

        requests:

          storage: 30Gi

我们尝试扩容更多的副本。

kubectl scale statefulset stable-diffusion-statefulset-image --replicas=15

可见 GKE 可以支持并行的启动这些 Pod,并且分别挂载相应的磁盘。

d0db3406a051cd7123ba3e71c1817186.png

PersistentVolumeClaims

7ef7b831ce1426678ac4c26cf54c1879.png

完整代码:

https://github.com/Leisureroad/volumesnapshot-from-diskimage

最终,我们可以看见如下的 Stable Diffusion 的 WebUI。

0d74efedb7717ce38768fd6f46ea591f.png


55ae0751dade9828a8335a7cd0f853cb.gif 点击屏末  | 了解更多 Google Cloud 技术趋势与最新解读

09378eb10201f2ef230cad8a9c963972.png

627b094a1a2900849284f809f80a8cda.png

34623645bf0884be6f5e7b7685c52b6f.png

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/Gausst松鼠会/article/detail/93888
推荐阅读
相关标签
  

闽ICP备14008679号