Каждый, кто работал с Apache Kyuubi в связке с Kubernetes, наверняка знаком с этим неприятным сообщением: "spark engine launch timeout". Оно появляется в самый неподходящий момент, когда задача критически важна, а дедлайн дышит в спину. Я сталкивался с этой проблемой десятки раз и хочу поделиться опытом её диагностики и устранения.

Что происходит под капотом при запуске Spark Engine

Apache Kyuubi выступает посредником между клиентскими приложениями и распределёнными вычислительными движками. Когда пользователь отправляет SQL-запрос через JDBC или Thrift, Kyuubi должен поднять Spark Engine - отдельный процесс, который фактически выполнит всю тяжёлую работу по обработке данных.

В среде Kubernetes этот процесс превращается в настоящий квест с множеством участников. Сначала Kyuubi Server получает запрос на создание сессии и определяет, нужен ли новый движок или можно использовать существующий. Проверить текущие сессии можно через REST API Kyuubi:

bash
 
# Список активных сессий
curl -X GET "http://kyuubi-server:10099/api/v1/sessions" \
  -H "Content-Type: application/json"

# Список запущенных движков
curl -X GET "http://kyuubi-server:10099/api/v1/admin/engine" \
  -H "Content-Type: application/json"

Если требуется свежий экземпляр, сервер формирует конфигурацию SparkConf и инициирует запуск через spark-submit с параметром --master k8s://https://kubernetes-api:6443. Kubernetes API Server принимает запрос на создание Driver Pod. Scheduler выбирает подходящий узел для размещения. Kubelet на целевом узле скачивает образ и запускает контейнер. Driver Pod стартует и регистрируется обратно в Kyuubi Server. После успешной регистрации создаются Executor Pods.

На каждом из этих этапов что-то может пойти не так. Таймаут возникает, когда вся цепочка не завершается за отведённое время, заданное параметром kyuubi.session.engine.launch.timeout. По умолчанию это значение составляет PT300S (5 минут), что может быть недостаточно в загруженных кластерах.

Kubernetes Namespace: первый подозреваемый

Пространства имён в Kubernetes создают логическую изоляцию ресурсов. Параметр spark.kubernetes.namespace определяет, в каком пространстве будут создаваться поды Spark Driver и Executor. Здесь начинаются первые подводные камни.

Проверим состояние целевого namespace:

bash
 
# Проверка существования и состояния namespace
kubectl get ns spark-workloads -o yaml

# Детальная информация
kubectl describe ns spark-workloads

Если namespace находится в состоянии Terminating, создавать в нём ресурсы невозможно. Такое бывает, когда кто-то запустил удаление namespace, но в нём остались ресурсы с финализаторами.

Администраторы кластера часто устанавливают Resource Quotas на пространства имён, чтобы предотвратить монополизацию ресурсов одной командой. Когда квота исчерпана, новые поды просто не создаются, а Spark Driver застревает на этапе ожидания:

bash
 
# Проверка квот в namespace
kubectl describe quota -n spark-workloads

# Или в формате YAML для автоматизации
kubectl get resourcequota -n spark-workloads -o yaml

Типичный вывод выглядит так:

Name: spark-quota Namespace: spark-workloads Resource Used Hard -------- ---- ---- pods 45 50 requests.cpu 90 100 requests.memory 180Gi 200Gi limits.cpu 180 200 limits.memory 360Gi 400Gi

Если значения Used приближаются к Hard, это прямой путь к таймаутам. Также стоит проверить LimitRange, который может блокировать поды с "неправильными" запросами ресурсов:

bash
kubectl get limitrange -n spark-workloads -o yaml

Пример LimitRange, который может вызвать проблемы:

yaml
 
 
apiVersion: v1
kind: LimitRange
metadata:
  name: spark-limits
  namespace: spark-workloads
spec:
  limits:
  - default:
      cpu: "2"
      memory: "4Gi"
    defaultRequest:
      cpu: "500m"
      memory: "1Gi"
    max:
      cpu: "4"
      memory: "8Gi"
    min:
      cpu: "100m"
      memory: "256Mi"
    type: Container

Если Spark запрашивает 16Gi памяти для Driver, а LimitRange разрешает максимум 8Gi - под не создастся.

В конфигурации Kyuubi namespace задаётся в файле kyuubi-defaults.conf или spark-defaults.conf:

properties
 
 
spark.kubernetes.namespace=spark-workloads
spark.kubernetes.driver.label.app=kyuubi-spark
spark.kubernetes.executor.label.app=kyuubi-spark

Service Account: ключ к королевству ресурсов

Service Account в Kubernetes подобен удостоверению личности для приложения. Spark Driver Pod запускается от имени определённого аккаунта и использует его токен для взаимодействия с API Server. Параметр spark.kubernetes.authenticate.driver.serviceAccountName определяет этот аккаунт.

Проблема возникает, когда у Service Account недостаточно прав. Для нормальной работы Spark на Kubernetes требуются разрешения на создание, чтение, обновление и удаление подов, сервисов и ConfigMap. Без этих прав Driver не сможет запустить Executor'ы.

Создадим правильно настроенный Service Account со всеми необходимыми правами:

yaml
 
 
apiVersion: v1
kind: ServiceAccount
metadata:
  name: spark-sa
  namespace: spark-workloads
---
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  name: spark-role
  namespace: spark-workloads
rules:
- apiGroups: [""]
  resources: ["pods"]
  verbs: ["get", "list", "watch", "create", "delete", "patch"]
- apiGroups: [""]
  resources: ["pods/log"]
  verbs: ["get", "list"]
- apiGroups: [""]
  resources: ["pods/status"]
  verbs: ["get"]
- apiGroups: [""]
  resources: ["configmaps"]
  verbs: ["get", "list", "create", "delete", "update"]
- apiGroups: [""]
  resources: ["services"]
  verbs: ["get", "list", "create", "delete"]
- apiGroups: [""]
  resources: ["persistentvolumeclaims"]
  verbs: ["get", "list", "create", "delete"]
- apiGroups: [""]
  resources: ["secrets"]
  verbs: ["get", "list"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  name: spark-role-binding
  namespace: spark-workloads
subjects:
- kind: ServiceAccount
  name: spark-sa
  namespace: spark-workloads
roleRef:
  kind: Role
  name: spark-role
  apiGroup: rbac.authorization.k8s.io

Проверить права можно командой kubectl auth can-i:

bash
 
 
# Проверка конкретных разрешений
kubectl auth can-i create pods \
  --as=system:serviceaccount:spark-workloads:spark-sa \
  -n spark-workloads

kubectl auth can-i delete pods \
  --as=system:serviceaccount:spark-workloads:spark-sa \
  -n spark-workloads

kubectl auth can-i create configmaps \
  --as=system:serviceaccount:spark-workloads:spark-sa \
  -n spark-workloads

# Проверка всех разрешений разом
kubectl auth can-i --list \
  --as=system:serviceaccount:spark-workloads:spark-sa \
  -n spark-workloads

Если любой ответ "no", RBAC настроен неверно. Для диагностики полезно посмотреть существующие привязки ролей:

bash
 
 
kubectl get rolebindings -n spark-workloads -o wide
kubectl describe rolebinding spark-role-binding -n spark-workloads

Классическая ошибка - создать Role в одном namespace, а RoleBinding в другом, или опечататься в имени ServiceAccount.

Spark Properties Override: тонкая настройка против грубой силы

Kyuubi позволяет переопределять свойства Spark на нескольких уровнях: в конфигурационных файлах сервера, через параметры сессии и даже в самих SQL-запросах. Эта гибкость становится источником проблем, когда настройки конфликтуют между собой.

Иерархия конфигураций в Kyuubi (от низшего приоритета к высшему):

  1. spark-defaults.conf - базовые настройки Spark
  2. kyuubi-defaults.conf - настройки Kyuubi и переопределения Spark
  3. Переменные окружения
  4. Параметры сессии от клиента
  5. SET-команды в SQL

Пример базовой конфигурации kyuubi-defaults.conf:

properties
 
 
# Kyuubi Server settings
kyuubi.session.engine.launch.timeout=PT600S
kyuubi.engine.share.level=USER
kyuubi.engine.type=SPARK_SQL
kyuubi.session.engine.startup.error.max.size=8192

# Kubernetes connection
spark.master=k8s://https://kubernetes.default.svc:443
spark.kubernetes.namespace=spark-workloads
spark.kubernetes.authenticate.driver.serviceAccountName=spark-sa

# Container images
spark.kubernetes.container.image=apache/spark:3.5.0
spark.kubernetes.container.image.pullPolicy=IfNotPresent
spark.kubernetes.container.image.pullSecrets=registry-secret

# Driver configuration
spark.kubernetes.driver.request.cores=1
spark.kubernetes.driver.limit.cores=2
spark.driver.memory=2g
spark.driver.memoryOverhead=512m
spark.kubernetes.driver.label.app=kyuubi-spark
spark.kubernetes.driver.label.component=driver
spark.kubernetes.driver.annotation.prometheus.io/scrape=true

# Executor configuration  
spark.kubernetes.executor.request.cores=1
spark.kubernetes.executor.limit.cores=2
spark.executor.memory=4g
spark.executor.memoryOverhead=1g
spark.executor.instances=3
spark.dynamicAllocation.enabled=true
spark.dynamicAllocation.minExecutors=1
spark.dynamicAllocation.maxExecutors=10
spark.dynamicAllocation.executorIdleTimeout=60s

# File staging
spark.kubernetes.file.upload.path=s3a://bucket/spark-uploads

# Network and timeouts
spark.kubernetes.submission.connectionTimeout=60000
spark.kubernetes.submission.requestTimeout=60000
spark.kubernetes.driver.connectionTimeout=60000
spark.kubernetes.driver.requestTimeout=60000

Если администратор установил spark.executor.instances=10 на уровне сервера, но Resource Quota позволяет создать только 5 подов, движок может зависнуть в ожидании ресурсов. При включённом dynamic allocation ситуация сглаживается, но статическая конфигурация приводит к таймауту.

Проверить итоговую конфигурацию можно в логах Driver Pod:

bash
 
 
# Найти Driver pod
kubectl get pods -n spark-workloads -l spark-role=driver --sort-by=.metadata.creationTimestamp

# Посмотреть конфигурацию в логах
kubectl logs <driver-pod-name> -n spark-workloads | grep "spark\." | head -50

# Или конкретные параметры
kubectl logs <driver-pod-name> -n spark-workloads | grep -E "spark\.(executor|driver|kubernetes)"

Также можно извлечь конфигурацию из ConfigMap, который Spark создаёт для Driver:

bash
 
 
kubectl get configmap -n spark-workloads -l spark-role=driver
kubectl get configmap <configmap-name> -n spark-workloads -o yaml

Pod Template: анатомия настраиваемого запуска

Pod Template - мощный инструмент кастомизации, который позволяет задать практически любые параметры пода: от nodeSelector до init-контейнеров. Указывается через свойства spark.kubernetes.driver.podTemplateFile и spark.kubernetes.executor.podTemplateFile.

Вот пример шаблона для Driver Pod, который я использую в production:

yaml
 
 
apiVersion: v1
kind: Pod
metadata:
  labels:
    app: spark-driver
    component: kyuubi-engine
    team: data-platform
spec:
  # Размещение на определённых узлах
  nodeSelector:
    node-type: compute
    spark-workload: "true"
  
  # Tolerations для tainted узлов
  tolerations:
  - key: "spark-workload"
    operator: "Equal"
    value: "true"
    effect: "NoSchedule"
  - key: "dedicated"
    operator: "Equal"
    value: "spark"
    effect: "NoSchedule"
  
  # Affinity rules для распределения
  affinity:
    podAntiAffinity:
      preferredDuringSchedulingIgnoredDuringExecution:
      - weight: 100
        podAffinityTerm:
          labelSelector:
            matchLabels:
              spark-role: driver
          topologyKey: kubernetes.io/hostname
  
  # Security context
  securityContext:
    runAsUser: 1000
    runAsGroup: 1000
    fsGroup: 1000
  
  # Init containers для подготовки
  initContainers:
  - name: wait-for-metastore
    image: busybox:1.36
    command: 
    - 'sh'
    - '-c'
    - |
      echo "Waiting for Hive Metastore..."
      until nc -z hive-metastore.hive.svc.cluster.local 9083; do
        echo "Metastore not ready, sleeping 5s..."
        sleep 5
      done
      echo "Metastore is ready!"
    resources:
      requests:
        cpu: "50m"
        memory: "64Mi"
      limits:
        cpu: "100m"
        memory: "128Mi"
  
  - name: download-jars
    image: amazon/aws-cli:2.13.0
    command:
    - 'sh'
    - '-c'
    - |
      aws s3 cp s3://my-bucket/jars/ /opt/spark/jars/ --recursive
    volumeMounts:
    - name: spark-jars
      mountPath: /opt/spark/jars
    env:
    - name: AWS_REGION
      value: "us-east-1"
  
  containers:
  - name: spark-kubernetes-driver
    resources:
      requests:
        memory: "2Gi"
        cpu: "1"
      limits:
        memory: "4Gi"
        cpu: "2"
    
    env:
    - name: SPARK_USER
      value: "spark"
    - name: HADOOP_CONF_DIR
      value: "/etc/hadoop/conf"
    - name: HIVE_CONF_DIR
      value: "/etc/hive/conf"
    
    volumeMounts:
    - name: spark-conf
      mountPath: /opt/spark/conf
    - name: hadoop-conf
      mountPath: /etc/hadoop/conf
    - name: hive-conf
      mountPath: /etc/hive/conf
    - name: spark-jars
      mountPath: /opt/spark/user-jars
    - name: tmp-volume
      mountPath: /tmp
  
  volumes:
  - name: spark-conf
    configMap:
      name: spark-config
  - name: hadoop-conf
    configMap:
      name: hadoop-config
  - name: hive-conf
    configMap:
      name: hive-config
  - name: spark-jars
    emptyDir: {}
  - name: tmp-volume
    emptyDir:
      sizeLimit: 10Gi
  
  # Graceful termination
  terminationGracePeriodSeconds: 60

Частая причина таймаутов - nodeSelector или affinity rules, которым не соответствует ни один узел кластера. Проверить доступные узлы с нужными метками:

bash
 
 
# Узлы с конкретной меткой
kubectl get nodes -l node-type=compute

# Все метки на узлах
kubectl get nodes --show-labels

# Taints на узлах
kubectl describe nodes | grep -A5 "Taints:"

Init-контейнеры добавляют ещё один слой сложности. В примере выше контейнер wait-for-metastore ждёт доступности Hive Metastore. Если сервис метастора недоступен или DNS не резолвится, init-контейнер никогда не завершится, и основной контейнер не стартует.

Диагностика init-контейнеров:

bash
 
 
# Статус init-контейнеров
kubectl get pod <driver-pod> -n spark-workloads -o jsonpath='{.status.initContainerStatuses}'

# Логи конкретного init-контейнера
kubectl logs <driver-pod> -n spark-workloads -c wait-for-metastore

# События пода
kubectl describe pod <driver-pod> -n spark-workloads | grep -A20 "Events:"

Container Image: подводные камни Docker-образов

Образ контейнера, заданный в spark.kubernetes.container.image, должен быть доступен всем узлам кластера. Проблемы возникают в нескольких случаях: приватный registry без настроенных secrets, слишком большой образ при медленной сети, отсутствие образа с указанным тегом.

Проверка доступности образа:

bash
 
 
# Проверить imagePullSecrets в namespace
kubectl get secrets -n spark-workloads | grep -i registry

# Проверить, что secret содержит правильные credentials
kubectl get secret registry-secret -n spark-workloads -o jsonpath='{.data.\.dockerconfigjson}' | base64 -d

# Попробовать вручную стянуть образ на узле (требуется доступ к узлу)
crictl pull apache/spark:3.5.0

Если используется приватный registry, убедитесь что ImagePullSecret создан и указан:

yaml
 
 
apiVersion: v1
kind: Secret
metadata:
  name: registry-secret
  namespace: spark-workloads
type: kubernetes.io/dockerconfigjson
data:
  .dockerconfigjson: <base64-encoded-docker-config>

И в конфигурации Spark:

properties
 
 
spark.kubernetes.container.image.pullSecrets=registry-secret

Диагностика проблем с образом:

bash
 
 
# События пода покажут ImagePullBackOff или ErrImagePull
kubectl describe pod <driver-pod> -n spark-workloads | grep -A10 "Events:"

# Статус контейнеров
kubectl get pod <driver-pod> -n spark-workloads -o jsonpath='{.status.containerStatuses[*].state}'

Network Policies: невидимый барьер

В кластерах с включённым сетевым плагином (Calico, Cilium, и др.) Network Policies могут блокировать критически важные соединения. Spark Driver должен общаться с Kubernetes API, Executor'ами и внешними сервисами (S3, Hive Metastore).

Проверка Network Policies в namespace:

bash
 
 
kubectl get networkpolicies -n spark-workloads
kubectl describe networkpolicy <policy-name> -n spark-workloads

Пример Network Policy, разрешающей необходимый трафик для Spark:

yaml
 
 
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: spark-network-policy
  namespace: spark-workloads
spec:
  podSelector:
    matchLabels:
      app: kyuubi-spark
  policyTypes:
  - Ingress
  - Egress
  
  ingress:
  # Разрешить трафик между Driver и Executors
  - from:
    - podSelector:
        matchLabels:
          app: kyuubi-spark
    ports:
    - protocol: TCP
      port: 7078  # Driver port
    - protocol: TCP
      port: 7079  # Block manager
  
  # Разрешить трафик от Kyuubi Server
  - from:
    - namespaceSelector:
        matchLabels:
          name: kyuubi
      podSelector:
        matchLabels:
          app: kyuubi-server
  
  egress:
  # Разрешить DNS
  - to:
    - namespaceSelector: {}
      podSelector:
        matchLabels:
          k8s-app: kube-dns
    ports:
    - protocol: UDP
      port: 53
  
  # Разрешить Kubernetes API
  - to:
    - ipBlock:
        cidr: 10.0.0.1/32  # Kubernetes API server IP
    ports:
    - protocol: TCP
      port: 443
  
  # Разрешить трафик между Spark pods
  - to:
    - podSelector:
        matchLabels:
          app: kyuubi-spark
  
  # Разрешить Hive Metastore
  - to:
    - namespaceSelector:
        matchLabels:
          name: hive
      podSelector:
        matchLabels:
          app: hive-metastore
    ports:
    - protocol: TCP
      port: 9083
  
  # Разрешить S3 (AWS endpoints)
  - to:
    - ipBlock:
        cidr: 0.0.0.0/0
    ports:
    - protocol: TCP
      port: 443

Для диагностики сетевых проблем можно запустить debug-под:

bash
 
 
# Запустить под для тестирования сети
kubectl run nettest --rm -it --image=nicolaka/netshoot -n spark-workloads -- /bin/bash

# Внутри пода проверить connectivity
nc -zv kubernetes.default.svc 443
nc -zv hive-metastore.hive.svc.cluster.local 9083
nslookup kubernetes.default.svc

Практическая диагностика: пошаговый алгоритм

Когда сталкиваюсь с таймаутом запуска Spark Engine, следую проверенному алгоритму диагностики.

Шаг 1: Логи Kyuubi Server

bash
 
 
# Найти под Kyuubi Server
kubectl get pods -n kyuubi -l app=kyuubi-server

# Посмотреть логи с фильтрацией по engine launch
kubectl logs kyuubi-server-0 -n kyuubi | grep -i "engine" -A 10

# Или в реальном времени
kubectl logs -f kyuubi-server-0 -n kyuubi | grep -i "launch\|timeout\|error"

# Полные логи за последний час
kubectl logs kyuubi-server-0 -n kyuubi --since=1h > kyuubi-logs.txt

Шаг 2: Состояние pods в целевом namespace

bash
 
 
# Все поды Spark
kubectl get pods -n spark-workloads -l app=kyuubi-spark --sort-by=.metadata.creationTimestamp

# Поды в проблемном состоянии
kubectl get pods -n spark-workloads --field-selector=status.phase!=Running,status.phase!=Succeeded

# Подробности по конкретному поду
kubectl describe pod <driver-pod-name> -n spark-workloads

Шаг 3: События в namespace

bash
 
 
# Последние события
kubectl get events -n spark-workloads --sort-by=.lastTimestamp | tail -50

# События только для Spark pods
kubectl get events -n spark-workloads --field-selector involvedObject.kind=Pod | grep spark

# Warnings
kubectl get events -n spark-workloads --field-selector type=Warning

Шаг 4: Проверка ресурсов кластера

bash
 
 
# Доступные ресурсы на узлах
kubectl top nodes

# Allocatable vs Capacity
kubectl describe nodes | grep -A10 "Allocatable:"

# Pods на узлах
kubectl get pods -A -o wide | grep <node-name>

Шаг 5: Проверка конфигурации

bash
 
 
# ConfigMaps Kyuubi
kubectl get configmap -n kyuubi
kubectl get configmap kyuubi-defaults -n kyuubi -o yaml

# Secrets
kubectl get secrets -n spark-workloads

# Service Account
kubectl get sa spark-sa -n spark-workloads -o yaml

Для автоматизации диагностики можно создать скрипт:

bash
 
 
#!/bin/bash
# spark-debug.sh - диагностика проблем запуска Spark Engine

NAMESPACE=${1:-spark-workloads}
KYUUBI_NS=${2:-kyuubi}

echo "=== Checking namespace $NAMESPACE ==="

echo -e "\n--- Resource Quotas ---"
kubectl describe quota -n $NAMESPACE

echo -e "\n--- Limit Ranges ---"
kubectl get limitrange -n $NAMESPACE -o yaml

echo -e "\n--- Service Accounts ---"
kubectl get sa -n $NAMESPACE

echo -e "\n--- RBAC Check for spark-sa ---"
kubectl auth can-i --list --as=system:serviceaccount:$NAMESPACE:spark-sa -n $NAMESPACE

echo -e "\n--- Recent Pods ---"
kubectl get pods -n $NAMESPACE --sort-by=.metadata.creationTimestamp | tail -20

echo -e "\n--- Pending Pods ---"
kubectl get pods -n $NAMESPACE --field-selector=status.phase=Pending

echo -e "\n--- Recent Events ---"
kubectl get events -n $NAMESPACE --sort-by=.lastTimestamp | tail -30

echo -e "\n--- Kyuubi Server Logs (last 100 lines) ---"
kubectl logs -n $KYUUBI_NS -l app=kyuubi-server --tail=100 | grep -i "engine\|error\|timeout"

echo -e "\n--- Network Policies ---"
kubectl get networkpolicies -n $NAMESPACE

echo -e "\n--- Nodes with spark-workload label ---"
kubectl get nodes -l spark-workload=true

Оптимизация таймаутов и производительности

Иногда проблема не в конфигурации, а в недостаточном времени ожидания. Вот параметры, которые стоит настроить:

properties
 
 
# Kyuubi timeouts
kyuubi.session.engine.launch.timeout=PT900S
kyuubi.session.engine.initialize.timeout=PT300S
kyuubi.engine.connection.url.use.hostname=false

# Kubernetes submission timeouts
spark.kubernetes.submission.connectionTimeout=120000
spark.kubernetes.submission.requestTimeout=120000
spark.kubernetes.driver.connectionTimeout=120000
spark.kubernetes.driver.requestTimeout=120000

# Allocation tuning
spark.kubernetes.allocation.batch.size=5
spark.kubernetes.allocation.batch.delay=1s

# Executor allocation для быстрого старта
spark.dynamicAllocation.enabled=true
spark.dynamicAllocation.initialExecutors=2
spark.dynamicAllocation.minExecutors=1
spark.dynamicAllocation.maxExecutors=20
spark.dynamicAllocation.executorAllocationRatio=0.5

Для ускорения запуска образов можно использовать image pre-pulling через DaemonSet:

yaml
 
 
apiVersion: apps/v1
kind: DaemonSet
metadata:
  name: spark-image-prepuller
  namespace: spark-workloads
spec:
  selector:
    matchLabels:
      app: spark-prepuller
  template:
    metadata:
      labels:
        app: spark-prepuller
    spec:
      nodeSelector:
        spark-workload: "true"
      initContainers:
      - name: prepull
        image: apache/spark:3.5.0
        command: ["sh", "-c", "echo Image pulled successfully"]
      containers:
      - name: pause
        image: gcr.io/google_containers/pause:3.2
        resources:
          requests:
            cpu: "1m"
            memory: "8Mi"

Мониторинг и алертинг

Для предотвращения проблем настройте мониторинг. Kyuubi экспортирует метрики в формате Prometheus:

yaml
 
 
# ServiceMonitor для Prometheus Operator
apiVersion: monitoring.coreos.com/v1
kind: ServiceMonitor
metadata:
  name: kyuubi-monitor
  namespace: monitoring
spec:
  selector:
    matchLabels:
      app: kyuubi-server
  namespaceSelector:
    matchNames:
    - kyuubi
  endpoints:
  - port: metrics
    interval: 30s
    path: /metrics

Полезные метрики для алертинга:

yaml
 
 
# PrometheusRule для алертов
apiVersion: monitoring.coreos.com/v1
kind: PrometheusRule
metadata:
  name: kyuubi-alerts
  namespace: monitoring
spec:
  groups:
  - name: kyuubi
    rules:
    - alert: KyuubiEngineStartupSlow
      expr: |
        histogram_quantile(0.95, 
          rate(kyuubi_engine_startup_duration_seconds_bucket[5m])
        ) > 300
      for: 5m
      labels:
        severity: warning
      annotations:
        summary: "Spark Engine startup is slow"
        description: "95th percentile of engine startup time is above 5 minutes"
    
    - alert: KyuubiEngineStartupFailures
      expr: |
        rate(kyuubi_engine_startup_failures_total[5m]) > 0.1
      for: 5m
      labels:
        severity: critical
      annotations:
        summary: "Spark Engine startup failures detected"

Заключение

Таймауты запуска Spark Engine в Kyuubi на Kubernetes - это симптом, за которым может скрываться множество причин. Системный подход к диагностике, понимание всех компонентов цепочки запуска и правильная конфигурация превращают эту задачу из непредсказуемой головной боли в управляемый инженерный процесс.

Ключевые моменты для запоминания:

  • Всегда проверяйте Resource Quotas и LimitRanges
  • RBAC должен быть настроен корректно для Service Account
  • NodeSelector и tolerations должны соответствовать реальным узлам
  • Network Policies могут блокировать критически важные соединения
  • Init-контейнеры - частая причина зависания подов
  • Логи и события - ваши лучшие друзья при диагностике

Надеюсь, эти команды, конфигурации и примеры сэкономят вам часы отладки!