在事件驱动系统里,CPU 和内存并不总能反映真实压力。一个消费者 Pod 可能只占用很少的 CPU,但 Amazon SQS 中已经堆积了数千条消息。此时如果只依赖 Kubernetes HPA 的资源指标,扩容往往发生得太晚。
KEDA 可以把 SQS 队列深度转换成 Kubernetes 扩缩容信号,让 Worker 副本数跟随待处理消息数量变化。系统关心的不再只是“Pod 有多忙”,而是“还有多少工作没完成”。
为什么 CPU 指标会误判事件处理压力
队列消费者经常包含大量 I/O 操作,例如调用外部 API、查询数据库或等待对象存储响应。在等待期间,CPU 使用率可能很低,但消息仍会持续进入队列。
只看 CPU 会带来两个典型问题:
- 消息快速积压时,Pod 的 CPU 尚未达到阈值,HPA 不会及时扩容。
- 队列已经清空后,某些批处理或运行时行为仍可能维持较高的资源占用,缩容出现延迟。
SQS 队列深度更接近业务压力,但它也不是吞吐量本身。扩缩容配置还需要考虑单条消息的处理时间、每个 Pod 的并发度、可见性超时,以及下游服务能承受的请求速率。
一个简单的容量估算是:
目标副本数 ≈ 待处理消息数 / 每个 Pod 期望承担的消息数
例如,将 KEDA 的目标队列长度设置为 20,队列中有约 200 条待处理消息时,扩缩容器会倾向于请求约 10 个副本。实际结果仍受 minReplicaCount、maxReplicaCount、指标采样和 Kubernetes HPA 行为影响。
KEDA 如何把 SQS 接入 Kubernetes 扩缩容
KEDA 通过 ScaledObject 描述扩缩容目标和外部事件源。对于 SQS,配置通常包含:
- 要扩缩的
Deployment。 - 队列 URL 和 AWS 区域。
- 每个副本期望承担的队列长度。
- 轮询间隔、冷却时间以及副本上下限。
- KEDA 查询 SQS 指标所需的 AWS 身份与 IAM 权限。
KEDA 负责轮询队列并向 Kubernetes 暴露外部指标,真正调整 Deployment 副本数的仍是 HPA。排障时因此需要同时检查 ScaledObject、KEDA Operator 日志和生成的 HPA。
SQS 返回的是近似消息数量,而且“可见消息”与“正在处理但尚未删除的消息”代表不同状态。KEDA 版本和配置可能决定是否把 in-flight 消息计入计算。上线前应根据所用版本核对 scaleOnInFlight 等选项,并观察长耗时任务是否造成副本数持续偏高。
可以这样实践:部署 Worker 和 ScaledObject
下面是一套可改造的最小配置。运行前需要完成三项修改:替换镜像地址、SQS 队列 URL 和 AWS 区域;同时确保集群已安装 KEDA,并为它配置能够读取该队列属性的 AWS 身份。
apiVersion: apps/v1
kind: Deployment
metadata:
name: orders-worker
namespace: workers
spec:
replicas: 0
selector:
matchLabels:
app: orders-worker
template:
metadata:
labels:
app: orders-worker
spec:
containers:
- name: worker
image: ghcr.io/example/orders-worker:1.0.0
env:
- name: SQS_QUEUE_URL
value: https://sqs.us-east-1.amazonaws.com/123456789012/orders
- name: AWS_REGION
value: us-east-1
resources:
requests:
cpu: 100m
memory: 128Mi
limits:
memory: 256Mi
---
apiVersion: keda.sh/v1alpha1
kind: TriggerAuthentication
metadata:
name: sqs-auth
namespace: workers
spec:
podIdentity:
provider: aws-eks
---
apiVersion: keda.sh/v1alpha1
kind: ScaledObject
metadata:
name: orders-worker-sqs
namespace: workers
spec:
scaleTargetRef:
name: orders-worker
pollingInterval: 15
cooldownPeriod: 120
minReplicaCount: 0
maxReplicaCount: 30
fallback:
failureThreshold: 3
replicas: 2
triggers:
- type: aws-sqs-queue
authenticationRef:
name: sqs-auth
metadata:
queueURL: https://sqs.us-east-1.amazonaws.com/123456789012/orders
awsRegion: us-east-1
queueLength: "20"
activationQueueLength: "1"
scaleOnInFlight: "true"
将文件保存为 sqs-worker.yaml 后,可以执行:
kubectl create namespace workers
kubectl apply -f sqs-worker.yaml
kubectl get scaledobject,hpa,deployment -n workers
kubectl describe scaledobject orders-worker-sqs -n workers
这里假设使用 EKS Pod Identity 或 IRSA 一类的无静态密钥身份方案。具体绑定方式取决于集群和 KEDA 版本。KEDA 查询队列至少需要读取 SQS 队列属性;可以从以下最小策略开始,再按实际错误补充权限:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"sqs:GetQueueAttributes"
],
"Resource": "arn:aws:sqs:us-east-1:123456789012:orders"
}
]
}
不要把 AWS Access Key 和 Secret Key 直接写进 YAML。优先使用工作负载身份,并将权限限制到目标队列。
用测试消息验证扩容链路
可以向队列发送一批测试消息,再观察副本数变化。下面的命令要求本机已经配置 AWS CLI 凭证;请替换队列 URL:
QUEUE_URL="https://sqs.us-east-1.amazonaws.com/123456789012/orders"
for i in $(seq 1 100); do
aws sqs send-message \
--region us-east-1 \
--queue-url "$QUEUE_URL" \
--message-body "{\"order_id\":\"test-$i\"}" >/dev/null
done
kubectl get deployment orders-worker -n workers -w
同时查看队列属性,有助于区分可见消息和正在处理的消息:
aws sqs get-queue-attributes \
--region us-east-1 \
--queue-url "$QUEUE_URL" \
--attribute-names \
ApproximateNumberOfMessages \
ApproximateNumberOfMessagesNotVisible
如果没有扩容,可按这个顺序检查:
kubectl describe scaledobject中是否存在认证或指标错误。- KEDA Operator 是否有
sqs:GetQueueAttributes权限。 - 队列 URL、区域和 IAM Resource ARN 是否一致。
- KEDA Metrics Server 和生成的 HPA 是否正常。
activationQueueLength是否高于当前积压量。
参数不要只按“越快越好”调整
queueLength 决定扩容敏感度。设置太大,消息等待时间会上升;设置太小,则可能产生大量 Pod、拉高成本,并冲击数据库或第三方 API。
pollingInterval 越短,KEDA 越快看到积压变化,但查询频率也会增加。cooldownPeriod 太短会导致负载波动时频繁缩容和重新拉起 Pod。对于启动慢的 Worker,还应考虑镜像拉取、节点扩容和应用预热耗时。
maxReplicaCount 应当由下游容量决定,而不是由队列可能达到的最大深度决定。若数据库最多承受 200 个并发连接,而每个 Worker 会占用 10 个连接,那么将上限直接设为 100 很可能把队列问题转化为数据库故障。
投入生产前,至少确认以下事项:
- 使用真实处理时长估算
queueLength和最大副本数。 - 校准 SQS visibility timeout,避免长任务被重复投递。
- Worker 能够处理重复消息,并在收到终止信号时停止领取新任务。
- 配置死信队列、失败重试和消息年龄告警。
- 同时监控可见消息数、in-flight 消息数、最老消息年龄和处理失败率。
- 验证 KEDA 或 AWS API 暂时不可用时的 fallback 行为。
KEDA 解决的是“根据工作量分配消费者”这一层问题。它不能自动保证消息幂等、下游容量或任务正确终止。把队列深度扩缩容与这些工程约束一起设计,才能真正缩短消息等待时间,而不是单纯增加 Pod 数量。