TT Lab
블로그

한국어

시작하기
블로그

블로그

아무도 쓰지 않는 웹훅을 6초마다 찾던 operator: KubeVirt 본체 PR 이 2일 16시간 만에 병합되기까지

결과: 병합 (2026-10-20 16:05, kubevirt/kubevirt#19499). 2026-10-29 기준으로 이 변경을 담은 릴리스는 아직 없습니다. 사실은 GitHub 의 PR·리뷰·줄 댓글·타임라인·검사 기록, KubeVirt 소스(PR 의 바탕 커밋 580886af), 그리고 기여 원장의 실측 원자료(operator 지표 원값, reconcile 간격)와 초안에서 가져왔습니다. 시각은 한국 시간입니다.

목차

한 줄 요약

PR kubevirt/kubevirt#19499 virt-operator: stop GETting the disabled ContainerPath webhook on every reconcile
바꾼 것 세 파일. 시작 전 동기화 목록에 한 줄 추가, 직접 GET 하던 12줄 삭제, 기존 단위 시험 확장. 10줄 추가, 13줄 삭제
반응 리뷰어가 주석 하나를 빼자는 줄 댓글을 남기고 승인, 그 코드를 처음 넣은 approver 가 /approve
결과 PR 공개 10-17 23:37 → 리뷰 10-19 16:54 → 승인 22:23 → 병합 10-20 16:05, 약 2일 16시간 27분. 검사 42개 통과
꺼진 웹훅을 reconcile 마다 찾던 길 KubeVirt v1.9.0 리더 virt-operator, 59일 누적 카운터(실측) ① 고치기 전: 게이트가 꺼져 있으면(기본값) reconcile 인포머 캐시 조회 없음 → 직접 GET API 서버 404 약 6초마다 한 번 (중앙 6.055초, 236구간) GET mutatingwebhookconfigurations 404 843,995 LIST nodes(reconcile 마다 한 번, 비교용) 843,995 operator 전체 요청 15,509,446 중 404 가 5.4% ② 고친 뒤(#19499) 시작 전에 웹훅 인포머 동기화 캐시만 믿고 조회 없음 → 요청 없음 있음 → 지움 게이트를 켰다 끈 경우 웹훅은 캐시에 보이므로 예전처럼 지운다
그림: 꺼진 웹훅을 reconcile 마다 찾던 길과 고친 뒤

1. 무엇이 문제였나: 59일 동안 843,995번의 404

TT Lab 은 수강생 실습 VM 을 KubeVirt 로 띄웁니다. 2026-10-17 에 TT Lab 클러스터의 KubeVirt 구성 요소를 읽기 명령만으로 훑으면서 (기여 원장의 measure/kv), 리더 virt-operator 가 내보내는 kubevirt_rest_client_requests_total 지표를 그대로 받아 두었습니다. 판은 KubeVirt v1.9.0, 쿠버네티스 v1.34.10 이고, 그 operator 는 59일째 떠 있었습니다. 카운터는 프로세스가 뜬 뒤로 쌓인 값입니다.

요청 59일 누적
GET mutatingwebhookconfigurations → 404 843,995
LIST nodes → 200 (reconcile 마다 한 번, 비교용) 843,995
operator 의 모든 REST 요청 15,509,446
2xx 가 아닌 operator 요청 843,999

404 를 받는 GET 의 수가 reconcile 마다 한 번 하는 LIST nodes 와 한 자리까지 같았습니다. reconcile 한 번에 404 가 정확히 한 번 난다는 뜻입니다. 이 404 는 operator 전체 요청의 5.4%(843,995 ÷ 15,509,446)이고, 2xx 가 아닌 응답 843,999개 중 넷을 뺀 전부입니다.

같은 날 리더 operator 로그의 「Handling KubeVirt resource」 237줄을 세어 보니 reconcile 간격은 최소 6.007초, 중앙 6.055초, 최대 6.088초(236구간)였습니다. 59일을 6.055초로 나누면 약 84만 2천 번이라, 카운터와 크게 어긋나지 않습니다(계산). reconcile 이 6초마다 도는 것 자체는 Lease 갱신 때문에 생기는 별도 문제로 이미 이슈(#19342)와 수정 PR(#19417, 2026-10-29 기준 열려 있음)이 있었습니다. 우리가 본 것은 그 위에 얹힌 다른 문제였습니다. reconcile 이 몇 초마다 돌든, 이 GET 은 처음부터 할 필요가 없는 요청이었습니다.

눈에 띄는 피해는 없었습니다. 그래도 PR 본문에 적은 대로, 이 404 들은 VirtOperatorRESTErrorsBurst 경보가 보는 오류 비율 밑에 늘 5% 정도의 바닥을 깔아 둡니다. 경보 문턱(80%)에는 한참 못 미쳐 경보는 울리지 않지만, 대시보드에서 진짜 오류를 가립니다.

2. 원인: 소스의 어느 줄인가

줄 번호는 PR 의 바탕 커밋 580886af 기준입니다.

그러면 「캐시가 안 찼을 수도 있다」는 걱정은 맞는 걱정일까요. controller 는 일을 시작하기 전에 여러 인포머의 동기화를 기다립니다. kubevirt.go 169–194행 의 hasSynced 목록입니다. 여기에 검증 웹훅 인포머(180행 ValidationWebhook)는 있는데 변형 웹훅 인포머(MutatingWebhook)만 빠져 있었습니다. 그러니 캐시를 믿지 못한 것에는 이유가 있었고, 고칠 곳은 GET 이 아니라 동기화 목록으로 보였습니다.

이 직접 GET 은 웹훅과 함께 #16662(VEP 165, 2026-02-24 병합)에서 들어왔습니다.

3. 무엇을 바꿨나: diff 요지

 # pkg/virt-operator/kubevirt.go: 시작 전에 기다리는 인포머 목록
 			informers.ValidationWebhook.HasSynced() &&
+			informers.MutatingWebhook.HasSynced() &&

 # pkg/virt-operator/resource/apply/admissionregistration.go
-	// Feature gate is disabled, delete the webhook if it exists
+	// Feature gate is disabled, delete the webhook if it exists.
 	_, exists, _ := r.stores.MutatingWebhookCache.Get(webhook)
-	if !exists {
-		// Also check directly in case cache is not populated
-		_, err := r.k8sClient.AdmissionregistrationV1().MutatingWebhookConfigurations().Get(
-			context.Background(), webhook.Name, metav1.GetOptions{})
-		... (404 면 nil, 그 밖의 오류면 반환, 있으면 exists = true)
-	}

캐시가 믿을 만해지면 캐시에 없다는 것은 지울 것이 없다는 뜻이므로 API 요청을 하지 않습니다. 게이트를 켰다가 끈 경우에는 operator 가 만든 웹훅이 늘 인포머가 고르는 managed-by 라벨을 달고 있으므로 캐시에 보이고, 예전처럼 지워집니다. PR 본문에는 이 선택의 대가도 적었습니다. 누가 손으로 그 라벨을 떼면 operator 는 웹훅을 못 보고 지우지 않습니다. operator 가 라벨로 고르는 다른 객체들도 같은 식으로 다룹니다. 「첫 동기화 전까지만 GET 을 남기는」 안도 생각했지만, 상태를 하나 더 들고 다녀야 해서 한 줄짜리 동기화 추가를 골랐습니다.

시험은 기존 단위 시험 「should not create virt-launcher-pod-mutator webhook when disabled」 을 넓혔습니다. 가짜 클라이언트에 이 웹훅 이름으로 get 이 한 번이라도 기록되면 실패하게 했습니다. PR 본문에 적은 결과는 이렇습니다(Go 1.27.2, linux/amd64, GOFLAGS=-mod=vendor, main 6e80bca).

시험 결과
시험만 바꾸고 고치지 않은 상태, 「ContainerPathVolumes feature gate」 묶음 3개 중 1개 실패
고친 뒤 같은 묶음 통과
go test ./pkg/virt-operator/... 통과

한 번은 ./pkg/virt-operator/resource/apply/ 의 TestInstallStrategy 가 실패했는데, 고친 판과 안 고친 판 모두에서 다시 돌리면 재현되지 않았다는 것도 본문에 그대로 적었습니다. 이 출력의 원본 파일은 원장에 따로 남기지 않았습니다. 고친 operator 를 TT Lab 클러스터에 올려 404 가 사라지는 것까지 재 보지는 않았습니다(미실측). TT Lab 클러스터는 아직 v1.9.0 이고, 이 변경을 담은 릴리스가 없기 때문입니다.

AI 사용은 이렇게 밝혔습니다. KubeVirt 의 AI 기여 정책은 AI 도구를 썼으면 커밋 꼬리말(Assisted-by:·Co-authored-by:·Generated-by:)로 밝히기를 권하고, DCO 서명은 그대로 요구합니다. 커밋에는 Assisted-by: Claude 와 Signed-off-by 를 붙였고, PR 템플릿의 「AI Contributions」 칸에 표시한 뒤 본문 끝에 「지표에서 원인을 추적하고 변경과 시험의 초안을 쓰는 데 Claude 의 도움을 받았고, 모든 줄을 검토했으며 위 숫자는 내 클러스터에서 쟀다」는 문단을 넣었습니다. 한 가지는 원칙대로 되지 않았습니다. 리뷰를 반영해 다시 만든 커밋 47a37b0a 는 GitHub 에서 서명되지 않은 커밋(unsigned)으로 보입니다. 원칙 3 의 SSH 서명이 다시 쓴 커밋에서 빠진 것으로 보입니다.

4. 반응: 줄 댓글 하나, 승인 둘

시각 일
10-17 23:37 PR 공개. prow 가 size/S·area/operator·sig/control-plane 라벨, 리뷰어 둘 지정
10-17 23:38 CodeRabbit 은 「이 저장소는 자동 리뷰가 꺼져 있다」며 건너뜀. prow 는 「신뢰되지 않은 작성자는 /ok-to-test 로 시험을 못 연다, 협업자가 /test all 을 쳐야 한다」고 안내
10-19 16:52 리뷰어의 줄 댓글. 16:54 리뷰에서 /test all 과 함께 approver 한 명을 /cc
10-19 22:03 주석을 고친 커밋으로 다시 push, 22:13 줄 댓글에 답
10-19 22:23 리뷰어 승인, lgtm 라벨
10-19 23:27 approver 가 /approve, 봇이 2단계 e2e 8개(sig-network·storage·compute·operator, Windows, vGPU, IPv6)를 돌림
10-20 16:05 병합

리뷰의 내용은 줄 댓글 하나였습니다. 처음 올린 판에는 지운 GET 자리에 다섯 줄짜리 주석이 있었습니다. 「controller 는 웹훅 인포머의 동기화를 기다리고, 웹훅은 늘 라벨을 달고 있으므로 캐시에 없으면 지울 것이 없다, 여기서 API 서버에 물으면 게이트를 안 쓰는 모든 클러스터에서 reconcile 마다 404 GET 이 난다」는 설명이었습니다. 리뷰어는 이것이 그렇게 큰 문제였는지 모르겠다고 했습니다. 웹훅이 생기거나 사라지면 이벤트 처리기가 알아채고 다시 큐에 넣을 테니, 주석은 빼도 되겠다는 의견이었습니다.

맞는 말이었습니다. 동기화 전의 짧은 틈에 캐시가 비어 있더라도, 웹훅이 나타나는 순간 인포머가 다시 reconcile 을 부릅니다. 주석이 이 GET 의 쓸모를 과장하고 있었던 셈입니다. 주석 다섯 줄을 지우고 커밋을 고쳐 다시 올린 뒤, 「인포머가 추가·삭제 때 어차피 다시 큐에 넣으니 주석이 부풀려 말했다, 뺐다」고 짧게 답했습니다. 그 결과 PR 의 코드는 한 줄 추가와 GET 블록 삭제만 남았습니다.

approver 는 virt-operator 디렉터리의 OWNERS 에 있는 사람이고, 이 직접 GET 을 넣은 #16662 의 작성자이기도 합니다. 남긴 말은 /approve 와 감사 한 줄이었습니다. 2단계 e2e 를 포함한 검사 42개가 모두 통과했고, 다음 날 오후 tide 가 병합했습니다. 원장에는 이 PR 이 KubeVirt 를 주력으로 정한 뒤 본체 저장소에 들어간 첫 병합으로 적혀 있습니다.

5. 배운 것: 오픈소스 기여 요령

티티 한마디. 문지기 아저씨가 6초마다 창고 문을 두드리며 「여기 상자 있어요?」 하고 물었대요. 창고에는 처음부터 그 상자가 없었는데 말이죠. 수첩을 보면 되는데, 수첩 목록에 그 창고만 빠져 있어서 못 믿었던 거예요. 수첩에 한 줄을 적어 드렸더니 두 달 동안 84만 번 넘게 두드리던 일이 사라졌어요. 「노크가 왜 필요했는지」 길게 써 붙인 쪽지는 「그건 빼도 돼요」 하셔서 떼었답니다.

링크

모든 자료는 2026-10-29 에 열람했습니다.

로그인하면 좋아요를 누를 수 있습니다

댓글

아직 댓글이 없습니다.

로그인하면 댓글을 쓸 수 있습니다