이 글은 지금까지 다룬 스크립트(자막) 흐름을 중심으로, Docker의 스토리지 구조에서 시작해 Kubernetes의 PV/PVC, 그리고 StorageClass(동적 프로비저닝)와 volumeBindingMode까지 한 번에 이어서 정리한 “총 편집본”입니다.
중간중간 학습 과정에서 헷갈렸던 질문 포인트(예: src=data_volume은 어떻게 아는지, --mount는 어떻게 동작하는지, RWO 의미, PVC 50이 PV 100에 붙으면 100을 쓰는지 등)는 문맥에 자연스럽게 녹여 설명합니다.
1. Docker는 데이터를 어디에 저장할까? /var/lib/docker의 정체
Docker 설치 시 호스트에는 기본적으로 Docker의 작업 디렉터리가 생깁니다.
- 일반적으로:
/var/lib/docker - 하위에 예시로 다음과 같은 디렉터리들이 존재:
containers/: 컨테이너별 메타데이터, 로그 등image/: 이미지 관련 데이터volumes/: Docker Volume 데이터(영구 데이터)- (환경에 따라)
overlay2/,aufs/,btrfs/등: 스토리지 드라이버가 쓰는 영역
즉, Docker가 관리하는 “이미지/컨테이너/볼륨” 데이터가 이 루트 아래에 쌓입니다.
2. Docker 이미지/컨테이너의 핵심: 레이어드 파일시스템 + Copy-on-Write
2.1 Dockerfile 한 줄 = 이미지 레이어 하나
Docker는 이미지를 만들 때 레이어(layer) 방식으로 빌드합니다.
예:
FROM ubuntu→ base layerRUN apt-get ...→ 패키지 레이어RUN pip install ...→ 파이썬 의존성 레이어COPY . /app→ 소스 코드 레이어ENTRYPOINT ...→ 엔트리포인트 레이어
각 레이어는 “이전 레이어 대비 변경분”만 저장하므로,
- 재빌드 시 캐시를 활용해 빨라지고
- 여러 이미지가 레이어를 공유해 디스크를 절약합니다.
2.2 컨테이너 실행 시: 읽기 전용 이미지 레이어 + 쓰기 가능 컨테이너 레이어
이미지는 빌드 완료 후 불변(immutable) 에 가깝습니다. 즉 이미지 레이어는 읽기 전용입니다.
컨테이너를 실행하면 Docker는:
- 이미지 레이어들(RO) 위에
- 컨테이너 전용 쓰기 가능 레이어(RW) 를 하나 얹습니다.
컨테이너 내부에서:
- 로그 파일 생성
- 임시 파일 생성
- 파일 수정
같은 모든 변경사항은 이 RW 레이어에 기록됩니다.
컨테이너 삭제하면 왜 데이터가 날아가나?
RW 레이어의 수명은 “컨테이너 수명”과 같습니다.
컨테이너를 지우면 RW 레이어도 함께 사라지므로 컨테이너 내부에서 만든 데이터도 같이 날아갑니다.
2.3 이미지의 파일을 수정할 수는 없나? → 가능은 한데 “Copy-on-Write”
이미지 레이어는 RO라서 “원본 자체”는 바뀌지 않습니다.
하지만 컨테이너 안에서 파일을 수정하려고 하면 Docker/스토리지 드라이버가:
- 해당 파일을 RW 레이어로 복사해오고
- 이후 수정은 RW 레이어의 복사본에 적용합니다.
이게 Copy-on-Write 메커니즘입니다.
3. 데이터 영속성: Docker Volume이 필요한 이유
예: MySQL 컨테이너가 /var/lib/mysql에 DB 파일을 쓴다고 할 때,
그대로 컨테이너만 쓰면 DB 파일은 컨테이너 RW 레이어에 생기고 컨테이너 삭제 시 같이 삭제됩니다.
그래서 Volume을 붙입니다.
4. Docker 볼륨 실전: Volume mount vs Bind mount, -v vs --mount
4.1 Volume mount (Docker가 관리하는 볼륨)
- 볼륨 생성:
docker volume create data_volume
- 컨테이너 실행하며 마운트:
docker run -v data_volume:/var/lib/mysql mysql
이때 Docker는 호스트의:
/var/lib/docker/volumes/data_volume/_data
에 실제 데이터를 저장하고, 이를 컨테이너의/var/lib/mysql로 마운트합니다.
질문: src=data_volume에서 data_volume은 어떻게 아는 거지?
- 내가 만든 볼륨 이름입니다.
docker volume create data_volume로 만들었으면 그 이름이data_volume.- 또는 사전에 만든 볼륨 목록을 보고 결정할 수 있습니다:
docker volume ls
docker volume inspect data_volume
질문: src=mysql_data는 미리 만들어놔야 해?
- “볼륨 마운트” 관점에서는 두 케이스가 있습니다.
- 미리 만들기:
docker volume create mysql_data - 안 만들고 써도 됨:
docker run -v mysql_data:/var/lib/mysql ...- 이 경우 Docker가 자동으로 볼륨을 생성합니다.
- 미리 만들기:
즉 “Docker 볼륨 이름”을 쓰는 경우엔 반드시 미리 만들 필요는 없습니다(자동 생성 가능).
4.2 Bind mount (호스트의 임의 경로를 마운트)
호스트의 특정 디렉터리(예: /data/mysql)를 컨테이너로 붙이고 싶다면:
docker run -v /data/mysql:/var/lib/mysql mysql
이건 Docker가 /var/lib/docker/volumes 아래에 관리하는 “볼륨 객체”가 아니라,
그냥 호스트 파일시스템의 특정 경로를 그대로 바인딩합니다.
정리:
- Volume mount: Docker가 만든 볼륨 객체(저장 위치는
/var/lib/docker/volumes/...) - Bind mount: 호스트의 “원하는” 경로(
/data/...,/home/...)를 직접 마운트
4.3 -v vs --mount 차이
-v는 오래된 스타일(짧고 간단하지만 의미가 섞임),--mount는 명시적이고 키=값 형태라 실무에서 선호됩니다.
Bind mount 예시 (--mount)
docker run \
--mount type=bind,src=/data/mysql,dst=/var/lib/mysql \
mysql
Volume mount 예시 (--mount)
docker run \
--mount type=volume,src=mysql_data,dst=/var/lib/mysql \
mysql
질문: -v는 “컨테이너 경로를 VM 경로에 마운트”인데, --mount는 어떻게 동작?
동작은 같습니다. 차이는 “표기 방식과 명확성”입니다.
-v는 한 줄에 “소스/대상/옵션”이 콜론으로 섞여 들어가고,
소스가 “볼륨 이름인지/호스트 경로인지”가 문맥으로 해석됩니다.--mount는type=bind또는type=volumesrc=...dst=...
를 명시하므로 실수 가능성이 줄고 읽기 쉽습니다.
5. 스토리지 드라이버 vs 볼륨 드라이버: 역할 분리
5.1 스토리지 드라이버(Storage Driver)
- 이미지 레이어 관리
- 컨테이너 RW 레이어 생성
- Copy-on-Write 구현
을 담당합니다.
예: overlay2, aufs, btrfs, devicemapper 등
운영체제/커널/환경에 따라 최적 드라이버가 선택됩니다.
5.2 볼륨 드라이버(Volume Driver Plugin)
볼륨은 스토리지 드라이버가 아니라 볼륨 드라이버 플러그인이 담당합니다.
- 기본:
local드라이버- 볼륨 데이터를
/var/lib/docker/volumes에 저장
- 볼륨 데이터를
- 외부: EBS, Azure Disk/File, GCE PD, NetApp, Portworx 등 다양한 플러그인
컨테이너 실행 시 특정 드라이버를 지정해 클라우드 스토리지에 자동으로 붙이는 방식도 가능합니다(드라이버 구현에 따라).
6. Kubernetes로 넘어오기: “Pod도 일시적이다” → Volume이 필요하다
Docker와 마찬가지로 Kubernetes에서 Pod는 기본적으로 일시적입니다.
- Pod가 삭제되면 컨테이너 FS에 있던 데이터도 기본적으로 사라집니다.
- 그래서 Pod에도 Volume을 붙입니다.
7. Kubernetes Volume (Pod 수준)부터: 가장 단순한 형태
예: Pod가 /opt/number.out에 랜덤 값을 쓰는데 Pod 삭제해도 남기고 싶다.
가장 단순한 예로 hostPath를 쓰면:
- “노드의 디렉터리”를 Pod에 마운트할 수 있습니다.
하지만 중요한 경고:
hostPath는 멀티 노드에서 권장되지 않습니다.- Pod가 다른 노드로 가면, 다른 노드의 디렉터리를 보게 됩니다.
- “모든 노드가 같은 데이터”를 기대하면 깨집니다.
- 멀티 노드/프로덕션은 보통 NFS/Ceph/EBS/GCE PD 등 외부 스토리지를 씁니다.
8. PersistentVolume(PV) & PersistentVolumeClaim(PVC): 스토리지의 “중앙 관리”
Pod 스펙에 매번 스토리지 상세를 쓰는 건 대규모 환경에서 관리가 어렵습니다.
그래서 Kubernetes는 스토리지를 두 계층으로 나눕니다.
- PV (PersistentVolume): 관리자가 미리 준비한 “스토리지 풀(자원)”
- PVC (PersistentVolumeClaim): 사용자가 “필요한 스토리지”를 요청(Claim)
8.1 PV 생성 예시(개념)
- capacity, accessModes, 스토리지 타입(hostPath/EBS/NFS 등) 정의
hostPath PV는 실습용으로만(프로덕션 비권장)
8.2 PVC 생성과 바인딩
PVC가 생성되면 Kubernetes는 조건에 맞는 PV를 찾아 1:1로 바인딩합니다.
매칭 기준:
- 용량(capacity)이 요청 이상인지
- accessModes가 일치하는지
- (있다면) storageClassName
- (있다면) selector/label 매칭 등
질문: pvc50이 pvc100사이즈에 맵핑되면 실제로 100 쓸 수 있게 되나?
일반적으로는 “아니오” 로 이해하는 게 안전합니다.
- PV가 100Gi이고 PVC가 50Gi를 요청해 PV 100Gi에 바인딩되더라도,
PVC의 “요청량(requested)”은 50Gi입니다. - 많은 CSI/스토리지 구현에서는 PVC 요청 크기 기준으로 쿼터/리사이즈 정책이 적용됩니다.
- 그리고 중요한 점: PV-PVC는 1:1 관계라서
- 남는 50Gi를 다른 PVC가 “같은 PV에서 나눠 쓰는” 구조가 아닙니다.
정리:
- “물리적으로 100Gi 디스크를 붙였는데 논리 요청은 50Gi”일 수 있고,
- 실제 파일시스템/스토리지 구현, 쿼터, 리사이즈 설정에 따라 사용 가능/불가능이 갈립니다.
- 운영 관점에서는 PVC 요청 크기 = 내가 쓸 수 있는 크기로 설계하는 게 안전합니다.
9. Access Modes: 특히 RWO를 정확히 이해하기
PVC/PV에서 자주 등장하는 accessModes:
ReadWriteOnce (RWO)ReadOnlyMany (ROX)ReadWriteMany (RWX)등
질문: “RWO: 한 노드에만 Read/Write로 마운트 가능 (파드 여러 개여도 같은 노드면 가능)”이 무슨 말?
핵심은 “동시에 붙는 범위”가 노드 기준이라는 점입니다.
- RWO는 “한 번에 하나의 노드에만 read/write로 attach/mount 가능”
- 하지만 그 한 노드 안에서는:
- 여러 Pod가
- 같은 PVC를
- 동시에 마운트하는 것이 “가능한 구현/상황”이 많습니다(파일시스템/스토리지와 workload 특성에 따라 동시 쓰기 충돌은 별도 고려).
즉 문장의 의미는:
- 다중 노드에 걸쳐 동시에 RW로 마운트는 불가
- 단일 노드 내부에서 여러 Pod가 공유 마운트는 가능할 수 있음
주의:
- “가능”은 “스토리지 타입/드라이버/파일시스템/애플리케이션 동시쓰기 안전성”에 좌우됩니다.
- DB 같은 경우 “한 데이터 디렉터리를 여러 인스턴스가 동시에 RW”하면 망가질 수 있어, RWO라도 일반적으로 단일 writer 패턴을 씁니다.
10. Pod에서 PVC를 쓰는 방법: volumes + volumeMounts
다음 예시를 기준으로 “볼륨 관련 필드”를 해부합니다.
apiVersion: v1
kind: Pod
metadata:
name: mypod
spec:
containers:
- name: myfrontend
image: nginx
volumeMounts:
- mountPath: "/var/www/html"
name: mypd
volumes:
- name: mypd
persistentVolumeClaim:
claimName: myclaim
10.1 volumes는 “어떤 볼륨을 쓸지” 정의(컨테이너 밖, Pod 레벨)
volumes:
- name: mypd
persistentVolumeClaim:
claimName: myclaim
name: mypd- Pod 내부에서 이 볼륨을 참조할 “별칭”
persistentVolumeClaim.claimName: myclaim- “myclaim PVC를 이 볼륨으로 쓰겠다”
- PVC는 이미 어떤 PV에 바인딩되어 있을 것이고, 결국 그 PV의 실제 스토리지를 사용합니다.
10.2 volumeMounts는 “컨테이너 안의 어디에 붙일지” 정의(컨테이너 레벨)
volumeMounts:
- mountPath: "/var/www/html"
name: mypd
name: mypd로 volumes에서 정의한 볼륨을 참조mountPath: /var/www/html에 마운트
질문: 결국 컨테이너 내부의 /var/www/html 파일들은 myclaim에 연결된 PV 경로에 쌓이게 되는거지?
네. 정확히는:
- 컨테이너가
/var/www/html아래에 쓰는 데이터는 - PVC
myclaim이 바인딩된 PV(및 그 스토리지)로 저장됩니다.
즉, 컨테이너 관점에서는 /var/www/html이지만,
실제로는 “PV가 가리키는 스토리지(EBS/NFS/hostPath 등)”에 기록됩니다.
11. PVC 삭제 시 PV는 어떻게 되나? Reclaim Policy
PVC를 지우면 PV의 처리 정책은 PV의 persistentVolumeReclaimPolicy에 좌우됩니다.
대표:
Retain(기본으로 많이 보게 됨): PV와 실제 데이터 유지(관리자가 수동 정리)Delete: PVC 삭제 시 PV 및 실제 스토리지도 함께 삭제(동적 프로비저닝에서 흔함)Recycle: 과거 방식(Deprecated). 단순rm -rf수준이라 안전/이식성/기능 부족으로 사실상 사용하지 않음
12. StorageClass: “PV를 미리 만들어두는 방식(정적)”에서 “PVC 요청 시 자동 생성(동적)”으로 넘어가기
여기까지는 PV를 미리 만들어두고 PVC가 그 PV를 가져가는 “정적 프로비저닝” 흐름이었습니다.
하지만 클라우드 환경에서 이 방식은 다음 문제가 있습니다.
- 애플리케이션이 스토리지를 필요로 할 때마다
- 클라우드 콘솔/CLI에서 디스크(EBS/GCE PD 등) 만들고
- 그 디스크를 참조하는 PV YAML을 만들고
- PVC가 그걸 바인딩하도록 기다려야 함
이 과정을 자동화하기 위한 장치가 StorageClass 입니다.
12.1 StorageClass가 하는 일
StorageClass에는 보통:
- 어떤 프로비저너(CSI driver 등)를 쓸지
- 어떤 타입/성능/파라미터(SSD, 복제, IOPS 등)를 쓸지
를 정의합니다.
그리고 PVC에서 storageClassName을 지정하면:
- PVC 생성
- StorageClass가 프로비저너를 통해 “실제 디스크/볼륨”을 자동 생성
- PV 자동 생성
- PVC ↔ PV 자동 바인딩
즉, 사용자는 “PVC만 만들면” 되고 PV/디스크는 자동입니다.
12.2 volumeBindingMode까지 포함해서 이해하기
StorageClass에는 volumeBindingMode라는 중요한 옵션이 있습니다.
이 옵션은 “PVC가 만들어졌을 때 PV(및 실제 볼륨)를 언제 확정(바인딩/프로비저닝)할지”를 결정합니다.
대표 값:
(1) Immediate
- PVC가 생성되는 즉시 PV를 만들고 바인딩을 진행합니다.
- 단점(특히 멀티 AZ/토폴로지 환경):
- 어떤 노드에 Pod가 스케줄될지 아직 모르는 상태에서
- 볼륨이 특정 AZ에 먼저 만들어져버리면
- Pod 스케줄링이 꼬일 수 있습니다(노드는 AZ A인데 볼륨은 AZ B 같은 상황).
(2) WaitForFirstConsumer
- “처음으로 그 PVC를 사용하는 Pod가 스케줄될 때까지” 바인딩/프로비저닝을 미룹니다.
- 장점:
- Pod가 어느 노드(어느 존)에 배치되는지 결정된 후
- 그 토폴로지에 맞게 볼륨을 생성/선택하므로
- 스케줄링 실패/비효율을 줄입니다.
실무 감각으로 정리하면:
- 단일 존/단순 환경이면
Immediate도 무난한 편 - 멀티 존(특히 클라우드) + 상태ful 워크로드면
WaitForFirstConsumer가 문제를 줄이는 경우가 많습니다.
마무리: 전체 흐름 한 장 요약
- Docker
- 이미지 레이어(RO) + 컨테이너 레이어(RW) + Copy-on-Write
- 컨테이너 삭제 시 RW 레이어 삭제 → 데이터 유실
- 영속 데이터는 Volume(volume mount / bind mount)
-v는 축약형,--mount는 명시형- 스토리지 드라이버는 레이어/CoW, 볼륨 드라이버는 영속 볼륨
- Kubernetes
- Pod도 일시적 → Volume 필요
- 단순히 Pod에 직접 붙이는 방식(예: hostPath)은 한계
- PV(관리자) / PVC(사용자) 로 스토리지를 중앙 관리
- AccessMode(RWO 등)와 ReclaimPolicy 이해가 중요
- StorageClass로 동적 프로비저닝(자동 PV/자동 볼륨 생성)
volumeBindingMode로 “언제 바인딩할지” 제어(Immediate vs WaitForFirstConsumer)
'CKA' 카테고리의 다른 글
| Network - 네트워크 기초(DNS) (0) | 2026.01.05 |
|---|---|
| Network - 네트워크 기초(스위치, 라우터, 게이트웨이) (0) | 2026.01.05 |
| Storage - Storage Class (0) | 2026.01.03 |
| Storage - PV & PVC (0) | 2026.01.03 |
| Storage - Volume (0) | 2026.01.03 |