Appearance
00. 总览与准备
这是一套“照着命令能搭起来”的 Runbook,不是过程日志。
目标读者:了解一点点 Kubernetes 和容器,但没完整搭过数据平台的人。
1. 你会搭出什么
text
3 台云服务器(Ubuntu 22.04,16C32G)
│
├─ node1 control-plane
├─ node2 worker
└─ node3 worker
│
├─ data namespace
│ ├─ PostgreSQL 16 元数据库
│ ├─ MySQL 8 业务库 + CDC 源
│ ├─ Kafka 3 broker CDC 消息
│ ├─ Flink JM + 2 TM 实时计算
│ ├─ Doris 1FE + 1BE 查询
│ ├─ Spark History Server 批任务监控
│ └─ DolphinScheduler 调度
│
├─ storage namespace
│ └─ SeaweedFS 3 master + 3 volume + S3
│
├─ harbor namespace
│ └─ Harbor 2.15
│
└─ openmetadata namespace
└─ OpenMetadata + OpenSearch最终端到端链路:
text
MySQL/PostgreSQL
→ Flink CDC
→ Kafka
→ Iceberg(S3)
→ Doris2. 为什么是这套组合
教程不是“把热门组件堆在一起”,每个组件都在解决一个具体问题。先理解取舍,再执行命令,出错时才知道该查哪一层。
| 选择 | 解决什么问题 | 代价 / 边界 |
|---|---|---|
| 3 节点 K8s | 让组件有统一调度、服务发现、滚动升级和故障漂移 | 单 control-plane 不是生产 HA;3 节点资源有限 |
| Flink 做实时 | CDC 是长期运行的有状态流,Flink 的 checkpoint、状态恢复更合适 | 需要 JM/TM、slot 和 checkpoint 存储 |
| Spark 做批量 | 大范围回刷、聚合、Iceberg 重写更适合批引擎 | driver/executor 启动慢,不适合低延迟单条查询 |
| Kafka 做缓冲 | 把源库变更和下游解耦,源库抖动不直接打挂湖仓 | 多一套消息系统要运维;要管分区/副本 |
| Iceberg + S3 | 一份数据多引擎读,支持快照、演进、批流共用 | 小文件、快照、孤儿文件需要维护 |
| PostgreSQL + JDBC catalog | 测试期不引入额外目录服务,元数据可查可备份 | 不是生产级 REST catalog;并发和权限弱 |
| Doris 做查询 | 面向 BI/交互式查询,比 Spark SQL 起步快 | 单 FE/BE 不是 HA;大 Join 仍要看数据模型 |
| DolphinScheduler | 把 Spark/Flink/Shell/SQL 统一编排,失败重试和依赖可见 | 需要外部 PG/ZK;调度器本身也要 HA |
| Harbor | 自建镜像仓库,K8s 拉镜像不依赖公网 | 要维护存储、证书、清理策略 |
| SHS | driver Pod 会重建,固定 History Server 才能查历史 | 事件日志目录要统一,否则只能看到一部分 |
2.1 为什么不直接上“全家桶”
- 全上 HA:3 节点资源不够,且会掩盖功能问题;
- 全用托管服务:成本高,且无法验证私有化部署;
- 全用单机脚本:没有 K8s 的调度和服务发现,后面还要重做。
本教程的目标是先跑通、再按瓶颈加 HA,不是一次到位。
3. 教程怎么读
每个步骤都按三段组织:
- 做什么:命令 / YAML;
- 为什么:这一步解决什么问题、不做会怎样;
- 怎么验证:预期输出,不通过不要继续。
命令里的 <...> 是占位符,执行前替换成自己的值;不要直接复制密码和 IP 到 Git。
3.1 先看懂 K8s YAML
后面的 manifest 都遵循同一套结构,先理解这几段:
| 字段 | 作用 |
|---|---|
apiVersion | 用哪个 API 版本创建对象 |
kind | 对象类型:Secret/ConfigMap/Service/StatefulSet... |
metadata.name | 对象名,同 namespace 内唯一 |
metadata.namespace | 放在哪个 namespace |
metadata.labels | 标签,Service/Deployment 用它选 Pod |
spec | 期望状态:副本数、镜像、端口、存储 |
spec.selector | 选择哪些 Pod 归这个控制器管 |
spec.template | Pod 模板:容器、环境变量、挂载 |
spec.containers[].image | 容器镜像,建议固定 tag |
spec.containers[].env | 环境变量,密码从 Secret 注入 |
spec.containers[].volumeMounts | 容器内挂载点 |
spec.volumes | ConfigMap/Secret/PVC 等卷定义 |
volumeClaimTemplates | StatefulSet 每个副本自动建 PVC |
status | 控制器回写的实际状态,不要手写 |
读 YAML 的方法:先看
kind判断对象类型,再看metadata确认名字/namespace,最后看spec的镜像、端口、存储、副本数。
4. 版本基线
| 组件 | 版本 |
|---|---|
| Ubuntu | 22.04 |
| Kubernetes | v1.37.1(kubeadm) |
| containerd | 2.2.1 |
| Calico | 3.30.3 |
| PostgreSQL | 16 |
| MySQL | 8 |
| Kafka | 3.9.0 |
| Flink | 1.20.5 |
| Spark | 3.5.9 |
| Iceberg | 1.6.1 |
| Doris | 4.1.0 |
| DolphinScheduler | 3.2.2 |
| OpenMetadata | 2.0.5 |
| Harbor | 2.15.4 |
5. 机器要求
| 项 | 最低 | 推荐 |
|---|---|---|
| 节点数 | 3 | 3 |
| CPU | 8C | 16C |
| 内存 | 16G | 32G |
| 系统盘 | 40G | 100G |
| 数据盘 | 100G | 200G |
| 带宽 | 5M | 100M |
| CPU 指令 | AVX2(Doris BE 需要) | AVX2 |
下单前确认:
- 三台在同一个 VPC/内网,能互相访问;
- 有公网 IP 或跳板机,能 SSH;
- 数据盘是裸盘,没有重要数据;
- 安全组能放行 NodePort 端口;
- CPU 支持 AVX2:
lscpu | grep avx2。
6. 变量约定
教程里所有命令都使用下面这些变量。先在一台机器上 export,再复制命令:
bash
# 节点 IP:把尖括号替换成你的真实值
export NODE1=<node1内网IP>
export NODE2=<node2内网IP>
export NODE3=<node3内网IP>
export NODE1_PUBLIC=<node1公网IP>
# 密码:自己设置,不要用示例值上生产
export SSH_PASS='<ssh密码>'
export PG_PASS='<postgres密码>'
export MYSQL_PASS='<mysql密码>'
export DORIS_PASS='<doris密码>'
export HARBOR_PASS='<harbor密码>'
export OM_PASS='<openmetadata密码>'
# 镜像仓库地址:Harbor 部署后填 node1 内网 IP:30002
export REGISTRY=<node1内网IP>:30002把变量保存到 ~/env.sh,每次新开终端先:
bash
source ~/env.sh不要把真实 IP、密码提交到 Git。
7. 目录规划
在三台机器上统一使用:
text
/data
├─ containerd containerd 数据
├─ k8s kubelet 数据
├─ local-path PVC 数据
├─ images 镜像 tar(临时)
└─ logs 日志(可选)教程里的 manifest 放在本地 deploy/:
text
deploy/
├─ 00-namespace.yaml
├─ 01-local-path-storage.yaml
├─ 10-postgres.yaml
├─ 20-mysql.yaml
├─ 30-kafka.yaml
├─ 40-flink.yaml
├─ 50-doris.yaml
├─ 60-spark-shs.yaml
├─ 70-ds-values.yaml
└─ 80-harbor-values.yaml8. 工具准备
在 node1 上安装:
bash
apt-get update
apt-get install -y curl wget jq unzip tar socat conntrack ipset \
apt-transport-https ca-certificates gnupg lsb-release检查:
bash
kubectl version --client
helm version
jq --version后面的命令默认在 node1 执行;需要到 node2/node3 执行的会单独标注。
9. 每步都要验证
教程按“操作 → 验证 → 再下一步”组织:
- 每段 YAML 后面都有
kubectl apply; - 每个组件后面都有
kubectl get/curl/ SQL 验证; - 验证不通过,不要继续下一步;
- 排障见 06.排障与清理。
10. 阅读顺序
| 顺序 | 页面 | 内容 |
|---|---|---|
| 0 | 本页 | 目标、变量、目录 |
| 1 | 节点与 Kubernetes | 系统、containerd、kubeadm、Calico |
| 2 | 存储与元数据 | local-path、PostgreSQL、SeaweedFS、Iceberg |
| 3 | 消息与实时 | Kafka、MySQL/PG、Flink CDC |
| 4 | 查询与调度 | Doris、Harbor、DolphinScheduler、SHS |
| 5 | Spark 与端到端 | Spark 镜像、批量、1M、Stream Load |
| 6 | 排障与清理 | 常见错误、销毁、复用 |
11. 时间预估
| 阶段 | 时间 |
|---|---|
| 节点 + K8s | 60–90 分钟 |
| 存储 + 元数据 | 30–45 分钟 |
| Kafka + Flink | 45–60 分钟 |
| Doris + DS + Harbor | 60–90 分钟 |
| Spark + 端到端 | 45–60 分钟 |
| 排障缓冲 | 30–60 分钟 |
第一次搭建议留 6 小时,不要按“1 小时能搞定”排期。