EKS上实时特征服务按需计算层压测实战:Latency、RPS与Scalability深度优化
1. 项目概述:为什么要在EKS上给Volga的按需计算层“体检”
最近三个月,我带着团队在生产环境里把Volga这套特征服务架构从概念验证推到了千QPS级真实流量支撑阶段。Volga不是开源项目,而是我们内部孵化的一套面向实时特征工程的统一服务框架——它的核心创新点在于把传统离线特征计算、在线预计算和真正意义上的“按需即时计算”(on-demand compute)揉进一个可编排的执行图里。而其中最关键的模块,就是标题里提到的On-Demand Compute Layer:它不依赖预热缓存,不走批处理流水线,而是当模型推理请求抵达时,动态拉起轻量计算单元,现场拼装、清洗、归一化、组合多个上游数据源(Kafka流、S3快照、Redis特征库),生成该请求专属的特征向量,再毫秒级返回。听起来很理想,但上线前没人敢拍胸脯说它在弹性伸缩场景下到底稳不稳。
所以这次压测不是走流程,是救命——我们得知道它在EKS(Amazon Elastic Kubernetes Service)这个最贴近生产的真实调度平台上,面对突发流量时的真实心跳。我们不只看P99延迟是不是<150ms,更关注当RPS从500飙到3000时,系统是平滑扩容还是雪崩式抖动;不只看单Pod能扛多少并发,更要看Horizontal Pod Autoscaler(HPA)触发策略是否合理、节点组扩容延迟是否可控、冷启动带来的长尾延迟是否可接受。关键词里的Latency、RPS、Scalability,每一个背后都连着一个线上事故预案:延迟超标意味着推荐排序降权;RPS卡在2200不动了,说明下游特征DB连接池或gRPC线程数成了瓶颈;扩展性失效,则意味着大促期间我们得手动半夜起来扩节点——这事儿我干过两次,咖啡当水喝,黑眼圈比代码注释还密。
适合谁参考?如果你正在评估自研或选型特征服务平台,尤其已确定用K8s做底座,那这篇就是你跳过试错期的“避坑地图”。它不讲Volga的Go语言实现细节,也不堆砌Prometheus指标截图,而是把一次完整压测拆解成可复现的决策链:为什么选k6而不是wrk?为什么用ClusterIP Service暴露压测入口而非NodePort?HPA的targetCPUUtilizationPercentage设为60%而非80%的数学依据是什么?这些答案,全来自我们在EKS上反复重启、调整、抓包、查日志的实操现场。
2. 整体设计思路:拒绝“假大空”压测,构建逼近真实的流量沙盒
2.1 压测目标不是“跑出最高数字”,而是暴露系统拐点
很多团队压测一上来就冲峰值RPS,结果跑出个“5000 RPS,平均延迟80ms”的漂亮报表,上线后却在3000 RPS时开始超时。问题出在哪?——他们没定义业务可接受的SLA边界。我们给Volga On-Demand Layer定的硬性红线只有三条:
- 延迟红线:P99 ≤ 150ms(业务方容忍的最长等待时间,超过则降级为兜底特征)
- 成功率红线:HTTP 2xx/5xx比例 ≥ 99.95%(允许每万次请求最多5次失败)
- 扩展性红线:RPS从500提升至3000过程中,P99延迟增幅 ≤ 40%(即不能从120ms涨到200ms)
这三条红线直接决定了压测方案的设计逻辑。比如,如果我们发现RPS=2500时P99突然跳到180ms,那就立刻停住,不再往上冲——因为拐点已出现,此时要做的不是挑战极限,而是回溯:是StatefulSet的volumeMount等待超时?是Envoy sidecar的HTTP/2流控阈值太低?还是特征计算函数里某个正则匹配用了回溯爆炸(catastrophic backtracking)?这种“见红即停”的思路,让我们的压测从性能竞赛变成了精准诊断。
2.2 环境拓扑:为什么必须1:1复刻生产EKS集群
我们没用minikube或Kind搞本地模拟,也没租用小规格EC2自己搭K8s。所有压测都在与生产环境同Region、同AZ、同节点类型、同IAM权限策略的独立EKS集群上进行。具体配置如下:
| 组件 | 配置 | 说明 |
|---|---|---|
| Control Plane | EKS托管,1.27版本 | 启用Managed Node Groups自动修复 |
| Worker Nodes | m6i.2xlarge(8 vCPU / 32 GiB) × 3 | 每节点部署1个Volga Compute Pod + 1个Datadog Agent + 1个nginx-ingress controller |
| Storage | EBS gp3(100 GiB, 3000 IOPS) | Volga Pod的临时计算目录挂载点,避免/tmp内存盘OOM |
| Network | VPC内网直连,Security Group放行6443/10250/30000-32767 | 禁用NAT Gateway,杜绝网络抖动干扰 |
关键决策点:不用Spot实例。虽然成本低,但Spot中断会导致HPA误判负载,且节点重建期间Pod驱逐会污染延迟数据。我们宁可多花30%成本,也要保证压测窗口的原子性——连续4小时无中断的稳定压测,比断断续续跑10次更有价值。
2.3 流量建模:用真实请求模式代替均匀随机
很多压测工具默认用“恒定RPS”发请求,这完全违背现实。用户点击推荐流是脉冲式的:首页刷新带来一波高峰,详情页停留产生长尾请求,分享链接又引发二次传播。我们从生产Kafka的request-log topic里抽样了24小时真实流量,提取出三个核心特征:
- 请求分布:85%请求集中在20%的特征组合路径上(如
user_profile+item_embedding+context_time),其余15%是长尾稀疏组合 - Payload大小:90%请求body < 1KB,但5%请求含base64编码的图像指纹,达120KB
- 时间间隔:相邻请求间隔服从泊松分布,λ=120(即平均每秒120次请求),但每15分钟会出现一次持续90秒的λ=450脉冲
于是我们用k6的scenarios功能构建了混合场景:
export const options = { scenarios: { // 主流量:泊松分布基础流 steady: { executor: 'per-vu-iterations', vus: 100, iterations: 10000, startTime: '0s', gracefulStop: '30s', exec: 'steadyLoad' }, // 脉冲流:每15分钟一次爆发 burst: { executor: 'ramping-vus', startVUs: 0, stages: [ { duration: '30s', target: 300 }, // 30秒冲到300并发 { duration: '60s', target: 300 }, // 维持60秒 { duration: '30s', target: 0 } // 30秒退回到0 ], gracefulStop: '30s', exec: 'burstLoad' } } };这样构造的流量,才能真实触发Volga的冷热路径分离机制——高频路径走JIT编译缓存,低频路径才真正调用Python UDF解释器。如果只用均匀流量,你会误以为系统“很稳”,其实只是没碰到那个要命的冷启动瞬间。
2.4 监控维度:不止看应用层,更要穿透到K8s内核
我们没只盯着Volga服务的/healthz和/metrics端点。监控体系分三层布防:
- 应用层:Volga自埋点(OpenTelemetry SDK)上报
feature_compute_duration_ms(含子步骤:data_fetch、transform、serialize)、http_server_request_duration_seconds - 平台层:Prometheus + kube-state-metrics采集
kube_pod_container_status_restarts_total(容器重启次数)、container_cpu_usage_seconds_total(每个容器CPU累积耗时)、kube_pod_status_phase(Pod状态变迁) - 节点层:Node Exporter抓取
node_cpu_seconds_total{mode="idle"}(CPU空闲率)、node_memory_MemAvailable_bytes(可用内存)、node_network_receive_bytes_total(网卡入流量)
特别关键的是关联分析:当P99延迟突增时,我们不是先看Volga日志,而是先查container_cpu_usage_seconds_total的rate值——如果某Pod CPU使用率在延迟飙升前10秒就冲到95%,那基本锁定是计算密集型瓶颈;如果CPU平稳但container_memory_usage_bytes曲线陡升,则大概率是Python GC风暴或内存泄漏。这种跨层关联,让我们三次定位到根因:一次是PyTorch DataLoader的num_workers设为0导致单线程阻塞,一次是Redis连接池maxIdle设为100但实际并发超200,还有一次是EBS卷IOPS不足导致特征快照读取卡顿。
3. 核心细节解析:Latency、RPS、Scalability三大指标的实操解法
3.1 Latency深度归因:从P99到P99.99的每一毫秒都值得抠
很多人以为降低延迟就是升级CPU或加内存,但在Volga的On-Demand场景下,P99延迟的构成远比想象复杂。我们用OpenTelemetry的trace采样(采样率1:100)对10万次请求做了分解,得到以下典型调用链耗时分布(单位:ms):
| 步骤 | P50 | P90 | P99 | P99.9 | 主要耗时来源 |
|---|---|---|---|---|---|
| Ingress处理 | 1.2 | 3.5 | 8.1 | 15.3 | Envoy TLS握手、路由匹配 |
| Volga Dispatcher | 0.8 | 2.1 | 5.7 | 12.4 | 请求解析、特征路径路由、上下文注入 |
| Data Fetch | 12.4 | 38.2 | 85.6 | 210.7 | Kafka消费延迟、S3 ListObjectsV2、Redis GET |
| Compute Execution | 8.3 | 22.5 | 41.2 | 135.8 | Python UDF执行、PyTorch张量运算、正则匹配 |
| Serialize & Return | 1.1 | 2.9 | 6.3 | 14.2 | Protobuf序列化、gRPC写响应 |
看到没?P99延迟85.6ms里,Data Fetch占了近70%,Compute Execution只占48%。这意味着优化Python代码可能收效甚微,而应该优先解决数据源访问问题。我们针对性做了三件事:
- Kafka Consumer优化:把
session.timeout.ms从30000降到10000,heartbeat.interval.ms从3000降到1000,避免Consumer Group重平衡导致的fetch阻塞。实测P99 fetch延迟从85.6ms降至52.3ms。 - S3访问加速:对特征快照文件启用S3 Transfer Acceleration,并在Volga Pod内预热DNS缓存(
dig s3.us-east-1.amazonaws.com +short)。同时把ListObjectsV2操作改为HEAD单个对象(用ETag校验更新),避免遍历整个prefix。 - Redis连接池扩容:将
maxIdle=100提升至maxIdle=300,并增加minIdle=50保活连接。这里有个经验:maxIdle值不能简单按并发数设置,而要按并发数 × 平均每个请求的Redis命令数来算。我们每个请求平均发4条命令(3次GET + 1次EXPIRE),所以3000 RPS时理论需12000连接,但通过连接复用和Pipeline,300 idle连接已足够。
提示:别迷信“P99越低越好”。我们发现当P99压到40ms时,P99.9反而飙升到320ms——这是典型的资源过度争抢现象。最终我们选择P99=55ms作为平衡点,此时P99.9稳定在110ms,整体成功率99.97%。
3.2 RPS瓶颈定位:不是“能跑多快”,而是“卡在哪一层”
RPS测试我们采用阶梯式加压:从100 RPS开始,每2分钟+200 RPS,直到触发熔断或延迟超标。全程记录各组件指标,绘制RPS-延迟-Pod数三维曲线。关键发现如下:
RPS=1800时出现首次拐点:P99延迟从48ms跳至62ms,同时
kube_pod_container_status_restarts_total计数器开始增长。查日志发现是OOMKilled——Volga Pod的memory limit设为2Gi,但Python进程在处理120KB大payload时,临时Tensor分配导致RSS峰值达2.3Gi。解法:不盲目加内存limit,而是用
--memory-swappiness=0禁止swap,并优化PyTorch内存管理:# 在UDF执行前强制清空缓存 torch.cuda.empty_cache() # 如果用GPU gc.collect() # 强制Python GC # 关键:用torch.utils.data.DataLoader的pin_memory=False # 避免CUDA pinned memory占用过多host内存RPS=2600时遭遇网络瓶颈:
node_network_receive_bytes_total显示单节点网卡入流量达950MB/s(m6i.2xlarge网卡上限1.2Gbps),但Volga Pod的container_network_receive_bytes_total只统计到700MB/s,差额250MB/s正是Envoy sidecar的转发开销。此时envoy_cluster_upstream_cx_overflow指标暴涨。解法:调整Envoy资源配置,在sidecar injection template中增加:
containers: - name: envoy resources: limits: memory: "1Gi" cpu: "1000m" requests: memory: "512Mi" cpu: "500m" env: - name: ENVOY_MAX_CONNECTIONS value: "100000"同时将Service的
externalTrafficPolicy从Cluster改为Local,绕过kube-proxy的DNAT,减少1次网络跳转。RPS=3000时触达gRPC线程瓶颈:Volga用gRPC-Go实现server,
grpc_server_handled_total{grpc_code="OK"}增速变缓,而grpc_server_handled_total{grpc_code="UNAVAILABLE"}激增。查pprof发现runtime.mcall调用占比超60%——这是goroutine调度器过载的典型信号。解法:调整gRPC Server参数:
opts := []grpc.ServerOption{ grpc.MaxConcurrentStreams(1000), // 默认100,提升10倍 grpc.NumStreamWorkers(512), // 默认RuntimeNumCPU,设为固定值防波动 grpc.WriteBufferSize(1024*1024), // 1MB缓冲区,减少系统调用 }
注意:每次调参后必须做回归压测。我们曾把
MaxConcurrentStreams设为5000,结果P99延迟反升——因为goroutine创建销毁开销超过了并发收益。最终2600 RPS时MaxConcurrentStreams=1000是实测最优解。
3.3 Scalability实战:HPA不是开箱即用,而是需要“手调”的精密仪器
Volga的Scalability测试最烧脑。我们期望RPS从500→3000时,Pod数能从3个平滑扩到12个,延迟增幅<40%。但初始配置下,HPA表现极不稳定:
问题1:CPU指标滞后。HPA基于
container_cpu_usage_seconds_total的rate值,但该指标是1分钟滑动窗口聚合。当RPS在5秒内从500飙到2000时,HPA要等60秒才感知到CPU飙升,此时Volga早已开始排队。解法:改用自定义指标
volga_requests_per_second(由Volga自身暴露的Prometheus counter计算rate)。在HPA配置中:metrics: - type: Pods pods: metric: name: volga_requests_per_second target: type: AverageValue averageValue: 250 # 每Pod目标250 RPS这样HPA响应延迟从60秒降至8秒(Prometheus scrape interval=15s,rate窗口=30s)。
问题2:扩容过猛。HPA默认
scaleUp步长是当前副本数的100%,即3→6→12。但Volga Pod冷启动需8-12秒(拉镜像+初始化Python环境+加载特征模型),6个新Pod同时启动会造成API Server压力,且首批请求全部打到旧Pod上,导致P99瞬间破百。解法:限制HPA扩缩容速率:
behavior: scaleUp: stabilizationWindowSeconds: 30 policies: - type: Pods value: 2 # 每30秒最多加2个Pod periodSeconds: 30 scaleDown: stabilizationWindowSeconds: 600同时在Deployment中设置
minReadySeconds: 15,确保Pod就绪后再接收流量。问题3:节点扩容延迟高。当HPA扩到12个Pod,但当前3个节点已满(每个节点limit=4 Pod),Cluster Autoscaler需要启动新EC2。实测从触发到新节点Ready平均耗时3分42秒,期间新Pod Pending,请求失败率飙升。
解法:预热节点组。我们配置了两个Managed Node Group:
ng-spot:Spot实例,用于承载稳定流量Pod(设priority=10)ng-on-demand:按需实例,始终维持2个空闲节点(设minSize=2, maxSize=10, desiredCapacity=2,priority=100)
这样当HPA需要扩容时,优先调度到
ng-on-demand的空闲节点,规避了CA启动新EC2的延迟。实测节点扩容时间从3分42秒降至0秒。
4. 实操过程全记录:从环境搭建到报告输出的每一步
4.1 环境准备:EKS集群与Volga部署的硬性要求
我们用Terraform v1.5.7 + eksctl v0.132.0搭建EKS集群,关键配置如下(省略VPC等基础模块):
# eks-cluster.tf module "eks" { source = "terraform-aws-modules/eks/aws" cluster_name = "volga-benchmark" cluster_version = "1.27" subnets = module.vpc.private_subnets # 必须启用Managed Node Groups,否则CA无法工作 manage_aws_auth_configmap = true enable_irsa = true # Worker节点配置 node_groups_defaults = { ami_type = "AL2_x86_64" disk_size = 100 instance_types = ["m6i.2xlarge"] desired_capacity = 3 min_capacity = 3 max_capacity = 12 } # 关键:启用Cluster Autoscaler所需的标签 node_groups = { ng-spot = { name = "ng-spot" labels = { "lifecycle" = "Ec2Spot", "k8s.io/cluster-autoscaler/node-template/taint/spot" = "true:NoSchedule" } taints = [{ key = "spot", value = "true", effect = "NO_SCHEDULE" }] priority = 10 on_demand_base_capacity = 0 spot_instance_pools = 3 } ng-on-demand = { name = "ng-on-demand" labels = { "lifecycle" = "OnDemand" } priority = 100 desired_capacity = 2 # 预热2个空闲节点 min_capacity = 2 max_capacity = 10 } } }Volga Deployment必须满足以下条件才能被HPA正确识别:
Resource Requests/Limits必须设置(HPA只认有requests的Pod):
resources: requests: memory: "1536Mi" cpu: "1000m" limits: memory: "2Gi" cpu: "2000m"Service必须是ClusterIP类型(NodePort/LoadBalancer会干扰HPA指标采集):
apiVersion: v1 kind: Service metadata: name: volga-compute spec: type: ClusterIP # 关键! selector: app: volga-compute ports: - port: 8080 targetPort: 8080Metrics Server必须安装(v0.6.3,兼容K8s 1.27):
kubectl apply -f https://github.com/kubernetes-sigs/metrics-server/releases/download/v0.6.3/components.yaml # 等待metrics-server就绪 kubectl wait --for=condition=Available --timeout=180s apiservice/v1beta1.metrics.k8s.io
实操心得:第一次部署时忘了给Volga Service加
prometheus.io/scrape: "true"annotation,导致HPA一直报unknown metrics。查了2小时日志才发现是ServiceMonitor没生效——K8s里90%的“神秘故障”都源于annotation或label漏配。
4.2 压测工具链部署:k6 + Prometheus + Grafana黄金组合
我们放弃JMeter(Java内存开销大)和locust(Python协程模型对长连接支持弱),选用k6(Go编写,内存占用低,原生支持WebSockets和gRPC):
# 在CI/CD pipeline中安装k6 curl -sS https://raw.githubusercontent.com/grafana/k6/master/install.sh | shk6脚本核心结构(benchmark.js):
import http from 'k6/http'; import { check, sleep, group } from 'k6'; import { Rate } from 'k6/metrics'; // 从环境变量读取压测参数 const BASE_URL = __ENV.BASE_URL || 'http://volga-compute.default.svc.cluster.local:8080'; const FEATURE_PATH = __ENV.FEATURE_PATH || 'user_profile+item_embedding'; export const options = { stages: [ { duration: '2m', target: 100 }, // ramp-up { duration: '10m', target: 100 }, // steady at 100 { duration: '2m', target: 1000 }, // ramp-up to 1000 { duration: '10m', target: 1000 }, // steady at 1000 { duration: '2m', target: 3000 }, // ramp-up to 3000 { duration: '10m', target: 3000 }, // steady at 3000 ], thresholds: { // 定义SLA红线 'http_req_duration{expected_response:true}': ['p(99)<150'], 'http_req_failed': ['rate<0.0005'], // 0.05%失败率 } }; export default function () { // 构造真实请求体(含随机用户ID和时间戳) const payload = JSON.stringify({ user_id: `user_${__ENV.USER_ID_PREFIX || 'test'}_${Math.floor(Math.random() * 10000)}`, timestamp: Date.now(), features: [FEATURE_PATH] }); const res = http.post(`${BASE_URL}/compute`, payload, { headers: { 'Content-Type': 'application/json' } }); check(res, { 'is status 200': (r) => r.status === 200, 'response time < 150ms': (r) => r.timings.duration < 150, }); // 模拟真实用户思考时间(泊松分布) const thinkTime = Math.random() * 1000; sleep(thinkTime / 1000); }Prometheus配置(prometheus.yml)需添加Volga和k6的target:
scrape_configs: - job_name: 'volga-compute' static_configs: - targets: ['volga-compute.default.svc.cluster.local:8080'] - job_name: 'k6' static_configs: - targets: ['k6-prometheus.default.svc.cluster.local:9100']Grafana仪表盘我们基于社区模板(ID: 13002)二次开发,重点强化三个视图:
- 实时RPS热力图:X轴时间,Y轴RPS,颜色深浅表示P99延迟区间(<50ms绿色,50-100ms黄色,>100ms红色)
- Pod生命周期追踪:展示每个Pod的
startTime、phase变迁、restartCount,与RPS曲线叠加 - 节点资源饱和度雷达图:CPU、内存、网络、磁盘IOPS四维指标,标出各节点当前负载百分比
4.3 压测执行与数据采集:如何让一次压测产生十倍价值
我们把单次压测拆成四个阶段,每个阶段产出不同价值:
Phase 1:Baseline(基线)
用100 RPS压测30分钟,记录所有指标基线值。重点确认:Volga Pod是否稳定(restartCount=0)、Prometheus是否正常抓取指标、Grafana仪表盘数据是否实时刷新。这阶段不求结果,只验环境。我们有2次压测失败,都是因为Phase 1没做彻底——一次是Prometheus scrape timeout,一次是Volga metrics endpoint返回503。Phase 2:Staircase(阶梯)
按前述stages配置执行,全程开启k6 run --out influxdb=http://influxdb:8086/k6将原始指标写入InfluxDB。关键动作:在每个RPS台阶稳定后,手动执行kubectl top pods和kubectl describe nodes,记录CPU/Memory Allocatable vs Used比率。例如RPS=1000时,发现某节点cpu used 3200m / 6400m,但Volga Pod只用了1800m,说明有500m被sidecar吃掉——这提示我们要优化Envoy配置。Phase 3:Burst(脉冲)
单独运行burstLoad场景,持续5分钟,专门捕获冷启动延迟。此时开启kubectl logs -l -c volga-compute --since=10s实时跟踪日志,搜索cold_start_duration_ms字段。我们发现第1个请求耗时1120ms,第2个降为280ms,第3个稳定在42ms——证明JIT缓存生效。这个数据直接决定了我们是否要为长尾特征路径预热。Phase 4:Soak(浸泡)
在目标RPS(3000)下持续压测2小时,观察内存泄漏和连接泄漏。用kubectl exec -it <pod> -- pstack <pid>抓取Go runtime栈,确认goroutine数量是否稳定。我们曾发现goroutine从初始200涨到1200,定位到是gRPC client未关闭stream——加了defer stream.CloseSend()后问题消失。
实操心得:压测时永远保留一份“干净”的集群快照(EKS Control Plane Snapshot + EBS Volume Snapshot)。我们有一次误操作把HPA的
averageValue设成2500(单位错写成RPS而非每Pod),导致集群疯狂扩容到48个Pod,差点把AWS账户刷爆。靠快照10分钟内回滚,损失可控。
4.4 报告生成:用数据讲故事,而非堆砌数字
最终报告不是Excel表格,而是用Grafana生成的PDF可交付物,包含四个核心章节:
- SLA达成度总览:用大号字体标出三项红线达成情况(✅ P99=54.2ms < 150ms;✅ 成功率99.97% > 99.95%;✅ 扩展性增幅38.6% < 40%),配趋势图。
- 瓶颈定位地图:用甘特图形式展示RPS增长过程中,各组件(Ingress、Dispatcher、Data Fetch、Compute)的P99延迟贡献占比变化。例如RPS=2000时,Data Fetch占比从65%升至78%,箭头直指S3访问优化。
- 调优效果对比表:列出所有生效的调优项及其量化收益:
调优项 实施前P99 实施后P99 提升幅度 备注 Kafka Consumer优化 85.6ms 52.3ms ↓39% 减少rebalance Envoy资源调优 62.1ms 48.7ms ↓22% 降低sidecar开销 gRPC MaxConcurrentStreams 71.4ms 54.2ms ↓24% 提升并发吞吐 - 生产部署建议清单:用checklist形式给出上线必做项:
- [ ] 将
volga-computeDeployment的replicas设为3(非HPA管理,作为最小保障) - [ ] 在
ng-on-demandNode Group中将desiredCapacity从2提升至3(预留1个节点应对突发) - [ ] 将Prometheus scrape interval从30s缩短至15s(提升HPA响应速度)
- [ ] 在Volga代码中增加
/debug/vars端点,暴露goroutine数和内存分配统计
- [ ] 将
5. 常见问题与排查技巧实录:那些文档里不会写的血泪教训
5.1 “P99延迟忽高忽低,找不到规律”——时间同步漂移陷阱
现象:压测中P99延迟在40ms和120ms之间无规律跳变,且只发生在部分Pod上。查Volga日志发现compute_start_time和compute_end_time的时间戳差值正常,但http_request_received_time到compute_start_time的间隔波动极大。
根因:EKS Worker节点的NTP时间同步失效。ntpq -p显示offset达120ms。K8s节点时间不同步会导致:
- Envoy的access log时间戳错乱,影响延迟计算
- Volga的
time.Since()计算出现负值(系统时钟回拨) - Prometheus的
rate()函数因时间戳跳跃产生错误导数
解法:在Node Group的Launch Template中加入NTP校准脚本:
#!/bin/bash # 启动时强制同步时间 systemctl stop systemd-timesyncd ntpdate -s time.amazon.com systemctl start systemd-timesyncd # 设置crontab每5分钟校准一次 (crontab -l 2>/dev/null; echo "*/5 * * * * /usr/sbin/ntpdate -s time.amazon.com") | crontab -注意:不要用
timedatectl set-ntp true,EKS AMI默认禁用systemd-timesyncd,必须显式启用。
5.2 “HPA不扩容,但top显示CPU 95%”——指标采集范围错位
现象:kubectl top nodes显示某节点CPU使用率95%,但kubectl get hpa显示TARGETS列为<unknown>/60%,HPA完全不响应。
根因:HPA默认采集的是container_cpu_usage_seconds_total指标,但该指标在Prometheus中是按container_name和pod_name维度聚合的。如果Volga Pod里有多个容器(如main container + sidecar + init container),而HPA的metrics配置没指定container标签,它就会找不到对应指标。
解法:在HPA YAML中明确指定容器名:
metrics: - type: Pods pods: metric: name: container_cpu_usage_seconds_total selector: matchLabels: container: volga-compute # 关键!指定主容器名 target: type: AverageValue averageValue: 600m # 600m = 60%5.3 “RPS上不去,wrk显示Connection refused”——Service端口暴露错误
现象:用wrk压测http://<ELB>:80,大量Connection refused。但kubectl port-forward svc/volga-compute 8080:8080本地curl正常。
根因:Volga Service的`port