Сеть в домашнем кластере
Сеть — самая непривычная часть Kubernetes для новичка. Здесь разберёмся по порядку: как поды видят друг друга внутри кластера, как открыть сервис для домашней сети и как впустить трафик из интернета через роутер.
CNI: как поды общаются между собой
Каждый под в кластере получает собственный IP-адрес из внутренней подсети (обычно 10.x.x.x). Под на узле 1 может напрямую обратиться к поду на узле 2 по этому адресу — как будто все они в одной плоской сети. Эту магию обеспечивает сетевой плагин, который называется CNI (Container Network Interface).
flannel — то, что у вас уже работает
Если вы ставили k3s или следовали пути kubeadm с нашей инструкции, у вас уже стоит flannel — простой и надёжный плагин. Он создаёт оверлейную сеть поверх вашей домашней: трафик между узлами упаковывается в UDP-пакеты (VXLAN) и распаковывается на другом конце. Для домашнего кластера этого достаточно на годы вперёд.
Посмотреть, как устроена сеть подов:
# подсети, назначенные узлам
kubectl get nodes -o custom-columns=NAME:.metadata.name,PODCIDR:.spec.podCIDR
# поды flannel (в kubeadm) или их роль в k3s
kubectl get pods -n kube-flannel
Calico — когда захочется большего
Calico — более продвинутый плагин. Главные причины перейти на него дома:
- NetworkPolicy — правила «кто кому может ходить» внутри кластера (flannel их не поддерживает; в k3s есть свой network-policy-controller, но Calico — индустриальный стандарт);
- более эффективная маршрутизация без оверлея (BGP/прямая маршрутизация);
- желание изучить инструмент, который используется в продакшене.
Service: стабильные адреса для подов
Поды смертны: пересоздался под — изменился IP. Поэтому к подам никогда не обращаются напрямую. Поверх них создают Service — объект с постоянным внутренним IP и DNS-именем, который распределяет трафик между подами-мишенями.
# пример: сервис поверх deployment с тремя копиями nginx
apiVersion: v1
kind: Service
metadata:
name: my-web
spec:
selector:
app: my-web # какие поды обслуживать (по метке)
ports:
- port: 80 # порт сервиса
targetPort: 80 # порт контейнера
Изнутри кластера такой сервис доступен по имени: http://my-web (а из другого namespace — http://my-web.имя-namespace). Имена раздаёт встроенный DNS — CoreDNS.
Типов Service четыре, и понимать разницу между ними важно:
| Тип | Что делает | Когда использовать дома |
|---|---|---|
ClusterIP (по умолчанию) |
Внутренний IP, доступен только внутри кластера | Для связи сервисов между собой: приложение → база данных |
NodePort |
Открывает порт 30000–32767 на каждом узле | Быстрый доступ из домашней сети без лишних настроек |
LoadBalancer |
Выдаёт сервису отдельный «внешний» IP | Дома работает через MetalLB (см. ниже) — красивые адреса вида 192.168.1.200 |
ExternalName |
DNS-запись CNAME на внешнее имя | Редко; для ссылок на внешние сервисы по имени |
DNS внутри кластера (CoreDNS)
За служебные имена отвечает CoreDNS — он работает подом в namespace kube-system. Каждому сервису автоматически выдаётся имя вида имя-сервиса.имя-namespace.svc.cluster.local. Проверить DNS изнутри кластера:
# тестовый под с busybox
kubectl run dnstest --rm -it --image=busybox:1.36 --restart=Never -- nslookup kubernetes.default
# Server: 10.43.0.10 (в k3s) — это ClusterIP CoreDNS
# Name: kubernetes.default.svc.cluster.local
Если имена внутри кластера не резолвятся — первым делом смотрите, жив ли CoreDNS: kubectl get pods -n kube-system -l k8s-app=kube-dns.
NodePort: быстрый доступ из дома
Самый простой способ открыть сервис домашней сети — NodePort. Kubernetes откроет указанный порт (из диапазона 30000–32767) на каждом узле кластера и будет пересылать трафик на ваши поды.
apiVersion: v1
kind: Service
metadata:
name: my-web
spec:
type: NodePort
selector:
app: my-web
ports:
- port: 80
targetPort: 80
nodePort: 30080 # можно не указывать — выберется автоматически
Теперь с любого устройства дома сервис доступен по адресу http://192.168.1.50:30080 (где 192.168.1.50 — любой узел кластера).
:30080), и никакой маршрутизации по доменным именам. Для «проверить, работает ли» — идеально. Для постоянных сервисов — переходите на LoadBalancer или Ingress.
MetalLB: настоящий LoadBalancer дома
Тип LoadBalancer в облаках выдаёт сервису публичный IP. Дома облачного балансировщика нет, но есть MetalLB — он делает то же самое из диапазона вашей домашней сети. Вы выделяете пул адресов (например, 192.168.1.200–192.168.1.220), и каждый сервис типа LoadBalancer получает свой постоянный IP.
Шаг 1. Установите MetalLB
kubectl apply -f https://raw.githubusercontent.com/metallb/metallb/v0.14.8/config/manifests/metallb-native.yaml
# дождитесь запуска подов
kubectl get pods -n metallb-system -w
--disable servicelb (или удалите манифест из /var/lib/rancher/k3s/server/manifests/).
Шаг 2. Настройте пул адресов
Создайте файл metallb-pool.yaml. Адреса должны быть из вашей домашней подсети и не пересекаться с DHCP-диапазоном роутера:
apiVersion: metallb.io/v1beta1
kind: IPAddressPool
metadata:
name: home-pool
namespace: metallb-system
spec:
addresses:
- 192.168.1.200-192.168.1.220
---
apiVersion: metallb.io/v1beta1
kind: L2Advertisement
metadata:
name: home-l2
namespace: metallb-system
spec:
ipAddressPools:
- home-pool
kubectl apply -f metallb-pool.yaml
Шаг 3. Проверьте
# создадим сервис типа LoadBalancer
kubectl create deployment web-test --image=nginx
kubectl expose deployment web-test --type=LoadBalancer --port=80
# смотрим выданный внешний IP
kubectl get svc web-test
# EXTERNAL-IP: 192.168.1.200
# проверяем с любого домашнего устройства
curl http://192.168.1.200
MetalLB в режиме L2 назначает один из узлов «ответственным» за адрес и отвечает на ARP-запросы в локальной сети. Если узел падает — адрес перехватывает другой узел.
Проброс портов на роутере
Чтобы сервис был доступен не только из дома, но и из интернета (например, ваш Nextcloud с телефона из любой точки), нужно пробросить порты на домашнем роутере.
Шаг 4. Настройте проброс на роутере
Общая схема одинакова для всех роутеров (раздел обычно называется «Port Forwarding», «Виртуальные серверы» или «Переадресация портов»):
- Внешний порт 80 → внутренний адрес узла или LoadBalancer-IP, порт 80;
- Внешний порт 443 → тот же адрес, порт 443.
Куда пробрасывать, зависит от вашей схемы: если Ingress-сервис получил IP от MetalLB (например, 192.168.1.200) — пробрасывайте на него. Если используете NodePort — на адрес любого узла и соответствующий порт 3xxxx.
- У большинства домашних провайдеров «серый» (CG-NAT) внешний IP — проброс портов с ним не работает. Проверьте: если WAN-адрес роутера начинается с 10., 100.64. или 172.16. — вы за CG-NAT. Выходы: белый IP у провайдера (обычно недорогая опция) или туннели вроде Cloudflare Tunnel, WireGuard на арендованной VPS.
- Открывая сервис в интернет, позаботьтесь о HTTPS (Let's Encrypt через cert-manager) и аутентификации. Голый Nextcloud без HTTPS в интернете — плохая идея.
Ingress: один вход для всех сервисов
NodePort и LoadBalancer выставляют сервисы наружу «в лоб», каждый на своём порту или IP. Ingress — умнее: это единая точка входа (обычно порты 80/443), которая маршрутизирует запросы по доменному имени и пути: cloud.home.lan → Nextcloud, media.home.lan → Jellyfin, и всё это через один IP-адрес.
Ingress состоит из двух частей:
- Ingress-контроллер — прокси, который реально принимает трафик. В k3s встроен Traefik; в ванильном k8s ставят ingress-nginx;
- Ingress-ресурсы — ваши манифесты с правилами маршрутизации.
Если у вас k3s — Traefik уже стоит
Проверьте:
kubectl get pods -n kube-system | grep traefik
kubectl get svc -n kube-system traefik
Если у вас kubeadm — поставьте ingress-nginx
kubectl apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/main/deploy/static/provider/baremetal/deploy.yaml
Для bare-metal-версии назначьте сервису контроллера тип NodePort или LoadBalancer (если стоит MetalLB):
kubectl get svc -n ingress-nginx
# при MetalLB: контроллер получит, например, 192.168.1.201
Шаг 5. Создайте Ingress-ресурс
Пример: у нас есть два сервиса — jellyfin и nextcloud (оба типа ClusterIP). Откроем их по разным именам через один контроллер. Файл ingress-home.yaml:
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: home-services
annotations:
# для traefik класс обычно не нужен; для nginx раскомментируйте:
# kubernetes.io/ingress.class: nginx
spec:
rules:
- host: media.home.lan
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: jellyfin
port:
number: 8096
- host: cloud.home.lan
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: nextcloud
port:
number: 80
kubectl apply -f ingress-home.yaml
kubectl get ingress
Шаг 6. Научите домашние устройства резолвить имена
Имена media.home.lan сами собой не заработают — нужна DNS-запись. Три варианта по возрастанию правильности:
- Файл hosts на вашем компьютере — для быстрой проверки. В Windows:
C:\Windows\System32\drivers\etc\hosts, в Linux/macOS:/etc/hosts. Строка:192.168.1.200 media.home.lan cloud.home.lan(IP — адрес Ingress/MetalLB). - DNS на роутере — многие роутеры (Keenetic, OpenWrt, RouterOS) умеют статические DNS-записи: пропишите
*.home.lanили каждое имя отдельно на адрес Ingress. - Свой DNS — AdGuard Home или Pi-hole в том же кластере решают и блокировку рекламы, и локальные имена. Классический следующий шаг домашней лаборатории.
Шаг 7. Проверьте цепочку целиком
# с вашего компьютера:
curl -H "Host: media.home.lan" http://192.168.1.200
# или, если DNS уже настроен:
curl http://media.home.lan
Путь запроса: браузер → DNS (имя → IP Ingress) → Ingress-контроллер → смотрит поле Host → Service jellyfin → под Jellyfin. Если что-то не работает, проверяйте звенья по порядку снизу вверх: жив ли под, есть ли у сервиса endpoints (kubectl get endpoints jellyfin), создан ли ingress (kubectl describe ingress home-services).
Диагностика сетевых проблем
Когда «сервис не открывается», идите по цепочке от пода наружу:
Проверка 1. Жив ли под
kubectl get pods -o wide
# STATUS должен быть Running, IP — внутренний адрес пода
Проверка 2. Отвечает ли приложение внутри пода
# подставьте IP пода и порт контейнера
kubectl run tmp --rm -it --image=busybox:1.36 -- wget -qO- http://10.42.0.15:80
Проверка 3. Есть ли у сервиса endpoints
kubectl get endpoints имя-сервиса
# пустой список = selector сервиса не совпадает с метками подов — классическая опечатка
Проверка 4. Отвечает ли сервис по ClusterIP
kubectl get svc имя-сервиса
kubectl run tmp --rm -it --image=busybox:1.36 -- wget -qO- http://10.43.x.x:80
Проверка 5. NodePort / Ingress
# с самого узла — открыт ли NodePort
curl http://localhost:30081
# правила ingress
kubectl describe ingress имя
# логи контроллера
kubectl logs -n kube-system -l app.kubernetes.io/name=traefik # k3s
Подробный разбор типовых поломок — на странице «Советы».