历元智能投研由Antares多因子选股系统驱动
ANTARES / PUBLIC/doc/杂项/多机多卡集群-SwarmUI统一操作台部署流程
Skip to content

多机多卡集群|SwarmUI 统一操作台 全套部署流程 ​

目标:N 台 Ubuntu 机器、M 张显卡 → 一个网页操作台,任务自动分派,卡不闲置 架构:SwarmUI 主控 + 每卡一个 ComfyUI 后端 + 跨机 Swarm-API 级联 预计耗时:主控机半天,每台从机 1–2 小时(模型同步另算)


阶段 0|规划表(先填完再动手) ​

动手前把这张表填掉。 后面所有配置都依赖它,边做边想必返工。

主机名内网 IP显卡显存角色池备注
node-master192.168.1.50RTX 5090 ×232G ×2主控 + 视频池对外唯一入口
node-w1192.168.1.51RTX 409024G视频池
node-w2192.168.1.52RTX 4080 ×216G ×2出图池
node-w3192.168.1.53RTX 306012G杂活池(TTS/放大)
nas192.168.1.60——素材/成片/LoRA库

三条铁律,全流程依赖:

  1. 所有机器用同一个用户名 comfy、同一套路径(/opt/ComfyUI、/data/models)。路径不统一,rsync 和后端配置全部要单独改,机器一多就失控。
  2. 所有机器 ComfyUI 主版本 + 自定义节点必须完全一致。 版本不一致的表现是:任务随机失败,主控只显示"生成失败",真正的报错在某台从机的日志里。这是多机部署最耗时的坑。
  3. 每个节点的模型盘必须是 NVMe。 同样 30G 模型,NVMe 加载 10 秒,SATA SSD 60 秒,机械盘 5 分钟。

网络: 千兆是底线,2.5G 起步,10G 更好。交换机别用路由器自带口。


阶段 1|网络与主机准备(每台机器都做) ​

1.1 固定内网 IP ​

路由器里做 DHCP 静态绑定(推荐,最省事),或 netplan 配静态:

yaml
# /etc/netplan/01-static.yaml
network:
  version: 2
  ethernets:
    enp5s0:                       # 用 ip a 查实际网卡名
      dhcp4: no
      addresses: [192.168.1.51/24]
      routes:
        - to: default
          via: 192.168.1.1
      nameservers:
        addresses: [223.5.5.5, 119.29.29.29]
bash
sudo netplan apply

1.2 主机名与 hosts ​

bash
sudo hostnamectl set-hostname node-w1

每台机器的 /etc/hosts 加上全集群(后面所有命令用主机名,不用记 IP):

192.168.1.50  node-master
192.168.1.51  node-w1
192.168.1.52  node-w2
192.168.1.53  node-w3
192.168.1.60  nas

1.3 统一用户 ​

bash
sudo useradd -m -s /bin/bash comfy
sudo mkdir -p /opt/ComfyUI /data/models /data/output
sudo chown -R comfy:comfy /opt/ComfyUI /data

1.4 SSH 免密(主控 → 所有从机) ​

只在主控机上做:

bash
sudo -u comfy -i
ssh-keygen -t ed25519 -N ""
for h in node-w1 node-w2 node-w3; do
  ssh-copy-id comfy@$h
done
# 验证
for h in node-w1 node-w2 node-w3; do ssh comfy@$h hostname; done

这一步通了,后面模型分发和批量运维才能脚本化。


阶段 2|基础环境(每台机器都做) ​

写成脚本,一台一台跑,别手敲:

bash
#!/bin/bash
# bootstrap.sh —— 在每台机器上以 sudo 执行
set -e
apt update && apt upgrade -y
apt install -y build-essential git curl wget vim htop nvtop rsync \
  python3-venv python3-pip python3-dev \
  ffmpeg libgl1 libglib2.0-0 \
  nginx apache2-utils ufw net-tools openssh-server
timedatectl set-timezone Asia/Shanghai
systemctl enable --now ssh
echo "=== 基础环境完成 ==="
bash
# 分发并执行
for h in node-w1 node-w2 node-w3; do
  scp bootstrap.sh comfy@$h:/tmp/
  ssh -t comfy@$h "sudo bash /tmp/bootstrap.sh"
done

阶段 3|NVIDIA 驱动(每台机器) ​

bash
ubuntu-drivers devices        # 看推荐版本
sudo ubuntu-drivers install

RTX 50 系(Blackwell)必须装 -open 后缀的开源内核模块包,闭源版不支持这代架构:

bash
sudo apt install -y nvidia-driver-595-open      # 版本以 ubuntu-drivers 输出为准
sudo apt-mark hold nvidia-driver-595-open       # 锁版本,防 apt 自动升级搞挂
sudo reboot

全集群驱动版本尽量统一。 不同版本会导致同一个工作流在 A 机能跑、B 机报 CUDA 错。

验证:

bash
for h in node-master node-w1 node-w2 node-w3; do
  echo "--- $h"; ssh comfy@$h nvidia-smi --query-gpu=index,name,memory.total --format=csv
done

不装 CUDA Toolkit。 PyTorch wheel 自带运行时,装 Toolkit 只会制造冲突。


阶段 4|ComfyUI 基准环境 ​

4.1 主控机先建一份"金标准" ​

bash
sudo -u comfy -i
git clone https://github.com/comfyanonymous/ComfyUI.git /opt/ComfyUI
cd /opt/ComfyUI

# 【关键】锁定版本,记下这个 commit
git rev-parse HEAD > /opt/ComfyUI/PINNED_COMMIT
cat /opt/ComfyUI/PINNED_COMMIT

python3 -m venv venv
source venv/bin/activate
pip install --upgrade pip
pip install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cu128
pip install -r requirements.txt

# Manager
cd custom_nodes
git clone https://github.com/ltdrdata/ComfyUI-Manager.git

pip 国内加速(注意装 torch 那条要保留 --index-url,不能走国内源):

bash
pip config set global.index-url https://mirrors.aliyun.com/pypi/simple/

4.2 模型路径外置 ​

/opt/ComfyUI/extra_model_paths.yaml(所有机器完全相同):

yaml
comfyui:
    base_path: /data/models/
    checkpoints: checkpoints/
    loras: loras/
    vae: vae/
    diffusion_models: unet/
    text_encoders: clip/
    clip_vision: clip_vision/
    controlnet: controlnet/
    upscale_models: upscale_models/
    instantid: instantid/

4.3 在主控机上装齐所有自定义节点 ​

一次只装一个,装完重启验证再装下一个。 90% 的"ComfyUI 起不来"是某个节点的 pip 依赖冲突。

装完导出清单:

bash
cd /opt/ComfyUI/custom_nodes
for d in */; do
  echo "$d $(git -C "$d" rev-parse HEAD 2>/dev/null)"
done > /opt/ComfyUI/NODES_MANIFEST

4.4 复制到所有从机 ​

bash
# nodes-sync.sh —— 主控机执行
for h in node-w1 node-w2 node-w3; do
  echo "=== 同步 $h"
  rsync -avh --delete \
    --exclude 'venv/' --exclude 'output/' --exclude 'input/' \
    --exclude 'models/' --exclude 'user/' --exclude '.git/' \
    /opt/ComfyUI/ comfy@$h:/opt/ComfyUI/
done

venv 不同步(各机自建),其余全同步。从机上建 venv:

bash
for h in node-w1 node-w2 node-w3; do
  ssh comfy@$h 'cd /opt/ComfyUI && python3 -m venv venv && \
    source venv/bin/activate && pip install --upgrade pip && \
    pip install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cu128 && \
    pip install -r requirements.txt'
done

校验版本一致性(每次升级后都跑一遍):

bash
for h in node-master node-w1 node-w2 node-w3; do
  echo -n "$h: "; ssh comfy@$h 'git -C /opt/ComfyUI rev-parse --short HEAD'
done

输出必须全部相同。


阶段 5|模型分发 ​

5.1 原则 ​

模型不走网络共享,每台机器本地 NVMe 各存一份。 千兆网 125MB/s,30G 的 Wan 模型走 NFS 加载一次 4 分钟,比推理本身还慢。素材、成片、LoRA 库这类小文件才走 NAS。

5.2 按池分发,不要全量同步 ​

视频池的机器不需要出图的一堆底模,出图池不需要 30G 视频权重。磁盘和同步时间都省一半。

bash
#!/bin/bash
# model-sync.sh —— 主控机执行

VIDEO_NODES="node-w1"
IMAGE_NODES="node-w2"
MISC_NODES="node-w3"

# 视频池
for h in $VIDEO_NODES; do
  rsync -avh --progress /data/models/unet/wan/        comfy@$h:/data/models/unet/wan/
  rsync -avh --progress /data/models/clip/            comfy@$h:/data/models/clip/
  rsync -avh --progress /data/models/vae/             comfy@$h:/data/models/vae/
done

# 出图池
for h in $IMAGE_NODES; do
  rsync -avh --progress /data/models/checkpoints/     comfy@$h:/data/models/checkpoints/
  rsync -avh --progress /data/models/instantid/       comfy@$h:/data/models/instantid/
  rsync -avh --progress /data/models/controlnet/      comfy@$h:/data/models/controlnet/
done

# 杂活池
for h in $MISC_NODES; do
  rsync -avh --progress /data/models/upscale_models/  comfy@$h:/data/models/upscale_models/
done

# 角色 LoRA 库 —— 所有机器都要,且最常变动
for h in $VIDEO_NODES $IMAGE_NODES $MISC_NODES; do
  rsync -avh --delete /data/models/loras/  comfy@$h:/data/models/loras/
done

echo "=== 模型分发完成 ==="

角色 LoRA 库变动最频繁,建议单独做成 systemd timer 每小时同步一次:

bash
# /etc/systemd/system/lora-sync.timer
[Unit]
Description=Sync LoRA library hourly
[Timer]
OnCalendar=hourly
Persistent=true
[Install]
WantedBy=timers.target

阶段 6|ComfyUI 后端服务(每卡一个实例) ​

用带参数的 systemd 模板,一份文件搞定所有卡。

/etc/systemd/system/comfyui@.service:

ini
[Unit]
Description=ComfyUI GPU %i
After=network.target

[Service]
User=comfy
Group=comfy
WorkingDirectory=/opt/ComfyUI
Environment="CUDA_VISIBLE_DEVICES=%i"
Environment="PATH=/opt/ComfyUI/venv/bin:/usr/bin:/bin"
ExecStart=/opt/ComfyUI/venv/bin/python main.py \
    --listen 127.0.0.1 \
    --port 818%i \
    --output-directory /data/output
Restart=always
RestartSec=10

[Install]
WantedBy=multi-user.target

启动(%i 就是 GPU 编号,端口自动变成 8180、8181…):

bash
sudo systemctl daemon-reload

# 单卡机器
sudo systemctl enable --now comfyui@0

# 双卡机器
sudo systemctl enable --now comfyui@0 comfyui@1

# 检查
systemctl status 'comfyui@*'
journalctl -u comfyui@0 -f

--listen 127.0.0.1 是硬性要求。 ComfyUI 无认证且自定义节点等于任意代码执行,任何情况下都不能绑 0.0.0.0。跨机通信由 SwarmUI 负责,不是由 ComfyUI 直连。


阶段 7|SwarmUI 安装 ​

7.1 每台机器都装(主控和从机都要) ​

bash
sudo -u comfy -i
cd /opt
git clone https://github.com/mcmonkeyprojects/SwarmUI
cd SwarmUI
./make.sh run          # 启动脚本以仓库 README 为准,也有 Docker 方式

首次运行会走安装向导。关键选择:ComfyUI 后端选 "none" / 不自动安装——我们已经有自己的 ComfyUI 了,让它自装会多出一套版本不一致的环境,这正是要避免的。

SwarmUI 需要 .NET 运行时,启动脚本一般会自动装。

Docker 方式(如果不想碰 .NET):

bash
docker run -d --gpus all -p 7801:7801 \
  -v /opt/swarm-data:/data -v /data/models:/models \
  --restart always --name swarmui mcmy/swarmui:latest

7.2 监听地址 ​

  • 主控机:--host 0.0.0.0 --port 7801(要接受从机之外的访问)
  • 从机:也要 --host 0.0.0.0,但靠防火墙只放行主控 IP

7.3 systemd 托管 ​

/etc/systemd/system/swarmui.service:

ini
[Unit]
Description=SwarmUI
After=network.target comfyui@0.service

[Service]
User=comfy
WorkingDirectory=/opt/SwarmUI
ExecStart=/opt/SwarmUI/launch-linux.sh --host 0.0.0.0 --port 7801
Restart=always
RestartSec=15

[Install]
WantedBy=multi-user.target
bash
sudo systemctl daemon-reload && sudo systemctl enable --now swarmui

7.4 防火墙 ​

从机(只允许主控访问):

bash
sudo ufw default deny incoming
sudo ufw default allow outgoing
sudo ufw allow 22/tcp
sudo ufw allow from 192.168.1.50 to any port 7801
sudo ufw enable

主控机:

bash
sudo ufw default deny incoming
sudo ufw allow 22/tcp
sudo ufw allow from 192.168.1.0/24 to any port 8080    # Nginx,仅内网
sudo ufw enable

7801 不对公网开,只经 Nginx 8080 出去。


阶段 8|注册后端(核心步骤) ​

浏览器打开主控机 http://node-master:7801,先建管理员账号,然后进 Server → Backends。

8.1 主控机本地后端(每卡一个) ​

点 Add Backend → ComfyUI Self-Starting,填:

字段值
Start Script/opt/ComfyUI/main.py
GPU ID0(第二张卡再加一个,填 1)
Extra Args--use-sage-attention(出黑图就去掉)
OverQueue0

OverQueue 必须设 0。 默认值会预先往后端塞任务,异构显卡混用时,慢卡会被塞一堆任务而快卡空闲。设 0 表示卡真正空闲才派活,这是多机集群里最关键的一个开关。

8.2 远程机器后端 ​

点 Add Backend → Swarm-API-Backend,填地址:

http://node-w1:7801
http://node-w2:7801
http://node-w3:7801

一台从机填一条,它自己会把本机的多张卡都暴露上来。这就是 SwarmSwarmBackend 机制——一个 SwarmUI 控制另一个 SwarmUI,跨机器就靠这个。

从机侧要允许主控访问其 API(从机 SwarmUI 的 Server Configuration 里放开 API 访问权限)。

8.3 验证 ​

Backends 页面所有条目应该显示绿色 running。灰色/红色就去对应机器看:

bash
ssh comfy@node-w1 'journalctl -u swarmui -n 50'
ssh comfy@node-w1 'systemctl status "comfyui@*"'

阶段 9|分池策略落地 ​

SwarmUI 调度时会优先派给已加载该模型的后端。利用这一点把池子分死,就能几乎消灭模型换入换出——这是整套架构里性价比最高的优化。

具体做法:

  1. 各池机器的 /data/models 里只放本池要用的模型(阶段 5 已按池分发)。SwarmUI 发现某后端没有目标模型,就不会往那派活,分池自动生效。
  2. 在 SwarmUI User Permissions 里按小组限定可用模型:视频组只看到视频模型,出图组只看到出图模型。人为误操作也堵住。
  3. 量化优先:24G 卡上用 fp8 / GGUF 版本的 Wan、Qwen-Image。短剧场景质量损失基本看不出,但模型能一直挂在显存里不卸载,每次省掉几十秒加载。

换入换出的代价:一张卡上午跑视频下午跑图,一天光加载模型就浪费两三小时。分池成本为零,收益最大。


阶段 10|对外访问(Nginx + 花生壳/frp) ​

只有主控机对外,从机永远不出内网。

10.1 账号 ​

bash
sudo htpasswd -c /etc/nginx/.htpasswd zhangsan   # 第一个用户用 -c
sudo htpasswd /etc/nginx/.htpasswd lisi          # 之后不要加 -c,否则清空文件

(SwarmUI 自带多用户登录,Nginx 这层 basic auth 是第二道门,两层都要。)

10.2 Nginx ​

/etc/nginx/sites-available/swarm:

nginx
server {
    listen 8080;
    server_name _;

    client_max_body_size 1024m;

    auth_basic "Restricted";
    auth_basic_user_file /etc/nginx/.htpasswd;

    location / {
        proxy_pass http://127.0.0.1:7801;
        proxy_http_version 1.1;

        # WebSocket,漏了界面一直转圈
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";

        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;

        # 视频任务能跑 20 分钟,默认 60s 直接断
        proxy_read_timeout 3600;
        proxy_send_timeout 3600;
        proxy_buffering off;
    }
}
bash
sudo ln -s /etc/nginx/sites-available/swarm /etc/nginx/sites-enabled/
sudo rm -f /etc/nginx/sites-enabled/default
sudo nginx -t && sudo systemctl reload nginx

10.3 穿透 ​

花生壳映射到主控机 127.0.0.1:8080,外网端口选动态端口(80/443 国内需备案)。

注意:花生壳免费版带宽约 1Mbps,集群规模上来后这条管子会成为瓶颈。 手上有公网 IP 服务器的话,用 frp 反向穿透,带宽跑满服务器上行;或用 Cloudflare Tunnel,免费不限带宽且不开入站端口。


阶段 11|夜间批渲染(产能杠杆) ​

白天卡要留给交互式调试和试片,不能被批渲染堵死。定稿镜头晚上无人值守跑。

/opt/scripts/night-render.sh:

bash
#!/bin/bash
# 从任务清单里读定稿镜头,调 SwarmUI API 排队
QUEUE_DIR=/data/queue/pending
DONE_DIR=/data/queue/done
mkdir -p "$DONE_DIR"

for job in "$QUEUE_DIR"/*.json; do
  [ -e "$job" ] || continue
  echo "[$(date '+%F %T')] 提交 $(basename "$job")"
  curl -s -X POST http://127.0.0.1:7801/API/GenerateText2Image \
       -H "Content-Type: application/json" \
       --data-binary @"$job"
  mv "$job" "$DONE_DIR/"
done
ini
# /etc/systemd/system/night-render.timer
[Unit]
Description=Nightly batch render
[Timer]
OnCalendar=*-*-* 20:00:00
Persistent=true
[Install]
WantedBy=timers.target
bash
sudo systemctl enable --now night-render.timer
systemctl list-timers night-render.timer

这一条对产能的提升往往比多买两张卡还大,而且不花钱。


阶段 12|验收清单 ​

按顺序打勾,任何一项不过不要往下走:

  • [ ] 每台机器 nvidia-smi 正常,驱动版本一致
  • [ ] 主控 ssh comfy@node-wX hostname 全部免密通过
  • [ ] git -C /opt/ComfyUI rev-parse --short HEAD 全集群输出相同
  • [ ] 每台机器 systemctl status 'comfyui@*' 全部 active
  • [ ] 每台机器 systemctl status swarmui 全部 active
  • [ ] 主控 Backends 页面所有后端显示绿色 running
  • [ ] 提交 8 张图的批量任务,nvtop 上多台机器同时有 GPU 占用
  • [ ] 提交一个视频任务,能正常出片且不超时
  • [ ] 从机执行 curl http://node-w1:7801 从非主控机器访问被拒绝
  • [ ] 局域网访问 http://node-master:8080 弹密码框
  • [ ] 手机切 4G,用穿透域名能打开并弹密码框
  • [ ] 全集群断电重启一次,所有服务自动恢复、后端自动重连

运维手册 ​

加一台新机器(5 步) ​

bash
# 1. 装系统、设固定IP、改主机名、建 comfy 用户
# 2. 主控推 SSH 公钥
ssh-copy-id comfy@node-w4
# 3. 跑 bootstrap.sh + 装驱动
# 4. 同步 ComfyUI 和模型
rsync -avh --delete --exclude 'venv/' --exclude 'models/' --exclude 'output/' \
  /opt/ComfyUI/ comfy@node-w4:/opt/ComfyUI/
bash model-sync.sh
# 5. 装 SwarmUI、配防火墙、在主控 Backends 加一条 Swarm-API-Backend

所有机器 /etc/hosts 记得加新条目。

升级 ComfyUI 或节点 ​

永远从主控机开始,一次性推全网,不要单机升级。

bash
# 1. 主控升级、验证能跑
cd /opt/ComfyUI && git pull && source venv/bin/activate && pip install -r requirements.txt
sudo systemctl restart 'comfyui@*'
# 跑一个标准工作流验证

# 2. 更新版本记录
git rev-parse HEAD > /opt/ComfyUI/PINNED_COMMIT

# 3. 推全网
bash nodes-sync.sh
for h in node-w1 node-w2 node-w3; do
  ssh comfy@$h 'cd /opt/ComfyUI && source venv/bin/activate && \
    pip install -r requirements.txt && sudo systemctl restart "comfyui@*"'
done

# 4. 校验一致性
for h in node-master node-w1 node-w2 node-w3; do
  echo -n "$h: "; ssh comfy@$h 'git -C /opt/ComfyUI rev-parse --short HEAD'
done

排障顺序 ​

任务失败时按这个顺序查,主控日志基本没用,真正的报错在从机上:

bash
# 1. 哪个后端接的活 —— SwarmUI 界面 Backends 页面看状态
# 2. 到那台机器看 ComfyUI 日志
ssh comfy@node-w2 'journalctl -u comfyui@0 -n 100 --no-pager'
# 3. 看 SwarmUI 日志
ssh comfy@node-w2 'journalctl -u swarmui -n 100 --no-pager'
# 4. 看显存
ssh comfy@node-w2 nvidia-smi

最常见三类:

现象原因处理
任务随机失败,换一次可能成功某台机器节点版本不一致,缺节点跑版本校验,重新 nodes-sync
后端掉线变灰从机 ComfyUI 崩了或 OOM看 journalctl,加 --lowvram 或降低并发
界面一直转圈无进度Nginx 少了 WebSocket 配置检查 Upgrade/Connection 两行

别做的两件事 ​

别用 Nginx 做 ComfyUI 的负载均衡。 WebSocket 有状态、队列不共享,轮询转发会导致进度错乱、结果丢失。调度只能交给 SwarmUI 或自建 Redis 队列。

别让各机器自行安装节点。 只从主控推。一旦有人在从机上"顺手装了个插件",就会出现只在那台机器上复现的诡异故障,排查成本极高。


最后一句提醒 ​

真人短剧跑到中后期,瓶颈通常不是 GPU,是人的审片和返工速度。集群跑满两周后统计两个数:卡空闲率和人的积压量。

如果卡空闲率超过 40%,继续加机器是纯浪费,该加的是人和流程。这套集群方案的价值在于"卡不闲置",不在于"卡越多越好"。

历元智能投研 · 由Antares多因子选股系统驱动知识库