一次 vLLM / Swift 多模态服务端口“消失”的完整排查记录

记录一次在使用 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。

三条实用经验:

  1. --port 只是"期望端口",不是最终端口

  2. 端口被占用时,Swift 会自动递增而不是失败

  3. 永远以 Uvicorn running on ...LISTEN socket 为准


这次问题本质不是"服务没起",而是对多进程推理架构 + 端口绑定关系理解不够清晰

记录下来,希望能帮到之后踩同样坑的人。

相关推荐
桌面运维家20 小时前
IDV云桌面vDisk机房部署方案模板特性解析
java·开发语言·devops
飞翔的SA20 小时前
从6.75%到100%!大模型Function Calling终极方案:Harness工程如何驯服
开发语言·ai·llm·harness
耿雨飞20 小时前
Python 后端开发技术博客专栏 | 第 09 篇 GIL 深度解析与并发编程实战 -- 多线程、多进程、协程的选型
开发语言·python
Stark-C20 小时前
NAS音乐必备神器,全平台音乐收割机!极空间部署『Go Music DL』
开发语言·后端·golang
Ulyanov20 小时前
像素迷宫:路径规划算法的可视化与实战
大数据·开发语言·python·算法
枫叶丹421 小时前
【HarmonyOS 6.0】ArkWeb PDF预览回调功能详解:让PDF加载状态可控可感
开发语言·华为·pdf·harmonyos
小陈工21 小时前
数据库Operator开发实战:以PostgreSQL为例
开发语言·数据库·人工智能·python·安全·postgresql·开源
耿雨飞21 小时前
Python 后端开发技术博客专栏 | 第 07 篇 元类与类的创建过程 -- Python 最深层的魔法
开发语言·python
qq_120840937121 小时前
Three.js AnimationMixer 工程实战:骨骼动画、剪辑切换与时间缩放
开发语言·javascript·ecmascript
Dxy123931021621 小时前
Python在图片上画多边形:从简单轮廓到复杂区域标注
开发语言·python