> For the complete documentation index, see [llms.txt](https://doc.ozforensics.com/oz-knowledge-ru/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://doc.ozforensics.com/oz-knowledge-ru/rukovodstva/rukovodstvo-administratora/kak-razvernut-instant-api-v-kubernetes.md).

# Как развернуть Instant API в Kubernetes

{% hint style="warning" icon="display" %}
Внимание: это машинный перевод.
{% endhint %}

Oz Instant API — это разворачивание Oz API без сохранения данных: он выполняет анализы Liveness и Face Matching и сразу возвращает результаты, не сохраняя ни медиафайлы, ни результаты. В этом руководстве описана установка в Kubernetes с помощью официального [Helm-чарта](broken://pages/3700c483ffa46e8d3563a4ecd4a759aefe193c58#instant-api).

Приведенный ниже пример основан на версии чарта 1.0.2. Однако это лишь иллюстративный пример, и мы рекомендуем использовать последнюю версию чарта.

## Предварительные требования

Прежде чем начать, запросите у представителя Oz следующее.

* **Учетные данные DockerHub** — для загрузки образа Instant API (используются в **шаге 2** раздела **Подготовка**).
* **Лицензионный ключ** — необходим для запуска Instant API (**шаг 3** раздела **Подготовка**).
* **Тег образа** — версия образа приложения для развертывания (используется в файле `values`).

Если вы планируете использовать OzCapsula, вам также потребуется сгенерировать пару ключей JWT (**шаг 4** раздела **Подготовка**).

**Kubernetes 1.23+** — чарт использует `autoscaling/v2` (HPA с политиками поведения, GA в 1.23), `networking.k8s.io/v1` (Ingress, GA в 1.19) и `policy/v1` (PDB, GA в 1.21). Кластеры версий 1.21–1.22 поддерживаются частично за счет автоматического отката к `autoscaling/v2beta2` и `policy/v1beta1`.

Перед установкой убедитесь, что в кластере запущены следующие компоненты:

* **Ingress-контроллер** (один из перечисленных, при использовании Ingress API):
  * [F5 NGINX Ingress Controller](https://github.com/nginx/kubernetes-ingress) >= 3.0 (рекомендуется, IngressClass: `f5-nginx`).
  * [ingress-nginx](https://github.com/kubernetes/ingress-nginx) >= 1.0 (устаревший, [выводится из эксплуатации](https://github.com/kubernetes/ingress-nginx?tab=readme-ov-file#retiring), IngressClass: `nginx`).
* **Gateway API CRD** >= 1.0 и совместимый контроллер (например, **Istio** >= 1.20, Envoy Gateway, Cilium) — при использовании Gateway API вместо Ingress. Чарт создает ресурсы `gateway.networking.k8s.io/v1` (Gateway, HTTPRoute).
* **cert-manager** >= 1.0 с настроенным `ClusterIssuer` (для TLS-сертификатов). Для TLS через Gateway API требуется cert-manager >= 1.5 с `--feature-gates=ExperimentalGatewayAPISupport=true`.
* **metrics-server** >= 0.5 (необходим для автомасштабирования HPA).
* **cluster-autoscaler** (опционально, для автоматического масштабирования узлов).
* **KEDA** >= 2.0 (опционально, альтернатива стандартному HPA; чарт использует API ScaledObject `keda.sh/v1alpha1`).
* **Prometheus Operator** (kube-prometheus-stack) >= 0.44 (опционально, для поддержки ServiceMonitor `monitoring.coreos.com/v1`).

## Подготовка

{% stepper %}
{% step %}

## Создайте namespace

`kubectl create namespace api-instant`
{% endstep %}

{% step %}

## Создайте секрет для загрузки образа

{% code expandable="true" %}

```bash
kubectl -n api-instant create secret docker-registry oz-dockerhub-creds \
  --docker-server=docker.io \
  --docker-username=<USERNAME> \
  --docker-password='<PASSWORD>' \
  --docker-email=<EMAIL>
```

{% endcode %}

Либо, если у вас уже есть предварительно закодированная строка `.dockerconfigjson` в base64 (например, предоставленная поддержкой Oz), используйте один из вариантов:

{% code title="Вариант A: декодирование в командной строке (Linux/macOS)" expandable="true" %}

```bash
kubectl -n api-instant create secret generic oz-dockerhub-creds \
  --type=kubernetes.io/dockerconfigjson \
  --from-literal=.dockerconfigjson="$(echo '<BASE64_ENCODED_DOCKERCONFIGJSON>' | base64 -d)"
```

{% endcode %}

{% code title="Вариант B: сначала сохранить в файл, затем создать из файла" expandable="true" %}

```bash
echo '<BASE64_ENCODED_DOCKERCONFIGJSON>' | base64 -d > dockerconfig.json
kubectl -n api-instant create secret generic oz-dockerhub-creds \
  --type=kubernetes.io/dockerconfigjson \
  --from-file=.dockerconfigjson=dockerconfig.json
rm dockerconfig.json
```

{% endcode %}

{% code title="Вариант C: применить манифест Secret со строкой base64 в поле data" expandable="true" %}

```bash
apiVersion: v1
kind: Secret
metadata:
  name: oz-dockerhub-creds
  namespace: api-instant
type: kubernetes.io/dockerconfigjson
data:
  .dockerconfigjson: <BASE64_ENCODED_DOCKERCONFIGJSON>
```

{% endcode %}

Затем:

{% code expandable="true" %}

```bash
kubectl apply -f secret.yaml
```

{% endcode %}

{% hint style="info" %}
Значение под `.dockerconfigjson` — это строка JSON в кодировке base64 в формате `{"auths":{"<https://index.docker.io/v1/>":{"username":"...","password":"...","auth":"..."}}}`. Учетные данные можно получить в поддержке Oz. Имя секрета должно совпадать с `imagePull.secret` в вашем файле values (по умолчанию: `oz-dockerhub-creds`).
{% endhint %}
{% endstep %}

{% step %}

## Получите лицензионный ключ

Обратитесь в [поддержку Oz](mailto:info@ozforensics.com), чтобы получить лицензионный ключ.
{% endstep %}

{% step %}

## Создайте секрет JWT (опционально)

При использовании OzCapsula для Instant API требуется пара ключей JWT для session token. Сгенерируйте ключи и создайте секрет:

{% code expandable="true" %}

```bash
openssl ecparam -name secp384r1 -genkey -noout | openssl pkcs8 -topk8 -nocrypt -out jwt.key
openssl ec -in jwt.key -pubout -out jwt.pub

kubectl -n api-instant create secret generic api-instant-jwt \
  --from-file=jwt.pub=./jwt.pub \
  --from-file=jwt.key=./jwt.key
```

{% endcode %}

{% hint style="info" %}
Имя секрета должно совпадать с `api.jwt.secretName` в вашем файле values (по умолчанию: `""`).
{% endhint %}
{% endstep %}
{% endstepper %}

## Установка

{% stepper %}
{% step %}

## Добавьте репозиторий Helm

{% code expandable="true" %}

```bash
helm repo add ozchartmuseum https://chartmuseum.infra.ozforensics.ai helm repo update
```

{% endcode %}
{% endstep %}

{% step %}

## Примените базовую конфигурацию

Создайте файл `my-values.yaml`:

{% code title="my-values.yaml" expandable="true" %}

```yaml
api:
  hostname: api-instant.example.com
  licenseKey: "YOUR_LICENSE_KEY"
  image:
    repository: ozforensics/oz-api-instant
    tag: "6.6-ask-oz-support-team"
```

{% endcode %}

Установите чарт:

{% code expandable="true" %}

```bash
helm install oz-api-instant ozchartmuseum/oz-api-instant \
  -n api-instant \
  -f my-values.yaml
```

{% endcode %}
{% endstep %}

{% step %}

## Проверьте установку

Чтобы убедиться, что API запущен и отвечает, вызовите `GET {{host}}/api/healthcheck`.

Чтобы подтвердить, что он обрабатывает запросы на верификацию, выполните анализ через развернутый Instant API.
{% endstep %}
{% endstepper %}

## Обновление

{% code expandable="true" %}

```bash
helm repo update
helm upgrade oz-api-instant ozchartmuseum/oz-api-instant \
  -n api-instant \
  -f my-values.yaml
```

{% endcode %}

## Основные параметры конфигурации

Задать необходимо только параметры, отмеченные как **Обязательный** (они присутствуют в разделе [Базовая конфигурация](https://doc.ozforensics.com/oz-knowledge-ru/rukovodstva/rukovodstvo-administratora/pages/5c805539097099ad9e142faab6063e9a7a345e5d#id-2.-apply-basic-configuration) выше). Все остальные параметры опциональны — достаточно знать об их существовании и при необходимости переопределять их.

<table data-header-hidden="false" data-header-sticky><thead><tr><th>Параметр</th><th>Описание</th><th>По умолчанию</th></tr></thead><tbody><tr><td><code>imagePull.secret</code></td><td>Имя секрета для загрузки образа</td><td><code>oz-dockerhub-creds</code></td></tr><tr><td><code>imagePull.policy</code></td><td>Политика загрузки образа</td><td><code>IfNotPresent</code></td></tr><tr><td><code>affinity</code></td><td>Глобальные правила affinity для подов (nodeAffinity, podAffinity, podAntiAffinity)</td><td><code>{}</code></td></tr><tr><td><code>nodeSelector</code></td><td>Глобальные ограничения nodeSelector</td><td><code>{}</code></td></tr><tr><td><code>tolerations</code></td><td>Глобальные tolerations</td><td><code>[]</code></td></tr><tr><td><code>api.hostname</code></td><td><strong>Обязательный.</strong> Публичное имя хоста для Instant API</td><td><code>somehost.example.local</code></td></tr><tr><td><code>api.additionalHostnames</code></td><td>Дополнительные имена хостов (например, псевдонимы CDN); добавляются в правила Ingress и слушатели Gateway</td><td><code>[]</code></td></tr><tr><td><code>api.licenseKey</code></td><td><strong>Обязательный.</strong> Лицензионный ключ для Instant API (от поддержки Oz)</td><td><code>""</code></td></tr><tr><td><code>api.extraEnvs</code></td><td>Дополнительные переменные окружения (пары «ключ-значение»). Хранятся в ConfigMap — не помещайте сюда секреты.</td><td><code>{NUM_WORKERS: "16", ...}</code></td></tr><tr><td><code>api.extraEnvVarsSecret</code></td><td>Имя существующего Secret, ключи которого внедряются как переменные окружения (через <code>envFrom.secretRef</code>). Используйте для конфиденциальных значений, таких как учетные данные S3.</td><td><code>""</code></td></tr><tr><td><code>api.image.tag</code></td><td><strong>Обязательный.</strong> Тег образа приложения (уточните в поддержке Oz)</td><td><code>""</code></td></tr><tr><td><code>api.resources</code></td><td>Запросы и лимиты CPU и памяти</td><td>requests/limits: 15 CPU, 28Gi RAM</td></tr><tr><td><code>api.autoscaling.hpa.enabled</code></td><td>Включить HPA</td><td><code>true</code></td></tr><tr><td><code>api.autoscaling.keda.enabled</code></td><td>Включить ScaledObject KEDA</td><td><code>false</code></td></tr><tr><td><code>api.ingress.enabled</code></td><td>Создать ресурс Ingress</td><td><code>true</code></td></tr><tr><td><code>api.ingress.className</code></td><td>Класс Ingress</td><td><code>nginx</code></td></tr><tr><td><code>api.ingress.annotations</code></td><td>Аннотации Ingress (proxy, CORS и т. д.)</td><td><code>{}</code></td></tr><tr><td><code>api.ingress.tls.enabled</code></td><td>Включить TLS</td><td><code>true</code></td></tr><tr><td><code>api.ingress.tls.clusterIssuer</code></td><td>ClusterIssuer cert-manager</td><td><code>letsencrypt-production</code></td></tr><tr><td><code>api.gateway.enabled</code></td><td>Создать ресурсы Gateway API (взаимоисключающе с <code>api.ingress</code>)</td><td><code>false</code></td></tr><tr><td><code>api.gateway.className</code></td><td>Имя класса Gateway</td><td><code>istio</code></td></tr><tr><td><code>api.gateway.annotations</code></td><td>Аннотации Gateway</td><td><code>{}</code></td></tr><tr><td><code>api.gateway.tls.enabled</code></td><td>Включить TLS на Gateway</td><td><code>true</code></td></tr><tr><td><code>api.gateway.tls.clusterIssuer</code></td><td>ClusterIssuer cert-manager для Gateway</td><td><code>letsencrypt-production-dns</code></td></tr><tr><td><code>api.proxy.enabled</code></td><td>Включить HTTP-прокси</td><td><code>false</code></td></tr><tr><td><code>api.proxy.url</code></td><td>URL прокси</td><td><code>&#x3C;http://proxy.local:3128></code></td></tr><tr><td><code>api.proxy.auth.existingSecret</code></td><td>Имя существующего Secret с ключом <code>all_proxy</code>, содержащим полный URL прокси. Если задано, <code>login</code>/<code>password</code> игнорируются.</td><td><code>""</code></td></tr><tr><td><code>api.proxy.auth.login</code></td><td>Имя пользователя прокси (используется, когда <code>existingSecret</code> не задан)</td><td><code>""</code></td></tr><tr><td><code>api.proxy.auth.password</code></td><td>Пароль прокси — специальные символы автоматически кодируются в percent-encoding (используется, когда <code>existingSecret</code> не задан)</td><td><code>""</code></td></tr><tr><td><code>api.serviceMonitor.enabled</code></td><td>Создать ServiceMonitor для Prometheus</td><td><code>true</code></td></tr></tbody></table>

Полный список параметров см. в `values.yaml`.

## Управление трафиком

Этот чарт поддерживает два способа маршрутизации внешнего трафика к Instant API: **K8s Ingress API** и **K8s Gateway API**. Они взаимоисключающие — включайте только один из них.

{% hint style="warning" %}
**Важно:** Любую конфигурацию управления трафиком (балансировка нагрузки, повторные попытки, пул соединений, CORS и т. д.) следует проверять нагрузочным тестированием, отражающим выбранную настройку и реальный профиль трафика клиента.
{% endhint %}

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

### Вариант 1: K8s Ingress API (по умолчанию)

Задайте `api.ingress.enabled: true` (по умолчанию) и `api.gateway.enabled: false`.

Чарт создает ресурс `Ingress`. В кластере должен быть установлен Ingress-контроллер. Задайте `api.ingress.className` в соответствии с IngressClass вашего контроллера.

`api.ingress.annotations` по умолчанию пуст. Добавьте аннотации, необходимые вашему контроллеру.

#### **Контроллер Ingress-NGINX — пример конфигурации**

{% hint style="info" %}
**Примечание:** Проект [Ingress-NGINX](https://github.com/kubernetes/ingress-nginx) [выводится из эксплуатации](https://github.com/kubernetes/ingress-nginx?tab=readme-ov-file#retiring). Рекомендуем перейти на F5 NGINX Ingress Controller.
{% endhint %}

{% code title="Ingress-NGINX" expandable="true" %}

```yaml
api:
  ingress:
    className: nginx
    annotations:
      nginx.ingress.kubernetes.io/proxy-body-size: "150m"
      nginx.ingress.kubernetes.io/load-balance: "least_conn"
      nginx.ingress.kubernetes.io/upstream-keepalive-connections: "0"
      nginx.ingress.kubernetes.io/proxy-next-upstream: "error timeout http_500"
      nginx.ingress.kubernetes.io/proxy-next-upstream-timeout: "10"
      nginx.ingress.kubernetes.io/proxy-next-upstream-tries: "2"
      nginx.ingress.kubernetes.io/enable-cors: "true"
      nginx.ingress.kubernetes.io/cors-allow-origin: "*"
      nginx.ingress.kubernetes.io/cors-allow-methods: "GET, POST, OPTIONS, PATCH, DELETE"
      nginx.ingress.kubernetes.io/cors-allow-headers: "DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,x-forensic-access-token"
```

{% endcode %}

#### **Контроллер F5 NGINX Ingress — пример конфигурации**

{% code title="F5 NGINX" expandable="true" %}

```yaml
api:
  ingress:
    className: f5-nginx
    annotations:
      nginx.org/client-max-body-size: "150m"
      nginx.org/lb-method: "least_conn"
      nginx.org/keepalive: "0"
      nginx.org/proxy-next-upstream: "error timeout http_500"
      nginx.org/proxy-next-upstream-timeout: "10"
      nginx.org/proxy-next-upstream-tries: "2"
      nginx.org/location-snippets: |
        add_header Access-Control-Allow-Origin "*" always;
        add_header Access-Control-Allow-Methods "GET, POST, OPTIONS, PATCH, DELETE" always;
        add_header Access-Control-Allow-Headers "DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,x-forensic-access-token" always;
```

{% endcode %}

{% hint style="info" %}
У F5 NGINX Ingress Controller нет отдельных аннотаций CORS. Вместо них используйте `nginx.org/location-snippets` с директивами `add_header`.

См. [документацию по аннотациям F5 NGINX IC](https://docs.nginx.com/nginx-ingress-controller/configuration/ingress-resources/advanced-configuration-with-annotations/) и [руководство по миграции с ingress-nginx](https://docs.nginx.com/nginx-ingress-controller/install/migrate-ingress-nginx/).
{% endhint %}

#### **Другие Ingress-контроллеры**

Подойдет любой контроллер, реализующий Kubernetes Ingress API. Задайте `api.ingress.className` в соответствии с IngressClass вашего контроллера и укажите нужные аннотации.

### Вариант 2: Gateway API

Задайте `api.ingress.enabled: false` и `api.gateway.enabled: true`.

Чарт создает ресурсы `Gateway` и `HTTPRoute`. Вам нужен контроллер, реализующий [Kubernetes Gateway API](https://gateway-api.sigs.k8s.io/) (например, Istio, Envoy Gateway, Cilium). Задайте `api.gateway.className` в соответствии с вашим GatewayClass.

#### **Istio — пример конфигурации**

**Предварительные требования:**

* Istio >= 1.20 с установленными [Gateway API CRD](https://gateway-api.sigs.k8s.io/)
* cert-manager >= 1.5 с `--feature-gates=ExperimentalGatewayAPISupport=true`

{% code title="Istio" expandable="true" %}

```yaml
api:
  ingress:
    enabled: false
  gateway:
    enabled: true
    className: istio
    tls:
      clusterIssuer: letsencrypt-production-dns
      secretName: api-gateway-tls-certs
    annotations:
      # Cloud load balancer annotations (AWS NLB example)
      service.beta.kubernetes.io/aws-load-balancer-attributes: load_balancing.cross_zone.enabled=true
      service.beta.kubernetes.io/aws-load-balancer-proxy-protocol: '*'
      service.beta.kubernetes.io/aws-load-balancer-scheme: internet-facing
```

{% endcode %}

Чарт создает ресурсы `Gateway` и `HTTPRoute`. Политики трафика, такие как балансировка нагрузки, повторные попытки и CORS, **не управляются этим чартом** при использовании Gateway API. Настройте их с помощью CRD Istio, применяемых вместе с релизом Helm.

<table data-header-hidden="false" data-header-sticky><thead><tr><th>Политика трафика</th><th>Ресурс Istio</th><th>Конфигурация</th></tr></thead><tbody><tr><td>Балансировка нагрузки</td><td><code>DestinationRule</code></td><td><code>loadBalancer.simple: LEAST_REQUEST</code></td></tr><tr><td>Пул соединений</td><td><code>DestinationRule</code></td><td><code>connectionPool.http.maxRequestsPerConnection</code></td></tr><tr><td>Обнаружение аномалий</td><td><code>DestinationRule</code></td><td><code>outlierDetection</code> (исключение неисправных эндпойнтов при 5xx)</td></tr><tr><td>Повторные попытки</td><td><code>EnvoyFilter</code></td><td><code>retryPolicy</code> на уровне маршрута</td></tr><tr><td>CORS</td><td><code>EnvoyFilter</code></td><td>Фильтр CORS Envoy (<code>typedPerFilterConfig</code>)</td></tr></tbody></table>

**Istio — балансировка нагрузки, пул соединений и обнаружение аномалий**

Создайте `DestinationRule`, нацеленный на сервис Instant API (замените `<release-name>` на имя вашего релиза Helm, например `oz-api-instant`):

{% code title="DestinationRule" expandable="true" %}

```yaml
apiVersion: networking.istio.io/v1
kind: DestinationRule
metadata:
  name: api-instant-traffic-policy
  namespace: api-instant
spec:
  host: <release-name>-api.api-instant.svc.cluster.local
  trafficPolicy:
    loadBalancer:
      simple: LEAST_REQUEST
    connectionPool:
      http:
        maxRequestsPerConnection: 1            # disable keepalive between gateway and upstream pods
    outlierDetection:
      consecutive5xxErrors: 1                  # eject endpoint on first 5xx
      interval: 10s
      baseEjectionTime: 30s
```

{% endcode %}

По умолчанию `LEAST_REQUEST` выбирает наименее загруженный эндпойнт из 2 случайных кандидатов. Для более равномерного распределения по подам (особенно полезно при интенсивных нагрузках Liveness-верификации) создайте `EnvoyFilter`, чтобы увеличить размер пула кандидатов:

{% code title="EnvoyFilter for candidate pool size" expandable="true" %}

```yaml
apiVersion: networking.istio.io/v1alpha3
kind: EnvoyFilter
metadata:
  name: api-instant-least-request-lb-config
  namespace: api-instant
spec:
  workloadSelector:
    labels:
      gateway.networking.k8s.io/gateway-name: <release-name>-gateway-api
  configPatches:
  - applyTo: CLUSTER
    match:
      context: GATEWAY
      cluster:
        name: outbound|8080||<release-name>-api.api-instant.svc.cluster.local
    patch:
      operation: MERGE
      value:
        lb_policy: LEAST_REQUEST
        least_request_lb_config:
          choice_count: 5
```

{% endcode %}

{% hint style="info" %}
Более высокие значения `choice_count` улучшают распределение нагрузки, но добавляют небольшие накладные расходы на каждый запрос. Значение 5 — хорошая отправная точка для кластеров с 5 и более подами.
{% endhint %}

В качестве альтернативы в **Istio >= 1.22** (Envoy >= 1.30) можно использовать метод выбора `FULL_SCAN`, который сканирует **все** эндпойнты и выбирает тот, у которого меньше всего активных запросов (ничьи разрешаются через reservoir sampling). Это гарантирует оптимальный выбор, но обходится в O(n) на запрос, поэтому лучше всего подходит для развертываний с небольшим числом подов или низким RPS:

{% code title="EnvoyFilter with FULL\_SCAN" expandable="true" %}

```yaml
apiVersion: networking.istio.io/v1alpha3
kind: EnvoyFilter
metadata:
  name: api-instant-least-request-lb-full-scan
  namespace: api-instant
spec:
  workloadSelector:
    labels:
      gateway.networking.k8s.io/gateway-name: <release-name>-gateway-api
  configPatches:
  - applyTo: CLUSTER
    match:
      context: GATEWAY
      cluster:
        name: outbound|8080||<release-name>-api.api-instant.svc.cluster.local
    patch:
      operation: MERGE
      value:
        load_balancing_policy:
          policies:
          - typed_extension_config:
              name: envoy.load_balancing_policies.least_request
              typed_config:
                "@type": type.googleapis.com/envoy.extensions.load_balancing_policies.least_request.v3.LeastRequest
                selection_method: FULL_SCAN
```

{% endcode %}

{% hint style="info" %}
Когда задано `load_balancing_policy`, оно имеет приоритет над устаревшими полями `lb_policy` / `least_request_lb_config`. Используйте только один из подходов, но не оба.
{% endhint %}

**Istio — повторные попытки**

Создайте `EnvoyFilter`, чтобы добавить политику повторных попыток на уровне маршрута:

{% code title="EnvoyFilter for retry policy" expandable="true" %}

```yaml
apiVersion: networking.istio.io/v1alpha3
kind: EnvoyFilter
metadata:
  name: api-instant-retries
  namespace: api-instant
spec:
  workloadSelector:
    labels:
      gateway.networking.k8s.io/gateway-name: <release-name>-gateway-api
  configPatches:
  - applyTo: HTTP_ROUTE
    match:
      context: GATEWAY
    patch:
      operation: MERGE
      value:
        route:
          retryPolicy:
            retryOn: "connect-failure,reset,5xx"
            numRetries: 2
```

{% endcode %}

**Istio — CORS**

Создайте `EnvoyFilter`, чтобы настроить заголовки CORS:

{% code title="EnvoyFilter for CORS" expandable="true" %}

```yaml
apiVersion: networking.istio.io/v1alpha3
kind: EnvoyFilter
metadata:
  name: api-instant-cors
  namespace: api-instant
spec:
  workloadSelector:
    labels:
      gateway.networking.k8s.io/gateway-name: <release-name>-gateway-api
  configPatches:
  - applyTo: HTTP_ROUTE
    match:
      context: GATEWAY
    patch:
      operation: MERGE
      value:
        typedPerFilterConfig:
          envoy.filters.http.cors:
            "@type": type.googleapis.com/envoy.extensions.filters.http.cors.v3.CorsPolicy
            filterEnabled:
              defaultValue:
                numerator: 100
                denominator: HUNDRED
            allowOriginStringMatch:
            - safeRegex:
                regex: ".*"
            allowMethods: "GET, POST, OPTIONS, PATCH, DELETE"
            allowHeaders: "DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,x-forensic-access-token"
```

{% endcode %}

{% hint style="info" %}
Замените `<release-name>` на имя вашего релиза Helm (например, `oz-api-instant`). Ресурсы `DestinationRule` и `EnvoyFilter`, приведенные выше, **не входят в этот чарт** — применяйте их через `kubectl apply` после установки релиза Helm.

Точная конфигурация EnvoyFilter может отличаться в зависимости от версии Istio. Проверьте с помощью `istioctl proxy-config routes` и `istioctl proxy-config listeners`, что фильтры применены корректно.
{% endhint %}

#### **Другие контроллеры Gateway API**

Подойдет любой контроллер, реализующий Kubernetes Gateway API. Задайте `api.gateway.className` в соответствии с вашим GatewayClass и добавьте настройки, специфичные для контроллера, через `api.gateway.annotations`.

### OpenShift Route

Этот чарт не создает ресурсы OpenShift Route. Если вам нужно использовать Route, задайте `api.ingress.enabled: false` и `api.gateway.enabled: false`, а затем вручную создайте Route для порта `http-api` сервиса (`<release-name>-api`).

### TLS-сертификаты

И вариант с Ingress, и вариант с Gateway поддерживают автоматическую выдачу TLS-сертификатов через **cert-manager**. Задайте `clusterIssuer` или `issuer` под `api.ingress.tls` или `api.gateway.tls`, чтобы сослаться на ваш issuer cert-manager.

## Настройка прокси

Если поду Instant API требуется доступ в интернет через HTTP-прокси, задайте `api.proxy.enabled: true`.

Оба варианта ниже одинаково безопасны — выбирайте тот, который подходит вашей настройке.

### Вариант 1: учетные данные в конфигурации

Специальные символы в пароле автоматически кодируются чартом в percent-encoding.

{% code expandable="true" %}

```yaml
api:
  proxy:
    enabled: true
    url: "http://proxy.local:3128"
    auth:
      login: "your_login"
      password: "your_p@ss:w0rd!"
```

{% endcode %}

### Вариант 2: существующий Secret

Используйте этот вариант, когда учетные данные содержат символы, которые трудно экранировать в YAML, или когда вы хотите полностью исключить учетные данные из файлов values. Создайте Secret с полным URL прокси, закодированным заранее:

{% code expandable="true" %}

```bash
kubectl -n api-instant create secret generic oz-proxy-creds \
  --from-literal=all_proxy='http://your_login:your_p%40ss%3Aw0rd%21@proxy.local:3128'
```

{% endcode %}

Затем сошлитесь на него в values:

{% code expandable="true" %}

```yaml
api:
  proxy:
    enabled: true
    url: "http://proxy.local:3128"
    auth:
      existingSecret: oz-proxy-creds
```

{% endcode %}

{% hint style="info" %}
Когда задано `auth.existingSecret`, `auth.login` и `auth.password` игнорируются. Secret должен содержать ключ с именем `all_proxy` с полным URL прокси, включая учетные данные.
{% endhint %}

## Хранилище S3

Instant API может сохранять данные Liveness-верификации в бакете S3 или S3-совместимого хранилища вместо локального хранилища. Для включения S3 всегда требуются две переменные окружения:

* `OZ_INSTANT_SAVING_ARTIFACTS_ENABLED: "true"` (для версий API ниже 6.5: `OZ_INSTANT_SAVING_ARTIFACTS_IN_S3_ENABLED`)
* `OZ_FILE_STORAGE_TYPE: "S3"`.

Неконфиденциальные параметры S3 указываются в `api.extraEnvs`; учетные данные необходимо поместить в заранее созданный Secret, на который ссылается `api.extraEnvVarsSecret` — его ключи внедряются как переменные окружения вместе с ConfigMap.

### Вариант 1: AWS S3 с ключами доступа

Создайте Secret с учетными данными:

{% code expandable="true" %}

```bash
kubectl -n api-instant create secret generic oz-api-s3-creds \
  --from-literal=OZ_STATIC_S3_ACCESS_KEY='<YOUR_S3_ACCESS_KEY>' \
  --from-literal=OZ_STATIC_S3_SECRET_KEY='<YOUR_S3_SECRET_KEY>'
```

{% endcode %}

Сошлитесь на него в values вместе с неконфиденциальными параметрами S3:

{% code expandable="true" %}

```yaml
api:
  extraEnvVarsSecret: oz-api-s3-creds
  extraEnvs:
    OZ_INSTANT_SAVING_ARTIFACTS_IN_S3_ENABLED: "true"
    OZ_FILE_STORAGE_TYPE: "S3"
    OZ_STATIC_S3_BUCKET: "oz-bucket"
    OZ_STATIC_S3_ENDPOINT_URL: "https://s3.us-east-1.amazonaws.com/"
    OZ_STATIC_S3_REGION_NAME: "us-east-1"
    OZ_STATIC_S3_BUCKET_URL: "None"
    OZ_STATIC_S3_BASE_URL: "http://localhost/static"
    OZ_STATIC_S3_SUFFIX: ""           # root folder in bucket; empty = bucket root
```

{% endcode %}

### Вариант 2: AWS S3 с ролью IAM (EKS IRSA)

Используйте этот вариант при работе на EKS с [ролями IAM для сервисных аккаунтов](https://docs.aws.amazon.com/eks/latest/userguide/iam-roles-for-service-accounts.html). Ключи доступа не нужны — под принимает роль IAM через сервисный аккаунт.

**1. Создайте бакет S3.**

**2. Создайте OIDC Identity Provider в IAM:**

* Тип: OpenID Connect
* Audience: `sts.amazonaws.com`

**3. Создайте политику IAM:**

{% code expandable="true" %}

```json
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "ListBucket",
      "Effect": "Allow",
      "Action": "s3:ListBucket",
      "Resource": "arn:aws:s3:::YOUR_BUCKET_NAME"
    },
    {
      "Sid": "ObjectOperations",
      "Effect": "Allow",
      "Action": [
        "s3:GetObject",
        "s3:PutObject",
        "s3:DeleteObject"
      ],
      "Resource": "arn:aws:s3:::YOUR_BUCKET_NAME/*"
    }
  ]
}
```

{% endcode %}

**4. Создайте роль IAM со следующей trust-политикой, затем прикрепите политику из шага 3:**

{% code expandable="true" %}

```json
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": {
        "Federated": "<Identity provider ARN>"
      },
      "Action": "sts:AssumeRoleWithWebIdentity",
      "Condition": {
        "StringEquals": {
          "<Identity provider name>:aud": "sts.amazonaws.com"
        }
      }
    }
  ]
}
```

{% endcode %}

Пример:

{% code expandable="true" %}

```json
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": {
        "Federated": "arn:aws:iam::123456789000:oidc-provider/oidc.eks.us-east-2.amazonaws.com/id/1234567890ABCDEFGHIGHKLMOPQRSTUV"
      },
      "Action": "sts:AssumeRoleWithWebIdentity",
      "Condition": {
        "StringEquals": {
          "oidc.eks.us-somewhere-1.amazonaws.com/id/1234567890ABCDEFGHIGHKLMOPQRSTUV:aud": "sts.amazonaws.com"
        }
      }
    }
  ]
}
```

{% endcode %}

**5. Добавьте аннотацию с ARN роли к сервисному аккаунту чарта:**

{% code expandable="true" %}

```yaml
serviceAccount:
  enabled: true
  annotations:
    eks.amazonaws.com/role-arn: "arn:aws:iam::123456789000:role/<YOUR_ROLE_NAME>"
    eks.amazonaws.com/sts-regional-endpoints: "true"
```

{% endcode %}

**6. Задайте переменные окружения S3 (ключи доступа не нужны):**

{% code expandable="true" %}

```yaml
api:
  extraEnvs:
    OZ_INSTANT_SAVING_ARTIFACTS_IN_S3_ENABLED: "true"
    OZ_FILE_STORAGE_TYPE: "S3"
    OZ_STATIC_S3_BUCKET: "oz-bucket"
    OZ_STATIC_S3_ENDPOINT_URL: "https://s3.us-east-1.amazonaws.com/"
    OZ_STATIC_S3_REGION_NAME: "us-east-1"
    OZ_STATIC_S3_BUCKET_URL: "None"
    OZ_STATIC_S3_BASE_URL: "http://localhost/static"
    OZ_STATIC_S3_SUFFIX: ""
```

{% endcode %}

### Вариант 3: S3-совместимое хранилище (MinIO, NetApp StorageGRID и др.)

Проверенная совместимость: MinIO 5.4.0, NetApp StorageGRID 11.8.

{% code expandable="true" %}

```bash
kubectl -n api-instant create secret generic oz-api-s3-creds \
  --from-literal=OZ_STATIC_S3_ACCESS_KEY='<YOUR_S3_ACCESS_KEY>' \
  --from-literal=OZ_STATIC_S3_SECRET_KEY='<YOUR_S3_SECRET_KEY>'
```

{% endcode %}

{% code expandable="true" %}

```yaml
api:
  extraEnvVarsSecret: oz-api-s3-creds
  extraEnvs:
    OZ_INSTANT_SAVING_ARTIFACTS_IN_S3_ENABLED: "true"
    OZ_FILE_STORAGE_TYPE: "S3"
    OZ_STATIC_S3_BUCKET: "oz-bucket"
    OZ_STATIC_S3_ENDPOINT_URL: "http://your-s3-host:9000"   # protocol + host + port
    OZ_STATIC_S3_REGION_NAME: "us-east-1"                   # any value accepted by your storage
    OZ_STATIC_S3_BUCKET_URL: "None"
    OZ_STATIC_S3_BASE_URL: "http://localhost/static"
    OZ_STATIC_S3_SUFFIX: ""
```

{% endcode %}

## Удаление

{% code expandable="true" %}

```bash
helm uninstall oz-api-instant -n api-instant
```

{% endcode %}

{% hint style="info" %}
Это не удаляет секреты JWT и секрет для загрузки образа. При необходимости удалите их вручную.
{% endhint %}


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://doc.ozforensics.com/oz-knowledge-ru/rukovodstva/rukovodstvo-administratora/kak-razvernut-instant-api-v-kubernetes.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
