Kubernetes-оператор
Оператор публикует Service кластера через туннели Tuna. Туннель, домен или TCP-порт описывается ресурсом Kubernetes, а оператор создаёт его в аккаунте Tuna и держит подключение. Кластер сам подключается к Tuna исходящими соединениями, поэтому LoadBalancer, публичный IP и открытые входящие порты не нужны.
tuna kubernetesКоманда tuna kubernetes даёт временный доступ к Kubernetes API кластера. Оператор решает другую задачу: публикует приложения, которые работают в кластере.
Ресурсы
| Ресурс | Что делает | Когда нужен |
|---|---|---|
| Tunnel | HTTP- или 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).
- Откройте API-ключи в личном кабинете и создайте новый ключ.
- В блоке «Для чего ключ» выберите «Для API».
- В поле «Описание» укажите кластер, например
k8s-operator prod. Заведите отдельный ключ на каждый кластер: тогда доступ одного кластера можно отозвать, не трогая остальные. - В поле «Срок действия» выберите «Не ограничено». Набор «Для API» по умолчанию ставит 30 дней, и когда срок истечёт, оператор потеряет доступ к API. Если политика безопасности требует ограниченного срока, оставьте его, но запланируйте замену ключа.
- В блоке «Доступ к API» выберите «Выборочно» и для «Эндпоинты и домены» нажмите «Запись». У остальных продуктов оставьте «Нет».
- Создайте ключ и скопируйте его.
Переключатель «Запуск туннелей» включать не нужно. Оператор не подключает туннели своим ключом: для каждого туннеля он выпускает отдельный временный токен.
Ключ с полным доступом к API тоже подойдёт, но откроет оператору мониторинги, секреты и настройки команды, которые ему не нужны.
Установка
Чарт tuna-k8s-operator лежит в реестре пакетов code.tuna.am. В примерах оператор ставится в namespace tuna-system, а ключ передаётся параметром credentials.apiKey. Другие способы передать ключ — в разделе Где хранить ключ.
- Nelm
- Helm
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-ключ>
helm repo add tuna https://code.tuna.am/api/packages/tuna/helm
helm repo update
helm install tuna-operator tuna/tuna-k8s-operator \
--namespace tuna-system --create-namespace \
--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
- Helm
Выполните те же nelm release plan install и nelm release install с новой --chart-version: CRD обновятся вместе с релизом.
Helm не обновляет CRD, поэтому примените их до обновления чарта:
helm pull tuna/tuna-k8s-operator --untar --version <версия>
kubectl apply -f tuna-k8s-operator/crds/
helm upgrade tuna-operator tuna/tuna-k8s-operator --namespace tuna-system --reuse-values --version <версия>
Удаление
Сначала удалите ресурсы: у 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