运维笔记

Spark on K8s 日志采集翻车实录:Driver/Executor 日志写入 S3 并在 History Server 展示的完整修复指南

Cloud & DevOps 技术可视化

症状:History Server 一片空白,日志去哪了?

上周五下午,我们组的告警突然炸了——用户反馈 Spark History Server 上看不到任何历史任务。明明 spark-submit 跑完了,S3 bucket 里也看到了 spark-events 目录,但点进去就是空的。

更诡异的是,我手动 kubectl logs 看 driver pod,日志是有的。executor pod 也能看到 stdout/stderr。但 S3 里就是没有 application_xxx.inprogress 文件。

我第一反应:“日志采集管道断了”

根因分析:三个常见的"我以为"

翻了一下午代码和社区帖子,发现这个问题在 Spark on Kubernetes 场景下极其普遍。Reddit 上有人吐槽:“I am looking for an option to add sparkApplicationId to driver and executor pod names."——其实这背后暴露的是日志路径映射的混乱。

核心问题有三个:

  1. spark.eventLog.dir 配置不对。很多人设了 s3a://bucket/spark-events,但 Spark 在 K8s 上默认不走 Hadoop S3A connector,你得显式加上 spark.hadoop.fs.s3a.impl=org.apache.hadoop.fs.s3a.S3AFileSystem

  2. Driver 和 Executor 的日志路径不统一。默认情况下,Spark 的 event log 只由 driver 写入,executor 的 stdout/stderr 根本不会进 S3。你需要额外配置 spark.kubernetes.driver.log.persistspark.kubernetes.executor.log.persist

  3. History Server 读取权限问题。就算日志写进去了,History Server 的 service account 可能没有 S3 bucket 的 s3:GetObject 权限。这个坑我们踩了两天才发现。

修复步骤:从零到能看 History Server

1. 确认 S3 路径和 Hadoop 配置

首先,检查你的 spark-submit 命令或配置文件中是否包含以下关键项:

--conf spark.eventLog.enabled=true
--conf spark.eventLog.dir=s3a://my-bucket/spark-events/
--conf spark.hadoop.fs.s3a.impl=org.apache.hadoop.fs.s3a.S3AFileSystem
--conf spark.hadoop.fs.s3a.access.key=YOUR_ACCESS_KEY
--conf spark.hadoop.fs.s3a.secret.key=YOUR_SECRET_KEY
--conf spark.hadoop.fs.s3a.endpoint=https://s3.ap-southeast-1.amazonaws.com

注意:如果你的 S3 兼容存储(比如 MinIO)用了自签名证书,还得加上 --conf spark.hadoop.fs.s3a.connection.ssl.enabled=false(别问我怎么知道的)。

2. 开启 Driver 和 Executor 日志持久化

这是大部分人漏掉的一步。默认 Spark 只把 event log 写入 S3,但 driver 和 executor 的 stdout/stderr 不会自动上传。你需要显式开启:

--conf spark.kubernetes.driver.log.persist=true
--conf spark.kubernetes.executor.log.persist=true
--conf spark.kubernetes.driver.log.persist.dir=s3a://my-bucket/driver-logs/
--conf spark.kubernetes.executor.log.persist.dir=s3a://my-bucket/executor-logs/

注意spark.kubernetes.driver.log.persist 在 Spark 3.1.1 之后才稳定,如果你用老版本,可能需要升级。

3. 给 History Server 配好 IAM 权限

History Server 本身是一个 Spark 应用,它需要读取 S3 里的 event log。如果你用 IRSA(IAM Roles for Service Accounts),需要确保 service account 绑定了正确的 policy:

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": [
        "s3:ListBucket",
        "s3:GetObject"
      ],
      "Resource": [
        "arn:aws:s3:::my-bucket",
        "arn:aws:s3:::my-bucket/*"
      ]
    }
  ]
}

如果你用 access key/secret key 的方式,直接在 History Server 的 Deployment 环境变量里设置 AWS_ACCESS_KEY_IDAWS_SECRET_ACCESS_KEY

4. 验证日志写入

跑一个简单的 Spark Pi 作业测试:

spark-submit \
  --master k8s://https://<api-server> \
  --deploy-mode cluster \
  --conf spark.kubernetes.container.image=apache/spark:3.5.0 \
  --conf spark.eventLog.enabled=true \
  --conf spark.eventLog.dir=s3a://my-bucket/spark-events/ \
  --conf spark.hadoop.fs.s3a.impl=org.apache.hadoop.fs.s3a.S3AFileSystem \
  --conf spark.hadoop.fs.s3a.access.key=xxx \
  --conf spark.hadoop.fs.s3a.secret.key=xxx \
  local:///opt/spark/examples/jars/spark-examples.jar 100

作业跑完后,检查 S3 bucket:

aws s3 ls s3://my-bucket/spark-events/

你应该能看到类似 app-20260624020000-0000.inprogress 的文件。如果看不到,先看 driver pod 的日志:

kubectl logs <driver-pod> | grep -i "eventlog"

常见错误是 java.io.IOException: No FileSystem for scheme: s3a,说明 Hadoop S3A 依赖没加载。

5. 重启 History Server 并验证

如果日志已经写入 S3,但 History Server 还是空白,检查 History Server 的启动参数。正确的配置应该是:

# history-server-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: spark-history-server
spec:
  replicas: 1
  selector:
    matchLabels:
      app: spark-history-server
  template:
    metadata:
      labels:
        app: spark-history-server
    spec:
      containers:
      - name: history-server
        image: apache/spark:3.5.0
        args:
          - "/opt/spark/bin/spark-class"
          - "org.apache.spark.deploy.history.HistoryServer"
        env:
        - name: SPARK_HISTORY_OPTS
          value: "-Dspark.history.fs.logDirectory=s3a://my-bucket/spark-events/ -Dspark.hadoop.fs.s3a.impl=org.apache.hadoop.fs.s3a.S3AFileSystem -Dspark.hadoop.fs.s3a.access.key=xxx -Dspark.hadoop.fs.s3a.secret.key=xxx"
        ports:
        - containerPort: 18080

注意SPARK_HISTORY_OPTS 环境变量是 History Server 读取配置的关键。很多人直接在 spark-defaults.conf 里配,但 History Server 启动时不一定加载那个文件。

配置对比表

配置项作用范围常见错误正确值
spark.eventLog.dirDriver使用 s3:// 而非 s3a://s3a://bucket/spark-events/
spark.hadoop.fs.s3a.implDriver/Executor缺失org.apache.hadoop.fs.s3a.S3AFileSystem
spark.kubernetes.driver.log.persistDriver未设置true
spark.kubernetes.executor.log.persistExecutor未设置true
spark.history.fs.logDirectoryHistory Server与 eventLog.dir 不一致必须指向同一个 S3 路径
IAM PolicyHistory Server缺少 s3:GetObject必须包含 s3:GetObjects3:ListBucket

FAQ

Q1: 为什么我的 History Server 显示 “No completed applications found” 但 S3 里有文件?

A: 最常见的原因是 History Server 的 spark.history.fs.logDirectory 配置与 driver 的 spark.eventLog.dir 不匹配。检查两点:

  1. 路径必须完全一致(包括 bucket 名和前缀)。
  2. History Server 必须能访问 S3——如果是 IRSA,检查 service account 的 annotation 是否正确;如果是 access key,检查环境变量是否传递。

Q2: 开启 spark.kubernetes.driver.log.persist 后,日志文件在哪里?

A: 默认路径是 s3a://<eventLog.dir>/<app-id>/driver/。你可以通过 spark.kubernetes.driver.log.persist.dir 自定义。注意:这个功能只在 Spark 3.1.1+ 可用,并且需要 Hadoop S3A 依赖。

Q3: 我的 Spark 版本是 3.0.0,能支持日志持久化到 S3 吗?

A: 勉强可以,但不推荐。Spark 3.0.0 的 spark.kubernetes.driver.log.persist 功能是实验性的,且没有 executor 支持。建议升级到 3.1.1 或更高版本。如果必须用 3.0.0,可以考虑用 sidecar 容器手动将日志上传到 S3。

Q4: S3 兼容存储(如 MinIO)需要额外配置吗?

A: 需要。除了 fs.s3a.impl,还需要:

  • spark.hadoop.fs.s3a.endpoint:指向 MinIO 的 API 地址。
  • spark.hadoop.fs.s3a.path.style.access=true:MinIO 强制要求 path-style 访问。
  • 如果 MinIO 没有 HTTPS,加上 spark.hadoop.fs.s3a.connection.ssl.enabled=false

Q5: 日志写入 S3 后,History Server 加载很慢怎么办?

A: 考虑启用 spark.history.fs.updateInterval 来降低轮询频率(默认 10 秒)。另外,如果日志文件很大,可以设置 spark.history.fs.maxLogEntries 限制每个应用的最大日志条目数。对于生产环境,建议将 S3 bucket 的生命周期策略设置为归档旧日志到 Glacier,减少 History Server 的扫描范围。

社区灵感与参考 (References & Community Insights)

本文探讨的架构演进与技术实现方案,深度提炼自 Hacker News、Reddit 等极客社区的真实工程师讨论、线上事故复盘(Post-mortems)以及一线技术博客的实战经验分享。

Elvin Hui

关于作者:Elvin Hui

Elvin 拥有 10+ 年企业级数据中心、云原生架构和网络安全经验。持有 CCNA、AWS 解决方案架构师认证。我致力于将一线的“踩坑”经验沉淀为真实、硬核的技术指南,拒绝空洞理论。