Устранение неполадок

Раздел по ML-сценариям уточняется. Отдельные шаги и примеры могут быть дополнены. При возникновении вопросов обратитесь к команде платформы «Штурвал».

Эта страница не заменяет установку Kubeflow и GPU Operator и NVIDIA-драйверы. Если ресурс отсутствует или создан с неверными параметрами, вернитесь к соответствующему шагу установки и примените манифест повторно. Проверки после каждого шага приведены на вкладках GUI и CLI страниц установки.

Симптом Раздел
Поды Pending, Insufficient memory, taint на Control Plane Недостаточно Worker-узлов
Поды Kubeflow создаются без istio-proxy Поды Kubeflow создаются без sidecar-контейнера
SSC kubeflow-step-* долго не Healthy Завис один из kubeflow-step
После SSO браузер уходит на чужой хост (flow.…, не kubeflow.<commonName>) Неверный redirect URL
upstream connect error, 503 в UI Kubeflow UI: 503 или upstream connect error
В инкогнито работает, в обычном профиле — нет Браузер показывает старые ошибки
Profile не появляется, нет istio-injection Profile не создаётся или неймспейс без Istio
NCI nvidia-drivers Applied 0/0 Если лейбл не появился
NCI драйверов в ошибке, пакеты ядра не ставятся Версии ядра и пакеты NVIDIA
Notebook с GPU в CrashLoop Notebook с GPU не стартует

В первые минуты после включения сервиса часть подов Kubeflow может перезапускаться, ожидая sidecar-контейнер Istio или MySQL. Оценивайте не общее число перезапусков, а то, переходят ли поды в состояние Running/Ready. Если состояние долго не меняется, проверьте события пода.


С чего начать

Соберите состояние в клиентском кластере. По SSC и NCI видно, на каком шаге установки Kubeflow или GPU остановились.

  1. Откройте клиентский кластер.
  2. Сервисы и репозитории → Установленные сервисы: shturval-policy-manager, kubeflow-step-0 … kubeflow-step-4, а при установленном GPU Operator — ещё gpu-operator. Режим Авто, состояние Healthy. Если сервис завершился с ошибкой, откройте его и проверьте события и поды неймспейса.
  3. Администрирование → Управление узлами: сколько Worker-узлов в Ready, нет ли taint Control Plane на единственных узлах с нагрузкой.
  4. В неймспейсах default, istio-system, kyverno и, при наличии GPU Operator, gpu-operator найдите поды, состояние которых отличается от Running или Completed.
  5. Безопасность → Менеджер политик безопасности: ClusterPolicy enforce-istio-injection-kubeflow существует и не находится в состоянии ошибки.
  6. Если ставили NVIDIA: Администрирование → Конфигурация узлов: NCI nvidia-drivers, nvidia-runtime-minimal, nvidia-fabricmanager.
kubectl get nodes -o wide
kubectl get pods -A | grep -v "Running\|Completed"
kubectl get events -A --sort-by=.lastTimestamp
kubectl get ssc
kubectl get clusterpolicy enforce-istio-injection-kubeflow
kubectl get nci

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


Недостаточно Worker-узлов

Istio и цепочка kubeflow-step-* ставят много подов. GPU Operator — отдельно и только если ставили NVIDIA. Control Plane обычно с taint: обычные поды туда не планируются. На одном Worker Kubeflow с Istio часто не помещается: планировщик пишет Insufficient memory, SSC шага не становится Healthy.

Для установки Kubeflow в клиентском кластере нужно не меньше трёх Worker-узлов (узлы Control Plane в это число не входят). Если на Worker-узлах уже работают другие сервисы, может потребоваться больше трёх узлов.

Типичное событие пода:

0/N nodes are available: X Insufficient memory, Y node(s) had untolerated taint(s)
  1. Администрирование → Управление узлами: отделите Control Plane и Worker. У Worker статус Ready, без taint control-plane / master.
  2. Откройте Pending-под (часто istiod в istio-system, metadata-writer, workflow-controller, katib-mysql в default) → события. Ищите FailedScheduling и Insufficient memory.
  3. Если Worker меньше трёх — добавьте узлы: масштабирование групп узлов. Для Shturval v2 — добавление хостов.
  4. Дождитесь Ready у нового Worker и Running у агента Cilium на нём. Пока Cilium не Ready, webhook Istio может отвечать no route to host.
  5. Если после добавления узлов поды по-прежнему не переходят в состояние Running/Ready, удалите зависшие поды: контроллер создаст их заново.
kubectl get nodes -L node-role.kubernetes.io/control-plane
kubectl describe node <worker>
kubectl describe pod -n <неймспейс> <под> | grep -A 20 Events

Добавьте Worker штатным способом платформы, затем:

kubectl get pods -A -o wide --field-selector spec.nodeName=<новый-worker>
kubectl get pods -n istio-system
kubectl delete pod -n default <зависший-под>

Переустанавливать kubeflow-step-* из‑за нехватки узлов не нужно: сначала дайте планировщику куда поставить поды. Цепочка шагов: шаг 2 установки Kubeflow.


Завис один из kubeflow-step

Kubeflow устанавливается с помощью SSC kubeflow-step-0 … kubeflow-step-4 в режиме Авто (шаг 2.2). Если один из SSC долго не переходит в состояние Healthy, диагностируйте этот шаг; уже работающие SSC не пересоздавайте.

Сначала исключите нехватку Worker. Затем смотрите, чего ждёт под: образ, том, sidecar Istio, зависимость SSC.

  1. Установленные сервисы → зависший kubeflow-step-* → Управлять: режим, состояние, события.
  2. Неймспейс сервиса (default) → поды не Ready. Откройте под: события, контейнер istio-proxy, если он есть.
  3. Если sidecar Istio в Incomplete, проверьте SSP Cilium шаг 2.1: у shturval-networking должны быть cni.exclusive: false и socketLB.hostNamespaceOnly: true.
  4. Хранилище / PVC в default: том прошлой установки в Terminating или Bound к удалённому поду. Перед удалением проверьте связанный PV, его reclaimPolicy, владельца PVC и наличие резервной копии. Удаляйте только подтверждённый остаточный PVC, данные которого больше не нужны. Затем верните SSC в режим Авто.
kubectl get ssc kubeflow-step-0 kubeflow-step-1 kubeflow-step-2 kubeflow-step-3 kubeflow-step-4
kubectl describe ssc <kubeflow-step-name>
kubectl get pods,pvc -n default
kubectl get events -n default --sort-by=.lastTimestamp
kubectl describe pod -n default <под>

SSP Cilium: kubectl get shturvalservicepatch shturval-networking-for-istio.

Остаточный PVC:

kubectl get pvc -n default
kubectl describe pvc <имя> -n default
# Выполните только для подтверждённого остаточного PVC после резервного копирования данных.
kubectl delete pvc <имя> -n default

Если режим SSC не Авто, выставьте auto и снова смотрите get ssc / поды:

kubectl patch ssc <kubeflow-step-name> --type=merge -p '{"spec":{"mode":"auto"}}'
kubectl get ssc <kubeflow-step-name>

Неверный redirect URL

После SSO браузер должен вернуться на https://kubeflow.<commonName>/oauth2/callback, затем открыть UI Kubeflow. <commonName> берите у SSC shturval-ingress-controller в клиентском кластере — как на установке.

Если callback уходит на другой хост (часто дефолт чарта oauth2-proxy вроде https://flow.<…>/oauth2/callback или хост кластера управления), клиент OIDC тут ни при чём: AuthOidcClient на шаге 1 задаёт issuer SSO, а куда вернуться после логина задаёт oauth2-proxy.oauth2Cfg.redirect_url у kubeflow-step-1.

Issuer вида https://shturval.alpha.<хост-управления> — нормально. Ненормально, если после логина Location ведёт не на kubeflow.<commonName>.

  1. Клиентский кластер → Установленные сервисы → shturval-ingress-controller → Управлять → Манифест: сохраните controller.shturval.commonName.
  2. Установленные сервисы → kubeflow-step-2: в спецификации Ingress хост kubeflow.<commonName>, не flow. и не commonName управления.
  3. Установленные сервисы → kubeflow-step-1 → Управлять → Спецификация сервиса. Должно быть:
oauth2-proxy:
  oauth2Cfg:
    oidc_issuer_url: "<OIDC_ISSUER_URL>"
    redirect_url: https://kubeflow.<commonName>/oauth2/callback

Значение redirect_url возьмите с шага 2.2. Сохраните спецификацию. 4. Неймспейс oauth2-proxy: удалите под oauth2-proxy, чтобы он поднялся уже с новым URL. Править только ConfigMap в этом неймспейсе недостаточно: Helm-reconcile SSC перезапишет его.

kubectl get ssc shturval-ingress-controller \
  -o jsonpath='{.status.appliedCustomValues.controller.shturval.commonName}{"\n"}'

kubectl get ssc kubeflow-step-1 -o yaml | grep -A 20 'oauth2-proxy:'
kubectl get ingress -A | grep -i kubeflow

В customvalues kubeflow-step-1 выставьте oauth2-proxy.oauth2Cfg.redirect_url: https://kubeflow.<commonName>/oauth2/callback (тот же фрагмент, что на шаге 2.2), сохраните SSC и перезапустите под:

kubectl delete pod -n oauth2-proxy -l app.kubernetes.io/name=oauth2-proxy

Если лейбла нет, найдите под: kubectl get pods -n oauth2-proxy.

После исправления откройте https://kubeflow.<commonName>/ в инкогнито: иначе браузер может отдать старый Location из кэша.


Kubeflow UI: 503 или upstream connect error

В первые минуты после установки ошибка может возникать, пока компоненты Kubeflow и маршруты Istio не перешли в состояние готовности. Если ошибка сохраняется, проверьте Ingress, istio-ingressgateway и sidecar-контейнер целевого пода. Хост UI задаётся на шаге 2.2.

  1. Неймспейс istio-system: под istio-ingressgateway Running / Ready.
  2. Неймспейс default: поды Central Dashboard / jupyter-web-app Ready, у контейнеров есть istio-proxy.
  3. Если sidecar нет — лейбл Istio.
  4. Откройте UI в инкогнито. Если только там работает — кэш браузера.
kubectl get pods -n istio-system -l app=istio-ingressgateway
kubectl get pods -n default -l app=jupyter-web-app
kubectl get pods -n default -l app=jupyter-web-app -o yaml \
  | grep -A 3 "name: istio-proxy"

Прирост cx_connect_fail (не разовое значение) смотрите дважды с паузой:

GW=$(kubectl get pods -n istio-system -l app=istio-ingressgateway \
  -o jsonpath='{.items[0].metadata.name}')
kubectl exec -n istio-system "${GW}" -c istio-proxy -- \
  curl -s localhost:15000/clusters 2>&1 | grep "jupyter-web-app" | grep cx_connect_fail

Браузер показывает старые ошибки

Показательный тест: в инкогнито UI открывается, в обычном профиле — 503, 405 или старый редирект.

На странице https://kubeflow.<commonName>/:

  1. F12 → Application → Storage → Clear site data (cookies, cache, Service workers, Local storage).
  2. Закройте DevTools, жёсткое обновление.

Удаления только cookies недостаточно: Central Dashboard держит состояние в Service Worker, HTTP cache, IndexedDB и LocalStorage.


Profile не создаётся или неймспейс без Istio

Порядок создания — страница Профили Kubeflow: достаточно создать ресурс Profile, неймспейс появится сам. spec.owner.name — логин пользователя платформы (у служебного профиля это admin), не почта.

Лейбл istio-injection=enabled на неймспейсе профиля добавляет profile-controller по ConfigMap namespace-labels-data-* в неймспейсе установки Kubeflow (default). В значении ключа namespace-labels.yaml должна быть строка istio-injection: "enabled".

  1. Администрирование → Кастомные ресурсы → kubeflow.org → Profile: объект есть, в спецификации owner.
  2. Неймспейс с тем же именем: аннотация owner, лейбл istio-injection=enabled.
  3. Если лейбла нет, откройте ConfigMap namespace-labels-data-* в неймспейсе default и проверьте значение ключа namespace-labels.yaml.
  4. Если строки istio-injection: "enabled" нет, добавьте её без дублирования. После исправления создаваемые неймспейсы Profile будут получать лейбл автоматически.
  5. Для уже созданного неймспейса добавьте лейбл istio-injection=enabled вручную и пересоздайте только поды без sidecar-контейнера. Не удаляйте неймспейс профиля без резервного копирования данных.
export PROFILE_NAME=<profile-name>

kubectl get namespace "${PROFILE_NAME}" -o yaml
kubectl get profile.kubeflow.org "${PROFILE_NAME}" -o yaml
kubectl get namespace "${PROFILE_NAME}" -o jsonpath='{.metadata.labels.istio-injection}{"\n"}'
kubectl get configmap -n default -o name | grep '^configmap/namespace-labels-data-'
kubectl get events -A --sort-by=.lastTimestamp | grep "${PROFILE_NAME}"

Проверьте содержимое найденной ConfigMap:

kubectl get -n default configmap namespace-labels-data-<случайный-суффикс> \
  -o jsonpath='{.data.namespace-labels\.yaml}' \
  | grep '^istio-injection:'

Если команда ничего не вывела, отредактируйте ConfigMap и добавьте в data.namespace-labels.yaml строку istio-injection: "enabled". Для уже созданного неймспейса примените лейбл отдельно:

kubectl label namespace "${PROFILE_NAME}" istio-injection=enabled --overwrite

Поды Kubeflow создаются без sidecar-контейнера

Неймспейс default должен иметь лейбл istio-injection=enabled. Чарт установки проставляет его, а ClusterPolicy enforce-istio-injection-kubeflow возвращает лейбл, если контроллер платформы снимает его при обработке системных лейблов. Если поды создаются без istio-proxy, проверьте менеджер политик, ClusterPolicy и сам лейбл.

  1. Сервисы и репозитории → Установленные сервисы → shturval-policy-manager: режим Авто, состояние Healthy.
  2. Неймспейс kyverno: поды контроллеров находятся в состоянии Running.
  3. Безопасность → Менеджер политик безопасности → enforce-istio-injection-kubeflow: политика существует и не находится в состоянии ошибки.
  4. Неймспейс default → лейблы: istio-injection=enabled.
  5. Поды Kubeflow в default: контейнер istio-proxy. Если его нет после появления лейбла, пересоздайте только такие поды — контроллер создаст их заново уже с sidecar-контейнером.
kubectl get ssc shturval-policy-manager
kubectl get pods -n kyverno
kubectl get clusterpolicy enforce-istio-injection-kubeflow
kubectl get namespace default -o jsonpath='{.metadata.labels.istio-injection}{"\n"}'

Для shturval-policy-manager ожидается состояние Healthy, для подов Kyverno — Running, для лейбла неймспейса — enabled. Если SSP kyverno-enable или ClusterPolicy отсутствует, примените их по инструкции установки Kubeflow.

Если политика есть, но лейбла нет, добавьте его вручную:

kubectl label namespace default istio-injection=enabled --overwrite

Затем пересоздайте поды без sidecar.


На GPU-узле не появился лейбл nvidia.com/gpu.present

GPU Operator (шаг 1) выставляет nvidia.com/gpu.present=true, когда NFD видит PCI NVIDIA. Без лейбла NCI со шага 2 не находят узлы: у nvidia-drivers будет Applied 0/0. Это не «сломанный NCI». Как селекторы действуют дальше: GPU Operator и NVIDIA-драйверы.

  1. Установленные сервисы → gpu-operator: Авто, Healthy. Поды в неймспейсе gpu-operator Running.
  2. Управление узлами → узел с GPU → лейблы: nvidia.com/gpu.present=true.
  3. Конфигурация узлов → nvidia-drivers: Applied больше 0 на GPU-узлах.
  4. Если лейбла нет, проверьте состояние, события и логи подов gpu-operator, а также доступность устройства NVIDIA на узле.

Добавляйте лейбл вручную только для диагностики, но не в prod эксплуатации. После проверки снова ориентируйтесь на GPU Operator и манифесты шага 2.

kubectl get nodes -L nvidia.com/gpu.present
kubectl get nci nvidia-drivers
kubectl get pods -n gpu-operator
kubectl get events -n gpu-operator --sort-by=.lastTimestamp

Временно:

kubectl label node <имя-узла> nvidia.com/gpu.present=true --overwrite

Версии ядра и пакеты NVIDIA

NCI nvidia-drivers ставит kernel-lt-devel и kernel-lt-headers одной версии — той, что указана в NCI на странице GPU Operator и NVIDIA-драйверы. Пример в манифесте: 5.15.178-1.el8 (без суффикса .x86_64). Возьмите версию со страницы узла, блок Описание, не вписывайте произвольную строку. На всех GPU-узлах ядро должно совпадать. Иначе пакет не подберётся, драйвер не соберётся, Applied уйдёт в ошибку.

Версия пакетов драйвера (NVIDIA_DRIVER_VERSION) должна быть в зеркале shturval_rpm_nvidia. Список версий — таблица параметров на странице GPU, не повторяйте curl к репозиторию отсюда.

  1. Управление узлами → каждый узел → блок Описание: версия ядра. Все GPU-узлы — одна строка.
  2. Конфигурация узлов → nvidia-drivers → пакеты: kernel-lt-devel / kernel-lt-headers той же версии, что в Описании.
  3. Если ядра разные — сначала выровняйте версию ядра на узлах, затем исправьте версии пакетов в NCI (манифест шага 2). Не меняйте версию только на одном узле, если общий NCI применяется ко всей группе.
kubectl get nodes \
  -o jsonpath='{.items[*].status.nodeInfo.kernelVersion}' | tr ' ' '\n' | sort -u
kubectl get nci nvidia-drivers -o yaml | grep -A2 'kernel-lt'

Команда по ядрам должна вывести одну строку. Сверьте её с version у пакетов в NCI.


Notebook с GPU не стартует

Проверьте, что узел с лейблом GPU есть, NCI runtime применился и в событиях пода нет FailedScheduling. Сопровождение узлов: GPU Operator и NVIDIA-драйверы.

  1. Неймспейс профиля → под notebook: события и логи. Ищите нехватку nvidia.com/gpu, runtime, hook disable-device-node-modification.
  2. Управление узлами / Конфигурация узлов: лейбл GPU и NCI runtime, как в разделах выше.
  3. Правки CDI hook и toolkit — только в окно обслуживания, не на рабочем узле с нагрузкой.
kubectl describe pod -n <неймспейс-профиля> <notebook-pod>
kubectl logs -n <неймспейс-профиля> <notebook-pod> --all-containers --tail=100
kubectl get events -n <неймспейс-профиля> --sort-by=.lastTimestamp
kubectl get nci
kubectl get nodes -L nvidia.com/gpu.present

Полное удаление инсталляции вручную

Удаляйте в обратном порядке: профили, опциональный KServe снаружи, Kubeflow, связанные с ним политики, затем GPU Operator и NCI, если ставили. Манифесты заново — со страниц установки Kubeflow и GPU.

  1. Проверьте PVC/PV и сохраните необходимые данные, затем удалите только рабочие Profile: Профили Kubeflow. Служебный профиль kflow-user-admin-namespace не удаляйте вручную.
  2. Если настраивали внешний KServe — объекты из Внешний доступ к KServe: Ingress, AuthorizationPolicy, SSP knative-for-kserve-external и kserve-path-template, ConfigMap registry-root-ca в knative-serving, тестовый InferenceService.
  3. Установленные сервисы: kubeflow-step-4 … kubeflow-step-0, затем gpu-operator (если ставили) — режим Выкл, дождитесь снятия релиза, удалите экземпляр.
  4. Менеджер политик безопасности: удалите ClusterPolicy enforce-istio-injection-kubeflow.
  5. Удалите SSP kyverno-enable и shturval-networking-for-istio через вкладку SSP соответствующего сервиса или через список кастомных ресурсов ShturvalServicePatch.
  6. Если ставили NVIDIA: Конфигурация узлов — NCI nvidia-drivers, nvidia-runtime-minimal, nvidia-fabricmanager и правило NFD nvidia-nvswitch.
  7. Проверьте, что неймспейсы oauth2-proxy и PVC в default не остались «висеть».

Получите список профилей, проверьте связанные PVC/PV и удалите только рабочие профили. Служебный профиль kflow-user-admin-namespace не удаляйте:

kubectl get profile.kubeflow.org
kubectl get pvc -A
kubectl get pv
kubectl delete profile.kubeflow.org <имя-рабочего-профиля>

Если настраивали внешний доступ к KServe:

kubectl delete inferenceservice sklearn-iris -n <неймспейс-профиля> --ignore-not-found
kubectl delete -n istio-system ingress kserve-external --ignore-not-found
kubectl delete -n istio-system authorizationpolicy kserve-inference-access --ignore-not-found
kubectl delete shturvalservicepatch knative-for-kserve-external kserve-path-template --ignore-not-found
kubectl delete configmap registry-root-ca -n knative-serving --ignore-not-found

Удалите политику и SSP, добавленные для Kubeflow:

kubectl delete clusterpolicy enforce-istio-injection-kubeflow --ignore-not-found
kubectl delete shturvalservicepatch kyverno-enable --ignore-not-found
for ssc in kubeflow-step-4 kubeflow-step-3 kubeflow-step-2 kubeflow-step-1 kubeflow-step-0 gpu-operator; do
  kubectl patch ssc "${ssc}" --type=merge -p '{"spec":{"mode":"absent"}}' || true
done

Дождитесь снятия каждого Helm-релиза и проверьте состояние SSC. Только после этого удалите объекты:

kubectl get ssc kubeflow-step-4 kubeflow-step-3 kubeflow-step-2 kubeflow-step-1 kubeflow-step-0 gpu-operator

for ssc in kubeflow-step-4 kubeflow-step-3 kubeflow-step-2 kubeflow-step-1 kubeflow-step-0 gpu-operator; do
  kubectl delete ssc "${ssc}" --ignore-not-found
done

kubectl delete shturvalservicepatch shturval-networking-for-istio --ignore-not-found
kubectl delete nci nvidia-drivers nvidia-runtime-minimal nvidia-fabricmanager --ignore-not-found
kubectl delete nodefeaturerule nvidia-nvswitch --ignore-not-found

kubectl get ns default kubeflow-system oauth2-proxy knative-serving knative-eventing
kubectl get pvc -A
kubectl get pv

Диагностический файл для поддержки

Снимите состояние: список Установленных сервисов (имена, режим, Healthy), страница зависшего пода (события), Управление узлами (число Worker, Ready), Конфигурация узлов (Applied у NCI). Приложите URL, на который ушёл браузер после SSO, и ожидаемый https://kubeflow.<commonName>/.
{
  echo "=== nodes ==="; kubectl get nodes -o wide
  echo "=== pods ==="; kubectl get pods -A
  echo "=== events ==="; kubectl get events -A --sort-by='.lastTimestamp' | tail -50
  echo "=== ssc ==="; kubectl get ssc
  echo "=== nci ==="; kubectl get nci
} > /tmp/kubeflow-diag.txt