From 19b28f8f1cefb0457fdfd3ef90e5f8b94efc45fb Mon Sep 17 00:00:00 2001 From: Maysa Macedo Date: Tue, 11 Feb 2025 13:08:31 -0300 Subject: [PATCH] Enforce VIPs to be collocated at the same host When using dual-stack with OpenStack, both IPv4 and IPv6 share the same Neutron Port and this makes OVN thinks that both addresses are associated to the same Node, but that might not always be true as keepalived can put them in separate Nodes. To change that, let's make sure the API VIPs stays together through state changes, the same goes for Ingress VIPs. --- .../on-prem/files/keepalived-keepalived.yaml | 61 +++++++++++++++++++ .../on-prem/files/keepalived-keepalived.yaml | 34 +++++++++++ 2 files changed, 95 insertions(+) diff --git a/templates/master/00-master/on-prem/files/keepalived-keepalived.yaml b/templates/master/00-master/on-prem/files/keepalived-keepalived.yaml index 159321e593..e262e04b2d 100644 --- a/templates/master/00-master/on-prem/files/keepalived-keepalived.yaml +++ b/templates/master/00-master/on-prem/files/keepalived-keepalived.yaml @@ -69,6 +69,63 @@ contents: fall 2 } + # When using dual-stack with OpenStack, both IPv4 and IPv6 share the same Neutron Port, + # causing OVN to assume both addresses belong to the same node, which may not always be the case. + # To address this, we ensure that the API VIPs remain grouped through state changes, + # the same goes for Ingress VIPs. + {{- if and (eq .Infra.Status.PlatformStatus.Type "OpenStack") (or (eq .IPFamilies "DualStack") (eq .IPFamilies "DualStackIPv6Primary")) }} + vrrp_sync_group VG_API { + group { + {{`{{ range $i, $config := .Configs }}`}} + {{`{{$nonVirtualIP := .NonVirtualIP}}`}} + + {{`{{$participateInAPIVRRP := not .EnableUnicast}}`}} + {{`{{- if .EnableUnicast}} + {{- range .LBConfig.Backends}} + {{- if eq $nonVirtualIP .Address}} + {{$participateInAPIVRRP = true}} + {{- end}} + {{- end}} + {{- end}}`}} + + {{`{{if $participateInAPIVRRP}}`}} + {{`{{ .Cluster.Name }}`}}_API_{{`{{$i}}`}} + {{`{{ end }}`}} + {{`{{ end }}`}} + } + track_script { + chk_ocp_lb + chk_ocp_both + chk_mcs + } + } + + vrrp_sync_group VG_INGRESS { + group { + {{`{{ range $i, $config := .Configs }}`}} + {{`{{$nonVirtualIP := .NonVirtualIP}}`}} + {{`{{$participateInIngressVRRP := not .EnableUnicast}}`}} + {{`{{- if .EnableUnicast}} + {{- range .IngressConfig.Peers}} + {{- if eq $nonVirtualIP .}} + {{$participateInIngressVRRP = true}} + {{- end}} + {{- end}} + {{- end}}`}} + + {{`{{if $participateInIngressVRRP}}`}} + {{`{{ .Cluster.Name }}`}}_INGRESS_{{`{{$i}}`}} + {{`{{ end }}`}} + {{`{{ end }}`}} + } + track_script { + chk_ingress + chk_ingress_ready + chk_default_ingress + } + } + {{- end}} + {{`{{ range $i, $config := .Configs }}`}} {{`{{$nonVirtualIP := .NonVirtualIP}}`}} @@ -105,11 +162,13 @@ contents: virtual_ipaddress { {{`{{ .Cluster.APIVIP }}`}}/{{`{{ .Cluster.VIPNetmask }}`}} label vip } + {{- if not (and (eq .Infra.Status.PlatformStatus.Type "OpenStack") (or (eq .IPFamilies "DualStack") (eq .IPFamilies "DualStackIPv6Primary"))) }} track_script { chk_ocp_lb chk_ocp_both chk_mcs } + {{- end}} } {{`{{end}}`}} @@ -146,11 +205,13 @@ contents: virtual_ipaddress { {{`{{ .Cluster.IngressVIP }}`}}/{{`{{ .Cluster.VIPNetmask }}`}} label vip } + {{- if not (and (eq .Infra.Status.PlatformStatus.Type "OpenStack") (or (eq .IPFamilies "DualStack") (eq .IPFamilies "DualStackIPv6Primary"))) }} track_script { chk_ingress chk_ingress_ready chk_default_ingress } + {{- end}} } {{`{{ end }}`}} {{`{{ end }}`}} diff --git a/templates/worker/00-worker/on-prem/files/keepalived-keepalived.yaml b/templates/worker/00-worker/on-prem/files/keepalived-keepalived.yaml index 94912b10e9..ac1f04e01a 100644 --- a/templates/worker/00-worker/on-prem/files/keepalived-keepalived.yaml +++ b/templates/worker/00-worker/on-prem/files/keepalived-keepalived.yaml @@ -34,6 +34,38 @@ contents: fall 2 } + # When using dual-stack with OpenStack, both IPv4 and IPv6 share the same Neutron Port, + # causing OVN to assume both addresses belong to the same node, which may not always be the case. + # To address this, we ensure that the API VIPs remain grouped through state changes, + # the same goes for Ingress VIPs. + {{- if and (eq .Infra.Status.PlatformStatus.Type "OpenStack") (or (eq .IPFamilies "DualStack") (eq .IPFamilies "DualStackIPv6Primary")) }} + vrrp_sync_group VG_INGRESS { + group { + {{`{{ range $i, $config := .Configs }}`}} + {{`{{$nonVirtualIP := .NonVirtualIP}}`}} + + {{`{{$participateInIngressVRRP := not .EnableUnicast}}`}} + {{`{{- if .EnableUnicast}} + {{- range .IngressConfig.Peers}} + {{- if eq $nonVirtualIP .}} + {{$participateInIngressVRRP = true}} + {{- end}} + {{- end}} + {{- end}}`}} + + {{`{{if $participateInIngressVRRP}}`}} + {{`{{ .Cluster.Name }}`}}_INGRESS_{{`{{$i}}`}} + {{`{{ end }}`}} + {{`{{ end }}`}} + } + track_script { + chk_ingress + chk_ingress_ready + chk_default_ingress + } + } + {{- end}} + {{`{{ range $i, $config := .Configs }}`}} {{`{{$nonVirtualIP := .NonVirtualIP}}`}} @@ -70,11 +102,13 @@ contents: virtual_ipaddress { {{`{{ .Cluster.IngressVIP }}`}}/{{`{{ .Cluster.VIPNetmask }}`}} label vip } + {{- if not (and (eq .Infra.Status.PlatformStatus.Type "OpenStack") (or (eq .IPFamilies "DualStack") (eq .IPFamilies "DualStackIPv6Primary"))) }} track_script { chk_ingress chk_ingress_ready chk_default_ingress } + {{- end}} } {{`{{ end }}`}} {{`{{ end }}`}}