AIGC 推理加速:火山引擎镜像加速实践

技术

picture.image

来源 |火山引擎云原生团队

随着 Stable Diffusion 这类文生图模型的爆火,越来越多企业开始重视 AIGC 相关技术创新和技术实践,并积极探索应用落地。对于 AI 业务应用,一方面模型性能至关重要,算法工程师需要关注模型训练、参数调优以达到满意的识别率/准确率;另一方面,确保模型服务的稳定可靠同样重要,这依赖完善的云原生基础设施。

picture.image

上图是一个较为完整的文生图模型推理业务架构示意图,展示了一个在线推理业务在训练好模型后的业务部署流程:通过 CI/CD 流程,我们可以快速完成从代码提交到编译构建、容器镜像制作和发布的整个流程。

如图所示,开发者提交好修改的代码,就可以自动触发代码拉取构建业务容器镜像,将容器镜像推送到火山引擎的镜像仓库 CR 中,同时发布到容器服务 VKE 的集群上。火山引擎为业务负载也提供了非常灵活的选择,开发者可以使用 ECS 运行容器来应对长稳流量,使用弹性容器 VCI 来应对潮汐流量和突发流量请求。

相较传统业务,AI 推理业务有如下两个特点:

  • 容器镜像非常大,一般在 10G-40G,常规拉取需要耗时几小时,极大延长了 GPU 资源不可用时长,造成资源浪费
  • 业务具有明显的潮汐特点,为了控制成本需要在低峰时使用常驻资源运行,高峰时弹性使用新资源运行

为了解决上述问题,火山引擎推出了面向大规模、高并发 AI 推理业务场景的云原生解决方案,通过弹性伸缩,帮助用户 快速创建 GPU+CPU 资源 ,实现整体资源利用率的提升。同时,推出镜像加速方案, 将镜像拉取用时从小时级降低到分钟级 ,帮助用户大幅提升效率、降低云成本。

下面我们通过一个 Stable Diffusion 容器镜像,来整体演示/体验这一方案。

步骤一:快速构建镜像

首先我们需要制作一个 Stable Diffusion 的 webui 容器镜像。

Dockerfile 地址 : github.com/fengmingxing/vol-stable-diffusion-webui/tree/main (可以按照教程直接使用)

Dockerfile 内容:


          
FROM paas-cn-beijing.cr.volces.com/cuda/cuda:11.4.3-devel-ubuntu20.04
          
LABEL org.opencontainers.image.authors="xx@bytedance.com"
          
ENV PATH="/root/miniconda3/bin:${PATH}"
          
ARG DEBIAN_FRONTEND=noninteractive
          
ENV TZ=Europe/Moscow
          
RUN apt-get update && apt-get install -y git ffmpeg libsm6 libxext6 wget && \
          
    wget https://repo.anaconda.com/miniconda/Miniconda3-latest-Linux-x86_64.sh && \
          
    mkdir /root/.conda && \
          
    bash Miniconda3-latest-Linux-x86_64.sh -b && \
          
    rm -f Miniconda3-latest-Linux-x86_64.sh
          
#RUN conda install pytorch==1.12.1 torchvision==0.13.1 torchaudio==0.12.1 cudatoolkit=11.3 -c pytorch
          
RUN conda install pytorch==2.0.0 torchvision==0.15.0 torchaudio==2.0.0 pytorch-cuda=11.8 -c pytorch -c nvidia
          
RUN git clone https://github.com/AUTOMATIC1111/stable-diffusion-webui.git && \
          
    cd stable-diffusion-webui && \
          
    mkdir repositories && \
          
    git clone https://github.com/CompVis/stable-diffusion.git repositories/stable-diffusion && \
          
    git clone https://github.com/CompVis/taming-transformers.git repositories/taming-transformers && \
          
    git clone https://github.com/sczhou/CodeFormer.git repositories/CodeFormer && \
          
    git clone https://github.com/salesforce/BLIP.git repositories/BLIP && \
          
    git clone https://github.com/Stability-AI/stablediffusion repositories/stable-diffusion-stability-ai
          

          
RUN cd stable-diffusion-webui && \
          
    pip install transformers==4.19.2 diffusers==0.3.0 basicsr==1.4.2 gfpgan==1.3.8 gradio==3.30 numpy==1.23.3 Pillow==9.2.0 realesrgan==0.3.0 torch omegaconf==2.2.3 pytorch_lightning==1.7.6 scikit-image==0.19.2 fonts font-roboto timm==0.6.7 fairscale==0.4.9 piexif==1.1.3 einops==0.4.1 jsonmerge==1.8.0 clean-fid==0.1.29 resize-right==0.0.2 torchdiffeq==0.2.3 kornia==0.6.7 lark==1.1.2 inflection==0.5.1 GitPython==3.1.27 && \
          
    pip install git+https://github.com/crowsonkb/k-diffusion.git --prefer-binary && \
          
    pip install git+https://github.com/TencentARC/GFPGAN.git --prefer-binary && \
          
    pip install -r repositories/CodeFormer/requirements.txt --prefer-binary && \
          
    pip install -r requirements.txt  --prefer-binary && \
          
    pip install -U numpy  --prefer-binary && \
          
    pip install open_clip_torch && \
          
    pip install xformers==0.0.17
          
COPY ./misc.py /root/miniconda3/lib/python3.10/site-packages/basicsr/utils/misc.py
          
COPY ./ranged_response.py /root/miniconda3/lib/python3.10/site-packages/gradio/ranged_response.py
          
WORKDIR /stable-diffusion-webui
          
CMD ["python", "webui.py", "--xformers", "--enable-insecure-extension-access", "--api", "--skip-install", "--listen","--ckpt-dir", "/stable-diffusion-webui/models/Stable-diffusion" ]
          
#默认监听7860端口
      

注:如果在构建镜像过程中遇到网络问题,可以扫描文章末尾的二维码联系我们。

我们使用火山引擎持续交付 CP 创建流水线,来完成代码拉取和制作容器镜像:

picture.image

picture.image

选择推送到自定义镜像仓库,配置火山镜像仓库的地址。

  • 运行流水线

picture.image

镜像构建完成后,会推送到火山镜像仓库。

镜像示例: paas-cn-beijing.cr.volces.com/aigc/sd-webui:latest (大小 18.7GB)

步骤二:实现基于 GPU 使用率的弹性伸缩

如前文所述,互联网在线业务的流量有着这样一些特点:

  • 延时敏感
  • 稳定性要求高
  • 一般为长期服务
  • 具有明显峰谷潮汐变化

picture.image

单纯按照波峰准备资源易导致资源浪费

企业在部署业务时,为了使业务能安然度过高峰,必须按照高峰的流量预估准备资源。但当业务处于流量低谷时,很多资源会被浪费掉且无法通过超售进行回收。以抖音为例,它在波峰波谷间资源利用率的差距可能达到 40%,在促销等活动期间,更是会遇到极端峰值场景。

因此对于这类有着明显高低峰流量趋势的业务,通过弹性伸缩能力提高集群整体资源利用率显得尤为重要,而对于 AI 在线推理业务,因为 GPU 资源成本比 CPU 资源成本高得多,成本控制无疑是一大关键。

火山引擎容器服务 VKE 能根据业务流量自动弹性扩容或弹性自愈,保证业务平稳健康运行,它无缝集成弹性容器 VCI,可以实现海量实例快速秒级启动,帮助用户灵活应对业务高峰。以我们之前部署好的 Stable Diffusion WebUI 业务为例,我们可以 在 VKE 中进行配置 ,实现通过 GPU 指标进行弹性伸缩

  • 使用如下 YAML 部署,需要提前创建好 pv、pvc 用于存放模型(模型需要提前上传到 TOS;基于 GPU 指标的弹性伸缩有一些前置要求,如开通 VMP、安装 nvidia-device-plugin(dcgm-exporter)、prometheus-agent(node-exporter)、prometheus-adapter 组件等,参考: www.volcengine.com/docs/6460/166015

          
apiVersion: apps/v1
          
kind: Deployment
          
metadata:
          
  name: sd-a10
          
  namespace: default
          
spec:
          
  progressDeadlineSeconds: 600
          
  replicas: 1
          
  revisionHistoryLimit: 10
          
  selector:
          
    matchLabels:
          
      app: sd-a10
          
  strategy:
          
    rollingUpdate:
          
      maxSurge: 25%
          
      maxUnavailable: 25%
          
    type: RollingUpdate
          
  template:
          
    metadata:
          
      annotations:
          
        kubectl.kubernetes.io/restartedAt: "2023-06-21T03:43:08Z"
          
      creationTimestamp: null
          
      labels:
          
        app: sd-a10
          
    spec:
          
      containers:
          
      - image: paas-cn-beijing.cr.volces.com/aigc/sd-webui:latest
          
        imagePullPolicy: IfNotPresent
          
        name: sd
          
        resources:
          
          limits:
          
            nvidia.com/gpu: "1"
          
        terminationMessagePath: /dev/termination-log
          
        terminationMessagePolicy: File
          
        volumeMounts:
          
        - mountPath: /stable-diffusion-webui/models/Stable-diffusion/
          
          name: sd
          
      dnsPolicy: ClusterFirst
          
      restartPolicy: Always
          
      schedulerName: default-scheduler
          
      securityContext: {}
          
      terminationGracePeriodSeconds: 30
          
      volumes:
          
      - name: sd
          
        persistentVolumeClaim:
          
          claimName: fmx-sd-pvc
          

          
---
          
apiVersion: autoscaling/v2beta2
          
kind: HorizontalPodAutoscaler
          
metadata:
          
  name: gpu-hpa  
          
spec:
          
  scaleTargetRef:
          
    apiVersion: apps/v1
          
    kind: Deployment
          
    name: sd-a10 
          
  minReplicas: 1 
          
  maxReplicas: 10 
          
  metrics:
          
  - type: Pods
          
    pods:
          
      metric:
          
        name: k8s_pod_rate_gpu_used  
          
      target:
          
        averageValue: 20 
          
        type: AverageValue
      
  • 通过开启 GPU 节点池的节点自动伸缩能力,请求 Stable Diffusion WebUI,生成图片,增加负载,观察节点弹性伸缩的情况和时间

picture.image

picture.image

picture.image

picture.image

picture.image

  • 我们也可以通过 VMP 提供的容器组 GPU 监控能力来查看 Pod 的 GPU 使用情况

picture.image

当集群中不具备 pod 需要的 GPU 资源时,会先 pending,然后尝试扩容 GPU 节点,扩容成功后再正常拉起 pod。在这个过程,有两处比较耗时:一是拉起 ECS 加入到 VKE 集群,二是容器镜像的拉取。

从上述截图我们可以看到,ECS 拉起耗时约 90s,镜像拉起耗时约 240s,考虑到常规 AI 推理的容器镜像都非常大,再加上对弹性伸缩有明显要求,这里耗时过久 是不能接受的

步骤三:镜像加速

在上述示例中,我们的镜像大小为 18.7GB,接下来我们尝试使用火山引擎镜像仓库的加速能力,体验一下最终加速效果。

目前常见的镜像加速方案主要包括 P2P 加速和镜像缓存,我们先整体看下它们各自适合的业务场景。

picture.image

我们主要从集群节点规模和镜像变更频率两个维度来讨论镜像加速方案:

  • 推荐 P2P 加速 :集群节点规模比较大(超过 100 节点),镜像变更比较频繁,镜像经常有更新
  • 推荐镜像缓存 :集群节点规模不大(不超过 100 节点),镜像变更不频繁,即使有变更,更新内容也比较少

目前火山引擎容器服务同时支持通过 ECS 或弹性容器 VCI 两种方式部署业务,那么分别会有 ECS+P2P、ECS+镜像缓存、VCI+镜像缓存的方案组合。这里我们选择最常用的 ECS+P2P 和 VCI+镜像缓存两种方式做演示。

大规模集群节点的 P2P 加速

P2P 加速:

www.volcengine.com/docs/6460/658947

P2P 加速能力在大规模集群节点下加速效果非常明显,因本示例中的集群节点较少,所以加速效果有限,在文章的最后我们会给出我们实际压测的一些参考数据。我们建议集群 P2P 节点网络规模在 100~1000 节点之间。

  • 首先我们需要有一个镜像仓库 CR 的标准版,然后开启 P2P 加速,设置 P2P 加速范围的 VPC,该能力会和 VKE 一起使用。

picture.image

  • 在 VKE 中安装 P2P 加速组件

picture.image

  • 再次重新请求 Stable Diffusion WebUI,生成图片,增加负载,进行 HPA 和集群节点自动伸缩,观察拉取镜像的耗时情况

picture.image

具体的测试数据请见文末。

基于 VCI 镜像缓存加速

在 VKE 上,用户是可以基于 VCI 部署工作负载的,VCI 天然适合弹性伸缩场景,可以极速拉起 GPU 实例,同时基于我们提供的镜像缓存加速,快速拉起 pod。 操作参考: www.volcengine.com/docs/6460/187778

  • 首先创建镜像缓存

picture.image

picture.image

  • 通过控制台查询创建好的镜像缓存,获取镜像缓存 ID 以供后续使用

picture.image

  • 镜像缓存 ID:imc-3v0pt381bt5hlm3pdlxx。实际部署业务时,指定镜像缓存 ID 进行部署,我们使用 YAML 进行部署

          
apiVersion: apps/v1
          
kind: Deployment
          
metadata:
          
  name: vci-sd
          
  namespace: default
          
spec:
          
  progressDeadlineSeconds: 600
          
  replicas: 1
          
  revisionHistoryLimit: 10
          
  selector:
          
    matchLabels:
          
      app: vci-sd
          
  strategy:
          
    rollingUpdate:
          
      maxSurge: 25%
          
      maxUnavailable: 25%
          
    type: RollingUpdate
          
  template:
          
    metadata:
          
      annotations:
          
        vke.volcengine.com/burst-to-vci: enforce  # 强制使用 VCI
          
        vke.volcengine.com/preferred-subnet-ids: subnet-rqzrcez8jcaov0x57mnjk2q # 指定子网 ID
          
        vci.vke.volcengine.com/preferred-instance-types: vci.ini2.26c-243gi # 指定 VCI 的规格
          
        vci.vke.volcengine.com/image-cache-id: imc-3v0pt381bt5hlm3pdlxx #指定镜像缓存id
          
      creationTimestamp: null
          
      labels:
          
        app: vci-sd
          
    spec:
          
      containers:
          
      - image: paas-cn-beijing.cr.volces.com/aigc/sd-webui:latest
          
        imagePullPolicy: IfNotPresent
          
        name: sd
          
        terminationMessagePath: /dev/termination-log
          
        terminationMessagePolicy: File
          
        resources:
          
          limits:
          
            cpu: 26
          
            memory: 243Gi
          
            nvidia.com/GPU: 1
          
          requests:
          
            cpu: 26
          
            memory: 243Gi
          
            nvidia.com/GPU: 1
          
        volumeMounts:
          
        - mountPath: /stable-diffusion-webui/models/Stable-diffusion/
          
          name: sd
          
      dnsPolicy: ClusterFirst
          
      restartPolicy: Always
          
      schedulerName: default-scheduler
          
      securityContext: {}
          
      terminationGracePeriodSeconds: 30
          
      volumes:
          
      - name: sd
          
        persistentVolumeClaim:
          
          claimName: fmx-sd-pvc
      
  • 观察 deployment 创建到 pod 成功拉起的时间

picture.image

  • 从事件观察看从 deployment 创建到 pod 拉起总共使用 20 秒,我们再看下新增一个副本的耗时

picture.image

从截图可以看出,我们只需要 20s 就可以拉起一个 pod,相比没有使用镜像缓存的情况(基于 ECS 部署扩容一个实例需要 90+240s), 整个过程的 耗时缩短了约 93.9%

加速测试效果

测试环境:

块存储集群:15 台机器,总带宽 15*600MB/s≈9GB/s

picture.image

以上就是火山引擎推出的基于云原生产品的 AIGC 镜像加速实践,欢迎大家登录火山引擎控制台尝试!

picture.image

  • END -

相关链接

[1] 火山引擎: www.volcengine.com

[2] 火山引擎 VKE: www.volcengine.com/product/vke

[3] 火山引擎 CP: www.volcengine.com/product/cp

近期活动

时间:8 月 26 日

地点:深圳线下/线上直播

线下参会报名: https://www.bagevent.com/event/8659508

picture.image

0
0
0
0
评论
未登录
看完啦,登录分享一下感受吧~
暂无评论