Open WebUI в Managed Kubernetes Яндекс Cloud

Установим Open WebUI в готовый кластер Яндекс Cloud через Helm: отдельный kubeconfig, PVC, личный доступ, проверка данных и облачные расходы.

После урока вы сможете

  • Проверить доступ к готовому кластеру через отдельный kubeconfig и различать IAM и RBAC
  • Подготовить одиночный Helm-релиз с постоянным диском yc-network-ssd
  • Открыть личный интерфейс и проверить вход после штатной замены Pod с прежним PVC/PV
  • Отдельно проверить внешний API модели и определить сохраняющиеся облачные расходы

Проверяемый результат: Читатель умеет установить новый частный Open WebUI в разрешённом готовом кластере, проверить вход с прежним томом после замены Pod и определить статус внешнего API и оплачиваемых ресурсов. Фактическое выполнение требует отдельной проверки в своём облаке.

У вас уже есть работающий кластер Managed Service for Kubernetes в Яндекс Cloud и разрешение развернуть в нём отдельное приложение. Перенесём знакомую конфигурацию из материала «Open WebUI в Kubernetes через Helm» в облачную среду. Подключимся через изолированный файл kubeconfig, установим новый частный экземпляр Open WebUI чарта 17.0.0 с приложением v0.12.0 и откроем веб-интерфейс в браузере на своём компьютере по адресу http://127.0.0.1:13000.

После запуска создадим первого администратора и проверим сохранение данных на облачном диске после замены Pod. Отдельно определим статус подключения внешнего API языковой модели и разберём, какие ресурсы продолжают расходовать бюджет после закрытия локального туннеля.

Этот маршрут опирается на документацию Яндекс Cloud и проверенный офлайн-рендер шаблонов в Helm v3.14.2+gc309b6f. При подготовке урока реальный облачный кластер, выделение дисков CSI, скачивание образов и вызовы внешних моделей не запускались. Поэтому все состояния живой инфраструктуры вам предстоит последовательно подтвердить в своей среде.

Проверим, подходит ли нам этот маршрут

Создавать кластер специально ради одного интерфейса не нужно. Мы устанавливаем один новый частный экземпляр Open WebUI в уже работающий разрешённый кластер Яндекс Cloud. Предполагается, что вы умеете работать с kubectl, Helm, namespace, PersistentVolumeClaim (PVC) и Service, а особенности облачных прав, сети и дисков мы разберём по ходу действий. Если же готового кластера у вас нет, создавать его ради одного личного веб-интерфейса избыточно: проще выбрать маршрут из уроков «Выбираем схему установки» и «Open WebUI на виртуальной машине Яндекс Cloud».

Мы выполняем чистую установку в отдельном пространстве имён open-webui-yc-course с новым релизом open-webui-yc-course. Данные с домашнего компьютера, виртуальной машины или прежних релизов сюда автоматически не переносятся. Публиковать сервис через публичный Ingress или облачный LoadBalancer мы не будем. Локальный сервер моделей Ollama и видеокарты (GPU) тоже не понадобятся, поскольку генерацию текста обеспечит внешний серверный API.

Прежде чем вводить команды, проверьте вместе с оператором кластера стартовые условия:

  1. В целевом каталоге Яндекс Cloud работает мастер кластера и есть минимум один рабочий узел (worker node) в состоянии Ready с запасом доступных (allocatable) ресурсов процессора и памяти.
  2. С оператором согласованы свободные имена пространства имён open-webui-yc-course и релиза open-webui-yc-course, а также ваши права в облаке и внутри Kubernetes.
  3. Согласно правилам управления доступом Managed Service for Kubernetes, у самого кластера и группы узлов уже настроены штатные сервисные аккаунты облака. Сервисный аккаунт кластера управляет узлами, подсетями и дисками, а сервисный аккаунт узлов отвечает за доступ к реестрам Яндекса. В этом уроке мы эти аккаунты не создаём и роли им не назначаем.
  4. В кластере работает драйвер CSI, доступен класс хранилища yc-network-ssd и есть квота на SSD-диски в той зоне доступности, где работает ваш узел.
  5. У рабочих узлов настроен исходящий доступ в интернет и работает DNS для скачивания образов и обращения к внешнему API модели.
  6. Оператор подготовил или готов создать в целевом пространстве имён стабильный секрет open-webui-auth с ключом подписи сессий WEBUI_SECRET_KEY.
  7. Подтверждён бюджет на оплату ресурсов по правилам тарификации Managed Service for Kubernetes и выбранного поставщика API модели.

Чтобы не смешивать разные уровни инфраструктуры, разделим движение сетевых запросов и хранение данных на три независимых пути:

Путь в системеКак проходит соединение или записьЧто требуется от среды
Доступ пользователя к веб-интерфейсуБраузер (127.0.0.1:13000) → локальный kubectl port-forward → достижимый API кластера → выбранный Pod (порт 8080). В команде по имени Service его порт 80 указывает на targetPort приложения; Service используется для выбора Pod, а не как промежуточный адрес передачи данныхКомпьютер с браузером имеет сетевой доступ к выбранному API endpoint кластера и право pods/portforward
Сохранение базы данных и настроекPod (/app/backend/data) → PVC → PersistentVolume (PV) → зональный блочный диск Compute Cloud (yc-network-ssd); плюс чтение WEBUI_SECRET_KEY из SecretРабочий драйвер CSI, квота на диск и доступный узел в той же зоне доступности
Запросы к внешней языковой моделиPod на рабочем узле → DNS и исходящий HTTPS → внешний серверный API провайдера моделиПубличный IP на узле или настроенный оператором NAT в подсети узлов

У этой схемы есть ограничения, которые нужно учесть до установки:

  • По официальному руководству Kubernetes для Open WebUI, конфигурация с одной репликой (replicaCount: 1), одним рабочим процессом (UVICORN_WORKERS=1) и встроенной базой SQLite на блочном диске подходит для одиночного экземпляра, но не обеспечивает высокую доступность (HA). Даже если мастер вашего кластера региональный (распределён по трём зонам), один Pod на одном рабочем узле с зональным диском временно остановится при перезапуске контейнера или выходе узла из строя.
  • Заданные в примере ресурсы Pod (requests: 500m CPU, 1Gi RAM и limits: 2 CPU, 4Gi RAM) взяты из документации Open WebUI как отправная точка. Они не гарантируют скорость отклика и не равны размеру виртуальной машины рабочего узла: на самом узле часть ресурсов всегда занята системными компонентами Kubernetes (см. также общие ориентиры в уроке «Требования к Open WebUI»).
  • Приведённый в уроке файл настроек проверен офлайн командами helm lint и helm template в Helm v3.14.2 с параметром рендера Capabilities.KubeVersion = 1.34.0. Это значение описывает только локальную среду шаблонизатора и не является проверенной версией сервера Яндекс Cloud или минимальным требованием совместимости.

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

Подключимся через отдельный kubeconfig

Чтобы команды установки не попали в другой кластер из вашего списка контекстов, создадим на компьютере отдельную рабочую папку и запишем данные подключения в новый изолированный файл kubeconfig-yc-course.yaml. Во всех кластерных командах kubectl и Helm мы будем явно передавать путь к этому файлу и точное имя контекста. Основной файл ~/.kube/config при этом останется нетронутым.

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

yc version
kubectl version --client
helm version --short

Утилита yc должна быть заранее инициализирована и привязана к вашему рабочему профилю Яндекс Cloud. Как поясняют обзор подключения к кластеру и справочник команды get-credentials, подключение kubectl опирается на аутентификацию Yandex IAM и назначенные роли. Мы не будем вручную копировать IAM-токены в файлы настроек или выводить на экран сырое содержимое конфигурации (kubectl config view --raw или yc config list), чтобы не раскрыть учётные данные в терминале.

В Яндекс Cloud права управления облачными ресурсами и права внутри самого Kubernetes разделены. Согласно документации по управлению доступом, сервисная роль k8s.viewer позволяет прочитать метаданные кластера в облаке и сформировать kubeconfig, но сама по себе не даёт прав создавать объекты внутри Kubernetes. Для работы через Kubernetes API назначаются либо облачные роли (k8s.cluster-api.editor даёт права редактирования во всех пространствах имён, а k8s.cluster-api.cluster-admin соответствует полным правам cluster-admin), либо более узкие привязки ролей напрямую через Kubernetes RBAC в конкретном namespace. Выдавать себе полный административный доступ на весь кластер ради этого урока не нужно: операции уровня кластера может выполнить или подтвердить оператор.

Создайте новую пустую папку на своём компьютере, перейдите в неё и задайте переменные с путём к новому файлу kubeconfig, идентификатором согласованного каталога (FOLDER_ID), идентификатором целевого кластера (CLUSTER_ID) и именем контекста (CTX). Перед запросом учётных данных убедитесь, что файла kubeconfig-yc-course.yaml в этой папке ещё нет:

COURSE_KUBECONFIG="$PWD/kubeconfig-yc-course.yaml"
FOLDER_ID="REPLACE_WITH_APPROVED_FOLDER_ID"
CLUSTER_ID="REPLACE_WITH_APPROVED_CLUSTER_ID"
CTX="open-webui-yc-course"
yc managed-kubernetes cluster list --folder-id "$FOLDER_ID"
yc managed-kubernetes cluster get --id "$CLUSTER_ID"

Команды cluster list и cluster get должны вернуть метаданные вашего согласованного кластера в статусе RUNNING. Обратите внимание на блок сетевых адресов API в выводе cluster get.

Как отмечено в правилах подключения и описании сети кластера, флаги --external и --internal в команде get-credentials лишь выбирают, какой адрес API записать в kubeconfig, но сами по себе не прокладывают сетевой маршрут и не открывают файрвол. По документации групп безопасности, доступ к портам API (443 и 6443) ограничивается разрешёнными диапазонами IP-адресов (CIDR).

Выберите только одну из двух веток ниже в зависимости от того, как устроен доступ к API вашего кластера с компьютера, на котором открыт браузер.

Основная ветка: у кластера есть внешний адрес API (--external), открытый для вашего IP-адреса. Запишем конфигурацию в новый файл (без флага --force, чтобы случайно не перезаписать существующие данные) и проверим связь с API:

yc managed-kubernetes cluster get-credentials --id "$CLUSTER_ID" --external --context-name "$CTX" --kubeconfig "$COURSE_KUBECONFIG"
kubectl --kubeconfig "$COURSE_KUBECONFIG" --context "$CTX" cluster-info

Альтернативная ветка: у кластера есть только внутренний адрес API (--internal). Выполняйте этот вариант вместо предыдущего только в том случае, если на вашем компьютере с браузером уже работает согласованный корпоративный VPN или маршрутизируемое соединение с облачной сетью Яндекса. Учтите, что запуск kubectl port-forward на удалённом бастион-хосте откроет порт 127.0.0.1 самого бастиона, а не вашего ноутбука. Если прямой маршрут с компьютера с браузером до внутреннего API уже есть, выполните:

yc managed-kubernetes cluster get-credentials --id "$CLUSTER_ID" --internal --context-name "$CTX" --kubeconfig "$COURSE_KUBECONFIG"
kubectl --kubeconfig "$COURSE_KUBECONFIG" --context "$CTX" cluster-info

Если cluster-info возвращает адрес Kubernetes control plane, связь и базовая аутентификация работают. Если команда завершается по тайм-ауту, возвращает Forbidden или показывает чужой кластер, остановитесь и уточните у оператора идентификатор кластера, ваш внешний IP в группе безопасности и назначенные роли.

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

Проверим права, сеть и хранилище

Даже в исправном кластере Pod с Open WebUI может оставаться в статусе Pending или завершаться с ошибкой скачивания образа. YAML-манифест приложения сам по себе не создаёт сетевой маршрут в интернет, не выдаёт облачные квоты и не гарантирует наличие рабочего узла в нужной зоне. Проверим эти зависимости до установки.

В том же терминале зададим переменные с согласованным именем пространства имён (NS), именем будущего релиза (RELEASE) и селектором меток (SELECTOR), а затем выполним предпусковую проверку узлов, класса хранилища, свободных имён и наших прав RBAC:

NS="open-webui-yc-course"
RELEASE="open-webui-yc-course"
SELECTOR="app.kubernetes.io/instance=$RELEASE,app.kubernetes.io/component=open-webui"
kubectl --kubeconfig "$COURSE_KUBECONFIG" --context "$CTX" get nodes -o wide
kubectl --kubeconfig "$COURSE_KUBECONFIG" --context "$CTX" get storageclass yc-network-ssd -o yaml
kubectl --kubeconfig "$COURSE_KUBECONFIG" --context "$CTX" get namespace "$NS"
helm list --kube-context "$CTX" -n "$NS" --all --kubeconfig "$COURSE_KUBECONFIG"
kubectl --kubeconfig "$COURSE_KUBECONFIG" --context "$CTX" auth can-i create namespaces
kubectl --kubeconfig "$COURSE_KUBECONFIG" --context "$CTX" auth can-i create deployments.apps -n "$NS"
kubectl --kubeconfig "$COURSE_KUBECONFIG" --context "$CTX" auth can-i create persistentvolumeclaims -n "$NS"
kubectl --kubeconfig "$COURSE_KUBECONFIG" --context "$CTX" auth can-i create secrets -n "$NS"
kubectl --kubeconfig "$COURSE_KUBECONFIG" --context "$CTX" auth can-i create pods/portforward -n "$NS"

Разберём, что означают результаты этих команд и что делать, если ваших прав хватает не на все проверки:

  • Узлы и класс хранилища (get nodes, get storageclass). Чтение узлов и StorageClass требует прав на уровне всего кластера. Если у вас настроены права только внутри одного namespace и команды вернули отказ в доступе, попросите оператора подтвердить наличие Ready-узла с достаточным запасом CPU/RAM и присутствие класса yc-network-ssd. Если же вывод открылся, проверьте в описании yc-network-ssd три ключевых поля из документации по классам хранилищ Яндекс Cloud:
    • provisioner: disk-csi-driver.mks.ycloud.io указывает, что класс использует драйвер дисков Compute Cloud. Это поле не доказывает исправность CSI: её подтверждает оператор, а результат создания и подключения диска мы проверим по состоянию и событиям PVC и Pod;
    • volumeBindingMode: WaitForFirstConsumer означает, что облачный диск не будет создан в момент появления PVC, а подождёт, пока планировщик выберет конкретный узел для вашего Pod;
    • reclaimPolicy: Delete предупреждает, что при удалении объекта PVC из Kubernetes связанный с ним PV и сам облачный SSD-диск будут автоматически удалены.
  • Свободные имена (get namespace, helm list). Для совершенно новой установки команда get namespace "$NS" обычно возвращает NotFound, а список helm list должен быть пустым. Если пространство имён уже существует, потому что оператор заранее подготовил его специально для вас вместе со стабильным секретом, убедитесь, что helm list в нём пуст и внутри нет чужих рабочих объектов. Если же вы обнаружили незнакомый существующий релиз, остановитесь и согласуйте с оператором другое имя, чтобы не перезаписать чужие данные.
  • Проверка разрешений (auth can-i). Создание пространства имён (create namespaces) — операция уровня кластера. Если она вернула no, пространство имён перед установкой должен создать оператор. Остальные четыре проверки (deployments.apps, persistentvolumeclaims, secrets и pods/portforward внутри -n "$NS") должны вернуть yes. Право create secrets нужно самому Helm для сохранения служебных метаданных релиза, даже если секрет приложения open-webui-auth создаёт оператор. Приведённый набор auth can-i проверяет только ключевые действия нашего урока и не заменяет полную корпоративную матрицу RBAC.

Отдельно проверим с оператором устройство хранилища и исходящую сеть:

  1. Блочный диск и зона доступности. По рекомендациям Open WebUI для Kubernetes, база данных SQLite требует блочного хранилища, а сетевые файловые системы вроде NFS или SMB для неё не подходят из-за проблем с блокировками файлов. Встроенный класс yc-network-ssd создаёт сетевой блочный SSD-диск Compute Cloud, который монтируется к узлу как обычная локальная файловая система и подходит для нашей задачи. В настройках чарта мы оставим параметр persistence.provider: local. Он выбирает в самом приложении работу с локальной файловой системой контейнера (в отличие от облачных объектных корзин S3/GCS), а не физический диск внутри узла. Как объясняет документация по томам Яндекс Cloud, диск Compute Cloud жёстко привязан к конкретной зоне доступности. Режим ReadWriteOnce (RWO) разрешает монтировать диск на чтение и запись только одному узлу, а чтобы на этом узле к базе SQLite не обращались два контейнера одновременно, мы закрепим в конфигурации одну реплику и стратегию обновления Recreate.
  2. Исходящий доступ к реестру образов и внешним ресурсам. Наш частный интерфейс не требует открывать входящие порты NodePort (30000–32767) или SSH из примеров документации по группам безопасности (при этом существующие служебные правила обмена между мастером и узлами трогать нельзя). Однако согласно схеме сети Managed Service for Kubernetes, самим рабочим узлам нужен исходящий маршрут в интернет (через публичные IP узлов или настроенный оператором NAT-шлюз) и работающий DNS. Роль сервисного аккаунта узлов для доступа к Yandex Container Registry не даёт доступа к внешнему реестру ghcr.io. Узлы должны иметь разрешённый HTTPS-выход к ghcr.io (включая CDN-адреса скачивания слоёв образа) и к API выбранного вами провайдера языковой модели.
  3. Стартовая инициализация вспомогательной модели документов. Даже при выключенной Ollama приложение Open WebUI при запуске инициализирует вспомогательную embedding-модель для работы с документами. Как видно в исходном коде v0.12.0 (retrieval.py и retrieval/utils.py), в стандартной конфигурации используется sentence-transformers/all-MiniLM-L6-v2, и приложение может обращаться к Hugging Face за её файлами или проверкой кеша. Хотя стандартный образ обычно включает предзагруженный кеш при сборке, а чарт копирует стартовое содержимое /app/backend/data в том, одна лишь доступность ghcr.io ещё не гарантирует, что все стартовые обращения в закрытой сети пройдут успешно. Оператору следует заранее проверить разрешённый исходящий доступ или подготовленные ресурсы.

Когда целевой кластер, права, класс диска и исходящая сеть подтверждены, подготовим на своём компьютере точную конфигурацию Helm и проверим её локально до обращения к кластеру.

Подготовим настройки Helm

По сравнению с базовым уроком «Open WebUI в Kubernetes через Helm» мы сохраняем проверенную связку версий чарта и приложения, одиночный режим работы и внешнюю ссылку на секрет, но явно указываем облачный класс хранилища yc-network-ssd для создания сетевого SSD-диска в Яндекс Cloud.

Обратите внимание на версии и маршруты проверки здоровья:

  • В соседнем уроке про виртуальную машину закреплён образ v0.12.3, однако здесь мы строго сохраняем пару чарт 17.0.0 и приложение v0.12.0 из локально сохранённого официального чарта. Это обеспечивает точное совпадение шаблонов и переменных чарта. Такая фиксация нужна для воспроизводимости урока и не означает, что версия самая свежая или прошла отдельный аудит безопасности.
  • В текущей неверсионированной справке по Kubernetes на сайте Open WebUI встречается пример с более старой парой (16.5.0 / v0.11.3) и проверкой готовности /health/db. Мы не копируем тот фрагмент, а оставляем маршрут /ready: как подтверждает исходный код main.py версии v0.12.0, именно /ready возвращает код 200, когда завершена инициализация приложения и проверена связь с базой данных.

В рабочей папке на своём компьютере сохраните следующий файл под именем values-yc-course.yaml:

# One new release in its own namespace; chart 17.0.0 only.
replicaCount: 1
workload:
  kind: Deployment
strategy:
  type: Recreate
image:
  repository: ghcr.io/open-webui/open-webui
  tag: v0.12.0
ollama:
  enabled: false
pipelines:
  enabled: false
tika:
  enabled: false
terminals:
  enabled: false
websocket:
  enabled: true
  manager: ""
  redis:
    enabled: false
service:
  type: ClusterIP
  port: 80
  containerPort: 8080
ingress:
  enabled: false
route:
  enabled: false
persistence:
  enabled: true
  provider: local
  storageClass: yc-network-ssd
  accessModes:
    - ReadWriteOnce
  size: 10Gi
  annotations:
    helm.sh/resource-policy: keep
# No model endpoint is installed or tested by this lesson.
openaiApiKey: ""
resources:
  requests:
    cpu: 500m
    memory: 1Gi
  limits:
    cpu: "2"
    memory: 4Gi
startupProbe:
  httpGet:
    path: /health
    port: http
  periodSeconds: 10
  timeoutSeconds: 5
  failureThreshold: 60
readinessProbe:
  httpGet:
    path: /ready
    port: http
  periodSeconds: 10
  timeoutSeconds: 5
extraEnvVars:
  - name: WEBUI_SECRET_KEY
    valueFrom:
      secretKeyRef:
        name: open-webui-auth
        key: WEBUI_SECRET_KEY
  - name: UVICORN_WORKERS
    value: "1"

Разберём, за что отвечают блоки этого файла в облачном кластере:

  • Одиночный процесс и стратегия обновления. Параметры replicaCount: 1, workload.kind: Deployment и переменная UVICORN_WORKERS: "1" задают одну реплику с одним рабочим процессом. При штатном обновлении Deployment стратегия Recreate сначала завершает старые Pod, затем запускает новые; интерфейс на это время недоступен. Это не универсальная защита от одновременного доступа к SQLite: ручное удаление Pod или создание дополнительных объектов требует отдельного контроля оператора.
  • Выключенные локальные дополнения и частная сеть. Блоки ollama, pipelines, tika, terminals и websocket.redis отключены (false), чтобы не создавать в кластере лишние сервисы и не требовать Redis. Внутренний сервис ClusterIP направляет порт 80 на контейнерный порт 8080, а внешние входные точки (ingress.enabled: false, route.enabled: false) закрыты.
  • Хранилище в Яндекс Cloud. В блоке persistence явно выбран storageClass: yc-network-ssd вместо стандартного медленного класса yc-network-hdd. Запрашивается том 10Gi с режимом ReadWriteOnce. Аннотация helm.sh/resource-policy: keep указывает Helm не удалять этот PVC, если вы когда-нибудь решите удалить сам Helm-релиз.
  • Секретный ключ и внешние модели. Поле openaiApiKey: "" оставлено пустым: мы никогда не записываем ключи API или пароли в открытый YAML-файл, аргументы командной строки или рендер шаблонов. Переменная WEBUI_SECRET_KEY, которая подписывает токены сессий и защищает сохранённые в базе учётные данные, подключается через secretKeyRef из подготовленного оператором секрета open-webui-auth.

Теперь добавим официальный репозиторий чартов на своём компьютере, сверим описание версии 17.0.0, проверим наш файл линтером и выполним локальный рендер манифестов в файл rendered-open-webui.yaml. Эти команды работают офлайн или с репозиторием чартов и ничего не устанавливают в облачный кластер:

helm repo add open-webui https://open-webui.github.io/helm-charts
helm repo update open-webui
helm show chart open-webui/open-webui --version 17.0.0
helm pull open-webui/open-webui --version 17.0.0
helm lint ./open-webui-17.0.0.tgz -f values-yc-course.yaml
helm template "$RELEASE" open-webui/open-webui --version 17.0.0 --namespace "$NS" -f values-yc-course.yaml > rendered-open-webui.yaml

Вывод helm show chart должен подтвердить связку version: 17.0.0 и appVersion: 0.12.0, а helm lint — завершиться без ошибок (0 chart(s) failed). Откройте созданный локально файл rendered-open-webui.yaml и убедитесь, что в нём сгенерированы ровно четыре объекта в пространстве имён open-webui-yc-course:

  1. ServiceAccount с именем open-webui-yc-course-sa;
  2. PersistentVolumeClaim с именем open-webui-yc-course, классом yc-network-ssd, размером 10Gi и аннотацией helm.sh/resource-policy: keep;
  3. Service с именем open-webui-yc-course типа ClusterIP (порт 80 → 8080);
  4. Deployment с именем open-webui-yc-course, одной репликой, стратегией Recreate, образом ghcr.io/open-webui/open-webui:v0.12.0, монтированием /app/backend/data, проверками /health и /ready и ссылкой secretKeyRef на open-webui-auth без открытых значений секретов.

Успешный локальный рендер доказывает, что наш values-yc-course.yaml синтаксически верен и формирует ровно те объекты Kubernetes, которые мы ожидаем. При этом он не проверяет допуск объектов политиками кластера, наличие секрета open-webui-auth, скачивание образа и выделение диска в Яндекс Cloud. Перейдём к установке в живой кластер.

Установим Open WebUI и дождёмся готовности

Перед запуском helm install подготовим пространство имён и убедимся, что оператор создал в нём стабильный секрет. После установки сверим фактическое состояние объектов и события облачного драйвера дисков, в том числе если ожидание завершится по тайм-ауту.

Если пространство имён open-webui-yc-course ещё не создано оператором и на предыдущем шаге проверка auth can-i create namespaces вернула yes, создайте его:

kubectl --kubeconfig "$COURSE_KUBECONFIG" --context "$CTX" create namespace "$NS"

Прежде чем запускать установку чарта, оператор должен создать в пространстве имён open-webui-yc-course секрет open-webui-auth с непустым ключом WEBUI_SECRET_KEY (и сохранить само значение в корпоративном хранилище секретов). Проверим наличие метаданных этого секрета в кластере:

kubectl --kubeconfig "$COURSE_KUBECONFIG" --context "$CTX" -n "$NS" get secret open-webui-auth

Вызываем обычный get secret без флагов -o yaml или -o json, чтобы не выводить на экран закодированное в Base64 значение секрета. Команда подтверждает, что объект open-webui-auth существует в нужном namespace; правильность самого ключа WEBUI_SECRET_KEY внутри него подтверждает оператор.

Теперь запустим установку нового релиза с явным указанием версии чарта 17.0.0, нашего файла настроек, изолированного kubeconfig и контекста. Мы используем команду helm install (а не upgrade --install, чтобы случайно не замаскировать обновление существующего релиза) и задаём ожидание готовности до 15 минут:

helm install "$RELEASE" open-webui/open-webui --version 17.0.0 --kube-context "$CTX" --namespace "$NS" -f values-yc-course.yaml --wait --timeout 15m --kubeconfig "$COURSE_KUBECONFIG"

Независимо от того, завершилась ли команда сообщением об успехе или прервалась по тайм-ауту ожидания, сразу после неё проверьте фактическое состояние релиза, развёртывания, Pod и тома данных:

helm status "$RELEASE" --kube-context "$CTX" -n "$NS" --kubeconfig "$COURSE_KUBECONFIG"
kubectl --kubeconfig "$COURSE_KUBECONFIG" --context "$CTX" -n "$NS" get deployment,service,pvc -l "$SELECTOR"
kubectl --kubeconfig "$COURSE_KUBECONFIG" --context "$CTX" -n "$NS" rollout status deployment/open-webui-yc-course --timeout=15m
kubectl --kubeconfig "$COURSE_KUBECONFIG" --context "$CTX" -n "$NS" get pods -l "$SELECTOR" -o wide
kubectl --kubeconfig "$COURSE_KUBECONFIG" --context "$CTX" -n "$NS" get pvc open-webui-yc-course
kubectl --kubeconfig "$COURSE_KUBECONFIG" --context "$CTX" -n "$NS" describe pvc open-webui-yc-course

В рабочем состоянии helm status показывает STATUS: deployed, get pvc отображает статус Bound с привязанным томом PV, а get pods показывает один Pod в статусе Running с готовностью 1/1 (READY). Готовность 1/1 означает, что внутри контейнера успешно отработала проверка /ready (приложение запустилось и подключилось к своей базе SQLite на диске), однако связь с внешним API модели этим ещё не проверялась.

Если установка задержалась или helm install --wait вернул ошибку тайм-аута, не пытайтесь повторно запускать helm install, не меняйте StorageClass на лету и не удаляйте PVC наугад. Сначала определите причину задержки по таблице симптомов:

Что видно в выводе командВозможные причиныЧто проверить (самостоятельно или с оператором)
PVC в статусе Pending, а Pod ещё не назначен на узелДля класса yc-network-ssd с WaitForFirstConsumer диск ожидает выбора узла. Долгое ожидание может означать проблему планированияПосмотрите события Pod: что именно мешает назначению, например нехватка CPU/RAM
PVC долго остаётся Pending, хотя планирование Pod продвинулосьВозможна ошибка создания диска через CSI; само состояние ещё не устанавливает причинуИзучите Events заявки PVC. По указанной ошибке оператор проверит квоту SSD-дисков, CSI и права сервисного аккаунта кластера
Событие FailedScheduling у PodВозможны нехватка ресурсов, несовпадение зоны диска и узла или другие ограничения планированияОператор читает конкретное сообщение события, проверяет состояние узлов, свободные ресурсы и топологию тома
Статус ImagePullBackOff или ErrImagePullОбраз не скачан: возможны ошибка имени/тега, доступ к реестру или сетевая проблемаПо событию оператор проверяет имя образа, DNS и исходящий доступ к ghcr.io и адресам скачивания
Статус CreateContainerConfigErrorВозможно, отсутствует Secret open-webui-auth или ключ WEBUI_SECRET_KEY в нёмПроверьте сообщение в событиях Pod и попросите оператора проверить наличие ссылки и ключа, не выводя их значения
Статус Running, но READY 0/1, или CrashLoopBackOffПроцесс ещё инициализируется, не проходит пробу или завершается с ошибкой; состояние само по себе не объясняет причинуПроверьте события и ограниченный хвост лога: причину могут показать ошибка приложения или недоступные стартовые ресурсы. Не публикуйте чувствительные строки
Ошибка Forbidden или тайм-аут kubectl / HelmВозможны нехватка прав или потеря связи с APIВернитесь к проверкам cluster-info и auth can-i вместе с оператором

При чтении событий и последнего фрагмента лога явно указывайте файл подключения, контекст и пространство имён. Вместо POD_NAME подставьте имя Pod из уже выполненной команды get pods:

kubectl --kubeconfig "$COURSE_KUBECONFIG" --context "$CTX" -n "$NS" describe pod POD_NAME
kubectl --kubeconfig "$COURSE_KUBECONFIG" --context "$CTX" -n "$NS" describe pvc open-webui-yc-course
kubectl --kubeconfig "$COURSE_KUBECONFIG" --context "$CTX" -n "$NS" logs POD_NAME --tail=50

Если ваших прав чтения не хватает для просмотра событий уровня узлов или PersistentVolume, попросите оператора посмотреть их со своей стороны. Продолжайте урок только после того, как PVC перешёл в Bound, а единственный Pod достиг состояния Running и READY 1/1.

Откроем интерфейс и создадим администратора

У сервиса ClusterIP нет внешнего адреса в интернете. Чтобы открыть веб-интерфейс в своём браузере, пробросим локальный порт с компьютера, на котором запущен браузер, через защищённое соединение kubectl port-forward прямо к сервису внутри кластера.

Для этого не нужны доменное имя, TLS-сертификат, публичный Ingress или облачный LoadBalancer. При этом сохраняется сетевая граница из правил подключения к кластеру: частный доступ к веб-интерфейсу приложения через port-forward не делает внешний API-адрес самого кластера внутренним (защита API по-прежнему держится на IAM и группах безопасности).

Откройте второе окно терминала на том же компьютере, где запущен ваш браузер (не запускайте эту команду на удалённом сервере или бастионе). Укажите в переменной COURSE_KUBECONFIG тот же самый абсолютный путь к файлу kubeconfig-yc-course.yaml, который вы использовали в первом терминале, и запустите перенаправление с локального адреса 127.0.0.1:13000 на порт 80 сервиса:

COURSE_KUBECONFIG="REPLACE_WITH_SAME_ABSOLUTE_KUBECONFIG_PATH"
CTX="open-webui-yc-course"
NS="open-webui-yc-course"
kubectl --kubeconfig "$COURSE_KUBECONFIG" --context "$CTX" -n "$NS" port-forward --address 127.0.0.1 service/open-webui-yc-course 13000:80

Флаг --address 127.0.0.1 привязывает открытый порт 13000 строго к петлевому интерфейсу вашего компьютера, чтобы к нему нельзя было подключиться из локальной сети. Оставьте этот второй терминал открытым на всё время работы с браузером.

Откройте в браузере адрес:

http://127.0.0.1:13000

Поскольку мы подключились к новому экземпляру с чистой базой данных на новом диске, приложение предложит создать первую учётную запись администратора. Если вместо регистрации первого администратора вы сразу увидели окно обычного входа в чужой или старый экземпляр, остановитесь и проверьте COURSE_KUBECONFIG, контекст CTX и имя пространства имён NS.

Заполните имя, адрес электронной почты и пароль администратора. Как подтверждает исходный код роутера auths.py версии v0.12.0, когда в пустой базе создаётся самый первый пользователь (общее число пользователей равно единице), приложение автоматически назначает ему роль admin и сразу сохраняет в базу настройку ui.enable_signup: False, закрывая регистрацию для следующих посетителей.

Согласно справочнику переменных окружения Open WebUI, параметр ENABLE_SIGNUP относится к категории ConfigVar: при включённом по умолчанию сохранении конфигурации настройки, записанные в базу данных или изменённые через интерфейс администратора (Admin UI), имеют приоритет над переменными окружения контейнера. Поэтому пытаться управлять регистрацией через добавление ENABLE_SIGNUP в values-yc-course.yaml после первого запуска ненадёжно. Проверим безопасность входа прямо в интерфейсе:

  1. Откройте панель администратора (Admin Panel → Settings → General) и убедитесь, что самостоятельная регистрация новых пользователей (Enable New Sign Ups) выключена.
  2. Откройте адрес http://127.0.0.1:13000 в новом приватном (инкогнито) окне браузера и убедитесь, что на странице входа нет кнопки регистрации нового аккаунта.
  3. В основном окне браузера выйдите из системы (Sign Out) и снова войдите под только что созданным аккаунтом администратора.

Мы подтвердили, что веб-интерфейс открывается локально, первый администратор создан, а регистрация посторонних закрыта. Список моделей сейчас может быть пустым, поскольку мы ещё не подключали внешний API. Прежде чем настраивать модель, убедимся, что вход под созданным аккаунтом сохраняется после замены Pod с прежним облачным диском.

Проверим данные после замены Pod

Проверим сохранение аккаунта администратора при перезапуске или замене единственного Pod. В нашей схеме база SQLite лежит в каталоге /app/backend/data на сетевом SSD-диске Compute Cloud. Kubernetes Secret open-webui-auth передаёт стабильный WEBUI_SECRET_KEY: приложение использует его для подписи токенов сессий и защиты соответствующих зашифрованных учётных данных. Чтобы проверить сохранность данных при штатной замене контейнера, сравним уникальные идентификаторы (UID) объектов до и после перезапуска.

Вернитесь в первый терминал и запишите текущие UID работающего Pod, UID заявки PVC и имя привязанного тома PV (.spec.volumeName), а затем запустите штатный перезапуск развёртывания (rollout restart) и дождитесь его завершения:

kubectl --kubeconfig "$COURSE_KUBECONFIG" --context "$CTX" -n "$NS" get pods -l "$SELECTOR" -o custom-columns=NAME:.metadata.name,UID:.metadata.uid
kubectl --kubeconfig "$COURSE_KUBECONFIG" --context "$CTX" -n "$NS" get pvc open-webui-yc-course -o custom-columns=NAME:.metadata.name,UID:.metadata.uid,VOLUME:.spec.volumeName,STATUS:.status.phase
kubectl --kubeconfig "$COURSE_KUBECONFIG" --context "$CTX" -n "$NS" rollout restart deployment/open-webui-yc-course
kubectl --kubeconfig "$COURSE_KUBECONFIG" --context "$CTX" -n "$NS" rollout status deployment/open-webui-yc-course --timeout=15m

Во время перезапуска со стратегией Recreate старый Pod завершается до старта нового, поэтому веб-интерфейс на короткое время станет недоступен. Когда команда rollout status сообщит об успешном завершении, повторите в первом терминале две первые команды чтения UID:

kubectl --kubeconfig "$COURSE_KUBECONFIG" --context "$CTX" -n "$NS" get pods -l "$SELECTOR" -o custom-columns=NAME:.metadata.name,UID:.metadata.uid
kubectl --kubeconfig "$COURSE_KUBECONFIG" --context "$CTX" -n "$NS" get pvc open-webui-yc-course -o custom-columns=NAME:.metadata.name,UID:.metadata.uid,VOLUME:.spec.volumeName,STATUS:.status.phase

Сравните вывод до и после перезапуска:

  • Имя и UID у Pod должны стать новыми — это доказывает, что контейнер действительно был пересоздан.
  • UID у PVC, имя тома в колонке VOLUME (PV) и статус Bound должны остаться прежними — новый Pod подключился к тому же самому облачному диску Compute Cloud в той же зоне доступности (топология дисков).

При удалении старого Pod процесс kubectl port-forward во втором терминале мог разорвать соединение. Переключитесь во второй терминал: если перенаправление остановилось или перестало отвечать, завершите его клавишами Ctrl+C и запустите ту же команду port-forward заново.

После восстановления туннеля не полагайтесь на открытую ранее вкладку браузера. Откройте новое приватное окно, перейдите по адресу http://127.0.0.1:13000 и выполните вход под своим логином и паролем администратора.

Если вход прошёл успешно и идентификаторы PVC/PV остались прежними, проверка подтвердила вход под существующим аккаунтом после этой штатной замены Pod с прежним томом. Она не проверяет все настройки, чаты и загруженные файлы. Если же приложение снова просит зарегистрировать первого администратора или не принимает пароль, не сбрасывайте базу данных: проверьте вместе с оператором, не изменился ли контекст/пространство имён, смонтирован ли прежний том и не менялось ли значение WEBUI_SECRET_KEY в секрете open-webui-auth.

Учитывайте границу этой проверки: мы подтвердили работу штатной замены Pod с тем же самым облачным диском на доступном узле. Это не проверка восстановления из резервной копии (backup restore), не тест отказа самого диска и не проверка высокой доступности (HA).

После проверки интерфейса и входа под прежним аккаунтом отдельно проверим подключение внешнего API языковой модели.

Подключим внешний API модели

Готовность интерфейса и базы данных не доказывает, что из кластера Яндекс Cloud работает обращение к языковой модели. Серверное подключение к модели настраивается и проверяется отдельно.

Поскольку в нашей конфигурации сохранение настроек в базе данных включено по умолчанию, мы настраиваем подключение к внешнему поставщику через панель администратора (Admin UI), не записывая секретные API-ключи в values-yc-course.yaml, аргументы Helm или сгенерированные манифесты. Пошаговая настройка URL провайдера, ключа и списка моделей в интерфейсе подробно разобрана в опубликованном уроке «Подключаем OpenAI-совместимый API».

Выполните настройку серверного подключения в открытом интерфейсе администратора (http://127.0.0.1:13000), соблюдая правила вашей инфраструктуры:

  1. Используйте только согласованный адрес API (Base URL), разрешённый идентификатор модели и ключ, полученный по правилам безопасности вашей организации (создание аккаунтов у внешних провайдеров, настройка корпоративного SSO, функций или агентов в этот урок не входят).
  2. Если у вас есть разрешение и бюджет на платный вызов API, откройте новый чат, выберите подключённую модель и отправьте один короткий обезличенный тестовый вопрос без персональных или коммерческих тайн. Появление списка моделей в выпадающем меню ещё не доказывает работоспособность генерации — только фактически полученный текстовый ответ подтверждает, что весь маршрут от Pod через исходящую сеть рабочего узла до внешнего API исправен.
  3. Если вы сейчас не вводите реальный ключ или не отправляете платный запрос, урок можно завершить и без этого. Зафиксируйте для себя точный статус: интерфейс и вход под прежним аккаунтом после штатной замены Pod с тем же PVC/PV проверены, а вызов внешней модели не тестировался.

Если при отправке запроса возвращается ошибка соединения или авторизации, проверьте правильность введённого в Admin UI адреса и ключа, доступность выбранной модели на вашем тарифе и разрешённый исходящий HTTPS-маршрут с рабочих узлов кластера. Не пытайтесь устранить ошибку исходящего доступа к API публикацией Ingress или LoadBalancer: входящий доступ к веб-интерфейсу и исходящие запросы Pod к внешнему провайдеру работают по разным сетевым путям.

Завершим сеанс и проверим расходы

У локального туннеля, объектов Kubernetes и платных ресурсов Яндекс Cloud разные жизненные циклы. Разберём, что остановится после Ctrl+C во втором терминале и какие действия могут привести к удалению облачного диска с базой данных.

Когда вы закончите работу в браузере, перейдите во второй терминал, где запущен kubectl port-forward, и нажмите Ctrl+C. Локальный порт 127.0.0.1:13000 на вашем компьютере закроется.

Посмотрим, как разные операции влияют на данные и ресурсы кластера, опираясь на свойства нашего чарта и документацию по жизненному циклу томов и классов хранилищ Яндекс Cloud:

ОперацияЧто происходит с приложением и доступомЧто происходит с PVC, PV и облачным SSD-диском
Нажать Ctrl+C в окне kubectl port-forwardЗакрывается только локальный туннель на вашем компьютере. Pod и Service в кластере продолжают работатьНичего не меняется: PVC, PV и диск Compute Cloud продолжают работать и хранить данные
Удалить только Helm-релиз (helm uninstall)Удаляются Deployment, Pod, Service и ServiceAccount нашего приложенияБлагодаря аннотации helm.sh/resource-policy: keep объект PVC намеренно оставляется в кластере, поэтому PV и облачный диск сохраняются
Вручную удалить PVC или весь namespace в KubernetesПриложение теряет хранилище (или удаляется вместе с пространством имён)У встроенного класса yc-network-ssd стоит политика reclaimPolicy: Delete: удаление PVC может автоматически удалить PV и сам облачный диск Compute Cloud
Удалить весь кластер Managed Kubernetes в облакеКластер и рабочие узлы уничтожаютсяПо правилам работы томов диски Compute Cloud, связанные с PersistentVolumes, при удалении кластера не удаляются автоматически и остаются в каталоге

Аннотация helm.sh/resource-policy: keep защищает том только при удалении релиза через Helm. Она не спасает данные при ручном удалении PVC, удалении namespace или прямом удалении диска в консоли облака, и не заменяет регулярного резервного копирования.

В общем (разделяемом) кластере не нужно останавливать или удалять сам кластер, рабочие узлы, пространство имён или PVC просто ради завершения урока. Любое удаление ресурсов должно быть отдельным согласованным решением владельца инфраструктуры после проверки принадлежности объектов (ownership), точных идентификаторов томов и наличия резервной копии.

Прежде чем закрыть терминалы, проверьте структуру текущих расходов по правилам тарификации Managed Service for Kubernetes:

  • Мастер и рабочие узлы кластера. В работающем кластере тарифицируются ресурсы мастера (зависит от зонального или регионального типа) и вычислительные ресурсы виртуальных машин рабочих узлов (vCPU, RAM и их загрузочные диски) в Compute Cloud. То, что узлы уже существовали до нашего урока, не делает их бесплатными. Если владелец останавливает свой отдельный кластер, плата за мастер прекращается, но диски и сетевые адреса могут тарифицироваться дальше.
  • Диск приложения и снимки. Созданный через PVC блочный диск yc-network-ssd объёмом 10Gi (а также любые созданные для него снэпшоты или резервные копии) оплачивается всё время, пока этот диск существует в каталоге облака, даже если сам Pod или релиз остановлен.
  • Сеть и внешние сервисы. Исходящий трафик, публичные IP-адреса или NAT-шлюз (если они используются в вашей сети) тарифицируются по правилам облака, а запросы к языковой модели оплачиваются по счёту вашего внешнего API-провайдера. Точную текущую стоимость всегда сверяйте в актуальном прайс-листе и калькуляторе Яндекс Cloud для вашего региона и валюты.

Чтобы сохранить результаты установки для дальнейшей эксплуатации без утечки секретов, запишите для себя и оператора короткую памятку по итогам работы (без значений паролей, токенов и ключей):

  • идентификатор каталога и кластера (FOLDER_ID, CLUSTER_ID), путь к изолированному файлу kubeconfig-yc-course.yaml;
  • пространство имён и имя релиза: open-webui-yc-course;
  • имя созданного PVC (open-webui-yc-course), имя связанного с ним PV (.spec.volumeName) и имя ссылки на секрет (open-webui-auth);
  • три раздельных статуса проверки: вход администратора через 127.0.0.1:13000 (подтверждён), сохранение аккаунта на прежнем PVC/PV после замены Pod (подтверждено), ответ внешнего API модели (подтверждён фактическим ответом либо отмечен как «не проверялся»).

Теперь у вас есть рабочий частный экземпляр Open WebUI в Managed Kubernetes Яндекс Cloud с постоянным томом, проверенным входом под прежним аккаунтом после штатной замены Pod и понятной картой облачных ресурсов. Настройка регулярного резервного копирования дисков, выбор схемы публичного доступа или переход к отказоустойчивой архитектуре с внешней базой данных остаются отдельными эксплуатационными задачами, которые решаются поверх этой базовой установки.

Материалы урока

Отметить прохождение

Отметьте урок после того, как получили проверяемый результат из практической части.

stro:end-->