# Переход на Cilium Gateway

<p class="callout danger">Не решен вопрос с CORS</p>

Перед переходом на Cilium Gateway необходимо выполнить шаги, описанные [тут](https://kb.randmgroup.ru/books/k8s/page/perexod-s-ingress-na-cilium-gateway "Переход с Ingress на Cilium Gateway"). Здесь мы рассмотрим вариант для single node редакция Enterprise версия **2026.4.42**. Для редакции Hub все намного сложнее и будет описано позже.

Из справки [тут](https://elma365.com/ru/help/platform/cilium-gateway.html#create-gateway) создаем gateway:

```yaml
kubectl apply -n kube-system -f - <<EOF
apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
   name: cilium-gateway
spec:
   gatewayClassName: cilium
   listeners:
   - name: http
     port: 80
     protocol: HTTP
     allowedRoutes:
       namespaces:
        from: All
   - name: https
     port: 443
     protocol: HTTPS
     tls:
       mode: Terminate
       certificateRefs:
       - name: elma365-tls
     allowedRoutes:
       namespaces:
         from: All
EOF
```

<p class="callout warning">Нам не нужен 443, т.к. у нас используется reverse proxy, который выполняет все нужные активности в части HTTPS</p>

Поэтому в упрощенному случае у нас будет:

```yaml
kubectl apply -n kube-system -f - <<EOF
apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
   name: cilium-gateway
spec:
   gatewayClassName: cilium
   listeners:
   - name: http
     port: 80
     protocol: HTTP
     allowedRoutes:
       namespaces:
        from: All
EOF
```

Должны получить:

```shell
gateway.gateway.networking.k8s.io/cilium-gateway created
```

Смотрим что на самом деле:

```bash
kubectl get gateway -A
```

Видим, что появился gateway:

```shell
NAMESPACE     NAME             CLASS    ADDRESS   PROGRAMMED   AGE
kube-system   cilium-gateway   cilium             True         71s
```

Смотрим что у нас с сервисами:

```bash
kubectl get svc -n kube-system
```

Видим что появился LoadBalancer, но он еще не настроен (а еще какой то новый сервис hubble-peer):

```
NAME                                TYPE           CLUSTER-IP       EXTERNAL-IP   PORT(S)                      AGE
cilium-gateway-cilium-gateway       LoadBalancer   10.152.183.225   <pending>     80:30514/TCP                 4m13s
ck-storage-rawfile-csi-controller   ClusterIP      None             <none>        <none>                       189d
ck-storage-rawfile-csi-node         ClusterIP      10.152.183.56    <none>        9100/TCP                     189d
coredns                             ClusterIP      10.152.183.238   <none>        53/UDP,53/TCP                189d
hubble-peer                         ClusterIP      10.152.183.166   <none>        443/TCP                      189d
metrics-server                      ClusterIP      10.152.183.180   <none>        443/TCP                      189d
```

<p class="callout warning">Возможно можно как то настроить NodePort для ситуации single node, но эту гипотезу мы тут не проверяем.</p>

Так же load-balancer отключен по k8s status - его нужно включить:

```bash
k8s enable load-balancer
```

Должны получить:

```
Enabling load-balancer on the cluster. This may take a few seconds, please wait.
load-balancer enabled.

# k8s status

cluster status:           ready
control plane nodes:      192.168.56.83:6400 (voter)
high availability:        no
datastore:                etcd
network:                  enabled
dns:                      enabled at 10.152.183.238
ingress:                  disabled
load-balancer:            enabled, L2 mode
local-storage:            enabled at /var/snap/k8s/common/rawfile-storage
gateway                   enabled

# kubectl get pod,svc -n metallb-system

NAME                                      READY   STATUS    RESTARTS   AGE
pod/metallb-controller-676c8f595c-5chlt   1/1     Running   0          2m6s
pod/metallb-speaker-t2x96                 1/1     Running   0          2m6s

NAME                              TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)   AGE
service/metallb-webhook-service   ClusterIP   10.152.183.39   <none>        443/TCP   2m7s
```

Неоходимо для нашего LoadBalancer назначить пул IP адресов (в нашем случае у нас single node и поэтому укажем только один):

<p class="callout warning">Использовать IP нашего узла НЕЛЬЗЯ. Необходимо указать свободный адрес в вашей сети. (а может и можно)!</p>

```bash
k8s set load-balancer.cidrs=192.168.56.73-192.168.56.73
```

Получилось:

```bash
Configuration updated.

# kubectl get svc -n kube-system

NAME                                TYPE           CLUSTER-IP       EXTERNAL-IP     PORT(S)         AGE
cilium-gateway-cilium-gateway       LoadBalancer   10.152.183.225   192.168.56.73   80:30514/TCP    86m
ck-storage-rawfile-csi-controller   ClusterIP      None             <none>          <none>          189d
ck-storage-rawfile-csi-node         ClusterIP      10.152.183.56    <none>          9100/TCP        189d
coredns                             ClusterIP      10.152.183.238   <none>          53/UDP,53/TCP   189d
hubble-peer                         ClusterIP      10.152.183.166   <none>          443/TCP         189d
metrics-server                      ClusterIP      10.152.183.180   <none>          443/TCP         189d
```

Теперь переходим непосредственно к настройке ELMA365. Правим наш values-elma365.yaml. Отключаем Ingress и [включаем](https://elma365.com/ru/help/platform/cilium-gateway.html#gateway-api-support) Gateway API:

```
global:

  gatewayAPI:
    enabled: true
    parentRefs:
      - name: cilium-gateway
        namespace: kube-system

  ingress:
    enabled: false
```

Устанавливаем (обновляем) чарт ELMA365 как это мы делаем стандартно (у каждого свой подход), но суть - применить наши изменения.

<p class="callout danger">И тут начались танцы с бубном....</p>

```bash
helm upgrade --install elma365 ./elma365 -f values-elma365.yaml --timeout=30m --debug --wait
```

Получили (причем тут istio вообще?):

```shell
level=DEBUG msg="number of dependencies in the chart" chart=messenger-vkteamsbot dependencies=0
level=DEBUG msg="number of dependencies in the chart" chart=messenger-whatsapp dependencies=0
level=DEBUG msg="number of dependencies in the chart" chart=postman dependencies=0
Error: unable to build kubernetes objects from release manifest: [resource mapping not found for name: "balancer-auth" namespace: "" from "": no matches for kind "AuthorizationPolicy" in version "security.istio.io/v1"
ensure CRDs are installed first, resource mapping not found for name: "main-body-size-limit" namespace: "istio-system" from "": no matches for kind "EnvoyFilter" in version "networking.istio.io/v1alpha3"
ensure CRDs are installed first]
```

Изучив ~/elma365/charts/balancer/values.yaml было обнаружено интересное значение workloadSelector, про которое ничего не сказано в оффициальной справке:

```
global:
  gatewayAPI:
    enabled: false
    parentRefs:
      - name: main-gateway
        namespace: istio-system
    workloadSelector:
      gateway.istio.io/managed: istio.io-gateway-controller
    annotations: {}
    hosts: []
```

Заменили значение на

Смотрим ~/elma365/charts/balancer/templates/httproute.yaml а там захаркодено если включен gatewayAPI:

```
---
apiVersion: security.istio.io/v1
kind: AuthorizationPolicy
metadata:
  name: {{ template "balancer.name" . }}-auth
  labels:
    {{- include "balancer.labels" . | nindent 4 }}
spec:
  selector:
    matchLabels:
      app: {{ template "balancer.name" . }}
  action: DENY
  rules:
    - to:
        - operation:
            ports: ["{{ $servicePort }}"]
            paths: ["/balancer*"]
      when:
        - key: request.headers[authorization]
          values: ["*"]
```

Пробуем удалить (далее сделаем патч к дистрибутиву и пробуем установить снова ELMA365 - эта проблема ушла но осталась вторая:

```shell
level=DEBUG msg="number of dependencies in the chart" chart=textextractor dependencies=0
level=DEBUG msg="number of dependencies in the chart" chart=widget dependencies=0
Error: unable to build kubernetes objects from release manifest: resource mapping not found for name: "main-body-size-limit" namespace: "istio-system" from "": no matches for kind "EnvoyFilter" in version "networking.istio.io/v1alpha3"
ensure CRDs are installed first
```

Пробуем добавить отключение envoyFilter в нашей конфигурации, ведь мы никакой Envoy не устанавливали:

```
  gatewayAPI:
    enabled: true
    # annotations: {}
    parentRefs:
      - name: cilium-gateway
        namespace: kube-system
    # timeoutSeconds: "300s"
    # retries:
    #   attempts: 3
    envoyFilter:
      enabled: false
    #   namespace: "istio-system"
```

<p class="callout success">Установка пошла дальше! Ура! Ждем результат...</p>

Все поднялось без ошибок. Смотрим какие у нас появились ресурсы HTTPRoute:

```bash
kubectl get httproute -A
```

Получили что то похожее:

```shell
NAMESPACE   NAME             HOSTNAMES         AGE
default     balancer         ["d.rmg365.ru"]   9m33s
default     fileprotection   ["d.rmg365.ru"]   9m33s
default     front            ["d.rmg365.ru"]   9m33s
default     hydra-adaptor    ["d.rmg365.ru"]   9m33s
default     main             ["d.rmg365.ru"]   9m33s
default     main-publicapi   ["d.rmg365.ru"]   9m33s
default     notifier         ["d.rmg365.ru"]   9m33s
default     notigate         ["d.rmg365.ru"]   9m33s
default     vahter           ["d.rmg365.ru"]   9m33s
default     web-forms        ["d.rmg365.ru"]   9m33s
```

Перенастроили reverse proxy на наш новый адрес, что мы указали ранее для LB и вуаля:

[![image.png](https://kb.randmgroup.ru/uploads/images/gallery/2026-08/scaled-1680-/oZjlD7GJKBV2Y3Jw-image.png)](https://kb.randmgroup.ru/uploads/images/gallery/2026-08/oZjlD7GJKBV2Y3Jw-image.png)

Давайте попробуем все же обмануть LB и указать ему адрес хоста, чтобы не плодить у себя айпишники:

```
k8s set load-balancer.cidrs=192.168.56.83-192.168.56.83
```

Вроде не ругнулось даже:

```
Configuration updated.

# kubectl get svc -n kube-system
NAME                                TYPE           CLUSTER-IP       EXTERNAL-IP     PORT(S)         AGE
cilium-gateway-cilium-gateway       LoadBalancer   10.152.183.225   192.168.56.83   80:30514/TCP    158m
ck-storage-rawfile-csi-controller   ClusterIP      None             <none>          <none>          190d
ck-storage-rawfile-csi-node         ClusterIP      10.152.183.56    <none>          9100/TCP        190d
coredns                             ClusterIP      10.152.183.238   <none>          53/UDP,53/TCP   190d
hubble-peer                         ClusterIP      10.152.183.166   <none>          443/TCP         190d
metrics-server                      ClusterIP      10.152.183.180   <none>          443/TCP         190d
```

<p class="callout success">Эксперимент прошел удачно и мы вернули настройки reverse proxy как было в случае Ingress.</p>

Ну вот собственно и патч для 2026.4 (актуально для 2026.4.42, покка не исплавили) - balancer-httproute.patch.2026.4

```diff
--- elma365/charts/balancer/templates/httproute.yaml	2026-07-31 09:06:50.000000000 +0300
+++ ./httproute.yaml	2026-08-02 14:13:28.294230220 +0300
@@ -43,24 +43,4 @@
         - name: {{ $serviceName }}
           port: {{ $servicePort }}
 {{- end }}
----
-apiVersion: security.istio.io/v1
-kind: AuthorizationPolicy
-metadata:
-  name: {{ template "balancer.name" . }}-auth
-  labels:
-    {{- include "balancer.labels" . | nindent 4 }}
-spec:
-  selector:
-    matchLabels:
-      app: {{ template "balancer.name" . }}
-  action: DENY
-  rules:
-    - to:
-        - operation:
-            ports: ["{{ $servicePort }}"]
-            paths: ["/balancer*"]
-      when:
-        - key: request.headers[authorization]
-          values: ["*"]
 {{- end }}
```

После загрузки нового дистро применяем патч:

```bash
patch elma365/charts/balancer/templates/httproute.yaml < balancer-httproute.patch.2026.4
```