Добавлена локальная локальная балансировка
This commit is contained in:
1 parent
afd4f057be
commit
d80a6c44b0
17 files changed
+1628
-369
No files matched your search
+25
-3
@@ -19,8 +19,11 @@ lbctl — осмотр датапаса стенда hpnn_v2
|
||||
enable <член> вернуть член в балансировку
|
||||
metrics метрики в формате Prometheus
|
||||
conns таблица соединений ct (зона $CT_ZONE)
|
||||
trace <src_ip> [src_port]
|
||||
ofproto/trace для TCP-сессии клиент -> VIP:$VIP_PORT
|
||||
fdb таблица коммутации сегментов: выученные MAC и порты
|
||||
trace <src_ip> [src_port] [сегмент]
|
||||
ofproto/trace для TCP-сессии клиент -> VIP.
|
||||
Сегмент: pub (по умолчанию), p2, p3 — определяет порт входа
|
||||
и адрес листенера
|
||||
reload перегенерировать и применить пайплайн
|
||||
USAGE
|
||||
}
|
||||
@@ -68,11 +71,30 @@ metrics)
|
||||
conns)
|
||||
ovs-appctl dpctl/dump-conntrack | grep -F "zone=$CT_ZONE" || echo "соединений нет"
|
||||
;;
|
||||
fdb)
|
||||
echo "Выученные MAC (таблица 25, reg0 — номер сегмента):"
|
||||
# priority=100 — только записи, созданные обучением; правила рассылки
|
||||
# (priority 50 и 10) отбрасываются, иначе маска broadcast выглядела бы
|
||||
# как выученный MAC.
|
||||
ovs-ofctl -O "$OF" dump-flows "$BR" table=25 \
|
||||
| grep 'priority=100' \
|
||||
| sed -n 's/.*n_packets=\([0-9]*\).*reg0=\(0x[0-9a-f]*\).*dl_dst=\([0-9a-f:]*\).*output:\([0-9]*\).*/ сегмент \2 MAC \3 -> порт \4 (пакетов \1)/p' \
|
||||
| sort
|
||||
echo "Рассылка по сегменту (неизвестный MAC и broadcast):"
|
||||
ovs-ofctl -O "$OF" dump-flows "$BR" table=25 \
|
||||
| sed -n 's/.*n_packets=\([0-9]*\).*priority=\(50\|10\),reg0=\(0x[0-9a-f]*\).*/ сегмент \3 приоритет \2: пакетов \1/p' \
|
||||
| sort
|
||||
;;
|
||||
trace)
|
||||
src_ip="${2:?укажите IP клиента}"
|
||||
src_port="${3:-40000}"
|
||||
case "${4:-pub}" in
|
||||
p2) in_port=$CLI2_OFPORT; gw_mac=$P2_MAC; vip=$P2_VIP; vip_port=$PRIV_VIP_PORT ;;
|
||||
p3) in_port=$CLI3_OFPORT; gw_mac=$P3_MAC; vip=$P3_VIP; vip_port=$PRIV_VIP_PORT ;;
|
||||
*) in_port=$PUB_OFPORT; gw_mac=$PUB_MAC; vip=$VIP; vip_port=$VIP_PORT ;;
|
||||
esac
|
||||
ovs-appctl ofproto/trace "$BR" \
|
||||
"in_port=$PUB_OFPORT,dl_src=00:11:22:33:44:55,dl_dst=$PUB_MAC,dl_type=0x0800,nw_src=$src_ip,nw_dst=$VIP,nw_proto=6,nw_ttl=64,tp_src=$src_port,tp_dst=$VIP_PORT,tcp_flags=syn"
|
||||
"in_port=$in_port,dl_src=00:11:22:33:44:55,dl_dst=$gw_mac,dl_type=0x0800,nw_src=$src_ip,nw_dst=$vip,nw_proto=6,nw_ttl=64,tp_src=$src_port,tp_dst=$vip_port,tcp_flags=syn"
|
||||
;;
|
||||
reload)
|
||||
"$LB_DIR/apply.sh"
|
||||
|
||||
+222
-57
@@ -3,21 +3,33 @@
|
||||
# Применяется атомарным бандлом: ovs-ofctl --bundle -O OpenFlow15 replace-flows.
|
||||
#
|
||||
# Карта таблиц:
|
||||
# 0 — классификация по in_port и типу трафика, обучение MAC клиентов
|
||||
# 5 — ARP-респондер для собственных адресов узла и VIP
|
||||
# 0 — определение сегмента (reg0) по in_port, обучение MAC и adjacency
|
||||
# 1 — классификация: ARP, ICMP, листенер, L3-трафик к шлюзу, коммутация
|
||||
# 5 — ARP-респондер для собственных адресов узла и VIP, затем коммутация
|
||||
# 6 — ICMP echo-респондер для тех же адресов
|
||||
# 10 — листенеры: хэш сессии в слот (multipath), прочий IP -> маршрутизация
|
||||
# 11 — таблица слотов: слот -> член пула. Заливает и обновляет демон hcd
|
||||
# 12 — применение DNAT выбранным членом пула (по reg2)
|
||||
# 15 — обратный путь из приватных сегментов (снятие DNAT через ct)
|
||||
# 12 — применение выбранного члена: DNAT + выдача (L2 либо маршрутизация)
|
||||
# 15 — обратный путь L3 из приватных сегментов (снятие DNAT через ct)
|
||||
# 16 — пост-ct hook (счётчики, место под будущие проверки)
|
||||
# 20 — маршрутизация (LPM через приоритет = длина префикса)
|
||||
# 21 — adjacency: next-hop MAC + выходной порт
|
||||
# 24 — обратная трансляция коммутируемого трафика сегмента (шаг 3)
|
||||
# 25 — L2-коммутация сегмента: FDB, broadcast и unknown flood (шаг 3)
|
||||
#
|
||||
# Регистры: reg1 — номер слота, reg2 — идентификатор члена пула.
|
||||
# Регистры: reg0 — сегмент (1 — публичный, 2 и 3 — приватные), reg1 — номер
|
||||
# слота, reg2 — идентификатор члена пула.
|
||||
#
|
||||
# Нумерация не произвольна: goto_table в OpenFlow разрешает переход только
|
||||
# вперёд, поэтому обратный путь (15/16) стоит до общей маршрутизации (20).
|
||||
# вперёд, поэтому обратный путь (15/16) стоит до общей маршрутизации (20), а
|
||||
# коммутация сегмента (24/25) — после неё: в неё попадают и кадры, прошедшие
|
||||
# DNAT в таблице 12.
|
||||
#
|
||||
# Ключевое отличие шага 3: приватный сегмент коммутирует сам br-lb, каждая ВМ
|
||||
# подключена отдельным портом. Поэтому ответ бэкенда клиенту-соседу по подсети
|
||||
# проходит через таблицу 24, где ct снимает DNAT, — при этом ни в ОС ВМ, ни в
|
||||
# адресации сегмента ничего не меняется, а внутрисегментный трафик не
|
||||
# блокируется.
|
||||
set -euo pipefail
|
||||
|
||||
# shellcheck disable=SC1091
|
||||
@@ -34,20 +46,57 @@ LB_PUB_IP_H=$(ip2hex "$LB_PUB_IP")
|
||||
VIP_H=$(ip2hex "$VIP")
|
||||
LB_P2_IP_H=$(ip2hex "$LB_P2_IP")
|
||||
LB_P3_IP_H=$(ip2hex "$LB_P3_IP")
|
||||
P2_VIP_H=$(ip2hex "$P2_VIP")
|
||||
P3_VIP_H=$(ip2hex "$P3_VIP")
|
||||
|
||||
HC2_MAC_H=$(mac2hex "$HC2_MAC")
|
||||
HC3_MAC_H=$(mac2hex "$HC3_MAC")
|
||||
HC2_IP_H=$(ip2hex "$HC2_IP")
|
||||
HC3_IP_H=$(ip2hex "$HC3_IP")
|
||||
|
||||
# Действие обучения: по IP-адресу отправителя создаёт в таблице 21 запись
|
||||
# adjacency для обратного пути — куда и с каким MAC отправлять ответы этому
|
||||
# клиенту. Заменяет ARP-резолвер, которого у чисто-OpenFlow узла нет.
|
||||
LEARN="learn(table=21,priority=90,hard_timeout=300,eth_type=0x0800,NXM_OF_IP_DST[]=NXM_OF_IP_SRC[],load:NXM_OF_ETH_SRC[]->NXM_OF_ETH_DST[],load:$PUB_MAC_H->NXM_OF_ETH_SRC[],output:NXM_OF_IN_PORT[])"
|
||||
# --- описание сегментов ------------------------------------------------------
|
||||
# Производные величины: номер сегмента в reg0, состав портов и члены пула.
|
||||
P2_REG=0x2
|
||||
P3_REG=0x3
|
||||
P2_PORTS="$P2_OFPORT $HC2_OFPORT $BE1_OFPORT $BE3_OFPORT $CLI2_OFPORT"
|
||||
P3_PORTS="$P3_OFPORT $HC3_OFPORT $BE2_OFPORT $BE4_OFPORT $CLI3_OFPORT"
|
||||
P2_MEMBERS="BE1 BE3"
|
||||
P3_MEMBERS="BE2 BE4"
|
||||
|
||||
# arp_responder <ip> <ip_hex> <mac> <mac_hex>
|
||||
# flood_ports <список ofport> — действие рассылки по сегменту. Порт входа OVS
|
||||
# исключает сам, поэтому отдельного «кроме in_port» не требуется.
|
||||
flood_ports() {
|
||||
local out="" p
|
||||
for p in $1; do out="$out,output:$p"; done
|
||||
echo "${out#,}"
|
||||
}
|
||||
|
||||
# Включён ли приватный листенер в сегменте.
|
||||
listener_on() {
|
||||
local seg="$1" s
|
||||
for s in $PRIV_LISTENERS; do [ "$s" = "$seg" ] && return 0; done
|
||||
return 1
|
||||
}
|
||||
|
||||
# Действие обучения FDB: eth_src -> порт. Заполняет таблицу 25, то есть делает
|
||||
# из br-lb обычный обучающийся коммутатор в пределах сегмента.
|
||||
l2_learn() {
|
||||
echo "learn(table=25,priority=100,idle_timeout=300,NXM_NX_REG0[]=$1,NXM_OF_ETH_DST[]=NXM_OF_ETH_SRC[],output:NXM_OF_IN_PORT[])"
|
||||
}
|
||||
|
||||
# Действие обучения adjacency: по IP-адресу отправителя создаёт в таблице 21
|
||||
# запись «куда и с каким MAC отправлять ответы этому адресу». Заменяет
|
||||
# ARP-резолвер, которого у чисто-OpenFlow узла нет. Нужна маршрутизируемому
|
||||
# трафику: ответ бэкенда из соседнего сегмента приходит к клиенту через L3.
|
||||
l3_learn() {
|
||||
echo "learn(table=21,priority=90,hard_timeout=300,eth_type=0x0800,NXM_OF_IP_DST[]=NXM_OF_IP_SRC[],load:NXM_OF_ETH_SRC[]->NXM_OF_ETH_DST[],load:$1->NXM_OF_ETH_SRC[],output:NXM_OF_IN_PORT[])"
|
||||
}
|
||||
|
||||
LEARN_PUB=$(l3_learn "$PUB_MAC_H")
|
||||
|
||||
# arp_responder <reg0> <ip> <ip_hex> <mac> <mac_hex>
|
||||
arp_responder() {
|
||||
echo "table=5,priority=100,arp,arp_op=1,arp_tpa=$1 actions=move:NXM_OF_ETH_SRC[]->NXM_OF_ETH_DST[],mod_dl_src:$3,load:0x2->NXM_OF_ARP_OP[],move:NXM_NX_ARP_SHA[]->NXM_NX_ARP_THA[],move:NXM_OF_ARP_SPA[]->NXM_OF_ARP_TPA[],load:$4->NXM_NX_ARP_SHA[],load:$2->NXM_OF_ARP_SPA[],IN_PORT"
|
||||
echo "table=5,priority=100,reg0=$1,arp,arp_op=1,arp_tpa=$2 actions=move:NXM_OF_ETH_SRC[]->NXM_OF_ETH_DST[],mod_dl_src:$4,load:0x2->NXM_OF_ARP_OP[],move:NXM_NX_ARP_SHA[]->NXM_NX_ARP_THA[],move:NXM_OF_ARP_SPA[]->NXM_OF_ARP_TPA[],load:$5->NXM_NX_ARP_SHA[],load:$3->NXM_OF_ARP_SPA[],IN_PORT"
|
||||
}
|
||||
|
||||
# icmp_responder <ip> <ip_hex> <mac_hex>
|
||||
@@ -55,55 +104,114 @@ icmp_responder() {
|
||||
echo "table=6,priority=100,icmp,nw_dst=$1,icmp_type=8 actions=move:NXM_OF_ETH_SRC[]->NXM_OF_ETH_DST[],load:$3->NXM_OF_ETH_SRC[],move:NXM_OF_IP_SRC[]->NXM_OF_IP_DST[],load:$2->NXM_OF_IP_SRC[],load:0->NXM_OF_ICMP_TYPE[],IN_PORT"
|
||||
}
|
||||
|
||||
# segment_ingress <reg0> <mac_hex шлюза> <сеть> <порты> — таблица 0 для сегмента.
|
||||
# Обучение FDB идёт для любого кадра, обучение adjacency — только для адресов
|
||||
# самого сегмента: иначе с аплинка в таблицу 21 попал бы весь внешний мир.
|
||||
segment_ingress() {
|
||||
local reg="$1" gwmac_h="$2" net="$3" ports="$4" p
|
||||
local l2 l3
|
||||
l2=$(l2_learn "$reg")
|
||||
l3=$(l3_learn "$gwmac_h")
|
||||
for p in $ports; do
|
||||
echo "table=0,priority=200,in_port=$p,ip,nw_src=$net actions=load:$reg->NXM_NX_REG0[],$l2,$l3,goto_table:1"
|
||||
echo "table=0,priority=190,in_port=$p actions=load:$reg->NXM_NX_REG0[],$l2,goto_table:1"
|
||||
done
|
||||
}
|
||||
|
||||
# segment_classify <сегмент> <reg0> <mac шлюза> <vip> — таблица 1 для сегмента.
|
||||
segment_classify() {
|
||||
local seg="$1" reg="$2" gwmac="$3" vip="$4"
|
||||
echo "# --- сегмент $seg ---"
|
||||
if listener_on "$seg"; then
|
||||
echo "table=1,priority=130,reg0=$reg,dl_dst=$gwmac,tcp,nw_dst=$vip,tp_dst=$PRIV_VIP_PORT actions=goto_table:10"
|
||||
fi
|
||||
# Трафик на VIP, для которого листенера нет, отбрасывается со счётчиком —
|
||||
# так видно, что адрес занят стендом, а листенер выключен.
|
||||
echo "table=1,priority=125,reg0=$reg,dl_dst=$gwmac,ip,nw_dst=$vip actions=drop"
|
||||
# Кадр адресован маршрутизатору сегмента: транзит, ответы балансируемых
|
||||
# сессий из другого сегмента, обращения наружу через LB.
|
||||
echo "table=1,priority=120,reg0=$reg,dl_dst=$gwmac,ip actions=goto_table:15"
|
||||
# Всё остальное — трафик между ВМ сегмента: коммутация, при необходимости
|
||||
# с обратной трансляцией.
|
||||
echo "table=1,priority=100,reg0=$reg actions=goto_table:24"
|
||||
}
|
||||
|
||||
# segment_untranslate <reg0> <члены> — таблица 24.
|
||||
segment_untranslate() {
|
||||
local reg="$1" m ip port
|
||||
for m in $2; do
|
||||
eval "ip=\$${m}_IP; port=\$${m}_PORT"
|
||||
echo "table=24,priority=100,reg0=$reg,tcp,nw_src=$ip,tp_src=$port actions=ct(table=25,zone=$CT_ZONE,nat)"
|
||||
done
|
||||
}
|
||||
|
||||
cat <<EOF
|
||||
# =============================================================================
|
||||
# Таблица 0 — классификация
|
||||
# Таблица 0 — определение сегмента и обучение
|
||||
# =============================================================================
|
||||
# Публичный сегмент маршрутизируется как прежде: macvlan-порт один, коммутации
|
||||
# внутри него нет. Обучение adjacency для него живёт в таблице 1, где известно,
|
||||
# что трафик адресован стенду.
|
||||
table=0,priority=200,in_port=$PUB_OFPORT actions=load:0x1->NXM_NX_REG0[],goto_table:1
|
||||
|
||||
# Приватные сегменты: каждая ВМ — отдельный порт, br-lb работает коммутатором.
|
||||
$(segment_ingress "$P2_REG" "$P2_MAC_H" "$P2_NET" "$P2_PORTS")
|
||||
$(segment_ingress "$P3_REG" "$P3_MAC_H" "$P3_NET" "$P3_PORTS")
|
||||
|
||||
table=0,priority=0 actions=drop
|
||||
|
||||
# =============================================================================
|
||||
# Таблица 1 — классификация
|
||||
# =============================================================================
|
||||
# ARP обрабатывает собственный респондер (таблица 5): ядро в форвардинге не
|
||||
# участвует, поэтому штатного ARP-стека у узла нет.
|
||||
table=0,priority=200,arp actions=goto_table:5
|
||||
table=1,priority=200,arp actions=goto_table:5
|
||||
|
||||
# ICMP echo-request на собственные адреса узла и на VIP — в респондер.
|
||||
table=0,priority=190,icmp,nw_dst=$LB_PUB_IP,icmp_type=8 actions=goto_table:6
|
||||
table=0,priority=190,icmp,nw_dst=$VIP,icmp_type=8 actions=goto_table:6
|
||||
table=0,priority=190,icmp,nw_dst=$LB_P2_IP,icmp_type=8 actions=goto_table:6
|
||||
table=0,priority=190,icmp,nw_dst=$LB_P3_IP,icmp_type=8 actions=goto_table:6
|
||||
table=1,priority=190,icmp,nw_dst=$LB_PUB_IP,icmp_type=8 actions=goto_table:6
|
||||
table=1,priority=190,icmp,nw_dst=$VIP,icmp_type=8 actions=goto_table:6
|
||||
table=1,priority=190,icmp,nw_dst=$LB_P2_IP,icmp_type=8 actions=goto_table:6
|
||||
table=1,priority=190,icmp,nw_dst=$LB_P3_IP,icmp_type=8 actions=goto_table:6
|
||||
table=1,priority=190,icmp,nw_dst=$P2_VIP,icmp_type=8 actions=goto_table:6
|
||||
table=1,priority=190,icmp,nw_dst=$P3_VIP,icmp_type=8 actions=goto_table:6
|
||||
|
||||
# Вход из публичного сегмента: запоминаем MAC отправителя как adjacency для
|
||||
# обратного пути (замена ARP-резолвера на клиентской стороне), затем к
|
||||
# листенерам. Записи живут 300 с и обновляются каждым пакетом клиента.
|
||||
# обратного пути, затем к листенерам. Записи живут 300 с и обновляются каждым
|
||||
# пакетом клиента.
|
||||
#
|
||||
# Обучение включено только для трафика, адресованного стенду (VIP, адрес узла,
|
||||
# приватные сегменты). Иначе в таблицу 21 попадал бы весь широковещательный
|
||||
# шум LAN — macvlan-порт видит DHCP, mDNS и SSDP всех соседей по сегменту.
|
||||
table=0,priority=110,in_port=$PUB_OFPORT,ip,nw_dst=$VIP actions=$LEARN,goto_table:10
|
||||
table=0,priority=110,in_port=$PUB_OFPORT,ip,nw_dst=$LB_PUB_IP actions=$LEARN,goto_table:10
|
||||
table=0,priority=110,in_port=$PUB_OFPORT,ip,nw_dst=$P2_NET actions=$LEARN,goto_table:10
|
||||
table=0,priority=110,in_port=$PUB_OFPORT,ip,nw_dst=$P3_NET actions=$LEARN,goto_table:10
|
||||
table=0,priority=100,in_port=$PUB_OFPORT,ip actions=drop
|
||||
table=1,priority=110,reg0=0x1,ip,nw_dst=$VIP actions=$LEARN_PUB,goto_table:10
|
||||
table=1,priority=110,reg0=0x1,ip,nw_dst=$LB_PUB_IP actions=$LEARN_PUB,goto_table:10
|
||||
table=1,priority=110,reg0=0x1,ip,nw_dst=$P2_NET actions=$LEARN_PUB,goto_table:10
|
||||
table=1,priority=110,reg0=0x1,ip,nw_dst=$P3_NET actions=$LEARN_PUB,goto_table:10
|
||||
table=1,priority=100,reg0=0x1,ip actions=drop
|
||||
|
||||
# Вход из приватных сегментов: обратный путь балансируемых сессий и транзит.
|
||||
table=0,priority=100,in_port=$P2_OFPORT,ip actions=goto_table:15
|
||||
table=0,priority=100,in_port=$P3_OFPORT,ip actions=goto_table:15
|
||||
$(segment_classify p2 "$P2_REG" "$P2_MAC" "$P2_VIP")
|
||||
$(segment_classify p3 "$P3_REG" "$P3_MAC" "$P3_VIP")
|
||||
|
||||
# Health-пробы, порождённые самим узлом: сразу в маршрутизацию, без ct и без
|
||||
# обучения — это трафик из стека узла, а не клиентская сессия.
|
||||
table=0,priority=100,in_port=$HC2_OFPORT,ip actions=goto_table:20
|
||||
table=0,priority=100,in_port=$HC3_OFPORT,ip actions=goto_table:20
|
||||
|
||||
# Всё остальное (не-IP, не-ARP: broadcast, IPv6, DHCP) в прототипе не нужно.
|
||||
table=0,priority=0 actions=drop
|
||||
# Всё остальное (не-IP, не-ARP с публичного порта) в прототипе не нужно.
|
||||
table=1,priority=0 actions=drop
|
||||
|
||||
# =============================================================================
|
||||
# Таблица 5 — ARP-респондер
|
||||
# =============================================================================
|
||||
$(arp_responder "$LB_PUB_IP" "$LB_PUB_IP_H" "$PUB_MAC" "$PUB_MAC_H")
|
||||
$(arp_responder "$VIP" "$VIP_H" "$PUB_MAC" "$PUB_MAC_H")
|
||||
$(arp_responder "$LB_P2_IP" "$LB_P2_IP_H" "$P2_MAC" "$P2_MAC_H")
|
||||
$(arp_responder "$LB_P3_IP" "$LB_P3_IP_H" "$P3_MAC" "$P3_MAC_H")
|
||||
$(arp_responder 0x1 "$LB_PUB_IP" "$LB_PUB_IP_H" "$PUB_MAC" "$PUB_MAC_H")
|
||||
$(arp_responder 0x1 "$VIP" "$VIP_H" "$PUB_MAC" "$PUB_MAC_H")
|
||||
$(arp_responder "$P2_REG" "$LB_P2_IP" "$LB_P2_IP_H" "$P2_MAC" "$P2_MAC_H")
|
||||
$(arp_responder "$P3_REG" "$LB_P3_IP" "$LB_P3_IP_H" "$P3_MAC" "$P3_MAC_H")
|
||||
# VIP приватных листенеров отвечают тем же MAC, что и шлюз сегмента: по нему
|
||||
# таблица 1 отличает трафик к маршрутизатору от трафика между ВМ.
|
||||
$(arp_responder "$P2_REG" "$P2_VIP" "$P2_VIP_H" "$P2_MAC" "$P2_MAC_H")
|
||||
$(arp_responder "$P3_REG" "$P3_VIP" "$P3_VIP_H" "$P3_MAC" "$P3_MAC_H")
|
||||
# Адреса источника health-проб: бэкенд резолвит их, отвечая на пробу.
|
||||
$(arp_responder "$HC2_IP" "$HC2_IP_H" "$HC2_MAC" "$HC2_MAC_H")
|
||||
$(arp_responder "$HC3_IP" "$HC3_IP_H" "$HC3_MAC" "$HC3_MAC_H")
|
||||
$(arp_responder "$P2_REG" "$HC2_IP" "$HC2_IP_H" "$HC2_MAC" "$HC2_MAC_H")
|
||||
$(arp_responder "$P3_REG" "$HC3_IP" "$HC3_IP_H" "$HC3_MAC" "$HC3_MAC_H")
|
||||
# Остальной ARP сегмента — обычная коммутация: ВМ резолвят друг друга сами,
|
||||
# балансировщик в это не вмешивается.
|
||||
table=5,priority=10,reg0=$P2_REG actions=goto_table:25
|
||||
table=5,priority=10,reg0=$P3_REG actions=goto_table:25
|
||||
table=5,priority=0 actions=drop
|
||||
|
||||
# =============================================================================
|
||||
@@ -113,24 +221,38 @@ $(icmp_responder "$LB_PUB_IP" "$LB_PUB_IP_H" "$PUB_MAC_H")
|
||||
$(icmp_responder "$VIP" "$VIP_H" "$PUB_MAC_H")
|
||||
$(icmp_responder "$LB_P2_IP" "$LB_P2_IP_H" "$P2_MAC_H")
|
||||
$(icmp_responder "$LB_P3_IP" "$LB_P3_IP_H" "$P3_MAC_H")
|
||||
$(icmp_responder "$P2_VIP" "$P2_VIP_H" "$P2_MAC_H")
|
||||
$(icmp_responder "$P3_VIP" "$P3_VIP_H" "$P3_MAC_H")
|
||||
table=6,priority=0 actions=drop
|
||||
|
||||
# =============================================================================
|
||||
# Таблица 10 — листенеры
|
||||
# =============================================================================
|
||||
# Единственный листенер прототипа: TCP $VIP:$VIP_PORT.
|
||||
# multipath раскладывает сессию в один из $SLOTS слотов по симметричному
|
||||
# L4-хэшу; basis = POOL_ID, поэтому разные пулы дают независимые раскладки.
|
||||
# Хэш симметричен, то есть прямое и обратное направление дают один слот —
|
||||
# свойство, необходимое для будущего stateless-датапаса.
|
||||
table=10,priority=200,tcp,nw_dst=$VIP,tp_dst=$VIP_PORT actions=multipath(symmetric_l4,$POOL_ID,modulo_n,$SLOTS,0,NXM_NX_REG1[]),goto_table:11
|
||||
#
|
||||
# Публичный листенер: TCP $VIP:$VIP_PORT.
|
||||
table=10,priority=200,reg0=0x1,tcp,nw_dst=$VIP,tp_dst=$VIP_PORT actions=multipath(symmetric_l4,$POOL_ID,modulo_n,$SLOTS,0,NXM_NX_REG1[]),goto_table:11
|
||||
|
||||
# Трафик на VIP, для которого листенера нет, — отбрасываем со счётчиком.
|
||||
table=10,priority=150,ip,nw_dst=$VIP actions=drop
|
||||
table=10,priority=150,reg0=0x1,ip,nw_dst=$VIP actions=drop
|
||||
|
||||
# Прочий IP из публичного сегмента маршрутизируется обычным образом
|
||||
# (клиент может обратиться напрямую к бэкенду, минуя балансировку).
|
||||
table=10,priority=100,ip actions=goto_table:20
|
||||
table=10,priority=100,reg0=0x1,ip actions=goto_table:20
|
||||
EOF
|
||||
|
||||
# Приватные листенеры: тот же пул, та же таблица слотов, тот же хэш. Отличие
|
||||
# только в адресе и в том, что выдача пойдёт коммутацией (таблица 12).
|
||||
for seg in p2 p3; do
|
||||
listener_on "$seg" || continue
|
||||
if [ "$seg" = p2 ]; then reg=$P2_REG; vip=$P2_VIP; else reg=$P3_REG; vip=$P3_VIP; fi
|
||||
echo "table=10,priority=200,reg0=$reg,tcp,nw_dst=$vip,tp_dst=$PRIV_VIP_PORT actions=multipath(symmetric_l4,$POOL_ID,modulo_n,$SLOTS,0,NXM_NX_REG1[]),goto_table:11"
|
||||
done
|
||||
|
||||
cat <<EOF
|
||||
table=10,priority=0 actions=drop
|
||||
|
||||
# =============================================================================
|
||||
@@ -145,10 +267,28 @@ table=10,priority=0 actions=drop
|
||||
table=11,priority=0 actions=drop
|
||||
|
||||
# =============================================================================
|
||||
# Таблица 12 — DNAT выбранным членом пула
|
||||
# Таблица 12 — применение выбранного члена пула
|
||||
# =============================================================================
|
||||
# Слот положил идентификатор члена в reg2. SNAT не выполняется: бэкенд видит
|
||||
# реальный адрес клиента.
|
||||
# Слот положил идентификатор члена в reg2. SNAT не выполняется ни в одном из
|
||||
# путей: бэкенд видит реальный адрес клиента.
|
||||
#
|
||||
# Член в том же сегменте, что и клиент (приоритет 200) — выдача коммутацией:
|
||||
# меняется только MAC назначения, TTL не уменьшается, маршрутизации нет. Для
|
||||
# ВМ клиент и бэкенд остаются L2-соседями, какими и были.
|
||||
EOF
|
||||
|
||||
for seg_reg in "$P2_REG:$P2_MEMBERS" "$P3_REG:$P3_MEMBERS"; do
|
||||
reg="${seg_reg%%:*}"
|
||||
for m in ${seg_reg#*:}; do
|
||||
eval "mip=\$${m}_IP; mport=\$${m}_PORT; mmac=\$${m}_MAC; mid=\$${m}_ID"
|
||||
echo "table=12,priority=200,reg0=$reg,ip,reg2=$mid actions=mod_dl_dst:$mmac,ct(commit,zone=$CT_ZONE,nat(dst=$mip:$mport),table=25)"
|
||||
done
|
||||
done
|
||||
|
||||
cat <<EOF
|
||||
|
||||
# Член в другом сегменте либо публичный листенер — обычный путь через
|
||||
# маршрутизацию: адрес назначения уже переписан, дальше работают таблицы 20/21.
|
||||
table=12,priority=100,ip,reg2=0x1 actions=ct(commit,zone=$CT_ZONE,nat(dst=$BE1_IP:$BE1_PORT),table=20)
|
||||
table=12,priority=100,ip,reg2=0x2 actions=ct(commit,zone=$CT_ZONE,nat(dst=$BE2_IP:$BE2_PORT),table=20)
|
||||
table=12,priority=100,ip,reg2=0x3 actions=ct(commit,zone=$CT_ZONE,nat(dst=$BE3_IP:$BE3_PORT),table=20)
|
||||
@@ -156,11 +296,11 @@ table=12,priority=100,ip,reg2=0x4 actions=ct(commit,zone=$CT_ZONE,nat(dst=$BE4_I
|
||||
table=12,priority=0 actions=drop
|
||||
|
||||
# =============================================================================
|
||||
# Таблица 15 — обратный путь из приватных сегментов
|
||||
# Таблица 15 — обратный путь L3
|
||||
# =============================================================================
|
||||
# ct(nat) без commit снимает ранее выполненный DNAT: source возвращается к VIP.
|
||||
# Для сессий, которых нет в таблице соединений (трафик be1<->be2, обращения
|
||||
# бэкендов к клиентским префиксам), пакет проходит без изменений.
|
||||
# Для сессий, которых нет в таблице соединений (транзит между сегментами,
|
||||
# обращения бэкендов к клиентским префиксам), пакет проходит без изменений.
|
||||
table=15,priority=100,tcp actions=ct(table=16,zone=$CT_ZONE,nat)
|
||||
table=15,priority=50,ip actions=goto_table:20
|
||||
table=15,priority=0 actions=drop
|
||||
@@ -189,14 +329,39 @@ table=20,priority=0 actions=drop
|
||||
# =============================================================================
|
||||
# Таблица 21 — adjacency (next-hop MAC + выходной порт)
|
||||
# =============================================================================
|
||||
# Бэкенды прописаны статически: их MAC фиксирован в docker-compose.yml.
|
||||
table=21,priority=100,ip,nw_dst=$BE1_IP actions=mod_dl_dst:$BE1_MAC,output:$P2_OFPORT
|
||||
table=21,priority=100,ip,nw_dst=$BE2_IP actions=mod_dl_dst:$BE2_MAC,output:$P3_OFPORT
|
||||
table=21,priority=100,ip,nw_dst=$BE3_IP actions=mod_dl_dst:$BE3_MAC,output:$P2_OFPORT
|
||||
table=21,priority=100,ip,nw_dst=$BE4_IP actions=mod_dl_dst:$BE4_MAC,output:$P3_OFPORT
|
||||
# Члены пула прописаны статически: их MAC и порт фиксированы в topology.env.
|
||||
table=21,priority=100,ip,nw_dst=$BE1_IP actions=mod_dl_dst:$BE1_MAC,output:$BE1_OFPORT
|
||||
table=21,priority=100,ip,nw_dst=$BE2_IP actions=mod_dl_dst:$BE2_MAC,output:$BE2_OFPORT
|
||||
table=21,priority=100,ip,nw_dst=$BE3_IP actions=mod_dl_dst:$BE3_MAC,output:$BE3_OFPORT
|
||||
table=21,priority=100,ip,nw_dst=$BE4_IP actions=mod_dl_dst:$BE4_MAC,output:$BE4_OFPORT
|
||||
# Ответы на health-пробы — в стек узла через internal-порты.
|
||||
table=21,priority=100,ip,nw_dst=$HC2_IP actions=mod_dl_dst:$HC2_MAC,output:$HC2_OFPORT
|
||||
table=21,priority=100,ip,nw_dst=$HC3_IP actions=mod_dl_dst:$HC3_MAC,output:$HC3_OFPORT
|
||||
# priority=90 — записи клиентов, устанавливаемые действием learn из таблицы 0.
|
||||
# priority=90 — записи клиентов, устанавливаемые действием learn из таблиц 0 и 1.
|
||||
table=21,priority=0 actions=drop
|
||||
|
||||
# =============================================================================
|
||||
# Таблица 24 — обратная трансляция коммутируемого трафика сегмента
|
||||
# =============================================================================
|
||||
# Здесь решается задача шага 3. Бэкенд отвечает клиенту-соседу напрямую по L2,
|
||||
# но кадр всё равно проходит через br-lb, потому что порт ВМ — порт OVS.
|
||||
# ct(nat) без commit возвращает source к VIP:80 для балансируемых сессий и
|
||||
# пропускает без изменений всё остальное — обращения be<->be, ответы на
|
||||
# health-пробы, прямые обращения клиента к бэкенду.
|
||||
$(segment_untranslate "$P2_REG" "$P2_MEMBERS")
|
||||
$(segment_untranslate "$P3_REG" "$P3_MEMBERS")
|
||||
table=24,priority=0 actions=goto_table:25
|
||||
|
||||
# =============================================================================
|
||||
# Таблица 25 — L2-коммутация сегмента
|
||||
# =============================================================================
|
||||
# priority=100 — записи FDB, устанавливаемые действием learn из таблицы 0.
|
||||
# Ниже — рассылка по сегменту: broadcast и multicast отдельным правилом ради
|
||||
# счётчика, неизвестный unicast — тем же способом. Порт входа OVS исключает
|
||||
# из рассылки сам.
|
||||
table=25,priority=50,reg0=$P2_REG,dl_dst=01:00:00:00:00:00/01:00:00:00:00:00 actions=$(flood_ports "$P2_PORTS")
|
||||
table=25,priority=50,reg0=$P3_REG,dl_dst=01:00:00:00:00:00/01:00:00:00:00:00 actions=$(flood_ports "$P3_PORTS")
|
||||
table=25,priority=10,reg0=$P2_REG actions=$(flood_ports "$P2_PORTS")
|
||||
table=25,priority=10,reg0=$P3_REG actions=$(flood_ports "$P3_PORTS")
|
||||
table=25,priority=0 actions=drop
|
||||
EOF
|
||||
+39
-2
@@ -17,18 +17,35 @@ LB_PUB_IP=192.168.5.20
|
||||
VIP=192.168.5.21
|
||||
VIP_PORT=80
|
||||
|
||||
# --- сеть 2: приватный сегмент бэкенда be1 -----------------------------------
|
||||
# --- сеть 2: приватный сегмент 10.20.0.0/24 ----------------------------------
|
||||
# С шага 3 сегмент коммутирует сам br-lb: каждая ВМ сегмента подключена
|
||||
# отдельным портом (см. scripts/attach-segment.sh). Порт p2 остался аплинком к
|
||||
# docker-бриджу: через него идёт egress бэкендов на шлюз .254 (профиль A).
|
||||
#
|
||||
# Адрес шлюза LB_P2_IP и VIP приватного листенера P2_VIP отвечают одним MAC
|
||||
# (P2_MAC) — по нему пайплайн отличает L3-трафик к маршрутизатору от
|
||||
# L2-трафика сегмента.
|
||||
P2_IFNAME=p2
|
||||
P2_OFPORT=2
|
||||
P2_MAC=02:42:0a:14:00:01
|
||||
P2_NET=10.20.0.0/24
|
||||
P2_DOCKER_GW=10.20.0.254
|
||||
LB_P2_IP=10.20.0.1
|
||||
BE1_IP=10.20.0.2
|
||||
BE1_MAC=02:42:0a:14:00:02
|
||||
BE1_PORT=8080
|
||||
BE1_OFPORT=10
|
||||
BE1_ID=0x1
|
||||
BE3_IP=10.20.0.3
|
||||
BE3_MAC=02:42:0a:14:00:03
|
||||
BE3_PORT=8080
|
||||
BE3_OFPORT=11
|
||||
BE3_ID=0x3
|
||||
# Клиент внутри сегмента — та самая ВМ, что живёт рядом с бэкендами.
|
||||
# В его ОС не настраивается ничего, кроме адреса: VIP находится в его подсети.
|
||||
CLI2_IP=10.20.0.10
|
||||
CLI2_MAC=02:42:0a:14:00:0a
|
||||
CLI2_OFPORT=12
|
||||
# Уникальный адрес узла в сегменте — источник health-проб (§7.1 дизайна v1).
|
||||
# Живёт на internal-порту OVS: это единственный адрес, который узел держит в
|
||||
# ядре, весь остальной форвардинг идёт мимо стека.
|
||||
@@ -37,23 +54,43 @@ HC2_OFPORT=4
|
||||
HC2_MAC=02:42:0a:14:00:fd
|
||||
HC2_IP=10.20.0.253
|
||||
|
||||
# --- сеть 3: приватный сегмент бэкенда be2 -----------------------------------
|
||||
# --- сеть 3: приватный сегмент 10.30.0.0/24 ----------------------------------
|
||||
P3_IFNAME=p3
|
||||
P3_OFPORT=3
|
||||
P3_MAC=02:42:0a:1e:00:01
|
||||
P3_NET=10.30.0.0/24
|
||||
P3_DOCKER_GW=10.30.0.254
|
||||
LB_P3_IP=10.30.0.1
|
||||
BE2_IP=10.30.0.2
|
||||
BE2_MAC=02:42:0a:1e:00:02
|
||||
BE2_PORT=8080
|
||||
BE2_OFPORT=20
|
||||
BE2_ID=0x2
|
||||
BE4_IP=10.30.0.3
|
||||
BE4_MAC=02:42:0a:1e:00:03
|
||||
BE4_PORT=8080
|
||||
BE4_OFPORT=21
|
||||
BE4_ID=0x4
|
||||
CLI3_IP=10.30.0.10
|
||||
CLI3_MAC=02:42:0a:1e:00:0a
|
||||
CLI3_OFPORT=22
|
||||
HC3_IFNAME=hcif-p3
|
||||
HC3_OFPORT=5
|
||||
HC3_MAC=02:42:0a:1e:00:fd
|
||||
HC3_IP=10.30.0.253
|
||||
|
||||
# --- приватные листенеры (шаг 3) ---------------------------------------------
|
||||
# VIP берётся из подсети сегмента: клиенту не нужны ни маршруты, ни настройки —
|
||||
# адрес находится в его собственной сети, ARP-респондер OVS отвечает за него.
|
||||
#
|
||||
# PRIV_LISTENERS перечисляет сегменты, где листенер поднят. Пустое значение
|
||||
# выключает приватные листенеры целиком, "p2" — включает один сегмент,
|
||||
# "p2 p3" — оба. Пул и таблица слотов общие с публичным листенером.
|
||||
PRIV_LISTENERS="p2 p3"
|
||||
PRIV_VIP_PORT=80
|
||||
P2_VIP=10.20.0.100
|
||||
P3_VIP=10.30.0.100
|
||||
|
||||
# --- пул и таблица слотов ----------------------------------------------------
|
||||
# POOL_ID служит basis хэша multipath: разные пулы дают независимые раскладки.
|
||||
POOL_ID=1
|
||||
|
||||
Reference in new issue
Block a user