Consumer Group 协议:分配、重平衡与静态成员
前面四篇走完了写入端。这一篇转向读取端的核心问题:一组 consumer 怎么瓜分一个 topic 的所有 partition。 这个问题经常被简化成"负载均衡",但 consumer group 协议解决的不是负载均衡——它解决的是 partition 所有权的分配与变更。分配发生在 group 成员变化时,而每次变更的代价远比想象中大。 本文只抓一个问题:consumer group 的分配协议是怎么运作的,重平衡为什么贵,怎么降低代价。 consumer group 的基本契约 Kafka consumer group 有一条硬约束:同一个 group 内,一个 partition 在任意时刻只能被一个 consumer 消费。 这条约束的直接后果: group 内 consumer 数量超过 partition 数量时,多出来的 consumer 空转,分不到任何 partition。 partition 的消费进度由唯一的 consumer 负责推进,不存在两个 consumer 同时消费同一 partition 的情况。 consumer 数量变...
幂等 Producer 与序列号:消息不重不丢的第一层
上一篇走完了 Producer 的发送管线。这一篇解决一个写入端的经典问题:网络超时后重试,消息会不会写两遍。 重试导致重复是分布式系统的常见症状。Producer 发出一个请求,broker 写入成功但响应在网络中丢失,Producer 不知道成功了于是重发——同一条消息在日志中出现了两次。Kafka 从 0.11 版本开始,在 broker 端引入了基于序列号的去重机制,称为 idempotent producer。 本文只抓一个问题:幂等 Producer 是怎么用 PID 和序列号在 broker 端做去重的,它的边界在哪里。 重复问题的根源 先看一个没有幂等保护时的场景: 1234567891011Producer Broker (Leader) | | |--- ProduceRequest(msg-A) ------->| | |--- 写入 partition log...
Producer 内部机制:攒批、分区与 acks
上一篇拆解了 partition 内部的存储结构。这一篇转向写入端——Producer 把一条消息发出去,到底经过了哪些步骤。 很多使用者把 producer.send() 当成一次网络调用。实际情况是,send() 只是把消息丢进了一个内存缓冲区,真正的网络 I/O 发生在另一个线程里。Producer 内部是一条两阶段管线:主线程负责序列化和分区选择,后台 Sender 线程负责攒批和网络传输。 本文只抓一个问题:一条消息从 send() 调用到 broker 确认,经过了哪些对象、哪些线程、哪些等待。 发送管线总览 Producer 内部的数据流可以压成下面的路径: 123456789101112131415161718192021222324252627282930Main Thread Sender Thread | | v | Interceptors ...
日志存储:Segment、Index 与零拷贝
上一篇看到了 Kafka 集群的全景:broker 存储日志,controller 管理元数据,topic 通过 partition 映射到磁盘目录。这一篇进入单个 partition 内部,看日志文件到底怎么组织。 一个 partition 不是一个巨大的文件。更准确的说法是:一个 partition 是一组按 offset 范围切分的 segment 文件,每个 segment 由数据文件、offset 索引文件和时间索引文件组成。 本文只抓一个问题:一条 produce 请求写入的字节如何变成磁盘上的 segment 文件,以及 fetch 请求如何通过零拷贝把这些字节送到网络上。 Segment 文件:日志的物理切片 一个 partition 的日志目录中,segment 文件按 base offset 命名。Base offset 是该 segment 中第一条记录的 offset,用 20 位数字左补零表示。 12345678910partition 目录: orders-0/├── 00000000000000000000.log ← segme...
生产集群运维——升级、备份、故障排查与容量规划
把 Kubernetes 集群跑起来是第一步,让它在生产环境中长期稳定运行才是真正的挑战。版本升级、etcd 备份恢复、节点维护、故障排查、容量规划——这些 Day-2 运维场景,每一个都有足以让集群中断服务的操作风险。Kubernetes 为这些场景提供了明确的工具和流程约定,但操作顺序和细节上的失误仍然是生产事故的主要来源。 版本升级是最高风险的操作之一。Kubernetes 的 API 在版本间存在兼容性约束(skew policy),错误的升级顺序——例如先升级 kubelet 而非先升级 control plane——会导致集群进入不一致状态,轻则调度异常,重则 API Server 无法与节点通信。etcd 备份看似简单,但备份时机(必须在升级前)、恢复流程(必须停止 API Server 再恢复)、验证方式(恢复后检查关键对象)如果有任何一步出错,都可能导致数据丢失或集群无法启动。 节点维护(drain + upgrade + uncordon)有 PodDisruptionBudget(PDB)这道安全网,但前提是 PDB 本身配置正确。容量规划需要理解"...
可观测性——Metrics、Logging、Tracing 的集群实践
一个运行在 Kubernetes 上的分布式系统,出问题时最难回答的问题是"发生了什么"。单机时代,登录服务器查看进程状态和日志文件,基本能还原现场。容器化之后,Pod 随时可能被调度到不同节点,崩溃后立即重启,原始日志消失,这套方法彻底失效。可观测性(Observability)是对这个问题的系统性回答:通过在系统内部埋点、采集、存储和展示三类信号——Metrics(指标)、Logging(日志)、Tracing(链路追踪)——让工程师在不登录服务器的情况下理解系统行为。 Kubernetes 本身对可观测性有明确的架构分工。采集点(kubelet 内嵌的 cAdvisor、node-exporter DaemonSet、应用 SDK)由各组件负责暴露,聚合层(Prometheus、metrics-server)负责拉取和存储,展示层(Grafana、Jaeger UI)负责查询和可视化。这种"采集点内建、存储和展示留给生态"的设计,让 Kubernetes 本身保持简洁,同时允许不同规模的集群选择不同的存储方案。 理解可观测性的关键区分...
Helm 与应用打包——从 YAML 到可复用制品
在 Kubernetes 上部署一个生产级应用,往往需要几十个甚至上百个 YAML 文件:Deployment、Service、ConfigMap、Ingress、RBAC 规则、HorizontalPodAutoscaler……这些文件之间存在依赖关系,不同环境(开发、预发、生产)需要不同参数,手工管理极易出错。Helm 正是为了解决这个问题而生:把一组相关的 Kubernetes 资源打包成一个可版本化、可参数化、可共享的制品(Chart),并提供安装、升级、回滚、卸载的生命周期管理。 Helm 的本质是"带版本历史的 YAML 打包器"。它不追踪资源的真实运行状态(那是 Operator 的职责),只记录"这次 install/upgrade 渲染出了哪些 manifest,交给 Kubernetes 执行"。Release 状态存储在集群内的 Secret 对象中(key 为 helm.sh/release.v1),每次升级都新增一个版本,rollback 本质上是重新 apply 旧版本的 manifest 集合。 理解 Helm...
CRD 与 Operator 模式——扩展 Kubernetes 的标准路径
Kubernetes 的核心设计哲学之一是可扩展性。API Server 内置的资源类型——Pod、Deployment、Service——只能覆盖通用计算模型,但现实中的应用远不止于此:数据库集群有自己的主从切换逻辑,消息队列有 topic 副本的分配策略,机器学习训练任务有 GPU 亲和性和检查点管理。CustomResourceDefinition(CRD)和 Operator 模式正是 Kubernetes 给出的扩展机制:让用户把领域知识编码进集群本身,而不是写在外部脚本或运维手册里。 CRD 的核心能力是向 API Server 注册新的 Group/Version/Kind(GVK)。注册完成后,kubectl get myapp 和 kubectl get pod 在 API 层面对等——同样走 REST 接口,同样存储到 etcd,同样支持 watch、label selector 和 RBAC 授权。Operator 在 CRD 之上再加一层:部署一个 Controller,持续监听自定义资源的变化,把用户声明的期望状态翻译成若干内置 Kubernetes 资...
资源管理与自动伸缩:requests、limits、HPA 与 VPA
安全模型控制谁能做什么,资源管理控制能用多少。这两个维度共同决定了集群的多租户能力。requests 和 limits 是 Kubernetes 资源模型的基础,理解它们的精确语义是做好容量规划的前提。HPA 和 VPA 在此基础上提供了自动化的弹性能力。 在传统物理机和虚拟机时代,资源隔离靠硬件分区或 hypervisor,资源弹性靠人工扩容。Kubernetes 把资源管理内置为一等公民,通过 cgroup 在内核层面强制执行,通过控制器实现自动化弹性。这一篇的核心问题是:requests 和 limits 如何影响调度和运行时行为,HPA 和 VPA 如何根据负载自动伸缩? 资源模型全景 1234567891011121314151617181920212223242526调度时(kube-scheduler) ┌────────────────────────────────────────────────────────────┐ │ Node Allocatable = Node Capacity - System Reserved │ │ ─...
RBAC 与安全模型:认证、授权与准入控制
密钥只有在有效的访问控制下才有意义。Kubernetes 的安全控制分三层:认证(谁在访问)、授权(能做什么)、准入控制(允许什么操作)。三层各有职责,缺一不可。 在单体应用时代,安全通常集中在网络边界和数据库访问控制。容器化和微服务之后,工作负载数量激增,身份变得复杂:一个 Pod 就是一个独立的执行主体,可能需要读取 Secret、调用其他服务、操作集群资源。Kubernetes 的安全模型由哪三个层次组成、RBAC 的 Role/RoleBinding/ClusterRole/ClusterRoleBinding 如何组合使用,是本篇的核心内容。 三层安全模型 12345678910111213141516171819202122232425262728293031323334请求到达 API Server │ ▼┌──────────────────────────────────┐│ 1. Authentication(谁?) ││ ──────────────────────────── ││ X.509 cert → U...
