- addons/ext-proxy/ → addons/ingress-proxypass/ (git mv, история сохранена) - Все переменные Ansible: ext_proxy_* → ingress_proxypass_* - Все имена ресурсов K8s: ext-proxy → ingress-proxypass (namespace, chart, release) - Helm-хелперы: "ext-proxy.*" → "ingress-proxypass.*" - Makefile: addon-ext-proxy → addon-ingress-proxypass - group_vars/all/addons.yml: addon_ext_proxy → addon_ingress_proxypass - playbooks/addons.yml: обновлена ссылка на роль - docs/addons.md, README.md: обновлены все упоминания
25 KiB
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. Включи аддон и определи сервисы:
# 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. Разверни:
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.
Глобальные значения по умолчанию
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
Поля определения прокси
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:
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 для конкретного прокси:
ingress_proxypass_proxies:
- name: router
hosts: [router.home.ru]
ips: [192.168.1.1]
port: 8080
tls:
enabled: false # отключить TLS только для этого прокси
TLS — автоматический выпуск через cert-manager
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
Просто укажи логин и пароль — хэш генерируется автоматически:
# group_vars/all/addons.yml
ingress_proxypass_defaults:
auth:
enabled: true
username: admin
password: "{{ vault_ingress_proxypass_password }}" # пароль из vault
# group_vars/all/vault.yml
vault_ingress_proxypass_password: "мойсекретныйпароль"
Ansible автоматически вызовет openssl passwd -apr1 и запишет хэш в Kubernetes Secret. Пароль в открытом виде не попадает в Helm values, логи или конфиги.
Выборочно для конкретного прокси (остальные без auth):
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):
auth:
enabled: true
credentials: "admin:$apr1$Rh0Ycxl9$rPTH7gRHfMBkS.7.Q1BxM/"
# Сгенерировать вручную: htpasswd -nb admin 'пароль'
Использование существующего Secret (ключ должен называться auth, тип Opaque):
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. Отключи для конкретного прокси если не нужно:
- name: router
...
websocket: false
Несколько хостов для одного сервиса
- name: plex
hosts:
- plex.home.ru
- plex.internal
- plex.lan
ips: [192.168.1.50]
port: 32400
Для каждого хоста создаётся отдельное правило Ingress, все ссылаются на один и тот же backend.
Несколько backend IP (round-robin / резервирование)
- name: homeassistant
hosts: [ha.home.ru]
ips:
- 192.168.1.100 # основной
- 192.168.1.101 # резервный
port: 8123
K8s создаёт два адреса в объекте Endpoints. kube-proxy распределяет трафик между ними по round-robin. Для активно-пассивного резервирования необходим внешний механизм проверки работоспособности.
Маршрутизация по пути
- 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
- 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 и сгенерированные аннотации.
Как добавить новый внешний сервис
- Добавь запись в
ingress_proxypass_proxiesв файлеgroup_vars/all/addons.yml:
ingress_proxypass_proxies:
# ... существующие записи ...
- name: homeassistant
hosts: [ha.home.ru]
ips: [192.168.1.150]
port: 8123
websocket: true
- Запусти аддон повторно:
make addon-ingress-proxypass
Helm upgrade идемпотентен — существующие ресурсы обновляются, новые добавляются.
- Добавь 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:
# configmap coredns — добавить в Corefile
rewrite name plex.home.ru ingress-nginx.ingress-nginx.svc.cluster.local
Узнать VIP kube-vip
# EXTERNAL-IP сервиса ingress-nginx LoadBalancer — это и есть VIP:
kubectl -n ingress-nginx get svc ingress-nginx-controller
Проверка
1. Убедись что ресурсы созданы
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 заполнены
kubectl -n ingress-proxypass describe endpoints plex
# Должно показать "Addresses: 192.168.1.50" и "Ports: http 32400/TCP"
3. Проверь соединение изнутри кластера
kubectl run curl --rm -it --image=curlimages/curl -- \
curl -v http://plex.ingress-proxypass.svc.cluster.local:32400
4. Проверь внешний доступ
# Замени IP на kube-vip VIP
curl -H "Host: plex.home.ru" http://192.168.1.100/
# Через DNS:
curl http://plex.home.ru/
5. Убедись что ingress-nginx применил правила
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
Пример сгенерированных манифестов
При такой конфигурации:
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:
apiVersion: v1
kind: Service
metadata:
name: plex
namespace: ingress-proxypass
spec:
type: ClusterIP
ports:
- name: http
port: 32400
targetPort: 32400
protocol: TCP
Endpoints:
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:
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/. Развернуть напрямую:
# Из корня проекта:
helm upgrade --install ingress-proxypass addons/ingress-proxypass/role/chart \
--namespace ingress-proxypass \
--create-namespace \
--values my-values.yaml
Сгенерировать манифесты без деплоя (для проверки):
helm template ingress-proxypass addons/ingress-proxypass/role/chart \
--values my-values.yaml
Устранение неисправностей
502 Bad Gateway
Причина: ingress-nginx достигает Service, но Service не может достучаться до внешнего IP.
# 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.
# Проверь наличие 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 не совпало — ошибка в имени хоста или пути.
# 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 не резолвится
# Узнай куда резолвится хост:
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
# Проверь наличие 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
# Проверь с учётными данными:
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-соединения
# Проверь аннотацию 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
# Смотреть логи 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
Удаление
helm -n ingress-proxypass uninstall ingress-proxypass
kubectl delete namespace ingress-proxypass
Удалить только один прокси без полного сноса релиза:
- Убери запись из
ingress_proxypass_proxiesвgroup_vars/all/addons.yml - Запусти
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
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 — отображается в сводке после установки |