AI 应用是针对大模型在线推理场景推出的一站式开发部署运维平台。本文为您介绍如何通过模板一键部署 AI 生图 Stable Diffusion ComfyUI。
SD ComfyUI 是一个专为 Stable Diffusion 设计的基于节点的图形用户界面(GUI),以拖拽组件的方式搭建模型流程,支持实时查看和调整模型参数,简化了模型构建和训练的复杂性,使用户无需深入理解底层算法就能进行模型操作。SD ComfyUI 的原理简单来说就是将整个图像生成过程分解为多个独立的节点,每个节点都有自己独立的功能,例如加载模型,文本提示,生成图片等。每个模块通过输入和输出的线连在一起变成一个完整的工作流。
下文主要介绍测试并验证通过的实践内容,为了获得符合预期的结果,同时符合使用限制,请按照本文方案(或在本文推荐的资源上)操作。如需替换方案,您可以联系对应的客户经理咨询。
容器服务 VKE
持续交付 CP
(可选)如需挂载模型文件,请完成以下准备工作,任选一种存储即可。
(可选)如需配置外部访问方式,请完成以下准备工作,任选一种方式即可。
登录 持续交付控制台。
在左侧导航栏选择 AI 应用。
在 AI 应用页面,单击 创建应用。
选择 基于框架创建 > AI 生图 - Stable Diffusion ComfyUI 单机版,单击 部署。
按要求填写应用的相关配置信息。配置完成后单击 确定,应用将开始创建并部署。
配置项 | 说明 |
---|---|
应用标识 | 根据界面提示填写应用标识。应用标识是应用的唯一标识,创建后不可更改。 |
应用显示名 | 自定义应用的显示名称。 |
描述 | 填写当前应用的备注信息,可以为空。 |
配置项 | 说明 |
---|---|
部署资源 | 选择前提条件中接入的 VKE 部署集群。 注意 如需以弹性容器实例方式部署,仅适用于网络模型为 VPC-CNI 的 VKE 集群,且集群中需提前安装 vci-virtual-kubelet 组件。 |
命名空间 | 选择或输入要部署的命名空间。输入的命名空间不存在时会自动创建。 |
环境标识 | 自定义当前环境的唯一标识。环境标识创建成功后不支持修改。 |
配置项 | 说明 |
---|---|
镜像选择 | 本模板使用预置镜像 SD ComfyUI。 |
模型 | 支持 官方模型 和 自持模型,请按需选择。
|
插件 | 可选配置。将其他依赖资源,例如 LoRA 插件、VAE 模型、CLIP 模型等资源挂载到服务中。
|
出图位置 | 如果您希望对输出物进行持久化存储,需要对出图目录进行挂载。
|
启动命令 | 设置应用的启动命令和监听的容器端口。默认为 |
勾选 以弹性容器实例方式部署
配置项 | 说明 |
---|---|
实例数 | 根据业务规模,设置应用的副本数。 |
弹性容器实例 | 保持勾选 以弹性容器实例方式部署。使用该方式部署应用,无需管理底层云服务器等基础设施,只需提供镜像即可运行容器,并为实际消耗的资源付费。计费详情,请参见 弹性容器计费说明。 |
日志采集 | 是否通过火山引擎日志服务 TLS 采集 VCI 容器日志,对日志进行持久化存储。
注意 开启日志采集后,请确保已在 VKE 集群的日志中心中完成相关日志采集规则的配置,保证日志可正常投递至您配置的项目和主题中。具体操作请参见 采集容器日志。 |
资源配置类型 | 根据业务需求选择要使用的实例规格。不同计算规格提供的虚拟化能力不同,详情请参见 VCI 实例规格介绍。
|
系统盘 | 设置系统盘容量。系统盘主要用于缓存镜像,容量必须大于镜像大小。 说明 如果您在 VCI 侧 申请 了手动指定系统盘大小的邀测功能,支持在此处自定义系统盘的容量。 |
不勾选 以弹性容器实例方式部署
配置项 | 说明 |
---|---|
实例数 | 根据业务规模,设置应用的副本数。 |
资源配置类型 |
|
配置 AI 应用的访问方式。详细内容可参见 访问设置。
验证应用是否部署成功
在应用的 基本信息 > 资源配置 页签,查看应用的部署进度。支持通过实例的 运行状态、实时日志 和 事件,查看应用详细的启动和运行信息。
访问 SD ComfyUI
应用部署成功后,可在 访问设置 区域复制应用的访问地址。通过该地址去访问 SD ComfyUI。
文生图
在 SD ComfyUI 操作界面,编排您的文生图工作流。输入正向和反向提示词,单击 Queue Prompt,即可生成描述提示词的图片。
在 ComfyUI 访问设置 区域复制应用的访问地址。
开启开发者模式。
将工作流保存为 API 格式。
创建 Python 脚本 comfyui_api.py
。
workflow
文件。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
执行 python3 comfyui_api.py
将生成图片并保存至本地文件。
预置镜像默认的 ComfyUI 常用路径请参见下表。
名称 | 路径 | 说明 |
---|---|---|
root | /comfyui | ComfyUI 项目的根目录。 |
output | /comfyui/output | ComfyUI 图片生成目录。 |
custom_nodes | /comfyui/custom_nodes | ComfyUI 自定义节点目录。 |
workflows | /comfyui/user/default/workflows | ComfyUI 工作流保存目录。 |
checkpoints | /comfyui/models/checkpoints | ComfyUI checkpoints 路径。 |
lora | /comfyui/models/lora | ComfyUI LoRA 相关插件配置路径。 |
vae | /comfyui/models/vae | ComfyUI VAE 模型配置路径。 |
clip | /comfyui/models/clip | ComfyUI CLIP 模型配置路径。 |