Приложение Spark Streaming с RocksDB завершает работу OOKilled на K8S

Вопрос или проблема

Я запускаю приложение Spark Structured Streaming на K8S. Приложение развернуто с помощью spark-submit --master k8s://...

Используя RocksDB state backend, настраиваю spark.kubernetes.memoryOverheadFactor как 2, потому что сам Spark использует настроенную память исполнителя, а RocksDB требует дополнительную память для своей работы.

Когда приложение работает, потребление памяти медленно растет, пока не достигает настроенного лимита, затем остается близко к лимиту и через некоторое время превышает лимит и заканчивается ошибкой OOMKilled.

Потребление памяти отображается на графике.
введите описание изображения здесь
Лимит памяти драйвера составляет 2.5 Гб, и драйвер потребляет около 1.25 Гб

На графике несколько исполнителей, лимит для исполнителя составляет 5 Гб, и исполнители время от времени убиваются. Когда новый исполнитель запускается вместо убитого, он начинает с низкого потребления памяти, но медленно растет до лимита и снова убивается.

Я не могу знать, сколько фактических байтов состояния у приложения, потому что метрика Spark “State bytes” показывает значение, которое значительно больше фактически доступной памяти, но я уверен, что минимально необходимая память для обработки рабочей нагрузки значительно меньше лимита.
Еще одно иллюстрация: приложение с одним исполнителем. Пода исполнителя была убита, перезапущена с состоянием из контрольной точки и продолжает работать, потребляя лишь долю от предыдущей памяти.
перезапуск одного исполнителя

Когда потребление памяти пода близко к настроенному лимиту, оно постепенно растет, а время от времени падает. Увеличение области около лимита:
использование памяти около лимита k8s

Очевидно, что какой-то механизм в Spark или RocksDB “знает” лимит и пытается удержаться в его пределах, но в какой-то момент терпит неудачу.

Соответствующая конфигурация:

Spark 3.5.2 на Kubernetes
Конфигурация ресурсов (для приложения с одним исполнителем)
    --conf spark.kubernetes.memoryOverheadFactor=2 \
    --driver-cores=1 \
    --driver-memory 500m \
    --executor-memory 2g \  # + 4 Гб для RocksDB из-за memoryOverheadFactor
    --executor-cores 1 \
    --conf spark.executor.instances=1
Конфигурация приложения Spark
    "spark.sql.streaming.stateStore.providerClass" ->
      "org.apache.spark.sql.execution.streaming.state.RocksDBStateStoreProvider",
    "spark.sql.streaming.stateStore.rocksdb.changelogCheckpointing.enabled" -> "false"
    "spark.sql.streaming.stateStore.rocksdb.compactOnCommit" -> "true",
    "spark.sql.streaming.stateStore.rocksdb.boundedMemoryUsage" -> "true",
    "spark.sql.streaming.stateStore.rocksdb.blockSizeKB" -> "64",
    "spark.sql.streaming.stateStore.rocksdb.blockCacheSizeMB" -> "0",

Я пытался изменить приведенные выше параметры. executor-memory, executor.instances, compactOnCommit, changelogCheckpointing, boundedMemoryUsage, memoryOverheadFactor влияют на скорость роста памяти и время до сбоя, но основная картина остается прежней.

Вопросы:

  • Как работает этот механизм, контролирующий использование памяти?
  • Какие-то ссылки на документацию или источники по контролю памяти в k8s?
  • Как настроить этот механизм, чтобы сохранить использование памяти на уровне 90% от лимита?

Ответ или решение

Spark Streaming приложение с RocksDB, получает OOMKilled на Kubernetes

Ваша ситуация с приложением Spark Structured Streaming, развернутым на Kubernetes, и подсистемой хранения состояния RocksDB, действительно выглядит довольно сложной. Давайте разберемся с вашими вопросами и проблемами, которые вы наблюдаете.

Как работает механизм контроля использования памяти в Spark и RocksDB?

Когда вы используете RocksDB в качестве бэкенда состояния в Spark Streaming, необходимо учитывать несколько факторов, связанных с управлением памятью:

  1. Общая память – Spark выделяет память для выполнения задач и для хранения состояния. Ваша настройка spark.kubernetes.memoryOverheadFactor указывает, что на каждую гигабайт выделенной памяти Spark необходимо добавить дополнительную память для overhead. Но, как вы заметили, RocksDB также требует дополнительной памяти для своего функционирования, что усложняет ситуацию.

  2. Использование RocksDB – RocksDB использует память по-разному, включая кэш для блоков и для выполнения операций записи и чтения. При настройках, таких как boundedMemoryUsage, вы ограничиваете RocksDB в использовании памяти, устанавливая, таким образом, верхний предел, что может привести к дополнительному давлению на память.

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

Ссылки на документацию и источники по контролю памяти в Kubernetes

Как настроить механизм для поддержания использования памяти на 90% от лимита?

Для того чтобы оптимизировать использование памяти и избежать ситуации OOMKilled, вы можете предпринять следующие шаги:

  1. Пересмотрите конфигурацию памяти:

    • Убедитесь, что spark.executor.memory и spark.driver.memory не слишком малы для задач, которые они выполняют.
    • Рассмотрите возможность уменьшения spark.kubernetes.memoryOverheadFactor от 2 до более низкого значения, если это позволяет.
  2. Настройка параметров RocksDB:

    • Попробуйте отключить boundedMemoryUsage, чтобы дать RocksDB больше свободы в использовании памяти, но с учетом осторожности, так как это может привести к OOM.
    • Увеличьте значение blockCacheSizeMB, чтобы обеспечить RocksDB более эффективный кэш для блоков данных.
  3. Мониторинг и анализ поведения:

    • Активно используйте мониторинг памяти на уровне Kubernetes, чтобы понимать, как именно память используется и где возникают узкие места.
    • Ведите анализ журналов Spark, чтобы выявить, есть ли возможности для оптимизации выполнения задач.
  4. Тестирование и итерация:

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

Заключение

Для успешной работы вашего приложения с RocksDB в Kubernetes необходимо тщательно следить за использованием памяти и процессами ее управления. Убедитесь, что вы используете подходящую конфигурацию, чтобы предотвратить превышение лимитов памяти. Проведение тестирования позволяет найти оптимальные настройки и максимально эффективно использовать ресурсы, доступные вашему приложению.

Оцените материал
Добавить комментарий

Капча загружается...