[Перевод] Как работают Kubernetes Services: управление трафиком с помощью iptables
Примечание переводчика: статья является переводом оригинального материала Марка Бетца (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
Посмотрим, куда было запланировано наше приложение:
Вывод команды kubectl get po -n podinfo -o wide
показывает, что поды запланированы на узлы k8s-node04 и k8s-node06
А теперь посмотрим, какой IP-адрес был присвоен созданному сервису:
Результат команды kubectl get svc -n podinfo
показывает единственный сервис с адресом 10.43.156.98
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 обрабатывает пакеты. Для пакета, исходящего из системы, порядок следующий:
Более подробную информацию можно найти здесь в разделе 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
Вот схема, соответствующая приведённому выше выводу:
Схема прохождения пакета через raw в mangle
Первое правило в цепочке 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 в filter
Состояние пакета
Состояние пакета после цепочки nat:OUTPUT
:
IP-адрес отправителя: 192.168.1.88
Порт отправителя: 49058
IP-адрес получателя: 10.42.54.194
Порт получателя: 9898
Выходной интерфейс: enp3s0
Метка: 0×4000/0×4000
Таблица 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
Метка: 0×4000/0×4000
Таблица 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
Метка: 0×4000/0×4000
Более заметное изменение состояния пакета косвенно связано с модификациями, произведёнными 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.
Читайте также в нашем блоге: