diff --git a/VERSION b/VERSION index 51bdc7f..349813b 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -1.2.106 \ No newline at end of file +1.2.107 \ No newline at end of file diff --git a/internal/handlers/cluster.go b/internal/handlers/cluster.go index 3b24169..f58eca4 100644 --- a/internal/handlers/cluster.go +++ b/internal/handlers/cluster.go @@ -925,6 +925,14 @@ func (h *ClusterHandler) AgentRegisterPeer(c *gin.Context) { if req.PublicIP != "" { v := req.PublicIP n.PublicIP = &v + } else if ip := c.ClientIP(); ip != "" { + // Der Push-Payload (autoRegister, Primary→Secondary) trägt KEINE + // public_ip → sonst bliebe sie NULL und der Peer fehlt im nft- + // peer_ipv4-Set → VRRP-Adverts nur via conntrack → Flapping. Der + // pushende Peer verbindet sich über mTLS von seiner EIGEN-IP (nicht + // der VIP — der Kernel nimmt die primäre Interface-IP als Source), + // genau wie preRegisterJoiner die Joiner-IP übernimmt. Selbstheilend. + n.PublicIP = &ip } if req.InternalIP != "" { v := req.InternalIP diff --git a/internal/keepalived/keepalived.conf.tpl b/internal/keepalived/keepalived.conf.tpl index f01f7f7..9e0676f 100644 --- a/internal/keepalived/keepalived.conf.tpl +++ b/internal/keepalived/keepalived.conf.tpl @@ -44,7 +44,7 @@ vrrp_instance VI_1 { interface {{ .Interface }} virtual_router_id {{ .RouterID }} priority {{ .Priority }} - advert_int 1 + advert_int 2 nopreempt {{ if .SrcIP }} unicast_src_ip {{ .SrcIP }} unicast_peer { @@ -71,7 +71,7 @@ vrrp_instance VI_HB { interface {{ .HBInterface }} virtual_router_id {{ .HBRouterID }} priority {{ .Priority }} - advert_int 1 + advert_int 2 nopreempt {{ if .HBSrcIP }} unicast_src_ip {{ .HBSrcIP }} unicast_peer { diff --git a/internal/keepalived/keepalived_test.go b/internal/keepalived/keepalived_test.go index 70441a7..767cf74 100644 --- a/internal/keepalived/keepalived_test.go +++ b/internal/keepalived/keepalived_test.go @@ -54,6 +54,18 @@ func TestTemplateGARPRefresh(t *testing.T) { } } +// advert_int 2 (statt 1): Master-Down ~6s — reißt nicht bei kurzen +// VM-/Heartbeat-Hiccups (Flapping-Schutz im virtualisierten Cluster). +func TestTemplateAdvertInt(t *testing.T) { + out := render(t, testView()) + if strings.Contains(out, "advert_int 1\n") { + t.Fatalf("advert_int sollte 2 sein (nicht 1):\n%s", out) + } + if c := strings.Count(out, "advert_int 2"); c != 2 { + t.Fatalf("erwarte advert_int 2 in beiden Instanzen, gefunden: %d", c) + } +} + // Track-Scripts dürfen KEIN weight haben: in einer vrrp_sync_group ignoriert // keepalived gewichtete Scripts → Health-Checks wären wirkungslos. Ohne weight // wirken sie als FAULT-Trigger.