☰
视频AI中台落地实践:Docker多架构镜像与K8s弹性调度全解析
2026/10/18 3:24:50 网站建设 项目流程

先说个结论:视频AI中台这事儿,真正难的从来不是AI模型本身,而是视频接入、算力调度、镜像交付这一整条链路怎么拧成一股绳。我这大半年都泡在一个基于GB28181/RTSP接入的视频AI中台项目里,从最开始用脚本硬怼10路摄像头,到最后在K8s集群上跑几十路视频流加上多套AI算子,中间踩过的坑、推翻的方案,足够写满好几页笔记。

这篇文章我就以亲历者的身份,把整个项目的架构演进过程掰开揉碎了讲。内容包括:为什么一定要上Docker多架构镜像、GB28181和RTSP两种接入方式在真实项目里怎么取舍、K8s弹性调度到底怎么设计才不会被流量打崩,以及监控告警体系怎么搭才不变成摆设。同时会把埋点、镜像构建、调度参数、告警规则这些具体的实操细节一并放出来,给正在做同类项目或者准备入坑的朋友一些参考。


1. 项目背景与整体思路:为什么是“Docker + K8s + GB28181/RTSP + AI”这套组合

1.1 项目的真实痛点:视频接入冷启动与算力碎片化

先说背景。我所在的团队接到的任务,是把公司分散在不同业务里的视频能力收拢成一个统一的中台。这个中台要能做到三件事:对接不同厂商的摄像头和NVR,把视频流统一汇聚起来,再叠加AI分析能力(比如人脸、车辆、行为识别)输出结构化结果。

听起来挺常规的,但真正动手调研才发现问题比想象的多。

第一,设备接入协议极其混杂。手头有海康威视的摄像头,支持GB28181国标注册,也用RTSP拉流;有第三方厂商的设备只给了RTSP地址,但地址里带了一串动态token,过期就失效;还有一部分老旧的NVR,只能通过GB28181被动注册等待设备主动上报。接入层如果做不好,后面AI分析就是空谈。

第二,算力碎片化严重。公司内部既有x86服务器,也有刚采购的ARM架构机器,还有几块GPU卡插在旧机器上。不同架构、不同指令集、不同加速卡,导致同一份代码打包成镜像后并不能在所有机器上跑。

第三,流量峰值难预测。视频AI中台的特点是:平时业务量平稳,但一旦某个客户搞大型活动,或者某个区域发生异常事件,视频路数会瞬间暴涨。如果按照峰值去采购服务器,平时资源就白白浪费了;如果不按峰值设计,临时扩容又跟不上。

这三个痛点叠加在一起,决定了我们最终的技术选型:用Docker解决镜像交付的一致性,用K8s解决算力调度和弹性伸缩,用GB28181/RTSP双协议解决设备接入的兼容性。

1.2 技术选型的三个关键决策

先说Docker。为什么不是虚拟机,不是裸机部署?核心原因是交付物的一致性。视频接入服务和AI推理服务依赖的环境很复杂,FFmpeg的版本、OpenCV的依赖、GPU驱动、CUDA版本,任何一个不对劲都可能导致服务起不来。用Docker把整个运行环境固化到镜像里,开发、测试、生产环境保持完全一致,这是我们最快能见效的决策。

接着是K8s。说实话,小规模项目直接docker-compose就够了。但视频AI中台一旦上了规模,就需要自动伸缩、故障转移、滚动更新这些能力。K8s的调度模型天然适合这种场景:把视频接入服务、AI推理服务、流媒体网关全都声明成Pod,交给调度器去分配资源。

最后是GB28181和RTSP两条腿走路。很多项目一开始只想支持RTSP,因为拉流简单,一个URL就能搞定。但实际对接中就发现,国标设备(尤其是海康那种大厂设备)在跨网段、跨平台对接时,GB28181注册模式要比RTSP稳定得多。反过来,一些私有化部署的客户又只愿意给RTSP地址。所以接入层必须同时支持两种协议,不能押宝在单一方案上。

1.3 中台总体架构的演进路线

整个中台分成四层:

  • 接入层:负责GB28181注册和RTSP拉流,统一把视频流转成内部格式,这个环节我们用ZLMediaKit做核心流媒体网关,配合FFmpeg做转封装和拉流兜底。
  • 推理层:负责跑AI算子,人脸检测、车牌识别、行为分析等。每个算子被封装成独立的推理服务,通过消息队列订阅视频帧或视频片段。
  • 调度层:K8s集群统一管理所有服务的生命周期,通过HPA(HorizontalPodAutoscaler)做弹性伸缩,通过节点亲和性调度把推理任务分配到带GPU的节点上。
  • 监控层:Prometheus负责采集指标,Grafana做可视化,Alertmanager负责告警。不把监控做好,弹性调度和故障排查都是盲人摸象。

下面我会按照从镜像到接入、再从调度到监控的顺序,把每一层的落地细节都讲清楚。


2. 多架构镜像构建:Docker作为交付基座的技术细节

2.1 为什么必须搞定多架构镜像

先说说多架构镜像这个问题是怎么冒出来的。

项目里有一批ARM架构的服务器,主要用于边缘节点部署,低功耗、机架密度高。但是AI算子里有一部分依赖的第三方库只发布了x86版本,导致同一份Dockerfile在x86上构建没问题,到了ARM上就报错。反过来,有些跑在ARM上的轻量级服务,放到x86上虽然也能跑,却会白白浪费性能。

如果在部署时手动区分架构,逐个节点去构建镜像,那运维成本就失控了。我们急需一套机制:构建一次镜像,推到镜像仓库后,K8s节点拉取时能自动匹配本机架构。这个机制在Docker里叫做多架构镜像(multi-arch image),本质是利用manifest list在镜像名后面关联多个不同平台的镜像变体。

打个比方,平时我们下载安装包时会看到“Windows版”“macOS版”“Linux版”的区分,多架构镜像就像把这三个安装包装进一个下载入口,下载时自动识别你的系统,给你分发对应的版本。K8s节点在拉取镜像时就是这么干的:根据节点的操作系统和CPU架构,自动选择对应的镜像变体。

2.2 用buildx一次构建多平台镜像

构建多架构镜像用得最多的工具是Docker Buildx,它是Docker官方提供的构建插件,底层用BuildKit完成构建。关键命令如下:

# 创建多架构构建器 docker buildx create --name multiarch-builder --driver docker-container --use # 查看可用的构建器 docker buildx ls # 构建并推送多架构镜像 docker buildx build --platform linux/amd64,linux/arm64 \ -t registry.example.com/video-ai-middleware:1.2.0 \ --push .

这里有个注意点:普通的Docker构建器只能构建当前宿主机架构的镜像,必须使用docker-container驱动模式,才能跨架构构建。而且构建过程中如果镜像里有RUN指令执行编译操作(比如编译FFmpeg),构建器需要能模拟目标架构的执行环境,QEMU是默认的模拟方案。构建时如果发现某些平台失败,先检查是不是QEMU没有注册到位:

# 注册binfmt模拟器 docker run --privileged --rm tonistiigi/binfmt --install all

实际项目中,我们的AI推理镜像因为要装CUDA和OpenCV,单次构建可能要跑十几分钟。为了提速,BuildKit的缓存机制要好好用起来。比如把包安装、依赖下载这些不经常变的步骤放在Dockerfile的前面,保证缓存命中;把代码COPY放在后面,这样代码每次更新时不需要重装依赖。

2.3 Dockerfile优化:减少镜像体积与构建时间

多架构镜像每多一个平台,镜像仓库里就会多一份变体,如果镜像本身体积很大,拉取时间会严重影响Pod冷启动速度。我们在优化镜像体积上做了三件事:

第一,尽量用精简基础镜像。能选alpine就不用ubuntu,能选slim版本就不用完整版。对于视频流处理和AI推理,alpine有时候会缺底层库,用debian:slim会更稳妥,但体积也比完整版小很多。

第二,使用多阶段构建。编译阶段用带完整工具链的镜像,运行阶段只拷贝编译产物和必要依赖。

# 编译阶段 FROM golang:1.20 AS builder WORKDIR /app COPY . . RUN CGO_ENABLED=0 GOOS=linux go build -o stream-gateway . # 运行阶段 FROM alpine:3.18 RUN apk add --no-cache ca-certificates tzdata COPY --from=builder /app/stream-gateway /usr/local/bin/stream-gateway ENTRYPOINT ["stream-gateway"]

第三,不要把所有工具都装进同一个镜像。流媒体网关和AI推理服务拆开部署,各用各的镜像,而不是打一个大而全的“全家桶”镜像。

2.4 镜像仓库与拉取策略的配置

多架构镜像推送到镜像仓库后,K8s节点侧需要启用正确的拉取策略。我们用的是阿里云ACR作为镜像仓库,支持多架构manifest,拉取时Docker会自动按节点架构选择对应变体。

在K8s的Deployment中,可以不指定具体的架构,但建议加上镜像拉取策略:

containers: - name: stream-gateway image: registry.example.com/video-ai-middleware:1.2.0 imagePullPolicy: IfNotPresent

如果镜像版本更新频繁,建议把imagePullPolicy设为Always,否则节点可能复用本地缓存的旧镜像。我踩过这个坑:改了镜像tag但忘了改部署配置,结果新代码没生效,排查了半天才发现是拉取策略的问题。


3. GB28181与RTSP接入:让每一路视频都进得来、出得去

3.1 GB28181协议与RTSP协议的定位差异

接入层是整个视频AI中台的咽喉,但很多人对GB28181和RTSP的关系搞不清楚。这里我用自己的经验捋一下。

RTSP(Real Time Streaming Protocol)是一种流媒体控制协议,负责建立和控制流媒体会话,底层一般跑RTP传输音视频数据。可以理解成“你给播放器一个URL,播放器发起请求,服务器回传一路流”,适合点对点拉流。很多摄像头和NVR都支持RTSP,但它的问题是需要知道设备的IP、端口、用户名、密码,而且设备主动推流的能力不太强。

GB28181是国内安防行业的标准协议,定位和RTSP不一样。GB28181定义了设备如何注册到平台、平台如何向设备发起邀请取流、设备如何主动推送告警信息等完整流程。它更像一个“注册-邀请-取流”的会话体系:摄像头主动向SIP服务器注册,平台收到注册后下发指令,设备再通过RTP把视频流推到指定端口。

在项目里这两种协议是并存的,使用场景也分得很清楚:

维度GB28181RTSP
设备发现设备主动注册,平台被动接收需要手动获取URL,无法自动发现
跨网段支持很好,适合跨公网对接受端口映射、NAT影响较大
语音对讲通过SIP信令实现,比较成熟实现需要额外的RTSP扩展支持
拉流稳定性高,设备注册后平台可以反复邀请取流相对简单,但token过期或密码变动容易断流
适用厂商海康、大华等国内厂商普遍支持几乎所有厂商都支持

举个具体的例子:我们对接海康威视摄像头时,如果走GB28181接入,只需要在摄像头管理界面配置SIP服务器地址和设备编号,摄像头注册上来之后,平台就可以维护一个稳定的设备列表。但如果是通过海康的RTSP地址拉流,地址里通常带用户名密码,如果摄像头密码改了,RTSP流就会断,排查起来非常麻烦。

3.2 流媒体网关选型:ZLMediaKit还是Mediamtx

接入层需要有流媒体网关来接收、转发视频流。市面常见的开源方案有ZLMediaKit和Mediamtx(原RTSP-Simple-Server)。我们最终选了ZLMediaKit作为主力,原因有几个:

第一,GB28181支持成熟。ZLMediaKit原生支持GB28181协议的SIP信令处理,设备注册、邀请取流、语音广播和对讲都有对应的接口。对比之下,Mediamtx更偏向RTSP/RTMP/WebRTC这些常见的流媒体协议,对GB28181没有内置支持,需要额外开发信令模块。

第二,ZLM的RTP被动接收能力强。GB28181设备在收到平台的INVITE指令后,会把RTP流推到平台指定的端口,ZLM对TCP/UDP两种RTP传输模式都支持得很好。我们在实际压测中发现,ZLM同时接收几十路不同设备的RTP推流,稳定性和丢包率都控制得不错。

第三,HLS/WebRTC输出能力完善。AI分析后的视频结果需要给前端平台展示,ZLM可以直接把内部流转成HLS或WebRTC流,前端网页不用安装插件就能播放。

Mediamtx也不是一无是处。它部署极其轻量,一个二进制文件搞定,非常适合边缘节点。我们在某些边缘场景下用Mediamtx做RTSP拉流再转WebRTC输出,配合FFmpeg拉流兜底,效率很高。但作为GB28181的核心网关,它扛不起来。

3.3 GB28181接入的完整流程

GB28181接入流程分成几步:

  1. SIP服务启动,监听埠和端口。ZLM会启动一个SIP UDP服务,默认监听5060端口。
  2. 设备侧配置SIP服务器IP、端口、设备ID。我们在海康摄像头的配置页面填入平台侧的SIP信息,摄像头会主动发送REGISTER请求。
  3. 平台侧收到REGISTER请求后,返回200 OK,设备显示已注册。此时设备处于“在线待命”状态。
  4. 平台需要取流时,向设备发送INVITE请求,携带媒体描述(session description),告诉设备把流推到哪个IP的哪个端口。
  5. 设备回复200 OK并发送RTP流到指定端口,ZLM开始收流。
  6. 平台可以对这个通道进行播放、录制、AI分析。

整个流程里最容易出问题的是SIP端口和设备编号配置。SIP协议用UDP传输,如果设备所在网络对UDP有限制,注册就会失败。另外设备编号(20位数字)有严格的编码规则,中心编码、行业编码、类型编码、序号编码都不能乱填,填错了摄像头注册时会返回错误码。

3.4 RTSP拉流与推流:用ffmpeg处理不规范的流

虽然GB28181是主流,但总有客户不按套路出牌。有些第三方设备只提供RTSP地址,有些地址有效期很短,需要定时拉取。这时我们就要做RTSP接入。

我们的接入策略很简单:先用Mediamtx或ZLM内置的拉流代理去拉取RTSP源,再把流统一转换成内部格式。如果设备地址非常规,比如需要携带自定义请求头或者加密认证,就写一个专用的FFmpeg拉流脚本,把流推送回ZLM。

实际项目里RTSP地址的场景比想象中多:

  • 设备地址:rtsp://user:pass@192.168.1.64:554/Streaming/Channels/101,这是海康最经典的地址格式,101代表主码流通道1。
  • 云服务地址:rtsp://domain:554/openurl/xxxx,这种带token的地址必须按客户提供的有效期定时刷新。
  • 本地文件:很多测试场景直接拿一个MP4文件当成RTSP源,用FFmpeg循环推流,方便验证AI算法。

涉及RTSP拉流必须注意一个点:拉流拉不动时不要盲目重试。持续重试会给设备造成额外压力,有些设备还会主动锁定IP。建议设计退避机制,拉流失败后等待几秒再重试,连续失败多次就告警,让人工介入。

3.5 语音对讲、浏览器播放等实战问题

GB28181除了视频,还支持语音对讲,这个在安防场景里是刚需。比如门岗看到一个陌生人在大门外,需要远程喊话。ZLM支持GB28181的语音广播和语音对讲:平台拿到设备的音频RTP流之后可以播放,同时麦克风采集的音频可以推给设备。

具体实现时会遇到音频编码格式不一致的问题。有些设备只支持G.711A,有些支持G.711U,还有的支持AAC。ZLM内部一般能自动转码,但如果前端页面需要播放对讲声音,还要把音频转成WebRTC兼容的格式,这中间涉及音频转码和混音,处理起来比较费劲。

浏览器播放是另一个高频需求。RTSP协议本身浏览器不支持直接播放,所以必须借助流媒体中转。我们的方案是让ZLM把RTSP/GB28181流转成HLS或WebRTC。HLS延迟高但兼容性好,适合监控回放;WebRTC延迟低,适合实时预览和语音对讲场景。


4. K8s弹性调度:算力分配的核心机制

4.1 集群节点规划与部署要点

视频AI中台的K8s集群分为控制面和数据面,我用三台机器做控制面,二十多台机器做数据面。节点类型上进行了区隔:

  • 通用计算节点:跑接入服务、流媒体网关、业务后端,CPU密集。
  • GPU节点:跑AI推理服务,必须带NVIDIA GPU。
  • 边缘节点:有些ARM机器部署在客户机房,跑轻量接入和部分推理算子。

K8s集群的安装部署这里不展开讲,但几个容易忽略的点值得提一下:

第一,容器运行时选型。我们用的containerd,因为K8s从1.24之后默认就移除了dockershim,虽然Docker镜像格式还能兼容,但没必要额外加一层shim。

第二,网络插件选择。视频流服务对网络带宽和延迟很敏感,我们选了Calico的BGP模式,避免VXLAN封装带来的性能损耗。如果节点跨网段通信不多,Flannel也够用,但如果视频流跨节点转发频繁,Calico BGP模式的优势非常明显。

第三,存储规划。视频AI中台一般需要两块存储:一个是NFS或云盘用于持久化业务数据(比如设备列表、告警记录),另一个是大容量存储用于录像文件。

4.2 弹性伸缩:HPA与CPU Throttling

弹性伸缩是K8s最大的优势之一。我们对接入服务和AI推理服务做HPA,核心指标是CPU利用率和QPS。

apiVersion: autoscaling/v2 kind: HorizontalPodAutoscaler metadata: name: ai-inference-hpa spec: scaleTargetRef: apiVersion: apps/v1 kind: Deployment name: ai-inference minReplicas: 3 maxReplicas: 20 metrics: - type: Resource resource: name: cpu target: type: Utilization averageUtilization: 70

但HPA不是万能的,这里最大的坑是CPU Throttling。

K8s默认用CFS(Completely Fair Scheduler)来做CPU配额限制。当一个Pod设置了CPU limit,比如2核,K8s会在每个CFS周期内限制Pod最多只能使用2核的时间片。如果Pod的CPU实际使用量超过了limit,Pod里的进程会被强制节流,导致线程被迫暂停。这时候从外部看CPU利用率不高,但服务响应变慢了,这就是典型的CPU Throttling。

我们在AI推理服务上遇到过:开了HPA,CPU利用率还没触发扩容,但推理延迟已经飙升。排查后发现是CPU limit太小,导致Pod内的进程被持续节流。解决办法有两个:

  1. 调高CPU limit,或者直接不设limit,让Pod在节点空闲时可以用满节点CPU。不设limit有风险,一个异常Pod可能把整台机器的CPU打满,但对内部集群来说,可以通过命名空间资源配额来兜底。
  2. 适当调整K8s的CFS周期参数。默认CFS周期是100ms,如果Pod每周期只能跑5ms,持续节流是必然的。调大周期到200ms或者500ms,能让Pod在单周期内跑更久,减少节流频率。

实测下来,对推理服务不设CPU limit,改用节点级别的资源配额做软限制,反而比硬limit+节流更稳定。但前提是你需要监控配合,不然一个跑飞的任务会拖垮整个节点。

4.3 节点亲和性、污点与GPU调度

视频AI中台的调度策略必须精细控制,不能全靠K8s默认调度器随机分配。

首先,AI推理服务必须固定调度到GPU节点。实现方式有两种:

  • 给GPU节点打标签,然后利用nodeSelector。
  • 更推荐用nodeAffinity,支持更灵活的匹配规则。

其次,GPU节点的调度需要用设备插件机制。NVIDIA官方提供了nvidia-device-plugin,安装后节点会暴露nvidia.com/gpu这个资源,你可以像申请CPU、内存一样在Pod里声明GPU数量。

resources: limits: nvidia.com/gpu: 1

还有污点(Taint)与容忍(Toleration)的配合。GPU节点打上污点,只允许带容忍度的推理Pod调度上去,防止普通业务Pod把GPU节点CPU占满影响推理。

kubectl taint nodes gpu-node-01 gpu=true:NoSchedule

然后在推理服务的Deployment中加入toleration,这样只有推理Pod能调度到GPU节点上。

4.4 多架构节点的统一调度策略

我们的集群里同时有x86和ARM节点,K8s版本从1.25开始支持基于节点的架构标签匹配镜像。在Deployment里可以声明:

spec: template: spec: affinity: nodeAffinity: requiredDuringSchedulingIgnoredDuringExecution: nodeSelectorTerms: - matchExpressions: - key: kubernetes.io/arch operator: In values: - arm64

如果某个服务只支持ARM架构(比如某些在ARM上做过指令集优化的视频编码库),就给它加NodeAffinity只允许调度到ARM节点。反过来,如果某个服务两架构都支持,不必加亲和性,多架构镜像就能自动选择。

但这里有个管理上的风险:如果同一套服务的多架构镜像,在x86和ARM上表现得不太一样(比如推理精度或性能差异),建议用两套Deployment分别管理,而不是共用一套Deployment。否则滚动更新时会同时更新两个架构的实例,出了问题很难定位。


5. 监控与告警体系:让K8s集群“有话好好说”

5.1 Prometheus + Grafana + node-exporter三件套

没有监控的K8s集群是聋子和瞎子。我们的监控体系是基于Prometheus + Grafana + node-exporter搭起来的。

  • node-exporter:在每个节点上以DaemonSet方式运行,采集节点级别的CPU、内存、磁盘、网络指标。
  • Prometheus:负责抓取和存储指标,支持K8s的服务发现,能自动发现新增的Pod并抓取指标。
  • Grafana:负责可视化展示,直接用Prometheus作为数据源,配好看板。
  • Alertmanager:Prometheus的告警组件,负责把告警推送到钉钉、企业微信或者邮件。

部署方式很简单,用Helm一键安装:

helm repo add prometheus-community https://prometheus-community.github.io/helm-charts helm install kube-prometheus-stack prometheus-community/kube-prometheus-stack --namespace monitoring

这个chart会顺手把node-exporter、Alertmanager、Grafana一并装上,省去手工配置的时间。

5.2 磁盘告警规则配置

视频AI中台最容易爆的就是磁盘,因为视频录像是存储大杀器。摄像头如果7x24小时录,一个码率4Mbps的画面一天就要占掉约43GB,几十路视频一个月就是几TB的量级。

我们的磁盘告警规则分三级:

  • 磁盘使用率超过70%,发出警告,通知运维关注。
  • 磁盘使用率超过85%,发出严重告警,需要在24小时内清理或扩容。
  • 磁盘使用率超过95%,发出紧急告警,立刻干预,否则服务可能写入失败。

在Prometheus中配置告警规则:

groups: - name: node-disk-alerts rules: - alert: DiskUsageWarning expr: (1 - (node_filesystem_avail_bytes / node_filesystem_size_bytes)) * 100 > 70 for: 5m labels: severity: warning annotations: summary: "{{ $labels.instance }} 磁盘使用率超过70%" - alert: DiskUsageCritical expr: (1 - (node_filesystem_avail_bytes / node_filesystem_size_bytes)) * 100 > 85 for: 10m labels: severity: critical annotations: summary: "{{ $labels.instance }} 磁盘使用率超过85%"

注意,添加for: 5m或10m是为了避免磁盘短时抖动导致误报。磁盘在写满前阶段经常有波动,比如日志轮转、录像文件切换,会瞬间释放很多空间。如果一超过70%就告警,告警风暴能把人烦死。

5.3 监控告警之外的进阶经验

监控系统本身也会成为瓶颈。Prometheus默认的本地TSDB存储不擅长长期保存大量历史数据,如果视频中台的监控指标太多,建议启用远程存储(Thanos或VictoriaMetrics)。

另外,告警规则的阈值不是一成不变的。视频AI中台在业务高峰期CPU和内存指标会普遍偏高,如果告警阈值定得太死,会频繁误报。我建议先跑两周基线数据,根据业务实际表现动态调整告警阈值,再逐渐收窄告警范围。


6. 实操踩坑与排查速查:我踩过的那些雷

6.1 GB28181注册失败、403等问题的排查思路

GB28181接入调试是视频项目中最耗时、最崩溃的环节。这里整理几个高频问题的排查思路:

现象一:设备注册不上,抓包看到SIP 403 Forbidden。

403在GB28181里通常表示认证失败。排查思路:

  1. 确认设备侧注册密码与平台侧一致。GB28181采用Digest认证,密码错一个字符都不行。
  2. 确认设备编码和平台侧配置的设备列表匹配。如果平台没有预先录入设备编码,认证通过后也可能被拒绝。
  3. 确认SIP服务器的realm配置和设备侧一致。有的设备对realm字段很敏感。

现象二:设备显示已注册,但邀请取流时设备没有响应。

大概率是平台侧RTP接收端口没开放,或者设备无法访问该端口。GB28181取流时,平台在INVITE的SDP里会告诉设备往哪个IP:端口推流。如果该端口被防火墙拦截或者网络NAT有问题,设备推不出流。排查时需要抓包确认INVITE和200 OK的交互过程。

现象三:取流后视频花屏或卡顿。

可能是RTP传输模式不匹配。GB28181支持TCP和UDP两种RTP传输模式,如果设备默认发送UDP,但平台只监听了TCP端口,就会丢包。配置ZLM时,建议把TCP和UDP两个端口都监听,并做好兼容。

6.2 RTSP拉流、浏览器播放的常见坑

RTSP拉流最常见的坑是:源地址有效,但FFmpeg拉不起来,或者拉起来之后延迟越来越大。

第一,确认RTSP传输协议。有些设备默认用UDP传输RTP,但UDP在弱网环境下丢包严重,导致花屏。FFmpeg可以强制走TCP:

ffmpeg -rtsp_transport tcp -i rtsp://xxx -c copy output.mp4

第二,确认解码格式兼容。FFmpeg版本过旧可能导致某些新设备的H.265流解不出来。保持FFmpeg版本更新,必要时升级到最新稳定版。

第三,浏览器播放RTSP没有直接方案,必须转成浏览器支持的协议。我们的做法是:在接入层就统一把RTSP流转成HLS或WebRTC,通过ZLM或Mediamtx做协议转换,前端用video.js播放HLS或直接用WebRTC连接。

6.3 Docker Desktop启动失败、K8s集群中Pod卡在ContainerCreating等

Docker Desktop在Windows上经常报“virtualization support not detected”,这个报错一般是因为没有开启Windows的Hyper-V或WSL2虚拟化。解决办法是在“控制面板 -> 程序 -> 启用或关闭Windows功能”里勾选“Hyper-V”和“适用于Linux的Windows子系统”,然后重启电脑。如果还是不行,检查BIOS里是否开启了虚拟化(Intel VT-x或AMD-V)。

K8s集群中Pod卡在ContainerCreating的原因很多,但视频中台最常见的两个原因:

  1. 镜像拉取失败。多架构镜像在节点上找不到对应架构,或者镜像仓库认证失败。
  2. 存储卷挂载失败。Pod需要挂载NFS存储,但NFS服务未启动,或者挂载点权限不对。

排查方法:

kubectl describe pod <pod-name> kubectl logs <pod-name> --previous

describe能看到Event里的失败原因,logs能看程序启动日志。多数问题在这两步就能定位。


7. 个人实操经验总结与后续演进方向

项目做到现在,最大的体会是:视频AI中台本质上是一个工程问题,而不是算法问题。算法模型再强大,如果接入层撑不住几十路视频流,调度层扛不住峰值流量,监控层看不到风险,整个系统就像一座地基不牢的高楼,盖得越高越危险。

从Docker多架构镜像到K8s弹性调度,从GB28181/RTSP接入到Prometheus监控告警,这是一整套体系,每个环节都必须打通,缺一环就掉链子。如果让我给刚开始做类似项目的朋友一个建议,我会说:不要一上来就追求大而全的中台,先从一条链路跑通开始。比如先接好一路视频,走完“摄像头 -> GB28181/RTSP接入 -> 流媒体网关 -> AI推理 -> 监控呈现”这条完整链路,再逐步横向扩展。链路跑通了,扩容只是增加资源的问题;链路没跑通,堆再多机器也白搭。

还有一个很实用的建议:所有配置、脚本、规则,都要版本化管理。Dockerfile、K8s YAML、Prometheus告警规则,全部放进Git仓库。我们有过一次教训:某台GPU节点的驱动升级后,AI推理Pod起不来,查了半天,发现是部署时的镜像tag和当前环境的CUDA版本不匹配。因为镜像tag没有及时更新到脚本里,才导致了这个低级问题。后来我们把所有部署配置纳入代码评审和自动化发布,这类问题就少了很多。

后续这个中台还有几个方向可以继续演进:把AI算子标准化成可以通过消息队列异步调用的服务,进一步降低算子之间的耦合;探索在ARM边缘节点上运行更轻量的推理模型,把一部分AI计算下沉到边缘;再就是完善告警自愈能力,让K8s在出现异常时能自动处理一部分故障,而不是每次都要人工介入。

做这种级别的项目,没有什么一步到位的银弹,靠的是把每一层的基础都打好,然后一层层往上搭。希望这篇文章里那些实操细节和踩过的坑,能让你少走一些弯路。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询