Kubernetes 消息队列 配置避坑指南
前阵子把消息队列从裸机迁到 K8s 上,本以为就是个写写 yaml 的事儿,结果 RabbitMQ 加 Kafka 前后折腾了一个多星期,中间踩的坑一个接一个,有些报错跟实际问题八竿子打不着,排查起来相当折磨(也可能是我太菜了...)。这里把印象比较深的几个坑记一下,没准儿能帮后来的人少走点弯路。
先交代一下环境:K8s 1.28,RabbitMQ 用的 bitnami 的 helm chart,Kafka 是自己手写的 StatefulSet,为什么不用现成的 chart 后面会提到。存储一开始图省事,全挂在了 NFS 上,这也埋下了第一个大坑。
存储这坑踩得最疼,千万别随手挂 NFS
一开始把 Kafka 的 PVC 直接指到 NFS 的 StorageClass 上,部署完看起来一切正常,消息收发都没问题,我还挺得意。结果一压测就露馅了:吞吐量掉到惨不忍睹,consumer 疯狂 rebalance,日志里全是 leader 切换和拉取超时的告警。
后来才想明白,Kafka 对磁盘延迟是真的敏感,NFS 这玩意儿随便一次网络抖动就够它喝一壶的,fsync 一慢,broker 把请求全卡住,外面看起来就是假死,跟挂了没两样。
解决方式就是老老实实上本地盘。我是给节点挂了块 NVMe,装了 local-path-provisioner:
kubectl apply -f https://raw.githubusercontent.com/rancher/local-path-provisioner/v0.0.28/deploy/local-path-storage.yaml
正常的话几秒钟就能跑起来,kubectl get pods -n local-path-storage 看一眼是 Running 就行。
有两个细节要注意。一是 local-path 的 reclaimPolicy 默认是 Delete,哪天手滑删了 PVC,数据就真没了,重要队列记得改成 Retain,别问我怎么知道的。二是 local-path 不支持跨节点挂载,Pod 重建后被调度到别的节点,PVC 就会一直 Pending。RabbitMQ 的 StatefulSet 我是直接加了 nodeSelector 钉死节点,反正就两个副本,牺牲点灵活性换省心,值。
内存限制和水位线,别照抄默认值
RabbitMQ 官方镜像默认的 vm_memory_high_watermark 是 0.4,也就是内存的 40%。注意,至少在我的环境里它按宿主机的内存算,不是按容器 limit 算。我的节点 32G,容器 limit 只给了 2G,结果 RabbitMQ 一直觉得自己内存富余,可劲儿吃,然后被 cgroup 一刀 OOMKill,起来接着吃,接着杀,无限循环。
解决办法是直接配绝对值,不猜了:
# rabbitmq.conf
vm_memory_high_watermark.absolute = 1.2GB
vm_memory_high_watermark_paging_ratio = 0.75
这个 1.2G 要跟容器的 limit 一起规划,除了队列占的内存,Erlang 虚拟机本身也有开销,我 limit 给 2G、水位线设 1.2G,实测比较稳。
Kafka 同理,KAFKA_HEAP_OPTS 默认是 1G 堆,堆外还有一堆开销,容器 limit 千万别就给 1G。我给的是堆 1G、limit 2.5G,跑了几个月没再被杀过。
探针配太狠,恢复反而更慢
liveness 探针这个事儿,我被误杀过好几次。一开始照网上抄的配置,RabbitMQ 用 rabbitmq-diagnostics ping 当 liveness,initialDelay 给 10 秒,failureThreshold 给 3。然后有一次网络分区,集群正在自己恢复,探针一失败直接把容器杀了,恢复进度清零重来,越杀越乱,属实是帮倒忙。
后来学乖了,原则就一条:liveness 宁松勿紧,慢启动的活儿交给 startupProbe。
startupProbe:
exec:
command: ["rabbitmq-diagnostics", "-q", "check_running"]
periodSeconds: 15
failureThreshold: 30 # 最多等7分半,大内存节点启动时加载队列索引是真的慢
livenessProbe:
exec:
command: ["rabbitmq-diagnostics", "-q", "check_running"]
periodSeconds: 30
failureThreshold: 10
配了 startupProbe 过后,liveness 就不用管 initialDelay 了,容器没启动完成之前探针压根不生效。Kafka 那边我用的是最简单的 tcp 探针,至少不会因为磁盘抖个别劲就被杀。
Kafka 想从集群外面连,得整明白 advertised.listeners
这个问题报错特别迷惑:集群里面一切正常,kafka-console-producer 跑得好好的,可从外面连就一直卡住,或者报 UnknownHostException,你根本想不到问题出在 broker 身上。
原因是这样:Kafka 客户端 bootstrap 成功之后,会拿 broker 通过 advertised.listeners 上报的地址做后续连接。StatefulSet 里配的是 pod 的 hostname,类似 kafka-0.kafka-headless.kafka.svc.cluster.local:9092,这个域名你笔记本上当然解析不了。
所以得配两个 listener,内部
评论
还没有评论。
发表评论
提交后评论将经过自动审核,审核通过后公开展示。