外观
系统基础速查
使用建议
本文是一张「排障速查卡」:每节按 场景 → 命令 → 怎么读输出 组织。遇到「服务起不来、GPU 打不开、延迟莫名高」这类问题时先翻对应小节,再回到正文链接里补系统知识。
Linux 常用命令速查
| 场景 | 命令 | 怎么看输出 |
|---|---|---|
| 看进程在不在、占用多少 | ps aux | grep python | 第 3 列 CPU、第 4 列 RSS(内存),看 PID 用于后续 kill 或 top -p。 |
| 实时看资源占用 | top(或 htop) | %CPU/%MEM 高的进程;load average 超核数说明排队。top -p PID 单看目标进程。 |
| 看内存还剩多少 | free -h | available 才是真可用;buff/cache 可回收。swap 飙升 = 内存告急。 |
| 看磁盘还剩多少 | df -h | Use% 接近 100% 会引发写失败;模型仓库目录单独挂载要重点看。 |
| 看端口谁在监听 | ss -tlnp(旧系统用 netstat -tlnp) | LISTEN 行才是服务就绪;PID/Program 列确认是不是自己的进程。 |
| 看某个端口被谁占 | lsof -i :8000 | 排查「端口被占用」时直接定位到进程,然后决定 kill 或换端口。 |
| 看进程打开了哪些文件 | lsof -p PID | 定位「文件被删了但进程还握着句柄」导致的磁盘不释放问题。 |
| 看日志尾部 | tail -f /path/to/log | 实时跟踪错误;grep -i error 过滤关键字。 |
| 后台运行服务 | nohup cmd > log 2>&1 & | 断开 SSH 后仍运行;日志重定向避免占用终端。 |
容器与 Docker
镜像构建最佳实践(直接影响镜像体积、构建速度与安全性):
- 分层与缓存:
Dockerfile每一条RUN/COPY生成一层,变更靠前的层会使后续缓存全部失效。把很少变化的依赖安装放前面(如apt-get install、pip install),把频繁变化的源码COPY放最后。 - 合并指令:多个
RUN用&&合并,减少层数(如RUN apt-get update && apt-get install -y curl)。 - 非 root 运行:
USER nonroot(或创建专用用户),生产容器绝不用 root——这是镜像安全扫描的头号告警项。 - 多阶段构建:
FROM ... AS builder编译、FROM ...只拷贝产物,把编译工具链留在构建阶段。 - 指定基础镜像 tag(
python:3.11-slim),不要用latest。
docker run 常用参数:
| 参数 | 作用 | 例子 |
|---|---|---|
-d | 后台运行 | docker run -d -p 8000:8000 my-svc |
-p 宿主机:容器 | 端口映射 | -p 8000:8000 |
-v 宿主机:容器 | 挂载目录(模型权重、日志) | -v /models:/models |
--gpus all | 透传全部 GPU(需 nvidia-container-toolkit) | --gpus '"device=0,1"' 指定卡 |
--name | 容器命名 | --name triton-server |
--restart=unless-stopped | 异常退出自动重启 | 生产服务必备 |
--shm-size | 加大共享内存(多进程/NCCL 需要) | --shm-size 1g |
--network host | 用宿主机网络(高性能/调试场景) | 注意端口冲突 |
docker compose(docker-compose.yml):用 YAML 声明「服务 + 网络 + 卷」多容器编排。常用命令:docker compose up -d(启动)、docker compose logs -f(看日志)、docker compose down(停止)。推理服务 + Prometheus + Grafana 一套起在 compose 里是最快的本地模拟环境。
排障场景:容器内看不到 GPU 现象:宿主机
nvidia-smi正常,容器里报CUDA driver version is insufficient或找不到设备。 排查链:nvidia-smi(驱动 OK?)→ 宿主装了nvidia-container-toolkit并sudo nvidia-ctk runtime configure --runtime=docker后systemctl restart docker?→docker run --gpus all nvidia/cuda:... nvidia-smi验证。详见 NVIDIA 官方安装文档。
Kubernetes 核心概念
| 对象 | 一句话定位 | 与推理服务的关系 |
|---|---|---|
| Pod | 最小调度单元,一个或多个容器共享网络/IP | 模型服务的实例,Pod 内通常「一个推理进程」 |
| Deployment | 声明式管理 Pod 副本数(ReplicaSet)与滚动更新 | 推理服务无状态副本的标准载体 |
| Service | 稳定的集群内访问入口(ClusterIP/DNS) | 负载均衡到各 Pod,配合 kubectl port-forward 调试 |
| Ingress | 集群外部的 HTTP(S) 入口路由 | 对外暴露 /v1/models 等 API,可挂 TLS |
| HPA(Horizontal Pod Autoscaler) | 按 CPU/自定义指标自动扩缩副本 | 按 QPS 或 GPU 利用率扩缩推理副本 |
| ConfigMap / Secret | 配置与密钥注入 | 模型路径、HF Token、环境变量 |
| PVC | 持久化存储声明 | 挂载模型权重避免每次冷启动下载 |
资源请求与限制(resources.requests/limits)——推理服务最容易踩的坑:
yaml
resources:
requests: { cpu: "4", memory: 16Gi, nvidia.com/gpu: 1 }
limits: { cpu: "4", memory: 24Gi, nvidia.com/gpu: 1 }requests决定调度与 QoS(不设 requests 的 Pod 是 BestEffort,最容易被驱逐);limits防止进程吃光节点内存触发 OOMKill。- 给推理容器
limits.cpu与requests.cpu相等(Guaranteed QoS)可避免被抢占,代价是预留。 - GPU 用
nvidia.com/gpu声明(设备插件),GPU 的 requests 必须等于 limits。
健康探针——决定「流量切给它还是摘掉它」:
| 探针 | 用途 | 推理场景建议 |
|---|---|---|
livenessProbe | 进程僵死时重启容器 | 探测 /healthz,失败自动重启 |
readinessProbe | 就绪后才接流量 | 模型加载完成前应返回失败,防止流量打进「模型还没起来」的副本 |
startupProbe | 慢启动保护 | 大模型加载要几十秒,用 startup 放宽,避免 liveness 误杀 |
排障场景:服务起来了但请求 503/Connection Refused 先
kubectl get pods看状态 →kubectl logs看模型是否加载完 → 查 readinessProbe 路径与返回码;Pod 反复 CrashLoop 则先kubectl describe pod看 Events 里的 OOMKilled/ImagePullBackOff。
网络基础
HTTP 状态码速记(排障第一直觉):
| 状态码 | 含义 | 常见触发点 |
|---|---|---|
| 200 | 成功 | 正常推理响应 |
| 4xx | 客户端错误 | 400 参数错、401/403 鉴权、404 路径、429 限流 |
| 5xx | 服务端错误 | 500 推理异常、502 网关连不上后端、503 服务未就绪、504 超时 |
- 502/503 在推理场景:先分清是「负载均衡器连不上 Pod」(看 Ingress/网关日志)还是「Pod 内服务没就绪」(看探针)。
- 429(限流):动态批处理积压或副本数不足时常见,配合
Retry-After头重试。
TCP 三次握手与超时:
- 三次握手(SYN → SYN-ACK → ACK)建立连接;抓包可用
tcpdump -i any port 8000观察握手是否完成。 - 常见超时参数:客户端连接超时
connect_timeout、服务端处理超时read_timeout、总体超时total_timeout。推理服务建议分别设置:连接 5s、首字节 60s+(模型推理可能慢)、总超时按最大单请求时长放大。 - 连接池与 keep-alive:推理是高频小请求场景,务必复用连接(HTTP keep-alive、gRPC channel 复用),否则每次请求都在付握手成本——
ab -k(keep-alive)与不带-k压测的差距往往就是性能瓶颈。
gRPC:基于 HTTP/2 的高性能 RPC,适合吞吐优先的推理(Triton 的 gRPC 端口就是典型)。特征:二进制协议、流式支持、连接长期复用。选型上「延迟敏感 + 内部系统」用 gRPC,「对外 + 简单调试」用 REST。
进程 / 线程 / 异步模型
| 概念 | 定义 | 对推理服务的影响 |
|---|---|---|
| 进程(Process) | 资源隔离的执行单元,各占独立内存空间 | 多进程部署(如 Gunicorn/Uvicorn workers)可并行利用多核,但每份都要加载一份模型(显存翻倍) |
| 线程(Thread) | 进程内的执行流,共享内存 | Python 中受 GIL 限制 |
| GIL(全局解释器锁) | CPython 同一时刻只允许一个线程执行字节码 | CPU 密集型 Python 代码多线程不加速;但推理的密集计算发生在 C/CUDA 库内部,会释放 GIL,所以「Python + 推理引擎」场景多线程仍有效 |
| 协程(Coroutine) | 用户态协作式调度,单线程内切换 | FastAPI 的 async def、asyncio 事件循环模型,适合 IO 密集(网络等待、流式转发) |
| 事件循环(Event Loop) | 单线程管理 IO 事件回调 | LLM 流式输出场景,事件循环让一个进程同时服务大量在途流 |
关键结论(推理服务并发模型)
- CPU 密集的模型计算 → 交给推理引擎(释放 GIL 的 C/CUDA 代码),用线程/批处理榨干硬件。
- IO 密集的请求处理 → 用 asyncio/FastAPI async 处理并发,一个 worker 扛大量长连接。
- 需要多核并行时再上多进程,但要算清「进程数 × 每进程模型显存」是否放得下。
GPU 环境
nvidia-smi 输出解读:
+-----------------------------------------------------------------------------+
| NVIDIA-SMI 550.54.15 Driver Version: 550.54.15 CUDA Version: 12.4 |
|-------------------------------+----------------------+----------------------+
| GPU Name Persistence-M| Bus-Id Disp.A | Volatile Uncorr. ECC |
| Fan Temp Perf Pwr:Usage/Cap| Memory-Usage | GPU-Util Compute M. |
|===============================+======================+======================|
| 0 A100-SXM4-40GB On | 00000000:00:04.0 On | 0 |
| 0% 47C P0 68W / 400W | 3640MiB / 40960MiB | 44% Default |
+-----------------------------------------------------------------------------+| 列 | 含义 | 排障关注 |
|---|---|---|
Driver Version / CUDA Version | 驱动版本 / 驱动支持的最高 CUDA | 驱动「支持」12.4 ≠ 运行时装了 CUDA 12.4,容器内报 libcudart 错先查这层 |
Memory-Usage | 已用/总显存 | GPU-Util 低但显存占满 = 模型卡在加载/泄漏 |
GPU-Util | SM 利用率 | 持续接近 0 但服务有请求 = CPU/带宽瓶颈或没真正用 GPU |
Temp / Pwr | 温度 / 当前功耗 | 撞功耗墙或温度墙会性能骤降 |
Ecc Errors | 显存 ECC 错误计数 | 持续增长是硬件故障信号 |
CUDA / cuDNN 版本匹配(部署排障第一大坑):
- 三层要匹配:NVIDIA 驱动 → CUDA 运行时 → 推理框架(PyTorch/TensorRT 等)编译时用的 CUDA 版本。
- 驱动是「向下兼容」的:驱动 ≥ 框架需要的 CUDA 版本即可。查法:
nvidia-smi看驱动支持的 CUDA 版本,python -c "import torch; print(torch.version.cuda)"看 PyTorch 绑定的版本。 - 首选容器方案:直接用官方镜像(
nvidia/cuda:12.4-runtime-ubuntu22.04、NGC 的 PyTorch/Triton 镜像),框架与 CUDA 版本已配对,别再手工装。
驱动与容器(nvidia-container-toolkit):容器要访问 GPU 必须装 toolkit 并把运行时配置给 Docker/containerd,见上文容器小节链接。
显存泄漏排查:
| 场景 | 命令/手段 | 怎么判断 |
|---|---|---|
| 显存被吃光但不释放 | nvidia-smi 连续观察 Memory-Usage | 服务空闲时显存还在涨 = 泄漏(推理引擎/框架 bug 常见) |
| 定位谁在占显存 | nvidia-smi --query-compute-apps=pid,used_memory --format=csv | 看到残留的僵尸 Python 进程,kill 之 |
| 容器内看不到进程占用 | nvidia-smi 只显示当前容器 | 用 --display 或宿主直接 fuser -v /dev/nvidia* 排查 |
| PyTorch 缓存导致误报 | torch.cuda.empty_cache() | 显存被 PyTorch 缓存池占着不释放,属正常,压力测试才见真峰值 |
性能排查命令
| 工具 | 看什么 | 推理服务场景怎么用 |
|---|---|---|
vmstat 1 | 进程/内存/IO/CPU 总览 | r(运行队列)长期大于核数 = CPU 排队;si/so 非零 = 内存吃紧开始换页 |
iostat -x 1 | 磁盘吞吐与利用率 | 模型冷启动/权重加载慢时看 %util 与 await;await 高 = 磁盘慢 |
pidstat 1 | 单进程 CPU/内存 | 定位是哪个进程在吃 CPU(如 tokenizer 线程 vs 推理线程) |
perf top / perf record | 热点函数采样 | Python 服务先 py-spy top --pid 看 Python 栈,底层热点再上 perf |
flamegraph(火焰图) | 调用栈耗时可视化 | 从 perf/py-spy 数据生成,一眼看出「时间花在哪个调用栈」 |
sar -n DEV 1 | 网卡流量与错误 | 判断是否网络成为吞吐瓶颈 |
nload / iftop | 实时流量与连接 | 排查大模型流式输出的带宽占用 |
排障思路:延迟高先分层 客户端耗时 = 网络往返 + 服务端排队 + 推理计算 + 后处理。用
time curl分开测网络;用服务端指标(如 Triton/vLLM 的请求级指标)看「排队 vs 执行」占比;用火焰图看执行内部热点。分层定位比在单一层面瞎猜高效得多。