大数据K8S基础:从 Pod 到 Service,看懂数据负载如何在云原生上运行

一、引言

大数据工程师学习 Kubernetes,最容易陷入两个误区:要么把 K8s 当成"另一个 Yarn",只关心任务能不能提交;要么一上来学习 Deployment、StatefulSet、Operator、Ingress、Gateway、CNI、CSI、CRD 等一整套生态,结果概念很多,和日常数据作业的关系却不清楚。更有效的入口,是先理解数据负载在 K8s 上如何被创建、放置、限制、挂载和访问。

文章聚焦五个和大数据最相关的 K8s 基础概念:Pod 生命周期、CPU/内存请求与限制、调度机制、卷模型、Service 与网络路径。它们分别回答五个工程问题:一个数据任务如何开始和结束,资源怎么被预留和约束,任务为什么被调度到某台节点,中间数据和持久数据该放在哪里,以及 Driver、Executor、JobManager、TaskManager、Coordinator、Worker 之间如何通信。

二、K8s 整体架构与角色分工

一个 Kubernetes 集群由控制平面和一组工作节点组成,控制平面负责保存和协调集群状态,工作节点负责真正运行 Pod。

kube-apiserver是 Kubernetes API 的入口,所有对象的创建、查询、更新和删除都要经过它;etcd是一致且高可用的键值存储,用来保存 API Server 的数据;kube-scheduler查找尚未绑定节点的 Pod,并把它们分配到合适的节点;kube-controller-manager运行各种控制器,让集群实际状态逐步靠近声明的期望状态;cloud-controller-manager是可选组件,用来对接底层云厂商能力。

工作节点上的角色更贴近数据工程师日常排障。kubelet负责确保 Pod 及其容器正在运行;容器运行时负责真正启动容器;kube-proxy通常负责维护节点上的网络规则,以实现 Service 访问路径。

组件 所在位置 核心职责 需关注的问题
kube-apiserver 控制平面 暴露 Kubernetes HTTP API 作业提交、对象创建、权限与准入是否成功
etcd 控制平面 保存集群状态 一般由平台团队维护,业务侧重点是不要误删对象
kube-scheduler 控制平面 为未调度 Pod 选择节点 Executor、TaskManager 为什么 Pending
kube-controller-manager 控制平面 运行控制器,协调期望状态和实际状态 Job、Deployment、StatefulSet 是否按预期拉起 Pod
cloud-controller-manager 控制平面,可选 对接云厂商负载均衡、节点等能力 LoadBalancer、云盘、节点生命周期相关问题
kubelet 每个节点 启动并维护 Pod 和容器 容器启动失败、探针失败、OOM、挂载失败
kube-proxy 每个节点,可选 维护 Service 网络规则 Service 能否访问、端口转发路径是否正常
容器运行时 每个节点 运行容器 镜像拉取、容器启动、cgroup 资源限制

从数据负载视角看,控制平面更像"调度和状态中枢",工作节点更像"实际执行环境"。当你提交一个 Spark 或 Flink 作业时,平台会创建一批 Kubernetes 对象;API Server 接收这些对象,调度器决定 Pod 放到哪里,kubelet 在目标节点拉起容器,容器运行时执行进程,Service 和 DNS 让不同角色之间能够互相发现。

yaml 复制代码
提交数据作业
    |
    v
API Server 接收对象
    |
    v
Scheduler 选择节点
    |
    v
Kubelet 拉起 Pod
    |
    v
Container Runtime 启动 Driver / Executor / Worker
    |
    v
Service / DNS 支撑组件间通信

三、Pod 生命周期

Pod 的生命周期从Pending开始,如果至少一个主容器正常启动,会进入Running;最终根据容器是否成功结束,进入SucceededFailed;如果无法获得 Pod 状态,则可能显示为Unknown。另外像CrashLoopBackOffTerminating常出现在 kubectl 的展示字段中,属于过渡状态,通常最终会转化为上述五种状态。

Pod 被创建后会获得唯一 UID,被调度到节点上运行;如果节点死亡,相关 Pod 会被标记删除,Kubernetes 依赖更高层控制器创建替代 Pod,而不是把同一个 Pod 原地搬到另一个节点。

这对 Spark、Flink 这类系统很关键。Executor 或 TaskManager 所在的 Pod 如果消失,不能假设它会在另一个节点以同样身份复活;更可靠的设计是让上层框架感知失败、重新申请资源、恢复状态或重跑分区。K8s 负责容器生命周期和节点绑定,数据框架负责计算语义和容错语义。

css 复制代码
错误理解:
Pod A on Node1 失败
        |
        v
Pod A 被"迁移"到 Node2

正确理解:
Pod A on Node1 失败
        |
        v
Pod A 被删除,控制器或框架创建 Pod B
        |
        v
Pod B 可能被调度到 Node2,也可能不是

Pod 内部的容器还有自己的状态:WaitingRunningTerminated。例如镜像拉取、Secret 应用、启动前准备可能让容器处于 Waiting;容器执行中是 Running;正常结束或失败退出后是 Terminated,并会携带 reason、exit code、开始和结束时间等信息 。

这解释了为什么排查数据作业时,不能只看 "Pod Running"。一个 Pod 可能已经 Running,但其中某个容器反复重启;也可能 Pod 卡在 Pending,根因并不是应用代码,而是资源不足、镜像拉取失败、PVC 无法绑定或调度条件不满足。

四、资源请求与限制

K8s 中最容易被低估的两个字段是requestslimits。当你为容器指定 resource request 时,调度器会用它决定 Pod 能放到哪台节点上;当你指定 resource limit 时,kubelet 和容器运行时会约束容器实际使用资源,Linux 节点上最终通常由 cgroups 执行限制。

CPU 和内存的限制行为并不一样。CPU limit 通过 CPU throttling 执行,容器接近 CPU limit 时,内核会限制它继续获得 CPU 时间;memory limit 则通常通过 OOM kill 执行,当容器超过内存限制并触发内存压力时,内核可能终止进程,因此内存限制是更"反应式"的。

资源 request 的主要作用 limit 的主要作用 超限后常见现象
CPU 调度时计算节点是否有足够可分配 CPU;争抢时也可能影响权重 设置 CPU 使用上限 变慢、延迟升高、CPU throttling
Memory 调度时计算节点是否有足够可分配内存 设置内存上限 OOMKilled、容器重启、作业失败
Ephemeral Storage 调度和本地临时存储容量相关 限制本地临时存储使用 临时目录写满、Pod 被驱逐
HugePages 特定高性能场景 不允许超额使用 分配失败

对大数据作业来说,request是"调度承诺",limit是"运行边界"。如果 Spark Executor 的 request 太低,调度器可能把过多 Executor 放到同一节点,运行时发生争抢;如果 memory limit 太贴近 JVM heap,JVM 堆外内存、shuffle buffer、Netty、native memory、Python worker 等开销可能触发 OOM。

还有一个容易被忽略的细节:如果指定了 limit 但没有指定 request,且没有准入机制设置默认 request,Kubernetes 会把 limit 复制为 request。这在数据平台上可能导致"看起来只是设了上限,实际却提高了调度预留",从而让 Pod 更难被调度。

五、调度机制

Kubernetes 调度的核心动作是把未绑定节点的 Pod 匹配到合适的 Node。默认调度器 kube-scheduler 会观察新创建但尚未指定节点的 Pod,为每个 Pod 选择最合适的节点,并通过 binding 把决策写回 API Server 。

kube-scheduler 的节点选择分成两步:Filtering 和 Scoring。Filtering 找出能运行该 Pod 的可行节点,例如资源请求是否能被满足;Scoring 对通过过滤的节点打分,然后选择得分最高的节点;如果多个节点得分相同,则随机选择其中一个 。

调度器考虑的因素不只有单个 Pod 的资源请求,也包括硬件、软件、策略约束、亲和性和反亲和性、数据本地性、负载间干扰等。这句话对大数据尤其重要,因为数据负载通常不只是"能不能跑",还关心"跑在哪里更合适"。

大数据诉求 对应的 K8s 调度关注点 典型影响
避免 Driver 和 Executor 全挤在同一节点 资源请求、拓扑分布、反亲和 降低单点资源争抢
GPU / SSD / 高内存节点专用 nodeSelector、node affinity、taints/tolerations 把特殊负载放到特殊节点
减少跨机架或跨可用区流量 topology spread、node labels、调度策略 降低 shuffle 或远程读延迟
有状态组件稳定落点 StatefulSet、PV 拓扑、调度约束 降低存储重绑定成本
多租户隔离 namespace、quota、priority、taints 限制资源互相影响

对于 Spark on K8s 或 Flink on K8s,资源 request 的设置会直接影响调度结果。调度器会确保已调度容器的资源请求之和小于节点容量;即使节点当前实际 CPU 或内存使用很低,只要 request 容量检查失败,调度器也会拒绝把 Pod 放到该节点 。

这解释了一个常见现象:集群监控显示 CPU 使用率并不高,但 Executor Pod 仍然 Pending。原因可能不是"集群闲着不用",而是 K8s 调度看的是 request 预留模型,不是当前瞬时使用率。

六、卷模型

容器文件系统是短暂的,容器崩溃或停止后,容器生命周期内创建或修改的文件不会被保存;Kubernetes Volume 抽象用于解决数据持久化和 Pod 内多个容器共享文件的问题 。

Kubernetes 的 volume 本质上是 Pod 中容器可访问的目录。Pod 在 .spec.volumes 中声明卷,在每个容器的 .spec.containers[*].volumeMounts 中声明挂载路径;同一个 Pod 内不同容器必须各自声明要把卷挂载到哪里 。

volume 分成很多类型,可以先抓住四类:emptyDirpersistentVolumeClaimconfigMap/secrethostPath/local。其中 emptyDir 在 Pod 分配到节点时创建,Pod 从节点移除时数据会永久删除;容器崩溃不会删除 emptyDir 数据,因此它适合作为 Pod 生命周期内的临时空间,例如磁盘排序、长计算 checkpoint 的临时恢复材料、或同 Pod 容器间共享文件 。

卷类型 生命周期 大数据场景 风险
emptyDir 跟随 Pod shuffle 临时目录、spill、缓存 Pod 删除后数据消失
emptyDir.medium: Memory 跟随 Pod,使用 tmpfs 小型高速临时数据 计入内存,可能触发 OOM
persistentVolumeClaim 独立于 Pod 状态存储、日志、元数据、checkpoint 依赖存储后端和访问模式
configMap / secret 对象生命周期 配置文件、密钥 ConfigMap 只读挂载;subPath 下更新不可见
hostPath 节点本地路径 访问节点日志或特殊目录 安全风险高、跨节点行为不一致

hostPath要谨慎使用,hostPath会带来很多安全风险;它可能暴露节点文件系统中的特权凭据或容器运行时 socket,也可能导致同样配置的 Pod 在不同节点上行为不同。在大数据场景里,如果只是想使用本地盘,应优先考虑localPersistentVolume 或由平台团队封装好的存储能力,而不是让业务作业直接挂任意宿主机路径。

七、Service 与网络路径

Pod 是短暂的,每个 Pod 又有自己的 IP。Deployment 等工作负载会动态创建和销毁 Pod,因此某一时刻可用 Pod 的集合会变化;Service 的作用,是为一组 Pod 提供稳定的网络抽象,让客户端不用自己追踪后端 Pod IP。

Service 通常通过 selector 选择一组 Pod,并由控制器持续更新对应的 EndpointSlice。EndpointSlice 是表示 Service 后端网络端点子集的对象;默认情况下,当已有 EndpointSlice 至少包含 100 个 endpoint 后,Kubernetes 会在需要新增 endpoint 时创建新的 EndpointSlice 。

这对大数据系统有两个含义。第一,Driver、JobManager、Coordinator 这类需要被其他组件找到的角色,通常需要稳定服务名。第二,大规模 Executor 或 Worker 不一定都需要被 Service 暴露,尤其是只需要主动向上游注册或通过框架协议通信时,过度暴露反而会增加网络对象复杂度。

Kubernetes 还会为 Service 和 Pod 创建 DNS 记录。默认情况下,Pod 的 DNS 搜索列表包含自己的 namespace 和集群默认域,因此同 namespace 内可以用短 Service 名访问,跨 namespace 则需要带上 namespace,例如 data.prod 或完整的 data.prod.svc.cluster.local

kotlin 复制代码
Pod namespace: test
Service: data in namespace prod

查询 data
  -> data.test.svc.cluster.local
  -> 找不到 prod/data

查询 data.prod
  -> data.prod.svc.cluster.local
  -> 命中 prod namespace 下的 Service

Service 类型也要按路径理解,ClusterIP暴露集群内部 IP,是默认类型;NodePort在每台 Node 的 IP 上暴露静态端口;LoadBalancer依赖外部负载均衡器暴露服务;ExternalName把 Service 映射到外部 DNS 名称。

Service 类型 访问范围 数据平台中的常见用途
ClusterIP 集群内部 Driver、JobManager、Coordinator 内部通信
Headless Service 返回后端 Pod IP 集合 有状态组件、需要直连 Pod 的场景
NodePort 节点 IP + 静态端口 调试或特殊内网暴露,生产慎用
LoadBalancer 云厂商或外部 LB 对外暴露 UI、SQL Gateway、REST API
ExternalName DNS CNAME 风格映射 把集群内访问名映射到外部系统

网络路径可以抽象为两条:控制面路径和数据面路径。控制面路径负责对象创建、调度、EndpointSlice 更新、DNS 记录生成;数据面路径则是 Pod 通过 Service 名、ClusterIP 或后端 Pod IP 发起真实连接。理解这两条路径,有助于排查"Service 存在但访问失败""DNS 能解析但连接超时""Pod 能访问短名但跨 namespace 失败"等问题。

yaml 复制代码
控制面路径:
Pod label change
      |
      v
Service selector
      |
      v
EndpointSlice update
      |
      v
DNS / proxy rules update


数据面路径:
client container
      |
      v
DNS resolve service name
      |
      v
Service cluster IP or endpoint IP
      |
      v
target Pod port

八、数据负载视角下的五个判断

第一,Pod 生命周期决定了数据作业的失败语义边界。K8s 可以重启容器、删除失败 Pod、由控制器创建替代实例,但不会替数据框架理解 shuffle、checkpoint、exactly-once 或 lineage,要把 K8s 生命周期和框架自身容错机制分开看。

第二,request 决定调度能不能发生,limit 决定运行时能不能越界。CPU limit 通常体现为 throttling,memory limit 通常体现为 OOM kill;这也是为什么 CPU 配小了常表现为慢,内存边界错了常表现为失败 。

第三,调度机制不是"哪里空就去哪"。调度器基于 Filtering 和 Scoring 工作,资源请求、节点约束、亲和性、数据本地性、负载干扰等都会影响最终节点选择 。

第四,卷模型要先分清临时、持久、配置和节点本地。emptyDir 很适合数据计算过程中的临时目录,但 Pod 删除后数据会永久消失;PVC 更适合需要跨 Pod 生命周期保留的数据;hostPath 虽然强大,但安全和可移植性风险很高 。

第五,Service 解决的是"动态 Pod 集合如何被稳定访问"。它不等于网络本身,也不等于负载均衡的一切;它通过 selector、EndpointSlice、DNS 和代理机制,让客户端不必直接追踪后端 Pod IP 。

一个大数据作业在 K8s 上的运行路径,可以概括如下:

sql 复制代码
                  submit job
                     |
                     v
              +--------------+
              | API Server   |
              +--------------+
                     |
                     v
              +--------------+
              | Pod created  |
              +--------------+
                     |
                     v
              +--------------+
              | Scheduler    |
              | filter/score |
              +--------------+
                     |
               bind to node
                     |
                     v
+------------------------------------------------+
| Node                                           |
|                                                |
|  +----------------------+                      |
|  | kubelet              |                      |
|  | start containers     |                      |
|  | apply resources      |                      |
|  | mount volumes        |                      |
|  +----------------------+                      |
|                                                |
|  +------------------------------------------+  |
|  | Pod                                      |  |
|  |  container: driver / executor / worker   |  |
|  |  resources: request / limit              |  |
|  |  volumes: emptyDir / PVC / config        |  |
|  +------------------------------------------+  |
+------------------------------------------------+
                     |
                     v
              +--------------+
              | Service/DNS  |
              | discovery    |
              +--------------+
                     |
                     v
              peer Pods / clients
相关推荐
whcyhhh1 小时前
头歌实践教学平台:数据科学与大数据技术导论(七上)
大数据·数据库·python
东方护航数据恢复(深圳)2 小时前
国产信创数据库(达梦DM8/人大金仓)损坏恢复技术:从DMF文件解析到数据字典重建
大数据·数据库
starzy19902 小时前
SparkStreaming 之 foreachRDD 算子详解及代码实现
大数据·spark
用户594404103562 小时前
Redis 高并发高可用实战:从主从哨兵到集群分片,附完整 Java 代码
大数据
FoldWinCard2 小时前
K8s -- 控制器管理
云原生·容器·kubernetes
ApacheSeaTunnel3 小时前
一个关于数据集成的真相:链路 Success 不等于数据真实可靠
大数据·开源·数据集成·seatunnel·技术分享·数据同步
hengcaib3 小时前
2026工程项目信息平台能力解析:中策大数据建筑工业双赛道的全周期服务实力
大数据
无望__wsk3 小时前
k8s的pod管理
云原生·容器·kubernetes
tedcloud1233 小时前
Awesome DSH Plugin 怎么用?给 DeepSeek Harness 搭建自己的插件生态
大数据·linux·服务器·人工智能·音视频