Перейти к основному содержимому

Kubernetes-оператор

Оператор публикует Service кластера через туннели Tuna. Туннель, домен или TCP-порт описывается ресурсом Kubernetes, а оператор создаёт его в аккаунте Tuna и держит подключение. Кластер сам подключается к Tuna исходящими соединениями, поэтому LoadBalancer, публичный IP и открытые входящие порты не нужны.

Не путайте с tuna kubernetes

Команда tuna kubernetes даёт временный доступ к Kubernetes API кластера. Оператор решает другую задачу: публикует приложения, которые работают в кластере.

Ресурсы​

РесурсЧто делаетКогда нужен
TunnelHTTP- или TCP-туннель к одному Serviceопубликовать одно приложение или базу данных
Ingress с ingressClassName: tunaмаршруты по путям к нескольким Service за одним доменом/api ведёт в один Service, / — в другой
Domainпостоянный поддомен или свой домен в аккаунтеадрес не должен меняться; хост для Ingress
TCPEndpointзарезервированный TCP-порт с алиасомпостоянный порт для TCP-туннеля

Tunnel, Domain и TCPEndpoint — ресурсы оператора из группы k8s.tuna.am/v1alpha1. Ingress — стандартный ресурс Kubernetes, оператор обслуживает его как Ingress-контроллер.

Что понадобится​

  • Kubernetes 1.32 или новее;
  • Nelm (рекомендуем) или Helm 4;
  • API-ключ с записью в «Эндпоинты и домены» — как его создать, описано ниже.

API-ключ для оператора​

Ключ нужен оператору, чтобы резервировать домены и TCP-порты и выпускать временные токены для туннелей. Всё это — продукт «Эндпоинты и домены», поэтому ключу достаточно права записи в него (скоуп tunnels:write).

  1. Откройте API-ключи в личном кабинете и создайте новый ключ.
  2. В блоке «Для чего ключ» выберите «Для API».
  3. В поле «Описание» укажите кластер, например k8s-operator prod. Заведите отдельный ключ на каждый кластер: тогда доступ одного кластера можно отозвать, не трогая остальные.
  4. В поле «Срок действия» выберите «Не ограничено». Набор «Для API» по умолчанию ставит 30 дней, и когда срок истечёт, оператор потеряет доступ к API. Если политика безопасности требует ограниченного срока, оставьте его, но запланируйте замену ключа.
  5. В блоке «Доступ к API» выберите «Выборочно» и для «Эндпоинты и домены» нажмите «Запись». У остальных продуктов оставьте «Нет».
  6. Создайте ключ и скопируйте его.

Переключатель «Запуск туннелей» включать не нужно. Оператор не подключает туннели своим ключом: для каждого туннеля он выпускает отдельный временный токен.

Ключ с полным доступом к API тоже подойдёт, но откроет оператору мониторинги, секреты и настройки команды, которые ему не нужны.

Установка​

Чарт tuna-k8s-operator лежит в реестре пакетов code.tuna.am. В примерах оператор ставится в namespace tuna-system, а ключ передаётся параметром credentials.apiKey. Другие способы передать ключ — в разделе Где хранить ключ.

Nelm сначала показывает план изменений, а потом применяет его:

export NELM_FEAT_REMOTE_CHARTS=true
nelm repo add tuna https://code.tuna.am/api/packages/tuna/helm
nelm release plan install -n tuna-system -r tuna-operator tuna/tuna-k8s-operator \
--set credentials.apiKey=<API-ключ>
nelm release install -n tuna-system -r tuna-operator tuna/tuna-k8s-operator \
--set credentials.apiKey=<API-ключ>

Проверьте, что оператор запустился:

kubectl get pods -n tuna-system

В namespace работают два пода: …-agent-manager обслуживает Tunnel и Ingress, …-api-manager — Domain и TCPEndpoint.

Где хранить ключ​

ПараметрКак работаетКогда выбрать
credentials.apiKeyчарт сам создаёт Secret с ключомпробный запуск, тестовый кластер
credentials.existingSecretимя вашего Secret с полем api-keyключ приходит из External Secrets Operator, Sealed Secrets или создан вручную
extraEnvпеременная TUNA_API_KEY от инжектора секретовSecret в Kubernetes не нужен, ключ берётся прямо из хранилища

Пример extraEnv для Bank-Vaults:

podAnnotations:
vault.security.banzaicloud.io/vault-addr: https://vault.vault.svc:8200
vault.security.banzaicloud.io/vault-role: tuna-operator
extraEnv:
- name: TUNA_API_KEY
value: vault:secret/data/tuna#TUNA_API_KEY

Параметры чарта​

Все параметры с описаниями покажет helm show values tuna/tuna-k8s-operator. Чаще всего нужны эти:

ПараметрЧто задаёт
watchNamespaceследить только за одним namespace; по умолчанию оператор следит за всеми
ingressClass.defaultсделать класс tuna классом по умолчанию для Ingress без ingressClassName
agentManager.allowExternalUpstreamsразрешить публиковать адреса вне namespace ресурса, см. Безопасность
apiManager.replicasчисло реплик api-manager; активна одна, остальные в резерве

Туннель к Service​

apiVersion: k8s.tuna.am/v1alpha1
kind: Tunnel
metadata:
name: web
spec:
protocol: http
subdomain: my-app
upstream:
url: http://web:8080
  • protocol — http или tcp;
  • subdomain — поддомен на базовом домене Tuna. Вместо него можно указать domain — домен, зарезервированный в аккаунте;
  • upstream.url — Service из того же namespace и его порт.

Публичный адрес появится в колонке URL:

kubectl get tunnel web

Свой ключ для туннеля​

По умолчанию туннель подключается временным токеном, который оператор выпускает своим ключом. Если туннель должен работать под другим ключом, например под ключом команды с её лимитами, положите ключ в Secret того же namespace. Такому ключу нужно только право на запуск туннелей (tunnels:connect): при создании выберите набор «Для туннелей».

kubectl create secret generic team-tuna-api-key --from-literal=api-key=<API-ключ>
spec:
apiKeySecretRef:
name: team-tuna-api-key

Ingress с маршрутами по путям​

Хостом Ingress может быть только зарезервированный домен: ресурс Domain или домен из личного кабинета. Полное имя домена покажет kubectl get domain my-app.

apiVersion: k8s.tuna.am/v1alpha1
kind: Domain
metadata:
name: my-app
spec:
subdomain: my-app
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: my-app
spec:
ingressClassName: tuna
rules:
- host: my-app.a.2na.am
http:
paths:
- path: /api
pathType: Prefix
backend:
service:
name: api
port:
number: 8080
- path: /
pathType: Prefix
backend:
service:
name: web
port:
name: http

Как это работает:

  • на каждый хост создаётся один туннель;
  • изменения путей и бэкендов применяются без переподключения;
  • префиксы сравниваются по сегментам пути: /api подходит для /api/users, но не для /apix;
  • бэкенд получает публичный заголовок Host, а также X-Forwarded-Host и X-Forwarded-Proto.

Ограничения:

  • бэкендом может быть только Service;
  • правила без хоста и wildcard-хосты пропускаются;
  • несколько Ingress на один хост не согласуются между собой.

Свой домен​

apiVersion: k8s.tuna.am/v1alpha1
kind: Domain
metadata:
name: api-example-com
spec:
domain: api.example.com
reclaimPolicy: Retain

DNS-записи, которые нужно создать у DNS-провайдера, появятся в status.dnsRecords:

kubectl describe domain api-example-com

Если проверка DNS не прошла, оператор повторяет её раз в 5 минут. Переключить существующий ресурс между subdomain и domain нельзя — создайте новый. Что делает reclaimPolicy, описано в разделе Существующие резервации.

TCP-порт​

TCPEndpoint резервирует порт, а Tunnel с protocol: tcp публикует через него Service:

apiVersion: k8s.tuna.am/v1alpha1
kind: TCPEndpoint
metadata:
name: postgres
spec:
alias: postgres
---
apiVersion: k8s.tuna.am/v1alpha1
kind: Tunnel
metadata:
name: postgres
spec:
protocol: tcp
portOrAlias: postgres
upstream:
url: tcp://postgres:5432

Хост и номер порта покажет kubectl get tcpendpoint postgres.

Справка по всем полям — kubectl explain tunnel.spec, kubectl explain domain.spec и kubectl explain tcpendpoint.spec. Другие примеры лежат в каталоге examples/ чарта.

Существующие резервации​

Если домен или алиас уже есть в аккаунте — создан в кабинете или другим кластером, — Domain и TCPEndpoint подхватывают эту резервацию и не падают с ошибкой. У такого ресурса status.adopted: true, и оператор никогда не удалит его резервацию.

Для остальных ресурсов действует reclaimPolicy:

  • Delete (по умолчанию) — освободить резервацию при удалении ресурса;
  • Retain — сохранить резервацию в аккаунте.

Если резервацию удалили в кабинете, оператор создаст её снова.

Состояние и ошибки​

Состояние каждого ресурса — в условии Ready и в событиях Kubernetes:

kubectl describe tunnel web

У Ingress условий нет, смотрите его события: TunnelConnected, TunnelStopped, RoutesUpdated, RuleSkipped, BackendSkipped.

Безопасность​

  • Только свой namespace. Tunnel и Ingress публикуют только Service своего namespace. IP-адреса, внешние хосты, Service других namespace и Service типа ExternalName отклоняются. Параметр agentManager.allowExternalUpstreams=true снимает ограничение, но тогда право создавать Tunnel или Ingress в любом namespace означает право опубликовать любой адрес, доступный поду оператора.
  • Токен на туннель. У каждого туннеля свой временный токен. Он только запускает туннели, без доступа к API, работает с одним протоколом и держит не больше одного туннеля одновременно. Токен отзывается при удалении ресурса. Токены ресурсов, удалённых, пока оператор не работал, отзываются автоматически.
  • Минимум прав в кластере. Поды работают от непривилегированного пользователя, с корневой ФС только для чтения и без capabilities. Secret и Service читает только agent-manager, и только get.
  • Трафик идёт через под. Туннели работают внутри пода agent-manager: трафик идёт из Tuna через под к Service. У agent-manager одна реплика, поэтому при обновлении оператора туннели переподключаются.

Обновление​

Выполните те же nelm release plan install и nelm release install с новой --chart-version: CRD обновятся вместе с релизом.

Удаление​

Сначала удалите ресурсы: у Domain и TCPEndpoint есть финализаторы, и им нужен работающий оператор.

kubectl delete tunnels,domains,tcpendpoints --all --all-namespaces
nelm release uninstall -n tuna-system -r tuna-operator # или: helm uninstall tuna-operator -n tuna-system
kubectl delete crd tunnels.k8s.tuna.am domains.k8s.tuna.am tcpendpoints.k8s.tuna.am