Files
article/spark/spark kerberos认证与 YARN 的代理机制.md
2025-10-14 10:58:35 +08:00

177 lines
8.2 KiB
Markdown
Raw Permalink Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

## spark kerberos认证与 YARN 的代理机制
在 Spark 中,**自行实现 Kerberos 认证**与依赖**YARN 的代理认证机制**存在本质区别,尤其当 YARN 本身已启用 Kerberos 时,需要明确两者的协作方式。以下是详细说明:
### **一、Spark 自行认证 vs YARN 代理认证的核心区别**
| **维度** | **Spark 自行实现认证** | **YARN 代理认证机制** |
|------------------------|---------------------------------------------------|---------------------------------------------------|
| **认证主体** | 直接使用 Spark 应用的 Kerberos Principal`spark@EXAMPLE.COM` | YARN 作为代理,使用 YARN 的 Principal 替 Spark 完成认证 |
| **凭证管理** | 由 Spark 应用手动加载 Keytab 并定期刷新 TGT如通过定时任务 | YARN 自动为 Spark 分配临时凭证Delegation Token并管理生命周期 |
| **适用场景** | - 长期运行的应用(如 Streaming<br>- 非 YARN 模式Standalone/K8s<br>- 需要细粒度控制认证逻辑 | - 短期批处理作业<br>- YARN 集群模式<br>- 依赖 YARN 统一管理凭证 |
| **配置方式** | 代码中手动调用 `UserGroupInformation.loginUserFromKeytab()`,需指定 Keytab 路径 | 通过 `--keytab``--principal` 提交参数,依赖 YARN 自动处理 |
| **凭证分发范围** | 需确保 Keytab 能被 Driver 和 Executor 访问(如通过 `--files` 分发) | YARN 自动将临时凭证分发到所有容器Container无需显式处理 |
### **二、当 YARN 已启用 Kerberos 时的处理方式**
若 YARN 集群本身启用了 Kerberos 认证(即访问 YARN 资源管理器需要 Kerberos 凭证Spark 应用需要同时处理**两层认证**
1. **Spark 与 YARN 的认证**:证明 Spark 有权限提交作业到 YARN。
2. **Spark 与其他服务的认证**:证明 Spark 有权限访问 HDFS、Hive、Kafka 等服务。
此时需结合两种认证方式,具体配置如下:
#### **1. 提交作业时的基础配置**
必须通过 `--keytab``--principal` 告知 YARN 应用的身份,确保 Spark 能成功提交到 YARN 并获取资源:
```bash
spark-submit \
--master yarn \
--deploy-mode cluster \
--keytab /path/to/spark.keytab \ # Spark 应用的 Keytab
--principal spark@EXAMPLE.COM \ # Spark 应用的 Principal
--files /path/to/hive-site.xml \ # 可选:分发 Hive 配置
--class com.example.SparkApp \
your-application.jar
```
**核心作用**
- YARN 通过 `--keytab``--principal` 验证 Spark 提交者的身份,允许作业提交。
- YARN 会自动为 Spark 申请访问 HDFS、Hive 等服务的临时凭证Delegation Token并分发给 Executor。
#### **2. 长期运行应用的特殊处理**
对于 Streaming 等长期运行的应用YARN 自动分配的临时凭证可能过期(默认有效期较短,通常几小时),需结合**自行认证逻辑**刷新凭证:
```scala
import org.apache.hadoop.security.UserGroupInformation
import org.apache.spark.sql.SparkSession
import org.apache.spark.sql.streaming.Trigger
import java.util.concurrent.{Executors, TimeUnit}
import org.slf4j.LoggerFactory
object SparkYARNKerberosApp {
private val logger = LoggerFactory.getLogger(SparkYARNKerberosApp.getClass)
// 从提交参数中获取 Principal通过 --conf 传递)
private def getPrincipal(spark: SparkSession): String = {
spark.conf.get("spark.yarn.principal")
}
// 获取 YARN 分发的 Keytab 本地路径(通过 --files 分发)
private def getKeytabPath(): String = {
val keytabFileName = System.getenv("SPARK_KEYTAB_FILENAME") // 从环境变量传入文件名
new java.io.File(keytabFileName).getAbsolutePath
}
def main(args: Array[String]): Unit = {
// 1. 创建 SparkSession已通过 --keytab 和 --principal 完成 YARN 认证)
val spark = SparkSession.builder()
.appName("Spark on YARN with Kerberos")
.enableHiveSupport()
.getOrCreate()
// 2. 获取 YARN 传递的认证信息
val principal = getPrincipal(spark)
val keytabPath = getKeytabPath()
logger.info(s"YARN 认证信息 - Principal: $principal, Keytab: $keytabPath")
// 3. 启动长期任务的凭证刷新机制(处理 YARN 临时凭证过期问题)
startCredentialRefresh(principal, keytabPath)
// 4. 运行长期 Streaming 任务
runStreamingJob(spark)
spark.stop()
}
/**
* 启动凭证刷新任务,同时兼容 YARN 代理凭证和手动认证
*/
private def startCredentialRefresh(principal: String, keytabPath: String): Unit = {
val scheduler = Executors.newSingleThreadScheduledExecutor()
// 每 12 小时刷新一次(短于 YARN 临时凭证有效期)
scheduler.scheduleAtFixedRate(() => {
try {
val currentUser = UserGroupInformation.getCurrentUser()
// 优先使用 YARN 代理的凭证刷新
if (currentUser.hasKerberosCredentials) {
currentUser.checkTGTAndReloginFromKeytab()
logger.info("通过 YARN 代理刷新凭证成功")
} else {
// YARN 凭证失效时,手动重新登录
logger.warn("YARN 代理凭证失效,尝试手动登录")
UserGroupInformation.loginUserFromKeytab(principal, keytabPath)
}
} catch {
case e: Exception =>
logger.error("凭证刷新失败,强制重新登录", e)
UserGroupInformation.loginUserFromKeytab(principal, keytabPath)
}
}, 0, 12, TimeUnit.HOURS)
// 应用退出时关闭线程池
sys.addShutdownHook {
scheduler.shutdown()
}
}
/**
* 示例访问需要认证的服务Hive/Kafka 等)
*/
private def runStreamingJob(spark: SparkSession): Unit = {
import spark.implicits._
// 从 Hive 读取数据(依赖 YARN 分配的 Hive 凭证)
val hiveDF = spark.sql("SELECT * FROM secure_db.secure_table")
// 写入 HDFS依赖 YARN 分配的 HDFS 凭证)
val query = hiveDF.writeStream
.outputMode("append")
.format("parquet")
.option("path", "hdfs:///user/spark/streaming/output")
.trigger(Trigger.ProcessingTime("5 minutes"))
.start()
query.awaitAnyTermination()
}
}
```
#### **3. 关键配置说明**
- **YARN 代理凭证的局限性**YARN 分配的临时凭证(如 HDFS Delegation Token有有效期通常由 `dfs.delegation.token.lifetime` 控制,默认 7 天),长期运行的应用需主动刷新。
- **双重认证的协作**
- 短期作业:仅依赖 YARN 代理认证即可(无需手动处理)。
- 长期作业:需在代码中添加定时刷新逻辑,优先使用 YARN 代理凭证,失效时通过 Keytab 重新登录。
- **安全配置传递**:确保 `hive-site.xml``core-site.xml` 等配置文件通过 `--files``HADOOP_CONF_DIR` 传递给 Spark避免认证配置缺失。
### **三、常见问题与解决方案**
1. **YARN 提交失败:`No valid credentials`**
- 原因:未通过 `--keytab``--principal` 提供 Spark 应用的身份,或 Keytab 权限错误。
- 解决:确保 Keytab 权限为 `600`,且 `--principal` 与 Keytab 中的主体一致。
2. **长期运行后 HDFS 访问失败:`Token expired`**
- 原因YARN 分配的 HDFS 临时凭证过期,且未手动刷新。
- 解决:在代码中添加定时刷新逻辑(如示例中的 `startCredentialRefresh` 方法)。
3. **Executor 认证失败:`Keytab not found`**
- 原因Keytab 未通过 `--files` 分发到 Executor或路径硬编码导致无法访问。
- 解决:通过 `--files` 分发 Keytab代码中通过当前工作目录动态获取路径`new File(keytabFileName).getAbsolutePath`)。
### **总结**
- **短期作业**:直接依赖 YARN 代理认证(`--keytab` + `--principal`),无需额外代码处理。
- **长期作业**:需结合 YARN 代理认证和手动刷新逻辑,确保凭证不过期。
- **核心原则**YARN 负责 Spark 与集群资源的认证Spark 自行处理与其他服务的长期认证,两者协作实现全链路安全访问。