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

Требования

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

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

    hadoop_conf.yaml
    sites:
      core:
        fs.defaultFS: hdfs://adh
        hadoop.security.authentication: simple
        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
        hadoop.ssl.enabled: true
      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.service.ids: adhom
      hive:
        hive.metastore.sasl.enabled: false
        hive.metastore.uris: thrift://tsn-adh-k8s-1.ru-central1.internal:9083
        metastore.use.SSL: true
  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.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: simple
          hadoop.ssl.enabled: "true"
        hdfs:
          dfs.client.read.shortcircuit: "false"
        hive:
          hive.metastore.sasl.enabled: "false"
          hive.metastore.uris: thrift://tsn-adh-k8s-1.ru-central1.internal:9083
          metastore.use.SSL: "true"
        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.service.ids: adhom
    
      ## Kerberos configuration for authentication.
      #kerberos:
      #  realm: EXAMPLE.COM
      #
      #  # 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: history.example.com
      #  keytab:
      #    # true - kerberos-operator creates the keytab Secret.
      #    # false (default) - reference an existing keytab Secret with name keytab.secretName.
      #    create: false
      #
      #    # 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: (4)
      #  ## 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: /etc/ssl/truststore.jks
          keyStorePath: /etc/ssl/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" (5)
      store:
        path: /var/log/spark/history (6)
        emptyDir: {}
        #pvc: (7)
        #  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 Настройки SSL. Установите параметры keystore, чтобы веб-интерфейс был доступен в браузере.
    5 URI директории в файловой системе, из которой History Server будет считывать данные (например, hdfs:///var/log/spark/apps, ofs://vol.bucket/spark/apps, s3a://bucket/spark/apps). Директория должна уже существовать. Приложения Spark должны записывать логи в эту же директорию.
    6 Локальный путь хранения логов событий.
    7 Запрос на хранилище 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.ha.namenodes.adh</name>
            <value>nn_tsn-adh-k8s-1,nn_tsn-adh-k8s-3</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>simple</value>
          </property>
          <property>
            <name>hadoop.ssl.enabled</name>
            <value>true</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.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.sasl.enabled</name>
            <value>false</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>true</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.provider org.apache.spark.deploy.history.FsHistoryProvider
        spark.history.store.path /var/log/spark/history
        spark.history.ui.port 18080
        spark.ssl.historyServer.enabled true
        spark.ssl.historyServer.keyPassword bigdata
        spark.ssl.historyServer.keyStore /etc/ssl/keystore.jks
        spark.ssl.historyServer.keyStorePassword bigdata
        spark.ssl.historyServer.keyStoreType JKS
        spark.ssl.historyServer.port 18480
        spark.ssl.historyServer.protocol TLSv1.2
        spark.ssl.historyServer.trustStore /etc/ssl/truststore.jks
        spark.ssl.historyServer.trustStorePassword bigdata
        spark.ssl.historyServer.trustStoreType JKS
    type: Opaque
    ---
    apiVersion: v1
    data:
      keystore.jks: <encoded-keystore>
      truststore.jks: <encoded-truststore>
    kind: Secret
    metadata:
      name: custom-spark-history-ssl
      namespace: spark-history-server
    type: Opaque
    ---
    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
          image: hub.arenadata.io/adc-enterprise/spark3:<tag>
          imagePullPolicy: Always
          mountSecrets:
          - mountPath: /etc/hadoop/conf
            options:
              containerName: app
            secretName: spark-history-server-configs
          - mountPath: /etc/ssl/keystore.jks
            options:
              containerName: app
              readOnly: true
              subPath: keystore.jks
            secretName: custom-spark-history-ssl
          - mountPath: /etc/ssl/truststore.jks
            options:
              containerName: app
              readOnly: true
              subPath: truststore.jks
            secretName: custom-spark-history-ssl
          ports:
          - containerPort: 18080
            name: http
            servicePort: 18080
          - containerPort: 18480
            name: https
            servicePort: 18480
    status: {}
  5. Если манифест корректный, примените конфигурацию Spark History Server:

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

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

    time="20260831105641UTC" 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          87s

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

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

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

    NAMESPACE              NAME                                        TYPE           CLUSTER-IP     EXTERNAL-IP    PORT(S)                           AGE
    spark-history-server   spark-lb                                    LoadBalancer   10.85.56.180   10.92.40.88    18080:30930/TCP,18480:32502/TCP   4d
  2. Откройте веб-интерфейс Spark History Server в браузере, используя URL https://10.92.40.88:18480.

    Веб-интерфейс 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. Проверьте, что логи доступны в веб-интерфейсе Spark History Server.

    Завершенная задача в веб-интерфейсе Spark History Server
    Завершенная задача в веб-интерфейсе Spark History Server
    Завершенная задача в веб-интерфейсе Spark History Server
    Завершенная задача в веб-интерфейсе Spark History Server
Нашли ошибку? Выделите текст и нажмите Ctrl+Enter чтобы сообщить о ней