Примечание переводчика: статья является переводом оригинального материала Марка Бетца (Mark Betz). В ней рассматриваются ключевые аспекты работы Kubernetes Services (далее — сервисы) и то, какое участие в этом принимает iptables. Этот перевод поможет русскоязычным читателям лучше разобраться в сложных концепциях Kubernetes и эффективно применять их на практике. Статья особенно актуальна для DevOps/DevSecOps-инженеров и администраторов кластеров Kubernetes.
TL;DR — iptables могут сломать вам мозг (надеюсь, это достаточно хорошее предупреждение для того, о чём пойдёт речь ниже…).
Если вы хоть раз сталкивались с Kubernetes, вам точно приходилось развёртывать сервисы или взаимодействовать с ними. Но как же они работают? В этой публикации мы об этом и поговорим. А о сервисах Kubernetes в целом и о том, зачем они нужны, читайте в этой статье (перевод статьи на Хабре).
Окружение
Краткое описание окружения:
-
Кластер RKE2 с версией 1.28.4+rk2r1 на борту
-
Calico CNI 3.26.3 с использованием оверлея VxLAN
-
Все узлы работают под управлением Ubuntu 22.04
Запускаем рабочую нагрузку
Прежде чем начать разбираться в том, как работает сервис, нам понадобятся рабочая нагрузка и сам сервис. Для этого воспользуемся веб-приложением Podinfo:
# При необходимости добавьте репозиторий Podinfo $ helm repo add podinfo https://stefanprodan.github.io/podinfo "podinfo" already exists with the same configuration, skipping # Обновите все репозитории Helm, чтобы убедиться, что будет использоваться последняя версия Podinfo $ helm repo update Hang tight while we grab the latest from your chart repositories... ...Successfully got an update from the "podinfo" chart repository ...Successfully got an update from the "haproxytech" chart repository Update Complete. ⎈Happy Helming!⎈ # Разверните podinfo в целевой кластер $ helm upgrade -i --install --wait frontend --namespace podinfo \ --set replicaCount=2 \ --set backend=http://backend-podinfo:9898/echo \ podinfo/podinfo Release "frontend" does not exist. Installing it now. NAME: frontend LAST DEPLOYED: Wed Mar 13 15:41:29 2024 NAMESPACE: podinfo STATUS: deployed REVISION: 1 NOTES: 1. URL-адрес приложения можно получить, выполнив следующие команды: echo "Visit http://127.0.0.1:8080 to use your application" kubectl -n podinfo port-forward deploy/frontend-podinfo 8080:9898
Посмотрим, куда было запланировано наше приложение:
А теперь посмотрим, какой IP-адрес был присвоен созданному сервису:
Kube-proxy и iptables
Тут необходимо сделать небольшое отступление и объяснить, почему мы говорим об iptables. Как отмечает Марк в своей статье о сервисах, iptables — это программа в пространстве пользователя, которая выступает фронтендом для netfilter. Это механизм обработки пакетов в слое данных, который, помимо прочего, умеет перенаправлять трафик в другое место.
В большинстве дистрибутивов Kubernetes kube-proxy по умолчанию использует iptables. Список других программ, которые поддерживает kube-proxy, можно найти здесь. Эта статья актуальна только для инсталляций на базе iptables.
Трейсинг iptables
Команды ниже выполняются на «пустом» узле (то есть на узле, на котором нет запланированных Podinfo-подов). Включим трейсинг в iptables для трафика, который попадает на порт 9898 — порт сервиса Podinfo.
$ iptables -t raw -A PREROUTING -p tcp --dport 9898 -j TRACE $ iptables -t raw -A OUTPUT -p tcp --dport 9898 -j TRACE
Обратите внимание, что, поскольку фильтр выше перехватывает только порт назначения 9898, обратный трафик от сервиса Podinfo перехватываться не будет.
Команда ниже запускается с правами root и работает в отдельной сессии терминала:
$ xtables-monitor --trace
Вернемся к первой сессии терминала и сделаем curl-запрос на сервис Podinfo:
$ curl http://10.43.156.98:9898 { "hostname": "frontend-podinfo-7854c7cd4c-jvdx5", "version": "6.6.0", "revision": "357009a86331a987811fefc11be1350058da33fc", "color": "#34577c", "logo": "https://raw.githubusercontent.com/stefanprodan/podinfo/gh-pages/cuddle_clap.gif", "message": "greetings from podinfo v6.6.0", "goos": "linux", "goarch": "amd64", "runtime": "go1.21.7", "num_goroutine": "8", "num_cpu": "8" }
Прежде чем продолжать, нужно отключить трейсинг пакетов, поскольку на загруженных системах может собираться слишком много данных:
$ iptables -t raw -D PREROUTING -p tcp --dport 9898 -j TRACE $ iptables -t raw -D OUTPUT -p tcp --dport 9898 -j TRACE
Если вернуться к терминалу, на котором была запущена команда xtables-monitor
, увидим здоровенный вывод. Сначала пара слов о столбцах в этом бардаке:
PACKET: 2 0427ffe5 OUT=enp3s0 SRC=192.168.1.88 DST=10.43.156.98 LEN=60 TOS=0x0 TTL=64 ID=45471DF SPORT=49058 DPORT=9898 SYN TRACE: 2 0427ffe5 raw:OUTPUT:rule:0x18:CONTINUE -4 -t raw -A OUTPUT -p tcp -m tcp --dport 9898 -j TRACE TRACE: 2 0427ffe5 raw:OUTPUT:return: TRACE: 2 0427ffe5 raw:OUTPUT:policy:ACCEPT TRACE: 2 0427ffe5 mangle:OUTPUT:return: TRACE: 2 0427ffe5 mangle:OUTPUT:policy:ACCEPT PACKET: 2 0427ffe5 OUT=enp3s0 SRC=192.168.1.88 DST=10.43.156.98 LEN=60 TOS=0x0 TTL=64 ID=45471DF SPORT=49058 DPORT=9898 SYN TRACE: 2 0427ffe5 nat:OUTPUT:rule:0xb9:JUMP:cali-OUTPUT -4 -t nat -A OUTPUT -m comment --comment "cali:tVnHkvAo15HuiPy0" -j cali-OUTPUT TRACE: 2 0427ffe5 nat:cali-OUTPUT:rule:0xb8:JUMP:cali-fip-dnat -4 -t nat -A cali-OUTPUT -m comment --comment "cali:GBTAv2p5CwevEyJm" -j cali-fip-dnat TRACE: 2 0427ffe5 nat:cali-fip-dnat:return: TRACE: 2 0427ffe5 nat:cali-OUTPUT:return: TRACE: 2 0427ffe5 nat:OUTPUT:rule:0x9:JUMP:KUBE-SERVICES -4 -t nat -A OUTPUT -m comment --comment "kubernetes service portals" -j KUBE-SERVICES TRACE: 2 0427ffe5 nat:KUBE-SERVICES:rule:0x4aaa:JUMP:KUBE-SVC-Y4T5L63IYP3YFEBS -4 -t nat -A KUBE-SERVICES -d 10.43.156.98/32 -p tcp -m comment --comment "podinfo/frontend-podinfo:http cluster IP" -j KUBE-SVC-Y4T5L63IYP3YFEBS TRACE: 2 0427ffe5 nat:KUBE-SVC-Y4T5L63IYP3YFEBS:rule:0x471b:JUMP:KUBE-MARK-MASQ -4 -t nat -A KUBE-SVC-Y4T5L63IYP3YFEBS ! -s 10.42.0.0/16 -d 10.43.156.98/32 -p tcp -m comment --comment "podinfo/frontend-podinfo:http cluster IP" -j KUBE-MARK-MASQ TRACE: 2 0427ffe5 nat:KUBE-MARK-MASQ:rule:0x4aa4:CONTINUE -4 -t nat -A KUBE-MARK-MASQ -j MARK --set-xmark 0x4000/0x4000 TRACE: 2 0427ffe5 nat:KUBE-MARK-MASQ:return: TRACE: 2 0427ffe5 nat:KUBE-SVC-Y4T5L63IYP3YFEBS:rule:0x471d:JUMP:KUBE-SEP-OYNVDBAPYJ623W6H -4 -t nat -A KUBE-SVC-Y4T5L63IYP3YFEBS -m comment --comment "podinfo/frontend-podinfo:http -> 10.42.54.194:9898" -j KUBE-SEP-OYNVDBAPYJ623W6H TRACE: 2 0427ffe5 nat:KUBE-SEP-OYNVDBAPYJ623W6H:rule:0x4721:ACCEPT -4 -t nat -A KUBE-SEP-OYNVDBAPYJ623W6H -p tcp -m comment --comment "podinfo/frontend-podinfo:http" -m tcp -j DNAT --to-destination 10.42.54.194:9898 PACKET: 2 0427ffe5 OUT=enp3s0 SRC=192.168.1.88 DST=10.42.54.194 LEN=60 TOS=0x0 TTL=64 ID=45471DF SPORT=49058 DPORT=9898 SYN MARK=0x4000 TRACE: 2 0427ffe5 filter:OUTPUT:rule:0x91:JUMP:cali-OUTPUT -4 -t filter -A OUTPUT -m comment --comment "cali:tVnHkvAo15HuiPy0" -j cali-OUTPUT TRACE: 2 0427ffe5 filter:cali-OUTPUT:rule:0x8a:CONTINUE -4 -t filter -A cali-OUTPUT -m comment --comment "cali:iC1pSPgbvgQzkUk_" -j MARK --set-xmark 0x0/0xf0000 TRACE: 2 0427ffe5 filter:cali-OUTPUT:return: TRACE: 2 0427ffe5 filter:OUTPUT:rule:0x17:JUMP:KUBE-PROXY-FIREWALL -4 -t filter -A OUTPUT -m conntrack --ctstate NEW -m comment --comment "kubernetes load balancer firewall" -j KUBE-PROXY-FIREWALL TRACE: 2 0427ffe5 filter:KUBE-PROXY-FIREWALL:return: TRACE: 2 0427ffe5 filter:OUTPUT:rule:0x12:JUMP:KUBE-SERVICES -4 -t filter -A OUTPUT -m conntrack --ctstate NEW -m comment --comment "kubernetes service portals" -j KUBE-SERVICES TRACE: 2 0427ffe5 filter:KUBE-SERVICES:return: TRACE: 2 0427ffe5 filter:OUTPUT:rule:0x6:JUMP:KUBE-FIREWALL -4 -t filter -A OUTPUT -j KUBE-FIREWALL TRACE: 2 0427ffe5 filter:KUBE-FIREWALL:return: TRACE: 2 0427ffe5 filter:OUTPUT:return: TRACE: 2 0427ffe5 filter:OUTPUT:policy:ACCEPT PACKET: 2 0427ffe5 OUT=vxlan.calico SRC=192.168.1.88 DST=10.42.54.194 LEN=60 TOS=0x0 TTL=64 ID=45471DF SPORT=49058 DPORT=9898 SYN MARK=0x4000 TRACE: 2 0427ffe5 mangle:POSTROUTING:rule:0x15:JUMP:cali-POSTROUTING -4 -t mangle -A POSTROUTING -m comment --comment "cali:O3lYWMrLQYEMJtB5" -j cali-POSTROUTING TRACE: 2 0427ffe5 mangle:cali-POSTROUTING:rule:0x12:CONTINUE -4 -t mangle -A cali-POSTROUTING -m comment --comment "cali:nnqPh8lh2VOogSzX" -j MARK --set-xmark 0x0/0xf0000 TRACE: 2 0427ffe5 mangle:cali-POSTROUTING:rule:0x13:JUMP:cali-to-host-endpoint -4 -t mangle -A cali-POSTROUTING -m comment --comment "cali:nquN8Jw8Tz72pcBW" -m conntrack --ctstate DNAT -j cali-to-host-endpoint TRACE: 2 0427ffe5 mangle:cali-to-host-endpoint:return: TRACE: 2 0427ffe5 mangle:cali-POSTROUTING:return: TRACE: 2 0427ffe5 mangle:POSTROUTING:return: TRACE: 2 0427ffe5 mangle:POSTROUTING:policy:ACCEPT PACKET: 2 0427ffe5 OUT=vxlan.calico SRC=192.168.1.88 DST=10.42.54.194 LEN=60 TOS=0x0 TTL=64 ID=45471DF SPORT=49058 DPORT=9898 SYN MARK=0x4000 TRACE: 2 0427ffe5 nat:POSTROUTING:rule:0xba:JUMP:cali-POSTROUTING -4 -t nat -A POSTROUTING -m comment --comment "cali:O3lYWMrLQYEMJtB5" -j cali-POSTROUTING TRACE: 2 0427ffe5 nat:cali-POSTROUTING:rule:0xb5:JUMP:cali-fip-snat -4 -t nat -A cali-POSTROUTING -m comment --comment "cali:Z-c7XtVd2Bq7s_hA" -j cali-fip-snat TRACE: 2 0427ffe5 nat:cali-fip-snat:return: TRACE: 2 0427ffe5 nat:cali-POSTROUTING:rule:0xb6:JUMP:cali-nat-outgoing -4 -t nat -A cali-POSTROUTING -m comment --comment "cali:nYKhEzDlr11Jccal" -j cali-nat-outgoing TRACE: 2 0427ffe5 nat:cali-nat-outgoing:return: TRACE: 2 0427ffe5 nat:cali-POSTROUTING:rule:0xb7:ACCEPT -4 -t nat -A cali-POSTROUTING -o vxlan.calico -m comment --comment "cali:e9dnSgSVNmIcpVhP" -m addrtype ! --src-type LOCAL --limit-iface-out -m addrtype --src-type LOCAL -j MASQUERADE --random-fully
-
PACKET:
— указывает на начало нового события типа «пакет». В строке содержится информация об обрабатываемом пакете, его источнике, пункте назначения, длине и других сведениях на уровне пакета. СтрокиPACKET:
также содержат исходящий интерфейс, основанный на текущем IP-адресе получателя пакета. -
TRACE:
— строки трейса, в которых прослеживается путь пакета через различные цепочки и правила iptables. Каждая строкаTRACE:
представляет собой шаг в обработке пакета, показывая, по какому правилу или цепочке пакет оценивался на тот или иной момент. -
Номер протокола — та самая двойка (2) после
TRACE:
илиPACKET:
. В нашем случае представляетAF_INET
(трафик IPv4). -
Идентификатор пакета — значение
0427ffe5
в приведённом выше выводе служит уникальным идентификатором пакета, позволяющим соотнести с ним трейсы. -
Таблица:Цепочка —для
TRACE
-строк следующий столбец указывает на таблицу и цепочку iptables, которая обрабатывает пакет. Например,raw:OUTPUT
относится к таблицеraw
и цепочкеOUTPUT
. Чтобы вывести конкретную цепочку iptables, выполнитеsudo iptables -t raw -L OUTPUT -n --line-numbers
.
Еще один важный момент — понимание того, как netfilter обрабатывает пакеты. Для пакета, исходящего из системы, порядок следующий:
-
Маршрутизация
-
Raw (OUTPUT)
-
Mangle (OUTPUT)
-
NAT (OUTPUT)
-
Filter (OUTPUT)
-
Маршрутизация
-
Mangle (POSTROUTING)
-
NAT (POSTROUTING)
Более подробную информацию можно найти здесь в разделе 6–2. Приведённая ниже схема отлично показывает порядок обработки пакетов:
Если быть точнее, дальше мы сосредоточимся на этом участке схемы:
Теперь рассмотрим, как обрабатывается пакет 0427ff35
.
Таблица raw
Цель таблицы raw
— дать пакетам возможность обойти функцию отслеживания соединений (conntrack) в iptables. В данном потоке эта функциональность не используется, поэтому таблица проходится довольно быстро:
PACKET: 2 0427ffe5 OUT=enp3s0 SRC=192.168.1.88 DST=10.43.156.98 LEN=60 \ TOS=0x0 TTL=64 ID=45471DF SPORT=49058 DPORT=9898 SYN TRACE: 2 0427ffe5 raw:OUTPUT:rule:0x18:CONTINUE -4 -t raw -A OUTPUT \ -p tcp -m tcp --dport 9898 -j TRACE TRACE: 2 0427ffe5 raw:OUTPUT:return: TRACE: 2 0427ffe5 raw:OUTPUT:policy:ACCEPT
Первая строка — информационная. Она показывает, что пакет получен и начинается его обработка. Обратите внимание, что SRC=192.168.1.88
— IP-адрес узла, инициировавшего curl-запрос.
Вторая строка — это TRACE
, который ранее мы перехватывали с помощью xtables-monitor (iptables -t raw -A PREROUTING -p tcp --dport 9898 -J TRACE
).
Третья и четвёртая строки показывают, что конечным результатом обработки в таблице raw
стала цель ACCEPT
. Она означает, что пакет передаётся в следующую таблицу. Напомним, что таблицы и цепочки можно просмотреть с помощью соответствующих команд iptables.
$ iptables -t raw -L OUTPUT -n --line-numbers Chain OUTPUT (policy ACCEPT) num target prot opt source destination 1 cali-OUTPUT all -- 0.0.0.0/0 0.0.0.0/0 /* cali:tVnHkvAo15HuiPy0 */ $ iptables -t raw -L cali-OUTPUT -n --line-numbers Chain cali-OUTPUT (1 references) num target prot opt source destination 1 MARK all -- 0.0.0.0/0 0.0.0.0/0 /* cali:njdnLwYeGqBJyMxW */ MARK and 0xfff0ffff 2 cali-to-host-endpoint all -- 0.0.0.0/0 0.0.0.0/0 /* cali:rz86uTUcEZAfFsh7 */ 3 ACCEPT all -- 0.0.0.0/0 0.0.0.0/0 /* cali:pN0F5zD0b8yf9W1Z */ mark match 0x10000/0x10000 $ iptables -t raw -L cali-to-host-endpoint -n --line-numbers Chain cali-to-host-endpoint (1 references) num target prot opt source destination
Вот схема, соответствующая приведённому выше выводу:
Первое правило в цепочке cali-OUTPUT
— MARK
. Неясно, почему оно не отобразилось в выводе xtables-monitor. Рабочая версия состоит в том, что оно никак не повлияло на пакет и потому не было включено в вывод. MARK
— это нетерминирующая цель, то есть обработка в текущей цепочке продолжается, даже если MARK
— правило срабатывает.
Состояние пакета
Состояние пакета после цепочки raw:OUTPUT
:
IP-адрес отправителя: 192.168.1.88
Порт отправителя: 49058
IP-адрес получателя: 10.43.156.98
Порт получателя: 9898
Таблица mangle — OUTPUT
Как следует из названия, таблица mangle используется для модификации пакета определённым образом. Пример — изменение полей TTL или ToS/DSCP в заголовке IPv4. Как и в случае с raw:OUTPUT
, обработка здесь происходит быстро. Пакет добирается до цели ACCEPT
и переходит к следующей таблице.
TRACE: 2 0427ffe5 mangle:OUTPUT:return: TRACE: 2 0427ffe5 mangle:OUTPUT:policy:ACCEPT
Состояние пакета
Состояние пакета после цепочки mangle:OUTPUT
:
IP-адрес отправителя: 192.168.1.88
Порт отправителя: 49058
IP-адрес получателя: 10.43.156.98
Порт получателя: 9898
Выходной интерфейс: enp3s0
Метка: нет
Таблица NAT — OUTPUT
В таблице nat:OUTPUT
пакетам гораздо «веселее»:
00: PACKET: 2 0427ffe5 OUT=enp3s0 SRC=192.168.1.88 DST=10.43.156.98 LEN=60 \ TOS=0x0 TTL=64 ID=45471DF SPORT=49058 DPORT=9898 SYN 01: TRACE: 2 0427ffe5 nat:OUTPUT:rule:0xb9:JUMP:cali-OUTPUT -4 -t nat \ -A OUTPUT -m comment --comment "cali:tVnHkvAo15HuiPy0" -j cali-OUTPUT 02: TRACE: 2 0427ffe5 nat:cali-OUTPUT:rule:0xb8:JUMP:cali-fip-dnat -4 \ -t nat -A cali-OUTPUT -m comment --comment "cali:GBTAv2p5CwevEyJm" \ -j cali-fip-dnat 03: TRACE: 2 0427ffe5 nat:cali-fip-dnat:return: 04: TRACE: 2 0427ffe5 nat:cali-OUTPUT:return: 05: TRACE: 2 0427ffe5 nat:OUTPUT:rule:0x9:JUMP:KUBE-SERVICES -4 \ -t nat -A OUTPUT -m comment --comment "kubernetes service portals" \ -j KUBE-SERVICES 06: TRACE: 2 0427ffe5 nat:KUBE-SERVICES:rule:0x4aaa:JUMP:KUBE-SVC-Y4T5L63IYP3YFEBS \ -4 -t nat -A KUBE-SERVICES -d 10.43.156.98/32 -p tcp -m comment \ --comment "podinfo/frontend-podinfo:http cluster IP" \ -j KUBE-SVC-Y4T5L63IYP3YFEBS 07: TRACE: 2 0427ffe5 nat:KUBE-SVC-Y4T5L63IYP3YFEBS:rule:0x471b:JUMP:KUBE-MARK-MASQ \ -4 -t nat -A KUBE-SVC-Y4T5L63IYP3YFEBS ! -s 10.42.0.0/16 -d 10.43.156.98/32 \ -p tcp -m comment --comment "podinfo/frontend-podinfo:http cluster IP" \ -j KUBE-MARK-MASQ 08: TRACE: 2 0427ffe5 nat:KUBE-MARK-MASQ:rule:0x4aa4:CONTINUE \ -4 -t nat -A KUBE-MARK-MASQ -j MARK --set-xmark 0x4000/0x4000 09: TRACE: 2 0427ffe5 nat:KUBE-MARK-MASQ:return: 10: TRACE: 2 0427ffe5 nat:KUBE-SVC-Y4T5L63IYP3YFEBS:rule:0x471d:JUMP:KUBE-SEP-OYNVDBAPYJ623W6H \ -4 -t nat -A KUBE-SVC-Y4T5L63IYP3YFEBS -m comment \ --comment "podinfo/frontend-podinfo:http -> 10.42.54.194:9898" \ -j KUBE-SEP-OYNVDBAPYJ623W6H 11: TRACE: 2 0427ffe5 nat:KUBE-SEP-OYNVDBAPYJ623W6H:rule:0x4721:ACCEPT \ -4 -t nat -A KUBE-SEP-OYNVDBAPYJ623W6H -p tcp -m comment \ --comment "podinfo/frontend-podinfo:http" -m tcp \ -j DNAT --to-destination 10.42.54.194:9898
Первые пять строк показывают, как пакет проходит через nat:OUTPUT
и попадает в nat:cali-OUTPUT
, далее в nat:cali-fip-dnat
, а затем возвращается в nat:OUTPUT
и передаётся в следующее правило в этой цепочке — nat:KUBE-SERVICES
.
Обработка в цепочке nat:KUBE-SERVICES
начинается с пятой строки, и далее пакет оказывается в nat:KUBE-SVC-Y4T5L63IYP3YFEBS
по IP-адресу сервиса (10.43.156.98).
Восьмая строка перекидывает его в цепочку nat:KUBE-MARK-MASQ
, поскольку адрес отправителя пакета не принадлежит подсети 10.42.0.0/16 (то есть отправителем не является другой под). Далее он помечается значением 0x4000
. Эта метка носит локальный характер и не сохраняется при передаче на другой сервер/хоп. Она потребуется позже, при обработке пакета. В строке 10 пакет возвращается в цепочку nat:KUBE-SVC-Y4T5L63IYP3YFEBS
.
В строке 11 происходит балансировка нагрузки внутри kube-proxy в режиме iptables. Ее логику можно посмотреть, выведя цепочку iptables:
$ iptables -t nat -L KUBE-SVC-Y4T5L63IYP3YFEBS -n --line-numbers Chain KUBE-SVC-Y4T5L63IYP3YFEBS (1 references) num target prot opt source destination 1 KUBE-MARK-MASQ tcp -- !10.42.0.0/16 10.43.156.98 /* podinfo/frontend-podinfo:http cluster IP */ 2 KUBE-SEP-6G2GMHWEBXQ5W3DV all -- 0.0.0.0/0 0.0.0.0/0 /* podinfo/frontend-podinfo:http -> 10.42.217.66:9898 */ statistic mode random probability 0.50000000000 3 KUBE-SEP-OYNVDBAPYJ623W6H all -- 0.0.0.0/0 0.0.0.0/0 /* podinfo/frontend-podinfo:http -> 10.42.54.194:9898 */
Обратите внимание на statistic mode random probability 0.50000000000
. Это правило будет срабатывать в 50 % случаев и перекидывать пакет в цепочку nat:KUBE-SEP-6G2GMHWEBXQ5W3DV
. В остальных случаях он будет попадать в nat:KUBE-SEP-OYNVDBAPYJ623W6H
. Эти SEP-цепочки выполняют одну и ту же функцию: выполняют DNAT, меняя IP-адрес получателя с адреса сервиса 10.43.156.98 на адрес одного из двух подов, развёрнутых для сервиса Podinfo. Цепочка, которая заканчивается на W3DV
, ведёт в под с IP-адресом 10.42.217.66, а цепочка, заканчивающаяся на 3W6h
, — в под с IP-адресом 10.43.54.194. Конкретно этот пакет попал в под 10.43.54.194, поэтому в строке 11 происходит переход от цепочки SVC к SEP (Service Endpoint) для пода 10.43.54.194. Наконец, строка 12 показывает, что DNAT действительно произошёл и IP-адрес получателя теперь равен 10.42.54.194:
Состояние пакета
Состояние пакета после цепочки nat:OUTPUT
:
-
IP-адрес отправителя: 192.168.1.88
-
Порт отправителя: 49058
-
IP-адрес получателя: 10.42.54.194
-
Порт получателя: 9898
-
Выходной интерфейс: enp3s0
-
Метка: 0x4000/0x4000
Таблица filter — OUTPUT
Таблица filter не даёт трафику покинуть хост. Учитывая это, существенных изменений пакета не ожидается.
00: PACKET: 2 0427ffe5 OUT=enp3s0 SRC=192.168.1.88 DST=10.42.54.194 \ LEN=60 TOS=0x0 TTL=64 ID=45471DF SPORT=49058 DPORT=9898 SYN MARK=0x4000 01: TRACE: 2 0427ffe5 filter:OUTPUT:rule:0x91:JUMP:cali-OUTPUT -4 \ -t filter -A OUTPUT -m comment --comment "cali:tVnHkvAo15HuiPy0" \ -j cali-OUTPUT 02: TRACE: 2 0427ffe5 filter:cali-OUTPUT:rule:0x8a:CONTINUE -4 \ -t filter -A cali-OUTPUT -m comment --comment "cali:iC1pSPgbvgQzkUk_" \ -j MARK --set-xmark 0x0/0xf0000 03: TRACE: 2 0427ffe5 filter:cali-OUTPUT:return: 04: TRACE: 2 0427ffe5 filter:OUTPUT:rule:0x17:JUMP:KUBE-PROXY-FIREWALL -4 \ -t filter -A OUTPUT -m conntrack --ctstate NEW -m comment \ --comment "kubernetes load balancer firewall" -j KUBE-PROXY-FIREWALL 05: TRACE: 2 0427ffe5 filter:KUBE-PROXY-FIREWALL:return: 06: TRACE: 2 0427ffe5 filter:OUTPUT:rule:0x12:JUMP:KUBE-SERVICES -4 \ -t filter -A OUTPUT -m conntrack --ctstate NEW -m comment \ --comment "kubernetes service portals" -j KUBE-SERVICES 07: TRACE: 2 0427ffe5 filter:KUBE-SERVICES:return: 08: TRACE: 2 0427ffe5 filter:OUTPUT:rule:0x6:JUMP:KUBE-FIREWALL -4 \ -t filter -A OUTPUT -j KUBE-FIREWALL 09: TRACE: 2 0427ffe5 filter:KUBE-FIREWALL:return: 10: TRACE: 2 0427ffe5 filter:OUTPUT:return: 11: TRACE: 2 0427ffe5 filter:OUTPUT:policy:ACCEPT
Первая запись TRACE
говорит о том, что пакет из цепочки filter:OUTPUT
перешёл в цепочку filter:cali-OUTPUT
. Цепочка filter:cali-OUTPUT
меняет метку пакета и затем передаёт обработку обратно в цепочку filter:OUTPUT
в третьей строке (обратите внимание, что метка пакета на самом деле не меняется из-за того, как работает побитовая AND между 0xf000
и 0x4000
). Далее пакет проходит через различные Kubernetes-цепочки, которые в конечном итоге не вносят никаких изменений и пакет попадает в ACCEPT
.
Но есть тут и кое-что полезное. Посмотрите на строки, в которых есть -m conntrack -ctstate NEW
. Поскольку это пакет TCP SYN (первый в трёхстороннем рукопожатии), он подлежит более тщательной проверке. Вышеупомянутые строки служат для обновления таблицы соединений, используемой iptables. Любопытно, что в полном выводе xtable-monitor --trace
трейсы последующих пакетов содержат гораздо меньше строк.
Состояние пакета
Состояние пакета после цепочки nat:OUTPUT
:
IP-адрес отправителя: 192.168.1.88
Порт отправителя: 49058
IP-адрес получателя: 10.42.54.194
Порт получателя: 9898
Выходной интерфейс: enp3s0
Метка: 0x4000/0x4000
Таблица mangle — POSTROUTING
Опять же, цель таблиц mangle — модифицировать пакеты (преимущественно поля TTL и ToS/DSCP в заголовках). Учитывая это, серьёзных изменений здесь также не предвидится.
00: PACKET: 2 0427ffe5 OUT=vxlan.calico SRC=192.168.1.88 DST=10.42.54.194 \ LEN=60 TOS=0x0 TTL=64 ID=45471DF SPORT=49058 DPORT=9898 SYN MARK=0x4000 01: TRACE: 2 0427ffe5 mangle:POSTROUTING:rule:0x15:JUMP:cali-POSTROUTING -4 \ -t mangle -A POSTROUTING -m comment --comment "cali:O3lYWMrLQYEMJtB5" \ -j cali-POSTROUTING 02: TRACE: 2 0427ffe5 mangle:cali-POSTROUTING:rule:0x12:CONTINUE -4 \ -t mangle -A cali-POSTROUTING -m comment --comment "cali:nnqPh8lh2VOogSzX" \ -j MARK --set-xmark 0x0/0xf0000 03: TRACE: 2 0427ffe5 mangle:cali-POSTROUTING:rule:0x13:JUMP:cali-to-host-endpoint \ -4 -t mangle -A cali-POSTROUTING -m comment --comment "cali:nquN8Jw8Tz72pcBW" \ -m conntrack --ctstate DNAT -j cali-to-host-endpoint 04: TRACE: 2 0427ffe5 mangle:cali-to-host-endpoint:return: 05: TRACE: 2 0427ffe5 mangle:cali-POSTROUTING:return: 06: TRACE: 2 0427ffe5 mangle:POSTROUTING:return: 07: TRACE: 2 0427ffe5 mangle:POSTROUTING:policy:ACCEPT
Ещё одна попытка обновить метку происходит в строке 02. Как и в прошлый раз, она остаётся прежней (0x40000
).
Состояние пакета
Состояние пакета после цепочки nat:OUTPUT
:
IP-адрес отправителя: 192.168.1.88
Порт отправителя: 49058
IP-адрес получателя: 10.42.54.194
Порт получателя: 9898
Выходной интерфейс: vxlan.calico
Метка: 0x4000/0x4000
Более заметное изменение состояния пакета косвенно связано с модификациями, произведёнными iptables. IP-адрес получателя пакета изменился в цепочке nat:OUTPUT
с адреса сервиса 10.43.156.98 на адрес пода 10.42.54.194. Далее был проведён поиск по таблице маршрутизации по адресу пода, и исходящим интерфейсом оказался vxlan.calico
(что и подтверждает вывод ниже):
$ ip route get 10.42.54.194 10.42.54.194 via 10.42.54.193 dev vxlan.calico src 10.42.135.130 uid 0 cache
Таким образом, оверлей VxLAN доставит пакет от узла, на котором был инициирован запрос, к узлу, на котором работает под, а заголовки а-ля «состояние пакета» будут обработаны узлом-получателем после удаления VxLAN-инкапсуляции. Вся VxLAN-операции происходят за пределами iptables.
Таблица NAT — POSTROUTING
Таблица NAT отвечает за любые NAT-изменения отправителя и получателя пакета. Поскольку получатель уже был изменён, никаких дополнительных действий не требуется:
00: PACKET: 2 0427ffe5 OUT=vxlan.calico SRC=192.168.1.88 DST=10.42.54.194 LEN=60 TOS=0x0 TTL=64 ID=45471DF SPORT=49058 DPORT=9898 SYN MARK=0x4000 01: TRACE: 2 0427ffe5 nat:POSTROUTING:rule:0xba:JUMP:cali-POSTROUTING -4 \ -t nat -A POSTROUTING -m comment --comment "cali:O3lYWMrLQYEMJtB5" \ -j cali-POSTROUTING 02: TRACE: 2 0427ffe5 nat:cali-POSTROUTING:rule:0xb5:JUMP:cali-fip-snat -4 \ -t nat -A cali-POSTROUTING -m comment --comment "cali:Z-c7XtVd2Bq7s_hA" \ -j cali-fip-snat 03: TRACE: 2 0427ffe5 nat:cali-fip-snat:return: 04: TRACE: 2 0427ffe5 nat:cali-POSTROUTING:rule:0xb6:JUMP:cali-nat-outgoing \ -4 -t nat -A cali-POSTROUTING -m comment --comment "cali:nYKhEzDlr11Jccal" \ -j cali-nat-outgoing 05: TRACE: 2 0427ffe5 nat:cali-nat-outgoing:return: 06: TRACE: 2 0427ffe5 nat:cali-POSTROUTING:rule:0xb7:ACCEPT -4 -t nat \ -A cali-POSTROUTING -o vxlan.calico -m comment \ --comment "cali:e9dnSgSVNmIcpVhP" -m addrtype ! --src-type LOCAL \ --limit-iface-out -m addrtype --src-type LOCAL -j MASQUERADE --random-fully
Закругляемся
Честно говоря, я в шоке, что вы дочитали до этого места. Что ж, спасибо. Надеюсь, что вы вынесли из этой статьи что-то, кроме головной боли и презрения к iptables.
P. S.
Читайте также в нашем блоге:
ссылка на оригинал статьи https://habr.com/ru/articles/856944/
Добавить комментарий