llm-startup-notes

LLM 服务一键启动与端口转发架构

记录于 2026/06/01


一、背景与目标

场景:本地 Mac → SSH 免密登录 lab-server(内网服务器)→ 在 lab-server 上启动 Qwen3-32B vLLM 服务 → 通过双层 autossh 隧道暴露到公网。

目标:写一个本地启动脚本,一键完成:

  1. 远程启动 LLM 服务(后台)
  2. 本地监控启动状态(后台子进程)
  3. 启动成功后自动开启端口映射
  4. 主脚本立即退出,不占用终端
  5. 统一停止脚本(关闭 LLM + 隧道)

二、架构图

本地 Mac                      lab-server(内网)
+--------+   SSH   +------------------+    vLLM (port 8001)
| 客户端 | <------ | autossh 隧道     | <------------------+
+--------+         | L:18001 → 8001   |                  |
                   +------------------+                  |
                          | SSH 远程端口转发              |
                          v                               |
                   +--------------------+                 |
                   | llm.litearch.cn   | <---------------+
                   | R:18001 → :18001  |
                   +--------------------+
                          |
                          v 公网可访问

三、已实现的脚本

文件 作用
llm_start.sh 启动 LLM + 隧道的总脚本
llm_stop.sh 停止 LLM + 隧道的总脚本
tunnel_start.sh 仅启动 autossh 隧道(手动)
tunnel_stop.sh 仅停止 autossh 隧道(手动)
status_llm.sh 查看 LLM 进程、端口、健康状态、远程日志

四、核心问题排查记录

问题 1:vLLM 找不到命令

  • 现象exec: vllm: not found
  • 原因:SSH 非交互式命令不加载 conda 环境
  • 解决:使用绝对路径 /root/miniconda3/bin/vllm

问题 2:网络不通,模型下载失败

  • 现象Network is unreachable → huggingface.co:443
  • 原因:SSH 远程命令不加载 .bashrcproxy_on 函数未定义
  • 排查方法
    ssh lab-server "type proxy_on"                  # not found
    ssh lab-server "grep -r proxy_on ~/.bash*"      # 发现在 .bashrc 里
    
  • 解决:直接在 SSH 命令里设置代理环境变量:
    export https_proxy=http://kb314314.asuscomm.com:8007
    export http_proxy=http://kb314314.asuscomm.com:8007
    export all_proxy=http://kb314314.asuscomm.com:8007
    

问题 3:服务已启动但端口未监听

  • 原因:vLLM 加载模型需要时间,未加载完时端口未打开
  • 解决:monitor 子进程每 10s 通过 curl http://127.0.0.1:8001/health 检查,直到返回 200 才启动隧道

问题 4:测试模式调试

  • 解决:加了 --test 参数,所有步骤前台执行,实时显示日志
    ./llm_start.sh --test
    

五、关键实现细节

5.1 启动流程(llm_start.sh)

cleanup_existing()  → 清理旧进程
start_remote_llm()  → SSH 设置代理 + nohup 启动 vLLM
run_monitor()       → 后台轮询 /health,超时 600s
launch_tunnel()     → 健康检查通过后启动双层 autossh

5.2 非交互式 SSH 的坑

  • SSH 远程执行 ssh host "cmd" 走的是非交互式 non-login shell
  • .bashrc 里有 [ -z "$PS1" ] && return,非交互式直接 return
  • .bash_profile / .profile 才会被加载
  • 教训:不要假设远程函数在 SSH 里可用,直接设置环境变量最稳妥

5.3 健康检查机制

ssh lab-server "curl -sf http://127.0.0.1:8001/health"
  • vLLM 的 /health 端点在模型加载完成后才响应
  • 轮询间隔 10s,超时 600s(10分钟)
  • 成功后才启动 autossh 隧道

5.4 日志位置

日志 路径
远程 vLLM 日志 ~/llm_server.log(服务器)
本地启动日志 ~/.llm/startup.log
隧道1日志 ~/.llm/tunnel1.log
隧道2日志 ~/.llm/tunnel2.log
Monitor PID ~/.llm/monitor.pid
隧道 PID ~/.llm/tunnel.pid.{1,2}

六、代理信息

  • 代理地址:http://kb314314.asuscomm.com:8007
  • 代理函数 proxy_on 定义在 ~/.bashrc(非交互式 SSH 不可用)
  • 直接设置 https_proxy / http_proxy / all_proxy 环境变量即可

七、使用方法

# 启动(立即返回)
./llm_start.sh

# 查看状态
./status_llm.sh

# 停止
./llm_stop.sh

# 调试模式(前台运行,实时日志)
./llm_start.sh --test

八、后续可优化方向

  1. 隧道断开后自动重连(autossh 已有 -M 0 监控)
  2. LLM 启动失败时自动发通知(邮件/钉钉)
  3. 支持多卡(CUDA_VISIBLE_DEVICES 参数化)
  4. 模型加载进度从日志中解析百分比显示

转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可以在下面评论区评论,也可以邮件至 kipleyarch@gmail.com
Archive PDF预览 PPTX Obsidian