我们如何通过无头服务在 Kubernetes 中解决缓存失效

发布: (2025年12月29日 GMT+8 14:45)
6 分钟阅读
原文: Dev.to

Source: Dev.to

(请提供您希望翻译的正文内容,我将为您翻译成简体中文并保留原始的格式、Markdown 语法以及技术术语。)

🛑 问题:快速缓存与可扩展失效

我们决定使用 内存缓存(使用 @nestjs/cache-manager)来大幅降低数据库负载并提升响应时间。在 Kubernetes 集群中,服务运行在多个动态 pod 上,这会带来一个重大难题:缓存不一致

  • 如果管理员在 MySQL 中更新了套餐价格,只有处理写入的那个 pod 能立即看到变化。
  • 所有其他 pod 仍然提供陈旧的数据。

此外,pod 的 IP 是临时的;它们会在重启、部署和扩容时发生变化。硬编码 IP 列表几乎不可能,因此我们需要一种可靠的方式在数据更新的瞬间发现 所有 正在运行的 pod——一种广播机制。

🛠️ 解决方案:K8s 无头服务

我们的策略是混合方式:

  1. 本地缓存 以提升性能。
  2. 有针对性的内部 HTTP 广播 用于失效。

步骤 1 – 使用无头服务进行 Pod 发现

标准的 Kubernetes Service 充当带有单一虚拟 IP 的负载均衡器。无头服务clusterIP: None)跳过负载均衡器,返回每个活跃 Pod IP 地址的单独 DNS A 记录列表。

apiVersion: v1
kind: Service
metadata:
  name: booking-service-headless
spec:
  clusterIP: None               # THE TRICK: makes it headless
  selector:
    app: booking-service
  ports:
    - protocol: TCP
      port: 80
      targetPort: 3000

现在,解析 booking-service-headless.default.svc.cluster.local 会得到所有当前健康 Pod IP 的数组。

可视化 – 无头服务的实际运行
Kubernetes Headless Service Explained

步骤 2 & 3 – 实现广播

  1. 在 Docker 镜像中加入 kubectl(或使用 @kubernetes/client-node 库),使 Pod 能直接查询 Kubernetes API。
  2. 遵循最小权限原则:创建专用的 ServiceAccount 并绑定仅允许在其所在命名空间对 endpoints 资源执行 listgetRole。这样即使 Pod 被攻破,损害也被限制。
  3. 在预订服务上暴露内部 /invalidate-cache 接口。它接收缓存键并使用内存缓存的 del() 方法将其清除。

当管理员进行更新时,执行写入的 Pod 会运行脚本:

  • 从无头服务的 endpoints 中获取当前 Pod IP 列表。
  • 向每个 Pod 的 /invalidate-cache 接口发送 HTTP POST 请求。
# Executed by the Node.js process on update
pod_ips=$(kubectl get endpoints booking-service-headless \
          -o jsonpath='{.subsets[0].addresses[*].ip}')

for ip in $pod_ips; do
  curl -X POST http://$ip:3000/v2/invalidate \
       -H "Content-Type: application/json" \
       -d '{"key":"package-123"}'   # Clears the specific key
done

为什么可行

✅ 功能描述
动态在失效时获取最新的 IP 列表,能够无缝处理上下扩容和重启。
有针对性只清除陈旧的包键,最大化缓存保留。
成本低不需要额外的服务或外部消息中间件。
可靠curl 添加重试和日志记录,以处理广播失败。

替代方案 – 如果不想在镜像中安装 kubectl/curl,可以使用官方的 @kubernetes/client-node 库。它让你的 Node.js 代码直接查询 Kubernetes API,逻辑更易于测试,错误处理更完善,并且省去启动 shell 进程的开销。

📈 生产结果与收获

部署后:

  • MySQL 负载 持续保持在低水平。
  • 缓存命中率 显著提升(≈ 95 % 的包查询)。
  • 响应延迟 从约 200 ms 降至缓存请求的 < 30 ms。
  • 运维开销 仍然极低——没有新增服务、没有额外费用,且受 RBAC 限制的 ServiceAccount 将安全风险控制在最小范围。

关键经验

  1. Headless Service 是一种简单、内置的 pod 级别发现机制。
  2. 内存缓存 + 显式失效 能在不增加运维复杂度的情况下,提供分布式缓存的性能。
  3. 最小权限 ServiceAccount 能在容器内部暴露内部工具时,仍然保护集群安全。

如果你处理的大多是静态数据但仍需偶尔更新,这种模式提供了一种低成本、Kubernetes 原生的方式来保持每个 pod 的缓存同步。

结果概述

  • 可用性: 保持在 95 %+ 以上。
  • 事件: 未出现用户看到过期包数据的情况。
  • Headless Service: 该技巧在高流量部署期间也表现稳健。

Graph

收获
该方案展示了最优雅且成本效益最高的答案并不一定是昂贵的托管服务——它可以是对已有工具的创造性运用。关键在于聪明地使用基础设施,而不是单纯地向问题投放金钱。

Back to Blog

相关文章

阅读更多 »