결과: 병합 (2026-10-26 19:58, kubevirt/containerized-data-importer#4319), 이슈 #3005 닫힘. 2026-10-29 기준 이 변경을 담은 CDI 릴리스는 아직 없습니다(최신은 2026-09-06 의 v1.66.1). 사실은 GitHub 의 PR·리뷰·줄 댓글·본문 수정 이력·타임라인·검사 기록, CDI 소스(PR 의 바탕 커밋
9cd4fbdb), local-path-provisioner 소스(태그 v0.0.32), 기여 원장의 실측 원자료(StorageProfile 목록, 48시간 로그 집계)와 초안에서 가져왔습니다. 시각은 한국 시간입니다.
목차
- 한 줄 요약
- 1. 무엇이 문제였나: 비어 있는 StorageProfile
- 2. 원인: 소스의 어느 줄인가
- 3. 무엇을 바꿨나: 세 번에 걸친 diff
- 4. 반응: 묶자, 빼자, 그리고 다시 열린 이슈
- 5. 배운 것: 오픈소스 기여 요령
- 링크
한 줄 요약
| PR | kubevirt/containerized-data-importer#4319 Add storage capabilities for rancher.io/local-path |
| 바꾼 것 | storagecapabilities.go 한 파일. rancher.io/local-path 항목과, 노드 로컬 경로 프로비저너가 함께 쓰는 도우미 하나. 12줄 추가, 4줄 삭제 |
| 반응 | 리뷰어 둘의 줄 댓글 하나씩(묶자, 시험 둘은 빼자), 메인테이너가 옛 이슈를 다시 열어 연결 |
| 결과 | PR 공개 10-17 23:38 → /approve 10-19 16:59 → /lgtm 10-26 15:15 → 병합 19:58, 약 8일 20시간 20분. 커밋 셋, 검사 21개 통과 |
1. 무엇이 문제였나: 비어 있는 StorageProfile
CDI(Containerized Data Importer)는 KubeVirt VM 의 디스크를 PVC 로 만들어 주는 구성 요소입니다. DataVolume 을 spec.storage 로 쓰면
접근 모드나 볼륨 모드를 적지 않아도 되는데, 그 기본값을 StorageClass 마다 만든 StorageProfile 에서 가져옵니다. StorageProfile 의 기본값은
CDI 가 프로비저너 이름을 보고 채웁니다.
2026-10-17 에 TT Lab 클러스터(CDI v1.66.0)를 읽기 명령으로 훑었을 때 StorageProfile 넷 중 둘이 비어 있었습니다(원장 measure/kv).
| StorageProfile | 프로비저너 | claimPropertySets | Recognized |
|---|---|---|---|
| local-path | rancher.io/local-path |
없음 | UnrecognizedProvisioner |
| local-path-retain | rancher.io/local-path |
없음 | UnrecognizedProvisioner |
| NFS 클래스 둘 | nfs.csi.k8s.io |
RWX, Filesystem | RecognizedProvisioner |
두 local-path 클래스는 57일 전에 만들었고 프로비저너 이미지는 v0.0.32 였습니다. cdi-deployment 의 48시간 로그에는 두 클래스마다
Set metric:local-path complete:false … degraded:true 같은 줄이 열 번씩 있었습니다. PR 본문에 적은 대로 이 지표는 CDIStorageProfilesIncomplete
경보가 보는 값입니다.
이 상태에서 spec.storage 로 접근 모드를 비운 DataVolume 을 만들면 ErrClaimNotValid: no accessMode specified in StorageProfile local-path
로 멈춘다고 PR 본문에 적었습니다. 이것은 초안에 「사용자가 돌려 볼 명령과 기대 결과」로 적어 둔 것이고, 원장에는 실제로 돌린 기록이 없습니다(미실측).
원장에는 이 오류로 TT Lab 의 VM 이 멈췄다는 기록은 없습니다. 운영 사고가 아니라 조사 중에 찾은 문제입니다.
2. 원인: 소스의 어느 줄인가
CDI 줄 번호는 PR 의 바탕 커밋 9cd4fbdb 기준입니다.
- 프로비저너 이름과 기본값의 표는
storagecapabilities.go39행부터 의CapabilitiesByProvisionerKey입니다. hostpath-provisioner 와k8s.io/minikube-hostpath는{{rwo, file}}로 있는데,rancher.io/local-path는 없었습니다. - 표에 없으면
storageprofile-controller.go206–220행 의reconcileConditions가 Recognized 를False, 사유를UnrecognizedProvisioner(메시지 「Provisioner is not recognized」)로 둡니다. 1절의 로그 줄은 같은 파일 462행 에서 찍힙니다.
넣을 값이 {{rwo, file}} 인 근거는 프로비저너 쪽 소스입니다(local-path-provisioner v0.0.32 provisioner.go).
- 345행: 노드 경로 볼륨에 RWO·RWOP 가 아닌 접근 모드를
요청하면
NodePath only supports ReadWriteOnce and ReadWriteOncePod (1.22+) access modes로 거절합니다. - 405행·458행:
만드는 PV 는 늘
VolumeMode: &fs(Filesystem)이라 Block 은 나오지 않습니다. - 공유 파일시스템 경로를 따로 설정하면 RWX 도 되지만, 기본이 아니고 StorageClass 만 보고는 알 수 없습니다. 그런 사용자는 지금처럼 StorageProfile 의
spec.claimPropertySets를 직접 적으면 된다고 본문에 썼습니다. 스냅숏·CSI 복제 기능이 없으므로 복제 전략은 지금 고르는 기본값(copy)을 그대로 둡니다.
PR 본문에는 하나를 더 적었습니다. CDI 자신의 kind 개발 환경(cluster-up/cluster/kind/manifests/local-storage-class.yaml)도 이 프로비저너를 씁니다.
이 변경 뒤에는 그 환경의 StorageProfile 도 채워지므로, 혹시 그것이 비어 있다고 기대하는 e2e 가 있으면 알려 달라고 부탁했습니다.
3. 무엇을 바꿨나: 세 번에 걸친 diff
첫 커밋(10-17)은 표에 한 줄을 더하고, StorageProfile 컨트롤러 시험에 새 프로비저너용 시험 하나와 Recognized 표 항목 하나를 더했습니다.
비슷한 선례인 #4286(Mayastor, 2026-09-27 병합)과 같은 모양입니다.
PR 본문의 시험 결과는 이랬습니다(Go 1.27.2, GOFLAGS=-mod=vendor, main 9cd4fbd).
| 시험 | 결과 |
|---|---|
| 새 시험, 표 항목 없이 | 7개 중 2개 실패 |
| 새 시험, 표 항목과 함께 | 통과 |
go test ./pkg/controller/ 고친 판 / 고치기 전 |
385 통과·3 실패 / 383 통과·3 실패 |
실패 셋은 양쪽 모두 같은 VDDK 관련 시험(Update PVC from POD)이라 이 변경과 상관없다고 본문에 적었습니다.
둘째 커밋(10-18)은 리뷰어의 제안을 반영했습니다. 노드 하나의 디렉터리로 볼륨을 만드는 프로비저너는 결국 모두 {rwo, file} 이니 묶자는
의견이었습니다. 그래서 createLocalPathCapabilities() 도우미를 만들어 hostpath-provisioner(CSI·레거시), k8s.io/minikube-hostpath,
rancher.io/local-path 에 쓰게 했습니다. hostpath-provisioner 의 overlay 변형은 RWX 라 그대로 두었습니다. 디렉터리가 아니라 드라이브 하나를
통째로 내주는 DirectPV 는 빼 두고, 원하면 넣겠다고 답했습니다. 이때 go test ./pkg/controller/ 는 388개 모두 통과했습니다(답글에 적은 값).
셋째 커밋(10-26)은 다른 리뷰어의 의견대로 첫 커밋의 단위 시험 둘을 지웠습니다. 새 프로비저너는 이미 시험이 있는 도우미를 다시 쓸 뿐이라
새 논리가 없다는 지적이었습니다. 이 커밋은 gofmt·go vet 만 돌렸고 시험은 다시 돌리지 않았습니다(원장 기록). 병합 전 CI 의 시험은 통과했습니다.
var CapabilitiesByProvisionerKey = map[string][]StorageCapabilities{
// hostpath-provisioner
- ProvisionerHPPCSI: {{rwo, file}},
+ ProvisionerHPPCSI: createLocalPathCapabilities(),
ProvisionerHPPOverlayCSI: {{rwx, file}},
- ProvisionerHPPLegacy: {{rwo, file}},
- "k8s.io/minikube-hostpath": {{rwo, file}},
+ ProvisionerHPPLegacy: createLocalPathCapabilities(),
+ // other node-local path provisioners
+ "k8s.io/minikube-hostpath": createLocalPathCapabilities(),
+ "rancher.io/local-path": createLocalPathCapabilities(), // local-path-provisioner, default in k3s and kind
...
+// createLocalPathCapabilities is shared by provisioners that back each volume
+// with a directory on a single node, so they only offer RWO filesystem volumes.
+func createLocalPathCapabilities() []StorageCapabilities {
+ return []StorageCapabilities{{rwo, file}}
+}
최종 diff 는 한 파일, 12줄 추가와 4줄 삭제입니다. 처음보다 오히려 작아졌습니다.
AI 사용은 세 커밋 모두 Assisted-by: Claude 꼬리말과 DCO 서명으로, 본문 끝에는 「프로비저너 소스를 읽고 변경과 시험 초안을 쓰는 데 Claude 의
도움을 받았고, 모든 줄을 검토했으며 StorageProfile 출력은 내 클러스터의 것」이라는 문단으로 밝혔습니다. KubeVirt 조직의
AI 기여 정책이 권하는 꼴입니다. SSH 서명은 첫 커밋에만 붙어 있고,
리뷰를 반영한 둘째·셋째 커밋은 GitHub 에서 서명되지 않은 커밋으로 보입니다.
4. 반응: 묶자, 빼자, 그리고 다시 열린 이슈
| 시각 | 일 |
|---|---|
| 10-17 23:38 | PR 공개. prow 가 size/S 라벨, 「신뢰되지 않은 작성자는 시험을 직접 못 연다」 안내, 리뷰어 둘 지정 |
| 10-18 20:25 | 리뷰어 A 의 줄 댓글: 이런 로컬 경로 프로비저너들은 결국 모두 rwo, file 이니 묶을 수 있겠다 |
| 10-18 21:03 | 도우미로 묶은 둘째 커밋, 21:05 답글. 라벨이 size/M 으로 |
| 10-19 16:57 | 메인테이너가 2024-10 에 자동으로 닫힌 이슈 #3005 를 /reopen |
| 10-19 16:58, 17:00 | 같은 메인테이너가 우리 PR 본문의 「Which issue(s)」 칸을 고쳐 Fixes #3005 를 넣음(본문 수정 이력) |
| 10-19 16:59 | 리뷰어 A 가 /approve 와 /test all. 17:13 coveralls 가 범위 50.288%(+0.08%) |
| 10-25 11:33 | 엿새 동안 /lgtm 이 없어, 지정된 리뷰어 둘에게 「approve 와 시험 통과가 있으니 lgtm 만 남았다」고 한 번 부탁 |
| 10-25 17:53 | 리뷰어 B 의 줄 댓글: 새 논리가 없는데 단위 시험 둘이 왜 필요한지 모르겠다 |
| 10-26 00:04 | 시험 둘을 지운 셋째 커밋, 00:07 답글. 라벨이 다시 size/S |
| 10-26 15:15 | 리뷰어 B 가 /lgtm |
| 10-26 19:58 | tide 가 병합, 이슈 #3005 가 함께 닫힘 |
이번 PR 에서 가장 배울 것이 많았던 장면은 10-19 의 두 줄짜리 일입니다. 우리는 초안을 쓸 때 같은 문제를 다룬 이슈나 PR 이 없다고 적었고(원장의
초안 목록에 「검색 0」), PR 본문의 이슈 칸에도 NONE 을 넣었습니다. 그런데 #3005
「StorageProfile needs to be patched for the rancher local-path provisioner」 가 이미 있었습니다. 2023-11-24 에 열렸고, 다른 메인테이너가 「인식하지 못한
프로비저너의 기본 동작이니 DV 에 접근 모드를 적거나 프로필을 고치라, 아니면 기본값을 넣는 PR 을 보내도 된다」는 취지로 답했습니다(2024-01). 그 뒤 아무도 PR 을
보내지 않아 2024-10-03 에 봇이 자동으로 닫았습니다. 우리의 검색은 열린 것만 본 것으로 보입니다.
메인테이너는 이것을 따로 지적하지 않았습니다. 대신 이슈를 다시 열고 우리 PR 본문에 연결을 넣어, 병합과 함께 2년 11개월 묵은 이슈가 닫히게 했습니다.
두 줄 댓글에는 모두 한 번에 받아들이고 짧게 답했습니다. 첫째는 「리뷰 고맙다, 둘째 커밋에서 도우미로 묶었다, DirectPV 는 디렉터리가 아니라 드라이브를 통째로 내주니 뺐다」, 둘째는 「맞다, 기존 hostpath 시험이 이미 덮는 도우미를 다시 쓸 뿐이라 둘 다 지웠다」였습니다. 두 리뷰어의 의견이 첫 커밋에서 더한 것(시험)과 바꾼 모양(도우미) 양쪽을 줄였고, 결과적으로 PR 이 작아졌습니다.
5. 배운 것: 오픈소스 기여 요령
- 「중복 검색에는 닫힌 이슈도 넣습니다.」 자동으로 닫힌(rotten) 이슈는 「해결됨」이 아니라 「아무도 안 고침」인 경우가 많습니다. 그런 이슈를
찾아
Fixes #번호로 잇고, 이슈에 남은 메인테이너의 답(「PR 을 보내도 된다」)을 본문에 인용했다면 리뷰가 더 빨랐을 것으로 보입니다. - 「값의 근거는 상대 프로젝트의 소스에서 찾습니다.」
{rwo, file}을 고른 이유를 local-path-provisioner 의 오류 문구와 PV 만드는 줄로 보이니, 리뷰에서 값 자체를 묻는 사람은 없었습니다. 질문은 모두 코드 모양(묶기, 시험 수)에 관한 것이었습니다. - 「리뷰가 PR 을 줄이면 그대로 받습니다.」 시험을 더 넣는 것이 늘 좋은 것은 아닙니다. 새 논리가 없는데 시험을 늘리면 유지할 코드만 늘어난다는 것이 리뷰어의 관점이었습니다. 처음 낸 사람이 정한 범위보다 메인테이너가 원하는 범위가 더 좁을 수 있습니다.
- 「approve 뒤에 lgtm 이 오래 없으면 한 번, 사실만 적어 부탁합니다.」 「approve 가 있고 시험이 통과했으니 lgtm 만 남았다」는 한 문장이었고, 그날 리뷰가 다시 움직였습니다.
티티 한마디. 우리 동네 창고 관리인은 「흙길 창고」라는 이름을 처음 들어 봤대요. 그래서 거기 맡긴 상자마다 「어떻게 보관할지 모름」 딱지를 붙였어요. 흙길 창고는 한 칸에 한 사람만, 상자 모양으로만 받는다는 걸 그 창고 규칙서에서 찾아 알려 드렸죠. 관리인 아저씨는 비슷한 창고 넷을 한 줄로 묶어 적자고 하셨고, 다른 아저씨는 확인 도장 두 개는 필요 없다고 하셨어요. 그리고 누군가 3년 전에 똑같은 부탁을 써 둔 쪽지를 서랍에서 꺼내 와서, 우리 쪽지와 같이 「해결됨」 상자에 넣어 주셨어요.
링크
모든 자료는 2026-10-29 에 열람했습니다.
- PR: kubevirt/containerized-data-importer#4319 (병합 커밋
fd4146ac) - 닫힌 이슈: #3005 (2023-11-24 열림, 2024-10-03 자동으로 닫힘, 2026-10-19 다시 열림)
- 고친 소스(바탕 커밋):
storagecapabilities.go,storageprofile-controller.go - local-path-provisioner 소스(v0.0.32):
provisioner.go345행, 458행 - 선례: containerized-data-importer#4286 Mayastor 기본값
- 같은 날 같은 조사에서 나온 PR: 6편, kubevirt/kubevirt#19499