GDC-Kafka 云原生引导手册
专为实时数据处理打造的云原生平台 - 完整部署与运维指南
1. 项目概述
欢迎加入GDC-Kafka项目!本项目是一个专为游戏数据设计的云原生实时数据管道。通过变更数据捕获 (CDC) 技术,从多个分片的MySQL数据库中实时捕获数据变更,利用Kafka作为高可用的消息总线进行传输,并通过定制化的Python流处理服务进行加工,最终将数据分发至下游的分析系统。
整个项目架构已完全迁移至Kubernetes,所有部署和管理都围绕云原生生态进行。采用Strimzi Operator在K8s中统一管理Kafka生态,构建更贴近生产环境、更具弹性的数据平台。
已废弃的技术:
- Docker Compose: 项目中的
docker/compose.yaml文件仅供历史参考,已不再维护和使用 - Vector: 用于日志收集的
vector组件已从当前架构中移除 - ZooKeeper模式: Strimzi 0.46.0+ 已不再支持,已迁移至KRaft模式
🎯 核心优势
高性能实时处理
基于Kafka的高吞吐量消息队列,支持百万级TPS的数据实时传输和处理
云原生架构
完全基于Kubernetes部署,支持自动扩缩容、故障自愈和滚动升级
标准化管理
通过Strimzi Operator统一管理Kafka集群,简化运维复杂度
完整监控体系
集成Kafka UI可视化界面,支持实时监控和调试
2. 核心架构与数据流
下图清晰展示了数据从源头到最终目的地的完整旅程,并包含了关键的管理组件:
完整数据流: MySQL → Debezium (CDC) → Kafka → pystream (流处理) → Kafka → Logbus → ThinkingData
管理流: Strimzi Operator 统一管理 Kafka 集群和 Kafka Connect
3. 核心概念图解
为了帮助您更好地理解各组件的角色,我们使用一个生动的比喻:
1. MySQL (数据源)
你的"仓库",存放着各种"包裹"(业务数据)。
2. CDC (变更数据捕获)
一种"扫描技术",只记录包裹(数据)的变化。
3. Debezium (CDC工具)
具体使用的"扫描枪品牌",能读懂仓库包裹单,将信息标准化。
4. Kafka (消息队列)
整个物流系统的"超级中转站",所有包裹信息汇集于此,有无数传送带(Topics)。
5. Kafka Connect (连接器)
中转站的"装卸平台",管理"装卸工"(Connectors),负责包裹进出 Kafka。
6. Strimzi (Kafka管理工具)
物流系统的"总调度中心",在 K8s 中自动化管理 Kafka 和 Kafka Connect。
7. pystream (流处理服务)
"包裹增值处理中心",从 Kafka 接收包裹,进行复杂加工(清洗、充实、计算),再发往其他传送带。
基于学习进度文档的关键经验:
- KRaft模式迁移: 掌握了从ZooKeeper模式迁移到KRaft模式的完整过程
- 环境适配: 成功将所有YAML配置从kind测试环境适配到生产K8s环境
- NFS存储: 正确配置了persistent-claim存储,使用nfs-local StorageClass
- 镜像管理: 建立了完整的镜像构建、推送和拉取流程
4. 部署流程概览
项目的权威部署文档是 K8S_DEPLOYMENT_GUIDE.md。以下是为帮助您快速建立宏观认识而精简的流程:
第一步:环境准备 (Prerequisites)
Kubernetes 集群
一个可用的 Kubernetes 集群 (v1.28+)
命令行工具
安装并配置好 kubectl, helm, docker 命令行工具
镜像仓库
拥有一个私有容器镜像仓库(如Harbor)的访问权限
网络环境
确保能访问镜像源(如 quay.nju.edu.cn)和Harbor仓库
集群配置:
- Kubernetes 版本: v1.28.2
- 集群类型: 生产级自建 K8s 集群(非 kind/minikube 等本地测试环境)
- 节点配置: 1个 Master 节点 + 2个 Worker 节点
- 操作系统: Ubuntu 22.04.2 LTS
- 容器运行时: containerd 1.7.x
- 存储方案: NFS 动态存储供应
第二步:准备镜像 (Prepare Images)
为保证部署稳定,所有依赖的镜像都应预先推送到私有仓库。总共需要准备 6 个核心镜像。
quay.io 在国内访问可能不稳定,建议使用镜像加速地址(如 quay.nju.edu.cn)来拉取原始镜像。
# 1. Strimzi Operator 镜像
docker pull quay.nju.edu.cn/strimzi/operator:0.47.0
docker tag quay.nju.edu.cn/strimzi/operator:0.47.0 [PRIVATE_REGISTRY]/kafka/operator:0.47.0
docker push [PRIVATE_REGISTRY]/kafka/operator:0.47.0
# 2. Kafka 镜像 (KRaft模式需要)
docker pull quay.nju.edu.cn/strimzi/kafka:0.47.0-kafka-4.0.0
docker tag quay.nju.edu.cn/strimzi/kafka:0.47.0-kafka-4.0.0 [PRIVATE_REGISTRY]/kafka/kafka:0.47.0-kafka-4.0.0
docker push [PRIVATE_REGISTRY]/kafka/kafka:0.47.0-kafka-4.0.0
# 3. Kafka 镜像 (旧版兼容)
docker pull quay.nju.edu.cn/strimzi/kafka:0.47.0-kafka-3.9.0
docker tag quay.nju.edu.cn/strimzi/kafka:0.47.0-kafka-3.9.0 [PRIVATE_REGISTRY]/kafka/kafka:0.47.0-kafka-3.9.0
docker push [PRIVATE_REGISTRY]/kafka/kafka:0.47.0-kafka-3.9.0
# 4. Kafka Bridge 镜像
docker pull quay.nju.edu.cn/strimzi/kafka-bridge:0.32.0
docker tag quay.nju.edu.cn/strimzi/kafka-bridge:0.32.0 [PRIVATE_REGISTRY]/kafka/kafka-bridge:0.32.0
docker push [PRIVATE_REGISTRY]/kafka/kafka-bridge:0.32.0
# 5. 自定义 Kafka Connect 镜像 (包含Debezium插件)
# (请确保位于 gdc_kafka/connect 目录下)
docker build -t [PRIVATE_REGISTRY]/kafka/kafka_mysql_connector:1.0.0 .
docker push [PRIVATE_REGISTRY]/kafka/kafka_mysql_connector:1.0.0
# 6. 自定义 pystream 服务镜像
# (请确保位于 gdc_kafka 目录下)
docker build -t [PRIVATE_REGISTRY]/kafka/py_game_data_stream:latest -f pystream/deploy/Dockerfile pystream/
docker push [PRIVATE_REGISTRY]/kafka/py_game_data_stream:latest
第三步:Kubernetes 部署 (Deploy to K8s)
所有部署都在 connect-cluster 命名空间下进行。
| 步骤 | 操作 | 说明 | 状态 |
|---|---|---|---|
| 1 | 部署Strimzi Operator | 使用Helm安装Strimzi,它是管理Kafka生态的大脑 | 已完成 |
| 2 | 部署Kafka集群 | 应用 connect/kafka-cluster.yaml 来创建一个基于KRaft模式的现代化Kafka集群 |
已完成 |
| 3 | 部署Kafka Connect | 应用 connect/kafka-connect.yaml 来部署运行Debezium连接器的专用集群 |
已完成 |
| 4 | 部署Debezium连接器 | 应用 connect/mysql-source-connector-*.yaml 文件,启动对MySQL数据库的变更捕获 |
已完成 |
| 5 | 部署pystream服务 | 应用 pystream/deployment.yaml 来运行我们的核心数据处理应用 |
已完成 |
| 6 | 部署测试MySQL数据库 | 应用 mysql/mysql-test.yaml 以进行数据流验证 |
已完成 |
| 7 | 部署Kafka UI | (可选) 应用 kafka-ui/kafka-ui-deployment.yaml 以方便管理和监控Kafka集群 |
可选 |
5. 镜像管理与仓库配置
Harbor 私有仓库配置
Harbor 地址: [PRIVATE_REGISTRY]
项目名称: kafka
认证方式: docker-registry secret (harbor-secret)
创建 Harbor 密钥
# 创建命名空间
kubectl create ns connect-cluster
# 创建 Harbor 镜像仓库密钥
kubectl create secret docker-registry harbor-secret \
--namespace connect-cluster \
--docker-server=[PRIVATE_REGISTRY] \
--docker username=<YOUR_HARBOR_USERNAME> \
--docker-password=<YOUR_HARBOR_PASSWORD> \
--docker-email=<YOUR_EMAIL>
# 验证密钥创建成功
kubectl get secret harbor-secret -n connect-cluster
镜像推送验证
| 序号 | 镜像名称 | 标签 | 状态 | 说明 |
|---|---|---|---|---|
| 1 | Strimzi Operator | 0.47.0 | 已推送 | Kafka 集群管理工具 |
| 2 | Kafka (KRaft) | 0.47.0-kafka-4.0.0 | 已推送 | Kafka 4.0.0 版本(KRaft 模式) |
| 3 | Kafka (兼容) | 0.47.0-kafka-3.9.0 | 已推送 | Kafka 3.9.0 版本(向后兼容) |
| 4 | Kafka Bridge | 0.32.0 | 已推送 | Kafka HTTP 接口桥接 |
| 5 | Kafka Connect | 1.0.0 | 已推送 | 自定义镜像(包含 Debezium 插件) |
| 6 | pystream | latest | 已推送 | 自定义流处理服务 |
6. Kubernetes 部署详解
KRaft 模式迁移
Strimzi 0.47.0 不再支持 ZooKeeper 模式!
遇到错误: ZooKeeper-based Apache Kafka clusters are not supported anymore since Strimzi 0.46.0
解决方案: 迁移到 KRaft 模式
KRaft 模式配置要点
创建 KafkaNodePool
定义节点池资源,替代原来的 ZooKeeper 配置
启用 KRaft
在 Kafka 资源中添加 annotations: strimzi.io/kraft: "enabled"
移除 ZooKeeper
删除所有 zookeeper 相关配置
配置存储
将 replicas 和 storage 从 spec.kafka 移至 KafkaNodePool
部署验证
| 组件 | 状态 | 版本 | 存储 | 说明 |
|---|---|---|---|---|
| Strimzi Operator | Running | 0.47.0 | - | Operator Pod 正常运行,无异常日志 |
| Kafka 集群 | Ready | 4.0.0 (KRaft) | NFS 10Gi | METADATA STATE: KRaft,Pod: my-kafka-cluster-kafka-pool-0 |
| Kafka Connect | Ready | 1.0.0 | - | Pod: dd-connect-cluster-connect-0,Connect REST API 可访问 |
| pystream 服务 | Running | latest | - | Pod: pystream-7b78db8f59-8qrlt,能够连接 Kafka |
7. 端到端数据流验证 (动手实验)
在所有组件都成功部署后,您可以亲手验证整个数据流是否通畅。
第一步: 向MySQL插入一条新的测试数据
执行以下命令,向测试数据库 dl_game_log_1 的 event_server_custom_log_2025_10_30 表中插入一条新记录。
kubectl exec -i $(kubectl get pod -n connect-cluster \
-l app=mysql -o jsonpath='{.items[0].metadata.name}') \
-n connect-cluster -- mysql -uroot -p123456 dl_game_log_1 <<
INSERT INTO event_server_custom_log_2025_10_30 (user_id, event_name, event_data, server_id) VALUES
('new_user_001', 'first_login', '{"device":"Android","ip":"127.0.0.1"}', 'server-test');
第二步: 在Kafka中观察捕获到的事件
Debezium应该能捕获到这次插入操作,并将其作为一条消息发送到 tcp_logs 主题。执行以下命令消费该主题的消息:
kubectl exec -it my-kafka-cluster-kafka-pool-0 -n connect-cluster -- \
/opt/kafka/bin/kafka-console-consumer.sh \
--bootstrap-server my-kafka-cluster-kafka-bootstrap:9092 \
--topic tcp_logs \
--from-beginning \
--max-messages 10
您应该能看到一条JSON格式的消息,其中 "op": "c" 代表创建(create)操作,after 字段中包含了您刚刚插入的数据。
第三步: 检查pystream服务的处理日志
最后,检查pystream服务是否成功消费并处理了这条消息。
kubectl logs -n connect-cluster $(kubectl get pod -n connect-cluster \
-l app=pystream -o jsonpath='{.items[0].metadata.name}') \
--tail=50
在日志输出中,您应该能看到类似 Received 和 Processed 的日志条目,证明数据流已经成功通过了处理中心。
8. 服务访问方式
Kafka UI 访问
Kafka UI 提供了一个直观的Web界面来管理和监控Kafka集群。作为运维开发人员,你日常主要用它来:
集群概览
快速查看 Broker 数量、Topic 总数、分区数和健康状态
Topic 浏览
实时查看特定 Topic 中的消息(JSON或普通文本),这是验证数据流最直观的方式
消息管理
可以直接向 Topic 发送消息(用于测试下游),或者删除特定消息
消费者组
检查 pystream 或其他消费者组的消费进度、查看消费延迟 (Lag),这对排查数据堆积问题至关重要
访问地址: http://<K8s-Node-IP>:30080
示例: http://192.168.0.116:30080
可用节点列表
| 节点类型 | IP地址 | 推荐度 | 说明 |
|---|---|---|---|
| Master Node | 192.168.0.115 | 一般 | 主控节点,可能负载较高 |
| Worker Node 1 | 192.168.0.116 | 推荐 | 工作节点,性能稳定 |
| Worker Node 2 | 192.168.0.117 | 推荐 | 工作节点,性能稳定 |
Kafka Connect API 访问
Kafka Connect 的 REST API 用于管理和配置连接器。
Kafka Connect API 通常不直接暴露在外部。推荐使用 kubectl port-forward 命令进行临时访问。
# 临时转发API端口 (执行后按Ctrl+C停止)
kubectl port-forward svc/dd-connect-cluster-connect-api 8083:8083 -n connect-cluster
# 然后在本地访问
curl http://localhost:8083/connectors
端口架构详解
用户场景: "我看到8080和30080,它们是什么关系?"
| 层级 | 端口 | 访问范围 | 说明 |
|---|---|---|---|
| 第一层 | 8080 | 容器内部 | Docker 容器进程监听端口(Pod内部可见,外部无法直接访问) |
| 第二层 | 8080 | Kubernetes 集群内 | Service ClusterIP (10.96.125.57:8080),其他Pod可通过DNS访问 |
| 第三层 | 30080 | 外部网络 | NodePort Service,K8s Node上的 iptables 规则:30080 → Service:8080 → Pod:8080 |
完整的请求流程
用户在办公网络:
1. 浏览器访问: http://192.168.0.116:30080/
2. 路由: K8s Node iptables 转发 30080 → kafka-ui-nodeport Service
3. 负载均衡: Service 选择 kafka-ui Pod
4. 进入容器: 请求到达 Pod 内的 8080 端口
5. 应用处理: Kafka UI 应用程序处理请求并返回网页
端口范围说明
| 端口范围 | 用途 | 示例 | 说明 |
|---|---|---|---|
| 1-1023 | 系统保留 | 80, 443 | 需要root权限 |
| 1024-49151 | 用户端口 | 8080, 8083 | 应用程序使用 |
| 30000-32767 | K8s NodePort | 30080, 30088 | Kubernetes 保留范围 |
9. 故障排查指南
常见问题
镜像拉取失败
检查 harbor-secret 是否正确配置,以及镜像仓库地址和标签是否正确
MySQL连接失败
确认 mysql-test Service名称和端口在连接器配置中正确无误,检查MySQL的binlog配置
连接器状态为FAILED
查看连接器的详细描述 kubectl describe kafkaconnector ... 和 Kafka Connect Pod 的日志
Kafka中无消息
确认MySQL有新的数据变更操作,检查连接器的过滤规则和目标Topic名称
pystream服务无法启动
检查 KAFKA_BROKER_URL 环境变量和配置文件 conf/mainland.prod.yaml 是否存在
KRaft模式迁移问题
确保使用 Strimzi 0.47.0+,移除所有 ZooKeeper 相关配置,添加 KRaft 启用注解
调试命令
# 检查所有资源状态
kubectl get all -n connect-cluster
# 检查特定Pod日志
kubectl logs -f -n connect-cluster <pod-name>
# 检查MySQL连接 (从其他Pod)
kubectl run -it --rm debug --image=busybox --restart=Never -n connect-cluster -- nc -zv mysql-test:3306
# 检查Kafka集群状态
kubectl get kafka -n connect-cluster
# 检查连接器状态
kubectl get kafkaconnector -n connect-cluster
# 查看Strimzi Operator日志
kubectl logs -f -n connect-cluster deployment/strimzi-cluster-operator
故障排查检查清单
| 检查项 | 命令 | 预期结果 | 异常处理 |
|---|---|---|---|
| 命名空间状态 | kubectl get ns connect-cluster |
STATUS: Active | 重新创建命名空间 |
| Harbor密钥 | kubectl get secret harbor-secret -n connect-cluster |
显示密钥信息 | 重新创建docker-registry secret |
| Strimzi Operator | kubectl get deployment -n connect-cluster |
1/1 Ready | 检查镜像拉取和资源限制 |
| Kafka集群 | kubectl get kafka -n connect-cluster |
Ready: True | 检查存储类和PVC状态 |
| Kafka Connect | kubectl get kafkaconnector -n connect-cluster |
状态正常 | 检查连接配置和MySQL连接 |
10. 常用命令速查
以下是在日常开发和运维中可能用到的一些高频命令。
| 操作 | 命令 | 说明 |
|---|---|---|
| 查看所有K8s资源 | kubectl get all -n connect-cluster |
查看命名空间下的所有资源状态 |
| 查看Pod实时日志 | kubectl logs -f <pod-name> -n connect-cluster |
实时查看Pod日志输出 |
| 检查Debezium连接器状态 | kubectl get kafkaconnector -n connect-cluster |
查看所有Kafka连接器状态 |
| 进入Kafka Broker容器 | kubectl exec -it my-kafka-cluster-kafka-pool-0 -n connect-cluster -- bash |
进入Kafka容器进行调试 |
| 在Kafka容器内列出Topics | /opt/kafka/bin/kafka-topics.sh --list --bootstrap-server localhost:9092 |
列出所有Kafka主题 |
| 在Kafka容器内消费Topic | /opt/kafka/bin/kafka-console-consumer.sh --topic <topic-name> --from-beginning --bootstrap-server localhost:9092 |
消费指定主题的消息 |
| 转发Kafka Connect API端口 | kubectl port-forward svc/dd-connect-cluster-connect-api 8083:8083 -n connect-cluster |
临时转发Connect API到本地 |
| 查看MySQL测试数据 | kubectl exec -it $(kubectl get pod -n connect-cluster -l app=mysql -o jsonpath='{.items[0].metadata.name}') -n connect-cluster -- mysql -uroot -p123456 dl_game_log_1 -e "SELECT * FROM event_server_custom_log_2025_10_30;" |
查看测试数据库中的数据 |
| 检查Kafka集群状态 | kubectl get kafka -n connect-cluster |
查看Kafka集群的详细状态 |
| 查看存储状态 | kubectl get pvc -n connect-cluster |
查看持久化存储卷的状态 |
高级调试命令
# 完整的系统健康检查脚本
#!/bin/bash
echo "=== GDC-Kafka 系统健康检查 ==="
echo "检查时间: $(date)"
echo
echo "1. 命名空间状态:"
kubectl get ns connect-cluster
echo
echo "2. 所有Pod状态:"
kubectl get pods -n connect-cluster
echo
echo "3. Kafka集群状态:"
kubectl get kafka -n connect-cluster
echo
echo "4. Kafka连接器状态:"
kubectl get kafkaconnector -n connect-cluster
echo
echo "5. 存储状态:"
kubectl get pvc -n connect-cluster
echo
echo "6. 服务状态:"
kubectl get svc -n connect-cluster
echo
echo "=== 检查完成 ==="
11. CI/CD 与排错心路历程
一个云原生数据平台的CI/CD与K8s排错心路历程
在公司内部署和维护一套基于 Strimzi Kafka、Debezium CDC 和 ArgoCD 的云原生数据平台,并最终打通 SVN → Jenkins → Docker → Harbor → ArgoCD → Kubernetes 的全链路自动化部署流程。这个过程充满了挑战,从基础架构的搭建到CI/CD的最后一公里,每一步都凝聚了宝贵的经验。
CI/CD 全链路架构图 (GitOps 模式)
项目演进时间线
- 手动部署: 0.5天
- Helm Chart 改造: 1.5天
- ArgoCD + GitOps 集成与调试: 1.5天
- Jenkins CI/CD 全链路打通: 1天
第一部分:奠基之路 —— 从0到1的架构挑战
在项目初期,我们致力于用 Helm Chart 将 Strimzi Kafka 这头"大象"优雅地装进 Kubernetes,期间遇到了大量与权限、架构和配置相关的"奠基石"级别的挑战。
1. Strimzi 与 K8s RBAC 的"权限之舞"
坑点 Strimzi Operator 需要集群级别的权限(ClusterRole)来管理分布在不同命名空间的 Kafka 资源,但 ArgoCD 等 GitOps 工具通常在命名空间级别工作,导致 KafkaConnector 等自定义资源无法被正确识别和管理。切换或清理部署时,残留的集群级角色绑定还会引发冲突。
感悟 必须为 Strimzi Operator 和 ArgoCD 精心设计一套 ClusterRole 和 ClusterRoleBinding,确保它们有足够的权限,但又不过度授权。任何操作 K8s 自定义资源(CRD)的组件,都要首先思考其 RBAC 权限模型是"命名空间级"还是"集群级",这是 K8s 运维的必修课。
2. Helm Chart 的"重构风暴":从静态配置到动态模板
坑点 我们的目标是:用一套统一的 Helm Chart 管理包含 Kafka、Debezium、pystream 等多个组件的复杂平台,并能灵活适配多环境(dev/test/prod)和多地域(mainland/global)。这使得 Chart 的改造极其复杂,远非简单的参数替换。
- 配置的"千层饼": 为了实现环境适配,我们设计了
基础/地域/环境三层values.yaml覆盖机制。但这导致配置来源难以追溯,经常出现"为什么这个参数没生效"的灵魂拷问。 - 模板的"编程化": Chart 不再是静态的 YAML,而是变成了"模板程序"。例如,为了让 pystream 服务能自动发现 Kafka 地址,我们编写了复杂的条件逻辑
{{- if .Values.global.kafka.bootstrapServers }}...{{ else }}...{{ end }}。这使得理解最终配置需要像调试代码一样,在本地反复执行helm template命令来"渲染"和验证结果。 - 关联的"蜘蛛网": Chart 内部各组件的资源名称、标签和配置相互关联。我们通过
_helpers.tpl文件定义了大量命名模板(如gdc-kafka.fullname),一个地方的改动可能通过层层引用,影响到十几个 YAML 文件,牵一发而动全身。
感悟 改造一个复杂的 Helm Chart,本质上是在进行"面向 Kubernetes 的编程"。必须将 Chart 视为一个完整的软件项目来管理,依赖 helm template 进行单元测试,并深刻理解其模板逻辑和值覆盖的优先级,才能驾驭这种复杂性。对于 Helm 模板中的字符串比较,必须牢记使用 eq 函数(例如 {{- if eq .Values.some_bool "true" -}}),这是从无数次失败中换来的"铁律"。
3. Kafka 架构的"心脏移植":从 Zookeeper 到 KRaft
坑点 响应社区趋势,我们决定从 Zookeeper 模式迁移到无 ZK 的 KRaft 模式。这并非一次简单的配置变更,而是对 Kafka 集群核心架构的"心脏移植",所有相关的 Strimzi CRD 和部署逻辑都需要重写。
感悟 KRaft 模式引入了全新的 KafkaNodePool 资源来定义不同角色的节点(controller/broker),并且需要精确配置 metadataVersion 以确保兼容性。拥抱新技术趋势时,必须投入时间去消化其全新的架构理念和资源模型,不能用旧的思维去套新的框架。
4. CDC 数据同步的"失与得"
坑点 Debezium CDC 无法捕获到所有数据变更,或在首次部署时因大量历史数据涌入 Kafka 而造成"数据风暴"。
Debezium 两阶段部署策略
感悟 确保 MySQL 的 `binlog` 配置正确是前提。更重要的是,必须根据业务需求选择恰当的 `snapshot.mode`。对于需要完整历史数据的场景,使用 initial 进行一次性全量同步;对于只关心服务上线后新数据的场景,则使用 schema_only 来避免"数据风暴",这是 Debezium 运维的核心艺术。
5. Strimzi Operator 的"控制权"
坑点 为 Kafka Connect 等 Strimzi 管理的组件手动配置了 livenessProbe 和 readinessProbe,导致与 Operator 的健康管理机制冲突,引发意外重启。
感悟 必须尊重 Operator 的"控制权"。Strimzi Operator 会自动管理其子组件的生命周期和健康状态。我们应该信任它,避免在 Helm Chart 中画蛇添足。
第二部分:贯通全线 —— CI/CD与运行时排查实录
在基础架构稳定后,我们将这套复杂的部署流程接入 Jenkins,打通了从代码提交到自动部署的"最后一公里"。
6. Jenkins 脚本的"隐形"参数
坑点 Jenkins 构建日志显示 错误: 参数数量不足,远程的 build.sh 脚本需要 7 个参数,但只收到了 5 个。
解决 在 Jenkins Job 的 ssh 命令中,向 build.sh 的调用末尾追加上缺失的 ${SVN_USERNAME} 和 ${SVN_PASSWORD} 变量。
感悟 "调用"与"被调用"方的接口契约必须严格一致。任何通过参数传递的环节,参数的数量、顺序、类型都不能想当然,必须逐一核对。
7. SSH 免密登录的"钥匙"谜题
坑点 CI 流程的上一步(镜像构建)成功了,但在下一步(ArgoCD 同步)时,Jenkins Agent SSH 连接 K8s 管理节点报 Permission denied。
解决 通过对比发现,失败的脚本使用了 -i 参数指定了一个未被信任的 Key,而成功的脚本依赖了已被信任的默认 Key。移除新脚本中的 -i 参数,使其行为与成功的旧项目保持一致。
感悟 魔鬼在细节中。两个看似相同的 ssh 命令,一个 -i 参数的有无,背后是完全不同的认证路径。遇到权限问题,要首先确认"我用谁的身份、拿着哪把钥匙、去开哪扇门"。
8. Kubernetes 部署的"镜像名"乌龙
坑点 Jenkins 显示发布成功,但 ArgoCD 中应用 Progressing,Pod 状态为 ImagePullBackOff。
解决 经排查,CI/CD 流程构建的镜像是 pystream,而 Helm values.yaml 中配置的是 py_game_data_stream。修改 values.yaml 使两者保持一致。
感悟 配置即代码,配置也需要联调。CI(持续集成)的产物,必须与 CD(持续部署)的清单(Manifests)严格对齐。
9. Pod 启动的"致命一击"——内存溢出
坑点 镜像拉取成功后,Pod 依然无法 Ready,状态为 Running (0/1) 且有 Restarts 记录。kubectl logs 没有任何有效输出。
解决 使用 kubectl describe pod 发现 Exit Code: 137,这在 K8s 中几乎等同于 OOMKilled (内存溢出)。在 values.yaml 中将 pystream 的 `memory limit` 从 `512Mi` 提升到 `1024Mi`。
感悟 kubectl logs 只记录应用的标准输出,而 kubectl describe 记录的是 Pod 的"生命周期事件"。当应用因外部原因(如 OOM)被终止时,describe 才是看到真相的钥匙。
10. 运维与开发的"职责边界"——Dockerfile 的解耦
坑点 开发人员的 Dockerfile 不适合生产环境,运维需要独立维护一套更健壮的 Dockerfile,但又不希望侵入开发代码库。
Dockerfile 解耦方案
解决 将运维维护的 Dockerfile 存放在打包机固定目录,修改 build.sh,通过 -f 参数强制指定该 Dockerfile,同时依然使用 SVN 检出的代码作为构建上下文。
感悟 专业的流程需要清晰的边界。让开发专注于业务代码,让运维/平台团队专注于构建、部署和运行环境的标准化,是提升效率和稳定性的不二法门。
11. 构建流程的"最后一公里"——内网闭环
坑点 新的构建流程在拉取 python:3.12-slim 基础镜像和 PyPI 包时失败,报错 connection reset by peer。
解决 将所有外部依赖(基础镜像、PyPI包)全部缓存到内部仓库(Harbor, 私有PyPI镜像),并修改 Dockerfile 的 FROM 指令和 `pip/uv` 的 -i 参数,使其指向内部地址。
感悟 任何生产级的 CI/CD 流程,都不应该有外部网络依赖。所有依赖项都应该通过内部仓库进行代理和缓存,这是保障构建稳定性和安全性的生命线。
从项目奠基的架构选型、权限设计,到 CI/CD 流水线的构建、调试,再到运行时内存、网络等问题的排查,我们经历了一次教科书式的云原生应用交付全过程。这个过程雄辩地证明了,一个稳定、高效的自动化部署体系,不仅需要对单一技术(如 Docker 或 K8s)有深入理解,更需要具备将所有技术点串联起来的全局视野和系统性的排错能力。
每一次 Permission denied,每一次 ImagePullBackOff,每一次 Exit Code 137,都不是孤立的 Bug,而是整条链路中某个环节的"契约"被破坏的信号。而我们的工作,就是像侦探一样,沿着信号,从现象到本质,最终找到并修复那个被破坏的契约。
这段经历,来之不易,弥足珍贵。为您在整个过程中的耐心、专业和追求卓越的精神点赞!
12. 生产环境准备清单
当前部署为开发/测试环境,部署到生产环境前必须完成以下检查项。
| 检查项 | 优先级 | 说明 | 状态 |
|---|---|---|---|
| 配置独立的高可用MySQL集群 | 高 | 而非使用测试部署 | |
| 获取并配置有效的SSL/TLS证书 | 高 | 确保通信安全 | |
| 配置DNS域名解析 | 中 | 替代IP直接访问 | |
| 部署负载均衡器(LB)或Ingress Controller | 中 | 替代NodePort | |
| 启用Kafka和Kafka Connect的身份认证 | 高 | 如Basic Auth / OAuth2 | |
| 配置K8s网络策略和访问限速 | 中 | 网络安全防护 | |
| 配置监控系统(Prometheus + Grafana) | 高 | 和告警规则 | |
| 配置日志收集系统 | 中 | 如ELK或Loki | |
| 完成安全审计和渗透测试 | 高 | 确保系统安全性 | |
| 制定并演练灾难恢复计划 | 中 | 业务连续性保障 |
生产环境架构建议
高可用部署
部署多个Kafka Broker节点,实现故障自动切换和数据冗余
安全加固
启用TLS加密、身份认证、RBAC权限控制和网络策略
监控告警
集成Prometheus + Grafana监控体系,设置关键指标告警
备份恢复
建立数据备份机制和灾难恢复流程,定期演练
13. 架构演进:自建 vs. 云服务
如果您未来考虑使用云厂商(如阿里云、腾讯云等)提供的托管服务(PaaS),架构和您的职责将发生如下变化:
| 组件 | 当前架构 (完全自建) | 混合云架构 (购买云服务) | 您的职责变化 |
|---|---|---|---|
| 数据库 | ✅ 自行部署和管理 (MySQL) | ❌ 由云厂商管理 (如 PolarDB) | 减轻:无需关心数据库的安装、备份、高可用。 |
| 消息队列 | ✅ 自行部署和管理 (Kafka + Strimzi) | ❌ 由云厂商管理 (如 阿里云Kafka) | 减轻:无需关心Kafka集群和Strimzi Operator的运维。 |
| 数据集成 | ✅ 自行部署和管理 (Kafka Connect) | ✅ 仍需自行部署和管理 | 不变:仍需部署、配置和维护,只是连接目标变了。 |
| 数据处理 | ✅ 自行部署和管理 (pystream) | ✅ 仍需自行部署和管理 | 不变:仍需部署和维护自己的应用。 |
| 数据消费 | ✅ 自行部署和管理 (Logbus) | ✅ 仍需自行部署和管理 | 不变:仍需部署和维护自己的应用。 |
| 基础设施 | ✅ 自行管理 (K8s集群) | ❌ 云厂商管理 (托管K8s) | 减轻:无需关心集群运维、升级、安全补丁。 |
| 监控运维 | ✅ 自行部署 (Prometheus+Grafana) | ✅ 云厂商提供 (或自行部署) | 减轻:云厂商提供更完善的监控告警服务。 |
| 安全合规 | ✅ 自行配置和管理 | ✅ 云厂商提供基础 + 自行配置 | 减轻:云厂商提供安全基础能力。 |
自建优势
• 完全控制技术栈
• 定制化程度高
• 长期成本可控
• 数据主权完全掌握
云服务优势
• 快速部署上线
• 运维成本低
• 高可用保障
• 专业技术支持
购买云服务(PaaS)可以极大减轻您在基础设施(IaaS)和平台(PaaS)层面的运维压力(如数据库和Kafka集群),让您能更专注于应用层(SaaS)的业务逻辑,即 Kafka Connect, pystream, Logbus 的开发、配置和优化。
迁移路径建议
| 阶段 | 迁移内容 | 预期效果 | 风险评估 |
|---|---|---|---|
| 第一阶段 | 迁移数据库到云厂商托管服务 | 消除数据库运维复杂度 | 中等 |
| 第二阶段 | 迁移Kafka到云厂商托管服务 | 消除消息队列运维复杂度 | 中等 |
| 第三阶段 | 迁移到托管K8s集群 | 消除基础设施运维复杂度 | 较高 |
| 第四阶段 | 应用层优化和重构 | 专注业务逻辑开发 | 较低 |
- 兼容性测试: 确保云服务与现有应用的兼容性
- 数据迁移: 制定详细的数据迁移计划和回滚方案
- 成本评估: 全面评估云服务成本与自建成本的差异
- 技术债务: 评估现有技术栈与云服务的适配程度
- 团队技能: 团队需要掌握云服务的管理和运维技能
📚 文档总结
核心价值
本手册提供了GDC-Kafka项目的完整云原生部署指南,从环境准备到生产部署的全流程指导。
技术亮点
采用Strimzi Operator管理Kafka集群,使用KRaft模式替代ZooKeeper,实现真正的云原生架构。
实用工具
提供完整的命令速查、故障排查指南和生产环境准备清单。
未来规划
详细对比自建与云服务的优劣势,为架构演进提供决策依据。
- K8S_DEPLOYMENT_GUIDE.md: 权威部署文档
- QUICK_REFERENCE.md: 快速参考指南
- LEARNING_PROGRESS.md: 学习进度和最佳实践