Как стать автором
Обновить

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

Уровень сложностиСредний
Время на прочтение8 мин
Количество просмотров11K

Работа с 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 и проверили его работу.


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

Теги:
Хабы:
Всего голосов 8: ↑8 и ↓0+8
Комментарии12

Публикации

Истории

Работа

DevOps инженер
43 вакансии

Ближайшие события

15 – 16 ноября
IT-конференция Merge Skolkovo
Москва
22 – 24 ноября
Хакатон «AgroCode Hack Genetics'24»
Онлайн
28 ноября
Конференция «TechRec: ITHR CAMPUS»
МоскваОнлайн
25 – 26 апреля
IT-конференция Merge Tatarstan 2025
Казань