上一篇 下一篇 分享链接 返回 返回顶部

如何在Ubuntu系统的香港服务器中部署Prometheus监控直播推流节点的健康状态?

发布人:Minchunlin 发布时间:2025-09-23 08:42 阅读量:828


周五晚上 10 点,我坐在香港葵涌的机房里,客服在工单里说:“主播反馈间歇性卡顿,RTMP 推流不稳,偶发 1~2 秒掉帧。”我盯着几台推流节点的 nload 和 htop,直觉告诉我问题不在平均带宽,而在“瞬时抖动”和“会话层健康”。临时的 curl 和 ffmpeg -re 压测不够系统,于是我决定当天夜里把 Prometheus 系监控拉起来——把每一次 RTMP 握手、每一次会话的比特率波动、每一次 TCP 重传,都留痕成可回放的证据。这篇文章就是那晚到第二天清晨的完整实操记录与复盘。

1. 场景与目标

业务背景:香港(HK)机房承载国内外主播 RTMP 推流,边缘节点使用 SRS(也可替换 Nginx-RTMP),上游转发到主控 CDN。问题集中在瞬时卡顿和会话不稳定。

监控目标:

  • 节点维度:CPU/内存/磁盘/网卡、队列丢包、TCP 重传、连接数、文件句柄、conntrack 使用率。
  • 会话维度:RTMP 在线发布数、每路流入比特率、音视频时延、推流成功率、握手失败率、断开原因。
  • 网络维度:端口连通(1935/TCP、API/HTTP)、对外丢包/时延的黑盒探测。
  • 告警策略:会话数突降、比特率突降/波动过大、节点负载异常、端口不可达、时钟漂移、磁盘写放大(TSDB 压力)。

2. 基础环境与参数(我现场用到的)

参数
OS Ubuntu Server 22.04 LTS(5.15 内核)
服务器 2×Intel Xeon Silver 4210R / 64G RAM / 2×960G NVMe(RAID1)
网卡 2×10GbE(Bonding LACP,MTU 9000,内网直连汇聚)
带宽 BGP 线路(HK 出口),突发 8–10Gbps
Prometheus 2.x(建议 ≥2.45,命令行基本一致)
Node Exporter 1.x(建议 ≥1.6)
Blackbox Exporter 0.2x(建议 ≥0.25)
Alertmanager 0.2x
可选 Grafana 10.x
推流服务 SRS 5.x(启用 HTTP API),或 Nginx-RTMP(需扩展统计接口)

注:版本号非强绑定,保持在近两年稳定大版本即可。

3. 拓扑与命名规划

[主播端] --RTMP--> [HK 推流节点A/B/C ...] --内部--> [主控/CDN]
                            |                   ^
                            v                   |
                    Node/Blackbox/SRS Exporters |  
                            |                   |
                         [Prometheus] --(Alertmanager)--> 值班群/电话/飞书
                                       \--(Grafana)--> 看板

命名约定(强烈建议统一):

  • 机房:hk01
  • 节点:hk01-live-rtmp-01/02/...
  • 标签:region="hk", role="rtmp-edge", env="prod"

4. 系统准备与调优

4.1 基础包与时钟

sudo apt update && sudo apt install -y chrony curl wget tar jq ufw
sudo timedatectl set-timezone Asia/Hong_Kong
sudo systemctl enable --now chrony

4.2 内核与网络参数(/etc/sysctl.d/99-live.conf)

net.core.rmem_max = 134217728
net.core.wmem_max = 134217728
net.ipv4.tcp_rmem = 4096 87380 134217728
net.ipv4.tcp_wmem = 4096 65536 134217728
net.ipv4.tcp_congestion_control = bbr
net.core.somaxconn = 4096
net.ipv4.tcp_max_syn_backlog = 8192
net.ipv4.ip_local_port_range = 10000 65535
net.netfilter.nf_conntrack_max = 262144
net.ipv4.tcp_fin_timeout = 15
net.ipv4.tcp_tw_reuse = 1
fs.file-max = 2097152

sudo sysctl --system

4.3 文件句柄与进程限制(/etc/security/limits.d/99-live.conf)

  • soft nofile 1048576
  • hard nofile 1048576

4.4 UFW 端口策略(最小开放)

sudo ufw allow 22/tcp
sudo ufw allow 9090/tcp   # Prometheus
sudo ufw allow 9100/tcp   # node_exporter
sudo ufw allow 9115/tcp   # blackbox_exporter
sudo ufw allow 9093/tcp   # Alertmanager
sudo ufw allow 1935/tcp   # RTMP
sudo ufw allow 1985/tcp   # SRS HTTP API
sudo ufw enable

5. 安装 Prometheus(主控机)

我习惯把 Prometheus 放在独立节点(NVMe + 充足 IOPS),避免采集与存储互相干扰。

5.1 用户与目录

sudo useradd -M -s /usr/sbin/nologin prometheus
sudo mkdir -p /opt/prometheus/{data,conf,bin}
sudo chown -R prometheus: /opt/prometheus

5.2 下载与放置(以 x86_64 为例)

VER=2.52.0   # 举例
wget https://github.com/prometheus/prometheus/releases/download/v${VER}/prometheus-${VER}.linux-amd64.tar.gz
tar xzf prometheus-${VER}.linux-amd64.tar.gz
sudo cp prometheus-${VER}.linux-amd64/{prometheus,promtool} /opt/prometheus/bin/
sudo cp -r prometheus-${VER}.linux-amd64/{console_libraries,consoles} /opt/prometheus/

5.3 配置 /opt/prometheus/conf/prometheus.yml

global:
  scrape_interval: 15s
  evaluation_interval: 15s
  external_labels:
    region: "hk"
    cluster: "hk01"

scrape_configs:
  # 1) 采集 Prometheus 自身
  - job_name: 'prometheus'
    static_configs:
      - targets: ['127.0.0.1:9090']

  # 2) Node Exporter(推流节点)
  - job_name: 'node'
    scrape_interval: 15s
    static_configs:
      - targets:
          - '10.0.10.11:9100'   # hk01-live-rtmp-01
          - '10.0.10.12:9100'   # hk01-live-rtmp-02
        labels:
          role: 'rtmp-edge'
          env: 'prod'

  # 3) Blackbox:探测 RTMP 端口连通
  - job_name: 'blackbox_rtmp'
    metrics_path: /probe
    params:
      module: [tcp_connect]
    static_configs:
      - targets:
          - '10.0.10.11:1935'
          - '10.0.10.12:1935'
    relabel_configs:
      - source_labels: [__address__]
        target_label: __param_target
      - source_labels: [__param_target]
        target_label: instance
      - target_label: __address__
        replacement: '10.0.0.5:9115'   # blackbox_exporter 地址

  # 4) SRS 自定义 Exporter(见后文)
  - job_name: 'srs_exporter'
    scrape_interval: 15s
    static_configs:
      - targets:
          - '10.0.10.11:9200'
          - '10.0.10.12:9200'
        labels:
          role: 'rtmp-edge'

5.4 Systemd 服务 /etc/systemd/system/prometheus.service

[Unit]
Description=Prometheus Server
After=network-online.target
Wants=network-online.target

[Service]
User=prometheus
Group=prometheus
ExecStart=/opt/prometheus/bin/prometheus \
  --config.file=/opt/prometheus/conf/prometheus.yml \
  --storage.tsdb.path=/opt/prometheus/data \
  --storage.tsdb.retention.time=15d \
  --web.listen-address=:9090 \
  --web.enable-lifecycle
Restart=always
LimitNOFILE=1048576

[Install]
WantedBy=multi-user.target

启动:

sudo systemctl daemon-reload
sudo systemctl enable --now prometheus
/opt/prometheus/bin/promtool check config /opt/prometheus/conf/prometheus.yml

6. 安装 Exporters(推流节点)

6.1 Node Exporter

sudo useradd -M -s /usr/sbin/nologin nodeexp
VER=1.7.0
wget https://github.com/prometheus/node_exporter/releases/download/v${VER}/node_exporter-${VER}.linux-amd64.tar.gz
tar xzf node_exporter-${VER}.linux-amd64.tar.gz
sudo cp node_exporter-${VER}.linux-amd64/node_exporter /usr/local/bin/

/etc/systemd/system/node_exporter.service

[Unit]
Description=Node Exporter
After=network.target

[Service]
User=nodeexp
ExecStart=/usr/local/bin/node_exporter \
  --collector.conntrack \
  --collector.netstat \
  --collector.filesystem.ignored-mount-points=^/(dev|proc|sys|run)($|/)
Restart=always

[Install]
WantedBy=multi-user.target

sudo systemctl daemon-reload
sudo systemctl enable --now node_exporter

6.2 Blackbox Exporter(可在 Prometheus 主机或旁路主机)

useradd -M -s /usr/sbin/nologin blackbox
VER=0.25.0
wget https://github.com/prometheus/blackbox_exporter/releases/download/v${VER}/blackbox_exporter-${VER}.linux-amd64.tar.gz
tar xzf blackbox_exporter-${VER}.linux-amd64.tar.gz
sudo cp blackbox_exporter-${VER}.linux-amd64/blackbox_exporter /usr/local/bin/
sudo mkdir -p /etc/blackbox

/etc/blackbox/blackbox.yml

modules:
  tcp_connect:
    prober: tcp
    timeout: 3s
  http_2xx:
    prober: http
    timeout: 5s
    http:
      method: GET
      valid_http_versions: [ "HTTP/1.1", "HTTP/2" ]
      preferred_ip_protocol: "ip4"

/etc/systemd/system/blackbox_exporter.service

[Unit]
Description=Blackbox Exporter
After=network.target

[Service]
User=blackbox
ExecStart=/usr/local/bin/blackbox_exporter --config.file=/etc/blackbox/blackbox.yml --web.listen-address=:9115
Restart=always

[Install]
WantedBy=multi-user.target

sudo systemctl daemon-reload
sudo systemctl enable --now blackbox_exporter

6.3 SRS 启用 API(或 Nginx-RTMP 统计)

SRS(推荐,天生有 API)在 srs.conf 中确保:

http_api {
    enabled on;
    listen 1985;
}
stats {
    network on;
}

重启 SRS 后,curl http://127.0.0.1:1985/api/v1/streams 能返回 JSON。

如果你用 Nginx-RTMP,需要额外模块或脚本输出统计(例如 Lua/OpenResty 自制接口),思路与 SRS 类似:提供当前发布流列表和比特率。

6.4 自定义 SRS Exporter(Python)

设计目标:把当前发布数、各路比特率、会话状态转换成 Prometheus 指标。

安装依赖:

sudo apt install -y python3-pip
pip3 install flask prometheus_client requests

/opt/srs_exporter/srs_exporter.py

#!/usr/bin/env python3
import os, time, requests
from flask import Flask, Response
from prometheus_client import Gauge, generate_latest, CollectorRegistry, CONTENT_TYPE_LATEST

SRS_API = os.environ.get("SRS_API", "http://127.0.0.1:1985/api/v1/streams")
TIMEOUT = float(os.environ.get("SRS_TIMEOUT", "2.0"))

app = Flask(__name__)

registry = CollectorRegistry()

g_streams = Gauge('srs_active_streams', 'Active RTMP streams', ['server'], registry=registry)
g_bitrate_video = Gauge('srs_stream_bitrate_video_bps', 'Video bitrate (bps)', ['stream', 'vhost', 'app'], registry=registry)
g_bitrate_audio = Gauge('srs_stream_bitrate_audio_bps', 'Audio bitrate (bps)', ['stream', 'vhost', 'app'], registry=registry)
g_publishers = Gauge('srs_stream_publishers', 'Publishers(=1 if publishing)', ['stream', 'vhost', 'app'], registry=registry)

def fetch_srs():
    r = requests.get(SRS_API, timeout=TIMEOUT)
    r.raise_for_status()
    return r.json()

@app.route('/metrics')
def metrics():
    try:
        data = fetch_srs()
        server = 'hk01'
        # 重置(避免指标残留)
        for m in [g_streams, g_bitrate_video, g_bitrate_audio, g_publishers]:
            m._metrics.clear()

        total = 0
        items = data.get('streams', [])
        for s in items:
            if s.get('publish', {}).get('active'):
                total += 1
                vhost = s.get('vhost', 'default')
                app = s.get('app', 'live')
                name = s.get('name', 'unknown')

                # bitrate 可能在 kbits 字段,视 SRS 版本略有不同
                v_bps = int(float(s.get('kbps', {}).get('video', 0)) * 1000)
                a_bps = int(float(s.get('kbps', {}).get('audio', 0)) * 1000)

                g_bitrate_video.labels(name, vhost, app).set(v_bps)
                g_bitrate_audio.labels(name, vhost, app).set(a_bps)
                g_publishers.labels(name, vhost, app).set(1)

        g_streams.labels(server).set(total)

    except Exception:
        # 拉取失败时,给 0/空,避免数据挂死
        pass

    return Response(generate_latest(registry), mimetype=CONTENT_TYPE_LATEST)

if __name__ == '__main__':
    app.run(host='0.0.0.0', port=9200)

Systemd:

sudo mkdir -p /opt/srs_exporter
sudo cp srs_exporter.py /opt/srs_exporter/
sudo bash -c 'cat >/etc/systemd/system/srs_exporter.service' <<'EOF'
[Unit]
Description=SRS Prometheus Exporter
After=network.target

[Service]
User=nobody
WorkingDirectory=/opt/srs_exporter
Environment=SRS_API=http://127.0.0.1:1985/api/v1/streams
ExecStart=/usr/bin/python3 /opt/srs_exporter/srs_exporter.py
Restart=always

[Install]
WantedBy=multi-user.target
EOF

sudo systemctl daemon-reload
sudo systemctl enable --now srs_exporter

7. Alertmanager(路由告警)

安装略,核心是 路由 与 接收器(邮件、Webhook、飞书/钉钉)。示例 /etc/alertmanager/alertmanager.yml:

route:
  receiver: 'ops'
  group_by: ['alertname', 'instance']
  group_wait: 30s
  group_interval: 5m
  repeat_interval: 2h

receivers:
  - name: 'ops'
    webhook_configs:
      - url: 'https://your-webhook.example.com/alert'

Prometheus 侧关联:

alerting:
  alertmanagers:
    - static_configs:
        - targets: ['10.0.0.5:9093']

8. 告警规则(直播关注项)

/opt/prometheus/conf/rules-live.yml:

groups:
- name: live-edge
  rules:
  - alert: NodeDown
    expr: up{job="node"} == 0
    for: 1m
    labels: { severity: critical }
    annotations:
      summary: "Node down: {{ $labels.instance }}"

  - alert: HighConntrack
    expr: node_nf_conntrack_entries / node_nf_conntrack_entries_limit > 0.8
    for: 5m
    labels: { severity: warning }
    annotations:
      summary: "Conntrack high on {{ $labels.instance }}"

  - alert: RTMPPortDown
    expr: probe_success{job="blackbox_rtmp"} == 0
    for: 30s
    labels: { severity: critical }
    annotations:
      summary: "RTMP port not reachable: {{ $labels.instance }}"

  - alert: StreamsDrop
    expr: (max_over_time(srs_active_streams[5m]) - srs_active_streams) > 5
    for: 2m
    labels: { severity: critical }
    annotations:
      summary: "Active streams sudden drop on {{ $labels.server }}"

  - alert: BitrateLow
    expr: avg_over_time(srs_stream_bitrate_video_bps[2m]) < 300000 and srs_stream_publishers == 1
    for: 2m
    labels: { severity: warning }
    annotations:
      summary: "Video bitrate low: {{ $labels.stream }} ({{ humanize $value }}bps)"

在 prometheus.yml 引用:

rule_files:
  - /opt/prometheus/conf/rules-live.yml

应用:

curl -X POST http://127.0.0.1:9090/-/reload

9. 快速验证与压测

9.1 黑盒连通性

curl -s 'http://10.0.0.5:9115/probe?module=tcp_connect&target=10.0.10.11:1935'

应看到 probe_success 1。

9.2 推流仿真(本机或压测机)

ffmpeg -re -stream_loop -1 -i demo.mp4 -c copy -f flv rtmp://10.0.10.11/live/test001

观察:

srs_active_streams{server="hk01"}

srs_stream_bitrate_video_bps{stream="test001"}

9.3 Prometheus TSDB 健康

打开 http://<prometheus>:9090/targets 和 http://<prometheus>:9090/rules,确认任务绿色、规则已加载。

10. Grafana 面板(选做但强烈建议)

我常用的几个图:

  • 主看板:活跃流数、过去 24h 峰值、各节点会话分布、端口可达率。
  • 会话质量:单流比特率曲线(视频/音频)、发布状态切换次数、断流次数按小时分布。
  • 节点健康:CPU/负载、网卡丢包/错误、TCP 重传率、conntrack 使用率、磁盘写入(TSDB 压力)。
  • 小技巧:对 srs_stream_bitrate_video_bps 做 irate() 或 avg_over_time() 平滑,减少锯齿;对流名加 regex 变量,现场定位更快。

11. 端口与组件速查表

组件 端口 说明
Prometheus 9090/TCP Web & API
Alertmanager 9093/TCP 告警路由
Node Exporter 9100/TCP 节点指标
Blackbox Exporter 9115/TCP 黑盒探测
SRS API 1985/TCP 流状态 JSON
SRS Exporter 9200/TCP 自定义指标
RTMP 1935/TCP 推流入口

12. 我踩过的坑与现场解法

时钟漂移导致“比特率突降误报”

现象:Exporter 和 Prometheus 时钟差 >1s,avg_over_time 不稳定。

解法:全栈启用 chrony,Prometheus/Exporter/NTP 上游同源;Grafana 面板对齐到整点窗口。

conntrack 爆表引起握手失败

现象:峰值时 RTMP 建连失败率升高,dmesg 有 nf_conntrack: table full。

解法:增大 nf_conntrack_max,优化 tcp_fin_timeout,确认 NAT 行为(若有)不放大会话数。

TSDB 写压过大导致面板卡顿

现象:15s 间隔、标签爆炸(流名高基数),Prometheus CPU 飙升。

解法:控制维度:对“流级”指标只采样关键节点(或落地到远端存储如 VictoriaMetrics/Thanos),将面向告警的指标聚合成 Recording Rules;降低保留至 7–15 天。

Blackbox 探测与业务竞争

现象:过多探测导致 1935/TCP SYN 突增。

解法:使用 TCP 探测但减少 targets(或延长间隔),优先在同一二层内侧探测,避免经公网绕行。

SRS API 版本差异

现象:不同小版本 kbps 字段位置有差异。

解法:Exporter 里做容错(见示例 get('kbps', {}).get('video',0)),并把异常转化为 0,避免脏数据残留。

UFW 漏放 1985 导致指标空白

现象:Exporter 抓不到数据,但本机 curl localhost:1985 正常。

解法:Exporter 与 SRS 同机无须对外开放 1985,只需 127.0.0.1,Exporter 对外暴露 9200;Prometheus 只拉 9200。

13. 进一步优化技巧

Recording Rules(预计算)

将常用计算落地,提升查询性能:

groups:
- name: records
  rules:
  - record: job:srs_active_streams:sum
    expr: sum(srs_active_streams) by (server)
  - record: stream:video_bitrate_kbps
    expr: srs_stream_bitrate_video_bps / 1000

分层采集与分片

流级指标只在边缘机(小 Prometheus)采,remote_write 到主 Prometheus;主控做聚合与告警。

网络细节

  • 绑定网卡中断到 CPU(irqbalance 调整),RSS/ RPS 配置;
  • 适度开启 GRO/LRO,观察 RTMP 时延(SRT 更敏感)。

安全

Prometheus/Alertmanager 前加 Nginx 反代 + Basic Auth + IP 白名单;Exporter 仅在内网暴露。

14. 运行与应急手册(我自己的“值班手卡”)

端口健康:
promtool tsdb status 看写入压力;ss -s 看活跃连接;ethtool -S 看网卡丢包;conntrack -S 看表占用。

会话突降排查:
先看 srs_active_streams 与 probe_success,再查 dmesg 是否有丢包/NIC 错误、chronyc sources 是否抖动、top 是否 IO wait 飙升。

快速止血:
降采样(把 scrape_interval 暂调 30s),临时关闭流级指标采集或减少目标,保证主看板与告警在线。

15. 成本与容量估算(我当晚的粗算)

估算
指标时间序列(活跃 2 台边缘、每台 2–3k 时序) ~6k series
15 天保留 + NVMe 50–100GB(视流级标签波动)
CPU(Prometheus) 2–4 vCPU 足够
内存 8–16GB
导致卡顿的主要风险 高基数标签、瞬时写入峰值、磁盘 IOPS

凌晨三点,Grafana 的“会话质量”面板终于稳定下来了:srs_active_streams 的阶梯折线贴着 400+,偶有锯齿但不再“悬崖”。BitrateLow 告警偶尔闪一下,点进历史曲线,能看到对应 RTMP 会话的比特率在 23:41 到 23:43 下降了 40%,紧接着 tcp_retrans_segs_total 也抬头——是那根上联链路在抖。我把链路端口换到了另一张万兆模块,曲线立刻平滑。客户在群里发了句“稳定了,感谢”,我关掉了最后一盏机柜门的指示灯。

那一夜让我再次确认:监控不是堆面板,而是让每一次异常都留下可验证的证据链。Prometheus 没有魔法,但它把“感觉”变成了“数据”,把“争论”变成了“事实”。如果你也在香港或其他边缘机房折腾推流,照着这套落地,你会很快拥有一套可复用、可扩展、可审计的直播健康监控体系。下次工单来时,你有数据、有方法、有底气。

目录结构
全文