时序数据库后端数据存储可观测性云原生【免费下载链接】mimirGrafana Mimir provides horizontally scalable, highly available, multi-tenant, long-term storage for Prometheus.项目地址https://gitcode.com/gh_mirrors/mimir/mimir点击查看免费下载导读kuberesolver 是一个通过 Kubernetes API 为 gRPC 提供名称解析Name Resolver的轻量级库它内置了约 250 行代码的精简 Kubernetes 客户端用于发现 Service 的 EndpointSlice因此不会显著增大二进制体积。在 Grafana Mimir 的 Kubernetes 部署场景中kuberesolver 与 dskit 的 httpgrpc 客户端配合让各组件能够以kubernetes://协议动态解析集群内 Service 的真实端点地址并结合客户端侧负载均衡实现零停机滚动部署。读完本文你将掌握 kuberesolver 的注册方式、目标 URL 的完整语法、与 DNS 直连的本质区别、RBAC 权限要求、TLS 注意事项以及它在 Mimir 代码库中的真实落地路径。kuberesolver 是什么为 gRPC 而生的 Kubernetes 名称解析器kuberesolvervendor/github.com/sercand/kuberesolver/v6/README.md是一个 gRPC 名称解析器grpc name resolver。它的核心定位是用 Kubernetes API 替代 DNS 来解析服务地址。它自带一个非常小的 Kubernetes 客户端约 250 行代码只做一件事——查询并监听 Service 的 EndpointSlice 信息。由于不依赖client-go这类重型依赖它wont bloat your binaries非常适合对二进制体积敏感的组件。在 gRPC 的命名体系grpc naming docs中一个 URI 由scheme://authority/endpoint组成。kuberesolver 向 gRPC 注册自己的 Builder声明它支持kubernetes这个 scheme。此后只要 gRPC Dial 的目标 URL 以kubernetes://开头gRPC 就会把解析工作交给 kuberesolver。快速上手注册并拨号注册解析器使用前需要在调用grpc.Dial之前把 kuberesolver 注册到 gRPC// Import the module import github.com/sercand/kuberesolver/v6 // Register kuberesolver to grpc before calling grpc.Dial kuberesolver.RegisterInCluster() // it is same as resolver.Register(kuberesolver.NewBuilder(nil /*custom kubernetes client*/ , kubernetes))从源码看RegisterInCluster()实际就是RegisterInClusterWithSchema(kubernetesSchema)的简写builder.go而kubernetesSchema常量为字符串kubernetes。NewBuilder接收两个参数client K8sClient自定义 Kubernetes 客户端接口传nil时Build内部会自动调用NewInClusterK8sClient()创建集群内客户端builder.goschema string该解析器负责的 scheme 名称默认kubernetes。拨号建立连接注册完成后gRPC 遇到kubernetesschema 就会使用 kuberesolver 解析地址// if schema is kubernetes then grpc will use kuberesolver to resolve addresses cc, err : grpc.Dial(kubernetes:///service.namespace:portname, opts...)使用自定义 Schema如果需要使用其他 scheme例如grpc://可在启动时改用RegisterInClusterWithSchema(schema)代替RegisterInCluster()。支持的目标 URL 语法kuberesolver 接受以下形式的 URL详见 READMEkubernetes:///service-name:8080 kubernetes:///service-name:portname kubernetes:///service-name.namespace:8080 kubernetes:///service-name.namespace.svc.cluster_name kubernetes:///service-name.namespace.svc.cluster_name:8080 kubernetes://namespace/service-name:8080 kubernetes://service-name:8080/ kubernetes://service-name.namespace:8080/ kubernetes://service-name.namespace.svc.cluster_name kubernetes://service-name.namespace.svc.cluster_name:8080其中service-name是 Kubernetes Service 名称namespace是命名空间portname是 Service 端口的命名端口named portcluster_name是集群域名。重要提示cluster_name不会参与端点解析它只是为了让完整限定域名fully qualified service name写法合法例如test.default.svc.cluster.local。底层是如何解析这些 URL 的parseResolverTarget 按三种形态拆分目标kubernetes:///service.namespace:portURL.Host 为空从 Endpoint 路径中拆出 service、port、namespacekubernetes://namespace/service:portURL.Host 是 namespace从 Endpoint 中拆出 service 与 portkubernetes://service.namespace:port直接从 URL.Host 拆分。拆分逻辑splitServicePortNamespace先按最后一个冒号切出端口再按.最多切三段取前两段作为 service 与 namespace因此能正确处理service-name.namespace.svc.cluster-domain-name这类完整域名并忽略集群域名部分builder.go。关于端口的三种处理方式也在该函数中确定未指定端口 →useFirstPort true取 EndpointSlice 中第一个端口的实际数值端口是纯数字如8080→ 直接使用端口是非数字字符串如grpc→resolveByPortName true按命名端口匹配。如果目标未指定 namespace则通过读取/var/run/secrets/kubernetes.io/serviceaccount/namespace获取当前 Pod 所在命名空间读取失败时回退为defaultkubernetes.go。客户端侧负载均衡配合 Balancer 实现零停机kuberesolver 只负责解析出全部端点地址真正的流量分发需要配合客户端侧负载均衡策略。拨号时需要传入grpc.WithBalancerNamegrpc.DialContext(ctx, kubernetes:///service:grpc, grpc.WithBalancerName(round_robin), grpc.WithInsecure())这会让 gRPC 为每个可用 Service 端点建立子连接subconnection并按轮询round robin策略分发请求。为什么这能实现零停机部署每次端点集合变化Pod 滚动、扩缩容、就绪状态变化时kuberesolver 通过cc.UpdateState(resolver.State{...})向 gRPC 推送最新的地址列表builder.gogRPC 会断开不再存在的子连接同时为新地址建立连接因此滚动发布过程中请求会平滑地从旧 Pod 迁移到新 Pod实现零停机。makeAddresses构造地址时只保留Conditions.Ready true的端点builder.go确保只有处于就绪状态的 Pod 才会被纳入负载均衡池同时把ServerName设置为service.namespace形式的地址为后续 TLS 连接预留了 SNI 信息。与直接拨号service.namespace:8080的本质区别很多人会问既然 Kubernetes 自带 DNS为什么不直接grpc.Dial(service.namespace:8080)README 给出了明确的解释直接拨号走 DNSDNS 只返回 Service 的稳定 ClusterIPgRPC 只能看到这一个 ClusterIP不知道背后每个 Endpoint 的真实 Pod IP当某个 Pod 故障或 Service 端点变化时gRPC无法感知并重连到正确的目标造成请求失败或无法利用多副本能力。而 kuberesolver 通过 Kubernetes API获取并持续监听 Service 的 EndpointSlice把每个可用 Pod 的 IP:Port 都提供给 gRPC。配合客户端侧负载均衡器就能实现请求均匀分布到所有就绪副本故障 Pod 被自动剔除新副本自动加入滚动部署期间流量平滑切换实现零停机。源码级工作机制Watch 与兜底轮询kuberesolver 解析器的运行循环由 kResolver.watch 驱动其工作方式如下启动即全量拉取watchEndpointSlice会先发起一个 watch 流Kubernetes watch 语义保证流建立时会先推送当前已有的全部对象watch endpoints lists existing endpoints at start增量监听后续的ADDED、MODIFIED、DELETED事件见 models.go会实时驱动kResolver.handle更新 gRPC 状态定时兜底轮询内置一个默认30 分钟的定时器defaultFreq time.Minute * 30builder.go定时调用resolve()重新 GET 一次 EndpointSlice 列表防止 watch 流静默失效导致状态陈旧断线自动重连外层until循环util.go采用指数退避重试首次间隔 1 秒此后每次翻倍达到 30 秒上限后重置回 1 秒并捕获 panic 记录日志避免崩溃。Watch 流解析细节stream.go 中的streamWatcher将 Kubernetes watch 的 chunked JSON 流逐条解码为Event仅在ADDED / MODIFIED / DELETED / ERROR四种类型中放行其他类型视为非法事件遇到io.EOF、context.Canceled等正常终止情况会静默退出。自带的可观测性指标kuberesolver 通过 Prometheuspromauto注册了三个 Gauge 指标builder.go标签为target形如kubernetes://namespace/service:port指标名含义kuberesolver_endpoints_total目标 Service 的端点Endpoint数量kuberesolver_addresses_total实际下发到 gRPC 的可用地址数量kuberesolver_client_last_update解析器客户端最后一次成功更新的 Unix 时间戳这三个指标在 Mimir 集群运维中可直接用于排查地址解析是否跟上 Pod 变化。集群内客户端的构建方式NewInClusterK8sClientkubernetes.go在 Pod 内通过环境变量与挂载文件构建客户端从环境变量KUBERNETES_SERVICE_HOST与KUBERNETES_SERVICE_PORT得到 API Server 地址从/var/run/secrets/kubernetes.io/serviceaccount/token读取 ServiceAccount 令牌从/var/run/secrets/kubernetes.io/serviceaccount/ca.crt读取 CA 证书构造 HTTPS 客户端动态刷新令牌用fsnotify监听 token 文件Kubernetes 通过符号链接轮换 ServiceAccount 令牌时会触发Remove/Write事件客户端随即重新读取并更新 token源码注释明确说明这是为了兼容 configmap 的符号链接机制。此外还提供了NewInsecureK8sClient(apiURL)适用于通过代理访问 Kubernetes API 等非集群内场景。RBAC 权限要求如果集群启用了 RBAC运行 kuberesolver 的 ServiceAccount 需要具备对endpointslices资源的GET和WATCH权限。一个参考的最小 RBAC 配置apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole metadata: name: kuberesolver-endpointslices rules: - apiGroups: [discovery.k8s.io] resources: [endpointslices] verbs: [get, watch] --- apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRoleBinding metadata: name: kuberesolver-endpointslices subjects: - kind: ServiceAccount name: your-service-account namespace: your-namespace roleRef: kind: ClusterRole name: kuberesolver-endpointslices apiGroup: rbac.authorization.k8s.io从请求 URL 可以确认权限粒度kuberesolver 实际请求的是GET /apis/discovery.k8s.io/v1/namespaces/{ns}/endpointslices?labelSelectorkubernetes.io/service-name{svc}与对应的/watch/...路径kubernetes.go通过kubernetes.io/service-name标签选择器精确过滤目标 Service 的 EndpointSlice。使用 TLS如果要通过 TLS 连接后端服务需要准备一个名为service-name.namespace的证书。也就是说证书的通用名CN必须与服务目标的service.namespace完全匹配这样 kuberesolver 在构造resolver.Address时填充的ServerName才能与证书匹配从而通过 TLS 主机名校验。在 Grafana Mimir 中的实际落地kuberesolver 并不是 Mimir 直接 import 的依赖在 go.mod 中为 indirect 依赖而是通过 Mimir 依赖的 dskit 库间接使用。真实的调用链如下注册dskit 的 httpgrpc 客户端在NewClient(address)中调用kuberesolver.RegisterInCluster()vendor/github.com/grafana/dskit/httpgrpc/server/server.go确保解析器在拨号前完成注册URL 归一化ParseURL负责兼容处理直接透传带:///的 kuberesolver v2 URL无 scheme 的地址按kubernetes处理向后兼容direct://原样返回kubernetes://则把service.namespace:port规范化为kubernetes:///service.namespace:port形式server.go负载均衡拨号时通过grpc.WithDefaultServiceConfig传入{loadBalancingPolicy:round_robin}即用 round_robin 策略配合 kuberesolver 解析出的多端点实现客户端侧负载均衡使用场景Mimir 的 pkg/mimir/modules.go 使用httpgrpc_server.NewServer(...)提供 gRPC 化 HTTP 转发服务配合 query-scheduler 的 worker 在组件间通信pkg/alertmanager/multitenant.go、pkg/distributor/push.go 等文件也均引用了github.com/grafana/dskit/httpgrpc/server。这些组件以 Kubernetes Deployment/StatefulSet 方式部署时即可通过kubernetes:///地址解析彼此的真实端点。从 Mimir 的 CHANGELOG.md 也可以看到项目持续跟进 kuberesolver 的版本升级如从 v2.4.0 升级到 v4.0.0 以配合 gRPC 1.53.0 的 API 变化当前 vendored 版本为 v6.0.1说明该解析器在 Mimir 的长期演进中一直承担着集群内服务发现的关键职责。小结与最佳实践拨号前先调用kuberesolver.RegisterInCluster()或自定义 schema 版本完成注册目标地址优先使用kubernetes:///service.namespace:portname规范形式命名空间缺省时自动使用 Pod 所在命名空间务必为 ServiceAccount 配置endpointslices的get与watch权限需要多副本负载均衡与零停机滚动时配合round_robin客户端负载均衡策略而不要直接 DNS 拨号 Service ClusterIP使用 TLS 时确保证书 CN 为service-name.namespace可通过kuberesolver_endpoints_total、kuberesolver_addresses_total与kuberesolver_client_last_update三个指标观察解析器健康状态。赞分享时序数据库后端数据存储可观测性云原生【免费下载链接】mimirGrafana Mimir provides horizontally scalable, highly available, multi-tenant, long-term storage for Prometheus.项目地址https://gitcode.com/gh_mirrors/mimir/mimir点击查看免费下载相关推荐WAN2.2 All In OneAI视频生成在消费级硬件上的技术突破WAN2.2 All In OneAI视频生成在消费级硬件上的技术突破 技术挑战与解决方案演进 当前AI视频生成面临的核心技术瓶颈在于计算资源消耗与生成质量的人工智能计算机视觉基础模型Kubernetes 集群中的 Traefik Ingress服务发现与负载均衡实战指南Kubernetes 集群中的 Traefik Ingress服务发现与负载均衡实战指南 导读 本指南以 Traefik 作为 Ingress Control云原生教程PyTextRank源码深度剖析掌握四大TextRank算法的实现细节PyTextRank源码深度剖析掌握四大TextRank算法的实现细节 PyTextRank是一个基于Python的TextRank算法实现库专注于短语提取上一篇Oh My Zsh redis-cli 插件为 Redis 命令行客户端提供智能补全下一篇AdaNet模型部署指南从训练到生产环境的完整流程创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考