Перейти к содержанию

Установка через Wizard

Wizard — официальный Go-инсталлер AppSec.GenAI v2, упакованный в Docker-образ. Управляет полным жизненным циклом: namespace, pull-secret, инфраструктура, продуктовые сервисы — в правильном порядке.

Docker Compose

Инструкция по развёртыванию на отдельном сервере через Docker Compose — в разделе Установка и запуск.


Режимы развёртывания

  • Keycloak и Envoy Gateway не устанавливаются
  • Все сервисы работают в DEV_MODE — аутентификация отключена
  • Доступ через kubectl port-forward или ingress-nginx
  • TLS не требуется
Phase 1   — postgres, redis, nats, minio
Phase 1.1 — bootstrap внешних сервисов
Phase 2   — 11 продуктовых сервисов (DEV_MODE)
Phase 2.5 — Ingress (если ingress.enabled: true)
  • Keycloak + OIDC через Envoy Gateway
  • JWT-аутентификация во всех сервисах
  • Требует: wildcard TLS-сертификат и DNS на 3 хоста
Phase 0   — Gateway API CRDs + Envoy Gateway controller
Phase 1   — postgres, redis, nats, minio, keycloak
Phase 1.1 — bootstrap внешних сервисов
Phase 1.5 — genai-gateway (GatewayClass + OIDC SecurityPolicy)
Phase 2   — 11 продуктовых сервисов (JWT включён)

Быстрый старт: интерактивный Wizard

docker run --rm -it \
  -v ~/.kube/config:/kubeconfig:ro \
  -v $(pwd):/workspace \
  registry.appsec.global/appsecgenai-release/wizard:<VERSION> \
  wizard

OIDC / exec-based auth

Если ~/.kube/config использует OIDC (auth-provider: name: oidc) или exec-плагин (exec: command: d8/kubelogin/...) — wizard не сможет подключиться: внешние бинари недоступны в контейнере. Нужен kubeconfig со статическим Bearer-токеном.

Получите токен из вашего OIDC-провайдера (пример для Deckhouse/dex):

TOKEN=$(d8 login get-token ... | python3 -c "import sys,json; print(json.load(sys.stdin)['status']['token'])")

Создайте ~/kube-wizard.yaml и подставьте токен в поле user.token:

apiVersion: v1
kind: Config
current-context: my-cluster
clusters:
- cluster:
    server: https://api.<cluster-domain>
  name: my-cluster
contexts:
- context:
    cluster: my-cluster
    namespace: genai
    user: wizard
  name: my-cluster
users:
- name: wizard
  user:
    token: <TOKEN>

Монтировать: -v ~/kube-wizard.yaml:/kubeconfig:ro
Токен живёт ~1h — при истечении пересоздать.

Wizard задаёт 7 вопросов и записывает values.yaml в текущую директорию:

  AppSec GenAI — Setup Wizard
  ────────────────────────────

  Domain: app.example.com
  Namespace [genai]:
  Enable auth (Keycloak + Envoy OIDC)? [y/N]: n
  Expose via ingress-nginx? [y/N]: n
  Enable persistence (required for production)? [y/N]: y
  StorageClass (leave blank for cluster default):

  ✓ values.yaml written

  Run install now? [Y/n]: y

Куда записывается values.yaml

Инсталлер внутри контейнера обнаруживает /workspace и пишет файл туда — он появляется в текущей директории хоста благодаря монтированию -v $(pwd):/workspace.

По окончании wizard выводит endpoints:

kubectl port-forward svc/ui 8080:8080 -n genai
→ http://localhost:8080  (DEV_MODE, без логина)
UI:       https://app.example.com
Keycloak: https://keycloak.example.com
MinIO:    https://minio.example.com

DNS records (все → gateway.externalIPs[0]):
  app.example.com
  keycloak.example.com
  minio.example.com

Пароль bootstrap-пользователя genai-admin:
  kubectl -n genai get secret keycloak-auth \
    -o jsonpath='{.data.KEYCLOAK_BOOTSTRAP_USER_PASSWORD}' | base64 -d

Установка из готового values.yaml

docker run --rm \
  -v ~/.kube/config:/kubeconfig:ro \
  -v ./values.yaml:/values.yaml \
  -e HARBOR_PASS="<пароль>" \
  registry.appsec.global/appsecgenai-release/wizard:<VERSION> \
  install --harbor-user "<логин>" -f /values.yaml

Команды

Команда Описание
wizard Интерактивный ввод → values.yaml → опциональный install
install Полная установка всех компонентов
update Умный апгрейд — пропускает релизы без изменений
update --force Принудительный апгрейд всех релизов
delete Удалить все Helm-релизы (PVC сохраняются)
check Pre-flight проверки без изменений

Флаги

Флаг По умолчанию Описание
-f <path> values.yaml Путь к конфигурации
--dry-run false Отрендерить без применения
--only <a,b> Только указанные фазы
--skip <a,b> Пропустить фазы
--set key=value Переопределить значение
--harbor-user Логин Harbor для pull-secret
--harbor-pass $HARBOR_PASS Пароль Harbor
--parallel N 4 Параллельность Phase 2
--release-version вшита в образ Переопределить версию чартов

Пароль Harbor через env

Передавайте пароль через -e HARBOR_PASS="...", не через --harbor-pass (чтобы не светить в ps aux).


values.yaml — справочник

Обязательные параметры

Параметр Валидация Описание
domain required, не app.customer.com Базовый домен инсталляции
namespace required Kubernetes namespace (default: genai)

Полная схема

# ─── Базовые ────────────────────────────────────────────────────────────────
domain: "app.example.com"          # ОБЯЗАТЕЛЕН
namespace: "genai"
instance: ""                       # суффикс для мультиинсталляций

# ─── Аутентификация ─────────────────────────────────────────────────────────
auth:
  enabled: false                   # false = dev, true = prod (Keycloak + Envoy)

gateway:                           # заполнять только при auth.enabled: true
  publicScheme: "https"            # http | https
  externalIPs: []                  # bare-metal: ["10.0.0.1"]; cloud: []
  className: "genai-class"         # уникален в кластере
  name: "genai"
  proxyName: "envoy"
  controllerVersion: "v1.5.1"
  autoInstall: true                # false = BYOC (ваш Envoy Gateway)
  managed: true                    # false = routes-only

# ─── Доступ извне ────────────────────────────────────────────────────────────
ingress:
  enabled: false                   # только при auth=false
  className: ""
ingressTlsSecretName: ""           # при auth=true + https
imagePullSecretName: "harbor-cr"

# ─── Секреты ─────────────────────────────────────────────────────────────────
secrets:
  create: true                     # false = создать Secret'ы вручную до запуска

# ─── Инфра-компоненты ────────────────────────────────────────────────────────
postgres:
  enabled: true
  auth:
    username: genai_admin
    database: genai_db
  persistence: { enabled: false, size: "50Gi", storageClass: "" }

redis:
  enabled: true
  persistence: { enabled: false, size: "2Gi", storageClass: "" }

nats:
  enabled: true
  persistence: { enabled: false }

minio:
  enabled: true
  persistence: { enabled: false, size: "200Gi", storageClass: "" }

keycloak:
  enabled: true                    # только при auth.enabled: true
  persistence: { enabled: false, size: "1Gi", storageClass: "" }

# ─── Внешняя инфраструктура ──────────────────────────────────────────────────
global:
  image:
    registry: "registry.appsec.global"
    repositoryPrefix: "appsecgenai-release"
  deps:
    postgres:
      host: postgres
      port: 5432
      existingSecret: postgres-auth
      username: genai_admin
      database: genai_db
      # bootstrap: true   # создать схемы в существующей БД
    redis:
      host: redis
      port: 6379
      existingSecret: redis-auth
    nats:
      host: nats
      port: 4222
      # scheme: tls      # tls:// если внешний NATS за TLS (default: nats://)
      # bootstrap: true  # создать 6 JetStream streams
    minio:
      host: minio
      port: 9000
      existingSecret: minio-auth
      # scheme: https    # https если внешний MinIO за TLS (default: http)
      # bootstrap: true  # создать 10 S3 buckets
    keycloak:
      host: keycloak
      port: 8080
      # scheme: https    # https если внешний Keycloak за TLS (default: http)
      # bootstrap: true  # создать realm appsec-genai

# ─── Опционально ─────────────────────────────────────────────────────────────
license:
  centerPublicKey: ""              # RSA-2048 pubkey от SwordFish Security

tracing:
  enabled: false                   # true требует otel-collector в кластере

bootstrap: идемпотентность

Операции bootstrap безопасны для повторного запуска — бакеты, потоки и realm создаются с IF NOT EXISTS / --ignore-existing (409 Conflict = пропуск). Оставить bootstrap: true на каждый update — нормально.

Когда нужен bootstrap:

Ситуация Нужен?
Встроенный сервис (enabled: true) Нет — Helm hook jobs инициализируют автоматически
Внешний сервис (enabled: false), первый install Да
Внешний сервис (enabled: false), повторный update Оставить true (безвредно) или убрать (тоже нормально)

Секреты инфра-компонентов

Что делает secrets.create

secrets.create Компонент enabled Кто создаёт Secret Что делает Wizard
true (default) true Infra-чарт автоматически при helm install Ничего дополнительно
false true Вы — до запуска install Preflight проверяет наличие
любое false (внешний) Вы — всегда вручную Preflight НЕ проверяет

Внешний сервис — всегда ручной Secret

При postgres.enabled: false / redis.enabled: false / minio.enabled: false Wizard не создаёт никаких Secret'ов вне зависимости от secrets.create. Secret нужно создать самостоятельно до запуска.

Для чего existingSecret в global.deps.*

existingSecret — это имя K8s Secret'а, из которого продуктовые сервисы (jailbreak, results, orchestrator и т.д.) будут читать credentials при старте. Это не путь, не URL — просто имя объекта в namespace.

  • При secrets.create: true инфра-чарт сам создаёт Secret с этим именем.
  • При secrets.create: false или внешнем сервисе — вы создаёте Secret с этим именем сами.

Менять existingSecret нужно только если вы хотите использовать другое имя Secret'а (например, интеграция с ESO / Vault, где секрет уже существует под другим именем).

Какие ключи должны быть в каждом Secret'е

Secret Ключ Значение Кто читает
postgres-auth POSTGRES_PASSWORD пароль пользователя БД все сервисы с БД
redis-auth REDIS_PASSWORD пароль Redis сервисы с Redis
minio-auth MINIO_ROOT_USER access key (3–20 символов) сервисы с S3
minio-auth MINIO_ROOT_PASSWORD secret key (8+ символов) сервисы с S3

Postgres: пароль, не URL

В postgres-auth хранится только пароль (POSTGRES_PASSWORD). Полный DATABASE_URL продуктовые сервисы собирают сами из нескольких источников:

  • host, port, username, database — берутся из global.deps.postgres.* в values.yaml
  • POSTGRES_PASSWORD — читается из Secret postgres-auth через secretKeyRef
  • Итог: postgresql+asyncpg://{username}:{POSTGRES_PASSWORD}@{host}:{port}/{database}

Класть полный URL в секрет не нужно и не поддерживается в wizard-режиме.

Pre-create команды (при secrets.create: false или внешнем сервисе)

# PostgreSQL
kubectl create secret generic postgres-auth -n genai \
  --from-literal=POSTGRES_PASSWORD='<strong-password>'

# Redis
kubectl create secret generic redis-auth -n genai \
  --from-literal=REDIS_PASSWORD='<strong-password>'

# MinIO
kubectl create secret generic minio-auth -n genai \
  --from-literal=MINIO_ROOT_USER='<access-key>' \
  --from-literal=MINIO_ROOT_PASSWORD='<secret-key>'

Если используете нестандартное имя Secret'а — укажите его в global.deps.*.existingSecret.


Фазы установки

Фаза Имя Что делает Пропускается
Pre-flight preflight:secrets Проверяет Secret'ы при secrets.create: true
Pre-flight preflight:gateway Проверяет Envoy Gateway никогда
Phase 1 phase1:infra postgres, redis, nats, minio, keycloak
Phase 1.1 phase1.1 Bootstrap внешних сервисов если *.bootstrap не задан
Phase 1:secrets phase1:secrets Создаёт Secret'ы при secrets.create: false
Phase 1.5 phase1.5:genai-gateway Envoy Gateway + OIDC при auth=false
Phase 2 phase2:product 11 продуктовых сервисов
Phase 2.5 phase2.5 Ingress-ресурсы при auth=true или ingress=false

Частичный запуск

# Только продуктовые сервисы (после hotfix образа)
docker run --rm ... install --only phase2:product

# Пропустить инфраструктуру
docker run --rm ... install --skip phase1:infra,phase1.1

# Переопределить значение на лету
docker run --rm ... install --set tracing.enabled=true

Pre-flight проверки

docker run --rm \
  -v ~/.kube/config:/kubeconfig:ro \
  -v ./values.yaml:/values.yaml \
  registry.appsec.global/appsecgenai-release/wizard:<VERSION> \
  check -f /values.yaml
  CHECK                           STATUS  MESSAGE
  -----                           ------  -------
  gateway:envoy                   OK      Envoy Gateway controller running
  postgres                        WARN    storageClass is empty — will use cluster default
  phase1:infra                    OK      infra config looks good

✓ all checks passed
Статус Значение
OK Проверка пройдена
WARN Установка продолжится, стоит обратить внимание
FAIL Критическая ошибка — установка прервётся

Обновление

# Умный апгрейд (пропускает релизы без изменений)
docker run --rm \
  -v ~/.kube/config:/kubeconfig:ro \
  -v ./values.yaml:/values.yaml \
  registry.appsec.global/appsecgenai-release/wizard:<NEW_VERSION> \
  update -f /values.yaml

# Принудительный апгрейд (hotfix под тот же semver)
docker run --rm ... update --force -f /values.yaml

Версия инсталлера = версия продукта

Тег образа Wizard определяет версию всех Helm-чартов. Для апгрейда достаточно поменять тег — версия в values.yaml не указывается.


Проверка установки

# Статус подов
kubectl get pods -n genai

# Healthcheck через port-forward (auth=false)
kubectl port-forward svc/ui 8080:8080 -n genai &
curl http://localhost:8080/

# Пароль bootstrap-пользователя (auth=true)
kubectl -n genai get secret keycloak-auth \
  -o jsonpath='{.data.KEYCLOAK_BOOTSTRAP_USER_PASSWORD}' | base64 -d

Удаление

docker run --rm \
  -v ~/.kube/config:/kubeconfig:ro \
  -v ./values.yaml:/values.yaml \
  registry.appsec.global/appsecgenai-release/wizard:<VERSION> \
  delete -f /values.yaml

PVC сохраняются

Команда delete удаляет Helm-релизы, но не PVC. Для полного удаления:

kubectl delete pvc -n genai --all
kubectl delete namespace genai


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

ImagePullBackOff

kubectl delete secret harbor-cr -n genai 2>/dev/null || true
kubectl create secret docker-registry harbor-cr -n genai \
  --docker-server=registry.appsec.global \
  --docker-username=<логин> --docker-password=<пароль>

FAIL: gateway:envoy-required

При auth.enabled: true требуется Envoy Gateway:

helm install envoy-gateway \
  oci://docker.io/envoyproxy/gateway-helm \
  --version v1.5.1 -n envoy-gateway-system --create-namespace

Phase 2 зависла

# Логи конкретного сервиса
kubectl logs -l app.kubernetes.io/name=<service> -n genai --tail=50

# Переустановить только Phase 2
docker run --rm ... install --only phase2:product -f /values.yaml