亚马逊AWS官方博客

使用 Amazon EC2 构建 ComfyUI 结合 Krita 的实践

关于 Krita

Krita 是一款功能强大且完全免费的开源数字绘画软件,旨在为艺术家提供一个无障碍的创作平台。目前 Krita 支持在 Windows、MacOS 和 Linux 操作系统使用。

软件特点

  • 自由开源:Krita 遵循 GNU GPL 许可证,确保用户可以自由使用、修改和分发软件,而不受商业限制。
  • 多功能绘画工具:Krita 提供了丰富的绘画功能,包括:
    • 强大的笔刷引擎,支持多种绘画风格。
    • 图层管理、绘画辅助工具和笔刷防抖功能,帮助用户实现精细的绘画效果。
    • 动画制作工具,支持多图层动画和音频导入。
  • 用户友好的界面:Krita 的界面可高度定制,用户可以根据自己的需求调整面板布局和快捷键,以提高创作效率。
  • 社区支持:Krita 拥有一个活跃的国际社区,用户可以在论坛中分享作品、讨论创作经验,并与开发者直接交流。

关于 ComfyUI

ComfyUI 是基于 Stable Diffusion 的图像生成工具,采用节点式工作流设计,具有以下特点:

高效的图像生成

  • ComfyUI 在图像生成速度上优于传统的 WebUI,适合需要快速生成图像的用户。
  • 显存占用较低,即使在显存有限的情况下也能有效运行。

高度可定制的工作流

  • 用户可以创建和复用自定义工作流,通过节点连接实现复杂的图像生成过程。
  • 支持导入和导出工作流,便于分享和学习。

扩展性和社区支持

  • ComfyUI 允许用户开发自定义节点,社区贡献了大量自定义节点,极大丰富了功能选择。
  • 尽管学习曲线较陡,但其灵活性和可玩性吸引了许多高级用户。

实时反馈和调试

  • 通过节点式设计,用户可以实时查看生成过程中的每个步骤,便于调试和优化工作流。

通过上面的介绍可以看到,Krita 适合需要手绘和复杂图层管理的艺术创作,提供了丰富的工具和灵活的工作环境;而 ComfyUI 则更适合快速生成图像和需要高度定制化的用户,尤其在 AI 绘画和图像处理方面表现突出。本文会介绍如何通过 Krita 结合 ComfyUI 实现非常高效的实时绘画,并带大家一起来构建在亚马逊云科技 EC2 上的 ComfyUI 环境。整体架构信息如下所示:

架构说明

本方案中暴露了一系列 API 给到外部进行调用完成 EC2 创建维护等,其中使用的重要组件列举如下:

  • EC2:用来部署 ComfyUI 服务
  • EFS:存储全局、不同部门、不同用户的 Custom Nodes 以及 Model
  • DynamoDB:管理 Custom Nodes 以及服务器-用户的 mapping 信息
  • API Gateway,Lambda:通过 API gateway 和 Lambda 来暴露外部调用 API
  • Eventbridge:监听 EC2 的状态并调用 Lambda 来修改 DynamoDB 中 EC2 的状态信息

整个方案中使用的服务基本都是 Serverless 的服务,其中 EC2 启动之后会监控针对 GPU 的使用率。一旦一段时间没有使用后就会被停止,待下一次用户使用时再启动。

安装 Krita

Krita 官网下载后直接安装完后,下载 AI 插件 krita-ai-diffusion,进行安装

安装完毕后,在 Krita 的右下角可以看到插件信息

方案部署

下面我们开始部署整体方案。

EC2 AMI 制作

1. 使用 Ubuntu 系统来制作 EC2 Image,或者直接使用已经安装好环境的官方 Ubuntu 镜像,本文采用第一种

首先我们需要在亚马逊云科技控制台来启动一台 Ubuntu 系统的 EC2,EC2 的安全组需要开放 22 以及 8848 端口,EBS 选择 gp3, 200G。

2. 安装 AWS CLI

sudo apt install unzip
curl "https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip" -o "awscliv2.zip"
unzip awscliv2.zip
sudo ./aws/install

3. 安装 Nvidia 驱动

按照 AWS 官网来安装驱动,打开下面的网页,选择 Option4:Nvidia Gaming Driver。一定要安装下面的包,不然会导致 EC2 重启后 GPU Driver 不能 work:

sudo apt-get install -y nvidia-cuda-toolkit
sudo apt-get install -y ubuntu-drivers-common
sudo ubuntu-drivers autoinstall
sudo reboot

4. 安装 Cloudwatch Agent

下载 Cloudwatch Agent 后,使用以下命令启动。

Agent.json 文件参考下面内容,文件目录为:/home/ubuntu/cloudwatch-agent/agent.json

{
  "metrics": {
    "append_dimensions": {
      "InstanceId": "${aws:InstanceId}"
    },
    "metrics_collected": {
      "nvidia_gpu": {
        "measurement": [
          "utilization_gpu",
          "utilization_memory",
          "memory_total",
          "memory_used",
          "memory_free"
        ],
        "metrics_collection_interval": 30
      }
    }
  }
}
sudo /opt/aws/amazon-cloudwatch-agent/bin/amazon-cloudwatch-agent-ctl -a fetch-config -m ec2 -c file:/home/ubuntu/cloudwatch-agent/agent.json -s

5. 安装 EFS Driver

sudo apt-get update
sudo apt-get -y install git binutils rustc cargo pkg-config libssl-dev
git clone https://github.com/aws/efs-utils
cd efs-utils
./build-deb.sh
sudo apt-get -y install ./build/amazon-efs-utils*deb

6. 安装 ComfyUI 服务

此处不需要创建为系统服务,在后面的方案中会自动创建,下面的命令会自动安装 Comfy Manager 的 custom node。

# ubuntu根目录下执行以下命令, 根据本地python3版本调整
sudo apt install python3.12-venv
python3 -m venv venv
. venv/bin/activate
pip install comfy-cli
comfy install # 一路“Y”下去
#启动服务
/home/ubuntu/venv/bin/python3 /home/ubuntu/comfy/ComfyUI/main.py --listen 0.0.0.0 --port 8848

7. 安装 Krita 需要的 Custom Nodes

按照 Krita AI 插件的介绍 ComfyUI Setup,需要安装以下 Custom Nodes:

  • ComfyUI Nodes for External Tooling
  • ComfyUI’s ControlNet Auxiliary Preprocessors
  • ComfyUI_IPAdapter_plus
  • Inpaint Nodes

我们可以通过 Comfy Manager 中的 Custom Nodes Management 来下载安装

8. 下载测试模型

wget https://github.com/Acly/krita-ai-diffusion/releases/download/v1.22.0/krita_ai_diffusion-1.22.0.zip
unzip krita_ai_diffusion-1.22.0.zip
cd ai_diffusion/
pip3 install aiohttp tqdm
python3 download_models.py /home/ubuntu/comfy/ComfyUI --recommended # 注意ComfyUI Home

9. 安装完毕后,测试当前环境是否正常,显示 Connected 则为正常

10. 至此 EC2 环境已经完备,停止 EC2,制作 Image,制作完毕后,请记录一下 AMI 的 ID,以备后续使用

CDK 部署环境

创建部署用 EC2(推荐 t3.large,仅作为部署短期使用),默认使用 Amazon Linux 2023,赋予必要 IAM Role 角色权限,涉及权限如下:VPC、EC2、EFS、API Gateway、Lambda、DynamoDB、EventBridge 创建权限。

安装 CDK

curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.7/install.sh | bash 
source ~/.bashrc 
nvm --version 
nvm install 20 
nvm use 20 
node -v 
npm -v 
npm install -g aws-cdk
cdk --version

下载部署代码

yum install git -y
git clone https://github.com/aws-samples/using-EC2-build-Krita-with-ComfyUI-practice.git

修改配置,进行部署

cd using-EC2-build-Krita-with-ComfyUI-practice/
vim gen_env.sh
# 打包好的comfyui ubuntu的 AMI ID, 请务必替换成我们自己的AMI ID, 此处需要注意不同机型的AMI有可能不适配,请一定一定要使用指定机型的AMI
EC2_AMI_ID='ami-0633a0cxxxx'
# EC2 key pair name, 请务必替换成我们自己的EC2 Key Name
EC2_KEY_NAME='west'
# EC2 实例类型, 默认为: g6.2xlarge
EC2_INSTANCE_TYPE='g6.2xlarge'
# DynamoDB Table 名称, 用户分配的ComfyUI服务器信息, 默认为: user-comfyui-servers
USER_COMFYUI_SERVERS_TABLE='user-comfyui-servers'
# DynamoDB Table 名称, Custom Nodes信息, 默认为: comfyui_custom_nodes
COMFYUI_CUSTOM_NODES_TABLE='comfyui_custom_nodes'
# 指定自己的VPC CIDR
VPC_CIDR='10.0.0.0/16'
# 指定机器闲置时间,单位为分钟
SERVER_IDLE_TIME='30'

修改完毕后,执行

sh gen_env.sh
npm install
cdk bootstrap
cdk deploy

等待大约十多分钟后,部署完毕

检查 CloudFormation

根据 APIKeyARN 获取 APIKeyID,格式如下:

arn:aws:apigateway:region::/apikeys/{api-key-id}

根据 apikeyid 获取 apikey

aws apigateway get-api-key --api-key {api-key-id} --include-value --query "value" --output text

记录下 InvokeUrl、APIKey、FileBroswerInstanceIP。

使用 Postman 测试接口

下载测试 json 文件

导入到 Postman,定义环境变量 api-key 用上面的 APIKey 赋值,url 用 InvokeUrl 赋值

/ComfyUI-Servers 一共有三个接口

  • Post 接口,根据 userid 创建/启动 ComfyServer
  • Get 接口,根据 username 查询 ComfyServer 状态以及对外暴露的 IP 和 Port,直接配置在 Krita 中进行使用
  • Stop 管理接口,根据 username,group_name 停止 ComfyServer

/ComfyUI-Servers/custom_nodes 一共有四个接口,分别对应查询,创建,更新,删除

  • Post 接口,创建 custom_nodes
    {
      "node_name": "ComfyUI Inspire Pack",
      "node_type": "global",
      "group_name": "group1",
      "repo_url": "https://github.com/example/repo.git",
      "status": "available",
      "creator": "user1"
    }
    
  • 通过 id 获取 custom_node
  • 通过 id 更新 custom_node
    {
      "node_name": "ComfyUI Inspire Pack",
      "node_type": "global",
      "group_name": "group1",
      "repo_url": "https://github.com/ltdrdata/ComfyUI-Inspire-Pack.git",
      "status": "available"
    }
    
  • 通过 id 删除 custom_node

模型管理

模型是通过 EFS 进行共享,分为 global 和 group 模型

File browser 管理模型

通过使用 FileBroswerInstanceIP 地址打开管理界面,使用默认账号密码登录 admin/admin,默认工作目录是 /home/ec2-user/EFS/

models 目录结构为 global,{groups}/{group_name},{users}/{username};

output 目录结构为 {output_dir}/{group_name}/{username}

可以通过设置界面修改 admin 登录密码,和默认工作目录

模型上传

  • 通过 comfyui manager 下载模型到指定文件夹
  • 使用 file browser 上传,路径在 /home/ec2-user/EFS/models/checkpoints/global/
  • 通过密钥登录 file browser 服务器进行更新
    cd /home/ec2-user/EFS/models/checkpoints/global/
    wget -O sd_xl_base_1.0.safetensors https://huggingface.co/stabilityai/stable-diffusion-xl-base-1.0/resolve/main/sd_xl_base_1.0.safetensors?download=true
    

从 FileBrowser 使用命令下载模型

首先将 wget、curl 等常用文件下载和管理命令设置为允许在 FileBrowser 中使用。

点击左侧配置按钮

再点击用户管理,选择需要赋予命令权限的用户,点击铅笔图标

滑动到画面下端的用户命令(Shell 命令),添加 ls、wget、curl、rm、cp、mv 等文件操作命令,注意命令之间使用半角空格分割。编辑完成后点击保存按钮

返回文件操作界面,点击右上角的“<>”图标,开启 terminal

在页面下半部分的 terminal 执行各种文件管理命令,来下载和管理模型文件

关键代码解析

comfyui_servers_post.py

  1. 创建 EC2 的逻辑
    result = query_comfyui_servers_by_username(username) # 获取用户之前是否创建过EC2
    if result: # 如果已有EC2,则将其启动
        print(f"Username:{username} already has a comfyui server")
        for item in result:
            instance_id = item['instance_id']
            response = ec2_client.describe_instances(InstanceIds=[instance_id])
            instance_status = response['Reservations'][0]['Instances'][0]['State']['Name']
            if instance_status == 'stopped':
                start_instance(instance_id=instance_id, idle_time=idle_time)
                update_status(username=username, instance_id=instance_id, status='starting')
            else:
                return {
                    "statusCode": 400,
                    "body": json.dumps({"message": f"Can not start instance_id: {instance_id}, current status:{instance_status}", "code": 400})
                }
    else: # 若之前没有EC2,则为其创建信息的EC2
        print(f"Username:{username} doesn't have a comfyui server, now create a new one.")
        instances = create_instance(username=username, group_name=group_name, idle_time=idle_time)
        instance_id = instances[0].id
    
  2. 获取用户配置的 custom nodes 并组装安装命令
    repo_list = get_custom_nodes_by_type('global')
        # Convert repo_list to a string for the user data script
        repo_clone_commands = "\n".join([
            f"""
            if [ ! -d {comfyui_home_dir}/custom_nodes/{repo['repo_url'].split('/')[-1].replace('.git', '')} ]; then
                git clone {repo['repo_url']} {repo.get('extra_parameter', '')} {comfyui_home_dir}/custom_nodes/{repo['repo_url'].split('/')[-1].replace('.git', '')} &&
                if [ -f {comfyui_home_dir}/custom_nodes/{repo['repo_url'].split('/')[-1].replace('.git', '')}/requirements.txt ]; then
                    source /home/ubuntu/venv/bin/activate && pip install -r {comfyui_home_dir}/custom_nodes/{repo['repo_url'].split('/')[-1].replace('.git', '')}/requirements.txt;
                fi
            else
                echo "Repository {repo['repo_url']} already cloned."
            fi
            """
            for repo in repo_list
        ])
        print(repo_clone_commands)
    
  3. 使用 user data 精准挂载当前用户的 group 以及自身下载的模型
    user_data_script = f"""#!/bin/bash
        echo "---------user data start-----------"
        # Mount EFS
        if [ ! -d "{ec2_start_script_dir}" ]; then
            mkdir -p {ec2_start_script_dir}
        fi
        mount -t efs -o tls,iam,accesspoint={access_point_start_script_id} {file_system_id}:/{username} {ec2_start_script_dir}
        echo "{file_system_id}:/{username} {ec2_start_script_dir} efs _netdev,tls,iam,accesspoint={access_point_start_script_id} 0 0" >> /etc/fstab
        chmod +x {ec2_start_script_dir}/mount.sh
        bash {ec2_start_script_dir}/mount.sh
        
        # Create User Output Dir
        if [ ! -d "{user_output_dir}" ]; then
            mkdir -p {user_output_dir}
        fi
    
        # Custom Nodes Clone
        {repo_clone_commands}
        chown -R ubuntu:ubuntu {comfyui_home_dir}/custom_nodes/*
    
        # Set Comfyui as system service
        cat << EOF > /etc/systemd/system/comfyui.service
        [Unit]
        Description=ComfyUI Service
        After=network.target
    
        [Service]
        User=root
        WorkingDirectory=/home/ubuntu/comfy/ComfyUI
        ExecStart=/home/ubuntu/venv/bin/python3 main.py --listen 0.0.0.0 --port {comfyui_server_port} --output-directory {user_output_dir}
        Restart=always
    
        [Install]
        WantedBy=multi-user.target
    EOF
        # start comfyui as system service
        systemctl daemon-reload
        systemctl enable comfyui.service
        systemctl start comfyui.service
        echo "---------user data end-----------"
        """
    
  4. 设置 CloudWatch metrics 以及 alarm 一旦超过一段时间没有用 EC2,则直接关机
    def put_alarm_metric_alarm(instance_id, idle_time):
        gpu_info = next((item for item in INSTANCE_GPU if item['instance'] == instance_type[:2]), None)
        put_response = cw.put_metric_alarm(
            AlarmName=f'{alarm_name_prefix}{instance_id}',
            ComparisonOperator='LessThanThreshold',
            EvaluationPeriods=int(idle_time),
            MetricName='nvidia_smi_utilization_gpu',
            Namespace='CWAgent',
            Period=60,
            Statistic='Maximum',
            Threshold=1.0,
            ActionsEnabled=True,
            AlarmActions=[f'arn:aws:swf:{region}:{account_id}:action/actions/AWS_EC2.InstanceId.Stop/1.0'],
            AlarmDescription=f'Alarm when GPU utilization is low for {idle_time} minutes',
            Dimensions=[
                {
                    'Name': 'InstanceId',
                    'Value': instance_id
                },
                {
                    'Name': 'name', 
                    'Value': gpu_info['gpu']
                },
                {
                    'Name': 'index', 
                    'Value': '0'
                },
                {
                    'Name': 'arch', 
                    'Value': gpu_info['arch']
                }
            ],
            TreatMissingData='notBreaching' #没有数据的点不不触发告警.
        )
        print(f'Alarm reset response: {put_response}')
    

总结

在这篇博客中,我们探讨了如何在 Amazon EC2 上构建 Krita 与 ComfyUI 的集成环境,以实现高效的实时绘画。通过使用 Serverless 架构,结合 API Gateway 和 Lambda,我们实现了对 ComfyUI 服务的管理和监控,确保用户可以方便地创建和管理自己的绘画环境。

常见问题

执行命令 nvidia-smi 报错如下

Failed to initialize NVML: Driver/library version mismatch; 

通过以下方案

sudo apt-get --purge remove "*nvidia*"

之后重新安装 Driver

查看日志

systemctl status comfyui # 查看服务状态
journalctl -f -u comfyui # 查看服务日志

本篇作者

郭俊龙

亚马逊云科技解决方案架构师,主要负责游戏行业客户解决方案设计,比较擅长云原生微服务以及大数据方案设计和实践。

陈汉卿

亚马逊云科技解决方案架构师,负责基于亚马逊云科技云计算方案的咨询、架构设计及落地,拥有多年移动互联网研发经验,在云原生微服务以及云迁移等方向有丰富的实践经验。

秦镜高

亚马逊云科技资深解决方案架构师,负责基于亚马逊云科技云计算方案的架构设计,帮助客户利用领先的云服务技术构建更具创新性的应用。加入亚马逊云科技之前,有 10 多年丰富的游戏开发和架构设计实践经验。

刘硕

亚马逊云科技客户解决方案经理,在亚马逊云科技主要支持游戏和零售等行业的用户。专注于促进亚马逊云科技用户解决方案落地,提升上云体验,帮助用户实现自身的业务价值。