Spark on Kubernetes

Spark on Kubernetes

Spark on Kubernetes 是 Apache Spark 支持的原生集群部署模式(自 Spark 2.3 引入),允许 Spark 应用程序(批处理、流处理、机器学习)直接在 Kubernetes 集群上运行,利用 Kubernetes 强大的容器编排、资源调度和基础设施管理能力。以下是其部署架构和关键细节的详解:


核心架构与工作原理

  1. 提交 Spark 应用 (spark-submit):

    • 用户使用 spark-submit 命令(或通过 API)提交 Spark 应用。
    • 指定 --master k8s://<api-server-url> (如 k8s://https://<k8s-apiserver>:6443).
    • 提供应用 JAR、主类、配置(Executor 数量、内存、CPU 等)、Driver/Executor 的 Docker 镜像。
  2. 创建 Driver Pod:

    • spark-submit 直接与 Kubernetes API Server 通信。
    • Kubernetes API Server 根据提交的参数,在指定 Namespace 中创建一个 Driver Pod
    • 这个 Pod 运行 Spark Driver 程序(包含 SparkContext)。
  3. Driver 管理 Executor:

    • Driver Pod 启动后,SparkContext 初始化。
    • Driver 向 Kubernetes API Server 请求创建 Executor Pods
    • 请求基于用户配置(spark.executor.instances, spark.executor.memory, spark.executor.cores 等)。
  4. 创建 Executor Pods:

    • Kubernetes API Server 接收请求,调度器(Scheduler)根据资源请求、节点亲和性等策略,选择合适的工作节点(Node)。
    • Kubelet 在选定的节点上拉取指定的 Executor Docker 镜像并启动 Executor Pod
    • 每个 Executor Pod 运行一个 Spark Executor JVM 进程。
  5. 应用执行:

    • Executor Pods 启动后,主动向 Driver Pod 注册。
    • Driver 将 Task 分发给注册的 Executors。
    • Executors 执行 Task,与 Driver 通信(汇报状态、获取数据/指令)。
  6. 应用结束与清理:

    • 应用完成后(成功或失败),Driver Pod 的状态会更新。
    • Driver Pod 负责清理其创建的 Executor Pods(通过向 Kubernetes API Server 发送删除请求)。
    • Driver Pod 本身根据其 restartPolicy(通常是 NeverOnFailure)决定是否保留(用于日志查看或调试)。

关键组件详解

  1. spark-submit: 提交入口。配置 Kubernetes 认证信息(kubeconfig)、API Server URL、镜像、资源等。
  2. Kubernetes API Server: 核心枢纽。接收 spark-submit 请求创建 Driver Pod;接收 Driver Pod 请求创建/删除 Executor Pods。
  3. Driver Pod:
    • 核心管理者: 包含 Spark Driver 程序(SparkSession/SparkContext)。
    • 职责:
      • 应用初始化。
      • 向 Kubernetes 申请 Executor 资源。
      • 任务调度与分发。
      • 跟踪 Executor 状态和任务执行进度。
      • 管理 Executor Pod 的生命周期(创建、删除)。
      • 应用完成后的清理工作。
    • 配置要点:
      • 镜像 (spark.kubernetes.driver.container.image):必须包含 Spark 发行版、JDK 和应用程序依赖。
      • 资源 (spark.driver.memory, spark.driver.cores, spark.kubernetes.driver.request.cores, spark.kubernetes.driver.limit.cores 等)。
      • 服务账号 (spark.kubernetes.authenticate.driver.serviceAccountName):用于 Pod 访问 Kubernetes API 的权限。
      • restartPolicy:通常设为 NeverOnFailure
  4. Executor Pods:
    • 任务执行者: 每个 Pod 运行一个 Executor JVM 进程。
    • 职责: 执行 Driver 分配的 Task,管理本地数据和 shuffle 数据。
    • 配置要点:
      • 镜像 (spark.kubernetes.executor.container.image):通常与 Driver 镜像相同或兼容。
      • 资源 (spark.executor.memory, spark.executor.cores, spark.kubernetes.executor.request.cores, spark.kubernetes.executor.limit.cores 等)。
      • 数量 (spark.executor.instances)。
      • 本地存储 (spark.kubernetes.executor.volumes + .volumeMounts):用于 shuffle 溢出、临时文件等。
      • 重要特性:
        • 动态分配 (spark.dynamicAllocation.enabled=true): 根据工作负载自动增减 Executor 数量(需配合 External Shuffle Service 或 Kubernetes Shuffle Manager)。
        • Pod 亲和性/反亲和性: 优化调度(如将 Executor 分散在不同节点)。
  5. Kubernetes Scheduler: 负责将 Driver Pod 和 Executor Pod 调度到合适的 Worker Node 上运行(基于资源请求、约束、亲和性规则等)。
  6. Kubelet: 在工作节点上运行,负责拉取镜像、启动/停止容器、管理 Pod 生命周期。
  7. kube-proxy & CNI Plugin: 提供 Pod 网络通信(Driver 与 Executor 之间、Executor 之间 shuffle 数据传输)。
  8. (可选) Kubernetes Shuffle Manager: 替代传统的 Spark External Shuffle Service。直接在 Kubernetes 上管理 shuffle 数据的存储和获取(如使用本地卷或分布式存储),是实现动态分配的关键。常见实现:
    • KubernetesShuffleManager (Spark 内置实验性功能)。
    • Apache Celeborn (Incubating): 高性能、云原生的 Shuffle 服务,与 Spark on K8s 集成良好。

部署模式详解

Spark on K8s 主要支持两种部署模式:

  1. Cluster 模式 (推荐):

    • spark-submit客户端机器运行,提交后立即返回。
    • Driver Pod 在 Kubernetes 集群内部创建和运行。
    • 所有交互(日志、状态)通过 Kubernetes API (kubectl logs/describe) 或 Spark UI (需配置 Ingress/Service) 访问。
    • 优点: 资源管理统一,Driver 高可用性更易实现(配合 K8s 机制),适合生产环境。
    • 缺点: 调试稍复杂(需访问集群内日志/UI)。
  2. Client 模式:

    • spark-submit客户端机器运行,Driver 进程也在该客户端 JVM 中运行。
    • 仅 Executor Pod 在 Kubernetes 集群中创建。
    • 优点: Driver 日志/UI 直接在客户端可见,调试方便。
    • 缺点:
      • 客户端机器必须能访问 Kubernetes API Server 和 Executor Pods 网络(常受网络策略限制)。
      • 客户端机器资源需足够运行 Driver。
      • 客户端不稳定会导致整个应用失败。
      • 不推荐用于生产环境。

核心优势

  1. 统一资源管理与调度: 利用 Kubernetes 强大的调度器(支持 bin packing、亲和性/反亲和性、污点容忍、GPU 等),实现 Spark 与其他工作负载(如数据库、Web服务)共享集群资源,提高利用率。
  2. 简化基础设施: 无需单独维护 Hadoop/YARN 集群。Kubernetes 成为统一的基础设施层。
  3. 容器化与隔离: 每个 Spark 应用(Driver + Executors)运行在独立 Pod 中,环境隔离性好,依赖管理通过容器镜像解决。
  4. 弹性伸缩: 结合 Kubernetes HPA 和 Spark 动态分配,实现更精细的资源自动扩缩容。
  5. 高可用性: 可借助 Kubernetes 机制(如 Pod Disruption Budgets, 健康检查)提升 Driver/Executor 的可用性。
  6. 云原生集成: 无缝集成 Prometheus(监控)、Fluentd(日志)、Helm(包管理)、Service Mesh 等云原生生态工具。
  7. 多租户与命名空间: 利用 Kubernetes Namespace 实现资源配额、网络策略隔离,支持多团队/项目共享集群。
  8. 存储集成: 原生支持 Kubernetes Persistent Volumes (PV) / Persistent Volume Claims (PVC),方便访问 HDFS、S3、NFS、Local PV 等存储。

关键挑战与注意事项

  1. 镜像构建与管理:
    • 需要构建包含 Spark 发行版、JDK、应用代码及依赖的 Docker 镜像。
    • 优化镜像大小(分层构建、精简基础镜像)。
    • 建立镜像仓库(如 Harbor, Docker Hub, ECR, GCR)管理流程。
  2. Shuffle 管理:
    • 动态分配依赖可靠、高效的 Shuffle 服务。传统 External Shuffle Service 在 K8s 中部署复杂。
    • 优先考虑 Kubernetes Shuffle Manager (如 Celeborn) 或确保稳定 ESS 部署。
  3. 网络性能:
    • Pod 间通信(Driver-Executor, Executor-Executor shuffle)依赖 CNI 插件性能。
    • 选择高性能 CNI (如 Calico, Cilium) 并优化网络策略。
    • 考虑节点本地性(减少跨节点 shuffle)。
  4. 存储配置:
    • 为 Executor 配置 emptyDir 卷用于 shuffle 溢出和临时文件(注意大小限制)。
    • 访问外部存储(HDFS, S3)需正确配置卷挂载、Secret (认证信息)、ConfigMap。
  5. 权限控制 (RBAC):
    • Driver Pod 需要足够权限(通过 ServiceAccount + Role + RoleBinding)来创建/删除 Executor Pods。
    • 遵循最小权限原则。
  6. 日志与监控:
    • 日志: 配置 spark.kubernetes.container.image.pullPolicy 避免缓存旧日志,使用 kubectl logs 或集成集群日志方案(EFK/Loki+Promtail+Grafana)集中收集 Pod 日志。设置 spark.kubernetes.driver.label.[LabelName]/.annotation.[AnnotationName] 便于筛选。
    • 监控: 暴露 Spark Metrics(通过 spark.metrics.conf 或 JMX Exporter),集成 Prometheus + Grafana 进行可视化。利用 Kubernetes 原生监控(Metrics Server)。
  7. 资源配额与限制:
    • 使用 Kubernetes ResourceQuotas 限制 Namespace 级别的总资源使用。
    • 为 Driver/Executor Pod 设置合理的 requestslimits (spark.kubernetes.driver/executor.request.cores/memory, .limit.cores/memory) 防止单个应用耗尽节点资源。
  8. Spark 版本与 K8s 版本兼容性: 关注官方文档,确保使用的 Spark 版本与 Kubernetes 集群版本兼容。
  9. 安全:
    • 使用 Secrets 管理敏感信息(密码、密钥)。
    • 考虑启用 Pod 安全策略(PSP)或 Pod Security Admission (PSA)。
    • 镜像扫描。

部署流程示例 (Cluster 模式)

  1. 准备 Docker 镜像:

    • 基于官方 Spark 镜像或自定义镜像(添加应用 JAR 和依赖)。
    • 构建镜像并推送到可访问的镜像仓库。
    FROM apache/spark:3.5.0-scala2.12-java17-python3.10-ubuntu
    COPY target/my-spark-app.jar /opt/spark/work-dir/
    # (可选) 添加额外依赖、配置文件
    
    docker build -t myregistry.com/myteam/spark-app:3.5.0 .
    docker push myregistry.com/myteam/spark-app:3.5.0
    
  2. 配置 Kubernetes 权限 (RBAC):

    • 创建 ServiceAccount (e.g., spark-driver-sa)。
    • 创建 Role 赋予权限(create, get, list, watch, delete pods)。
    • 创建 RoleBinding 绑定 ServiceAccount 和 Role。
    apiVersion: v1
    kind: ServiceAccount
    metadata:
      name: spark-driver-sa
      namespace: spark-apps
    ---
    apiVersion: rbac.authorization.k8s.io/v1
    kind: Role
    metadata:
      name: spark-driver-role
      namespace: spark-apps
    rules:
    - apiGroups: [""]
      resources: ["pods"]
      verbs: ["create", "get", "list", "watch", "delete"]
    - apiGroups: [""]
      resources: ["configmaps"]
      verbs: ["create", "get", "delete"] # 可能需要用于某些配置
    ---
    apiVersion: rbac.authorization.k8s.io/v1
    kind: RoleBinding
    metadata:
      name: spark-driver-rb
      namespace: spark-apps
    roleRef:
      apiGroup: rbac.authorization.k8s.io
      kind: Role
      name: spark-driver-role
    subjects:
    - kind: ServiceAccount
      name: spark-driver-sa
      namespace: spark-apps
    
  3. 提交 Spark 应用 (spark-submit):

    bin/spark-submit \
      --master k8s://https://<k8s-apiserver>:6443 \
      --deploy-mode cluster \
      --name my-spark-pi \
      --class org.apache.spark.examples.SparkPi \
      --conf spark.executor.instances=5 \
      --conf spark.kubernetes.container.image=myregistry.com/myteam/spark-app:3.5.0 \
      --conf spark.kubernetes.namespace=spark-apps \
      --conf spark.kubernetes.authenticate.driver.serviceAccountName=spark-driver-sa \
      --conf spark.kubernetes.driver.request.cores=1 \
      --conf spark.kubernetes.driver.memory=2g \
      --conf spark.kubernetes.executor.request.cores=1 \
      --conf spark.kubernetes.executor.memory=4g \
      --conf spark.dynamicAllocation.enabled=false \ # 简化示例,禁用动态分配
      local:///opt/spark/work-dir/my-spark-app.jar # 注意 'local://' 表示镜像内路径
    
  4. 监控与管理:

    • 查看 Pods: kubectl get pods -n spark-apps -w
    • 查看 Driver 日志: kubectl logs -f <driver-pod-name> -n spark-apps
    • 访问 Spark UI:
      • 临时端口转发:kubectl port-forward <driver-pod-name> 4040:4040 -n spark-apps,然后访问 http://localhost:4040
      • 创建 NodePort/LoadBalancer Service + Ingress 暴露 UI(生产环境推荐)。
    • 应用结束: Executor Pods 会被自动清理,Driver Pod 保留(状态 CompletedError)供查日志。

总结

Spark on Kubernetes 提供了将 Spark 深度集成到现代云原生基础设施的标准方式。它利用 Kubernetes 的容器编排、资源调度和声明式 API 优势,实现了:

  • 统一资源池: Spark 与其他工作负载共享 K8s 集群资源。
  • 敏捷部署与运维: 容器化封装依赖,K8s 管理生命周期。
  • 弹性与效率: 结合 K8s 和 Spark 的动态伸缩能力。
  • 云原生集成: 无缝对接监控、日志、存储、网络等云原生生态。

成功部署的关键在于解决 镜像管理、Shuffle 性能、网络配置、RBAC 权限、存储访问和监控日志集成 等挑战。随着 Kubernetes Shuffle Manager(如 Celeborn)的成熟和 Spark 对 K8s 支持度的不断提升,Spark on Kubernetes 已成为生产环境部署 Spark 的重要选择,尤其适用于混合云/多云环境和追求统一技术栈的场景。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值