Шаг 5: динамическое изучение MAC/ARP для бэкендов
MAC каждого члена пула больше не статическая константа в topology.env, а резолвится демоном hcd через обычный ARP ядра — в реальном окружении MAC бэкенда заранее не известен (сервер ещё не подключён, NIC может замениться), топология не описывается статически, в отличие от стенда. hcif-порты (единственные адреса узла в ядре) уже были настоящими L3-интерфейсами в тех же сегментах, что и бэкенды — единственное, что мешало обычному ARP, это permanent-записи ip neigh в entrypoint.sh. Убрав их и добавив hc/neigh.go (читает ip -json neigh show, точечно заливает бандл в таблицу 21 на том же тикере, что и health-пробы), получили резолвер без нового OpenFlow-контроллера. Таблица 21 стала единственным источником MAC для обоих путей — маршрутизируемого и коммутируемого (шаг 3): таблица 12 больше не дублирует MAC инлайново, ct(commit) ведёт сразу в таблицу 21. Исправлен попутно найденный баг: после apply.sh (replace-flows) таблица 21 не восстанавливалась, поскольку syncNeighbors сравнивал MAC с памятью демона, а не с датапасом. Добавлен force-режим по аналогии с Agent.apply(), плюс регрессионная проверка в verify.sh. Проверено на живом стенде: MAC всех четырёх членов резолвлен и совпадает с реальными интерфейсами; смена MAC "железа" обнаружена и применена без вмешательства за счёт штатного старения ARP ядра. Регрессия: 94 из 94 проверок. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
1 parent
f34d2edefa
commit
fa389f119b
17 files changed
+678
-61
No files matched your search
+10
-15
@@ -82,31 +82,26 @@ attach_port "$P3_MAC" "$P3_IFNAME" "$P3_OFPORT"
|
||||
# --- 4a. Порты-источники health-проб -----------------------------------------
|
||||
# Единственные адреса, которые узел держит в ядре: с них уходят пробы (§7.1
|
||||
# дизайна v1 — источник проб должен быть уникальным адресом узла, не VIP).
|
||||
# MAC бэкенда прописывается статически: ARP-резолвер узлу не нужен, MAC членов
|
||||
# пула и так зафиксированы в docker-compose.yml.
|
||||
#
|
||||
# MAC членов пула здесь больше НЕ прописывается статически (шаг 5): hcif-порты
|
||||
# — настоящие L3-интерфейсы ядра в том же сегменте, что и бэкенды, и ядро само
|
||||
# резолвит их MAC обычным ARP. Первая проба после старта чуть медленнее (кадр
|
||||
# ARP-запроса уходит через br-lb и возвращается ответом реального бэкенда), но
|
||||
# дальше запись живёт в neigh-таблице ядра обычным старением ARP — именно её
|
||||
# читает hcd (hc/neigh.go) и заливает в таблицу 21 OpenFlow.
|
||||
hc_port() {
|
||||
local name="$1" ofport="$2" mac="$3" ip="$4" net="$5"
|
||||
shift 5
|
||||
ovs-vsctl --may-exist add-port "$BR" "$name" \
|
||||
-- set Interface "$name" type=internal ofport_request="$ofport" \
|
||||
-- set Interface "$name" mac="\"$mac\""
|
||||
ip link set dev "$name" address "$mac"
|
||||
ip addr replace "$ip/${net##*/}" dev "$name"
|
||||
ip link set dev "$name" up
|
||||
# Остальные аргументы — пары «адрес MAC» членов пула в этом сегменте.
|
||||
local peers=""
|
||||
while [ $# -ge 2 ]; do
|
||||
ip neigh replace "$1" lladdr "$2" dev "$name" nud permanent
|
||||
peers="$peers $1"
|
||||
shift 2
|
||||
done
|
||||
log "порт $name ($ip) — источник health-проб для$peers"
|
||||
log "порт $name ($ip) — источник health-проб"
|
||||
}
|
||||
|
||||
hc_port "$HC2_IFNAME" "$HC2_OFPORT" "$HC2_MAC" "$HC2_IP" "$P2_NET" \
|
||||
"$BE1_IP" "$BE1_MAC" "$BE3_IP" "$BE3_MAC"
|
||||
hc_port "$HC3_IFNAME" "$HC3_OFPORT" "$HC3_MAC" "$HC3_IP" "$P3_NET" \
|
||||
"$BE2_IP" "$BE2_MAC" "$BE4_IP" "$BE4_MAC"
|
||||
hc_port "$HC2_IFNAME" "$HC2_OFPORT" "$HC2_MAC" "$HC2_IP" "$P2_NET"
|
||||
hc_port "$HC3_IFNAME" "$HC3_OFPORT" "$HC3_MAC" "$HC3_IP" "$P3_NET"
|
||||
|
||||
# Ждём, пока vswitchd реально привяжет порты к датапасу.
|
||||
for name in "$PUB_IFNAME" "$P2_IFNAME" "$P3_IFNAME"; do
|
||||
|
||||
+17
-4
@@ -2,6 +2,10 @@
|
||||
# Генерация конфигурации демона hcd из topology.env — единственного источника
|
||||
# правды по адресам стенда. Пока пул задан статически; на следующем шаге его
|
||||
# место займёт модель Load_Balancer -> Listener -> Pool -> Member в OVSDB.
|
||||
#
|
||||
# MAC членов пула здесь не фигурирует (шаг 5): hcd резолвит его сам через
|
||||
# neigh-таблицу ядра (hc/neigh.go) на интерфейсе "iface" и заливает в таблицу
|
||||
# "adj_table" по указанному "ofport". См. docs/STEP5_SUMMARY.md.
|
||||
set -euo pipefail
|
||||
|
||||
LB_DIR=/opt/lb
|
||||
@@ -18,6 +22,7 @@ cat > "$OUT" <<EOF
|
||||
"slots": $SLOTS,
|
||||
"slot_table": 11,
|
||||
"dnat_table": 12,
|
||||
"adj_table": 21,
|
||||
"probe": "$HC_PROBE",
|
||||
"http_path": "$HC_HTTP_PATH",
|
||||
"interval": "$HC_INTERVAL",
|
||||
@@ -33,7 +38,9 @@ cat > "$OUT" <<EOF
|
||||
"address": "$BE1_IP",
|
||||
"port": $BE1_PORT,
|
||||
"weight": 1,
|
||||
"source": "$HC2_IP"
|
||||
"source": "$HC2_IP",
|
||||
"iface": "$HC2_IFNAME",
|
||||
"ofport": $BE1_OFPORT
|
||||
},
|
||||
{
|
||||
"id": 2,
|
||||
@@ -41,7 +48,9 @@ cat > "$OUT" <<EOF
|
||||
"address": "$BE2_IP",
|
||||
"port": $BE2_PORT,
|
||||
"weight": 1,
|
||||
"source": "$HC3_IP"
|
||||
"source": "$HC3_IP",
|
||||
"iface": "$HC3_IFNAME",
|
||||
"ofport": $BE2_OFPORT
|
||||
},
|
||||
{
|
||||
"id": 3,
|
||||
@@ -49,7 +58,9 @@ cat > "$OUT" <<EOF
|
||||
"address": "$BE3_IP",
|
||||
"port": $BE3_PORT,
|
||||
"weight": 1,
|
||||
"source": "$HC2_IP"
|
||||
"source": "$HC2_IP",
|
||||
"iface": "$HC2_IFNAME",
|
||||
"ofport": $BE3_OFPORT
|
||||
},
|
||||
{
|
||||
"id": 4,
|
||||
@@ -57,7 +68,9 @@ cat > "$OUT" <<EOF
|
||||
"address": "$BE4_IP",
|
||||
"port": $BE4_PORT,
|
||||
"weight": 1,
|
||||
"source": "$HC3_IP"
|
||||
"source": "$HC3_IP",
|
||||
"iface": "$HC3_IFNAME",
|
||||
"ofport": $BE4_OFPORT
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -20,6 +20,7 @@ lbctl — осмотр датапаса стенда hpnn_v2
|
||||
metrics метрики в формате Prometheus
|
||||
conns таблица соединений ct (зона $CT_ZONE)
|
||||
fdb таблица коммутации сегментов: выученные MAC и порты
|
||||
neigh MAC членов пула, резолвленный hcd через ARP ядра (таблица 21)
|
||||
trace <src_ip> [src_port] [сегмент]
|
||||
ofproto/trace для TCP-сессии клиент -> VIP.
|
||||
Сегмент: pub (по умолчанию), p2, p3 — определяет порт входа
|
||||
@@ -85,6 +86,12 @@ fdb)
|
||||
| sed -n 's/.*n_packets=\([0-9]*\).*priority=\(50\|10\),reg0=\(0x[0-9a-f]*\).*/ сегмент \3 приоритет \2: пакетов \1/p' \
|
||||
| sort
|
||||
;;
|
||||
neigh)
|
||||
api /neigh.txt
|
||||
echo
|
||||
echo "Правила таблицы 21 (priority=100 — заливает hcd, priority=90 — learn):"
|
||||
ovs-ofctl -O "$OF" dump-flows "$BR" table=21 | grep -E 'priority=(100|90),ip' | sort
|
||||
;;
|
||||
trace)
|
||||
src_ip="${2:?укажите IP клиента}"
|
||||
src_port="${3:-40000}"
|
||||
|
||||
+21
-12
@@ -272,16 +272,18 @@ table=11,priority=0 actions=drop
|
||||
# Слот положил идентификатор члена в reg2. SNAT не выполняется ни в одном из
|
||||
# путей: бэкенд видит реальный адрес клиента.
|
||||
#
|
||||
# Член в том же сегменте, что и клиент (приоритет 200) — выдача коммутацией:
|
||||
# меняется только MAC назначения, TTL не уменьшается, маршрутизации нет. Для
|
||||
# ВМ клиент и бэкенд остаются L2-соседями, какими и были.
|
||||
# Член в том же сегменте, что и клиент (приоритет 200) — TTL не уменьшается,
|
||||
# маршрутизации нет. MAC назначения здесь больше не пишется (шаг 5): следующий
|
||||
# переход — таблица 21, единственное место, где живёт актуальный MAC члена
|
||||
# (резолвит hcd через ARP, см. hc/neigh.go). Для ВМ клиент и бэкенд остаются
|
||||
# L2-соседями, какими и были — таблица 21 сама не декрементирует TTL.
|
||||
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)"
|
||||
eval "mip=\$${m}_IP; mport=\$${m}_PORT; mid=\$${m}_ID"
|
||||
echo "table=12,priority=200,reg0=$reg,ip,reg2=$mid actions=ct(commit,zone=$CT_ZONE,nat(dst=$mip:$mport),table=21)"
|
||||
done
|
||||
done
|
||||
|
||||
@@ -329,15 +331,22 @@ table=20,priority=0 actions=drop
|
||||
# =============================================================================
|
||||
# Таблица 21 — adjacency (next-hop MAC + выходной порт)
|
||||
# =============================================================================
|
||||
# Члены пула прописаны статически: их 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-порты.
|
||||
# Единственное место, где применяется MAC следующего перехода — и для
|
||||
# маршрутизируемого пути (после таблицы 20), и для коммутируемого (напрямую
|
||||
# из таблицы 12, без decrement TTL). Три источника записей, по приоритету:
|
||||
#
|
||||
# priority=100 члены пула — заливает и обновляет hcd (hc/neigh.go),
|
||||
# резолвя MAC настоящим ARP через hcif-порты. Пусто до
|
||||
# первого резолва после старта — fail-close ниже, окно то же,
|
||||
# что у таблицы слотов.
|
||||
# priority=90 клиенты (публичные и внутрисегментные) — действие learn из
|
||||
# таблиц 0 и 1: адрес заранее не известен, изучается пассивно.
|
||||
# priority=0 неизвестный next-hop — drop.
|
||||
#
|
||||
# Ответы на health-пробы — в стек узла через internal-порты; это собственные
|
||||
# адреса узла, а не next-hop, поэтому остаются статичными.
|
||||
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 и 1.
|
||||
table=21,priority=0 actions=drop
|
||||
|
||||
# =============================================================================
|
||||
|
||||
@@ -25,6 +25,12 @@ VIP_PORT=80
|
||||
# Адрес шлюза LB_P2_IP и VIP приватного листенера P2_VIP отвечают одним MAC
|
||||
# (P2_MAC) — по нему пайплайн отличает L3-трафик к маршрутизатору от
|
||||
# L2-трафика сегмента.
|
||||
#
|
||||
# BE*_MAC (шаг 5): это уже не источник правды для OpenFlow, а «заводской» MAC,
|
||||
# который attach-segment.sh назначает интерфейсу ВМ — эмуляция того, что в
|
||||
# реальности прошито в NIC сервера. В таблицу 21 этот MAC не подставляется:
|
||||
# его заново резолвит hcd через обычный ARP на hcif-порту (hc/neigh.go) и
|
||||
# заливает в датапас сам. pipeline.sh эти переменные больше не читает.
|
||||
P2_IFNAME=p2
|
||||
P2_OFPORT=2
|
||||
P2_MAC=02:42:0a:14:00:01
|
||||
|
||||
Reference in new issue
Block a user