spark
This commit is contained in:
177
spark/spark kerberos认证与 YARN 的代理机制.md
Normal file
177
spark/spark kerberos认证与 YARN 的代理机制.md
Normal file
@@ -0,0 +1,177 @@
|
||||
## 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 自行处理与其他服务的长期认证,两者协作实现全链路安全访问。
|
||||
Reference in New Issue
Block a user