Files
KindClustersDashboard/docs/k8s_runbook.md
T
2026-04-04 05:15:54 +03:00

14 KiB
Raw Blame History

Runbook эксплуатации Kubernetes модуля

Документ относится к приложению AppsTemplate (модуль Kubernetes в веб-платформе). Репозиторий kind-k8s-develop хранит этот runbook рядом с утилитами локального kind.

Автор: Сергей Антропов
Сайт: https://devops.org.ru

1. Кластер недоступен

  • Симптомы: GET /api/v1/k8s/clusters/{id}/health возвращает unreachable, preflight показывает ошибку connectivity.
  • Проверки:
    • Валиден ли kubeconfig кластера в записи k8s_clusters.
    • Доступен ли API endpoint кластера из контейнера приложения.
    • Не истекли ли сертификаты/токены в kubeconfig.
  • Действия:
    • Обновить kubeconfig кластера через PATCH /api/v1/k8s/clusters/{id}.
    • Повторно запустить health-check с force=1.
    • При необходимости временно отложить массовую выдачу доступов.

1.1. Docker Desktop: kubeconfig с 127.0.0.1, приложение в контейнере

  • Симптом: health-check и любые вызовы API кластера из приложения дают unreachable, хотя kubectl с хоста работает.
  • Причина: в kubeconfig server: https://127.0.0.1:… означает «loopback того процесса, который подключается». Внутри контейнера 127.0.0.1 — это не хост с Docker Desktop.
  • Что сделать: в записи кластера заменить адрес API на тот, который виден из контейнера приложения:
    • macOS / Windows, Docker Desktop: обычно https://host.docker.internal:ПОРТ (тот же порт, что был у 127.0.0.1, часто 6443).
    • Linux: при необходимости добавить в docker-compose для сервиса приложения extra_hosts: ["host.docker.internal:host-gateway"] и использовать host.docker.internal, либо указать IP шлюза к хосту / LAN-IP.
  • Имя вида Something.local (Bonjour / mDNS): с Mac такое имя обычно резолвится на хосте, но внутри контейнера приложения часто не резолвится — health-check снова будет unreachable при том же kubeconfig. Для Docker Desktop надёжнее https://host.docker.internal:6443 (или фиксированный LAN-IP Mac), а не *.local.
  • Только IPv6 у host.docker.internal: если в контейнере getent hosts host.docker.internal показывает один адрес fdc4:... (IPv6), а API слушает IPv4, соединение может не установиться. В репозитории AppsTemplate для сервиса app в docker-compose.yml задано extra_hosts: ["host.docker.internal:host-gateway"] — после docker compose up -d перепроверьте getent (должен появиться маршрут через IPv4-шлюз хоста). Kubeconfig: server: https://host.docker.internal:6443.
  • TLS / hostname mismatch: при server: https://host.docker.internal:6443 сертификат API чаще всего выписан не на это имя → ошибка вида CERTIFICATE_VERIFY_FAILED / Hostname mismatch. GET /clusters/{id}/health сначала подключается с kubeconfig из БД; при типичной ошибке TLS выполняется вторая попытка с временной копией, где для cluster из current-context включён insecure-skip-tls-verify (в БД ничего не пишется). В ответе может быть tls_insecure_fallback_used: true. Для apply RBAC / выдачи доступов по-прежнему нужен рабочий TLS в сохранённом kubeconfig или явный insecure-skip-tls-verify в YAML.
  • Явный insecure в kubeconfig (рекомендуется для dev, если нужны не только health): в записи того же cluster добавьте insecure-skip-tls-verify: true. Пример фрагмента:
    clusters:
      - name: docker-desktop
        cluster:
          server: https://host.docker.internal:6443
          insecure-skip-tls-verify: true
          # certificate-authority-data: ...  # при insecure можно убрать, чтобы не путаться
    
    В production так не делают: там server и SAN в сертификате должны совпадать, проверка TLS включена.
  • Обновление kubeconfig: UI «Редактировать кластер» (/k8s/clusters/{id}/edit) или PATCH /api/v1/k8s/clusters/{id}.

2. Частично примененные манифесты RBAC

  • Симптомы: часть пользователей получила доступ, часть — ошибки в POST /api/v1/k8s/access/bulk.
  • Проверки:
    • Использовать preflight (/access/preflight, /access/bulk/preflight) перед повторной операцией.
    • Проверить существующие активные записи в k8s_user_configs.
    • Проверить аудит k8s_access_audit.
  • Действия:
    • Повторить операцию с idempotency_key только после устранения причины.
    • Для конфликтных пользователей выполнить точечный revoke/restore или delete/create.
    • Если обнаружены дубликаты, очистить лишние записи и заново выдать доступ.

3. Восстановление после ошибок миграций

  • Симптомы: ошибки UndefinedTableError при старте приложения.
  • Проверки:
    • Убедиться, что применены миграции модуля из app/db/migrations/modules/k8s/ (репозиторий AppsTemplate).
    • Проверить наличие таблиц k8s_* в БД.
  • Действия:
    • Применить восстановительную миграцию app/db/migrations/102_k8s_repair_missing_tables.sql.
    • Перезапустить приложение.
    • Проверить работу API GET /api/v1/k8s/health.

4. Ротация ключа шифрования kubeconfig

  • Основной источник ключа: настройка в БД k8s.kubeconfig_encryption_key (страница Настройки → Модули → Kubernetes). При непустом значении в БД оно имеет приоритет над переменной окружения.
  • Резерв: K8S_KUBECONFIG_ENCRYPTION_KEY в .env — используется только если в БД ключ пустой (удобно для первого запуска и CI; см. env.example в AppsTemplate).
  • Рекомендуемый порядок смены ключа:
    1. Перевести сервис в окно обслуживания.
    2. Считать и дешифровать существующие значения из k8s_clusters.kubeconfig_encrypted и k8s_user_configs.kubeconfig_encrypted текущим ключом (из БД или ENV — в том же порядке приоритета, что у приложения).
    3. Перешифровать новым ключом и сохранить обратно в таблицы.
    4. Сохранить новый секрет в настройках модуля (или обновить ENV, если используете только резерв).
    5. Перезапустить приложение при необходимости и проверить health-check и скачивание kubeconfig.
  • Важно: не удалять старый ключ до завершения полной перешифровки.

5. Мониторинг и безопасность фоновых задач

  • Используйте страницу /k8s/jobs и API GET /api/v1/k8s/jobs (пагинация skip/limit, фильтр status) и GET /api/v1/k8s/jobs/{job_id} для полного текста ошибки и результата.
  • Детальные статусы:
    • GET /api/v1/k8s/access/jobs/{job_id}
    • GET /api/v1/k8s/access/bulk/jobs/{job_id}
    • GET /api/v1/k8s/kubeconfig/merge/jobs/{job_id}
  • Все job-status ответы проходят маскировку чувствительных полей (kubeconfig, token, secret, password, private_key, certificate).
  • Для merge-задач поле result.rendered не возвращается в статусе и доступно только через download endpoint.
  • Рекомендуется искать инциденты по correlation_id в логах приложения и в записи задачи в k8s_jobs.
  • Утилита наблюдаемости: GET /api/v1/k8s/observability/jobs/{job_id} для задач с job_type=k8s.observability; UI — /k8s/observability.

6. Утилита стека наблюдаемости (Metrics Server / Prometheus)

  • Настройки: k8s.observability_metrics_server_manifest_url (по умолчанию официальный components.yaml metrics-server), k8s.observability_metrics_server_kubelet_insecure_tls, k8s.observability_prometheus_manifest_bundle_url (опционально), таймауты и лимит кластеров за операцию.
  • Metrics Server: при типичных kubeadm/kind кластерах без корректных kubelet-сертификатов включите --kubelet-insecure-tls (чекбокс в UI или настройка/тело запроса install).
  • Prometheus stack: полный kube-prometheus-stack обычно ставят Helm вне приложения; через API имеет смысл подключать сокращённый multi-doc YAML, где все объекты имеют поддерживаемые kind. Иначе часть документов попадёт в skipped в ответе задачи.
  • Откат: POST .../uninstall/jobs удаляет ресурсы в обратном порядке того же бандла; при ручных правках в кластере возможны остаточные объекты — добейте kubectl delete / повторным uninstall.

6.1. Локальный кластер kind для разработки

Этот репозиторий (kind-k8s-develop): образ kind-k8s-tools, make create (Docker и make на хосте) поднимает kind; артефакты — clusters/<имя>/kubeconfig. Импорт kubeconfig в AppsTemplate см. §1.1 (адрес API должен быть достижим из контейнера приложения, не обязательно 127.0.0.1). Краткая инструкция по командам — в README.md в корне этого репозитория.

7. Срок действия доступа (access_expires_at)

  • В POST /access, POST /access/bulk и PATCH /access/{id} можно задать дату окончания; пустое значение в PATCH сбрасывает срок.
  • Фоновый цикл использует k8s.access_expiry_check_interval_hours; отзыв выполняется так же, как ручной revoke (RBAC в кластере, уведомление владельцу; в аудите reason: access_expired).
  • Уведомление при автоотзыве: в тексте указано «система (автоматически)».

8. Отправка kubeconfig в Telegram

  • В настройках модуля задать k8s.telegram_bot_token. У владельца доступа в профиле — поле telegram_chat_id (числовой id; миграция 105_profile_field_telegram_chat_id.sql добавляет описание в реестр полей).
  • Ссылка https://t.me/... не подставляет chat_id: пользователь должен написать боту и узнать id (например через @userinfobot), затем сохранить telegram_chat_id.
  • POST /api/v1/k8s/access/{config_id}/send-telegram: владелец или лид с modules.k8s:update. Длинный YAML режется по k8s.telegram_kubeconfig_max_chars — тогда в чат уходит короткое сообщение со ссылкой на /k8s/my-configs.

9. Дрейф RBAC и сверка с кластером (reconcile)

  • Симптом: в UI на карточке кластера колонка «Сверка RBAC» показывает drift_detected или sync_error, либо администратор вручную удалил Role/Binding в кластере.
  • Проверки:
    • POST /api/v1/k8s/access/{config_id}/reconcile-check (или кнопка «Сверка» на вкладке «Доступы») — пересчитать статус по сохранённым rbac_manifests.
    • Убедиться, что у записи доступа непустые rbac_manifests (иначе сначала apply-manifests или PATCH с пересборкой).
  • Действия при дрейфе: POST /api/v1/k8s/access/{config_id}/apply-manifests — повторное применение; после успешного apply статус сверки обновляется вместе с записью.
  • Настройки: k8s.reconcile_check_timeout_seconds, опционально k8s.reconcile_background_interval_hours (часы между фоновыми прогонами для активных доступов; 0 — только ручной вызов).