Repository navigation
fix: leader election 밖에서 쓰던 주기 작업을 리더로 좁힌다 (AIP-3458) - #127
Merged
Merged
Conversation
AIP-3439 는 "쓰기를 하는 건 워크큐뿐" 이라는 전제로 ControllerManager 만 리더에 묶었다. 틀렸다. 스스로 스케줄을 잡고 도는 작업이 따로 있고 그중 둘이 객체를 패치한다. replicas=2 로 올렸을 때 비리더 파드 로그에 [USER-LABEL-SYNC] 가 찍힌 것이 증거다. LeadershipState(volatile boolean) 를 두고 리스를 잡을 때 한 번 세운다. 리스를 잃으면 프로세스가 끝나므로 값은 한 방향으로만 바뀐다 — 강등 처리, 스케줄러 재기동, 경쟁 조건이 전부 불필요하다. 리더로 좁힌 것: - UserLabelSynchronizer / ClusterVolumeChildLabelSynchronizer — 전량 스캔 후 patch - OwnedObjectRoleResweeper — 워크큐 재큐잉. 비리더에서는 꺼내는 쪽이 없어 쌓이기만 한다 ⛔ 그대로 둔 것 — ApiResourceDiscoveryRefresher 는 파드별 CRD 캐시를 갱신한다. 리더로 묶으면 리더가 아닌 파드의 캐시가 낡아 어드미션 웹훅이 틀린 답을 한다. AIP-3439 에서 인포머를 리더 밖에 둬야 했던 것과 같은 이유다. 회귀 가드가 본체다. 이 문제는 레플리카가 하나인 동안 아무 증상이 없어 리뷰로는 반복해서 놓친다. PeriodicWorkLeadershipCoverageTest 가 소스에서 @scheduled·scheduleWithFixedDelay·scheduleAtFixedRate 사용처를 찾아 분류 목록과 대조하므로, 새로 추가하면 깨지고 작성자가 "리더만인가 파드마다인가" 를 판단하게 된다. 반대 방향(과하게 묶기)도 같이 고정했다. 돌연변이 4회로 가드를 확인했다: - ULS 가드 제거 → 행위 테스트 FAIL - CV 가드 제거 → 행위 테스트 FAIL - 새 @scheduled 클래스 추가 → 분류 가드 FAIL - 캐시 갱신기에 isLeader() 추가 → 과잉 게이팅 가드 FAIL⚠️ ULS 의 첫 검증은 verifyNoInteractions(discovery) 였는데 가드를 지워도 통과했다(그 전에 예외로 빠진다). LIST(buildCall GET) 검증으로 바꿔 돌연변이가 실제로 잡히는 것을 확인했다. 기존 동기화기 테스트는 픽스처를 리더로 세웠다 — 아니면 sync() 가 즉시 반환해 테스트 전부가 조용히 무의미해진다. 검증: ./gradlew clean test — 302 tests, 0 failed, 0 skipped
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Why
AIP-3439(leader election)은 "쓰기를 하는 건 워크큐뿐" 이라는 전제로
ControllerManager만 리더에 묶었습니다. 그 전제가 틀렸습니다.스스로 스케줄을 잡고 도는 작업이 따로 있고, 그중 둘이 클러스터 오브젝트를 패치합니다.
replicas=2로 올려 AIP-3439 를 검증하던 중 비리더 파드 로그에[USER-LABEL-SYNC]가 찍힌 것이 증거입니다.손상이 나는 종류는 아닙니다(같은 라벨을 양쪽이 계산해 patch). 다만 레플리카 수만큼 중복 쓰기가 되고, 설계 의도와 어긋납니다.
🟡 지금 당장 터지지는 않습니다 —
replicas=1이라 비리더가 없습니다.replicas=2로 가기 위한 선행 조건 중 하나입니다(나머지: 프로브 AIP-3457, PDB+anti-affinity).What
LeadershipState신설volatile boolean하나입니다. 리스를 잡을 때LeaderElectionRunner가 한 번 세웁니다.⭐ 되돌리는 메서드가 없습니다. 리스를 잃으면 프로세스가 끝나므로(AIP-3439 결정) 값이 한 방향으로만 바뀝니다. 덕분에 강등 처리·스케줄러 재기동·경쟁 조건이 전부 불필요합니다.
리더로 좁힌 것
UserLabelSynchronizermerge-patchClusterVolumeChildLabelSynchronizerpatchChildLabelsOwnedObjectRoleResweeper생성자의 self-start 는 그대로 뒀습니다. 스케줄러 스레드는 비리더에서도 돌지만 진입부에서 즉시 반환합니다(주기가 분 단위라 비용 무시 가능). 빈 생성 계약을 바꾸지 않아 변경이 작습니다.
⛔ 일부러 그대로 둔 것
ApiResourceDiscoveryRefresher는 파드별 CRD 캐시를 갱신합니다. 리더로 묶으면 리더가 아닌 파드의 캐시가 낡아 어드미션 웹훅이 틀린 답을 하거나 요청을 거부합니다. AIP-3439 에서 인포머를 리더 밖에 둬야 했던 것과 같은 이유입니다."안전하게 전부 리더로 묶자" 가 이 변경에서 가장 위험한 실수라, 그 방향도 테스트로 막았습니다.
동기화기들은
initialDelay=0이라 리스를 잡기 전에 첫 주기가 돕니다. 클러스터 실측에서 그대로 관측됐습니다.둘 다 백스톱 수렴 루프이고 주기가 60초라 다음 주기에 복구되므로 이번 PR 에서는 고치지 않았습니다. 영향은 "기동 직후 최대 한 주기만큼 수렴이 늦다" 입니다. 줄여야 한다고 보시면
initialDelay를 리스 획득 이후로 미루는 쪽으로 별도 처리하겠습니다.🔍 리뷰 포인트
PeriodicWorkLeadershipCoverageTest가 소스에서@Scheduled·scheduleWithFixedDelay·scheduleAtFixedRate사용처를 찾아 분류 목록과 대조하므로, 새로 추가하면 깨지고 작성자가 "리더만인가 파드마다인가" 를 판단하게 됩니다sync()가 즉시 반환해 기존 테스트 전부가 조용히 무의미해집니다LeadershipState.markLeader()를 public 으로 뒀습니다(테스트가 다른 패키지에 있어서). 호출 주체를 javadoc 에 못박았습니다 — 더 좁히는 편이 낫다고 보시면 의견 주세요돌연변이로 가드를 확인했습니다
UserLabelSynchronizer가드 제거ClusterVolumeChildLabelSynchronizer가드 제거@Scheduled클래스 추가isLeader()추가UserLabelSynchronizer테스트를 처음에verifyNoInteractions(discovery)로 썼는데, 가드를 지워도 통과했습니다(그 전에 예외로 빠집니다). LIST(buildCallGET) 검증으로 바꿔 돌연변이가 실제로 잡히는 것을 확인했습니다.Test plan
./gradlew clean test— 302 tests, 0 failed, 0 skipped1.10.0-33)를 올려replicas=2에서 실측 — 아래 결과replicas=1로 되돌리고 임시 로그 레벨 제거클러스터 검증 결과 (cluster10, 2026-10-09)
리더
…-4pgw2(vnode2.pnode3) / 비리더…-dlwrl(vnode6.pnode10).① 리더 전용 가드가 실제로 갈랐다
[USER-LABEL-SYNC][CV-CHILD-LABEL-SYNC][CV-CHILD-LABEL-SYNC]가 안 찍히는지" 항목은 그대로는 무의미했습니다. 이 동기화기는 기본 INFO 레벨에서 아무것도 찍지 않기 때문에, 가드가 없어도 로그는 비어 있습니다.LOGGING_LEVEL_…_MUTATING_SERVICE=DEBUG를 걸어 skip 로그가 찍히는 것(= 가드를 거쳐 돌아 나갔다) 을 양성 신호로 삼아야 판정이 됩니다.② 과잉 게이팅은 없다 (반대 방향 확인)
비리더에서
ApiResourceDiscovery관련 로그 103줄 — 캐시 갱신기는 계속 돕니다.not the leader로그는 전체 4줄뿐이라 가드가 의도한 둘에만 붙은 것이 확인됩니다.③ 비리더의 어드미션 웹훅 정상
port-forward pod/…-dlwrl로 비리더에 직접 쏴서 3경로 전부 HTTP 200 ·allowed=true· patch 반환:/api/v1/admissionreviews·/api/v1/workloadlabel/mutate·/api/v1/userrelationship/mutate④ 파드 생성 영향 없음 — dry-run 생성 12/12 통과, 양쪽 파드 ERROR 0건.
🚀 Release Version
v5.2.0