多机多卡集群|SwarmUI 统一操作台 全套部署流程
目标:N 台 Ubuntu 机器、M 张显卡 → 一个网页操作台,任务自动分派,卡不闲置 架构:SwarmUI 主控 + 每卡一个 ComfyUI 后端 + 跨机 Swarm-API 级联 预计耗时:主控机半天,每台从机 1–2 小时(模型同步另算)
阶段 0|规划表(先填完再动手)
动手前把这张表填掉。 后面所有配置都依赖它,边做边想必返工。
| 主机名 | 内网 IP | 显卡 | 显存 | 角色池 | 备注 |
|---|---|---|---|---|---|
| node-master | 192.168.1.50 | RTX 5090 ×2 | 32G ×2 | 主控 + 视频池 | 对外唯一入口 |
| node-w1 | 192.168.1.51 | RTX 4090 | 24G | 视频池 | |
| node-w2 | 192.168.1.52 | RTX 4080 ×2 | 16G ×2 | 出图池 | |
| node-w3 | 192.168.1.53 | RTX 3060 | 12G | 杂活池(TTS/放大) | |
| nas | 192.168.1.60 | — | — | 素材/成片/LoRA库 |
三条铁律,全流程依赖:
- 所有机器用同一个用户名
comfy、同一套路径(/opt/ComfyUI、/data/models)。路径不统一,rsync 和后端配置全部要单独改,机器一多就失控。 - 所有机器 ComfyUI 主版本 + 自定义节点必须完全一致。 版本不一致的表现是:任务随机失败,主控只显示"生成失败",真正的报错在某台从机的日志里。这是多机部署最耗时的坑。
- 每个节点的模型盘必须是 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 apply1.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 nas1.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 /data1.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 installRTX 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.gitpip 国内加速(注意装 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_MANIFEST4.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/
donevenv 不同步(各机自建),其余全同步。从机上建 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:latest7.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.targetbash
sudo systemctl daemon-reload && sudo systemctl enable --now swarmui7.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 enable7801 不对公网开,只经 Nginx 8080 出去。
阶段 8|注册后端(核心步骤)
浏览器打开主控机 http://node-master:7801,先建管理员账号,然后进 Server → Backends。
8.1 主控机本地后端(每卡一个)
点 Add Backend → ComfyUI Self-Starting,填:
| 字段 | 值 |
|---|---|
| Start Script | /opt/ComfyUI/main.py |
| GPU ID | 0(第二张卡再加一个,填 1) |
| Extra Args | --use-sage-attention(出黑图就去掉) |
| OverQueue | 0 |
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 调度时会优先派给已加载该模型的后端。利用这一点把池子分死,就能几乎消灭模型换入换出——这是整套架构里性价比最高的优化。
具体做法:
- 各池机器的
/data/models里只放本池要用的模型(阶段 5 已按池分发)。SwarmUI 发现某后端没有目标模型,就不会往那派活,分池自动生效。 - 在 SwarmUI User Permissions 里按小组限定可用模型:视频组只看到视频模型,出图组只看到出图模型。人为误操作也堵住。
- 量化优先: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 nginx10.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/"
doneini
# /etc/systemd/system/night-render.timer
[Unit]
Description=Nightly batch render
[Timer]
OnCalendar=*-*-* 20:00:00
Persistent=true
[Install]
WantedBy=timers.targetbash
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%,继续加机器是纯浪费,该加的是人和流程。这套集群方案的价值在于"卡不闲置",不在于"卡越多越好"。