# ReadWriteMany를 고르기 전에 - 액세스 모드는 파드가 아니라 노드 단위 계약이다

분산 워커가 같은 파일을 읽어야 할 때 가장 먼저 떠올리는 답은 ReadWriteMany 볼륨입니다. 그런데 액세스 모드는 흔히 생각하는 것보다 훨씬 약한 보장만 제공합니다. ReadWriteOnce를 "한 파드만 쓴다"로 이해하고 있었다면, 그 가정 위에 세운 데이터 안전성은 이미 깨져 있습니다.

이 글에서는 액세스 모드가 실제로 무엇을 제약하는지, 왜 RWX가 동시 쓰기 안전성을 보장하지 않는지, 그리고 공유 스토리지를 도입하기 전에 확인해야 할 것들을 정리합니다.

# 1. ReadWriteOnce는 노드 단위다

v1.22 이전까지 액세스 모드는 세 가지였고 정의의 주어가 전부 노드입니다.

  • ReadWriteOnce – the volume can be mounted as read-write by a single node
  • ReadOnlyMany – the volume can be mounted read-only by many nodes
  • ReadWriteMany – the volume can be mounted as read-write by many nodes

여기서 오해가 시작됩니다. 문서는 그 결과를 명시적으로 적어 두었습니다.

The ReadWriteOnce access mode restricts volume access to a single node, which means it is possible for multiple pods on the same node to read from and write to the same volume. This could potentially be a major problem for some applications, especially if they require at most one writer for data safety guarantees.

즉 RWO 볼륨은 같은 노드에 스케줄된 파드 여러 개가 동시에 읽고 쓸 수 있습니다. 레플리카 2개짜리 Deployment가 우연히 한 노드에 몰리면, RWO를 믿고 짠 "쓰는 쪽은 하나뿐" 가정이 그대로 무너집니다. 롤링 업데이트 중 구·신 파드가 같은 노드에 겹치는 순간도 마찬가지입니다.

파드 단위 보장이 필요하면 v1.22에서 추가된 모드를 써야 합니다.

This access mode enables you to restrict volume access to a single pod in the cluster, ensuring that only one pod can write to the volume at a time.

다만 제약이 붙습니다.

Note that ReadWriteOncePod is only supported for CSI volumes.

모드 보장 범위 쓰는 곳
ReadWriteOnce 노드 하나 (그 노드의 여러 파드는 동시 접근 가능) 일반 상태 저장 워크로드
ReadOnlyMany 여러 노드, 읽기만 공유 읽기 전용 데이터
ReadWriteMany 여러 노드, 읽기·쓰기 분산 워커 공유 저장소
ReadWriteOncePod 파드 하나 (클러스터 전체에서) 단일 기록자가 정합성의 전제인 경우

한 가지 더. 볼륨이 여러 모드를 지원하더라도 마운트 시점에는 하나의 모드로만 사용됩니다. PVC에 적은 모드가 그 사용의 계약입니다.

# 2. RWX는 동시 쓰기를 안전하게 만들어 주지 않는다

가장 중요한 지점입니다. ReadWriteMany는 "여러 노드에서 마운트할 수 있다"만 말합니다. 여러 프로세스가 같은 파일에 동시에 써도 안전한가는 전혀 다른 문제이고 그 답은 액세스 모드가 아니라 파일시스템과 애플리케이션이 정합니다.

Kubernetes가 보장해 주지 않는 것들:

  • 두 워커가 같은 파일을 동시에 열어 쓸 때의 원자성
  • 한 워커가 쓰는 중에 다른 워커가 읽을 때 보이는 내용
  • 파일 락(flock, fcntl)이 노드 경계를 넘어 동작하는지

세 번째가 특히 위험합니다. NFS 계열 스토리지에서 락 동작은 서버 구현과 프로토콜 버전, 클라이언트 마운트 옵션에 따라 달라집니다. 로컬 디스크에서 잘 돌던 "락 잡고 임시 파일에 쓴 뒤 rename" 패턴이 공유 마운트에서 그대로 성립한다고 가정하면 안 됩니다.

실무에서 안전한 설계는 대체로 셋 중 하나입니다.

패턴 방법 적합한 경우
쓰기 분리 워커마다 고유 경로에만 쓰고, 읽기만 공유 체크포인트·샤드 출력
단일 기록자 쓰는 주체를 하나로 두고 나머지는 읽기 인덱스·메타데이터
외부 조정 오브젝트 스토리지 + 조건부 쓰기, 또는 DB 정합성이 핵심인 데이터

가장 자주 통하는 건 첫 번째입니다. 공유가 필요한 이유가 대개 "여러 워커가 같은 파일을 읽어야 해서"이지 "같은 파일에 같이 써야 해서"는 아닙니다. 쓰기 경로를 워커 식별자로 나누기만 해도 문제 대부분이 사라집니다.

/shared/
├── models/            ← 읽기 전용 공유 (모델 가중치, 데이터셋)
├── outputs/
│   ├── worker-0/      ← 각자 자기 디렉터리에만 쓴다
│   └── worker-1/
└── checkpoints/
    └── run-2025-05/   ← 기록자 하나

# 3. 공유 스토리지의 실제 병목은 대역폭이 아니다

RWX 볼륨을 붙이고 나서 겪는 성능 문제는 대개 처리량이 아니라 메타데이터 지연에서 옵니다. 네트워크 파일시스템에서 open, stat, readdir은 각각 네트워크 왕복입니다. 로컬 디스크에서 마이크로초 단위였던 연산이 밀리초 단위가 되면, 파일 수가 많은 작업은 체감상 멈춘 것처럼 보입니다.

특히 취약한 패턴이 있습니다.

  • 작은 파일 수만 개를 순회하는 데이터 로더 - 파일당 왕복 비용이 그대로 누적됩니다.
  • Python 임포트 경로를 공유 볼륨에 둔 경우 - 인터프리터가 수천 번의 stat을 발생시킵니다.
  • 디렉터리 목록을 자주 읽는 로직 - 항목 수에 비례해 느려집니다.

측정부터 하는 것이 순서입니다. 처리량이 아니라 연산 횟수와 지연을 봐야 합니다.

# 메타데이터 연산 지연 확인 - 파일 하나가 아니라 개수로 재야 한다
time sh -c 'for i in $(seq 1 1000); do stat /shared/models/f$i >/dev/null; done'

# 어떤 시스템 콜에 시간이 쏠리는지
strace -c -f -p <pid> 2>&1 | head -20

대응은 대체로 구조 변경입니다. 작은 파일을 하나로 묶어(아카이브, 샤드 포맷) 읽거나, 자주 읽는 데이터를 파드 로컬에 캐시하거나, 코드·의존성은 이미지에 굽고 공유 볼륨은 데이터 전용으로 씁니다. 공유 볼륨을 만능 디스크처럼 쓰는 순간 비용이 발생합니다.

# 4. 도입 전 확인 목록

확인 항목 확인 방법
스토리지 클래스가 RWX를 지원하는가 프로비저너 문서 확인. 블록 스토리지 계열은 대부분 미지원
정말 동시 쓰기가 필요한가 쓰기 경로를 워커별로 나눌 수 있는지 먼저 검토
파일 락에 의존하는 코드가 있는가 있다면 해당 스토리지에서 동작을 실제로 시험
접근 패턴이 작은 파일 다수인가 그렇다면 묶기·캐싱 설계를 먼저
백업·스냅샷 정책이 있는가 공유 볼륨은 장애 시 영향 범위가 넓다
용량 확장이 온라인으로 되는가 확장 지원 여부와 절차

첫 줄부터 걸리는 경우가 많습니다. PVC가 Pending에 머물러 있다면 액세스 모드 미지원이 원인인지 먼저 확인해야 합니다.

kubectl get pvc <name> -o jsonpath='{.status.phase} {.spec.accessModes}'
kubectl describe pvc <name> | tail -10        # 이벤트에 프로비저너 오류가 남는다
kubectl get storageclass                       # 프로비저너 확인

# 5. 마무리

  • 액세스 모드의 주어는 노드입니다. ReadWriteOnce는 같은 노드의 여러 파드가 동시에 쓰는 것을 막지 않습니다. 파드 단위 보장이 필요하면 ReadWriteOncePod(CSI 전용)를 써야 합니다.
  • ReadWriteMany는 마운트 가능 범위만 넓힐 뿐, 동시 쓰기 안전성은 보장하지 않습니다. 그 책임은 파일시스템과 애플리케이션에 있습니다.
  • 공유가 필요한 이유는 대개 읽기 공유입니다. 쓰기 경로를 분리하면 문제 대부분이 사라집니다.
  • 성능 문제는 대역폭보다 메타데이터 지연에서 옵니다. 작은 파일 다수 패턴이면 도입 전에 구조를 바꾸는 편이 낫습니다.

# 참고