OCPBUGS-109633: on-prem: resign VIPs deterministically before node shutdown - #6402
OCPBUGS-109633: on-prem: resign VIPs deterministically before node shutdown#6402mkowalski wants to merge 1 commit into
Conversation
…utdown During 'systemctl reboot' systemd tears down all container scopes and the network stack in parallel. Whether keepalived manages to send its VRRP priority-0 resign advertisement and remove the VIPs from the interface before being killed is a race. When it loses, the VIP stays configured on the rebooting node while its kube-apiserver is still gracefully draining (and the local haproxy nftables redirect is already gone), so new client connections land directly on a not-ready apiserver for up to ~70 seconds, violating the LB /readyz contract. Add a keepalived-vip-resign.service oneshot unit whose ExecStop runs at shutdown before crio, kubelet and NetworkManager stop (stop order is the reverse of the After= start ordering). It sends SIGTERM to keepalived so the clean resign (priority-0 advert + VIP removal) happens while the network is still up and waits for the VIPs to disappear. If keepalived is not running or does not finish in time, it is killed and the VIPs are force-removed, so no stale VIP can survive into the drain window regardless of how the shutdown race resolves. The script only acts while the system is actually stopping, so a plain unit stop/restart on a healthy node (e.g. a MachineConfig rollout replacing the unit) does not flap the VIPs. The unit is rendered for on-prem platforms and enabled only when the cluster uses the OpenShift-managed load balancer and API or ingress VIPs are defined. Assisted-By: Claude Fable 5
|
Pipeline controller notification For optional jobs, comment This repository is configured in: LGTM mode |
|
Skipping CI for Draft Pull Request. |
|
@mkowalski: This pull request references Jira Issue OCPBUGS-109633, which is invalid:
Comment The bug has been updated to refer to the pull request using the external bug tracker. DetailsIn response to this:
Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the openshift-eng/jira-lifecycle-plugin repository. |
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Repository: openshift/coderabbit/.coderabbit.yaml Review profile: CHILL Plan: Enterprise Run ID: 📒 Files selected for processing (3)
WalkthroughThe change adds a keepalived VIP resignation script and systemd unit for on-premises load balancers. A table-driven test validates rendering across VIP, stack, ingress, and load-balancer configurations. ChangesKeepalived VIP resignation
Estimated code review effort: 3 (Moderate) | ~25 minutes Mergeability Score: ⚪ Minimal · up to This change adds deterministic VIP resignation during node shutdown, with no actionable merge-blocking risk remaining beyond normal checks and review. Sequence Diagram(s)sequenceDiagram
participant systemd
participant keepalived-vip-resign.sh
participant keepalived
systemd->>keepalived-vip-resign.sh: Run resignation on shutdown
keepalived-vip-resign.sh->>keepalived: Send SIGTERM
keepalived-->>keepalived-vip-resign.sh: Remove configured VIPs
keepalived-vip-resign.sh->>keepalived: Force cleanup if VIPs remain
🚥 Pre-merge checks | ✅ 15✅ Passed checks (15 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
Comment |
|
[APPROVALNOTIFIER] This PR is NOT APPROVED This pull-request has been approved by: mkowalski The full list of commands accepted by this bot can be found here. DetailsNeeds approval from an approver in each of these files:Approvers can indicate their approval by writing |
|
@mkowalski: This pull request references Jira Issue OCPBUGS-109633, which is valid. The bug has been moved to the POST state. 3 validation(s) were run on this bug
DetailsIn response to this:
Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the openshift-eng/jira-lifecycle-plugin repository. |
|
/jira refresh |
|
@mkowalski: This pull request references Jira Issue OCPBUGS-109633, which is valid. 3 validation(s) were run on this bug
DetailsIn response to this:
Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the openshift-eng/jira-lifecycle-plugin repository. |
|
Follow-up 6 from the review section is now implemented: openshift/baremetal-runtimecfg#397 teaches dynkeepalived to resign the VIPs on host shutdown (clean This comment was generated using AI. Please verify before acting on it. |
|
/hold Maybe openshift/baremetal-runtimecfg#397 is a better thing to do |
Fixes OCPBUGS-109633: during
systemctl reboot(e.g. an MCD-driven config-change reboot), keepalived non-deterministically loses the race against systemd's parallel teardown of container scopes and networking. When it loses, it is killed before sending the VRRP priority-0 resign advert and before removing the VIP from the interface. The stale VIP then keeps attracting new client connections which land directly on the local kube-apiserver — which is still serving its ~70s graceful drain withreadyz=false— because the local haproxyVIP:6443→:9445redirect is already gone. This is what the[Monitor:audit-log-analyzer] API LBs follow /readyz ...regression (Component Readiness regression 46508, triage 722) caught on vSphere upgrade jobs.What this PR does
Adds a
keepalived-vip-resign.serviceoneshot unit (rendered for on-prem platforms, enabled only with the OpenShift-managed LB and configured VIPs) whoseExecStopruns at shutdown before crio, kubelet and NetworkManager stop. The script:pgrep -ox— an unanchored match would hitdynkeepalivedor the script itself) so the clean resign (priority-0 advert + VIP removal) happens while the network is up, and waits up to 10s.pkill -9 -x, so it cannot re-add addresses) and remove the VIPs directly viaip addr del. VIP presence is checked withip addr show to <vip>, i.e. kernel-side address comparison, so non-canonical IPv6 spellings still match.The clean SIGTERM handoff is best-effort (transient
crio-*.scopeunits are not ordered against this unit); the hard guarantee that no stale VIP survives into the drain window comes from the fallback.Evidence from the failing runs
In job run 2086273547025518592: MCD reboots master-1 at 03:46:56; master-0 keepalived takes MASTER at 03:47:00 via master-down timeout (no
Backup received priority 0 advertisement— contrast the clean handoff at 03:41:05 in the same run); peers' haproxy correctly marks master-1 DOWN at 03:47:11; yet flagged requests keep terminating on master-1's draining apiserver until 03:48:06 — only possible via a stale VIP on master-1. Same signature in runs 2086092373405732864 and 2085772778182545408.Testing
go test ./pkg/controller/template/...— includes a new table test covering unit enablement and rendered VIP list for: no VIPs, single stack, dual stack, ingress-only VIPs, and user-managed LB (disabled).ip/pgrep/systemctlharness for: healthy-node stop (no-op), shutdown with dead keepalived (immediate force-removal, no wait), no VIP (no-op), deep-shutdown with systemctl unavailable (proceeds).systemctl reboot~20x and confirm peers logBackup received priority 0 advertisement(or take over within ~4s) andip addron the rebooting node never retains the VIP into the apiserver drain; then rerunperiodic-ci-openshift-release-main-ci-5.0-e2e-vsphere-ovn-upgrade.Note for reviewers: this adds a new MachineConfig unit+file, so it rolls out with a drain+reboot of on-prem nodes, as usual for template changes.
Suggested follow-ups (from panel review, non-blocking)
These were raised during a multi-specialist review of this change and are deliberately left out of this PR to keep it scoped; filing them here so they are not lost:
PlatformStatusVIP strings into a root-executed shell script. The trust boundary is intact today (cluster-admin-controlled, API-validated fields), but anet.ParseIPcheck in the render path (or a template helper) would make the pattern fail closed if the source fields or their validation ever change.pgrep -ox keepalivedis an exact-comm match, but name-based targeting is still spoofable by a local process namedkeepalivedand has a theoretical pgrep→kill PID-reuse window. Resolving the PID via the static pod (crictl/conmon pidfile) would close both. Impact today is degraded-cleanliness only: the force-removal fallback still removes the VIPs.controller_config_{vsphere,openstack,nutanix,ovirt}.yaml(incl. the vSphere-UPI nil-PlatformStatus.VSpherecase) would exercise everyonPremPlatformAPIServerInternalIPsswitch arm. Notecontroller_config_ovirt.yamlexists in test_data but is absent from theconfigsmap inrender_test.go, so the ovirt render path is currently untested by any unit test.hack/verify-templates.shdoes not lint the bash embedded in template yamls; a small extraction+shellcheck step would catch quoting/set -uregressions in this and sibling scripts (resolv-prepender.sh,mtu-migration.sh, ...).periodic-ci-openshift-release-main-ci-5.0-e2e-vsphere-ovn-upgrademonitor test is the effective signal.This PR was generated using AI. Please verify before acting on it.
Assisted-By: Claude Fable 5
Summary by CodeRabbit