Настройка Kerberos для Spark History Server в Kubernetes

Требования

  • Установленный и керберизованный кластер ADH совместимой версии (требуется, только если логи событий будут сохраняться в HDFS или Ozone).

  • Извлеченный и загруженный в ваш репозиторий образ hub.arenadata.io/adc-enterprise/spark3:<version>.

  • Оператор Spark, развернутый в Kubernetes согласно инструкции. Убедитесь, что пространство имен Spark History Server включено в payloadNamespaces оператора Spark.

  • Оператор Kerberos, развернутый в Kubernetes согласно инструкции. Убедитесь, что пространство имен Spark History Server включено в payloadNamespaces оператора Kerberos.

Шаг 1. Установка Spark History Server

  1. Подготовьте файл hadoop_conf.yaml с настройками Hadoop (необходимо, только если используются S3 или сервисы Hadoop):

    hadoop_conf.yaml
    sites:
      core:
        fs.defaultFS: hdfs://adh
        hadoop.security.authentication: kerberos
        dfs.client.failover.proxy.provider.adh: org.apache.hadoop.hdfs.server.namenode.ha.ObserverReadProxyProvider
        dfs.ha.namenodes.adh: nn_tsn-adh-k8s-1,nn_tsn-adh-k8s-3
        dfs.namenode.rpc-address.adh.nn_tsn-adh-k8s-1: tsn-adh-k8s-1.ru-central1.internal:8020
        dfs.namenode.rpc-address.adh.nn_tsn-adh-k8s-3: tsn-adh-k8s-3.ru-central1.internal:8020
        dfs.nameservices: adh
        dfs.namenode.kerberos.principal: hdfs-namenode/_HOST@AD.RANGER-TEST
        dfs.journalnode.kerberos.principal: hdfs-journalnode/_HOST@AD.RANGER-TEST
        dfs.datanode.kerberos.principal: hdfs-datanode/_HOST@AD.RANGER-TEST
        hadoop.ssl.enabled: false
      hdfs:
        dfs.client.read.shortcircuit: false
      ozone:
        ozone.om.address.adh.om_tsn-adh-k8s-1: tsn-adh-k8s-1.ru-central1.internal:9862
        ozone.om.address.adh.om_tsn-adh-k8s-2: tsn-adh-k8s-2.ru-central1.internal:9862
        ozone.om.address.adh.om_tsn-adh-k8s-3: tsn-adh-k8s-3.ru-central1.internal:9862
        ozone.om.nodes.adh: om_tsn-adh-k8s-1,om_tsn-adh-k8s-2,om_tsn-adh-k8s-3
        ozone.om.kerberos.principal: om/_HOST@AD.RANGER-TEST
        ozone.om.service.ids: adhom
      hive:
        hive.metastore.sasl.enabled: true
        hive.metastore.uris: thrift://tsn-adh-k8s-1.ru-central1.internal:9083
        hive.metastore.kerberos.principal: hive/_HOST@AD.RANGER-TEST
        metastore.use.SSL: false
  2. Инициализируйте Spark History Server:

    $ ./adc init --spark-history-server --hadoop-file=hadoop_conf.yaml -o spark-history-server.yaml

    Данная команда создаст файл spark-history-server.yaml с шаблоном конфигурации.

  3. Отредактируйте конфигурационный файл:

    spark-history-server.yaml
    apiVersion: adc.arenadata.io/v1alpha1
    kind: SparkHistoryServer
    metadata:
      name: spark-history-server
      namespace: spark-history-server (1)
    spec:
      image: hub.arenadata.io/adc-enterprise/spark3:<tag> (2)
    
      ## Image pull secret for a private registry.
      ## Set 'externalSecretName' to reference an existing Secret,
      ## or set 'credentials' and optionally 'secretName' to let the CLI create one.
      #imagePullSecret:
      #  # Use a Secret managed outside ADC.
      #  externalSecretName: existing-registry-secret
      #
      #  ## Or let ADC create the Secret.
      #  #secretName: custom-registry-secret
      #
      #  #credentials:
      #  #  registry: registry.example.com
      #  #  username: user
      #  #  password: pass
    
      hadoop: (3)
        core:
          dfs.client.failover.proxy.provider.adh: org.apache.hadoop.hdfs.server.namenode.ha.ObserverReadProxyProvider
          dfs.ha.namenodes.adh: nn_tsn-adh-k8s-1,nn_tsn-adh-k8s-3
          dfs.datanode.kerberos.principal: hdfs-datanode/_HOST@AD.RANGER-TEST
          dfs.journalnode.kerberos.principal: hdfs-journalnode/_HOST@AD.RANGER-TEST
          dfs.namenode.kerberos.principal: hdfs-namenode/_HOST@AD.RANGER-TEST
          dfs.namenode.rpc-address.adh.nn_tsn-adh-k8s-1: tsn-adh-k8s-1.ru-central1.internal:8020
          dfs.namenode.rpc-address.adh.nn_tsn-adh-k8s-3: tsn-adh-k8s-3.ru-central1.internal:8020
          dfs.nameservices: adh
          fs.defaultFS: hdfs://adh
          hadoop.security.authentication: kerberos
        hdfs:
          dfs.client.read.shortcircuit: "false"
        hive:
          hive.metastore.kerberos.principal: hive/_HOST@AD.RANGER-TEST
          hive.metastore.sasl.enabled: "true"
          hive.metastore.uris: thrift://tsn-adh-k8s-1.ru-central1.internal:9083
          metastore.use.SSL: "false"
        ozone:
          ozone.om.kerberos.principal: om/_HOST@AD.RANGER-TEST
          ozone.om.address.adh.om_tsn-adh-k8s-1: tsn-adh-k8s-1.ru-central1.internal:9862
          ozone.om.address.adh.om_tsn-adh-k8s-2: tsn-adh-k8s-2.ru-central1.internal:9862
          ozone.om.address.adh.om_tsn-adh-k8s-3: tsn-adh-k8s-3.ru-central1.internal:9862
          ozone.om.nodes.adh: om_tsn-adh-k8s-1,om_tsn-adh-k8s-2,om_tsn-adh-k8s-3
          ozone.om.service.ids: adhom
    
      ## Kerberos configuration for authentication.
      kerberos: (4)
        realm: AD.RANGER-TEST
      #
      #  # Service name in the Kerberos principal. Defaults to the product name.
        service: spark-history
      #
      #  # Hostname in the Kerberos principal.
      #  # Required for a fixed service principal; leave it empty only to derive one principal per pod from the cluster domain.
        hostname: spark-history-server.ru-central1.internal
        keytab: (5)
      #    # true - kerberos-operator creates the keytab Secret.
      #    # false (default) - reference an existing keytab Secret with name keytab.secretName.
          create: true
      #
      #    # Name of the keytab Secret.
      #    # Optional when create: true - names the generated Secret (default: <name>-keytab).
      #    # Required when create: false - must reference an existing Secret.
          secretName: spark-history-keytab
      #
      #    # Label selector for the Pod that generates the keytab.
      #    # Required when create: true; ignored when create: false.
          labelSelector:
            env: prod
      #    #additionalPrincipals:
      #    #  - HTTP/history-lb.example.com
      #
          rotation:
            interval: 720h
            checkInterval: 1h
        spnego: true
    
      ## Java KeyStore/TrustStore certificate configuration.
      ## Set externalSecretName to reference an existing Secret,
      ## or set files and optional secretName to have ADC create it.
      #ssl:
      #  ## Name of the Secret containing Java keystores.
      #  #secretName: custom-spark-history-ssl
      #  externalSecretName: existing-spark-history-ssl
      #
      #  # Key in the Secret containing the truststore file.
      #  trustStoreKey: truststore.jks
      #
      #  ## Password for the truststore (optional).
      #  #trustStorePassword: bigdata
      #
      #  ## Key in the Secret containing the keystore file (optional).
      #  #keyStoreKey: keystore.jks
      #
      #  ## Password for the keystore (optional).
      #  #keyStorePassword: bigdata
      #
      #  ## Local files 'adc apply' puts into the Secret named by ssl.secretName.
      #  ## Relative paths are resolved against the config file.
      #  #files:
      #  #  trustStorePath: /path/to/truststore.jks
      #  #  #keyStorePath: /path/to/keystore.jks
    
      ## Use an external Hadoop configs Secret instead of the one rendered by ADC.
      #hadoopConfigsSecret:
      #  # Use a Secret managed outside ADC.
      #  externalSecretName: existing-history-hadoop-configs
      #
      #  ## Or let ADC create the Secret.
      #  #secretName: custom-history-hadoop-configs
    
      ## Use an external Spark History properties Secret instead of the one rendered by ADC.
      #historyPropertiesSecret:
      #  # Use a Secret managed outside ADC.
      #  externalSecretName: existing-history-properties
      #
      #  ## Or let ADC create the Secret.
      #  #secretName: custom-history-properties
    
      eventLogDir: "hdfs:///logs/spark/apps" (6)
      store:
        path: /var/log/spark/history (7)
        emptyDir: {}
        #pvc: (8)
        #  size: 10Gi
        #  storageClassName: my-storage-class
        #  accessModes:
        #    - ReadWriteOnce
    
      ## Monitoring configuration.
      #monitoring:
      #  # Enables Prometheus metrics export from this product's pods.
      #  exportMetrics: true
    
      #properties:
      #  spark.history.store.maxDiskUsage: 16g
    
      server:
        replicas: 1
        #resources:
        #  limits:
        #    cpu: "1"
        #    memory: 1Gi
        #  requests:
        #    cpu: 400m
        #    memory: 256Mi
      #extraMountSecrets:
      #  - mountPath: /etc/spark/extra-secret
      #    options:
      #      containerName: app
      #    secretName: extra-secret
    
      ## Controls whether Secret/ConfigMap changes restart pods.
      ## Set enabled: false to update referenced Secrets without restarting
      ## the workload; pods keep running the previous configuration until
      ## the policy is re-enabled. Defaults to enabled.
      #configurationRollout:
      #  enabled: false
    1 Пространство имен, используемое Spark History Server.
    2 URL образа Spark в вашем репозитории.
    3 Настройки Hadoop, взятые из ранее созданного файла hadoop_conf.yaml.
    4 Настройки Kerberos. Значение kerberos.hostname используется для генерации сервисного принципала Spark History Server и HTTP-принципала для SPNEGO.
    5 Если параметр keytab.create имеет значение true, оператор Kerberos сгенерирует секрет с именем, указанным в параметре keytab.secretName; если параметр keytab.secretName не указан, имя секрета будет иметь формат <metadata.name>-keytab. Если параметр keytab.create имеет значение false, имеющийся секрет с именем keytab.secretName будет использован для получения keytab-файла.
    6 URI директории в файловой системе, из которой History Server будет считывать данные (например, hdfs:///var/log/spark/apps, ofs://vol.bucket/spark/apps, s3a://bucket/spark/apps). Директория должна уже существовать. Приложения Spark должны записывать логи в эту же директорию. Принципалу приложения Spark необходимо иметь права w+x на эту директорию, а принципалу Spark History Server — r+x.
    7 Локальный путь хранения логов событий.
    8 Запрос на хранилище PVC. Должен быть установлен либо параметр pvc, либо параметр emptyDir.
  4. Вы можете проверить конфигурацию перед ее применением, выполнив команду apply с флагом --dry-run:

    $ ./adc apply -f spark-history-server.yaml --dry-run > spark-history-server-render.yaml
    spark-history-server-render.yaml
    ---
    apiVersion: v1
    kind: Secret
    metadata:
      name: spark-history-server-configs
      namespace: spark-history-server
    stringData:
      core-site.xml: |-
        <configuration>
          <property>
            <name>dfs.client.failover.proxy.provider.adh</name>
            <value>org.apache.hadoop.hdfs.server.namenode.ha.ObserverReadProxyProvider</value>
          </property>
          <property>
            <name>dfs.client.read.shortcircuit</name>
            <value>false</value>
          </property>
          <property>
            <name>dfs.datanode.kerberos.principal</name>
            <value>hdfs-datanode/_HOST@AD.RANGER-TEST</value>
          </property>
          <property>
            <name>dfs.ha.namenodes.adh</name>
            <value>nn_tsn-adh-k8s-1,nn_tsn-adh-k8s-3</value>
          </property>
          <property>
            <name>dfs.journalnode.kerberos.principal</name>
            <value>hdfs-journalnode/_HOST@AD.RANGER-TEST</value>
          </property>
          <property>
            <name>dfs.namenode.kerberos.principal</name>
            <value>hdfs-namenode/_HOST@AD.RANGER-TEST</value>
          </property>
          <property>
            <name>dfs.namenode.rpc-address.adh.nn_tsn-adh-k8s-1</name>
            <value>tsn-adh-k8s-1.ru-central1.internal:8020</value>
          </property>
          <property>
            <name>dfs.namenode.rpc-address.adh.nn_tsn-adh-k8s-3</name>
            <value>tsn-adh-k8s-3.ru-central1.internal:8020</value>
          </property>
          <property>
            <name>dfs.nameservices</name>
            <value>adh</value>
          </property>
          <property>
            <name>fs.defaultFS</name>
            <value>hdfs://adh</value>
          </property>
          <property>
            <name>hadoop.security.authentication</name>
            <value>kerberos</value>
          </property>
          <property>
            <name>ozone.om.address.adh.om_tsn-adh-k8s-1</name>
            <value>tsn-adh-k8s-1.ru-central1.internal:9862</value>
          </property>
          <property>
            <name>ozone.om.address.adh.om_tsn-adh-k8s-2</name>
            <value>tsn-adh-k8s-2.ru-central1.internal:9862</value>
          </property>
          <property>
            <name>ozone.om.address.adh.om_tsn-adh-k8s-3</name>
            <value>tsn-adh-k8s-3.ru-central1.internal:9862</value>
          </property>
          <property>
            <name>ozone.om.kerberos.principal</name>
            <value>om/_HOST@AD.RANGER-TEST</value>
          </property>
          <property>
            <name>ozone.om.nodes.adh</name>
            <value>om_tsn-adh-k8s-1,om_tsn-adh-k8s-2,om_tsn-adh-k8s-3</value>
          </property>
          <property>
            <name>ozone.om.service.ids</name>
            <value>adhom</value>
          </property>
        </configuration>
      hive-site.xml: |-
        <configuration>
          <property>
            <name>hive.metastore.kerberos.principal</name>
            <value>hive/_HOST@AD.RANGER-TEST</value>
          </property>
          <property>
            <name>hive.metastore.sasl.enabled</name>
            <value>true</value>
          </property>
          <property>
            <name>hive.metastore.uris</name>
            <value>thrift://tsn-adh-k8s-1.ru-central1.internal:9083</value>
          </property>
          <property>
            <name>metastore.use.SSL</name>
            <value>false</value>
          </property>
        </configuration>
    type: Opaque
    ---
    apiVersion: v1
    kind: Secret
    metadata:
      name: spark-history-server-spark-history-properties
      namespace: spark-history-server
    stringData:
      history-server.properties: |
        spark.eventLog.dir hdfs:///logs/spark/apps
        spark.eventLog.enabled true
        spark.history.fs.logDirectory hdfs:///logs/spark/apps
        spark.history.kerberos.enabled true
        spark.history.kerberos.keytab /opt/spark/kerberos/keytab
        spark.history.kerberos.principal spark-history/spark-history-server.ru-central1.internal@AD.RANGER-TEST
        spark.history.provider org.apache.spark.deploy.history.FsHistoryProvider
        spark.history.store.path /var/log/spark/history
        spark.history.ui.port 18080
        spark.org.apache.spark.filter.AuthenticationFilter.param.kerberos.keytab /opt/spark/kerberos/keytab
        spark.org.apache.spark.filter.AuthenticationFilter.param.kerberos.name.rules DEFAULT
        spark.org.apache.spark.filter.AuthenticationFilter.param.kerberos.principal HTTP/spark-history-server.ru-central1.internal@AD.RANGER-TEST
        spark.org.apache.spark.filter.AuthenticationFilter.param.token.validity 36000
        spark.org.apache.spark.filter.AuthenticationFilter.param.type kerberos
        spark.ui.filters org.apache.spark.filter.AuthenticationFilter
    type: Opaque
    ---
    apiVersion: krb5.arenadata.io/v1alpha1
    kind: Keytab
    metadata:
      name: spark-history-keytab
      namespace: spark-history-server
    spec:
      items:
      - labelSelector:
          env: prod
        principals:
        - spark-history/spark-history-server.ru-central1.internal
        - HTTP/spark-history-server.ru-central1.internal
        realm: AD.RANGER-TEST
      rotation:
        checkInterval: 1h
        interval: 720h
    ---
    apiVersion: spark.arenadata.io/v1alpha1
    kind: SparkHistoryServer
    metadata:
      name: spark-history-server
      namespace: spark-history-server
    spec:
      properties:
        secretKey: history-server.properties
        secretName: spark-history-server-spark-history-properties
      server:
        metadata: {}
        replicas: 1
        spec:
          emptyDirs:
          - /var/log/spark/history
          envs:
          - name: KRB5_CONFIG
            value: /opt/spark/kerberos/krb5.conf
          - name: SPARK_DAEMON_JAVA_OPTS
            value: -Djava.security.krb5.conf=/opt/spark/kerberos/krb5.conf
          image: hub.arenadata.io/adc-enterprise/spark3:<tag>
          imagePullPolicy: Always
          mountSecrets:
          - mountPath: /etc/hadoop/conf
            options:
              containerName: app
            secretName: spark-history-server-configs
          - mountPath: /opt/spark/kerberos
            options:
              containerName: app
              readOnly: true
            secretName: spark-history-keytab
          ports:
          - containerPort: 18080
            name: http
            servicePort: 18080
    status: {}
  5. Если манифест корректный, примените конфигурацию Spark History Server:

    $ ./adc apply -f spark-history-server.yaml

    Ожидаемый вывод содержит сообщение с подтверждением успеха:

    time="20260827130158UTC" level="info" msg="cluster spark-history-server applied to namespace spark-history-server"
  6. Проверьте работоспособность подов Spark History Server:

    $ kubectl get pods -n spark-history-server

    Ожидаемый вывод должен быть похож на следующий:

    NAME                                          READY   STATUS    RESTARTS   AGE
    spark-history-server-spark-history-server-0   1/1     Running   0          67s

Шаг 2. Предоставление доступа к веб-интерфейсу Spark History Server

Для доступа к веб-интерфейсу Spark History Server необходимо настроить один из способов публикации сервиса, например, используя балансировщик нагрузки (load balancer) или Ingress-контроллер. Все настройки, связанные с публикацией сервиса, включая DNS, аннотации, параметры Ingress, правила балансировщика и прочие, должны быть указаны в соответствии с вашей инфраструктурой Kubernetes.

  1. Получите внешний адрес балансировщика или Ingress-контроллера. Например:

    NAME                                        TYPE           CLUSTER-IP     EXTERNAL-IP   PORT(S)           AGE
    spark-lb                                    LoadBalancer   10.85.56.180   10.92.40.88   18080:30930/TCP   119s
  2. Добавьте следующую строку в файл /etc/hosts на машине, с которой будет производиться доступ:

    <lb_ip> spark-history-server.ru-central1.internal
  3. Откройте веб-интерфейс Spark History Server в браузере, используя URL http://spark-history-server.ru-central1.internal:18080 (измените протокол на https и номер порта на spec.ssl.port, если используете SSL).

    Веб-интерфейс Spark History Server
    Веб-интерфейс Spark History Server
    Веб-интерфейс Spark History Server
    Веб-интерфейс Spark History Server

Шаг 3. Проверка запуском приложения Spark

  1. Создайте приложение Spark согласно инструкции.

  2. Измените в конфигурации приложения параметр spec.eventLogDir. Значение этого параметра и значение параметра spec.eventLogDir в конфигурации Spark History Server должны совпадать:

    eventLogDir: "hdfs:///logs/spark/apps"
  3. Запустите приложение Spark:

    $ ./adc apply -f spark-application.yaml
  4. Получите логи задач с помощью команды curl:

    $ curl --negotiate -u : http://spark-history-server.ru-central1.internal:18080/api/v1/applications

    Вывод:

    [ {
      "id" : "spark-595fd320089e46f4ab2baea0c325caed",
      "name" : "Spark SQL basic example",
      "attempts" : [ {
        "startTime" : "2026-08-31T09:49:06.344GMT",
        "endTime" : "2026-08-31T09:49:21.897GMT",
        "lastUpdated" : "2026-08-31T09:49:22.029GMT",
        "duration" : 15553,
        "sparkUser" : "spark",
        "completed" : true,
        "appSparkVersion" : "3.5.4.4-4.3.0-2",
        "startTimeEpoch" : 1788169746344,
        "endTimeEpoch" : 1788169761897,
        "lastUpdatedEpoch" : 1788169762029
      } ]
    } ]

    Если в браузере настроена поддержка SPNEGO, можно получить доступ к веб-интерфейсу Spark History Server по URL http://spark-history-server.ru-central1.internal:18080.

Нашли ошибку? Выделите текст и нажмите Ctrl+Enter чтобы сообщить о ней