全部产品
Search
文档中心

人工智能平台 PAI:AI视频生成-ComfyUI部署

更新时间:Jan 14, 2026

ComfyUI提供基于Stable Diffusion的节点式操作界面,可方便快速地构建复杂的AIGC流程,完成短视频内容生成、动画制作等任务。本文介绍如何在EAS中部署和使用ComfyUI。

重要限制与注意事项

部署前必读:了解以下关键限制,以避免不必要的资源浪费和常见错误。

  • GPU 使用限制:每个 EAS 实例仅运行一个 ComfyUI 进程,仅支持单张 GPU(单机单卡或多机单卡),不支持多卡并行推理。请勿选择多卡 GPU 规格(如 2*A10 等),这会造成资源浪费且无法提升单任务的性能。

  • 水平扩展方式:如需提升并发处理能力,请使用API版并增加实例数量,而非选择多卡规格。

  • Serverless 版核心限制Serverless版仅可使用内置模型和插件。如需上传自定义模型或安装第三方节点,请选择标准版、API版或集群版。

  • 插件安装方式:EAS 不支持通过 ComfyUI 管理器或 Git Clone 等方式从网络直接安装自定义节点(插件)。所有自定义内容都必须通过挂载对象存储(OSS)或文件存储(NAS)的方式上传。

选择部署版本

根据使用场景选择合适的版本:

部署版本

适用场景

调用方式

主要特点

计费模式

标准版

单用户开发和测试

  • WebUI

  • 在线调试

  • API调用(同步)

  • 并发能力有限

  • 建议部署单实例

按照服务的运行时长计费(部署成功后即使不使用也计费)。

API版

生产环境高并发

API调用(异步)

  • 基于EAS队列服务

  • 需要额外CPU资源

  • 支持负载均衡

集群版WebUI

多用户团队和教学

WebUI

  • 用户环境隔离

  • GPU资源共享

  • 资源消耗较高

原理参见集群版服务原理介绍

Serverless版

波动性工作负载,成本优化

WebUI

  • 无法使用自定义模型/插件

  • 仅华东2(上海)、华东1(杭州)地域可用

部署免费,仅在服务调用时按实际推理实时长计费。

更多计费详情请参见模型在线服务(EAS)计费说明

说明

此处API调用的同步与异步取决于是否使用EAS的队列服务:

  • 同步调用:直接请求推理实例,不使用EAS的队列服务;

  • 异步调用:使用EAS的队列服务,向输入队列发送请求,以订阅的方式获得结果推送。

由于ComfyUI本身具有异步队列系统,即使发起同步调用,实质上也是异步进行的。用户发送请求后,系统会返回一个Prompt ID,然后需要使用Prompt ID轮询以获取推理结果。

部署服务

Serverless版:只能使用场景化模型部署。

标准版、集群版、API版:可使用场景化模型部署(操作简单)或者自定义模型部署(支持更多功能)。

方式一:场景化模型部署(推荐)

  1. 登录PAI控制台,在页面上方选择目标地域,并在右侧选择目标工作空间,然后单击进入EAS

  2. 模型在线服务 (EAS)页面,单击部署服务,在场景化模型部署区域,单击AI视频生成-ComfyUI部署

  3. 配置以下关键参数:

    • 版本选择:参见选择部署版本

    • 模型配置:如需使用自己的模型、安装自定义节点或通过API调用,必须配置此项。以对象存储(OSS)为例,选择Bucket和目录,部署成功后系统会自动在其中创建ComfyUI所需目录。

    • 资源配置选择:资源规格必须选择GPU类型,推荐使用GU30、A10或T4卡型。ml.gu7i.c16m60.1-gu30性价比高,如库存不足可选择ecs.gn6i-c16g1.4xlarge

  4. 单击部署。等待约5分钟,当服务状态变为运行中,表示部署成功。

方式二:自定义模型部署

  1. 登录PAI控制台,在页面上方选择目标地域,并在右侧选择目标工作空间,然后单击进入EAS

  2. 推理服务页签,单击部署服务,然后在自定义模型部署区域,单击自定义部署

  3. 配置以下关键参数:

    • 部署方式:选择镜像部署,勾选开启Web应用复选框。

    • 镜像配置:在官方镜像列表中选择comfyui:1.9。其中:x.x:表示标准版,x.x-api:表示API版,x.x-cluster:表示集群版。

      说明
      • 由于版本迭代迅速,部署时镜像版本选择最高版本即可。

      • 更多关于每个版本的使用场景说明,请参见选择部署版本

    • 存储挂载:如需使用自己的模型、安装自定义节点或通过API调用,必须进行存储挂载。以对象存储(OSS)为例,选择Bucket和目录,部署成功后系统会自动在其中创建ComfyUI所需目录。请确保创建的存储空间与EAS服务位于同一地域。

      • Uri:单击image选择已创建的OSS存储目录。例如oss://bucket-test/data-oss/

      • 挂载路径:配置为/mnt/data,表示将您配置的OSS文件目录挂载到容器路径/mnt/data下 。

    • 运行命令

      • 选择镜像版本后,系统自动配置运行命令。

      • 若进行了存储挂载,则必须在运行命令中添加--data-dir参数, 并且其值与挂载路径完全一致。例如python main.py --listen --port 8000 --data-dir /mnt/data --cache-root /stable-diffusion-cache

    • 资源类型:选择公共资源

    • 部署资源:资源规格必须选择GPU类型,推荐使用GU30、A10或T4卡型。ml.gu7i.c16m60.1-gu30性价比高,如库存不足可选择ecs.gn6i-c16g1.4xlarge

  4. 单击部署。服务部署时间约为5分钟,当服务状态运行中时,表明服务已成功部署。

通过WebUI使用

重要

标准版、集群版和Serverless版支持通过WebUI使用。

进入Web界面

单击目标服务名称进入概览页面,在右上角单击Web应用

如页面长时间无法打开,请参见刷新页面时间过长或页面卡死

使用模板工作流

ComfyUI为常见任务提供预置模板。

  1. 选择一个模板(如Wan VACE 文生视频)。

    说明

    ComfyUI不同镜像版本中的模板有差别,若无示例模板,可选择其他模板使用。也可以加载本地文件系统中的工作流使用。

image

  1. 工作流加载成功后,如遇到报错缺少模型,可忽视(建议勾选不再显示此消息)。

  2. 由于路径变更,直接运行工作流可能会出现以下报错。

    image.png

    请先在Load models here区域重新选择模型wan2.1_vace_14B_fp16.safetensorsWan21_CausVid_14B_T2V_lora_rank32.safetensors

    image

  3. 工作流运行成功后,会在Save Video区域,展示生成的视频。image

使用自定义模型和节点

重要

Serverless版本不支持此功能。

  1. 确认服务已配置存储挂载。如使用自定义部署,需在运行命令中增加参数--data-dir挂载目录,详情见方式二:自定义模型部署

    服务部署成功后,系统会自动在已挂载的OSS或NAS存储空间中创建如下目录结构。

    data-oss/
    ├── custom_nodes/     # 存放节点文件(ComfyUI插件)
    ├── models/           # 存放模型文件
    ├── input/
    ├── output/
    ├── unet/
    └── temp/
  2. 上传模型或节点文件。以OSS为例,可控制台上传文件到OSS。对于大文件,请参见如何上传大文件到OSS?

    • 模型文件上传:根据模型使用节点的源项目库使用说明,将模型上传至models下的对应子目录。例如:

      • Checkpoint加载器:模型上传至models/checkpoints

      • 风格模型加载器:模型上传至models/styles

    • 节点文件上传:推荐您将自定义节点上传至挂载存储的custom_nodes目录。

  3. 加载新内容。

    • 模型:单击PaiCustom>加载新模型,如仍然找不到模型,单击重启进程,重启成功后,刷新浏览器页面。

    • 节点:直接单击重启进程。重启成功后,刷新浏览器页面。

导出工作流

在WebUI中调试好工作流后,将工作流保存为一个JSON文件,用于后续的API调用。

image

API调用

重要

标准版服务仅支持同步调用,并且提供在线调试。

API版服务仅支持异步调用,且仅支持api_prompt路径。

ComfyUI的API请求体取决于工作流配置。请先在WebUI页面设置并导出工作流的JSON文件

  • 同步调用:请求体需要将工作流JSON文件内容包装在"prompt"键值下面。

  • 异步调用:请求体就是工作流JSON文件内容。

因为上述Wan VACE Text to Video的工作流运行比较耗时,为方便测试提供以下工作流(运行一次需要约3分钟)。

单击查看测试工作流的请求体示例

同步调用

{
    "prompt": {
        "3": {
            "inputs": {
                "seed": 423988542100860,
                "steps": 40,
                "cfg": 7,
                "sampler_name": "dpmpp_sde_gpu",
                "scheduler": "karras",
                "denoise": 1,
                "model": [
                    "4",
                    0
                ],
                "positive": [
                    "6",
                    0
                ],
                "negative": [
                    "7",
                    0
                ],
                "latent_image": [
                    "5",
                    0
                ]
            },
            "class_type": "KSampler",
            "_meta": {
                "title": "K采样器"
            }
        },
        "4": {
            "inputs": {
                "ckpt_name": "LandscapeBING_v10.safetensors"
            },
            "class_type": "CheckpointLoaderSimple",
            "_meta": {
                "title": "Checkpoint加载器(简易)"
            }
        },
        "5": {
            "inputs": {
                "width": 720,
                "height": 1280,
                "batch_size": 1
            },
            "class_type": "EmptyLatentImage",
            "_meta": {
                "title": "空Latent"
            }
        },
        "6": {
            "inputs": {
                "text": "Rocket takes off from the ground, fire,sky, airplane",
                "speak_and_recognation": {
                    "__value__": [
                        false,
                        true
                    ]
                },
                "clip": [
                    "4",
                    1
                ]
            },
            "class_type": "CLIPTextEncode",
            "_meta": {
                "title": "CLIP文本编码器"
            }
        },
        "7": {
            "inputs": {
                "text": "",
                "speak_and_recognation": {
                    "__value__": [
                        false,
                        true
                    ]
                },
                "clip": [
                    "4",
                    1
                ]
            },
            "class_type": "CLIPTextEncode",
            "_meta": {
                "title": "CLIP文本编码器"
            }
        },
        "8": {
            "inputs": {
                "samples": [
                    "3",
                    0
                ],
                "vae": [
                    "4",
                    2
                ]
            },
            "class_type": "VAEDecode",
            "_meta": {
                "title": "VAE解码"
            }
        },
        "9": {
            "inputs": {
                "filename_prefix": "ComfyUI",
                "images": [
                    "8",
                    0
                ]
            },
            "class_type": "SaveImage",
            "_meta": {
                "title": "保存图像"
            }
        },
        "13": {
            "inputs": {
                "seed": 788620942678235,
                "steps": 40,
                "cfg": 2.5,
                "sampler_name": "euler_ancestral",
                "scheduler": "karras",
                "denoise": 1,
                "model": [
                    "17",
                    0
                ],
                "positive": [
                    "16",
                    0
                ],
                "negative": [
                    "16",
                    1
                ],
                "latent_image": [
                    "16",
                    2
                ]
            },
            "class_type": "KSampler",
            "_meta": {
                "title": "K采样器"
            }
        },
        "14": {
            "inputs": {
                "samples": [
                    "13",
                    0
                ],
                "vae": [
                    "18",
                    2
                ]
            },
            "class_type": "VAEDecode",
            "_meta": {
                "title": "VAE解码"
            }
        },
        "15": {
            "inputs": {
                "filename_prefix": "ComfyUI",
                "fps": 10.000000000000002,
                "lossless": false,
                "quality": 85,
                "method": "default",
                "images": [
                    "14",
                    0
                ]
            },
            "class_type": "SaveAnimatedWEBP",
            "_meta": {
                "title": "保存WEBP"
            }
        },
        "16": {
            "inputs": {
                "width": 512,
                "height": 768,
                "video_frames": 35,
                "motion_bucket_id": 140,
                "fps": 15,
                "augmentation_level": 0.15000000000000002,
                "clip_vision": [
                    "18",
                    1
                ],
                "init_image": [
                    "8",
                    0
                ],
                "vae": [
                    "18",
                    2
                ]
            },
            "class_type": "SVD_img2vid_Conditioning",
            "_meta": {
                "title": "SVD_图像到视频_条件"
            }
        },
        "17": {
            "inputs": {
                "min_cfg": 1,
                "model": [
                    "18",
                    0
                ]
            },
            "class_type": "VideoLinearCFGGuidance",
            "_meta": {
                "title": "线性CFG引导"
            }
        },
        "18": {
            "inputs": {
                "ckpt_name": "svd_xt.safetensors"
            },
            "class_type": "ImageOnlyCheckpointLoader",
            "_meta": {
                "title": "Checkpoint加载器(仅图像)"
            }
        },
        "19": {
            "inputs": {
                "frame_rate": 10,
                "loop_count": 0,
                "filename_prefix": "comfyUI",
                "format": "video/h264-mp4",
                "pix_fmt": "yuv420p",
                "crf": 20,
                "save_metadata": true,
                "trim_to_audio": false,
                "pingpong": false,
                "save_output": true,
                "images": [
                    "14",
                    0
                ]
            },
            "class_type": "VHS_VideoCombine",
            "_meta": {
                "title": "合并为视频"
            }
        }
    }
}

异步调用

{
    "3": {
        "inputs": {
            "seed": 423988542100860,
            "steps": 40,
            "cfg": 7,
            "sampler_name": "dpmpp_sde_gpu",
            "scheduler": "karras",
            "denoise": 1,
            "model": [
                "4",
                0
            ],
            "positive": [
                "6",
                0
            ],
            "negative": [
                "7",
                0
            ],
            "latent_image": [
                "5",
                0
            ]
        },
        "class_type": "KSampler",
        "_meta": {
            "title": "K采样器"
        }
    },
    "4": {
        "inputs": {
            "ckpt_name": "LandscapeBING_v10.safetensors"
        },
        "class_type": "CheckpointLoaderSimple",
        "_meta": {
            "title": "Checkpoint加载器(简易)"
        }
    },
    "5": {
        "inputs": {
            "width": 720,
            "height": 1280,
            "batch_size": 1
        },
        "class_type": "EmptyLatentImage",
        "_meta": {
            "title": "空Latent"
        }
    },
    "6": {
        "inputs": {
            "text": "Rocket takes off from the ground, fire,sky, airplane",
            "speak_and_recognation": {
                "__value__": [
                    false,
                    true
                ]
            },
            "clip": [
                "4",
                1
            ]
        },
        "class_type": "CLIPTextEncode",
        "_meta": {
            "title": "CLIP文本编码器"
        }
    },
    "7": {
        "inputs": {
            "text": "",
            "speak_and_recognation": {
                "__value__": [
                    false,
                    true
                ]
            },
            "clip": [
                "4",
                1
            ]
        },
        "class_type": "CLIPTextEncode",
        "_meta": {
            "title": "CLIP文本编码器"
        }
    },
    "8": {
        "inputs": {
            "samples": [
                "3",
                0
            ],
            "vae": [
                "4",
                2
            ]
        },
        "class_type": "VAEDecode",
        "_meta": {
            "title": "VAE解码"
        }
    },
    "9": {
        "inputs": {
            "filename_prefix": "ComfyUI",
            "images": [
                "8",
                0
            ]
        },
        "class_type": "SaveImage",
        "_meta": {
            "title": "保存图像"
        }
    },
    "13": {
        "inputs": {
            "seed": 788620942678235,
            "steps": 40,
            "cfg": 2.5,
            "sampler_name": "euler_ancestral",
            "scheduler": "karras",
            "denoise": 1,
            "model": [
                "17",
                0
            ],
            "positive": [
                "16",
                0
            ],
            "negative": [
                "16",
                1
            ],
            "latent_image": [
                "16",
                2
            ]
        },
        "class_type": "KSampler",
        "_meta": {
            "title": "K采样器"
        }
    },
    "14": {
        "inputs": {
            "samples": [
                "13",
                0
            ],
            "vae": [
                "18",
                2
            ]
        },
        "class_type": "VAEDecode",
        "_meta": {
            "title": "VAE解码"
        }
    },
    "15": {
        "inputs": {
            "filename_prefix": "ComfyUI",
            "fps": 10.000000000000002,
            "lossless": false,
            "quality": 85,
            "method": "default",
            "images": [
                "14",
                0
            ]
        },
        "class_type": "SaveAnimatedWEBP",
        "_meta": {
            "title": "保存WEBP"
        }
    },
    "16": {
        "inputs": {
            "width": 512,
            "height": 768,
            "video_frames": 35,
            "motion_bucket_id": 140,
            "fps": 15,
            "augmentation_level": 0.15000000000000002,
            "clip_vision": [
                "18",
                1
            ],
            "init_image": [
                "8",
                0
            ],
            "vae": [
                "18",
                2
            ]
        },
        "class_type": "SVD_img2vid_Conditioning",
        "_meta": {
            "title": "SVD_图像到视频_条件"
        }
    },
    "17": {
        "inputs": {
            "min_cfg": 1,
            "model": [
                "18",
                0
            ]
        },
        "class_type": "VideoLinearCFGGuidance",
        "_meta": {
            "title": "线性CFG引导"
        }
    },
    "18": {
        "inputs": {
            "ckpt_name": "svd_xt.safetensors"
        },
        "class_type": "ImageOnlyCheckpointLoader",
        "_meta": {
            "title": "Checkpoint加载器(仅图像)"
        }
    },
    "19": {
        "inputs": {
            "frame_rate": 10,
            "loop_count": 0,
            "filename_prefix": "comfyUI",
            "format": "video/h264-mp4",
            "pix_fmt": "yuv420p",
            "crf": 20,
            "save_metadata": true,
            "trim_to_audio": false,
            "pingpong": false,
            "save_output": true,
            "images": [
                "14",
                0
            ]
        },
        "class_type": "VHS_VideoCombine",
        "_meta": {
            "title": "合并为视频"
        }
    }
}

在线调试

模型在线服务 (EAS)页面,单击目标服务操作列下的在线调试,进入在线调试页面。

  1. 发送POST请求,获取Prompt ID。

    1. 在调试页面的在线调试请求参数区域的Body处填写已准备好的请求体。并在请求URL文本编辑框中添加/promptimage

    2. 单击发送请求,即可在调试信息区域查看返回结果,示例如下。image

  2. 发送GET请求,根据Prompt ID获取推理结果。

    1. 在线调试请求参数区域中,将请求方法修改为GET,并在文本框中配置/history/<prompt id>,示例如下。image

      其中<prompt id>需要替换为步骤1获取的Prompt ID。

    2. 单击发送请求,即可获取推理结果。

      您可以在挂载存储的output目录中,查看生成的推理结果。

同步调用

  1. 查看调用信息。

    1. 推理服务页签,单击目标服务名称进入概览页面,在基本信息区域单击查看调用信息

    2. 调用信息面板,可获取访问地址和Token。根据您的实际情况选择公网或VPC地址,后续使用<EAS_ENDPOINT>和<EAS_TOKEN>指代这两个值。

      image

  2. 发送请求获取Prompt ID。

    • HTTP请求方式:POST。

    • 请求路径(URL)<EAS_ENDPOINT>/prompt。其中<EAS_ENDPOINT>如地址末尾有/,请删除。最终URL如http://comfyui****.175805416243****.cn-beijing.pai-eas.aliyuncs.com/prompt

    • 请求头部(Headers)

      头部

      描述

      Authorization

      <EAS_TOKEN>

      授权密钥。

      Content-Type

      application/json

      指定请求体格式。

    代码示例:

    cURL

    curl --location --request POST '<EAS_ENDPOINT>/prompt' \
    --header 'Authorization: <EAS_TOKEN>' \
    --header 'Content-Type: application/json' \
    --data-raw '{
        "prompt":
        ...省略
    }'

    其中,--data-raw 为请求体。

    Python

    代码示例如下:

    import requests
    import json
    
    # <EAS_ENDPOINT> 和 <EAS_TOKEN> 替换为步骤1获取的地址和token。
    service_url = "<EAS_ENDPOINT>"
    token = "<EAS_TOKEN>"
    
    if service_url[-1] == "/":
        service_url = service_url[:-1]
    
    # 请求体,将payload中的prompt的值配置为工作流对应的JSON文件内容。
    payload = """{
        "prompt":
        ...省略
    }"""
    payload = json.loads(payload)
    
    session = requests.session()
    session.headers.update({"Authorization": token})
      
    response = session.post(url=f'{service_url}/prompt', json=payload)
    if response.status_code != 200:
        raise Exception(response.content)
    
    data = response.json()
    print(data)

    返回结果示例如下:

    {
        "prompt_id": "021ebc5b-e245-4e37-8bd3-00f7b949****",
        "number": 5,
        "node_errors": {}
    }

    从返回结果中获取Prompt ID。

  3. 获取推理结果。

    • HTTP请求方式GET

    • 请求URL<EAS_ENDPOINT>/history/<prompt_id>,其中<prompt_id>替换为步骤1中获取的prompt_id。

    • 请求头部

      头部

      描述

      Authorization

      <EAS_TOKEN>

      授权密钥,步骤1中获取。

    代码示例:

    cURL

    curl --location --request GET '<EAS_ENDPOINT>/history/<prompt_id>' \
         --header 'Authorization: <EAS_TOKEN>'

    Python

    import requests
    
    # <EAS_ENDPOINT> 和 <EAS_TOKEN> 替换为步骤1获取的地址和token。
    # <prompt_id>替换为步骤2中获取的prompt_id
    service_url = "<EAS_ENDPOINT>"
    token = "<EAS_TOKEN>"
    prompt_id = "<prompt_id>"
    
    if service_url[-1] == "/":
        service_url = service_url[:-1]
    
    session = requests.session()
    session.headers.update({"Authorization": token})
    
    response = session.get(url=f'{service_url}/history/{prompt_id}')
    
    if response.status_code != 200:
        raise Exception(response.content)
    
    data = response.json()
    print(data)

    返回结果示例如下:

    单击查看返回结果示例

    {
        "130bcd6b-5bb5-496c-9c8c-3a1359a0****": {
            "prompt": ...省略,
            "outputs": {
                "9": {
                    "images": [
                        {
                            "filename": "ComfyUI_1712645398_18dba34d-df87-4735-a577-c63d5506a6a1_.png",
                            "subfolder": "",
                            "type": "output"
                        }
                    ]
                },
                "15": {
                    "images": [
                        {
                            "filename": "ComfyUI_1712645867_.webp",
                            "subfolder": "",
                            "type": "output"
                        }
                    ],
                    "animated": [
                        true
                    ]
                },
                "19": {
                    "gifs": [
                        {
                            "filename": "comfyUI_00002.mp4",
                            "subfolder": "",
                            "type": "output",
                            "format": "video/h264-mp4"
                        }
                    ]
                }
            },
            "status": {
                "status_str": "success",
                "completed": true,
                "messages": ...省略,
            }
        }
    }

    在本示例返回的outputs中提供了prompt生成的图像、webp文件和mp4视频,您可以在挂载存储的output目录中,根据文件名称来查找这些文件。

异步调用

重要

异步调用仅支持api_prompt路径,其task_id参数是标识请求和结果的关键标志,请给每个请求分配一个唯一的值,以对应后面的队列结果。请求路径如下:

{service_url}/api_prompt?task_id={需分配唯一值}

  1. 查看调用信息。

    推理服务页签,单击目标服务名称进入概览页面,在基本信息区域单击查看调用信息。在调用信息对话框的异步调用页签,查看服务访问地址和Token。

    image

    下文使用<EAS_ENDPOINT>指代公网输入调用地址(如果调用端与EAS处于同一VPC,可使用VPC输入调用地址),<EAS_TOKEN>指代Token

  2. 发送请求。

    代码示例如下:

    import requests
    import json
    
    service_url = "<EAS_ENDPOINT>"
    token = "<EAS_TOKEN>"
    
    if service_url[-1] == "/":
        service_url = service_url[:-1]
    
    session = requests.session()
    session.headers.update({"Authorization":token})
    
    # 请求体-工作流JSON,无需"prompt"包装。
    # 使用 """ """ 将其定义为多行字符串,否则需要将工作流JSON中布尔值(true和false)的首字母改为大写。
    payload = """{
        '3': 
        ...省略
      }
      """
    payload = json.loads(payload)
    
    for i in range(5):
      # task_id是标识请求和结果的关键标志,请给每个请求分配一个唯一的值,以对应后面的队列结果
      response = session.post(url=f'{service_url}/api_prompt?task_id=txt2img_{i}', json=payload)
      if response.status_code != 200:
        exit(f"send request error:{response.content}")
      else:
        print(f"send {i} success, index is {response.content}")

  3. 订阅结果。

    1. 安装eas_prediction SDK。

      pip install eas_prediction  --user
    2. 执行以下代码,获取返回结果。

      from eas_prediction import QueueClient
      from urllib.parse import urlparse, urlunparse
      
      service_url     = "<EAS_ENDPOINT>"
      token           = "<EAS_TOKEN>"
      
      # 解析服务URL
      def parse_service_url(service_url):
          parsed = urlparse(service_url)
          service_domain = f"{parsed.scheme}://{parsed.netloc}"
          path_parts = [p for p in parsed.path.strip('/').split('/') if p]
          service_name = path_parts[-1]
          return service_domain, service_name
      
      service_domain, service_name = parse_service_url(service_url)
      print(f"service_domain: {service_domain}, service_name: {service_name}.")
         
      # 创建结果队列客户端 
      sink_queue = QueueClient(service_domain, f'{service_name}/sink')
      sink_queue.set_token(token)
      sink_queue.init()
      
      # 监听结果
      watcher = sink_queue.watch(0, 5, auto_commit=False)
      for x in watcher.run():
          if 'task_id' in x.tags:
              print('index {} task_id is {}'.format(x.index, x.tags['task_id']))
          print(f'index {x.index} data is {x.data}')
          # 提交确认接收
          sink_queue.commit(x.index)

      返回结果示例如下:

      index 42 task_id is txt2img_0
      index 42 data is b'[{"type": "executed", "data": {"node": "9", "output": {"images": [{"filename": "ComfyUI_1712647318_8e7f3c93-d2a8-4377-92d5-8eb552adc172_.png", "subfolder": "", "type": "output"}]}, "prompt_id": "c3c983b6-f92b-4dd5-b4dc-442db4d1736f"}}, {"type": "executed", "data": {"node": "15", "output": {"images": [{"filename": "ComfyUI_1712647895_.webp", "subfolder": "", "type": "output"}], "animated": [true]}, "prompt_id": "c3c983b6-f92b-4dd5-b4dc-442db4d1736f"}}, {"type": "executed", "data": {"node": "19", "output": {"gifs": [{"filename": "comfyUI_00001.mp4", "subfolder": "", "type": "output", "format": "video/h264-mp4"}]}, "prompt_id": "c3c983b6-f92b-4dd5-b4dc-442db4d1736f"}}, {"9": {"images": [{"filename": "ComfyUI_1712647318_8e7f3c93-d2a8-4377-92d5-8eb552adc172_.png", "subfolder": "", "type": "output"}]}, "15": {"images": [{"filename": "ComfyUI_1712647895_.webp", "subfolder": "", "type": "output"}], "animated": [true]}, "19": {"gifs": [{"filename": "comfyUI_00001.mp4", "subfolder": "", "type": "output", "format": "video/h264-mp4"}]}}]'
      index 43 task_id is txt2img_1
      index 43 data is b'[{"9": {"images": [{"filename": "ComfyUI_1712647318_8e7f3c93-d2a8-4377-92d5-8eb552adc172_.png", "subfolder": "", "type": "output"}]}, "15": {"images": [{"filename": "ComfyUI_1712647895_.webp", "subfolder": "", "type": "output"}], "animated": [true]}, "19": {"gifs": [{"filename": "comfyUI_00001.mp4", "subfolder": "", "type": "output", "format": "video/h264-mp4"}]}}]'
      index 44 task_id is txt2img_2
      index 44 data is b'[{"9": {"images": [{"filename": "ComfyUI_1712647318_8e7f3c93-d2a8-4377-92d5-8eb552adc172_.png", "subfolder": "", "type": "output"}]}, "15": {"images": [{"filename": "ComfyUI_1712647895_.webp", "subfolder": "", "type": "output"}], "animated": [true]}, "19": {"gifs": [{"filename": "comfyUI_00001.mp4", "subfolder": "", "type": "output", "format": "video/h264-mp4"}]}}]'
      index 45 task_id is txt2img_3
      index 45 data is b'[{"9": {"images": [{"filename": "ComfyUI_1712647318_8e7f3c93-d2a8-4377-92d5-8eb552adc172_.png", "subfolder": "", "type": "output"}]}, "15": {"images": [{"filename": "ComfyUI_1712647895_.webp", "subfolder": "", "type": "output"}], "animated": [true]}, "19": {"gifs": [{"filename": "comfyUI_00001.mp4", "subfolder": "", "type": "output", "format": "video/h264-mp4"}]}}]'
      index 46 task_id is txt2img_4
      index 46 data is b'[{"9": {"images": [{"filename": "ComfyUI_1712647318_8e7f3c93-d2a8-4377-92d5-8eb552adc172_.png", "subfolder": "", "type": "output"}]}, "15": {"images": [{"filename": "ComfyUI_1712647895_.webp", "subfolder": "", "type": "output"}], "animated": [true]}, "19": {"gifs": [{"filename": "comfyUI_00001.mp4", "subfolder": "", "type": "output", "format": "video/h264-mp4"}]}}]'

      您可以在挂载存储的output目录中,查看推理结果文件。

说明

生成的图片或视频存储在挂载的output目录中,API调用的结果返回的是文件名和子目录名。对于OSS,需自行拼接完整的文件路径进行下载,请参见使用阿里云SDK下载OSS文件

常见问题

模型与节点

Q:WebUI显示“缺少模型”错误

问题描述:报错如下:

image.png

解决方案:此报错可以忽略。PAI部署的ComfyUI此检查无效,请以运行时的报错为准。

建议勾选不再显示此消息,或者通过设置关闭模型校验。

image

Q:上传了新模型但找不到

解决方案:

  1. 确认使用的不是Serverless版(Serverless版不支持上传自己的模型,请使用标准版或集群版)。

  2. 单击PaiCustom,选择加载新模型image

  3. 如不可见,单击重启进程image

Q:模型加载器显示undefined

首先确认模型的目录位置是否正确,这依赖于模型加载器的要求。

如在部署后上传模型,请重启服务。

Q:找不到节点

Q:ComfyUI 管理器下载模型或安装节点失败

在EAS部署的ComfyUI中,不建议使用ComfyUI管理器。因为直接下载外网模型或安装插件(需要从GitHub等平台拉取代码),有可能存在网络连接失败的问题。

建议您将模型或节点文件上传到服务挂载的存储上,详情请参见使用第三方模型和安装节点(ComfyUI插件)

Q:如何查看当前可用的模型文件和节点(ComfyUI插件)列表

  • 模型文件:在相应模型加载节点查看。例如在Checkpoint加载器的下拉列表中查看当前可用的模型文件。

  • 节点:右键单击WebUI页面,在快捷菜单中单击添加节点,查看所有已安装的ComfyUI插件。

运行异常

Q:页面卡死或加载页面时间过长

  • 刷新页面,清理浏览器缓存或使用无痕/隐私模式访问。

  • 如挂载了存储,删除input/output/temp/文件夹中的文件。

  • 尝试重启服务。

Q:工作流跑一半,进程重启了

如果实例日志里面有run.sh: line 54: 531285 Killed python -u main_run.py "$@",那就是内存oom了,内存oom之后,进程会自动重启。

Q:RuntimeError: CUDA error: out of memory

显存超了,如果是图像模型就降低图像的分辨率或者batch size;视频模型降低一下帧数/分辨率

Q:API调用报错:url not found 或404 page not found?

  1. 确认使用的不是Serverless版本(Serverless类型不支持API调用)。

  2. 检查API端点URL是否完整。同步调用需拼接/prompt路径。

Q:服务一直显示等待中或者ComfyUI无法出图

通常是资源规格不够的原因。请检查服务镜像和资源规格配置是否正确,资源规格推荐使用GU30、A10或T4卡型,其中ml.gu7i.c16m60.1-gu30性价比高。

Q:服务部署一段时间后为什么会自动停止?

Serverless版的模型服务如果长时间没有接收到请求或计算任务,系统可能会自动释放相关资源以降低成本。

其他

Q:xFormer的加速效果

xFormers是基于Transformer的开源加速工具,能够有效缩短图片和视频生成时长,节省显存使用。

ComfyUI镜像部署默认已开启xFormers加速。加速效果跟工作流的大小相关,针对GPU调用的内容尤其是使用NVIDIA显卡的提升比较明显。

Q:EAS与函数计算在部署ComfyUI Serverless版的主要区别

  • EAS:适合有状态、长周期运行的服务,支持一键部署模型为在线推理服务或AI-Web应用,具备弹性扩缩容、蓝绿部署等功能。例如,您可以通过EAS的场景化模型部署或自定义模型部署方式来部署ComfyUI。

  • 函数计算:基于Serverless架构,提供按需付费、弹性伸缩等优势,适合需要高质量图像生成功能的场景,可自定义ComfyUI模型及安装插件。例如,您可以在函数计算3.0控制台创建应用、选择ComfyUI模板、设置配置项并创建应用。

参考信息

安装Python包

通过运行命令安装whl包

  1. 确保已为服务挂载存储。假设OSS路径Urioss://examplebucket/comfyui/,挂载路径为/mnt/data/image

  2. .whl文件上传到oss://examplebucket/comfyui/models/whl目录下,如无whl的文件夹,请先创建。

  3. 更新服务配置中的运行命令:在运行命令前增加pip install /mnt/data/models/whl/xxx.whl。其中/mnt/data为OSS的挂载路径xxx.whl表示whl包的名字。

    image

  4. 重启服务。

通过三方库配置安装

  1. 在服务详情页右上角单击更新

    image

  2. 如果是通过场景化部署,请切换为自定义部署。

    image

  3. 环境信息区域的三方库配置,设置依赖包。image

  4. 单击页面下方更新按钮,完成服务更新即可。

更新镜像版本

说明

Serverless版本不支持此功能。

若服务挂载OSS或者NAS存储空间,自定义模型保留在OSS或者NAS存储空间,更新镜像版本不会影响已安装的自定义模型。

  1. 在服务详情页右上角单击更新image

  2. 如通过场景化方法部署,请切换为自定义部署。

  3. 服务配置区域,编辑JSON配置并更新containersimage字段,如图将1.9改成需要的版本。image

  4. 单击直接更新

延长无登录态的URL时间

可通过API获取指定有效时长的免登录Web访问链接。

  1. 进入DescribeServiceSignedUrl API页面。

  2. 选择服务地址。image

  3. 设置参数:

    • ClusterId服务所在区域、ServiceName 服务名字:填写服务所在区域与服务名字。如下从EAS服务概览页面获取。

      image

    • Type 页面类型:下拉选择 webview。

    • Expire 过期时间:填写整数,单位为秒。目前最长只能是43200秒(12小时),建议按需填写。

    • Internal 是否为VPC链接:false(公网访问)或true(VPC访问)。

  4. 单击发起调用,返回结果中SignedUrl为服务免登录Web访问链接。

切换WebUI页面的默认语言

  1. 在WebUI页面,单击左下角的设置按钮。

  2. 分别在以下两个位置设置完成后,刷新页面并重新加载即可。

    • Comfy > 区域设置image

    • 语言 > 区域设置:image

附录

集群版服务原理介绍

实现原理图如下:

image

集群版服务主要针对多用户场景,通过引入一个 Proxy 代理层,实现了客户端和后端推理实例解耦。

  • 每个用户有独立的后端环境和工作目录,但共享后端的GPU推理实例池。

  • 当用户发起推理请求时,Proxy 代理会从池中寻找一个空闲的实例来处理该请求。

这种分时复用机制在保证用户环境隔离的同时,有效提升了 GPU 资源的利用率,降低了多用户场景下的推理成本。