Запускаем Kubernetes Ingress-контроллер c публичным ip на домашнем ноутбуке

от автора

Работа с Ingress-контроллерами обычно предполагает работу с Kubernetes в облаке, где внешние ip присваиваются автоматически. Я изучаю Kubernetes, обходясь обычным ноутбуком за NAT, на котором в виртуальных машинах запущены разные разновидности Kubernetes. Когда я разбирался с Ingress-контроллером, у меня возникло непреодолимое желание завести в него публичный ip и обратиться к нему извне. Давайте посмотрим, как это можно сделать. Линукс Всемогущий поможет нам в этом.

Публичный ip я решил позаимствовать у vps. Для этого в reg.ru (не реклама, просто здесь все заработало) я арендовал на пару часов виртуалку с ubuntu20.04 на борту и парой ip адресов. Один будем использовать для доступа по ssh, второй снимем с интерфейса виртуальной машины и заведем в наш Kubernetes (работу можно организовать и проще, DNATами, но так интересней). Понятно, что публичные ip адреса, указанные далее, у каждого будут свои, и их необходимо заменить соответственно.

VPS

Состояние vps на начальном этапе:

# ip a 1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000     link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00     inet 127.0.0.1/8 scope host lo        valid_lft forever preferred_lft forever     inet6 ::1/128 scope host         valid_lft forever preferred_lft forever 2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP group default qlen 1000     link/ether 52:54:00:73:f5:f6 brd ff:ff:ff:ff:ff:ff     inet 95.163.241.96/24 brd 95.163.241.255 scope global eth0        valid_lft forever preferred_lft forever     inet 89.108.76.161/24 brd 89.108.76.255 scope global eth0        valid_lft forever preferred_lft forever     inet6 2a00:f940:2:4:2::51d4/64 scope global         valid_lft forever preferred_lft forever     inet6 fe80::5054:ff:fe73:f5f6/64 scope link         valid_lft forever preferred_lft forever 3: eth1: <BROADCAST,MULTICAST> mtu 1500 qdisc noop state DOWN group default qlen 1000     link/ether 52:54:00:9a:da:36 brd ff:ff:ff:ff:ff:ff

Послушав eth0 убеждаемся, что гипервизор регулярно посылает arp запросы для подтверждения ip адресов. В дальнейшем мы отвяжем ip адрес 89.108.76.161 от интерфейса и запустим демон, который будет отвечать на эти arp запросы, изображая наличие ip адреса:

# tcpdump -i eth0 -n -v arp  tcpdump: listening on eth0, link-type EN10MB (Ethernet), capture size 262144 bytes 14:53:20.229845 ARP, Ethernet (len 6), IPv4 (len 4), Request who-has 95.163.241.96 tell 37.140.193.29, length 28 14:53:20.229879 ARP, Ethernet (len 6), IPv4 (len 4), Reply 95.163.241.96 is-at 52:54:00:73:f5:f6, length 28 14:54:05.031046 ARP, Ethernet (len 6), IPv4 (len 4), Request who-has 89.108.76.161 tell 37.140.193.29, length 28 14:54:05.031103 ARP, Ethernet (len 6), IPv4 (len 4), Reply 89.108.76.161 is-at 52:54:00:73:f5:f6, length 28 14:54:09.126771 ARP, Ethernet (len 6), IPv4 (len 4), Request who-has 95.163.241.96 tell 37.140.193.29, length 28 14:54:09.126827 ARP, Ethernet (len 6), IPv4 (len 4), Reply 95.163.241.96 is-at 52:54:00:73:f5:f6, length 28 14:54:49.573563 ARP, Ethernet (len 6), IPv4 (len 4), Request who-has 89.108.76.161 tell 37.140.193.29, length 28 14:54:49.573615 ARP, Ethernet (len 6), IPv4 (len 4), Reply 89.108.76.161 is-at 52:54:00:73:f5:f6, length 28 14:54:54.693462 ARP, Ethernet (len 6), IPv4 (len 4), Request who-has 95.163.241.96 tell 37.140.193.29, length 28 14:54:54.693493 ARP, Ethernet (len 6), IPv4 (len 4), Reply 95.163.241.96 is-at 52:54:00:73:f5:f6, length 28

Прокинем туннель с vps до домашнего ноута с помощью wireguard. Инструкций полно на просторах интернета, так что здесь ничего особенного:

# apt update # apt install wireguard # wg genkey | tee /etc/wireguard/private.key # chmod go= /etc/wireguard/private.key # cat /etc/wireguard/private.key | wg pubkey | tee /etc/wireguard/public.key # cat  > /etc/wireguard/wg0.conf <<EOF [Interface] Address = 10.15.0.1/24 SaveConfig = true ListenPort = 51820 PrivateKey = gFzlk6/oBAkRnqTSqRQ0A03IR8iX2NY0Q9518xMTDmI= EOF

Поднимаем wireguard:

# systemctl start wg-quick@wg0.service

Удаляем внешний ip с интерфейса:

# ip addr del 89.108.76.161/24 brd 89.108.76.255 dev eth0

Добавляем маршрутизацию к внешнему ip через туннель:

# ip r add 89.108.76.161 via 10.15.0.2

Команда ниже нужна, чтобы ноутбук не остался без доступа интернету, т.к. далее мы завернем весь его трафик в туннель:

# iptables -t nat -I POSTROUTING -o eth0 -j MASQUERADE

Разрешаем доступ к внешнему ip и адресу ноутбука в сети wireguard через туннель:

# wg set wg0 peer hd7clB/uztrTOlsWTrHCF7mu9g6ECp+FhE2lhohWf1s= allowed-ips 89.108.76.161,10.15.0.2

Разрешаем форвардинг между интерфейсами:

# sysctl -w net.ipv4.ip_forward=1

и убеждаемся, что цепочка FORWARD не заблокирована:

# iptables-save | grep FORWARD    :FORWARD ACCEPT [450722:544073659] :FORWARD ACCEPT [4633:3846037]

После запуска wireguard в системе появится интерфейс wg0:

# ip a 4: wg0: <POINTOPOINT,NOARP,UP,LOWER_UP> mtu 1420 qdisc noqueue state UNKNOWN group default qlen 1000     link/none      inet 10.15.0.1/24 scope global wg0        valid_lft forever preferred_lft forever

Ноутбук (Ubuntu20.04)

Устанавливаем wireguard и генерируем ключи по аналогии:

# cat  > /etc/wireguard/wg2.conf <<EOF  [Interface] PrivateKey = Some private key Address = 10.15.0.2/24 Table = off  [Peer] PublicKey = aU3tLYzJPTKCtelYgVTtAfgnvixWdNK5jC2wnXgvemw= AllowedIPs = 0.0.0.0/0 Endpoint = 95.163.241.96:51820 PersistentKeepalive = 25 EOF

Поднимаем туннель:

# systemctl start wg-quick@wg2.service

Проверяем наличие интерфейса wireguard:

# ip a 221: wg2: <POINTOPOINT,NOARP,UP,LOWER_UP> mtu 1420 qdisc noqueue state UNKNOWN group default qlen 1000     link/none      inet 10.15.0.2/24 scope global wg2        valid_lft forever preferred_lft forever

и связности с сервером:

# ping 10.15.0.1 PING 10.15.0.1 (10.15.0.1) 56(84) bytes of data. 64 bytes from 10.15.0.1: icmp_seq=1 ttl=64 time=16.3 ms 64 bytes from 10.15.0.1: icmp_seq=2 ttl=64 time=8.91 ms 64 bytes from 10.15.0.1: icmp_seq=3 ttl=64 time=9.00 ms

Для первоначальной проверки повесим внешний ip на loopback ноутбука:

# ip addr add 89.108.76.161 dev lo

Направляем весь трафик ноутбука через туннель, чтобы доходили обратные пакеты до клиентов, которые будут обращаться к 89.108.76.161 (192.168.88.1 — шлюз ноутбука по умолчанию):

# ip r add 95.163.241.96/32 via 192.168.88.1  # ip r add default via 10.15.0.1 

Убедимся, что цепочка FORWARD не заблокирована:

# iptables-save | grep FORWARD :FORWARD ACCEPT [67644779:42335638975] :FORWARD ACCEPT [149377:28667150]

и

# sysctl -w net.ipv4.ip_forward=1

VPS

Проверяем доступность 89.108.76.161 с VPS:

# ping 89.108.76.161 PING 89.108.76.161 (89.108.76.161) 56(84) bytes of data. 64 bytes from 89.108.76.161: icmp_seq=1 ttl=64 time=6.90 ms 64 bytes from 89.108.76.161: icmp_seq=2 ttl=64 time=38.7 ms 64 bytes from 89.108.76.161: icmp_seq=3 ttl=64 time=59.9 ms

Запускаем демон, который будет отвечать на arp запросы:

# farpd -d -i eth0 89.108.76.161

Теперь заработает ping 89.108.76.161 из внешней сети (например, с телефона, подключенного к сети оператора).

Ноутбук

Напомним, на ноутбуке (гипервизор) запущена виртуальная машина (ВМ), в которой бегает minikube. Она соединена с бриджем virbr0 гипервизора:

# ip a 19: virbr0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default qlen 1000     link/ether 52:54:00:c3:6e:e6 brd ff:ff:ff:ff:ff:ff     inet 192.168.122.1/24 brd 192.168.122.255 scope global virbr0        valid_lft forever preferred_lft forever

Удалим внешний адрес с lo:

# ip addr del 89.108.76.161 dev lo

Настроим маршрутизацию пакетов к 89.108.76.161 в сторону ВМ:

# ip r add 89.108.76.161 via 192.168.122.245

ВМ

Интерфейсы ВМ:

l@minikube2:~$ ip a 1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000     link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00     inet 127.0.0.1/8 scope host lo        valid_lft forever preferred_lft forever     inet6 ::1/128 scope host         valid_lft forever preferred_lft forever 2: enp1s0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP group default qlen 1000     link/ether 52:54:00:a5:b3:df brd ff:ff:ff:ff:ff:ff     inet 192.168.122.245/24 brd 192.168.122.255 scope global dynamic enp1s0        valid_lft 2292sec preferred_lft 2292sec     inet6 fe80::5054:ff:fea5:b3df/64 scope link         valid_lft forever preferred_lft forever 3: br-5b72cdfd77e4: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default      link/ether 02:42:01:94:a2:a5 brd ff:ff:ff:ff:ff:ff     inet 192.168.58.1/24 brd 192.168.58.255 scope global br-5b72cdfd77e4        valid_lft forever preferred_lft forever     inet6 fe80::42:1ff:fe94:a2a5/64 scope link         valid_lft forever preferred_lft forever

Состояние форвардинга:

l@minikube2:~$ sysctl -w net.ipv4.ip_forward net.ipv4.ip_forward = 1  l@minikube2:~$ sudo iptables-save | grep FORWARD :FORWARD ACCEPT [2663492:1312451658] :FORWARD ACCEPT [6299:278761] 

На машине запущен миникуб с тремя нодами, которые представляют из себя контейнеры, соединенные бриджем br-5b72cdfd77e4:

l@minikube2:~$ docker ps CONTAINER ID   IMAGE                                 COMMAND                  CREATED      STATUS        PORTS                                                                                                                                  NAMES d672c95f6adc   gcr.io/k8s-minikube/kicbase:v0.0.37   "/usr/local/bin/entr…"   5 days ago   Up 34 hours   127.0.0.1:49197->22/tcp, 127.0.0.1:49196->2376/tcp, 127.0.0.1:49195->5000/tcp, 127.0.0.1:49194->8443/tcp, 127.0.0.1:49193->32443/tcp   helm-m03 6eac7091ea0c   gcr.io/k8s-minikube/kicbase:v0.0.37   "/usr/local/bin/entr…"   5 days ago   Up 34 hours   127.0.0.1:49192->22/tcp, 127.0.0.1:49191->2376/tcp, 127.0.0.1:49190->5000/tcp, 127.0.0.1:49189->8443/tcp, 127.0.0.1:49188->32443/tcp   helm-m02 c02b9bb12c98   gcr.io/k8s-minikube/kicbase:v0.0.37   "/usr/local/bin/entr…"   5 days ago   Up 34 hours   127.0.0.1:49187->22/tcp, 127.0.0.1:49186->2376/tcp, 127.0.0.1:49185->5000/tcp, 127.0.0.1:49184->8443/tcp, 127.0.0.1:49183->32443/tcp   helm

Маршрутизируем пакеты на третью ноду:

l@minikube2:~$ sudo ip r add 89.108.76.161 via 192.168.58.4

Зайдем на нее:

l@minikube2:~$ minikube ssh -n helm-m03

Повесим внешний адрес на lo:

docker@helm-m03:~$ sudo ip addr add 89.108.76.161 dev lo docker@helm-m03:~$ ip a         1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000     link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00     inet 127.0.0.1/8 scope host lo        valid_lft forever preferred_lft forever         inet 89.108.76.161/32 scope global lo        valid_lft forever preferred_lft forever  21: eth0@if22: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default      link/ether 02:42:c0:a8:3a:04 brd ff:ff:ff:ff:ff:ff link-netnsid 0     inet 192.168.58.4/24 brd 192.168.58.255 scope global eth0        valid_lft forever preferred_lft forever

Поставим питон для проверки связности:

docker@helm-m03:~$ sudo apt update docker@helm-m03:~$ sudo apt install python

и запустим сервер на порту 8080:

docker@helm-m03:~$ python -m http.server

Проверим доступ к 89.108.76.161 извне по http://89.108.76.161:8000.

Переходим к Ingress-контроллеру. Добавляем его в кластер:

l@minikube2:~$ minikube addons enable ingress

Внесем внешний ip в ingress controller:

l@minikube2:~$ k patch svc -n ingress-nginx ingress-nginx-controller -p '{"spec":{"externalIPs":["89.108.76.161"]}}'

и у нас автоматически добавляется DNAT на pod, отвечающий за работу с ingress-nginx-controller:

l@minikube2:~$ sudo iptables-save | grep 89.108.76.161 -A KUBE-SERVICES -d 89.108.76.161/32 -p tcp -m comment --comment "ingress-nginx/ingress-nginx-controller:http external IP" -m tcp --dport 80 -j KUBE-EXT-CG5I4G2RS3ZVWGLK -A KUBE-SERVICES -d 89.108.76.161/32 -p tcp -m comment --comment "ingress-nginx/ingress-nginx-controller:https external IP" -m tcp --dport 443 -j KUBE-EXT-EDNDUDH2C75GIR6O

Развернем сервис whoami в Kubernetes:

l@minikube2:~$ cat > deployment.yaml <<EOF apiVersion: apps/v1 kind: Deployment metadata:   name: whoami     labels:     app: whoami spec:   replicas: 3   selector:     matchLabels:       app: whoami   template:     metadata:       labels:         app: whoami     spec:       containers:       - name: whoami         image: traefik/whoami         ports:         - containerPort: 80 EOF

l@minikube2:~$ cat > service.yaml <<EOF apiVersion: v1 kind: Service metadata:   name: extip spec:   ports:   - port: 80     targetPort: 80   selector:     app: whoami EOF

l@minikube2:~$ cat ingress.yaml <<EOF apiVersion: networking.k8s.io/v1 kind: Ingress metadata:   name: extip  spec:   ingressClassName: nginx   rules:   - host: extip.yourdomainhere     http:       paths:       - path: /         pathType: Prefix         backend:           service:             name: extip             port:               number: 80 EOF

l@minikube2:~$ k apply -f deployment.yaml l@minikube2:~$ k apply -f service.yaml l@minikube2:~$ k apply -f ingress.yaml

Пропишем в A записи домена extip.yourdomainhere внешний ip адрес 89.108.76.161. Обращаемся извне на http://extip.yourdomainhere, все работает!

curl extip.yourdomainhere Hostname: whoami-75d55b64f6-7q894 IP: 127.0.0.1 IP: 10.244.0.17 RemoteAddr: 10.244.0.3:50120 GET / HTTP/1.1 Host: extip.yourdomainhere User-Agent: Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/109.0 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8 Accept-Encoding: gzip, deflate Accept-Language: en-US,en;q=0.5 Upgrade-Insecure-Requests: 1 X-Forwarded-For: 192.168.58.4 X-Forwarded-Host: extip.yourdomainhere X-Forwarded-Port: 80 X-Forwarded-Proto: http X-Forwarded-Scheme: http X-Real-Ip: 192.168.58.4 X-Request-Id: f3c1f071b171b2ab1036241410acebcb X-Scheme: http

Итак, мы позаимствовали публичный ip у vps, завели его в Kubernetes, организовали маршрутизацию и связность до этого адреса, развернули сервис в Kubernetes и проверили его работу.

Надеюсь было интересно.


ссылка на оригинал статьи https://habr.com/ru/post/717842/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *