Прим. перев.: Автор оригинальной статьи — Nicolas Leiva — архитектор решений Cisco, который решил поделиться со своими коллегами, сетевыми инженерами, о том, как устроена сеть Kubernetes изнутри. Для этого он исследует простейшую её конфигурацию в кластере, активно применяя здравый смысл, свои познания о сетях и стандартные утилиты Linux/Kubernetes. Получилось объёмно, зато весьма наглядно.
Помимо того факта, что руководство Kubernetes The Hard Way от Kelsey Hightower просто работает (даже на AWS!), мне понравилось, что сеть поддерживается в чистоте и простоте; и это замечательная возможность понять, какова роль, например, Container Network Interface (CNI). Сказав это, добавлю, что сеть Kubernetes в действительности не очень-то интуитивно понятна, особенно для новичков… а также не забывайте, что «такой вещи, как сети для контейнеров, попросту не существует».
Хотя уже есть неплохие материалы по этой теме (см. ссылки здесь), я не смог найти такого примера, что объединил бы всё необходимое с выводами команд, которые так любят и ненавидят сетевые инженеры, демонстрируя, что же на самом деле происходит за кулисами. Поэтому я и решил собрать информацию из множества источников — надеюсь, это поможет и вам лучше разобраться, как всё связано друг с другом. Эти знания важны не только для того, чтобы проверить себя, но и для упрощения процесса диагностики проблем. Можете последовать приведенному примеру в своём кластере из Kubernetes The Hard Way: все IP-адреса и настройки взяты оттуда (по состоянию коммитов на май 2018 года, до использования контейнеров Nabla).
А начнём мы с конца, когда у нас есть три контроллера и три рабочих узла:
Вы можете заметить, что здесь также есть как минимум три частные подсети! Немного терпения, и все они будут рассмотрены. Помните, что, хоть мы и ссылаемся на очень специфичные IP-префиксы, они попросту взяты из Kubernetes The Hard Way, так что имеют лишь локальную значимость, а вы вольны выбрать для своего окружения любой другой блок адресов в соответствии с RFC 1918. Для случая с IPv6 будет отдельная статья в блоге.
--private-network-ip
в GCP или опцией --private-ip-address
в AWS при выделении вычислительных ресурсов.for i in 0 1 2; do
gcloud compute instances create controller-${i} # ...
--private-network-ip 10.240.0.1${i} # ...
done
controllers_gcp.sh
)for i in 0 1 2; do
declare controller_id${i}=`aws ec2 run-instances # ...
--private-ip-address 10.240.0.1${i} # ...
done
controllers_aws.sh
)10.240.0.1${i}/24
, воркеры — 10.240.0.2${i}/24
) и публичный, назначенный облачным провайдером, о котором мы поговорим попозже, как доберёмся до NodePorts
.$ gcloud compute instances list
NAME ZONE MACHINE_TYPE PREEMPTIBLE INTERNAL_IP EXTERNAL_IP STATUS
controller-0 us-west1-c n1-standard-1 10.240.0.10 35.231.XXX.XXX RUNNING
worker-1 us-west1-c n1-standard-1 10.240.0.21 35.231.XX.XXX RUNNING
...
$ aws ec2 describe-instances --query 'Reservations[].Instances[].[Tags[?Key==`Name`].Value[],PrivateIpAddress,PublicIpAddress]' --output text | sed '$!N;s/\n/ /'
10.240.0.10 34.228.XX.XXX controller-0
10.240.0.21 34.173.XXX.XX worker-1
...
ping
установлен на хост).POD_CIDR=10.200.${i}.0/24
для worker-${i}
.«Плагин CNI отвечает за добавление сетевого интерфейса в сетевом пространстве имён контейнера (например, один конец veth-пары) и выполнение необходимых изменений на хосте (например, подключение второго конца veth к мосту). Затем он должен назначить IP интерфейсу и настроить маршруты соответственно разделу «IP Address Management» путём вызова нужного плагина IPAM». (из Container Network Interface Specification)
«Пространство имён оборачивает глобальный системный ресурс в абстракцию, которая видна процессам в этом пространстве имён таким образом, что у них есть свой изолированный экземпляр глобального ресурса. Изменения в глобальном ресурсе видны другим процессам, входящим в это пространство имён, но не видны иным процессам». (из man-страницы namespaces)
Cgroup
, IPC
, Network
, Mount
, PID
, User
, UTS
). Сетевые (Network
) пространства имён (CLONE_NEWNET
) определяют сетевые ресурсы, которые доступны процессу: «У каждого сетевого пространства имён есть свои сетевые устройства, IP-адреса, таблицы IP-маршрутизации, директория /proc/net
, номера портов и так далее» (из статьи «Namespaces in operation»).«Виртуальная сетевая пара (veth) предлагает абстракцию в виде „трубы“, которую можно использовать для создания туннелей между сетевыми пространствами имён или же для создания моста к физическому сетевому устройству в ином сетевом пространстве. Когда пространство имён освобождается, все находящиеся в нём veth-устройства уничтожаются». (из man-страницы network namespaces)
«Плагин CNI выбирается передачей опции командной строки--network-plugin=cni
в Kubelet. Kubelet считывает файл из--cni-conf-dir
(по умолчанию это/etc/cni/net.d
) и использует конфигурацию CNI из этого файла для настройки сети каждого пода». (из Network Plugin Requirements)
--?cni-bin-dir
(по умолчанию это /opt/cni/bin
).kubelet.service
включают в себя --network-plugin=cni
:[Service]
ExecStart=/usr/local/bin/kubelet \ --config=/var/lib/kubelet/kubelet-config.yaml \ --network-plugin=cni \ ...
pause
, который «служит „родительским контейнером“ для всех контейнеров пода» (из статьи «The Almighty Pause Container»). Затем Kubernetes выполняет плагин CNI для присоединения контейнера pause
к сети. Все контейнеры пода используют сетевое пространство имён (netns
) этого pause
-контейнера.{
"cniVersion": "0.3.1",
"name": "bridge",
"type": "bridge",
"bridge": "cnio0",
"isGateway": true,
"ipMasq": true,
"ipam": {
"type": "host-local",
"ranges": [
[{"subnet": "${POD_CIDR}"}]
],
"routes": [{"dst": "0.0.0.0/0"}]
}
}
bridge
для конфигурации программного моста Linux (L2) в корневом пространстве имён под названием cnio0
(имя по умолчанию — cni0
), который выступает в роли шлюза ("isGateway": true
).ipam
). В данном случае используется тип host-local
, «который хранит состояние локально на файловой системе хоста, чем обеспечивает уникальность IP-адресов на одном хосте» (из описания host-local
). Плагин IPAM возвращает эту информацию предыдущему плагину (bridge
), благодаря чему могут быть настроены все указанные в конфиге маршруты ("routes": [{"dst": "0.0.0.0/0"}]
). Если gw
не указан, он берётся из подсети. Маршрут по умолчанию тоже настраивается в сетевом пространстве имён подов, указывая на мост (который настраивается как первый IP подсети пода)."ipMasq": true
) трафика, исходящего из сети подов. В действительности нам здесь не нужен NAT, но таков конфиг в Kubernetes The Hard Way. Поэтому для полноты картины я должен упомянуть, что записи в iptables
плагина bridge
настроены для этого конкретного примера. Все пакеты из пода, получатель которых не входит в диапазон 224.0.0.0/4
, будет за NAT'ом, что не совсем соответствует требованию «все контейнеры могут общаться с любыми другими контейнерами без использования NAT». Что ж, мы ещё докажем, почему NAT не нужен…nginx
отсюда. Воспользуемся lsns
с опцией -t
для выбора нужного типа пространства имён (т.е. net
):ubuntu@worker-0:~$ sudo lsns -t net
NS TYPE NPROCS PID USER COMMAND
4026532089 net 113 1 root /sbin/init
4026532280 net 2 8046 root /pause
4026532352 net 4 16455 root /pause
4026532426 net 3 27255 root /pause
-i
к ls
мы можем найти их номера inode:ubuntu@worker-0:~$ ls -1i /var/run/netns
4026532352 cni-1d85bb0c-7c61-fd9f-2adc-f6e98f7a58af
4026532280 cni-7cec0838-f50c-416a-3b45-628a4237c55c
4026532426 cni-912bcc63-712d-1c84-89a7-9e10510808a0
ip netns
:ubuntu@worker-0:~$ ip netns
cni-912bcc63-712d-1c84-89a7-9e10510808a0 (id: 2)
cni-1d85bb0c-7c61-fd9f-2adc-f6e98f7a58af (id: 1)
cni-7cec0838-f50c-416a-3b45-628a4237c55c (id: 0)
cni-912bcc63–712d-1c84–89a7–9e10510808a0
(4026532426
), можно выполнить, например, такую команду:ubuntu@worker-0:~$ sudo ls -l /proc/[1-9]*/ns/net | grep 4026532426 | cut -f3 -d"/" | xargs ps -p
PID TTY STAT TIME COMMAND
27255 ? Ss 0:00 /pause
27331 ? Ss 0:00 nginx: master process nginx -g daemon off;
27355 ? S 0:00 nginx: worker process
pause
в этом поде мы запустили nginx
. Контейнер pause
делит пространства имён net
и ipc
со всеми остальными контейнерами пода. Запомним PID от pause
— 27255; мы ещё вернёмся к нему.kubectl
:$ kubectl get pods -o wide | grep nginx
nginx-65899c769f-wxdx6 1/1 Running 0 5d 10.200.0.4 worker-0
$ kubectl describe pods nginx-65899c769f-wxdx6
Name: nginx-65899c769f-wxdx6
Namespace: default
Node: worker-0/10.240.0.20
Start Time: Thu, 05 Jul 2018 14:20:06 -0400
Labels: pod-template-hash=2145573259
run=nginx
Annotations: <none>
Status: Running
IP: 10.200.0.4
Controlled By: ReplicaSet/nginx-65899c769f
Containers:
nginx:
Container ID: containerd://4c0bd2e2e5c0b17c637af83376879c38f2fb11852921b12413c54ba49d6983c7
Image: nginx
...
nginx-65899c769f-wxdx6
— и ID одного из его контейнеров (nginx
), но про pause
— пока что ни слова. Копнём глубже рабочий узел, чтобы сопоставить все данные. Помните, что в Kubernetes The Hard Way не используется Docker, поэтому для получения подробностей о контейнере мы обращаемся к консольной утилите containerd — ctr (см. также статью «Интеграция containerd с Kubernetes, заменяющая Docker, готова к production» — прим. перев.):ubuntu@worker-0:~$ sudo ctr namespaces ls
NAME LABELS
k8s.io
k8s.io
), можно получить ID контейнера nginx
:ubuntu@worker-0:~$ sudo ctr -n k8s.io containers ls | grep nginx
4c0bd2e2e5c0b17c637af83376879c38f2fb11852921b12413c54ba49d6983c7 docker.io/library/nginx:latest io.containerd.runtime.v1.linux
pause
тоже:ubuntu@worker-0:~$ sudo ctr -n k8s.io containers ls | grep pause
0866803b612f2f55e7b6b83836bde09bd6530246239b7bde1e49c04c7038e43a k8s.gcr.io/pause:3.1 io.containerd.runtime.v1.linux
21640aea0210b320fd637c22ff93b7e21473178de0073b05de83f3b116fc8834 k8s.gcr.io/pause:3.1 io.containerd.runtime.v1.linux
d19b1b1c92f7cc90764d4f385e8935d121bca66ba8982bae65baff1bc2841da6 k8s.gcr.io/pause:3.1 io.containerd.runtime.v1.linux
nginx
, заканчивающийся на …983c7
, совпадает с тем, что мы получили от kubectl
. Посмотрим, получится ли выяснить, какой pause
-контейнер принадлежит поду nginx
:ubuntu@worker-0:~$ sudo ctr -n k8s.io task ls
TASK PID STATUS
...
d19b1b1c92f7cc90764d4f385e8935d121bca66ba8982bae65baff1bc2841da6 27255 RUNNING
4c0bd2e2e5c0b17c637af83376879c38f2fb11852921b12413c54ba49d6983c7 27331 RUNNING
cni-912bcc63–712d-1c84–89a7–9e10510808a0
?ubuntu@worker-0:~$ sudo ctr -n k8s.io containers info d19b1b1c92f7cc90764d4f385e8935d121bca66ba8982bae65baff1bc2841da6
{
"ID": "d19b1b1c92f7cc90764d4f385e8935d121bca66ba8982bae65baff1bc2841da6",
"Labels": {
"io.cri-containerd.kind": "sandbox",
"io.kubernetes.pod.name": "nginx-65899c769f-wxdx6",
"io.kubernetes.pod.namespace": "default",
"io.kubernetes.pod.uid": "0b35e956-8080-11e8-8aa9-0a12b8818382",
"pod-template-hash": "2145573259",
"run": "nginx"
},
"Image": "k8s.gcr.io/pause:3.1",
...
ubuntu@worker-0:~$ sudo ctr -n k8s.io containers info 4c0bd2e2e5c0b17c637af83376879c38f2fb11852921b12413c54ba49d6983c7
{
"ID": "4c0bd2e2e5c0b17c637af83376879c38f2fb11852921b12413c54ba49d6983c7",
"Labels": {
"io.cri-containerd.kind": "container",
"io.kubernetes.container.name": "nginx",
"io.kubernetes.pod.name": "nginx-65899c769f-wxdx6",
"io.kubernetes.pod.namespace": "default",
"io.kubernetes.pod.uid": "0b35e956-8080-11e8-8aa9-0a12b8818382"
},
"Image": "docker.io/library/nginx:latest",
...
nginx-65899c769f-wxdx6
) и сетевом пространстве имён (cni-912bcc63–712d-1c84–89a7–9e10510808a0
):4c0bd2e2e5c0b17c637af83376879c38f2fb11852921b12413c54ba49d6983c7
);d19b1b1c92f7cc90764d4f385e8935d121bca66ba8982bae65baff1bc2841da6
).nginx-65899c769f-wxdx6
) подключён к сети? Воспользуемся полученным ранее PID 27255 от pause
для запуска команд в его сетевом пространстве имён (cni-912bcc63–712d-1c84–89a7–9e10510808a0
):ubuntu@worker-0:~$ sudo ip netns identify 27255
cni-912bcc63-712d-1c84-89a7-9e10510808a0
nsenter
с опцией -t
, определяющей целевой PID, и -n
без указания файла, чтобы попасть в сетевое пространство имён целевого процесса (27255). Вот что скажет ip link show
:ubuntu@worker-0:~$ sudo nsenter -t 27255 -n ip link show
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN mode DEFAULT group default qlen 1000
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
3: eth0@if7: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP mode DEFAULT group default
link/ether 0a:58:0a:c8:00:04 brd ff:ff:ff:ff:ff:ff link-netnsid 0
ifconfig eth0
:ubuntu@worker-0:~$ sudo nsenter -t 27255 -n ifconfig eth0
eth0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500
inet 10.200.0.4 netmask 255.255.255.0 broadcast 0.0.0.0
inet6 fe80::2097:51ff:fe39:ec21 prefixlen 64 scopeid 0x20<link>
ether 0a:58:0a:c8:00:04 txqueuelen 0 (Ethernet)
RX packets 540 bytes 42247 (42.2 KB)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 177 bytes 16530 (16.5 KB)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
kubectl get pod
, настроен на интерфейсе eth0
пода. Этот интерфейс — часть veth-пары, один из концов которой в поде, а второй — в корневом пространстве имён. Чтобы узнать интерфейс второго конца, воспользуемся ethtool
:ubuntu@worker-0:~$ sudo ip netns exec cni-912bcc63-712d-1c84-89a7-9e10510808a0 ethtool -S eth0
NIC statistics:
peer_ifindex: 7
ifindex
пира — 7. Проверим, что он в корневом пространстве имён. Это можно сделать с помощью ip link
:ubuntu@worker-0:~$ ip link | grep '^7:'
7: veth71f7d238@if3: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master cnio0 state UP mode DEFAULT group default
ubuntu@worker-0:~$ sudo cat /sys/class/net/veth71f7d238/ifindex
7
brctl
посмотрим, кто ещё подключён к Linux-мосту:ubuntu@worker-0:~$ brctl show cnio0
bridge name bridge id STP enabled interfaces
cnio0 8000.0a580ac80001 no veth71f7d238
veth73f35410
vethf273b35f
ubuntu@worker-0:~$ sudo ip netns exec cni-912bcc63-712d-1c84-89a7-9e10510808a0 ip route show
default via 10.200.0.1 dev eth0
10.200.0.0/24 dev eth0 proto kernel scope link src 10.200.0.4
default via 10.200.0.1
). Теперь посмотрим таблицу маршрутизации хоста:ubuntu@worker-0:~$ ip route list
default via 10.240.0.1 dev eth0 proto dhcp src 10.240.0.20 metric 100
10.200.0.0/24 dev cnio0 proto kernel scope link src 10.200.0.1
10.240.0.0/24 dev eth0 proto kernel scope link src 10.240.0.20
10.240.0.1 dev eth0 proto dhcp scope link src 10.240.0.20 metric 100
kubectl create -f busybox.yaml
создадим два идентичных контейнера busybox
с Replication Controller:apiVersion: v1
kind: ReplicationController
metadata:
name: busybox0
labels:
app: busybox0
spec:
replicas: 2
selector:
app: busybox0
template:
metadata:
name: busybox0
labels:
app: busybox0
spec:
containers:
- image: busybox
command:
- sleep
- "3600"
imagePullPolicy: IfNotPresent
name: busybox
restartPolicy: Always
busybox.yaml
)$ kubectl get pods -o wide
NAME READY STATUS RESTARTS AGE IP NODE
busybox0-g6pww 1/1 Running 0 4s 10.200.1.15 worker-1
busybox0-rw89s 1/1 Running 0 4s 10.200.0.21 worker-0
...
$ kubectl exec -it busybox0-rw89s -- ping -c 2 10.200.1.15
PING 10.200.1.15 (10.200.1.15): 56 data bytes
64 bytes from 10.200.1.15: seq=0 ttl=62 time=0.528 ms
64 bytes from 10.200.1.15: seq=1 ttl=62 time=0.440 ms
--- 10.200.1.15 ping statistics ---
2 packets transmitted, 2 packets received, 0% packet loss
round-trip min/avg/max = 0.440/0.484/0.528 ms
tcpdump
или conntrack
:ubuntu@worker-0:~$ sudo conntrack -L | grep 10.200.1.15
icmp 1 29 src=10.200.0.21 dst=10.200.1.15 type=8 code=0 id=1280 src=10.200.1.15 dst=10.240.0.20 type=0 code=0 id=1280 mark=0 use=1
ubuntu@worker-1:~$ sudo conntrack -L | grep 10.200.1.15
icmp 1 28 src=10.240.0.20 dst=10.200.1.15 type=8 code=0 id=1280 src=10.200.1.15 dst=10.240.0.20 type=0 code=0 id=1280 mark=0 use=1
ubuntu@worker-0:~$ sudo iptables -t nat -Z POSTROUTING -L -v
Chain POSTROUTING (policy ACCEPT 0 packets, 0 bytes)
pkts bytes target prot opt in out source destination
...
5 324 CNI-be726a77f15ea47ff32947a3 all -- any any 10.200.0.0/24 anywhere /* name: "bridge" id: "631cab5de5565cc432a3beca0e2aece0cef9285482b11f3eb0b46c134e457854" */
Zeroing chain `POSTROUTING'
"ipMasq": true
из конфигурации плагина CNI, можно увидеть следующее (эта операция выполняется исключительно для образовательных целей — не рекомендуем менять конфиг на работающем кластере!):$ kubectl get pods -o wide
NAME READY STATUS RESTARTS AGE IP NODE
busybox0-2btxn 1/1 Running 0 16s 10.200.0.15 worker-0
busybox0-dhpx8 1/1 Running 0 16s 10.200.1.13 worker-1
...
$ kubectl exec -it busybox0-2btxn -- ping -c 2 10.200.1.13
PING 10.200.1.6 (10.200.1.6): 56 data bytes
64 bytes from 10.200.1.6: seq=0 ttl=62 time=0.515 ms
64 bytes from 10.200.1.6: seq=1 ttl=62 time=0.427 ms
--- 10.200.1.6 ping statistics ---
2 packets transmitted, 2 packets received, 0% packet loss
round-trip min/avg/max = 0.427/0.471/0.515 ms
ubuntu@worker-0:~$ sudo conntrack -L | grep 10.200.1.13
icmp 1 29 src=10.200.0.15 dst=10.200.1.13 type=8 code=0 id=1792 src=10.200.1.13 dst=10.200.0.15 type=0 code=0 id=1792 mark=0 use=1
ubuntu@worker-1:~$ sudo conntrack -L | grep 10.200.1.13
icmp 1 27 src=10.200.0.15 dst=10.200.1.13 type=8 code=0 id=1792 src=10.200.1.13 dst=10.200.0.15 type=0 code=0 id=1792 mark=0 use=1
busybox
, что IP-адреса, выделенные для пода busybox
, были разными в каждом случае. Что, если бы мы хотели сделать эти контейнеры доступными для связи со стороны других подов? Можно было бы взять текущие IP-адреса пода, но они изменятся. По этой причине нужно настроить ресурс Service
, который будет проксировать запросы ко множеству недолговечных подов.«Service в Kubernetes — абстракция, определяющая логическую совокупность подов и политику, по которой к ним можно обращаться». (из документации Kubernetes Services)
ClusterIP
, настраивающий IP-адрес из CIDR-блока кластера (т.е. доступного только из кластера). Одним из таких примеров является DNS Cluster Add-on, настроенный в Kubernetes The Hard Way.# ...
apiVersion: v1
kind: Service
metadata:
name: kube-dns
namespace: kube-system
labels:
k8s-app: kube-dns
kubernetes.io/cluster-service: "true"
addonmanager.kubernetes.io/mode: Reconcile
kubernetes.io/name: "KubeDNS"
spec:
selector:
k8s-app: kube-dns
clusterIP: 10.32.0.10
ports:
- name: dns
port: 53
protocol: UDP
- name: dns-tcp
port: 53
protocol: TCP
# ...
kube-dns.yaml
)kubectl
показывает, что Service
помнит об endpoint'ах и делает их трансляцию:$ kubectl -n kube-system describe services
...
Selector: k8s-app=kube-dns
Type: ClusterIP
IP: 10.32.0.10
Port: dns 53/UDP
TargetPort: 53/UDP
Endpoints: 10.200.0.27:53
Port: dns-tcp 53/TCP
TargetPort: 53/TCP
Endpoints: 10.200.0.27:53
...
iptables
. Пройдёмся по правилам, созданным для этого примера. Их полный список можно увидеть командой iptables-save
.OUTPUT
) или приходят на сетевой интерфейс (PREROUTING
), они проходят через следующие цепочки iptables
:-A PREROUTING -m comment --comment "kubernetes service portals" -j KUBE-SERVICES
-A OUTPUT -m comment --comment "kubernetes service portals" -j KUBE-SERVICES
-A KUBE-SERVICES -d 10.32.0.10/32 -p tcp -m comment --comment "kube-system/kube-dns:dns-tcp cluster IP" -m tcp --dport 53 -j KUBE-SVC-ERIFXISQEP7F7OF4
-A KUBE-SVC-ERIFXISQEP7F7OF4 -m comment --comment "kube-system/kube-dns:dns-tcp" -j KUBE-SEP-32LPCMGYG6ODGN3H
-A KUBE-SEP-32LPCMGYG6ODGN3H -p tcp -m comment --comment "kube-system/kube-dns:dns-tcp" -m tcp -j DNAT --to-destination 10.200.0.27:53
-A KUBE-SERVICES -d 10.32.0.10/32 -p udp -m comment --comment "kube-system/kube-dns:dns cluster IP" -m udp --dport 53 -j KUBE-SVC-TCOU7JCQXEZGVUNU
-A KUBE-SVC-TCOU7JCQXEZGVUNU -m comment --comment "kube-system/kube-dns:dns" -j KUBE-SEP-LRUTK6XRXU43VLIG
-A KUBE-SEP-LRUTK6XRXU43VLIG -p udp -m comment --comment "kube-system/kube-dns:dns" -m udp -j DNAT --to-destination 10.200.0.27:53
Services
в Kubernetes. В частности, в Kubernetes The Hard Way рассказано про NodePort
— см. Smoke Test: Services.kubectl expose deployment nginx --port 80 --type NodePort
NodePort
публикует сервис на IP-адресе каждого узла, размещая его на статический порт (он и называется NodePort
). К сервису NodePort
можно обратиться и извне кластера. Проверить выделенный порт (в данном случае — 31088) можно с помощью kubectl
:$ kubectl describe services nginx
...
Type: NodePort
IP: 10.32.0.53
Port: <unset> 80/TCP
TargetPort: 80/TCP
NodePort: <unset> 31088/TCP
Endpoints: 10.200.1.18:80
...
http://${EXTERNAL_IP}:31088/
. Здесь EXTERNAL_IP
— публичный IP-адрес любого рабочего экземпляра. В этом примере я использовал публичный IP-адрес worker-0. Запрос получается узлом с внутренним IP-адресом 10.240.0.20 (публичным NAT'ом занимается облачный провайдер), однако сервис в действительно запущен на другом узле (worker-1, что можно увидеть по IP-адресу endpoint'а — 10.200.1.18):ubuntu@worker-0:~$ sudo conntrack -L | grep 31088
tcp 6 86397 ESTABLISHED src=173.38.XXX.XXX dst=10.240.0.20 sport=30303 dport=31088 src=10.200.1.18 dst=10.240.0.20 sport=80 dport=30303 [ASSURED] mark=0 use=1
ubuntu@worker-1:~$ sudo conntrack -L | grep 80
tcp 6 86392 ESTABLISHED src=10.240.0.20 dst=10.200.1.18 sport=14802 dport=80 src=10.200.1.18 dst=10.240.0.20 sport=80 dport=14802 [ASSURED] mark=0 use=1
iptables
таковы:-A KUBE-NODEPORTS -p tcp -m comment --comment "default/nginx:" -m tcp --dport 31088 -j KUBE-SVC-4N57TFCL4MD7ZTDA
-A KUBE-SVC-4N57TFCL4MD7ZTDA -m comment --comment "default/nginx:" -j KUBE-SEP-UGTFMET44DQG7H7H
-A KUBE-SEP-UGTFMET44DQG7H7H -p tcp -m comment --comment "default/nginx:" -m tcp -j DNAT --to-destination 10.200.1.18:80
LoadBalancer
, — который делает сервис публично доступным с помощью балансировщика нагрузки облачного провайдера, но статья уже и без того получилась большой.К сожалению, не доступен сервер mySQL