亚马逊AWS官方博客
混合云架构下秒级多维网络监控方案
摘要:本文介绍一种面向混合云、跨可用区及本地数据中心网络链路的秒级主动拨测方案。方案通过部署轻量探针 Agent,在探测端使用 fping 与 nping 分别覆盖 ICMP 与 TCP SYN 场景,并结合 EC2 实例元数据、Prometheus、Grafana 与通知渠道,实现按源实例、私网 IP、可用区、目标端点和探测方式维度聚合的网络质量可观测能力。
一、前言
在企业构建跨云、跨区域、跨可用区(AZ)以及连接本地数据中心(IDC)的混合云网络架构时,网络稳定性与低延迟是支撑在线交易、即时零售、支付回调、外部 API 调用等核心业务的关键条件。传统分钟级监控或单点 Ping 检测容易掩盖瞬时抖动、短时丢包以及禁 Ping 边界带来的观测盲区。
- 多云及禁 Ping 边界的探测盲区:第三方 API、公共 DNS 或部分互联网服务可能禁用 ICMP,单纯 Ping 无法覆盖链路质量
- 多维标签缺失,故障定位困难:缺少源 AZ、实例名称、私网 IP、目标端点和探测协议等上下文时,告警很难直接指向故障域
- 监控粒度粗,无法捕捉瞬时抖动:分钟级采样会平滑掉秒级断连、短时丢包和链路抖动
为解决上述问题,本文基于亚马逊云科技环境设计并实现一套秒级、多维网络主动拨测系统。该系统每秒在 Agent 端执行探测,并通过 15 秒滑动窗口计算平均 RTT 与丢包率,Prometheus 以 10 秒间隔拉取指标,最终在 Grafana 中完成可视化与差异化告警。
二、架构总览
[图 1 混合云秒级网络监控架构] |
数据链路可以拆解为四个步骤:
- 探针 Agent 在 EKS Pod 中通过 Kubernetes Downward API 获取 pod_name、pod_ip、node_name 等运行时标签;EC2/systemd 部署模式下可继续使用 IMDSv2 获取实例元数据
- Agent 按目标配置选择 fping 或 nping,每秒执行一次探测,并在本地 15 秒滑动窗口中计算平均延迟与丢包率
- Prometheus Server 以 Pull 模式访问各 Agent 暴露的 /metrics 端点,拉取带多维标签的时序数据
- Grafana 根据 probe_method、source_az、instance_name、target_name 等标签拆分视图,并通过通知策略将告警投递到飞书、邮件或运营平台
三、前提条件
| 类别 | 要求 |
| 运行环境 | 探测端建议使用 Amazon Linux 2023;监控中心需要可运行 Prometheus 与 Grafana |
| 网络连通 | Prometheus 节点能够访问每个 Agent 的 8000 端口;Agent 能够访问被探测的 VPC、IDC、跨云或互联网目标 |
| 安全组/NACL | Agent 入站仅允许 Prometheus 所在安全组或固定 CIDR 访问 8000 端口;出站按探测目标开放 ICMP 或 TCP 端口 |
| 系统依赖 | Agent 需要 Python 3、prometheus_client、requests、fping、nmap/nping |
| 元数据访问 | EC2 实例需要启用 IMDSv2;如需读取 Name 标签,还需显式启用 instance metadata tags |
| EKS 集群 | 建议使用 Amazon EKS Managed Node Group 承载 探针 Agent Pod;因 fping/nping 需要原始套接字能力,不建议优先使用 Fargate。 |
| 镜像仓库 | 需要 Amazon ECR 私有仓库保存探针 Agent 镜像,并确保 EKS 节点角色具备拉取镜像权限。 |
| Kubernetes 权限 | Prometheus 若使用 kubernetes_sd_configs,需要具备 list/watch pods、endpoints 或 endpointslices 的 RBAC 权限。 |
四、设计实现
4.1 目标配置与双拨测引擎
Agent 支持两类目标配置:字符串形式默认使用 fping,适用于普通 ICMP 可达目标;字典形式可以指定 method 与 ports,适用于禁 Ping 但开放 TCP 端口的目标,例如 Apple StoreKit API。
TARGETS = {
"Weixin_api": "api.weixin.qq.com",
"Baidu": "www.baidu.com",
"ZHY50": "172.31.10.241",
"BJS11": "172.17.0.254",
"Apple_StoreKit": {
"host": "api.storekit.itunes.apple.com",
"method": "nping",
"ports": [443]
}
}
def normalize_target_config(name, config):
if isinstance(config, str):
return {"host": config, "method": "fping", "ports": [80, 443]}
return {
"host": config["host"],
"method": config.get("method", "fping"),
"ports": config.get("ports", [80, 443])
}
| 目标类型 | 配置方式 | 典型用途 |
| 普通域名/IP | “Baidu”: “www.baidu.com” | 验证公网或专线出口的 ICMP 延迟与丢包 |
| VPC/IDC 私网 IP | “ZHY50”: “172.31.10.241” | 验证跨 AZ、跨 VPC、Direct Connect 或 IDC 路由质量 |
| 禁 Ping 端点 | {“host”: “…”, “method”: “nping”, “ports”: [443]} | 使用 TCP SYN 模拟业务端口连通性,覆盖 ICMP 不可用场景 |
4.2 指标模型与标签设计
Agent 将平均延迟和丢包率注册为 Prometheus Gauge,并在每个指标上携带源端与目标端上下文。这样 Grafana 面板和告警消息可以直接呈现“哪个源节点、从哪个 AZ、访问哪个目标、通过哪种探测方式出现异常”。
LATENCY_AVG = Gauge(
'netsonar_latency_avg_ms',
'Avg latency over 15s',
['instance_name', 'private_ip', 'source_az', 'target_name', 'target_host', 'probe_method']
)
PACKET_LOSS = Gauge(
'netsonar_packet_loss_percent',
'Loss percent over 15s',
['instance_name', 'private_ip', 'source_az', 'target_name', 'target_host', 'probe_method']
)
| 标签 | 来源 | 用途 |
| instance_name | EC2 Name Tag 或 instance-id fallback | 告警中定位具体探测节点 |
| private_ip | IMDSv2 local-ipv4 | 定位源端私网地址,便于排查路由和安全组 |
| source_az | IMDSv2 placement/availability-zone | 按 AZ 判断故障是否具有局部性 |
| target_name | TARGETS 字典 key | 使用业务化名称展示目标 |
| target_host | 目标域名或 IP | 定位实际被探测端点 |
| probe_method | fping 或 nping | 区分 ICMP 与 TCP SYN 的延迟/丢包基线 |
4.3 元数据发现:IMDSv2 与 Kubernetes Downward API
EC2/systemd 部署模式下,Agent 启动时通过 IMDSv2 获取 Token,再读取可用区、私网 IP 和 Name 标签。当实例未开启 tags in metadata 或未配置 Name 标签时,脚本会 fallback 到 instance-id。EKS 容器化部署时,Pod 不应依赖宿主机 IMDS 作为主路径,建议通过 Kubernetes Downward API 注入 Pod 名称、Pod IP、节点名称和标签,再由应用侧映射为 instance_name、private_ip、source_az 等 Prometheus 标签。
token = requests.put(
"http://169.254.169.254/latest/api/token",
headers={"X-aws-ec2-metadata-token-ttl-seconds": "21600"},
timeout=2
).text
head = {"X-aws-ec2-metadata-token": token}
az = requests.get(f"{base}/meta-data/placement/availability-zone", headers=head).text
ip = requests.get(f"{base}/meta-data/local-ipv4", headers=head).text
name_req = requests.get(f"{base}/meta-data/tags/instance/Name", headers=head, timeout=2)
name = name_req.text if name_req.status_code == 200 else requests.get(
f"{base}/meta-data/instance-id", headers=head
).text
EKS 容器化部署时,可以在 Pod manifest 中使用 Downward API 把 Pod 运行时信息注入环境变量,应用读取这些环境变量后写入 Prometheus labels。
env:
- name: POD_NAME
valueFrom:
fieldRef:
fieldPath: metadata.name
- name: POD_NAMESPACE
valueFrom:
fieldRef:
fieldPath: metadata.namespace
- name: POD_IP
valueFrom:
fieldRef:
fieldPath: status.podIP
- name: NODE_NAME
valueFrom:
fieldRef:
fieldPath: spec.nodeName
4.4 秒级采样与 15 秒滑动窗口
Agent 每秒遍历一次目标列表,将成功探测的 RTT 写入内存队列,失败或超时写入 None。随后基于最近 15 个采样点计算平均延迟与丢包率。由于 Prometheus 拉取间隔为 10 秒,我们在 Grafana 中看到的是经过短窗口收敛后的秒级网络质量趋势,而不是单次探测的瞬时值。
LATENCY_AVG.labels(inst_name, private_ip, az, name, config["host"], config["method"]).set(sum(valid)/len(valid) if valid else 0)
PACKET_LOSS.labels(inst_name, private_ip, az, name, config["host"], config["method"]).set((data.count(None)/len(data))*100 if data else 0)
五、部署与实施步骤(EC2 与 EKS 容器化路径)
5.1 步骤一:开启 EC2 实例标签元数据访问
为了让 Agent 读取 EC2 控制台中的 Name 标签,需要在探测实例上显式开启 tags in instance metadata。以下命令中的实例 ID 请替换为实际 Agent 实例。
aws ec2 modify-instance-metadata-options \
--instance-id i-xxxxxxxxxxxxxxxxx \
--instance-metadata-tags enabled
aws ec2 describe-instances \
--instance-ids i-xxxxxxxxxxxxxxxxx \
--query 'Reservations[].Instances[].MetadataOptions'
5.2 步骤二:准备部署环境与最小权限授权
安装 fping 与 nmap 工具包,其中 nping 包含在 nmap 中。
sudo dnf install -y python3-pip fping nmap
pip3 install prometheus_client requests
现有脚本中的 nping 调用使用 sudo 执行 TCP SYN 探测。为了避免直接以 root 身份运行整个 Python 进程,推荐仅对 nping 配置最小 sudoers 授权,并对 fping 设置必要执行权限。
sudo chmod +s $(which fping)
sudo tee /etc/sudoers.d/netsonar-nping >/dev/null <<'EOF'
ec2-user ALL=(root) NOPASSWD: /usr/bin/nping
EOF
sudo chmod 440 /etc/sudoers.d/netsonar-nping
sudo visudo -cf /etc/sudoers.d/netsonar-nping
如果你选择通过 Linux capability 或 SetUID 方式让 nping 无需 sudo 运行,请同步将脚本中的 nping 命令改为不带 sudo 的形式,避免 systemd 服务因 sudo 权限策略而失败。
5.3 步骤三:启动 探针 NetSonar Agent 服务
将脚本保存至 /home/ec2-user/netsonar_agent.py,并使用 systemd 托管进程。
sudo systemctl daemon-reload
sudo systemctl enable --now netsonar
sudo systemctl status netsonar
curl http://127.0.0.1:8000/metrics
5.4 步骤四:配置 Prometheus 与 Grafana
Prometheus 通过静态 targets 拉取各 Agent 暴露的 /metrics。生产环境中建议将 8000 端口限制为仅 Prometheus 节点可访问。
global:
scrape_interval: 10s
evaluation_interval: 10s
scrape_configs:
- job_name: 'netsonar-agents'
static_configs:
- targets:
- 'localhost:8000'
- '172.31.20.250:8000'
- '172.17.0.254:8000'
如果 Docker 默认网桥网段与 VPC/IDC 网段冲突,Prometheus 容器可能无法访问 Agent。此时可以调整 Docker daemon 的默认 bip 网段后重启 Docker。
{
"bip": "192.168.100.1/24"
}
5.5 步骤五:EKS 容器化部署 NetSonar Agent
- 推荐路径:EKS Managed Node Group + DaemonSet + Pod/Service 暴露 :8000 metrics。
- 谨慎使用路径:EKS Fargate。Fargate 不支持 DaemonSet 和 privileged 容器,且 Fargate Pod 无法访问 EC2 IMDS;如果必须使用,需要单独验证 nping/fping 权限、标签来源和调度方式。
- 容器版脚本建议移除 nping 命令中的 sudo,由 Pod securityContext 授予 NET_RAW capability,避免容器内 sudo 依赖。
以下示例使用 eksctl 创建 EKS 集群和 Linux managed node group。生产环境中请替换 VPC、子网、实例规格和节点数,并确保节点所在子网能够访问 IDC、跨云和互联网探测目标。
eksctl create cluster \
--name netsonar-eks \
--region cn-northwest-1 \
--managed \
--nodes 3 \
--node-type t3.small
kubectl get nodes -o wide
kubectl create namespace netsonar
5.5.2 构建 Agent 镜像并推送到 Amazon ECR
容器镜像中需要包含 Python 运行时、prometheus_client、requests、fping 和 nping。以下 Dockerfile 仅作为最小示例,实际生产镜像建议固定基础镜像版本并进行漏洞扫描。
将镜像推送到 ECR。ECR 登录令牌需要按 Region 获取,镜像仓库需要提前创建。
AWS_ACCOUNT_ID=<your-account-id>
AWS_REGION=cn-northwest-1
IMAGE_REPO=netsonar-agent
IMAGE_TAG=v1
aws ecr create-repository --repository-name ${IMAGE_REPO} --region ${AWS_REGION}
aws ecr get-login-password --region ${AWS_REGION} \
| docker login --username AWS --password-stdin \
${AWS_ACCOUNT_ID}.dkr.ecr.${AWS_REGION}.amazonaws.com.cn
docker build -t ${IMAGE_REPO}:${IMAGE_TAG} .
docker tag ${IMAGE_REPO}:${IMAGE_TAG} \
${AWS_ACCOUNT_ID}.dkr.ecr.${AWS_REGION}.amazonaws.com.cn/${IMAGE_REPO}:${IMAGE_TAG}
docker push ${AWS_ACCOUNT_ID}.dkr.ecr.${AWS_REGION}.amazonaws.com.cn/${IMAGE_REPO}:${IMAGE_TAG}
5.5.3 外置目标配置
容器化部署时,建议把探测目标从代码中拆出为 ConfigMap。应用可以通过挂载文件或环境变量读取目标列表;如果暂时保留硬编码 TARGETS,则每次目标变更都需要重新构建镜像,不利于运维。
apiVersion: v1
kind: ConfigMap
metadata:
name: netsonar-targets
namespace: netsonar
data:
targets.yaml: |
targets:
Weixin_api:
host: api.weixin.qq.com
method: fping
Baidu:
host: www.baidu.com
method: fping
Apple_StoreKit:
host: api.storekit.itunes.apple.com
method: nping
ports: [443]
5.5.4 部署 Agent DaemonSet 与 Service
DaemonSet 示例中,Agent 容器暴露 8000 端口供 Prometheus 抓取;Downward API 注入 Pod 名称、Pod IP 和节点名;securityContext 只添加 NET_RAW capability,用于 ICMP/TCP SYN 探测。若你的运行时或安全策略仍然阻止原始套接字,请先在测试命名空间验证,必要时再评估 privileged DaemonSet 的风险。
apiVersion: apps/v1
kind: DaemonSet
metadata:
name: netsonar-agent
namespace: netsonar
spec:
selector:
matchLabels:
app: netsonar-agent
template:
metadata:
labels:
app: netsonar-agent
annotations:
prometheus.io/scrape: "true"
prometheus.io/port: "8000"
spec:
containers:
- name: netsonar-agent
image: <account-id>.dkr.ecr.<region>.amazonaws.com.cn/netsonar-agent:v1
imagePullPolicy: IfNotPresent
ports:
- name: metrics
containerPort: 8000
securityContext:
allowPrivilegeEscalation: false
capabilities:
add: ["NET_RAW"]
env:
- name: POD_NAME
valueFrom:
fieldRef:
fieldPath: metadata.name
- name: POD_IP
valueFrom:
fieldRef:
fieldPath: status.podIP
- name: NODE_NAME
valueFrom:
fieldRef:
fieldPath: spec.nodeName
volumeMounts:
- name: targets
mountPath: /etc/netsonar
readOnly: true
volumes:
- name: targets
configMap:
name: netsonar-targets
---
apiVersion: v1
kind: Service
metadata:
name: netsonar-agent
namespace: netsonar
spec:
selector:
app: netsonar-agent
ports:
- name: metrics
port: 8000
targetPort: metrics
kubectl apply -f netsonar-targets.yaml
kubectl apply -f netsonar-agent-daemonset.yaml
kubectl -n netsonar get pods -o wide
kubectl -n netsonar logs -l app=netsonar-agent --tail=50
5.5.5 配置 Prometheus 发现 Agent Pod
如果 Prometheus 运行在 EKS 集群内,可以使用 Kubernetes service discovery 自动发现 netsonar 命名空间下的 Agent Pod。以下配置基于 pod role 发现 Pod,并将 Pod IP 重写为 :8000 抓取地址。
scrape_configs:
- job_name: 'netsonar-agent-pods'
scrape_interval: 10s
kubernetes_sd_configs:
- role: pod
namespaces:
names:
- netsonar
relabel_configs:
- source_labels: [__meta_kubernetes_pod_label_app]
action: keep
regex: netsonar-agent
- source_labels: [__meta_kubernetes_pod_ip]
target_label: __address__
replacement: $1:8000
- source_labels: [__meta_kubernetes_pod_name]
target_label: pod_name
- source_labels: [__meta_kubernetes_pod_node_name]
target_label: node_name
- source_labels: [__meta_kubernetes_namespace]
target_label: namespace
如果 Prometheus 仍部署在集群外部,可改为通过 Service、Ingress、VPC 内部负载均衡或静态 Pod IP/Node IP 方式抓取;但生产环境更推荐使用集群内 Prometheus 或 Prometheus Operator 的 ServiceMonitor/PodMonitor,以降低静态配置维护成本。
5.5.6 验证容器化部署部署完成后,先确认 DaemonSet 覆盖了预期节点,再验证 /metrics 是否能返回 netsonar 指标。
kubectl -n netsonar get daemonset netsonar-agent
kubectl -n netsonar get pods -l app=netsonar-agent -o wide
POD_NAME=$(kubectl -n netsonar get pods -l app=netsonar-agent -o jsonpath='{.items[0].metadata.name}')
kubectl -n netsonar port-forward pod/${POD_NAME} 8000:8000
curl http://127.0.0.1:8000/metrics | grep netsonar_
六、Grafana 可视化与告警验证
6.1 面板表达式与 Legend 格式化
Grafana 面板按 probe_method 分成 ICMP 与 TCP SYN 两组视图,并分别观察平均延迟和丢包率。下面的 PromQL 可直接用于面板查询:
Legend 建议组合源实例、源 IP、源 AZ 和目标名称,便于运维人员直接从图例定位异常链路。
将 ICMP 与 TCP SYN 拨测结果分开展示,用于对比不同目标和源 AZ 的延迟基线与丢包趋势。
[图 2 Grafana 四宫格面板] |
从已提供的面板可以看到,ICMP 类目标整体延迟较稳定,丢包率在正常窗口内接近 0;TCP SYN 类目标的延迟基线更高且波动更明显,这与公网服务端口、跨境链路、WAF 或服务侧策略有关。因此在告警策略上需要区分 probe_method,避免 ICMP 与 TCP 指标共用同一阈值。
6.2 差异化告警阈值与飞书通知
Grafana Alerting 可以按 probe_method 配置不同阈值。示例中,ICMP 丢包率超过 10% 触发硬性链路异常告警,TCP SYN 丢包率可结合目标服务特点设置更宽松阈值,例如 20%。
- ICMP 告警规则:监听 netsonar_packet_loss_percent{probe_method=”fping”},设置阈值 IS ABOVE 10
- TCP SYN 告警规则:监听 netsonar_packet_loss_percent{probe_method=”nping”},设置阈值 IS ABOVE 20
告警消息中保留 instance_name、private_ip、source_az、target_host 和 target_name,可直接表达“从哪个源节点访问哪个目标出现异常”。例如附件中的飞书告警显示:BGP_ZHY51/cn-northwest-1b(172.31.20.250)访问 www.baidu.com 时丢包率达到 53.33%,告警状态为 Firing
飞书告警消息中包含 alertname、instance、private_ip、probe_method、source_az、target_host、target_name 和 description。
[图 3 飞书告警示例] |




