全部產品
Search
文件中心

Platform For AI:AI視頻產生-ComfyUI部署

更新時間:Nov 29, 2025

ComfyUI提供基於Stable Diffusion的節點式操作介面,可方便快速地構建複雜的AIGC流程,完成短視頻內容產生、動畫製作等任務。本文介紹如何在EAS中部署和使用ComfyUI。

部署版本說明

適用情境

調用方式

標準版

  • 單使用者WebUI或API調用,適合入門和開發測試。

  • 建議部署單一實例,並發能力有限。

  • WebUI

  • 線上調試

  • API調用(同步)

API版

  • 高並發,適合生產環境。

  • 部署相對複雜,需額外CPU資源以建立佇列服務執行個體。

API調用(非同步)

叢集版WebUI

  • 多使用者同時使用WebUI,環境隔離,適合團隊協作或教學。

  • 資源消耗較高,原理參見叢集版服務原理介紹

WebUI

Serverless版

  • 部署免費,按出圖時間長度計費,成本極低,適合波動性需求。

  • 無法使用自訂模型/外掛程式。僅華東2(上海)、華東1(杭州)地區支援部署。

WebUI

說明

此處API調用的同步與非同步取決於是否使用EAS的佇列服務:

  • 同步調用:直接請求推理執行個體,不使用EAS的佇列服務;

  • 非同步呼叫:使用EAS的佇列服務,向輸入隊列發送請求,以訂閱的方式獲得結果推送。

由於ComfyUI本身具有非同步隊列系統,即使發起同步調用,實質上也是非同步進行的。使用者發送請求後,系統會返回一個Prompt ID,然後需要使用Prompt ID輪詢以擷取推理結果。

計費說明

  • Serverless版:部署免費,按實際推理時間長度計費。

  • 其他版本:按照部署資源和運行時間長度計費(部署成功後即使不使用也計費)。

更多計費詳情請參見模型線上服務(EAS)計費說明

部署服務

Serverless版只能使用情境化模型部署,標準版、叢集版、API版可使用情境化模型部署(操作簡單)或者自訂模型部署(支援更多功能)。

重要
  • ComfyUI僅支援單卡(單機單卡或多機單卡),不支援多卡並行作業。因為一個EAS執行個體只有一個ComfyUI進程,部署時選擇多GPU規格的執行個體(如2*A10)也只會使用一張GPU卡,並不會讓單個生圖任務變快。

  • 負載平衡:需部署API版,通過非同步隊列實現。

方式一:情境化模型部署(推薦)

  1. 登入PAI控制台,在頁面上方選擇目標地區,並在右側選擇目標工作空間,然後單擊進入EAS

  2. 模型線上服務(EAS)頁面,單擊部署服務,在情境化模型部署地區,單擊AI視頻產生-ComfyUI部署

  3. AI視頻產生-ComfyUI部署頁面,配置以下關鍵參數。

    • 版本選擇:根據部署版本說明選擇。

    • 模型配置:如需使用自己的模型、安裝自訂節點或通過API調用,必須進行模型配置。以Object Storage Service為例,選擇Bucket和目錄,部署成功後系統會自動在其中建立ComfyUI所需目錄。請確保建立的儲存空間與EAS服務位於同一地區。

    • 資源配置選擇:推薦使用GU30、A10或T4卡型。系統預設選擇GPU > ml.gu7i.c16m60.1-gu30,性價比高。

  4. 單擊部署。等待約5分鐘,當服務狀態變為運行中,表示部署成功。

方式二:自訂模型部署

  1. 登入PAI控制台,在頁面上方選擇目標地區,並在右側選擇目標工作空間,然後單擊進入EAS

  2. 推理服務頁簽,單擊部署服務,然後在自訂模型部署地區,單擊自訂部署

  3. 自訂部署頁面,配置以下關鍵參數。更多配置,請參見自訂部署

    • 部署方式:選擇鏡像部署,並選中開啟Web應用複選框。

    • 鏡像配置:在官方鏡像列表中選擇comfyui>comfyui:1.9。其中:x.x:表示標準版,x.x-api:表示API版,x.x-cluster:表示叢集版。

      說明
      • 由於版本迭代迅速,部署時鏡像版本選擇最高版本即可。

      • 更多關於每個版本的使用情境說明,請參見部署版本說明

    • 儲存掛載:如需使用自己的模型、安裝自訂節點或通過API調用,必須進行儲存掛載。以Object Storage Service為例,選擇Bucket和目錄,部署成功後系統會自動在其中建立ComfyUI所需目錄。請確保建立的儲存空間與EAS服務位於同一地區。

      • Uri:單擊image選擇已建立的OSS儲存目錄。例如oss://bucket-test/data-oss/

      • 掛載路徑:配置為/code/data-oss,表示將您配置的OSS檔案目錄掛載到鏡像的/code/data-oss路徑下。

    • 運行命令

      • 配置鏡像版本後,系統自動設定運行命令python main.py --listen --port 8000,連接埠號碼為:8000。

      • 若進行了儲存掛載,需要在運行命令中增加--data-dir掛載目錄,其中掛載目錄需要與掛載路徑一致。例如python main.py --listen --port 8000 --data-dir /code/data-oss

    • 資源類型:選擇公用資源

    • 部署資源:資源規格必須選擇GPU類型,推薦使用ml.gu7i.c16m60.1-gu30(性價比最高)。如庫存不足可選擇ecs.gn6i-c16g1.4xlarge

  4. 單擊部署。服務部署時間約為5分鐘,當服務狀態運行中時,表明服務已成功部署。

調用服務

WebUI使用

重要

標準版、叢集版和Serverless版支援通過WebUI使用。

單擊目標服務名稱進入概覽頁面,在右上方單擊Web應用

如頁面長時間無法開啟,請參見重新整理頁面時間過長或頁面卡死

1. 使用模板工作流程

WebUI頁面支援自訂工作流程配置,同時預置多種模板,可在工作流程 > 瀏覽模板頁面中選擇。本文以視頻工作流程範本Wan VACE Text to Video為例。

說明

對於Serverless版本,模板中無該工作流程,可選擇其他模板使用。也可以通過工作流程 > 開啟,載入本地檔案系統中的工作流程。

image

工作流程載入成功後,如遇到報錯缺少模型,可忽視(建議勾選不再顯示此訊息)。

由於路徑變更,直接運行工作流程可能會出現以下報錯。

image.png

請先在Load models here地區重新選擇模型wan2.1_vace_14B_fp16.safetensorsWan21_CausVid_14B_T2V_lora_rank32.safetensors

image

工作流程運行成功後,會在Save Video地區,展示產生的視頻。image

2. 使用第三方模型和安裝自訂節點(ComfyUI外掛程式)

  1. 確認部署的ComfyUI版本非Serverless版本。Serverless版本只能使用內建的模型和節點。

  2. 確認服務已配置儲存掛載。如使用自訂部署,需在運行命令中增加參數--data-dir掛載目錄,詳情見方式二:自訂模型部署

    服務部署成功後,系統會自動在已掛載的OSS或NAS儲存空間中建立以下目錄結構。

    image

    其中:

    • custom_nodes:該目錄用來儲存節點檔案。

    • models:該目錄用來存放模型檔案。

  3. 上傳模型或節點檔案。以OSS為例,可控制台上傳檔案到OSS。對於大檔案,請參見如何上傳大檔案到OSS

    • 模型檔案上傳:根據模型使用節點的源專案庫使用說明,將模型上傳至models下的隊員子目錄。例如:

      • 對於Checkpoint載入器,模型應上傳至models/checkpoints

      • 對於風格模型載入器,模型應上傳至models/styles

    • 節點檔案上傳:推薦您將自訂節點上傳至掛載儲存的custom_nodes目錄。

  4. 載入新模型或重啟進程。

    在掛載的儲存空間上傳模型之後,單擊PaiCustom > 載入新模型,如仍然找不到模型,單擊重啟進程,重啟成功後,重新整理瀏覽器頁面。

    上傳節點檔案之後,直接單擊重啟進程。重啟成功後,重新整理瀏覽器頁面。

3. 匯出工作流程

在工作流程調試完成之後,單擊工作流程 > 匯出(API),可以將工作流程儲存為一個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. 發送POST請求,擷取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,io,base64
    from PIL import Image, PngImagePlugin
    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檔案內容。使用 """ """ 將其定義為多行字串,否則需要將工作流程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>"
      
      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檔案

常見問題

模型與節點

1. WebUI報錯:缺少模型

問題描述:報錯如下:

image.png

解決方案:PAI部署的ComfyUI此檢查無效,請以運行時的報錯為準。建議勾選不再顯示此訊息,或者通過設定關閉模型校正。

image

2. 上傳了新模型,但是找不到

首先檢查是否使用的是Serverless版(Serverless版不支援上傳自己的模型,請使用標準版或叢集版),版本沒問題,請按照以下步驟處理:

  1. 單擊頁面PaiCustom,選擇載入新模型。image

  2. 仍然不行,單擊重啟進程。image

3. 模型載入器顯示undefined

首先確認模型的目錄位置是否正確,這依賴於模型載入器的要求。

如果是服務啟動之後更新的模型檔案,請重啟服務。

4. 找不到節點

5. ComfyUI 管理器下載模型或安裝節點失敗

在EAS部署的ComfyUI中,不建議使用ComfyUI管理器。因為直接下載外網模型或安裝外掛程式(需要從GitHub等平台拉取代碼),有可能存在網路連接失敗的問題。

建議您需將模型或節點檔案上傳到服務掛載的儲存上,詳情請參見使用第三方模型和安裝節點(ComfyUI外掛程式)

6. 如何查看當前可用的模型檔案和節點(ComfyUI外掛程式)列表

  • 模型檔案:在相應模型載入節點查看。例如在Checkpoint載入器的下拉式清單中查看當前可用的模型檔案。

  • 節點:按右鍵WebUI頁面,在捷徑功能表中單擊添加節點,查看所有已安裝的ComfyUI外掛程式。

鏡像與依賴

1. 如何安裝whl包

  1. 請確保進行了掛載配置。以如下掛載路徑為例。假設oss路徑Uri為 oss://examplebucket/comfyui。image

  2. 將whl包傳到掛載oss的oss://examplebucket/comfyui/models/whl下面,如無whl的檔案夾,請先建立一個。

  3. 樣本中oss掛載路徑為/mnt/data,則在運行命令的前面增加命令 pip install /mnt/data/models/whl/xxx.whl。其中/mnt/data表示你的oss掛載路徑xxx.whl表示你的whl包的名字。

    image

2. 如何更新鏡像版本(保留已安裝的自訂模型)

Serverless版本無法更新ComfyUI鏡像。對於其他版本,如果服務掛載OSS或者NAS儲存空間,自訂模型保留在OSS或者NAS儲存空間,您只需要直接更新服務配置中的官方鏡像,不會影響已安裝的自訂模型。具體步驟如下:

  1. 在服務詳情頁右上方單擊更新。

    image

  2. 如果是通過情境化部署,請切換為自訂部署。單擊右側的服務配置,編輯JSON檔案,更新containers的image欄位,如圖將pai-eas/comfyui:後面的1.9改成需要的版本。

    image

  3. 在編輯視窗單擊直接更新

3. 鏡像依賴庫缺失

通過三方庫配置安裝缺失的依賴。步驟如下:

  1. 在服務詳情頁右上方單擊更新。

    image

  2. 如果是通過情境化部署,請切換為自訂部署。單擊右側的環境資訊,在更多配置地區找到三方庫配置,按頁面要求格式將所需的依賴填寫到裡面。

    image

    image

  3. 單擊頁面下方更新按鈕,完成服務更新即可。

運行異常

1. 頁面卡死或重新整理頁面時間過長

  • 重新整理頁面,清理瀏覽器緩衝或使用無痕/隱私模式訪問。

  • 如進行了儲存掛載,清理掛載目錄裡面的input/output/temp檔案夾裡面的檔案。

  • 嘗試重啟服務。

2. 工作流程跑一半,進程重啟了

如果執行個體日誌裡面有run.sh: line 54: 531285 Killed python -u main_run.py "$@",那就是記憶體oom了,記憶體oom之後,進程會自動重啟。

3. RuntimeError: CUDA error: out of memory

顯存超了,如果是映像模型就降低映像的解析度或者batch size;視頻模型降低一下幀數/解析度

4. API調用報錯:url not found 或404 page not found?

  1. 請檢查服務部署的是否是Serverless版本,Serverless類型不支援API調用。

  2. 若服務版本正確,請驗證API端點URL是否完整。同步調用需拼接/prompt路徑。

5. 服務一直顯示等待中或者ComfyUI無法出圖

通常是資源規格不夠的原因。請檢查服務鏡像和資源規格配置是否正確,資源規格推薦使用GU30、A10或T4卡型,其中ml.gu7i.c16m60.1-gu30性價比高。

6. 服務部署一段時間後為什麼會自動停止?

Serverless版的模型服務如果長時間沒有接收到請求或計算任務,系統可能會自動釋放相關資源以降低成本。

其他

1. 如何延長無登入態的url時間

可通過API擷取指定有效時間長度的免登入Web訪問連結。

  1. 單擊 DescribeServiceSignedUrl API進入頁面。

  2. 選擇服務region。

image

  1. 填寫服務所在地區與服務名字。如下從EAS服務概覽頁面擷取。

    image

  2. 其他參數如下:

    • Type頁面類型:下拉選擇 webview。

    • Expire到期時間:如果想長期有效就寫 9007199254740991(目前最長只能是12小時)

      否則就寫一個整數,單位為秒。

    • Internal是否為VPC連結:如果不是vpc調用選擇false,否則選擇true。

  3. 單擊發起調用,返回結果中SignedUrl為服務免登入Web訪問連結。

2. xFormer對圖片產生速度的加速效果

xFormers是基於Transformer的開源加速工具,能夠有效縮短圖片和視頻產生時間長度,節省顯存使用。ComfyUI鏡像部署預設已開啟xFormers加速。加速效果跟工作流程的大小相關,針對GPU調用的內容尤其是使用NVIDIA顯卡的提升比較明顯。

3. EAS與Function Compute在部署ComfyUI Serverless版的主要區別

  • EAS:適合有狀態、長周期啟動並執行服務,支援一鍵部署模型為線上推理服務或AI-Web應用,具備彈性擴縮容、藍綠部署等功能。例如,您可以通過EAS的情境化模型部署或自訂模型部署方式來部署ComfyUI。

  • Function Compute:基於Serverless架構,提供按需付費、Auto Scaling等優勢,適合需要高品質映像產生功能的情境,可自訂ComfyUI模型及安裝外掛程式。例如,您可以在Function Compute3.0控制台建立應用、選擇ComfyUI模板、設定配置項並建立應用。

4. 如何切換WebUI頁面的預設語言?

  1. 在WebUI頁面,單擊左下角的設定按鈕image

  2. 設定對話方塊,分別在以下兩個位置進行語言設定。參數設定完成後,重新整理頁面並重新載入即可。

    • 在左側導覽列選擇Comfy,在右側地區設定中,設定目標語言。image

    • 在左側導覽列選擇語言,在右側地區設定中,設定目標語言。image

附錄

叢集版服務原理介紹

實現原理圖如下:

  • 叢集版服務主要針對多使用者情境,實現了用戶端和後端推理執行個體解耦,以便多使用者可以分時複用後端推理執行個體,提升執行個體的利用率和降低推理成本。

  • 每個使用者有獨立的後端環境和工作目錄,實現高效的GPU共用和檔案管理。

  • Proxy代理主要負責用戶端進程和推理執行個體的管理。使用者的所有操作都在自己的進程中進行處理,相關的檔案操作僅限於公用目錄和個人目錄,從而實現了使用者間工作目錄的有效隔離。當使用者需要使用推理執行個體來處理請求時,Proxy代理會從後端推理執行個體中找到可用的空閑執行個體來處理該推理請求。