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

在香港服务器Debian 10系统上用 Prometheus + Loki 补齐 SaaS 平台“监控盲区”——黑盒合成监控、租户维度观测与日志收集体系全流程落地(含踩坑与参数细节)

发布人:Minchunlin 发布时间:2025-09-25 10:15 阅读量:724


香港服务器的业务监控面板一片绿,P95 延迟、CPU、内存都“健康”。偏偏客户打来电话,说香港区 SaaS 控制台间歇性 502,且只发生在租户 A、B 的管理页。更诡异的是,我们的 Prometheus 没有任何告警触发,Loki 也没有搜到对应的错误堆栈。

这就是“监控盲区”——系统认为一切正常,用户却已经崩溃。那一刻我意识到,我们的指标与日志体系只覆盖了“服务器与容器层面的自我感觉”,却没有从用户路径和租户维度做合成监控与精细观测。接下来两天,我在香港机房搭了把“梯子”:在 Debian 10 上把 Prometheus + Blackbox + Pushgateway + Loki/Promtail 系列拉起来,补齐了从黑盒合成监控、白盒业务指标到集中化日志的闭环。下面是我完整的落地过程、踩坑与参数细节。

1. 场景与约束

1.1 目标

  • 补齐 SaaS 平台“用户路径/租户维度”的监控盲区
  • 合成监控(黑盒)+ 应用/系统指标(白盒)+ 日志索引(Loki)三位一体
  • 告警要分层:SLO/SLA、租户级、依赖链路级(DNS、TLS、Upstream)

1.2 环境与硬件(香港机房)

角色 型号/规格 关键参数 用途
监控主机 2U 单机(AMD EPYC 7402P) 24C/48T,128GB RAM,2×1.92TB NVMe(RAID1),2×10GbE Prometheus + Alertmanager + Loki(单机版) + Grafana
应用节点(x6) 1U(Intel Silver 4310) 12C/24T,64GB RAM,1×960GB SSD,2×10GbE Docker/K8s 工作负载
边界节点 硬件防火墙 + LVS 10GbE,上连两家 ISP 四层转发 + eBGP
  • OS:Debian 10 (Buster),内核 4.19 系(默认)
  • 容器:Docker CE(生产用 K8s 亦可,本文以 Docker 演示)

1.3 网络与端口矩阵

组件 端口 方向 说明
Prometheus 9090 Intranet Web/UI & API
Alertmanager 9093 Intranet 告警路由
node_exporter 9100 Prometheus→节点 主机指标
blackbox_exporter 9115 Prometheus→黑盒 HTTP/TCP/DNS 检查
Pushgateway 9091 应用→Pushgateway→Prom 短任务指标
Loki 3100 Promtail→Loki 日志写入/查询 API
Promtail 9080 本地 采集端本地 HTTP(可闭)

2. 监控盲区的成因:不是 Prometheus 不行,是我们没问对问题

症状回放:

  • 只有部分租户的“管理页”502,公共首页正常
  • 容器、节点层指标均正常,无 CPU/内存异常
  • 无核心告警触发,日志侧也未检索到对应 5xx

复盘定位:

  • 黑盒合成监控缺失:我们只抓 Node/容器白盒指标,缺“从用户角度访问指定租户路径”的探测。
  • 租户维度缺失:日志标签只有 service、pod,没有 tenant。
  • 短任务与边缘依赖盲点:部分异步作业/三方依赖“偶发错”未被指标化(需要 Pushgateway 与黑盒探测 DNS/TLS/TCP)。
  • 日志采集在容器边界断档:某些容器只输出到 stdout,日志轮转策略与 Promtail pipeline 不匹配,导致检索不到异常段。

补救路线图:

黑盒(Blackbox)补用户路径 → 白盒(Prom + Exporter + Pushgateway)补业务与短任务指标 → Loki/Promtail 统一日志收集并加租户标签 → Alert 分层(SLO、租户、依赖节点)

3. 系统基线(Debian 10 上的那些“必须做”)

# 基本工具
apt-get update && apt-get install -y jq curl chrony netcat-traditional

# 时间同步(Loki/Prometheus 对时序非常敏感)
systemctl enable chrony && systemctl restart chrony

# 文件句柄 / 网络连接跟随
cat >/etc/security/limits.d/monitoring.conf <<'EOF'
* soft nofile 1048576
* hard nofile 1048576
* soft nproc  65535
* hard nproc  65535
EOF

# conntrack 调优(黑盒/高并发探测容易打满)
cat >/etc/sysctl.d/99-monitoring.conf <<'EOF'
net.netfilter.nf_conntrack_max=262144
net.core.somaxconn=4096
fs.file-max=2097152
vm.max_map_count=262144
EOF
sysctl --system

Debian 10 上 Docker 默认沿用 iptables(legacy),如果你切到 nftables,要确认 Docker 与防火墙规则一致,否则会导致 Promtail/Loki/Blackbox 访问异常(我就被这个坑过一次)。

4. Prometheus 与 Exporter 体系

4.1 安装(以二进制为例)

useradd --no-create-home --shell /sbin/nologin prometheus
mkdir -p /etc/prometheus /var/lib/prometheus

# 放置 prometheus 与 promtool 到 /usr/local/bin(略)
chown -R prometheus:prometheus /etc/prometheus /var/lib/prometheus

systemd 单元:

# /etc/systemd/system/prometheus.service
[Unit]
Description=Prometheus
Wants=network-online.target
After=network-online.target

[Service]
User=prometheus
Group=prometheus
ExecStart=/usr/local/bin/prometheus \
  --config.file=/etc/prometheus/prometheus.yml \
  --storage.tsdb.path=/var/lib/prometheus \
  --storage.tsdb.retention.time=45d \
  --storage.tsdb.wal-compression \
  --web.enable-lifecycle
Restart=on-failure
LimitNOFILE=1048576

[Install]
WantedBy=multi-user.target

4.2 Node / Blackbox / Pushgateway

# node_exporter(每台节点)
useradd -r -s /sbin/nologin nodeusr
# 放置二进制 /usr/local/bin/node_exporter
cat >/etc/systemd/system/node_exporter.service <<'EOF'
[Unit]
Description=Node Exporter

[Service]
User=nodeusr
ExecStart=/usr/local/bin/node_exporter \
  --collector.processes \
  --collector.systemd \
  --collector.tcpstat

[Install]
WantedBy=multi-user.target
EOF
systemctl enable --now node_exporter

blackbox_exporter:

# /etc/blackbox_exporter/config.yml
modules:
  http_tenant:
    prober: http
    timeout: 10s
    http:
      method: GET
      headers:
        X-Tenant-Id: "TENANT_PLACEHOLDER"
      valid_status_codes: [200,302]
      no_follow_redirects: false
  tcp_443:
    prober: tcp
    timeout: 5s
  dns_public:
    prober: dns
    timeout: 5s
    dns:
      query_name: "saas.example.hk"
      query_type: "A"
      valid_rcodes: ["NOERROR"]

Pushgateway(短任务/批处理指标):

  • 应用中向 http://pushgw:9091/metrics/job/<job>/instance/<id> 推送
  • Prometheus 定期抓取 Pushgateway,避免短任务指标丢失

4.3 Prometheus 抓取与重标签

# /etc/prometheus/prometheus.yml
global:
  scrape_interval: 15s
  evaluation_interval: 15s
  external_labels:
    dc: hk
    cluster: prod

scrape_configs:
  - job_name: 'nodes'
    static_configs:
      - targets: ['app01:9100','app02:9100','monitor:9100']
    relabel_configs:
      - source_labels: [__address__]
        regex: "(.*):9100"
        target_label: instance
        replacement: "$1"

  - job_name: 'blackbox-tenant'
    metrics_path: /probe
    params:
      module: [http_tenant]
    static_configs:
      - targets:
        - https://saas.example.hk/tenant/a/admin
        - https://saas.example.hk/tenant/b/admin
        labels:
          tenant: "a"   # 自定义标签
        - labels:
          tenant: "b"
    relabel_configs:
      - source_labels: [__address__]
        target_label: __param_target
      - target_label: __address__
        replacement: blackbox:9115
      - source_labels: [tenant]
        target_label: X-Tenant-Id   # 传入 blackbox headers
      - source_labels: [__param_target]
        target_label: instance

  - job_name: 'pushgateway'
    static_configs:
      - targets: ['pushgw:9091']

要点:用 relabel_configs 把租户信息、探测目标透传到 Blackbox,并把 X-Tenant-Id 注入 HTTP 头;这样合成监控就真的“走了用户路径”。

5. Alertmanager:分层路由 + 降噪

# /etc/alertmanager/alertmanager.yml
global:
  resolve_timeout: 5m

route:
  receiver: 'default'
  group_by: ['alertname','tenant']
  group_wait: 30s
  group_interval: 5m
  repeat_interval: 2h
  routes:
    - matchers:
        - severity="critical"
      receiver: 'oncall'
    - matchers:
        - tenant=~".+"
      receiver: 'tenant_owner'

receivers:
  - name: 'default'
    webhook_configs:
      - url: 'http://ops-webhook:9000/alerts'
  - name: 'oncall'
    pagerduty_configs:
      - routing_key: '<pd-key>'
  - name: 'tenant_owner'
    email_configs:
      - to: 'owner@tenant.example'

SLO/Burn-rate 告警(示例 PromQL):

# /etc/prometheus/rules/slo.yml
groups:
- name: slo
  rules:
  - alert: SaaSAvailabilityHighBurn
    expr: |
      (sum(rate(blackbox_probe_success{job="blackbox-tenant"}[5m]) == 0)
       /
       sum(rate(blackbox_probe_success{job="blackbox-tenant"}[5m])))
      > (1-0.999) * 14.4
    for: 5m
    labels:
      severity: critical
    annotations:
      summary: "租户可用性快速消耗(5m 窗口)"
      description: "tenant={{ $labels.tenant }}, target={{ $labels.instance }}"

6. Loki + Promtail:日志收集体系

6.1 Loki(单机,boltdb-shipper + filesystem)

# /etc/loki/config.yml
auth_enabled: false

server:
  http_listen_port: 3100

common:
  path_prefix: /var/lib/loki
  storage:
    filesystem:
      chunks_directory: /var/lib/loki/chunks
      rules_directory: /var/lib/loki/rules
  replication_factor: 1
  ring:
    instance_addr: 127.0.0.1

schema_config:
  configs:
    - from: 2024-01-01
      store: boltdb-shipper
      object_store: filesystem
      schema: v13
      index:
        prefix: index_
        period: 24h

limits_config:
  reject_old_samples: true
  reject_old_samples_max_age: 168h
  ingestion_rate_mb: 16
  ingestion_burst_size_mb: 32
  max_global_streams_per_user: 500000
  max_label_names_per_series: 30

compactor:
  working_directory: /var/lib/loki/compactor
  shared_store: filesystem
  compaction_interval: 5m

table_manager:
  retention_deletes_enabled: true
  retention_period: 360h   # 15 天

经验:Loki 的“标签基数”是第一大坑。租户维度要做,但避免把 request_id、user_id 这类高基数字段放到 label;放到日志体里即可。

6.2 Promtail(容器与文件双通道)

# /etc/promtail/config.yml
server:
  http_listen_port: 9080

clients:
  - url: http://loki:3100/loki/api/v1/push

positions:
  filename: /var/lib/promtail/positions.yaml

scrape_configs:
  - job_name: system
    static_configs:
      - targets: [localhost]
        labels:
          job: syslog
          host: ${HOSTNAME}
          __path__: /var/log/syslog

  - job_name: docker
    docker_sd_configs:
      - host: "unix:///var/run/docker.sock"
        refresh_interval: 30s
    relabel_configs:
      - source_labels: ['__meta_docker_container_name']
        target_label: 'container'
        regex: '/(.*)'
      - source_labels: ['__meta_docker_container_label_com_example_tenant']
        target_label: 'tenant'
      - source_labels: ['__meta_docker_container_label_com_docker_compose_service']
        target_label: 'service'

    pipeline_stages:
      - docker: {}
      - json:
          expressions:
            level: level
            msg: message
            trace: trace
      - labels:
          level:
      - drop:
          source: level
          expression: "debug"   # 降噪

做法:通过 Docker label 注入 tenant,例如 --label com.example.tenant=a。这在 Loki 中就能按租户聚合了。

LogQL 查询范例

最近 10 分钟租户 a 的 5xx:

{job="docker", tenant="a"} |= "HTTP" |~ " 5\\d\\d " | line_format "{{.msg}}"

API 网关 TLS 握手错误:

{service="edge-gw"} |= "tls: handshake failure"

7. 仪表盘与可观测性“闭环”

类别 仪表盘/面板 关键图表 价值
黑盒 租户可用性 blackbox_probe_success、RTT、TLS 直接感知用户路径
白盒 节点/容器健康 CPU/Mem/IO/GC/线程 快速归因资源瓶颈
白盒 业务指标 QPS、P95/P99、错误率 与 SLO 对齐
日志 错误热力图 per-tenant 5xx、trace 关联 定位租户与调用链
依赖 DNS/TCP 探测 解析耗时、TCP 连接失败 排查边缘依赖故障

我在 Grafana 里把黑盒、白盒与日志面板交叉链接(面板变量包括 tenant),一键切换租户视图,值班人员的“平均定位时间”从 20 分钟降到 5 分钟以内。

8. 存储与保留策略(容量核算)

| 组件 | 吞吐/基数假设 | 保留期 | 估算容量 | 备注 |
| Prometheus | 400k series @ 15s | 45 天 | ≈ 1.2–1.6TB | 开 WAL 压缩 |
| Loki | 1.5k msg/s(平均) | 15 天 | ≈ 1.8TB | JSON 压缩率 3–6x |
| Pushgateway | 低 | 按 Prometheus | 忽略 | 仅短任务 |

经验:Prometheus 的高基数来自 label 组合(如 per-pod、per-path)。合成监控要克制目标数量;租户黑盒尽量覆盖关键路径,不要无度扩张。

9. 典型告警清单(精炼版)

告警 规则/阈值 作用
SaaSAvailabilityHighBurn 上文 Burn-rate SLO 快速消耗
TenantHttpFailuresBurst rate(probe_http_status_code{=~"5.."}[5m]) > 0 租户级 5xx
UpstreamTcpConnectErrors increase(probe_failed_due_to_tcp_[5m]) > 10 上游 TCP 抖动
DNSFailure increase(probe_dns_lookup_time_seconds_count{rcode!="NOERROR"}[5m]) > 0 DNS 解析异常
LokiIngestionBacklog rate(loki_distributor_lines_dropped_total[5m]) > 100 日志丢弃
PromHighChurn changes(scrape_samples_post_metric_relabeling[10m]) > 5e6 高 churn 预警

10. 真实踩坑与修复过程

iptables/nftables 冲突

现象:Blackbox 无法探测外网,Loki 接收偶发超时。

根因:Docker 使用 iptables(legacy),宿主切换 nft 后 NAT 规则不生效。

处理:回退到 iptables-legacy 或统一切到 iptables-nft 并重建 DOCKER 链。

Loki “entry out of order”

现象:Promtail 写入被拒。

根因:部分应用容器时间漂移(NTP 未启用),日志时间戳乱序。

处理:统一 chrony;临时放宽 max_out_of_order_time: 5m,恢复后调回默认。

Prometheus WAL 膨胀

现象:磁盘写放大,I/O 飙高。

根因:黑盒目标过多 + 指标高 churn。

处理:精简黑盒目标、延长 scrape_interval(极端从 10s→15s/30s)、开启 --storage.tsdb.wal-compression。

日志标签爆炸

现象:Loki 查询变慢,索引剧增。

根因:把 request_id 当 label。

处理:移入日志体,保留 tenant/service/level 等低基数标签;promtail drop 降噪。

Pushgateway 反模式

现象:短任务清理不及时,旧时间序列残留。

处理:业务侧 push --expire 或调用完成后 DELETE,并在 Prometheus 端加 honor_labels 策略与 metric_relabel_configs 做 TTL。

11. 一键校验(可复制执行的健康检查清单)

# 黑盒:租户 a
curl -fsS "http://blackbox:9115/probe?target=https://saas.example.hk/tenant/a/admin&module=http_tenant" \
  -H "X-Tenant-Id: a" | grep -E 'probe_success|probe_duration_seconds'

# Prometheus 抓取现状
curl -s http://prom:9090/api/v1/targets | jq '.data.activeTargets[] | {job: .labels.job, health: .health, lastError: .lastError}' | head

# Loki 写入/查询
curl -s -G http://loki:3100/loki/api/v1/query --data-urlencode 'query={job="docker"} |= "error"' | jq '.data.stats'

12. 最终结构回放(ASCII 拓扑)

[User/Browser]
     |
  (Edge/LB)───DNS──► Blackbox(http/dns/tcp)
     |                    ▲
     ▼                    │
 [App Nodes: Docker]──Promtail──► Loki
     ▲                         ▲
 node_exporter                Grafana
     │                         │
     └────────► Prometheus ◄───┘
                   ▲
             Pushgateway(短任务)

13. 凌晨 3:06,第一次“正确的红色”

体系落地后的第一个凌晨,黑盒面板出现漂亮的红色告警:仅租户 B 的 /admin 在香港电信出口 RTT 拉长并伴随 5xx 抖动。我顺着告警跳转到日志,Loki 里清清楚楚:上游 TLS 握手超时集中发生在某 ISP 段。我们把边界策略切换到双线路优选并限流重试,5 分钟内恢复。

那一刻我意识到:红色并不可怕,沉默才可怕。把用户路径、租户维度与日志闭环接起来后,香港服务器不再“自我感觉良好”,而是对用户体验负责。

14. 附录:关键配置汇总(可直接落地)

  • prometheus.yml(含 blackbox + pushgateway)
  • alertmanager.yml(分层路由)
  • blackbox_exporter/config.yml(http_tenant/dns/tcp 模块)
  • loki/config.yml(boltdb-shipper + retention)
  • promtail/config.yml(docker_sd + JSON pipeline)
  • systemd 单元(prometheus/node_exporter/blackbox/loki/promtail)

如果你也在香港/海外机房跑 Debian 10 的 SaaS,我建议先从一个关键租户路径的黑盒做起,配好 X-Tenant-Id 与租户标签,一天之内你就能看到“沉默处的波纹”。

目录结构
全文