记录一次在使用 ms-swift + vLLM 部署多模态模型时,
"明明指定了端口,但服务却跑在另一个端口" 的完整排查过程,以及最终总结出的通用找端口方法。
一、问题背景
我在一台 A100 服务器上,用 swift deploy 启动一个多模态推理服务(vLLM 后端),启动脚本如下:
nohup swift deploy \
--model /xxx \
--infer_backend vllm \
--vllm_gpu_memory_utilization 0.90 \
--vllm_tensor_parallel_size 2 \
--vllm_max_model_len 4096 \
--vllm_max_num_seqs 16 \
--vllm_limit_mm_per_prompt '{"image": 1, "video": 0}' \
--max_new_tokens 4096 \
--host 0.0.0.0 \
--port 8012 \
--served_model_name Qwen3-VL-32B-Instruct-LoRA \
--verbose true \
> logs/swift_deploy.log 2>&1 &
按理说,服务应该监听在 8012 端口。
但启动后访问 http://<IP>:8012,发现并不是新启动的服务;
而日志中却出现了:
Uvicorn running on http://0.0.0.0:8014
端口为什么变成了 8014?
二、第一反应:是不是服务起错了?
先确认模型服务是否真的启动成功。
1. GPU 侧确认
nvidia-smi
看到:
Processes:
PID Process name
529225 VLLM::Worker_TP0
529226 VLLM::Worker_TP1
说明:
-
vLLM 的 GPU worker 已经起来
-
模型权重已加载
-
不是"服务没起"的问题
三、怀疑端口被占用:从 lsof 入手
直接检查 8012 端口:
lsof -i:8012
结果非常关键:
python3.1 2779809 hc TCP *:8012 (LISTEN)
python3.1 2779809 hc TCP 192.168.1.114:8012->183.xxx.xxx.xxx (ESTABLISHED)
...
这说明:
-
8012 端口早就被别的 Python 服务占用了
-
而且正在被外部客户端访问
继续检查:
lsof -i:8013
python3.1 509851 hc TCP *:8013 (LISTEN)
lsof -i:8014
python3.1 528965 hc TCP *:8014 (LISTEN)
👉 至此真相已经非常清楚:
| 端口 | 状态 | 进程 |
|---|---|---|
| 8012 | 已占用 | 老服务 |
| 8013 | 已占用 | 老服务 |
| 8014 | LISTEN | 本次新启动服务 |
四、为什么 Swift 会"自动换端口"?
这是 ms-swift 的设计行为:
如果指定的
--port已被占用→ 自动尝试
port + 1→ 直到找到一个可用端口
因此流程实际是:
--port 8012
↓
8012 被占 → 尝试 8013
↓
8013 被占 → 尝试 8014
↓
8014 可用 → 启动成功
而且 不会报错,只会体现在日志中。
五、一个误区:从 GPU worker PID 找端口
当时我尝试从 GPU worker 反查端口:
ps -o pid,ppid,cmd -p 529225,529226
PID PPID CMD
529225 529085 VLLM::Worker_TP0
529226 529085 VLLM::Worker_TP1
接着我对父进程做了:
lsof -Pan -p 529085 -iTCP -sTCP:LISTEN
结果没有任何输出。
一开始以为是命令错了,后来才意识到------
👉 这是 vLLM v1 架构的正常行为。
六、vLLM / Swift 的真实进程结构
结合日志中的关键行:
(EngineCore_DP0 pid=529085)
INFO: Started server process [528965]
INFO: Uvicorn running on http://0.0.0.0:8014
可以还原真实结构:
swift deploy
├── EngineCore_DP0 (529085) ← 调度 / KV cache / workers
│ ├── Worker_TP0 (529225) ← GPU 0
│ └── Worker_TP1 (529226) ← GPU 1
│
└── Uvicorn Server (528965) ← HTTP 服务,监听端口
关键点:
-
GPU worker 不监听端口
-
EngineCore 也不一定监听端口
-
只有 Uvicorn 进程监听 TCP 端口
所以:
lsof -p 529085
查不到端口是完全正常的。
七、正确 & 通用的"找服务端口流程"
✅ 方法 1:直接看日志(最推荐)
Uvicorn running on http://0.0.0.0:PORT
这行里的 PORT 就是最终端口。
✅ 方法 2:从 LISTEN socket 反查(最稳)
ss -lntp | grep LISTEN | grep python
或缩小范围:
ss -lntp | grep 801
✅ 方法 3:已知 HTTP 进程 PID 时
lsof -Pan -p <uvicorn_pid> -iTCP -sTCP:LISTEN
❌ 不推荐的方法
-
❌ 只看
--port参数 -
❌ 只看
nvidia-smi -
❌ 只从 GPU worker PPID 推端口(vLLM v1 下不可靠)
八、经验总结
一句话版结论:
在
ms-swift + vLLM的多进程架构下,
端口只属于 HTTP Server(Uvicorn),而不属于 GPU worker 或 EngineCore。
三条实用经验:
-
--port只是"期望端口",不是最终端口 -
端口被占用时,Swift 会自动递增而不是失败
-
永远以
Uvicorn running on ...或LISTENsocket 为准
这次问题本质不是"服务没起",而是对多进程推理架构 + 端口绑定关系理解不够清晰 。
记录下来,希望能帮到之后踩同样坑的人。