В этой статье я хочу показать, что меняется в состоянии кластера kubernetes при создании базовых ресурсов. В качестве кластерной платформы был выбран minikube, чтобы каждый мог повторить проделанные мной шаги безо всяких проблем.
Перед началом работы с кластером была проделана следующая подготовительная работа:
--vm-driver=docker \
--alsologtostderr \
--extra-config=kubelet.v=6 \
--extra-config=apiserver.v=6 \
--extra-config=controller-manager.v=6 \
--extra-config=scheduler.v=6 \
--extra-config=kube-proxy.v=6 \
--extra-config=etcd.log-level=debug \
--v=10 \
&>minikube.logs
В кластере Minikube по умолчанию были выбраны следующие компоненты: для CNI был выбран bridge, для DNS - CoreDNS, а для CRI - Containerd.
Что такое под и почему для каждого контейнера Control-Plane существует прихвостень с похожим именем разберемся в разделе создания собственного пода.
Касательно настройки сетевых интерфейсов, то существуют как базовые интерфейсы: lo и eth0, так и специализированные для Kubenrentes интерфейсы: bridge и veth<id>. Docker0 интерфейс создался автоматически Docker’ом, но находится в состоянии DOWN и нигде не используется:
$ ip a
1: lo: <LOOPBACK,UP,LOWER_UP> state UNKNOWN group default
inet 127.0.0.1/8 scope host lo
2: docker0: <NO-CARRIER,BROADCAST,MULTICAST,UP> state DOWN group default
inet 172.17.0.1/16 brd 172.17.255.255 scope global docker0
3: bridge: <BROADCAST,MULTICAST,UP,LOWER_UP> state UP group default
inet 10.244.0.1/16 brd 10.244.255.255 scope global bridge
4: vethe37be523@if2: <BROADCAST,MULTICAST,UP,LOWER_UP> master bridge state UP group default
352: eth0@if353: <BROADCAST,MULTICAST,UP,LOWER_UP> state UP group default
inet 192.168.49.2/24 brd 192.168.49.255 scope global eth0
Маршруты тоже базовые, но можно заметить что весь трафик Kubernetes сети (10.244.0.0/16) идет в Bridge:
$ ip r
default via 192.168.49.1 dev eth0
10.244.0.0/16 dev bridge proto kernel scope link src 10.244.0.1
172.17.0.0/16 dev docker0 proto kernel scope link src 172.17.0.1 linkdown
192.168.49.0/24 dev eth0 proto kernel scope link src 192.168.49.2
Что это за интерфейсы такие Bridge и vethe37be523@if2? Пока пропустим объяснение и вернемся к ним, когда будем разворачивать собственный под.
Переходим к созданию первого простого пода:
Зачем создалось два контейнера? Хороший вопрос. Ответ - минимальное количество контейнеров в одном Kubernetes поде - 2, и вот почему. Второй контейнер зовётся pause (вот кстати его исходный код), и после глубокого анализа я выяснил, что нужен он для выполнения следующих функций и только (все остальные проблемы, о которых рассказывают в интернете можно спокойно решить и без этого контейнера). Во-первых, резервация Namespaces, для того чтобы при перезапуске контейнера не пересоздавать выделенные для него Namespaces, благодаря тому что Pause контейнер продолжает работать даже при завершении всех других контейнеров пода, а завершить его может только сам Kubernetes (Kubelet). А во-вторых, выполнение функций процесса с PID 1 при создании контейнеров, разделяющих один PID Namespace. Делать в таком случае какой-то определенный контейнер главным не камильфо, так как появляются следующие проблемы:
$ lsns
NS TYPE NPROCS PID USER COMMAND
4026534267 mnt 1 6705 65535 /pause
4026534268 uts 1 6705 65535 /pause
4026534269 ipc 4 6705 65535 /pause
4026534270 pid 1 6705 65535 /pause
4026534271 net 4 6705 65535 /pause
4026534341 cgroup 1 6705 65535 /pause
4026534344 mnt 3 6922 root <vault>
4026534345 uts 3 6922 root <vault>
4026534346 pid 3 6922 root <vault>
4026534347 cgroup 3 6922 root <vault>
Файловая система берется из образа контейнера и каталог монтиорвания можно увидеть, если посмотреть на вывод mount внутри контейнера. Из вывода команды ниже можно заметить, что в контейнер по умолчанию также монтируются /etc/hosts, /etc/resolv.conf и /etc/hostname для работы DNS:
$ mount
overlay on / type overlay (
rw,
seclabel,
relatime,
lowerdir=/var/lib/docker/overlay2/l/HADIUDBRK6K6CA2EAYIPIN54CF:
/var/lib/docker/overlay2/l/ZEDDRHSJ5IEREEVGU2PV2YSNGI:
/var/lib/docker/overlay2/l/DEEBZ4NIHQEJX7VSYESA2YWCAJ:
/var/lib/docker/overlay2/l/CPE4IDREOEF54DX2XOUTMT3U2S:
/var/lib/docker/overlay2/l/FQBHQ3BPW6VV3YPG4WEKW4WMEB:
/var/lib/docker/overlay2/l/6IPXNYC6WPK4IGINXKMNVIQ4BH,
upperdir=/var/lib/docker/overlay2/443259d564638ef254e193583dbc23bd5e5a49f5930dd710edff45177454cc9a/diff,
workdir=/var/lib/docker/overlay2/443259d564638ef254e193583dbc23bd5e5a49f5930dd710edff45177454cc9a/work)
/dev/nvme0n1p3 on /etc/resolv.conf type btrfs (...)
/dev/nvme0n1p3 on /etc/hostname type btrfs (...)
/dev/nvme0n1p3 on /etc/hosts type btrfs (...)
LowerDir - включает файловые системы всех слоев внутри контейнера, кроме последнего, UpperDir - файловая система самого верхнего слоя контейнера где отображаются любые изменения во время выполнения, WorkDir - внутренний рабочий каталог, используемый для управления файловой системой (задается в Dockerfile).
Возвращаемся к вопросу о новых сетевых интерфейсах. Bridge является связующим звеном через которое идет весь внутренний трафик Kubernetes, как видно из вывода команды ниже он перенаправляет трафик на veth интерфейсы создаваемые для общения с другими NET Namespaces, в данном случае данный отдельный NET Namespace имеет созданный нами контейнер (еще CoreDNS контейнер, но принципиально они не отличается). Что касается компонентов Control-Plane, то они разворачиваются в NET Namespace хоста, а значит никаких veth не требуют:
bridge link show
5: veth2d3d80cb@if2@docker0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 master bridge state forwarding
Внутри пода есть связанный с veth eth0 интерфейс, на который перенаправляется весь трафик во внешнюю относительно пода сеть. Внутри же пода контейнеры общаются через localhost:
$ ip a
1: lo: <LOOPBACK,UP,LOWER_UP> state UNKNOWN
inet 127.0.0.1/8 scope host lo
2: eth0@if5: <BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN> state UP
inet 10.244.0.3/16 brd 10.244.255.255 scope global eth0
$ ip r
default via 10.244.0.1 dev eth0
10.244.0.0/16 dev eth0 scope link src 10.244.0.3
Что касается режима CNI - bridge, то у него есть один существенный минус, а именно он плохо работает в многоузловом режиме, и если необходимо отправлять трафик на контейнер на другом узле, то нужно выбирать другой CNI, например kindnet или calico. Благо Minikube делает это за нас.
Из iptables можно сделать вывод, что весь исходящий трафик контейнера идущий во внешнюю сеть меняет адрес отправителя на адрес сетевого интерфейса (функционал MASQUERADE):
:CNI-1f120c2dca3af9923d1e29d4 - [0:0] -- "Цепочка контейнера vault"
# Если пакет с ip отправителя 10.244.0.3, то перейти в цепочку контейнера vault
-A POSTROUTING -s 10.244.0.3/32 -j CNI-1f120c2dca3af9923d1e29d4
# Если пакет идет в сеть Kubernetes, то MASQUERADE не нужен
-A CNI-1f120c2dca3af9923d1e29d4 -d 10.244.0.0/16 -j ACCEPT
# Если пакет идет не в сеть Kubernetes, то выполнение MASQUERADE
-A CNI-1f120c2dca3af9923d1e29d4 ! -d 224.0.0.0/4 -j MASQUERADE
Касательно того как был создан контейнер (не под, о том как создается под в интернете куча информации) то вот схема:
/registry/services/endpoints/default/vault
/registry/services/specs/default/vault
:KUBE-SEP-6X3WIXWFLXPKXKEP - [0:0] -- "Endpoints цепочка"
:KUBE-SVC-VUGVVJAHU4RL2TIW - [0:0] -- "Clusert IP цепочка"
#Направление пакета в Clusert IP цепочку если адрес назначения 10.104.205.29
-A KUBE-SERVICES -d 10.104.205.29/32 -p tcp -m tcp --dport 8200 -j KUBE-SVC-VUGVVJAHU4RL2TIW
#Отправление пакета не из сети Kubernetes, идущих в Service, в цепочку для MASQUERADE
-A KUBE-SVC-VUGVVJAHU4RL2TIW ! -s 10.244.0.0/16 -d 10.104.205.29/32 -p tcp -m comment --comment "default/vault cluster IP" -m tcp --dport 8200 -j KUBE-MARK-MASQ
#Направление пакета в Endpoints цепочку
-A KUBE-SVC-VUGVVJAHU4RL2TIW -m comment --comment "default/vault -> 10.244.0.3:8200" -j KUBE-SEP-6X3WIXWFLXPKXKEP
#Отправление пакета контейнера в цепочку для MASQUERADE
-A KUBE-SEP-6X3WIXWFLXPKXKEP -s 10.244.0.3/32 -m comment --comment "default/vault" -j KUBE-MARK-MASQ
#Изменение адрса получателя на 10.244.0.3:8200 (ip контейнера)
-A KUBE-SEP-6X3WIXWFLXPKXKEP -p tcp -m comment --comment "default/vault" -m tcp -j DNAT --to-destination 10.244.0.3:8200
#Направление пакета в NodePort цепочку если порт назначения 30074
-A KUBE-NODEPORTS -p tcp -m comment --comment "default/vault" -m tcp --dport 30074 -j KUBE-EXT-VUGVVJAHU4RL2TIW
#Отправление всех пакетов в цепочку для MASQUERADE
-A KUBE-EXT-VUGVVJAHU4RL2TIW -m comment --comment "masquerade traffic for default/vault external destinations" -j KUBE-MARK-MASQ
#Направление пакета в Cluster IP цепочку
-A KUBE-EXT-VUGVVJAHU4RL2TIW -j KUBE-SVC-VUGVVJAHU4RL2TIW
Пояснение касательно KUBE-MARK-MASQ цепочки, более подробное объяснение такому особому подходу к MASQUERADE можно найти тут. Ниже моя личная интерпретация:
# Маркировака пакета для дальнейшего MASQUERADE
-A KUBE-MARK-MASQ -j MARK --set-xmark 0x4000/0x4000
# Если пакет не промаркирован, то выйти из KUBE-POSTROUTING цепочки
-A KUBE-POSTROUTING -m mark ! --mark 0x4000/0x4000 -j RETURN
# Честно говоря, без понятия
-A KUBE-POSTROUTING -j MARK --set-xmark 0x4000/0x0
# Изменение ардреса источника пакета на адрес сетевого интерфейса
-A KUBE-POSTROUTING -m comment --comment "kubernetes service traffic requiring SNAT" -j MASQUERADE --random-fully
Далее средствами Minikube создан кластер с двумя узлами:
/registry/certificatesigningrequests/csr-x6n7c
/registry/csinodes/minikube-m02
/registry/leases/kube-node-lease/minikube-m02
/registry/minions/minikube-m02
/registry/secrets/kube-system/bootstrap-token-u7p7xm
#Ресурсы для работы KindNet
/registry/clusterrolebindings/kindnet
/registry/clusterroles/kindnet
/registry/controllerrevisions/kube-system/kindnet-575d9d6996
/registry/daemonsets/kube-system/kindnet
/registry/pods/kube-system/kindnet-5xc7w
/registry/pods/kube-system/kindnet-8fd58
/registry/pods/kube-system/kube-proxy-v78ms
/registry/serviceaccounts/kube-system/kindnet
:KIND-MASQ-AGENT - [0:0] -- "Цепочка для MASQUERADE"
# Отправление пакета, идущего во внешнюю сеть, в цепочку для MASQUERADE
-A POSTROUTING -m addrtype ! --dst-type LOCAL -m comment --comment "kind-masq-agent: ensure nat POSTROUTING directs all non-LOCAL destination traffic to our custom KIND-MASQ-AGENT chain" -j KIND-MASQ-AGENT
# Если пакет идет в сеть Kubernetes, то MASQUERADE не нужен
-A KIND-MASQ-AGENT -d 10.244.0.0/16 -m comment --comment "kind-masq-agent: local traffic is not subject to MASQUERADE" -j RETURN
# Если пакет идет не в сеть Kubernetes, то выполнение MASQUERADE
-A KIND-MASQ-AGENT -m comment --comment "kind-masq-agent: outbound traffic is subject to MASQUERADE (must be last in chain)" -j MASQUERADE
Если присмотреться к сетевой конфигурации, то можно заметить, что Bridge интерфейса больше нет, что и ожидалось, ведь теперь функции CNI выполняет KindNet:
$ ip a
1: lo: <LOOPBACK,UP,LOWER_UP> state UNKNOWN group default
inet 127.0.0.1/8 scope host lo
2: docker0: <NO-CARRIER,BROADCAST,MULTICAST,UP> state DOWN group default
inet 172.17.0.1/16 brd 172.17.255.255 scope global docker0
3: veth70124147@if2: <BROADCAST,MULTICAST,UP,LOWER_UP> state UP group default
inet 10.244.0.1/32 scope global veth70124147
340: eth0@if341: <BROADCAST,MULTICAST,UP,LOWER_UP> state UP group default
inet 192.168.49.2/24 brd 192.168.49.255 scope global eth0
$ ip r
default via 192.168.49.1 dev eth0
10.244.0.2 dev veth70124147 scope host
10.244.1.0/24 via 192.168.49.3 dev eth0
172.17.0.0/16 dev docker0 proto kernel scope link src 172.17.0.1 linkdown
192.168.49.0/24 dev eth0 proto kernel scope link src 192.168.49.2
Создан Deployment с двумя репликами одного Pod:
/registry/pods/default/react-d-86c848f59-7mwt4
/registry/pods/default/react-d-86c848f59-qj6vq
/registry/replicasets/default/react-d-86c848f59
$ ip a
...
4: veth79f7eec0@if2: inet 10.244.0.1/32 scope global veth79f7eec0
$ ip r
...
10.244.0.3 dev veth79f7eec0 scope host
Создан Service для Deployment:
:KUBE-SEP-M6LBIRCLGOFYIL5C - [0:0] -- "Endpoints цепочка до контейнера на master узле"
:KUBE-SVC-LPLI5PP7N5LEMN6C - [0:0] -- "Cluster IP цепочка"
# Новое правило, которое с вероятностью 50% отправляет пакет на один Endpoint, да вот она ваша балансировка
-A KUBE-SVC-LPLI5PP7N5LEMN6C -m comment --comment "default/react-np:http -> 10.244.0.3:3000" -m statistic --mode random --probability 0.50000000000 -j KUBE-SEP-M6LBIRCLGOFYIL5C
# Если 50% не прокнуло, то отправляется на другой Enpoint
-A KUBE-SVC-LPLI5PP7N5LEMN6C -m comment --comment "default/react-np:http -> 10.244.1.2:3000" -
habr.com
Перед началом работы с кластером была проделана следующая подготовительная работа:
- Составления необходимая для сбора логов команда запуска Minikube (все логи можно найти тут):
--vm-driver=docker \
--alsologtostderr \
--extra-config=kubelet.v=6 \
--extra-config=apiserver.v=6 \
--extra-config=controller-manager.v=6 \
--extra-config=scheduler.v=6 \
--extra-config=kube-proxy.v=6 \
--extra-config=etcd.log-level=debug \
--v=10 \
&>minikube.logs
- Написаны скрипты просмотра состояния компонентов Kubernetes, а также iptables
- Написана простая программа на go для получения ключей и значений etcd
- Ключи etcd - ключей много, и полный их список находится здесь, но если обобщить, то происходит базовая настройка кластера, создаются роли, пространства имен, Control-Plane поды и ConfigMaps для конфигурации этих подов.
- Iptables - обработанные (убраны все правила связанные с docker, так как они никак не используются) iptables находятся тут, и в основном осуществляют:
- Базовую фильтрацию пакетов
- Маскарадинг пакетов, о котором поговорим позже
- Настройку цепочек до базовых сервисов, а именно CoreDNS и Kube-API-Server (похожие цепочки рассмотрим при создании собственного сервиса)
В кластере Minikube по умолчанию были выбраны следующие компоненты: для CNI был выбран bridge, для DNS - CoreDNS, а для CRI - Containerd.
Что такое под и почему для каждого контейнера Control-Plane существует прихвостень с похожим именем разберемся в разделе создания собственного пода.
Касательно настройки сетевых интерфейсов, то существуют как базовые интерфейсы: lo и eth0, так и специализированные для Kubenrentes интерфейсы: bridge и veth<id>. Docker0 интерфейс создался автоматически Docker’ом, но находится в состоянии DOWN и нигде не используется:
$ ip a
1: lo: <LOOPBACK,UP,LOWER_UP> state UNKNOWN group default
inet 127.0.0.1/8 scope host lo
2: docker0: <NO-CARRIER,BROADCAST,MULTICAST,UP> state DOWN group default
inet 172.17.0.1/16 brd 172.17.255.255 scope global docker0
3: bridge: <BROADCAST,MULTICAST,UP,LOWER_UP> state UP group default
inet 10.244.0.1/16 brd 10.244.255.255 scope global bridge
4: vethe37be523@if2: <BROADCAST,MULTICAST,UP,LOWER_UP> master bridge state UP group default
352: eth0@if353: <BROADCAST,MULTICAST,UP,LOWER_UP> state UP group default
inet 192.168.49.2/24 brd 192.168.49.255 scope global eth0
Маршруты тоже базовые, но можно заметить что весь трафик Kubernetes сети (10.244.0.0/16) идет в Bridge:
$ ip r
default via 192.168.49.1 dev eth0
10.244.0.0/16 dev bridge proto kernel scope link src 10.244.0.1
172.17.0.0/16 dev docker0 proto kernel scope link src 172.17.0.1 linkdown
192.168.49.0/24 dev eth0 proto kernel scope link src 192.168.49.2
Что это за интерфейсы такие Bridge и vethe37be523@if2? Пока пропустим объяснение и вернемся к ним, когда будем разворачивать собственный под.
Переходим к созданию первого простого пода:
- Ключи etcd - появились только два типа ключей: события по созданию пода, и сам под vault. События нужны только для мониторинга состояния, поэтому сейчас и далее я буду их опускать:
- Iptables - появилось новые правила регулирующие исходящий из созданного пода трафик, расскажу о них ниже.
CONTAINER ID | IMAGE | COMMAND | CREATED | STATUS | PORTS | NAMES |
---|---|---|---|---|---|---|
596fbd56d9aa | vault | "docker-entrypoint.s…" | 3 minutes ago | Up 3 minutes | | k8s_vault_vault_id... |
66d72da7ea45 | "/pause" | 3 minutes ago | Up 3 minutes | | k8s_POD_vault_default_id... |
- Если главный контейнер падает, то внутри пода исчезает процесс с PID 1, из-за чего контейнер не сможет вернуться в Namespace после перезапуска, так как некому быть его родителем.
- Кто-то должен собирать детей процессов в контейнерах, соответственно необходимо писать такой функционал для каждого главного контейнера, что не удобно.
$ lsns
NS TYPE NPROCS PID USER COMMAND
4026534267 mnt 1 6705 65535 /pause
4026534268 uts 1 6705 65535 /pause
4026534269 ipc 4 6705 65535 /pause
4026534270 pid 1 6705 65535 /pause
4026534271 net 4 6705 65535 /pause
4026534341 cgroup 1 6705 65535 /pause
4026534344 mnt 3 6922 root <vault>
4026534345 uts 3 6922 root <vault>
4026534346 pid 3 6922 root <vault>
4026534347 cgroup 3 6922 root <vault>
Файловая система берется из образа контейнера и каталог монтиорвания можно увидеть, если посмотреть на вывод mount внутри контейнера. Из вывода команды ниже можно заметить, что в контейнер по умолчанию также монтируются /etc/hosts, /etc/resolv.conf и /etc/hostname для работы DNS:
$ mount
overlay on / type overlay (
rw,
seclabel,
relatime,
lowerdir=/var/lib/docker/overlay2/l/HADIUDBRK6K6CA2EAYIPIN54CF:
/var/lib/docker/overlay2/l/ZEDDRHSJ5IEREEVGU2PV2YSNGI:
/var/lib/docker/overlay2/l/DEEBZ4NIHQEJX7VSYESA2YWCAJ:
/var/lib/docker/overlay2/l/CPE4IDREOEF54DX2XOUTMT3U2S:
/var/lib/docker/overlay2/l/FQBHQ3BPW6VV3YPG4WEKW4WMEB:
/var/lib/docker/overlay2/l/6IPXNYC6WPK4IGINXKMNVIQ4BH,
upperdir=/var/lib/docker/overlay2/443259d564638ef254e193583dbc23bd5e5a49f5930dd710edff45177454cc9a/diff,
workdir=/var/lib/docker/overlay2/443259d564638ef254e193583dbc23bd5e5a49f5930dd710edff45177454cc9a/work)
/dev/nvme0n1p3 on /etc/resolv.conf type btrfs (...)
/dev/nvme0n1p3 on /etc/hostname type btrfs (...)
/dev/nvme0n1p3 on /etc/hosts type btrfs (...)
LowerDir - включает файловые системы всех слоев внутри контейнера, кроме последнего, UpperDir - файловая система самого верхнего слоя контейнера где отображаются любые изменения во время выполнения, WorkDir - внутренний рабочий каталог, используемый для управления файловой системой (задается в Dockerfile).
Возвращаемся к вопросу о новых сетевых интерфейсах. Bridge является связующим звеном через которое идет весь внутренний трафик Kubernetes, как видно из вывода команды ниже он перенаправляет трафик на veth интерфейсы создаваемые для общения с другими NET Namespaces, в данном случае данный отдельный NET Namespace имеет созданный нами контейнер (еще CoreDNS контейнер, но принципиально они не отличается). Что касается компонентов Control-Plane, то они разворачиваются в NET Namespace хоста, а значит никаких veth не требуют:
bridge link show
5: veth2d3d80cb@if2@docker0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 master bridge state forwarding
Внутри пода есть связанный с veth eth0 интерфейс, на который перенаправляется весь трафик во внешнюю относительно пода сеть. Внутри же пода контейнеры общаются через localhost:
$ ip a
1: lo: <LOOPBACK,UP,LOWER_UP> state UNKNOWN
inet 127.0.0.1/8 scope host lo
2: eth0@if5: <BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN> state UP
inet 10.244.0.3/16 brd 10.244.255.255 scope global eth0
$ ip r
default via 10.244.0.1 dev eth0
10.244.0.0/16 dev eth0 scope link src 10.244.0.3
Что касается режима CNI - bridge, то у него есть один существенный минус, а именно он плохо работает в многоузловом режиме, и если необходимо отправлять трафик на контейнер на другом узле, то нужно выбирать другой CNI, например kindnet или calico. Благо Minikube делает это за нас.
Из iptables можно сделать вывод, что весь исходящий трафик контейнера идущий во внешнюю сеть меняет адрес отправителя на адрес сетевого интерфейса (функционал MASQUERADE):
:CNI-1f120c2dca3af9923d1e29d4 - [0:0] -- "Цепочка контейнера vault"
# Если пакет с ip отправителя 10.244.0.3, то перейти в цепочку контейнера vault
-A POSTROUTING -s 10.244.0.3/32 -j CNI-1f120c2dca3af9923d1e29d4
# Если пакет идет в сеть Kubernetes, то MASQUERADE не нужен
-A CNI-1f120c2dca3af9923d1e29d4 -d 10.244.0.0/16 -j ACCEPT
# Если пакет идет не в сеть Kubernetes, то выполнение MASQUERADE
-A CNI-1f120c2dca3af9923d1e29d4 ! -d 224.0.0.0/4 -j MASQUERADE
Касательно того как был создан контейнер (не под, о том как создается под в интернете куча информации) то вот схема:
- Kubelet приходит уведомление, что на его узел запланирован под.
- Kubelet обращается к Kube-API и берет описание пода.
- С помощью CRI, в моем случае это Containerd, первым делом качается образы контейнеров из репозитория (если их нет локально), после чего отправляется запрос на создание контейнеров пода, включая pause.
- Containerd в свою очередь через специальный интерфейс - containerd-shim-runc-v2 отправляет запрос на создание контейнера RunС.
- RunС является самым нижним уровнем, который непосредственно и запускает процессы в новых Namespace (синоним контейнеров).
- Ключи etcd - появились новые enpointslices, endpoints и сервис:
/registry/services/endpoints/default/vault
/registry/services/specs/default/vault
- Iptables - появились новые правила перенаправляющие трафик, идущий к сервису, в соответствующий под. А именно создалось две цепочки одна для Cluster IP сервиса, а другая для NodePort. Из чего можно сделать вывод, что service (если он типа NodePort) - это 11 строчек в iptables, за которыми следит kube-proxy:
:KUBE-SEP-6X3WIXWFLXPKXKEP - [0:0] -- "Endpoints цепочка"
:KUBE-SVC-VUGVVJAHU4RL2TIW - [0:0] -- "Clusert IP цепочка"
#Направление пакета в Clusert IP цепочку если адрес назначения 10.104.205.29
-A KUBE-SERVICES -d 10.104.205.29/32 -p tcp -m tcp --dport 8200 -j KUBE-SVC-VUGVVJAHU4RL2TIW
#Отправление пакета не из сети Kubernetes, идущих в Service, в цепочку для MASQUERADE
-A KUBE-SVC-VUGVVJAHU4RL2TIW ! -s 10.244.0.0/16 -d 10.104.205.29/32 -p tcp -m comment --comment "default/vault cluster IP" -m tcp --dport 8200 -j KUBE-MARK-MASQ
#Направление пакета в Endpoints цепочку
-A KUBE-SVC-VUGVVJAHU4RL2TIW -m comment --comment "default/vault -> 10.244.0.3:8200" -j KUBE-SEP-6X3WIXWFLXPKXKEP
#Отправление пакета контейнера в цепочку для MASQUERADE
-A KUBE-SEP-6X3WIXWFLXPKXKEP -s 10.244.0.3/32 -m comment --comment "default/vault" -j KUBE-MARK-MASQ
#Изменение адрса получателя на 10.244.0.3:8200 (ip контейнера)
-A KUBE-SEP-6X3WIXWFLXPKXKEP -p tcp -m comment --comment "default/vault" -m tcp -j DNAT --to-destination 10.244.0.3:8200
#Направление пакета в NodePort цепочку если порт назначения 30074
-A KUBE-NODEPORTS -p tcp -m comment --comment "default/vault" -m tcp --dport 30074 -j KUBE-EXT-VUGVVJAHU4RL2TIW
#Отправление всех пакетов в цепочку для MASQUERADE
-A KUBE-EXT-VUGVVJAHU4RL2TIW -m comment --comment "masquerade traffic for default/vault external destinations" -j KUBE-MARK-MASQ
#Направление пакета в Cluster IP цепочку
-A KUBE-EXT-VUGVVJAHU4RL2TIW -j KUBE-SVC-VUGVVJAHU4RL2TIW
Пояснение касательно KUBE-MARK-MASQ цепочки, более подробное объяснение такому особому подходу к MASQUERADE можно найти тут. Ниже моя личная интерпретация:
# Маркировака пакета для дальнейшего MASQUERADE
-A KUBE-MARK-MASQ -j MARK --set-xmark 0x4000/0x4000
# Если пакет не промаркирован, то выйти из KUBE-POSTROUTING цепочки
-A KUBE-POSTROUTING -m mark ! --mark 0x4000/0x4000 -j RETURN
# Честно говоря, без понятия
-A KUBE-POSTROUTING -j MARK --set-xmark 0x4000/0x0
# Изменение ардреса источника пакета на адрес сетевого интерфейса
-A KUBE-POSTROUTING -m comment --comment "kubernetes service traffic requiring SNAT" -j MASQUERADE --random-fully
Далее средствами Minikube создан кластер с двумя узлами:
- Ключи etcd - появились ресурсы для функционирования KindNet подов, а также отдельные ресурсы для присоединения рабочего узла:
/registry/certificatesigningrequests/csr-x6n7c
/registry/csinodes/minikube-m02
/registry/leases/kube-node-lease/minikube-m02
/registry/minions/minikube-m02
/registry/secrets/kube-system/bootstrap-token-u7p7xm
#Ресурсы для работы KindNet
/registry/clusterrolebindings/kindnet
/registry/clusterroles/kindnet
/registry/controllerrevisions/kube-system/kindnet-575d9d6996
/registry/daemonsets/kube-system/kindnet
/registry/pods/kube-system/kindnet-5xc7w
/registry/pods/kube-system/kindnet-8fd58
/registry/pods/kube-system/kube-proxy-v78ms
/registry/serviceaccounts/kube-system/kindnet
- Iptables - были убраны правила обработки исходящего трафика контейнеров и заменены на похожие правила kind-masq-agent.
:KIND-MASQ-AGENT - [0:0] -- "Цепочка для MASQUERADE"
# Отправление пакета, идущего во внешнюю сеть, в цепочку для MASQUERADE
-A POSTROUTING -m addrtype ! --dst-type LOCAL -m comment --comment "kind-masq-agent: ensure nat POSTROUTING directs all non-LOCAL destination traffic to our custom KIND-MASQ-AGENT chain" -j KIND-MASQ-AGENT
# Если пакет идет в сеть Kubernetes, то MASQUERADE не нужен
-A KIND-MASQ-AGENT -d 10.244.0.0/16 -m comment --comment "kind-masq-agent: local traffic is not subject to MASQUERADE" -j RETURN
# Если пакет идет не в сеть Kubernetes, то выполнение MASQUERADE
-A KIND-MASQ-AGENT -m comment --comment "kind-masq-agent: outbound traffic is subject to MASQUERADE (must be last in chain)" -j MASQUERADE
Если присмотреться к сетевой конфигурации, то можно заметить, что Bridge интерфейса больше нет, что и ожидалось, ведь теперь функции CNI выполняет KindNet:
$ ip a
1: lo: <LOOPBACK,UP,LOWER_UP> state UNKNOWN group default
inet 127.0.0.1/8 scope host lo
2: docker0: <NO-CARRIER,BROADCAST,MULTICAST,UP> state DOWN group default
inet 172.17.0.1/16 brd 172.17.255.255 scope global docker0
3: veth70124147@if2: <BROADCAST,MULTICAST,UP,LOWER_UP> state UP group default
inet 10.244.0.1/32 scope global veth70124147
340: eth0@if341: <BROADCAST,MULTICAST,UP,LOWER_UP> state UP group default
inet 192.168.49.2/24 brd 192.168.49.255 scope global eth0
$ ip r
default via 192.168.49.1 dev eth0
10.244.0.2 dev veth70124147 scope host
10.244.1.0/24 via 192.168.49.3 dev eth0
172.17.0.0/16 dev docker0 proto kernel scope link src 172.17.0.1 linkdown
192.168.49.0/24 dev eth0 proto kernel scope link src 192.168.49.2
Создан Deployment с двумя репликами одного Pod:
- Ключи etcd - появились ключи развертывания, набора реплик и двух подов:
/registry/pods/default/react-d-86c848f59-7mwt4
/registry/pods/default/react-d-86c848f59-qj6vq
/registry/replicasets/default/react-d-86c848f59
- iptables - новых правил не появилось.
$ ip a
...
4: veth79f7eec0@if2: inet 10.244.0.1/32 scope global veth79f7eec0
$ ip r
...
10.244.0.3 dev veth79f7eec0 scope host
Создан Service для Deployment:
- Ключи etcd - в etcd все аналогично созданию service в одноузловом кластере
- iptables - новые правила практически аналогичны одноузловому кластеру, разве что теперь iptables рабочих узлов должны периодически синхронизироваться для правильной обработки трафика. Внизу представлены новые изменения цепочки Cluster IP (при создании развертывания в одноузловом режиме, изменения правил были бы такими же):
:KUBE-SEP-M6LBIRCLGOFYIL5C - [0:0] -- "Endpoints цепочка до контейнера на master узле"
:KUBE-SVC-LPLI5PP7N5LEMN6C - [0:0] -- "Cluster IP цепочка"
# Новое правило, которое с вероятностью 50% отправляет пакет на один Endpoint, да вот она ваша балансировка
-A KUBE-SVC-LPLI5PP7N5LEMN6C -m comment --comment "default/react-np:http -> 10.244.0.3:3000" -m statistic --mode random --probability 0.50000000000 -j KUBE-SEP-M6LBIRCLGOFYIL5C
# Если 50% не прокнуло, то отправляется на другой Enpoint
-A KUBE-SVC-LPLI5PP7N5LEMN6C -m comment --comment "default/react-np:http -> 10.244.1.2:3000" -

Kubernetes, ищем базу
В этой статье я хочу показать, что меняется в состоянии кластера kubernetes при создании базовых ресурсов. В качестве кластерной платформы был выбран minikube, чтобы каждый мог повторить проделанные...
