Files
K3S/addons/ingress-proxypass/README.md
Sergey Antropoff 38aaadbfb1 docs: sync addon docs with explicit external/internal service modes
Обновлена документация под новые аддоны (gitlab, redis, mongodb, kafka, kafka-ui, rabbitmq) и новую модель явного выбора зависимостей. Добавлены и унифицированы описания переключателей *_database_mode и *_redis_mode, обновлена таблица зависимостей аддонов, примеры конфигурации и список vault-секретов.
2026-04-29 23:21:04 +03:00

731 lines
25 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# External Services Ingress Proxy
Проксирует сервисы, работающие **вне Kubernetes**, через существующий стек `ingress-nginx` + `kube-vip` с одним общим VIP. Для каждого внешнего сервиса аддон автоматически создаёт:
- **Service** (ClusterIP, без selector) — стабильный адрес внутри кластера
- **Endpoints** — указывает на внешний IP(s) и порт
- **Ingress** — правила маршрутизации по хосту/пути через ingress-nginx
- **Secret** *(опционально)* — htpasswd-учётные данные для basic auth
Маршрут трафика:
```
Интернет ──► kube-vip VIP ──► ingress-nginx ──► Service ──► Endpoints ──► Внешний IP:PORT
```
Все прокси-домены резолвятся в один IP kube-vip. Дополнительный LoadBalancer не создаётся.
---
## Быстрый старт
**1. Включи аддон и определи сервисы:**
```yaml
# group_vars/all/addons.yml
addon_ingress_proxypass: true
ingress_proxypass_proxies:
- name: plex
hosts: [plex.home.ru]
ips: [192.168.1.50]
port: 32400
- name: router
hosts: [router.home.ru]
ips: [192.168.1.1]
port: 8080
```
**2. Разверни:**
```bash
make addon-ingress-proxypass
# с явным VIP в сводке:
make addon-ingress-proxypass ARGS="-e ingress_proxypass_vip=192.168.1.100"
```
**3. Направь DNS на kube-vip:**
```
plex.home.ru IN A 192.168.1.100 # kube-vip VIP
router.home.ru IN A 192.168.1.100
```
**4. Открой в браузере:** `http://plex.home.ru`
---
## Архитектура
```
┌─────────────────────────────────────────────────────────────────┐
│ Kubernetes Cluster │
│ │
│ ┌─────────────┐ ┌─────────────┐ ┌────────────────────┐ │
│ │ Ingress │ │ Service │ │ Endpoints │ │
│ │ (nginx) │───►│ ClusterIP │───►│ 192.168.1.50:32400│ │
│ │ plex.home.ru│ │ без sel-ra │ └────────────────────┘ │
│ └─────────────┘ └─────────────┘ │
│ ▲ │
│ │ │
│ ┌──────────────┐ │
│ │ kube-vip │ VIP: 192.168.1.100 │
│ └──────────────┘ │
└─────────────────────────────────────────────────────────────────┘
Клиент: curl http://plex.home.ru
```
**Почему Service + Endpoints, а не ExternalName?**
Сервисы типа `ExternalName` используют DNS CNAME, что обходит kube-proxy и ломает TLS SNI. `ClusterIP + Endpoints` маршрутизирует трафик через обычный service mesh и поддерживает все возможности ingress-nginx (auth, терминация TLS, rewrite и т.д.).
---
## Справочник по конфигурации
Все настройки задаются в `group_vars/all/addons.yml`.
### Глобальные значения по умолчанию
```yaml
ingress_proxypass_namespace: "ingress-proxypass" # Kubernetes namespace
ingress_proxypass_release_name: "ingress-proxypass" # имя Helm release
ingress_proxypass_defaults:
ingressClass: nginx # класс Ingress (должен совпадать с именем в ingress-nginx)
tls:
enabled: false # включить TLS-терминацию на Ingress
secretName: "" # имя существующего TLS Secret (wildcard cert и т.п.)
certManager:
enabled: false # автоматически выпускать/обновлять сертификат через cert-manager
issuer: "" # имя ClusterIssuer (например, letsencrypt-prod)
issuerKind: ClusterIssuer # ClusterIssuer | Issuer
auth:
enabled: false # включить nginx basic authentication
username: "" # логин (пароль хэшируется автоматически)
password: "" # пароль в открытом виде — задавать через vault!
credentials: "" # готовая htpasswd-строка (приоритет над username/password)
secretName: "" # использовать существующий Secret вместо генерации нового
websocket: true # включить WebSocket (заголовки HTTP/1.1 upgrade)
path: / # путь Ingress по умолчанию
pathType: Prefix # Prefix | Exact | ImplementationSpecific
proxyConnectTimeout: 60 # nginx proxy_connect_timeout (секунды)
proxyReadTimeout: 3600 # nginx proxy_read_timeout
proxySendTimeout: 3600 # nginx proxy_send_timeout
proxyBodySize: "1g" # nginx client_max_body_size (0 = без ограничений)
annotations: {} # дополнительные аннотации для каждого Ingress
```
### Поля определения прокси
```yaml
ingress_proxypass_proxies:
- name: myservice # (обязательно) уникальное имя → имя ресурса в K8s
hosts: # (обязательно) список хостов
- myservice.home.ru
ips: # (обязательно) внешние IP(s)
- 192.168.1.100
port: 8080 # (обязательно) внешний порт
# --- Все поля ниже опциональны (переопределяют глобальные defaults) ---
path: / # путь Ingress
pathType: Prefix # Prefix | Exact
websocket: true # поддержка WebSocket
ingressClass: nginx # переопределить класс Ingress для этого прокси
tls:
enabled: true
secretName: wildcard-cert
certManager:
enabled: false
auth:
enabled: true
username: admin # логин — хэш генерируется автоматически
password: "{{ vault_mypass }}" # пароль из vault (рекомендуется)
# ИЛИ готовая строка: credentials: "admin:$apr1$..."
# ИЛИ существующий Secret: secretName: my-existing-auth-secret
annotations: # аннотации уровня прокси (переопределяют всё)
nginx.ingress.kubernetes.io/proxy-body-size: "0"
nginx.ingress.kubernetes.io/proxy-read-timeout: "7200"
```
---
## Руководство по функциям
### TLS — существующий Secret (wildcard-сертификат)
Если есть wildcard-сертификат, управляемый cert-manager и хранящийся в Secret:
```yaml
ingress_proxypass_defaults:
tls:
enabled: true
secretName: wildcard-tls # должен существовать в ingress_proxypass_namespace
ingress_proxypass_proxies:
- name: plex
hosts: [plex.home.ru]
ips: [192.168.1.50]
port: 32400
```
Переопределить TLS для конкретного прокси:
```yaml
ingress_proxypass_proxies:
- name: router
hosts: [router.home.ru]
ips: [192.168.1.1]
port: 8080
tls:
enabled: false # отключить TLS только для этого прокси
```
### TLS — автоматический выпуск через cert-manager
```yaml
ingress_proxypass_defaults:
tls:
enabled: true
certManager:
enabled: true
issuer: letsencrypt-prod # должен существовать как ClusterIssuer
issuerKind: ClusterIssuer
```
cert-manager автоматически выпустит сертификат для каждого хоста и сохранит его в Secret с именем `<имя-прокси>-tls`.
> **Важно:** ACME-челлендж cert-manager требует публичной доступности домена. Для локальных/LAN-доменов используй DNS-01 challenge или wildcard-сертификат.
### Basic Authentication
Просто укажи логин и пароль — хэш генерируется автоматически:
```yaml
# group_vars/all/addons.yml
ingress_proxypass_defaults:
auth:
enabled: true
username: admin
password: "{{ vault_ingress_proxypass_password }}" # пароль из vault
```
```yaml
# group_vars/all/vault.yml
vault_ingress_proxypass_password: "мойсекретныйпароль"
```
Ansible автоматически вызовет `openssl passwd -apr1` и запишет хэш в Kubernetes Secret. Пароль в открытом виде **не попадает** в Helm values, логи или конфиги.
Выборочно для конкретного прокси (остальные без auth):
```yaml
ingress_proxypass_proxies:
- name: router
hosts: [router.home.ru]
ips: [192.168.1.1]
port: 8080
auth:
enabled: true
username: admin
password: "{{ vault_router_password }}"
- name: plex # без auth
hosts: [plex.home.ru]
ips: [192.168.1.50]
port: 32400
```
Разные пароли для разных прокси — каждый прокси создаёт свой отдельный Secret.
**Если уже есть готовая htpasswd-строка** (поле `credentials` имеет приоритет над `username`/`password`):
```yaml
auth:
enabled: true
credentials: "admin:$apr1$Rh0Ycxl9$rPTH7gRHfMBkS.7.Q1BxM/"
# Сгенерировать вручную: htpasswd -nb admin 'пароль'
```
**Использование существующего Secret** (ключ должен называться `auth`, тип `Opaque`):
```yaml
auth:
enabled: true
secretName: my-shared-htpasswd # создать вручную через kubectl
```
**Приоритет:** `secretName` > `credentials` > `username` + `password`
> **Примечание:** `configuration-snippet` должен быть разрешён в ingress-nginx (`allow-snippet-annotations: true`), если добавляешь кастомные сниппеты. Стандартные аннотации работают без этого.
### WebSocket (Plex, Grafana, Home Assistant и т.д.)
WebSocket включён по умолчанию (`websocket: true`). Это устанавливает:
```
nginx.ingress.kubernetes.io/proxy-http-version: "1.1"
```
ingress-nginx автоматически проксирует заголовки `Upgrade` / `Connection` при использовании HTTP/1.1. Отключи для конкретного прокси если не нужно:
```yaml
- name: router
...
websocket: false
```
### Несколько хостов для одного сервиса
```yaml
- name: plex
hosts:
- plex.home.ru
- plex.internal
- plex.lan
ips: [192.168.1.50]
port: 32400
```
Для каждого хоста создаётся отдельное правило Ingress, все ссылаются на один и тот же backend.
### Несколько backend IP (round-robin / резервирование)
```yaml
- name: homeassistant
hosts: [ha.home.ru]
ips:
- 192.168.1.100 # основной
- 192.168.1.101 # резервный
port: 8123
```
K8s создаёт два адреса в объекте `Endpoints`. kube-proxy распределяет трафик между ними по round-robin. Для активно-пассивного резервирования необходим внешний механизм проверки работоспособности.
### Маршрутизация по пути
```yaml
- name: grafana
hosts: [tools.home.ru]
ips: [192.168.1.60]
port: 3000
path: /grafana
pathType: Prefix
annotations:
nginx.ingress.kubernetes.io/rewrite-target: / # убрать префикс /grafana
```
### Кастомные аннотации nginx
```yaml
- name: plex
hosts: [plex.home.ru]
ips: [192.168.1.50]
port: 32400
annotations:
nginx.ingress.kubernetes.io/proxy-body-size: "0" # без ограничений на загрузку
nginx.ingress.kubernetes.io/proxy-read-timeout: "3600" # 1 час
nginx.ingress.kubernetes.io/proxy-send-timeout: "3600"
nginx.ingress.kubernetes.io/ssl-redirect: "true"
nginx.ingress.kubernetes.io/force-ssl-redirect: "true"
```
Аннотации уровня прокси имеют наивысший приоритет и переопределяют все defaults и сгенерированные аннотации.
---
## Как добавить новый внешний сервис
1. Добавь запись в `ingress_proxypass_proxies` в файле `group_vars/all/addons.yml`:
```yaml
ingress_proxypass_proxies:
# ... существующие записи ...
- name: homeassistant
hosts: [ha.home.ru]
ips: [192.168.1.150]
port: 8123
websocket: true
```
2. Запусти аддон повторно:
```bash
make addon-ingress-proxypass
```
Helm upgrade идемпотентен — существующие ресурсы обновляются, новые добавляются.
3. Добавь DNS-запись для нового хоста (см. раздел DNS ниже).
---
## Настройка DNS
Все прокси-хосты должны резолвиться в **kube-vip VIP**.
### Вариант A — роутер / домашний DNS-сервер
В настройках DHCP/DNS роутера (Keenetic, pfSense, AdGuard Home, Pi-hole и т.п.):
```
plex.home.ru → 192.168.1.100 (kube-vip VIP)
router.home.ru → 192.168.1.100
grafana.home.local → 192.168.1.100
```
### Вариант B — /etc/hosts (на конкретной машине, для тестирования)
```
192.168.1.100 plex.home.ru router.home.ru grafana.home.local
```
### Вариант C — rewrite в CoreDNS (только внутри кластера)
Если прокси доступен только из кластера, добавь rewrite в CoreDNS:
```yaml
# configmap coredns — добавить в Corefile
rewrite name plex.home.ru ingress-nginx.ingress-nginx.svc.cluster.local
```
### Узнать VIP kube-vip
```bash
# EXTERNAL-IP сервиса ingress-nginx LoadBalancer — это и есть VIP:
kubectl -n ingress-nginx get svc ingress-nginx-controller
```
---
## Проверка
### 1. Убедись что ресурсы созданы
```bash
kubectl -n ingress-proxypass get all,ingress,endpoints
# Ожидаемый вывод:
# NAME TYPE CLUSTER-IP ...
# service/plex ClusterIP 10.43.x.x ...
# service/router ClusterIP 10.43.x.x ...
#
# NAME ENDPOINTS ...
# endpoints/plex 192.168.1.50:32400 ...
# endpoints/router 192.168.1.1:8080 ...
```
### 2. Проверь Endpoints заполнены
```bash
kubectl -n ingress-proxypass describe endpoints plex
# Должно показать "Addresses: 192.168.1.50" и "Ports: http 32400/TCP"
```
### 3. Проверь соединение изнутри кластера
```bash
kubectl run curl --rm -it --image=curlimages/curl -- \
curl -v http://plex.ingress-proxypass.svc.cluster.local:32400
```
### 4. Проверь внешний доступ
```bash
# Замени IP на kube-vip VIP
curl -H "Host: plex.home.ru" http://192.168.1.100/
# Через DNS:
curl http://plex.home.ru/
```
### 5. Убедись что ingress-nginx применил правила
```bash
kubectl -n ingress-proxypass describe ingress plex
# Должно показать Rules → host → plex.home.ru → plex:32400
# Проверь что конфиг nginx обновился:
kubectl -n ingress-nginx exec -it \
$(kubectl -n ingress-nginx get pod -l 'app.kubernetes.io/name=ingress-nginx' -o name | head -1) \
-- nginx -T | grep plex
```
---
## Пример сгенерированных манифестов
При такой конфигурации:
```yaml
ingress_proxypass_proxies:
- name: plex
hosts:
- plex.home.ru
ips:
- 192.168.1.50
port: 32400
annotations:
nginx.ingress.kubernetes.io/proxy-body-size: "0"
```
Создаются следующие ресурсы Kubernetes:
**Service:**
```yaml
apiVersion: v1
kind: Service
metadata:
name: plex
namespace: ingress-proxypass
spec:
type: ClusterIP
ports:
- name: http
port: 32400
targetPort: 32400
protocol: TCP
```
**Endpoints:**
```yaml
apiVersion: v1
kind: Endpoints
metadata:
name: plex
namespace: ingress-proxypass
subsets:
- addresses:
- ip: "192.168.1.50"
ports:
- name: http
port: 32400
protocol: TCP
```
**Ingress:**
```yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: plex
namespace: ingress-proxypass
annotations:
nginx.ingress.kubernetes.io/proxy-connect-timeout: "60"
nginx.ingress.kubernetes.io/proxy-read-timeout: "3600"
nginx.ingress.kubernetes.io/proxy-send-timeout: "3600"
nginx.ingress.kubernetes.io/proxy-body-size: "0" # переопределено на уровне прокси
nginx.ingress.kubernetes.io/proxy-http-version: "1.1" # websocket=true
spec:
ingressClassName: nginx
rules:
- host: plex.home.ru
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: plex
port:
number: 32400
```
---
## Helm Chart — самостоятельное использование без Ansible
Чарт находится в `addons/ingress-proxypass/role/chart/`. Развернуть напрямую:
```bash
# Из корня проекта:
helm upgrade --install ingress-proxypass addons/ingress-proxypass/role/chart \
--namespace ingress-proxypass \
--create-namespace \
--values my-values.yaml
```
Сгенерировать манифесты без деплоя (для проверки):
```bash
helm template ingress-proxypass addons/ingress-proxypass/role/chart \
--values my-values.yaml
```
---
## Устранение неисправностей
### 502 Bad Gateway
**Причина:** ingress-nginx достигает Service, но Service не может достучаться до внешнего IP.
```bash
# 1. Проверь Endpoints заполнены:
kubectl -n ingress-proxypass get endpoints plex
# "Addresses" не должен быть пустым. Если "<none>" — Endpoints отсутствует или некорректен.
# 2. Проверь доступность внешнего IP с ноды кластера:
ssh master01
curl -v http://192.168.1.50:32400
# 3. Проверь файрвол на внешнем хосте — он должен разрешать входящие из CIDR кластера.
```
### 503 Service Temporarily Unavailable
**Причина:** у ingress-nginx нет здоровых Endpoints.
```bash
# Проверь наличие endpoints:
kubectl -n ingress-proxypass describe endpoints plex
# Убедись что у service есть ClusterIP:
kubectl -n ingress-proxypass get svc plex
# Проверь логи ingress-nginx:
kubectl -n ingress-nginx logs -l app.kubernetes.io/name=ingress-nginx --tail=100
```
### 404 Not Found
**Причина:** правило Ingress не совпало — ошибка в имени хоста или пути.
```bash
# 1. Проверь заголовок Host при запросе:
curl -v -H "Host: plex.home.ru" http://192.168.1.100/
# 2. Посмотри описание Ingress:
kubectl -n ingress-proxypass describe ingress plex
# Раздел "Rules" — хост и путь должны совпадать точно.
# 3. Проверь класс Ingress:
kubectl -n ingress-proxypass get ingress plex -o jsonpath='{.spec.ingressClassName}'
# Должен совпадать с классом ingress-nginx (обычно "nginx")
```
### DNS не резолвится
```bash
# Узнай куда резолвится хост:
nslookup plex.home.ru
dig plex.home.ru
# Должен вернуть kube-vip VIP, а не реальный IP сервера Plex.
# Проверь с явным IP:
curl -H "Host: plex.home.ru" http://<kube-vip-IP>/
```
### Проблемы с TLS / HTTPS
```bash
# Проверь наличие TLS Secret в нужном namespace:
kubectl -n ingress-proxypass get secret wildcard-tls
# Проверь что cert-manager выпустил сертификат:
kubectl -n ingress-proxypass get certificate
kubectl -n ingress-proxypass describe certificate plex
# Логи cert-manager:
kubectl -n cert-manager logs -l app=cert-manager --tail=50
```
### Basic Auth возвращает 401
```bash
# Проверь с учётными данными:
curl -u admin:мойпароль http://plex.home.ru/
# Убедись что в Secret есть ключ "auth":
kubectl -n ingress-proxypass get secret plex-auth -o jsonpath='{.data.auth}' | base64 -d
# Должно вывести: admin:$apr1$...
# Проверь аннотации в Ingress:
kubectl -n ingress-proxypass get ingress plex -o yaml | grep auth
```
### Обрывы WebSocket-соединения
```bash
# Проверь аннотацию proxy-http-version:
kubectl -n ingress-proxypass get ingress plex -o yaml | grep proxy-http
# Для сервисов где нужен длинный таймаут (стриминг):
# Добавь в annotations:
# nginx.ingress.kubernetes.io/proxy-read-timeout: "86400"
# nginx.ingress.kubernetes.io/proxy-send-timeout: "86400"
```
### Логи ingress-nginx
```bash
# Смотреть логи nginx в реальном времени:
kubectl -n ingress-nginx logs \
-l app.kubernetes.io/name=ingress-nginx \
--tail=100 -f
# Проверить nginx.conf применился:
kubectl -n ingress-nginx exec \
$(kubectl -n ingress-nginx get pod -l 'app.kubernetes.io/name=ingress-nginx' -o name | head -1) \
-- cat /etc/nginx/nginx.conf | grep -A5 plex
```
---
## Удаление
```bash
helm -n ingress-proxypass uninstall ingress-proxypass
kubectl delete namespace ingress-proxypass
```
Удалить только один прокси без полного сноса релиза:
1. Убери запись из `ingress_proxypass_proxies` в `group_vars/all/addons.yml`
2. Запусти `make addon-ingress-proxypass` — Helm upgrade удалит убранные ресурсы
---
## Интеграция с проектом
| Зависимость | Примечание |
|---|---|
| `ingress-nginx` (`addon_ingress_nginx: true`) | Должен быть установлен первым |
| `kube-vip` | Предоставляет VIP для LoadBalancer сервиса ingress-nginx |
| `cert-manager` (опционально) | Нужен только при `tls.certManager.enabled: true` |
### Команды Makefile
```bash
make addon-ingress-proxypass # развернуть / обновить
make addon-ingress-proxypass ARGS="-e ingress_proxypass_vip=..." # с явным VIP в сводке
```
### Переменные Ansible
| Переменная | По умолчанию | Описание |
|---|---|---|
| `ingress_proxypass_namespace` | `ingress-proxypass` | Kubernetes namespace |
| `ingress_proxypass_release_name` | `ingress-proxypass` | Имя Helm release |
| `ingress_proxypass_proxies` | `[]` | Список определений внешних сервисов |
| `ingress_proxypass_defaults.*` | см. defaults | Глобальные значения по умолчанию |
| `ingress_proxypass_vip` | `""` | kube-vip VIP — отображается в сводке после установки |
## Официальные ресурсы
- Официальный сайт: [https://kubernetes.io/docs/concepts/services-networking/ingress/](https://kubernetes.io/docs/concepts/services-networking/ingress/)
- Официальная документация: [https://kubernetes.io/docs/concepts/services-networking/ingress/](https://kubernetes.io/docs/concepts/services-networking/ingress/)
- Версии Helm chart / ПО: [https://kubernetes.github.io/ingress-nginx/deploy/](https://kubernetes.github.io/ingress-nginx/deploy/)