201 lines
6.8 KiB
Markdown
201 lines
6.8 KiB
Markdown
|
|
---
|
|||
|
|
tags:
|
|||
|
|
- MQ
|
|||
|
|
- Kubernetes
|
|||
|
|
- 容器化
|
|||
|
|
- 运维
|
|||
|
|
create time: 2026-05-24 19:52
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
# MQ 容器化与 K8s 部署
|
|||
|
|
|
|||
|
|
## 概述
|
|||
|
|
|
|||
|
|
将消息队列部署到 Kubernetes 上,是有状态服务容器化的核心挑战之一。本文从持久存储、网络标识、有序部署三个维度分析难点,介绍 StatefulSet、Operator 模式、Helm Chart 等主流方案,并以 Strimzi Kafka Operator 为例展示生产级部署实践。
|
|||
|
|
|
|||
|
|
## 正文
|
|||
|
|
|
|||
|
|
### 为什么 MQ 上 K8s 是个难题
|
|||
|
|
|
|||
|
|
Kubernetes 天生为无状态服务设计——Pod 可以随时被杀掉重建、IP 随时变化、多个副本之间没有区别。但 MQ 作为有状态服务,恰恰对这三点都很敏感:
|
|||
|
|
|
|||
|
|
- **持久存储**:消息必须落盘,Pod 重建后数据不能丢
|
|||
|
|
- **网络标识**:Broker 之间需要通过固定地址互相发现(如 Kafka 的 `broker.id` 对应固定端点)
|
|||
|
|
- **有序部署**:集群启动/扩容时需要按顺序进行,不能所有 Pod 同时拉起
|
|||
|
|
|
|||
|
|
> [!question]
|
|||
|
|
> 想象一下 Kafka 的 3 个 Broker 同时启动并互相注册,会发生什么?为什么有序启动很重要?
|
|||
|
|
|
|||
|
|
### StatefulSet:有状态应用的基石
|
|||
|
|
|
|||
|
|
StatefulSet 是 K8s 专门为有状态服务设计的控制器,解决了 Deployment 的两个核心问题:
|
|||
|
|
|
|||
|
|
1. **稳定的网络标识**:每个 Pod 拥有固定名称(`kafka-0`、`kafka-1`、`kafka-2`),配合 Headless Service 可以通过 DNS 直接访问
|
|||
|
|
2. **稳定的持久存储**:每个 Pod 绑定独立的 PVC(PersistentVolumeClaim),Pod 重建后自动挂载原来的卷
|
|||
|
|
|
|||
|
|
```yaml
|
|||
|
|
# StatefulSet 核心配置片段
|
|||
|
|
apiVersion: apps/v1
|
|||
|
|
kind: StatefulSet
|
|||
|
|
metadata:
|
|||
|
|
name: kafka
|
|||
|
|
spec:
|
|||
|
|
serviceName: kafka-headless # 关联 Headless Service
|
|||
|
|
replicas: 3
|
|||
|
|
template:
|
|||
|
|
spec:
|
|||
|
|
containers:
|
|||
|
|
- name: kafka
|
|||
|
|
volumeMounts:
|
|||
|
|
- name: data
|
|||
|
|
mountPath: /var/lib/kafka/data
|
|||
|
|
volumeClaimTemplates: # 每个 Pod 自动创建独立 PVC
|
|||
|
|
- metadata:
|
|||
|
|
name: data
|
|||
|
|
spec:
|
|||
|
|
accessModes: ["ReadWriteOnce"]
|
|||
|
|
storageClassName: fast-ssd
|
|||
|
|
resources:
|
|||
|
|
requests:
|
|||
|
|
storage: 100Gi
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
但 StatefulSet 只解决了基础设施层面的问题。**什么时候该扩容、扩容后 Partition 怎么重分配、配置变更怎么滚动生效**——这些业务运维知识它一概不管。于是 Operator 登场了。
|
|||
|
|
|
|||
|
|
### Operator 模式:把运维知识编码
|
|||
|
|
|
|||
|
|
Operator = Custom Resource Definition(CRD)+ Custom Controller。核心思想是:**将人类运维专家的知识编码为程序**,让 K8s 自动执行运维操作。
|
|||
|
|
|
|||
|
|
对于 MQ 来说,Operator 能做到:
|
|||
|
|
- 一键部署完整集群(含 ZooKeeper/KRaft)
|
|||
|
|
- 滚动升级 Broker 版本(保证零停机)
|
|||
|
|
- 自动处理 Partition 重分配
|
|||
|
|
- 证书自动签发和轮转
|
|||
|
|
|
|||
|
|
主流 MQ Operator:
|
|||
|
|
|
|||
|
|
| MQ | Operator | 维护方 |
|
|||
|
|
|----|----------|--------|
|
|||
|
|
| Kafka | Strimzi | CNCF |
|
|||
|
|
| RocketMQ | rocketmq-operator | Apache |
|
|||
|
|
| RabbitMQ | cluster-operator | VMware |
|
|||
|
|
| Pulsar | pulsar-helm-chart | Apache |
|
|||
|
|
|
|||
|
|
### Strimzi Kafka Operator 实战
|
|||
|
|
|
|||
|
|
Strimzi 是 CNCF 孵化项目,通过 CRD 将 Kafka 集群声明式管理。核心 CRD 有三个:
|
|||
|
|
|
|||
|
|
- **Kafka**:定义 Kafka 集群拓扑(Broker 数量、存储、配置)
|
|||
|
|
- **KafkaTopic**:声明式管理 Topic
|
|||
|
|
- **KafkaUser**:声明式管理用户和 ACL 权限
|
|||
|
|
|
|||
|
|
```yaml
|
|||
|
|
# Strimzi Kafka CR 示例:3 节点集群
|
|||
|
|
apiVersion: kafka.strimzi.io/v1beta2
|
|||
|
|
kind: Kafka
|
|||
|
|
metadata:
|
|||
|
|
name: my-cluster
|
|||
|
|
spec:
|
|||
|
|
kafka:
|
|||
|
|
version: 3.7.0
|
|||
|
|
replicas: 3
|
|||
|
|
listeners:
|
|||
|
|
- name: plain
|
|||
|
|
port: 9092
|
|||
|
|
type: internal
|
|||
|
|
tls: false
|
|||
|
|
- name: tls
|
|||
|
|
port: 9093
|
|||
|
|
type: internal
|
|||
|
|
tls: true
|
|||
|
|
config:
|
|||
|
|
offsets.topic.replication.factor: 3
|
|||
|
|
transaction.state.log.replication.factor: 3
|
|||
|
|
transaction.state.log.min.isr: 2
|
|||
|
|
default.replication.factor: 3
|
|||
|
|
min.insync.replicas: 2
|
|||
|
|
storage:
|
|||
|
|
type: jbod
|
|||
|
|
volumes:
|
|||
|
|
- id: 0
|
|||
|
|
type: persistent-claim
|
|||
|
|
size: 100Gi
|
|||
|
|
class: fast-ssd
|
|||
|
|
zookeeper:
|
|||
|
|
replicas: 3
|
|||
|
|
storage:
|
|||
|
|
type: persistent-claim
|
|||
|
|
size: 20Gi
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
Kafka 集群的完整部署架构如下:
|
|||
|
|
|
|||
|
|
```mermaid
|
|||
|
|
graph TB
|
|||
|
|
subgraph K8s["Kubernetes Cluster"]
|
|||
|
|
subgraph NS["namespace: kafka"]
|
|||
|
|
Operator["Strimzi Operator"]
|
|||
|
|
CR["Kafka CR"]
|
|||
|
|
Operator -->|"watch & reconcile"| CR
|
|||
|
|
CR -->|"create"| STS["StatefulSet"]
|
|||
|
|
STS -->|"manage"| Pod0["kafka-0"]
|
|||
|
|
STS -->|"manage"| Pod1["kafka-1"]
|
|||
|
|
STS -->|"manage"| Pod2["kafka-2"]
|
|||
|
|
Pod0 ---|"PVC"| PV0["PV-0 100Gi SSD"]
|
|||
|
|
Pod1 ---|"PVC"| PV1["PV-1 100Gi SSD"]
|
|||
|
|
Pod2 ---|"PVC"| PV2["PV-2 100Gi SSD"]
|
|||
|
|
HS["Headless Service"] -->|"DNS: kafka-0.kafka-headless"| Pod0
|
|||
|
|
HS -->|"DNS: kafka-1.kafka-headless"| Pod1
|
|||
|
|
HS -->|"DNS: kafka-2.kafka-headless"| Pod2
|
|||
|
|
LB["LoadBalancer Service"] -->|"external access"| Pod0
|
|||
|
|
LB --> Pod1
|
|||
|
|
LB --> Pod2
|
|||
|
|
end
|
|||
|
|
end
|
|||
|
|
Client["Client App"] -->|"produce/consume"| LB
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### Helm Chart 部署
|
|||
|
|
|
|||
|
|
对于不想引入 Operator 的轻量场景,Helm Chart 是更简单的选择。Bitnami 提供了完善的 Kafka Helm Chart,核心优势是参数化配置和环境隔离:
|
|||
|
|
|
|||
|
|
```bash
|
|||
|
|
# 安装 Kafka 集群
|
|||
|
|
helm install my-kafka bitnami/kafka \
|
|||
|
|
--namespace kafka --create-namespace \
|
|||
|
|
--set replicaCount=3 \
|
|||
|
|
--set persistence.size=100Gi \
|
|||
|
|
--set persistence.storageClass=fast-ssd \
|
|||
|
|
--set resources.requests.memory=4Gi \
|
|||
|
|
--set resources.requests.cpu=2 \
|
|||
|
|
--set resources.limits.memory=6Gi \
|
|||
|
|
--set resources.limits.cpu=4
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
Helm 适合快速搭建和开发/测试环境,但在生产环境中,Operator 的自动化运维能力(升级、扩容、故障恢复)是 Helm 无法替代的。
|
|||
|
|
|
|||
|
|
### 资源规划建议
|
|||
|
|
|
|||
|
|
MQ 在 K8s 上的资源规划需要特别注意:
|
|||
|
|
|
|||
|
|
- **CPU/Memory**:Kafka Broker 建议至少 2C4G,生产环境推荐 4C8G。请求值和限制值都要设置,避免被驱逐或抢占资源
|
|||
|
|
- **存储**:SSD 用于高吞吐场景(低延迟随机读写),HDD 用于大容量归档。通过 StorageClass 区分
|
|||
|
|
- **JVM 堆**:Kafka Broker 堆不宜过大(4-6GB 足够),留更多内存给页缓存。推荐 G1GC 或 ZGC
|
|||
|
|
- **磁盘调度**:容器内使用 `none`(noop)调度算法,避免与宿主机调度冲突
|
|||
|
|
|
|||
|
|
### 网络方案
|
|||
|
|
|
|||
|
|
| 方式 | 适用场景 | 特点 |
|
|||
|
|
|------|----------|------|
|
|||
|
|
| Headless Service | Broker 间内部通信 | Pod 直连,无负载均衡 |
|
|||
|
|
| ClusterIP | 集群内客户端访问 | 默认方式 |
|
|||
|
|
| NodePort | 开发测试外部访问 | 端口范围 30000-32767 |
|
|||
|
|
| LoadBalancer | 生产外部访问 | 云厂商 LB,费用较高 |
|
|||
|
|
| Ingress | HTTP 协议暴露 | MQ 一般不走 HTTP,适用 REST Proxy |
|
|||
|
|
|
|||
|
|
## 关联笔记
|
|||
|
|
|
|||
|
|
- [[40-MQ-性能调优]]
|
|||
|
|
- [[42-MQ-认证与授权]]
|
|||
|
|
- [[43-MQ-加密与审计]]
|