traefik: migrate to bgp-pool (10.2.99.86 / fd99::86)

Traefik's LoadBalancer moves from the default-pool (10.2.40.86 / fd40::86,
L2-announced) to the BGP-routed off-link pool. Every hostname behind
traefik — every *.home.renner.uno, vscode.home.renner.uno, the cluster
status page, paperless, immich, etc. — now reaches the ingress via the
UDM's BGP-learned routes instead of L2 ARP on the mgmt LAN.

This is the lever that takes vscode "fully working via the routed
network" — the TLS + DNS access path goes through the same BGP route
as the direct bgp-pool LB IPs. Same migration covers every other
traefik-fronted service in one step.

Companion updates: explicit `external-dns.alpha.kubernetes.io/target`
annotations on the pve and test-browser IngressRoutes (Traefik doesn't
populate IngressRoute.status.loadBalancer, so the target is hardcoded
and has to track the LB IP); the bootstrap.sh / e2e-test.sh smoke-test
IPs; a stale code comment in cilium-l2.yaml.

The label/annotation pattern matches the ha-vscode migration: chart's
`service.labels` sets `lb-pool: bgp` (single Service handles both TCP
and UDP for HTTP/3, so one label is enough), and the
`io.cilium/lb-ipam-ips` annotation pins the IPs deterministically.

external-dns publishes the new IPs to Cloudflare + UniFi DNS providers
on its next 60s reconcile cycle after the Service IP flips.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-05-19 22:05:11 +02:00
co-authored by Claude Opus 4.7
parent d96027fbc8
commit 6fe33cbb96
6 changed files with 15 additions and 8 deletions
+1 -1
View File
@@ -32,7 +32,7 @@ metadata:
# external-dns-unifi (traefik-proxy source) has no implicit target.
# Pin to the traefik Service VIP (kubernetes/infrastructure/traefik
# service.annotations io.cilium/lb-ipam-ips).
external-dns.alpha.kubernetes.io/target: "10.2.40.86,fd40::86"
external-dns.alpha.kubernetes.io/target: "10.2.99.86,fd99::86"
spec:
entryPoints:
- websecure
@@ -30,7 +30,7 @@ metadata:
annotations:
# See pve/ingressroute.yaml — IngressRoute status isn't populated
# by Traefik, so target the traefik Service VIP explicitly.
external-dns.alpha.kubernetes.io/target: "10.2.40.86,fd40::86"
external-dns.alpha.kubernetes.io/target: "10.2.99.86,fd99::86"
spec:
entryPoints:
- websecure
+1 -1
View File
@@ -352,6 +352,6 @@ if phase_runs wait-apps; then
echo ""
echo -n "Ingress (local): "
curl -sk --connect-timeout 5 --resolve status.renner.uno:443:10.2.40.86 https://status.renner.uno -o /dev/null -w "%{http_code}" 2>/dev/null || echo "N/A"
curl -sk --connect-timeout 5 --resolve status.renner.uno:443:10.2.99.86 https://status.renner.uno -o /dev/null -w "%{http_code}" 2>/dev/null || echo "N/A"
echo ""
fi
+2 -2
View File
@@ -3,10 +3,10 @@
# Exits non-zero if any check fails. Prints a summary at the end.
#
# Assumes kubeconfig already loaded at ~/.kube/config and the runner can
# reach the ingress LB IP (10.2.40.86) and the cluster API endpoint.
# reach the ingress LB IP (10.2.99.86) and the cluster API endpoint.
set -uo pipefail
LB_IP="10.2.40.86"
LB_IP="10.2.99.86"
PASS=0
FAIL=0
@@ -94,7 +94,7 @@ spec:
#
# IPv4 + IPv6 ranges in this pool keep matching last bytes (10.2.99.x ↔
# fd99::x) so an LB has the same "tail" in both families, matching the
# pattern the existing default-pool already follows (10.2.40.86 / fd40::86).
# pattern the existing default-pool already follows.
apiVersion: cilium.io/v2alpha1
kind: CiliumLoadBalancerIPPool
metadata:
@@ -92,8 +92,15 @@ spec:
ipFamilies:
- IPv4
- IPv6
# `lb-pool: bgp` selects the off-link BGP-routed pool (10.2.99.0/24
# + fd99::/64). Cilium advertises this LB to the UDM via BGP and
# clients reach it via the UDM's BGP-learned routes — no L2 announce,
# no on-link ARP, no GARP race. Single Service handles both TCP
# (HTTP/HTTPS) and UDP (HTTP/3) so one label covers everything.
labels:
lb-pool: bgp
annotations:
io.cilium/lb-ipam-ips: "10.2.40.86,fd40::86"
io.cilium/lb-ipam-ips: "10.2.99.86,fd99::86"
# HTTP/3 adds a UDP port on the entrypoint; the chart emits a
# separate UDP Service when any UDP port is exposed. Sharing-key
# tells Cilium LB-IPAM to put the TCP and UDP Services on the
@@ -101,7 +108,7 @@ spec:
# UDP Service joins the same allocation deterministically.
io.cilium/lb-ipam-sharing-key: "traefik-vip"
annotationsUDP:
io.cilium/lb-ipam-ips: "10.2.40.86,fd40::86"
io.cilium/lb-ipam-ips: "10.2.99.86,fd99::86"
io.cilium/lb-ipam-sharing-key: "traefik-vip"
metrics:
prometheus: