You need to enable JavaScript to run this app.
导航
一键部署 SD ComfyUI 单机版
最近更新时间:2025.06.17 19:23:57首次发布时间:2024.08.28 14:53:18
我的收藏
有用
有用
无用
无用

AI 应用是针对大模型在线推理场景推出的一站式开发部署运维平台。本文为您介绍如何通过模板一键部署 AI 生图 Stable Diffusion ComfyUI。

应用介绍

SD ComfyUI 是一个专为 Stable Diffusion 设计的基于节点的图形用户界面(GUI),以拖拽组件的方式搭建模型流程,支持实时查看和调整模型参数,简化了模型构建和训练的复杂性,使用户无需深入理解底层算法就能进行模型操作。SD ComfyUI 的原理简单来说就是将整个图像生成过程分解为多个独立的节点,每个节点都有自己独立的功能,例如加载模型,文本提示,生成图片等。每个模块通过输入和输出的线连在一起变成一个完整的工作流。

使用说明

下文主要介绍测试并验证通过的实践内容,为了获得符合预期的结果,同时符合使用限制,请按照本文方案(或在本文推荐的资源上)操作。如需替换方案,您可以联系对应的客户经理咨询。

前提条件

  • 容器服务 VKE

    • 已创建 VKE 集群,具体操作可参见 创建集群
    • (可选)如需挂载对象存储 TOS,请提前安装 csi-tos 组件。具体操作请参见 安装组件
    • (可选)如需以弹性容器实例方式部署,仅适用于网络模型为 VPC-CNI 的 VKE 集群,且集群中需提前安装 vci-virtual-kubelet 组件。具体操作请参见 安装组件
    • (可选)如需使用 GPU 资源,请提前安装 nvidia-device-plugin 组件。具体操作请参见 安装组件
  • 持续交付 CP

    • 已接入部署资源。部署资源必须为容器服务 VKE 集群。具体操作请参见 接入部署资源
  • (可选)如需挂载模型文件,请完成以下准备工作,任选一种存储即可。

    • 对象存储 TOS
      • 已将模型上传至 TOS Bucket 存储。具体操作请参见 对象存储快速入门
      • 已获取当前账号的访问密钥 AK & SK。建议使用最小化授权的 IAM 用户的访问密钥,要求已授权对应文件的访问权限。具体操作请参见 AI 仓库挂载和存储加速
    • 文件存储 NAS
      • 已将模型上传至 NAS 存储,要求 NAS 与待部署的 VKE 集群处于相同的 VPC。具体操作请参见 文件存储快速入门
    • 制品仓库
      • 已将模型上传至制品仓库存储。具体操作请参见 上传和下载 AI 格式制品快速入门
      • 已获取当前账号的访问密钥 AK & SK。建议使用最小化授权的 IAM 用户的访问密钥,要求该 IAM 用户已授权仓库挂载权限。
  • (可选)如需配置外部访问方式,请完成以下准备工作,任选一种方式即可。

    • API 网关:已创建网关实例,要求网关实例与待部署的 VKE 集群处于相同的 VPC。具体操作请参见 创建实例
    • 负载均衡 CLB:已创建负载均衡实例,要求负载均衡实例与待部署的 VKE 集群处于相同的 VPC。具体操作请参见 创建负载均衡实例

操作步骤

  1. 登录 持续交付控制台

  2. 在左侧导航栏选择 AI 应用

  3. 在 AI 应用页面,单击 创建应用

  4. 选择 基于框架创建 > AI 生图 - Stable Diffusion ComfyUI 单机版,单击 部署
    alt

  5. 按要求填写应用的相关配置信息。配置完成后单击 确定,应用将开始创建并部署。

    基本信息

    配置项说明
    应用标识根据界面提示填写应用标识。应用标识是应用的唯一标识,创建后不可更改。
    应用显示名自定义应用的显示名称。
    描述填写当前应用的备注信息,可以为空。

    部署集群

    配置项说明

    部署资源

    选择前提条件中接入的 VKE 部署集群。

    注意

    如需以弹性容器实例方式部署,仅适用于网络模型为 VPC-CNI 的 VKE 集群,且集群中需提前安装 vci-virtual-kubelet 组件。

    命名空间选择或输入要部署的命名空间。输入的命名空间不存在时会自动创建。
    环境标识自定义当前环境的唯一标识。环境标识创建成功后不支持修改。

    模型配置

    配置项说明
    镜像选择本模板使用预置镜像 SD ComfyUI

    模型

    支持 官方模型自持模型,请按需选择。

    • 官方模型:内置 SD 常用官方模型,帮助您快速拉起服务。

    • 自持模型:支持通过以下方式配置您自己的模型文件,请按需选择。

      • TOS 挂载:通过火山引擎对象存储 TOS 挂载模型文件。

        • 选择模型文件所在的 地域Bucket,并填写具体 路径

        • AK & SK:输入当前账号的访问密钥 AccessKey ID(AK)和 Secret Access Key(SK)。建议使用最小化授权的 IAM 用户的访问密钥,要求已授权对应文件的访问权限。

      • 制品库挂载:通过火山引擎制品仓库挂载模型文件。

        • 制品库:选择模型文件所在的 地域实例仓库命名空间模型名称版本。仅支持选择 AI 制品仓库
        • AK & SK:输入当前账号的访问密钥 AccessKey ID(AK)和 Secret Access Key(SK)。建议使用最小化授权的 IAM 用户的访问密钥,要求该 IAM 用户已授权仓库挂载权限。
      • NAS 挂载:通过火山引擎文件存储 NAS 挂载模型文件。

        • 文件系统类型:选择文件系统类型,支持 极速型容量型。要求 NAS 与待部署的 VKE 集群处于相同的 VPC。

        • 挂载点:选择要挂载的 文件系统挂载点

        • 子目录:输入待挂载的文件系统的子目录信息,默认为根目录/

    • 挂载路径:挂载到 SD ComfyUI 中的目标路径。 SD ComfyUI 默认的模型加载目录为/comfyui/models/checkpoints

    插件

    可选配置。将其他依赖资源,例如 LoRA 插件、VAE 模型、CLIP 模型等资源挂载到服务中。
    支持以下挂载方式,请按需选择。一个服务可添加多个插件。

    • TOS 挂载:通过火山引擎对象存储 TOS 挂载插件。

      • 选择插件所在的 地域Bucket,并填写具体 路径

      • AK & SK:输入当前账号的访问密钥 AccessKey ID(AK)和 Secret Access Key(SK)。建议使用最小化授权的 IAM 用户的访问密钥,要求已授权对应文件的访问权限。

    • NAS 挂载:通过火山引擎文件存储 NAS 挂载插件。

      • 文件系统类型:选择文件系统类型,支持 极速型容量型。要求 NAS 与待部署的 VKE 集群处于相同的 VPC。

      • 挂载点:选择要挂载的 文件系统挂载点

      • 子目录:输入待挂载的文件系统的子目录信息,默认为根目录/

    • 挂载路径:挂载到 SD ComfyUI 中的目标路径。SD ComfyUI 的根目录为/comfyui ,根据不同的资源需求,需要挂载到不同的目录下,具体请参考 预置镜像默认的 ComfyUI 常用路径

    出图位置

    如果您希望对输出物进行持久化存储,需要对出图目录进行挂载。
    支持以下挂载方式,请按需选择。

    • TOS 挂载:通过火山引擎对象存储 TOS 挂载出图目录。

      • 选择出图目录所在的 地域Bucket,并填写具体 路径

      • AK & SK:输入当前账号的访问密钥 AccessKey ID(AK)和 Secret Access Key(SK)。建议使用最小化授权的 IAM 用户的访问密钥。

    • NAS 挂载:通过火山引擎文件存储 NAS 挂载出图目录。

      • 文件系统类型:选择文件系统类型,支持 极速型容量型。要求 NAS 与待部署的 VKE 集群处于相同的 VPC。

      • 挂载点:选择要挂载的 文件系统挂载点

      • 子目录:输入待挂载的文件系统的子目录信息,默认为根目录/

    • 挂载路径:挂载到 SD ComfyUI 中的目标路径。SD ComfyUI 项目的默认出图目录为/comfyui/output

    启动命令

    设置应用的启动命令和监听的容器端口。默认为python -u main.py --listen --port 7860,支持根据业务需求修改启动命令。

    服务规格

    • 勾选 以弹性容器实例方式部署

      配置项说明
      实例数根据业务规模,设置应用的副本数。

      弹性容器实例

      保持勾选 以弹性容器实例方式部署。使用该方式部署应用,无需管理底层云服务器等基础设施,只需提供镜像即可运行容器,并为实际消耗的资源付费。计费详情,请参见 弹性容器计费说明

      说明

      如果您在 VCI 侧申请镜像缓存 的邀测功能,AI 应用创建时将默认开启镜像缓存,加速镜像拉取。

      日志采集

      是否通过火山引擎日志服务 TLS 采集 VCI 容器日志,对日志进行持久化存储。

      • 勾选,开启日志采集。

      • 不勾选,不开启日志采集。

      注意

      开启日志采集后,请确保已在 VKE 集群的日志中心中完成相关日志采集规则的配置,保证日志可正常投递至您配置的项目和主题中。具体操作请参见 采集容器日志

      资源配置类型

      根据业务需求选择要使用的实例规格。不同计算规格提供的虚拟化能力不同,详情请参见 VCI 实例规格介绍

      • 通用型:搭配多种 vCPU、内存配比的通用型规格族。

      • GPU 计算型:基于多种 NVIDIA Tesla 显卡提供的 GPU 计算型实例规格族。

      系统盘

      设置系统盘容量。系统盘主要用于缓存镜像,容量必须大于镜像大小。

      说明

      如果您在 VCI 侧 申请 了手动指定系统盘大小的邀测功能,支持在此处自定义系统盘的容量。

    • 不勾选 以弹性容器实例方式部署

      配置项说明
      实例数根据业务规模,设置应用的副本数。

      资源配置类型

      • CPU:定义每个实例的 CPU 规格。其中,CPU 请求 ≤ CPU 上限。

      • 内存:定义每个实例的内存规格。其中,内存请求 ≤ 内存上限。

      • GPU 资源类型:是否使用 GPU 资源。

        • 不开启:使用传统 CPU 计算资源。

        • Nvdia:使用 GPU 计算资源,当前提供 Nvdia 卡型,支持自定义 GPU 算力

    访问配置(可选)

    配置 AI 应用的访问方式。详细内容可参见 访问设置

结果验证

通过控制台使用 ComfyUI

  1. 验证应用是否部署成功
    在应用的 基本信息 > 资源配置 页签,查看应用的部署进度。支持通过实例的 运行状态实时日志事件,查看应用详细的启动和运行信息。
    alt

    • 查看实时日志:单击 实时日志,可查看应用的运行信息,方便运维和排障。
    • 查看事件:单击 事件,可查看应用的启动信息。最多可查看最近 1 小时内发生的事件信息。
  2. 访问 SD ComfyUI
    应用部署成功后,可在 访问设置 区域复制应用的访问地址。通过该地址去访问 SD ComfyUI。

  3. 文生图
    在 SD ComfyUI 操作界面,编排您的文生图工作流。输入正向和反向提示词,单击 Queue Prompt,即可生成描述提示词的图片。
    alt

通过 API 使用 ComfyUI

  1. 在 ComfyUI 访问设置 区域复制应用的访问地址。

    alt

  2. 开启开发者模式。

    alt

  3. 将工作流保存为 API 格式。

    alt

  4. 创建 Python 脚本 comfyui_api.py

    • workflowfile:替换为您的工作流 workflow 文件。
    • COMFYUI_ENDPOINT:替换为访问设置中的访问地址。
    import json
    import os
    
    import websocket
    import uuid
    import urllib.request
    import urllib.parse
    
    
    def show_gif(fname):
        import base64
        from IPython import display
        with open(fname, 'rb') as fd:
            b64 = base64.b64encode(fd.read()).decode('ascii')
        return display.HTML(f'<img src="data:image/gif;base64,{b64}" />')
    
    
    def queue_prompt(prompt):
        p = {"prompt": prompt, "client_id": client_id}
        data = json.dumps(p).encode('utf-8')
        req = urllib.request.Request("http://{}/prompt".format(server_address), data=data)
        return json.loads(urllib.request.urlopen(req).read())
    
    
    def get_image(filename, subfolder, folder_type):
        data = {"filename": filename, "subfolder": subfolder, "type": folder_type}
        url_values = urllib.parse.urlencode(data)
        with urllib.request.urlopen("http://{}/view?{}".format(server_address, url_values)) as response:
            return response.read()
    
    
    def get_history(prompt_id):
        with urllib.request.urlopen("http://{}/history/{}".format(server_address, prompt_id)) as response:
            return json.loads(response.read())
    
    
    def get_images(ws, prompt):
        prompt_id = queue_prompt(prompt)['prompt_id']
        print('prompt')
        print(prompt)
        print('prompt_id:{}'.format(prompt_id))
        output_images = {}
        while True:
            out = ws.recv()
            if isinstance(out, str):
                message = json.loads(out)
                if message['type'] == 'executing':
                    data = message['data']
                    if data['node'] is None and data['prompt_id'] == prompt_id:
                        print('执行完成')
                        break  # 执行完成
            else:
                continue  # 预览为二进制数据
    
        history = get_history(prompt_id)[prompt_id]
        print(history)
        for o in history['outputs']:
            for node_id in history['outputs']:
                node_output = history['outputs'][node_id]
                # 图片分支
                if 'images' in node_output:
                    images_output = []
                    for image in node_output['images']:
                        image_data = get_image(image['filename'], image['subfolder'], image['type'])
                        images_output.append(image_data)
                    output_images[node_id] = images_output
                # 视频分支
                if 'videos' in node_output:
                    videos_output = []
                    for video in node_output['videos']:
                        video_data = get_image(video['filename'], video['subfolder'], video['type'])
                        videos_output.append(video_data)
                    output_images[node_id] = videos_output
    
        print('获取图片完成')
        # print(output_images)
        return output_images
    
    
    def parse_worflow(ws, prompt, seed, workflowfile):
        workflowfile = workflowfile
        print('workflowfile:' + workflowfile)
        with open(workflowfile, 'r', encoding="utf-8") as workflow_api_txt2gif_file:
            prompt_data = json.load(workflow_api_txt2gif_file)
            # 设置文本提示
            prompt_data["6"]["inputs"]["text"] = prompt
    
            return get_images(ws, prompt_data)
    
    
    def generate_clip(prompt, seed, workflowfile, idx):
        print('seed:' + str(seed))
        ws = websocket.WebSocket()
        ws.connect("ws://{}/ws?clientId={}".format(server_address, client_id))
        images = parse_worflow(ws, prompt, seed, workflowfile)
    
        for node_id in images:
            for image_data in images[node_id]:
                from datetime import datetime
    
                # 获取当前时间,并格式化为 YYYYMMDDHHMMSS 的格式
                timestamp = datetime.now().strftime("%Y%m%d%H%M%S")
    
                # 使用格式化的时间戳在文件名中
                GIF_LOCATION = "{}/{}_{}_{}.png".format(SageMaker_ComfyUI, idx, seed, timestamp)
    
                os.makedirs(os.path.dirname(GIF_LOCATION), exist_ok=True)  # 自动创建目录
    
                print('GIF_LOCATION:' + GIF_LOCATION)
                with open(GIF_LOCATION, "wb") as binary_file:
                    # 写入二进制文件
                    binary_file.write(image_data)
    
                # show_gif(GIF_LOCATION)
    
                print("{} DONE!!!".format(GIF_LOCATION))
    
    
    if __name__ == "__main__":
        # 设置工作目录和项目相关的路径
        WORKING_DIR = 'output'
        SageMaker_ComfyUI = WORKING_DIR
        workflowfile = 'workflow_api.json'
        COMFYUI_ENDPOINT = '14.**.**.**:***'
    
        server_address = COMFYUI_ENDPOINT
        client_id = str(uuid.uuid4())  # 生成一个唯一的客户端ID
    
        seed = 15465856
        prompts = [
            "Anime girl with long pastel pink hair, large sapphire gradient eyes, fair skin, wearing a floral kimono - inspired outfit with floating sakura petals.",
            "Anime girl with long flowing mint green hair, striking emerald gradient eyes, smooth porcelain skin, adorned in a celestial - themed yukata with tiny glowing stars and floating iridescent feathers."]
    
        idx = 1
        for prompt in prompts:
            generate_clip(prompt, seed, workflowfile, idx)
            idx += 1
    
  5. 执行 python3 comfyui_api.py 将生成图片并保存至本地文件。

常见问题

预置镜像默认的 ComfyUI 常用路径有哪些?

预置镜像默认的 ComfyUI 常用路径请参见下表。

名称路径说明
root/comfyuiComfyUI 项目的根目录。
output/comfyui/outputComfyUI 图片生成目录。
custom_nodes/comfyui/custom_nodesComfyUI 自定义节点目录。
workflows/comfyui/user/default/workflowsComfyUI 工作流保存目录。
checkpoints/comfyui/models/checkpointsComfyUI checkpoints 路径。
lora/comfyui/models/loraComfyUI LoRA 相关插件配置路径。
vae/comfyui/models/vaeComfyUI VAE 模型配置路径。
clip/comfyui/models/clipComfyUI CLIP 模型配置路径。