Рубрики
Без рубрики

Мое путешествие со искрой на Кубернане … В Python (2/3)

В предыдущей статье мы увидели, как запустить Spark Applications с оператором Spark. В этом искусстве … помечено искру, Куберане, Python.

В предыдущей статье мы увидели, как запустить Spark Applications с оператором Spark. В этой статье мы увидим, как сделать то же самое, но родом с искрой. Сначала объясним различия между двумя способами развертывания вашего водителя на рабочих узлах.

Spark-toke в режиме кластера

В Режим кластера Ваше заявление подано от машины далеко от рабочих машин (например, на вашем ноутбуке). Вам нужно распределение Spark, установленное на этой машине, чтобы на самом деле запустить Spark-Отправить скрипт В этом режиме скрипт выходит нормально, как только приложение было представлено. Затем водитель отсоединяется и может работать самостоятельно в кластере Куберанес. Вы можете распечатать журналы POD драйвера с Kubectl Logs Команда, чтобы увидеть вывод приложения.

Можно использовать аутентификацию kubectl Прокси для связи с API Kubernetes.

Локальный прокси может быть запущен:

kubectl proxy &

Если локальный прокси работает на localhost: 8001, удаленную кластер Kubernetes можно добраться до Spark-Отправить Указав --master k8s://http://127.0.0.1: 8001 как аргумент для Spark-Отправить Отказ

Spark – отправить в режиме клиента

В Режим клиента , Spark-Отправить Команда напрямую передается со своими аргументами к контейнеру искры в POD драйвера. С Режим развертывания Опция установить на клиент Драйвер запущен прямо в пределах Spark-Отправить процесс, который действует как клиент к кластеру. Вход и вывод приложения прикреплен к журналам от POD.

Кто что делает?

С «родной» искрой мы будем выполнять искровые приложения в клиентском режиме, чтобы не зависеть от локального распределения искры. В частности, пользователь создает ресурс POD драйвера с kubectl и стручок водителя будет работать Spark-Отправить В клиентском режиме внутренне для запуска водительской программы.

С искровой оператором, A Свечающее применение должен установить .spec.deploymode. к кластер , как клиент в настоящее время не реализован. За кулисами поведение точно так же, как и с собственной искрой: контроллер оператора встраивает распределение искры, которое играет роль планировщика Spark; Стручки водителя порождают от контроллера … а затем запустить Spark-Отправить В клиентском режиме внутренне для запуска водительской программы. Но это глобально прозрачно для конечного пользователя.

Дополнительные детали того, как Свечания запуска можно найти в Проектная документация Отказ

С коренной искрой основным ресурсом является драйвером POD. Для запуска программы PI пример, как с оператором Spark, POD драйвера должен быть создан с использованием данных в следующем файле YAML:

Pyspark-Pi-Driver-Pod.yaml

apiVersion: v1
kind: Pod
metadata:
  labels:
    app-name: pyspark-pi-${PRIORITY_CLASS_NAME}${NAME_SUFFIX}
    spark-role: driver
  name: pyspark-pi-${PRIORITY_CLASS_NAME}${NAME_SUFFIX}-driver
  namespace: ${NAMESPACE}
spec:
  containers:
  - name: pyspark-pi
    image: eu.gcr.io/yippee-spark-k8s/spark-py:3.0.1
    imagePullPolicy: IfNotPresent
    ports:
    - containerPort: 5678
      name: headless-svc
    - containerPort: 4040
      name: web-ui
    resources:
      requests:
        memory: 512Mi
        cpu: 1
      limits:
        cpu: 1200m
    env:
    # Overriding configuration directory
    - name: SPARK_CONF_DIR
      value: /spark-conf
    - name: SPARK_HOME
      value: /opt/spark
    # Configure all key-value pairs in ConfigMap as container environment variables
    envFrom:
      - configMapRef:
          name: pyspark-pi-${PRIORITY_CLASS_NAME}${NAME_SUFFIX}-cm
    args:
    - $(SPARK_HOME)/bin/spark-submit
    - /opt/spark/examples/src/main/python/pi.py
    - "10"
    volumeMounts:
      - name: spark-config
        mountPath: /spark-conf
        readOnly: true
  affinity:
    nodeAffinity:
      requiredDuringSchedulingIgnoredDuringExecution:
        nodeSelectorTerms:
          - matchExpressions:
            - key: type
              operator: In
              values: [${DRIVER_NODE_AFFINITIES}]
  priorityClassName: ${PRIORITY_CLASS_NAME}
  restartPolicy: OnFailure
  schedulerName: volcano
  serviceAccountName: ${SERVICE_ACCOUNT_NAME}
  volumes:
    # Add the executor pod template in read-only volume, for the driver to read
    - name: spark-config
      configMap:
        name: pyspark-pi-${PRIORITY_CLASS_NAME}${NAME_SUFFIX}-cm
        items:
        - key: spark-defaults.conf
          path: spark-defaults.conf
        - key: spark-env.sh
          path: spark-env.sh
        - key: executor-pod-template.yaml
          path: executor-pod-template.yaml

Не обращайте внимания на вариабельные заполнители уже сейчас Несмотря на то, что вы можете себе представить, для чего они могут быть использованы.

Как видите, драйвер POD зависит от других ресурсов Kubernetes. Мы подробно расскажем каждого из них.

Мы используем Configmap Для установки данных конфигурации искры отдельно от определения POD драйвера.

apiVersion: v1
kind: ConfigMap
metadata:
  labels:
    app-name: spark-${PRIORITY_CLASS_NAME}${NAME_SUFFIX}
  name: spark-${PRIORITY_CLASS_NAME}${NAME_SUFFIX}-cm
  namespace: ${NAMESPACE}
data:
  # Comma-separated list of .zip, .egg, or .py files dependencies for Python apps.
  # spark.submit.pyFiles can be used instead in spark-defaults.conf below.
  # PYTHONPATH: ...
  spark-env.sh: |
    #!/usr/bin/env bash

    export DUMMY=dummy
    echo "Here we are, ${DUMMY}!"
  spark-defaults.conf: |
    spark.master                                k8s://https://kubernetes.default
    spark.submit.deployMode                     client

    spark.executor.instances                    2
    spark.executor.cores                        1
    spark.executor.memory                       512m

    spark.kubernetes.executor.container.image   eu.gcr.io/yippee-spark-k8s/spark-py:3.0.1
    spark.kubernetes.container.image.pullPolicy IfNotPresent
    spark.kubernetes.namespace                  spark-jobs
    # Must match the mount path of the ConfigMap volume in driver pod
    spark.kubernetes.executor.podTemplateFile   /spark-conf/executor-pod-template.yaml
    spark.kubernetes.pyspark.pythonVersion      2
    spark.kubernetes.driver.pod.name            spark-${PRIORITY_CLASS_NAME}${NAME_SUFFIX}-driver

    spark.driver.host                           spark-${PRIORITY_CLASS_NAME}${NAME_SUFFIX}-driver-svc
    spark.driver.port                           5678

    # Config params for use with an ingress to expose the Web UI
    spark.ui.proxyBase                          /spark-${PRIORITY_CLASS_NAME}${NAME_SUFFIX}
    # spark.ui.proxyRedirectUri                   http://
  executor-pod-template.yaml: |
    apiVersion: v1
    kind: Pod
    metadata:
      labels:
        app-name: spark-${PRIORITY_CLASS_NAME}${NAME_SUFFIX}
        spark-role: executor
    spec:
      affinity:
        nodeAffinity:
          requiredDuringSchedulingIgnoredDuringExecution:
            nodeSelectorTerms:
            - matchExpressions:
              - key: type
                operator: In
                values: [${EXECUTOR_NODE_AFFINITIES}]
      priorityClassName: ${PRIORITY_CLASS_NAME}
      schedulerName: volcano

Шаблон POD исполнителя

Мы используем файл шаблона в Configmap Чтобы определить конфигурацию POD исполнителя. Шаблоны Обычно используются для настройки Spark Pods, которые не могут быть настроены в противном случае, через Spark Properties или переменные среды. Таким образом, файлы шаблонов в основном содержат мелкозернируемую конфигурацию, связанную с развертыванием на уровне Kubernetes: здесь, аффинность узла и имя класса приоритета.

Чтобы сделать файл шаблона POD доступен для процесса отправки Spark, мы должны установить свойство искры Spark.kubernetes.executor.podtemplateFile с его локальным путем в драйвере POD. Для этого файл будет автоматически установлен на том объеме водителя POD, когда он создан.

Коллекция мусора исполнителя

Мы также должны установить Spark.kubernetes.draver.pod.name. Для исполнителей на имя драйвера POD. Когда это свойство установлено, Spark SCECTULER развернут Pods Исполнителя с помощью OlludherReference , который, в свою очередь, будет гарантировать, что после удаления POD драйвера удаляется из кластера, все Pods исполнителя приложения также будут удалены.

В клиентском режиме водитель проходит внутри стручка. Искрыватели искры должны быть в состоянии подключиться к драйверу искры с помощью сетей Kubernetes. Для этого мы используем без головы Сервис, позволяющий регулировать POD драйвера из исполнителей устойчивым именем хоста. При развертывании службы без головы, мы гарантируем, что сервис будет соответствовать только для POD драйверов и никаких других стручков, назначая драйверу POD (достаточно) уникальную метку и, используя эту метку в селектор этикетки безголового обслуживания. Затем мы можем перейти к исполняющим пользователям водительское имя хоста через Spark.driver.host С именем службы и портом Spark Driver на spark.driver.port Отказ

Spark-Driver-Svc.yaml

apiVersion: v1
kind: Service
metadata:
  labels:
    app-name: spark-${PRIORITY_CLASS_NAME}${NAME_SUFFIX}
  name: spark-${PRIORITY_CLASS_NAME}${NAME_SUFFIX}-driver-svc
  namespace: spark-jobs
spec:
  clusterIP: None
  ports:
  - name: 5678-5678
    port: 5678
    protocol: TCP
    targetPort: 5678
  selector:
    app-name: spark-${PRIORITY_CLASS_NAME}${NAME_SUFFIX}
    spark-role: driver
  type: ClusterIP

Проходность

Зажигая UI доступна путем создания службы типа Clusterip который обнажает интернет-интерфейс из стручка драйвера:

Spark-ui-svc.yaml

apiVersion: v1
kind: Service
metadata:
  labels:
    app-name: spark-${PRIORITY_CLASS_NAME}${NAME_SUFFIX}
  name: spark-${PRIORITY_CLASS_NAME}${NAME_SUFFIX}-ui-svc
  namespace: spark-jobs
spec:
  ports:
  - name: spark-driver-ui-port
    port: 4040
    protocol: TCP
    targetPort: 4040
  selector:
    app-name: spark-${PRIORITY_CLASS_NAME}${NAME_SUFFIX}
    spark-role: driver
  type: ClusterIP

С помощью этой услуги UI доступно только изнутри кластера. Затем мы должны создать Вход выставлять пользовательский интерфейс вне кластера. Для работы входного ресурса на работу кластер должен иметь контроллер входа Бег. Вы можете выбрать Реализация контроллера входа Что лучше всего подходит для вашего кластера. Трафик и Nginx очень популярные варианты. Приведенный ниже проникновение настроен для Nginx:

Spark-ui-Ingress.yaml

apiVersion: networking.k8s.io/v1beta1
kind: Ingress
metadata:
  labels:
    app-name: spark-${PRIORITY_CLASS_NAME}${NAME_SUFFIX}
  name: spark-${PRIORITY_CLASS_NAME}${NAME_SUFFIX}-ui-ingress
  namespace: spark-jobs
  annotations:
    kubernetes.io/ingress.class: nginx
    nginx.ingress.kubernetes.io/rewrite-target: /$2
    nginx.ingress.kubernetes.io/proxy-redirect-from: "http://$host/"
    nginx.ingress.kubernetes.io/proxy-redirect-to: "http://$host/spark-${PRIORITY_CLASS_NAME}${NAME_SUFFIX}/"
spec:
  rules:
  - http:
      paths:
      - path: /spark-${PRIORITY_CLASS_NAME}${NAME_SUFFIX}(/|$)(.*)
        backend:
          serviceName: spark-${PRIORITY_CLASS_NAME}${NAME_SUFFIX}-ui-svc
          servicePort: 4040

Приступ подкрепленный как многие услуги, что и стручки драйверов, которые работают одновременно в кластере. Каждое UI должно быть адресовано уникальным путь в проникновении. Оцененный путь напрямую получен от имени зажигания приложения с его уникальным идентификатором.

Интерфейс всегда доступен в http://: 4040 (Изнутри кластера), поскольку каждый POD драйвера присваивается уникальный IP и есть только один SparkContext Бег в хосте POD (в противном случае UI будет связан с последовательными портами, начиная с 4040: 4041, 4042 и т. Д.).

Таким образом, в результате чего разные UIS, которые могут быть доступен в данном времени на одном хосте, отличаются только их назначенным номером порта. UI разработан по этому принципу, и все операции HTTP выполняются относительно того же корневого пути в URL, который … / Отказ

Таким образом, для пользовательского интерфейса будет эффективно доступен через проникновение, мы должны настроить перенаправление HTTP с альтернативным корневым путем (тот, который настроен в проникновении). Чтобы работать гладко, сам пользовательский интерфейс должен знать об этом перенаправлении, установив Spark.ui.Proxybase на этот корневой путь … И вот и все!

Оператор искры

Оператор поддерживает создание необязательного входа для веб-интерфейса Spark Web. Это может быть включено, установив Ingress-URL-формат Флаг командной строки. Ingress-URL-формат должен быть шаблон, как {{$ appname}}. {ingress_suffix}/{{$ appjects}}/{{$ appname}} Отказ {ingress_suffix} Следует заменяться пользователем, чтобы указать URL-адрес кластера, и оператор заменит {{$ appname}} и {{$ appnamespace}} с соответствующим значением. Обратите внимание, что входная поддержка требует, чтобы маршрутизатор URL-адреса кластера правильно установлен. Для е.Г. Если Ingress-URL-формат это {{$ appname}}. Ingress.Cluster.com это требует что-нибудь * .ingress.Cluster.com. должен быть направлен на контроллер входа на кластере K8S.

Пример Оператора Spark Установите с Ingress-URL-формат Флаг командной строки:

./helm install spark-operator incubator/sparkoperator --namespace spark-operator --set enableWebhook=true --set enableBatchScheduler=true --set ingressUrlFormat="\{\{\$appName\}\}.ingress.cluster.com"

Обратите внимание, что фигурные скобки должны быть сбережены.

Будущая работа

Я не мог включить вход за искровой оператор во время моих экспериментов, просто потому, что требуется имя DNS. Вместо этого тот же проникновение, что и для родной искры, «привилось» к Свечающее применение , с маршрутизацией на основе путь.

Операция, предложенная оператором Spark, с маршрутизацией на основе подстановочных знаков HostName (например * .ingress.Cluster.com. ), тем не менее, интересно, так как он преодолел проблему перенаправления HTTP, описанных выше.

С помощью подстановочных знаков хоста и, следовательно, без перенаправления HTTP, служба пользовательского интерфейса может быть переключена на NodePort Тип (сервис NodePort обнаруживает службу на IP каждого узла в статическом порту) и все еще будет совместимым с проникновением. Таким образом, интернет-интерфейс будет доступен за пределами кластера с помощью обоих попаданий с его внешним URL-адресом, и NodePort Сервис на http://: Отказ Сервис типа NodePort все еще актуален в частном облаке.

Мы используем Имя приложения Этикетка до семантически группы все ресурсы Kubernetes, связанные с одним зажимом. В нашем случае эта этикетка обеспечивает уникальность, и мы не ожидаем, что множественные зажимательные приложения для несут одинаковое значение для этой этикетки (по крайней мере, в Maintefet Tame – время пространства имен

Для данного зажимного приложения все имена объектов затем выводятся из Имя приложения Отказ Мы просто добавляем суффикс, который также квалифицирует тип объекта: -раживая Для подвода драйвера -Ивер-SVC Для службы водителя, -Уи-SVC Для сервиса Spark UI, -у-проход для зажигания ui вход, а -cm для конфигурации.

Мы также устанавливаем этикетку искровая роль на уровне стручка, чтобы дифференцировать драйверы от своих исполнителей.

Это именование и маркировка согласуются с оператором Spark. В результате искровые приложения могут быть обработаны и отфильтрованы таким же образом, независимо от того, запущены ли они с помощью оператора Spark или Spark-Sead. 👍.

Теперь нам удалось подражать тому же поведению, которое мы получили с оператором искры, вот то, что мы развернули в Кубейнетах:

Теперь, когда мы определили все наши ресурсы Kubernetes для Spark-Dover, мы собираемся получить наши руки на какой-нибудь код Python, чтобы организовать все это. Увидимся в третьей и окончательной статье в этой серии.

Оригинал: “https://dev.to/stack-labs/my-journey-with-spark-on-kubernetes-in-python-2-3-5e8j”