Storage
Docker Storage
Docker 에서는 두가지의 주요 스토리지 개념이 있다
- storage drivers
- volume driver plugins
Storage driver
도커의 데이터의 기본 저장 경로는
/var/lib/docker 이다
해당 경로에서 주요 디렉토리는 아래와 같다
- containers/ : 컨테이너 관련 데이터
- image/ : 이미지 관련 데이터
- volumes/ : 도커 볼륨 데이터
Docker 이미지 = 계층 구조 (Layered Architecture)
Dockerfile의 각 명령은 하나의 이미지 계층(Layer)을 생성한다
그래서 각 레이어는 이전 레이어의 변경 사항만 저장 (최적화됨)
장점
- 캐싱: 중복된 레이어 재사용 (다른 도커 이미지 사용한다해도 레이어 같은 거 있으면 재 사용이 가능한것)
(디스크 절약 + 빌드 속도 향상)
이미지 레이어는 읽기 전용(READ-ONLY)
컨테이너를 실행할 때, 이미지 레이어(dockerfile로 만든거, 불변!) 위에 쓰기 가능한 레이어가
(앱 실행 중 생기는 데이터 예를들어 로그, 캐시, 수정된 설정 등 을 저장) 생성됨
새로운 파일이나 수정은 복사본을 생성해 변경 (Copy-On-Write 방식)
Copy-On-Write
읽기 전용 이미지 레이어의 파일을 변경하거나 삭제하려고 할 때 발생하는 동작 방식
-> 읽기 전용 레이어에서 해당 파일을 컨테이너 레이어에 복사본으로 생성하고 그 복사본을 수정하는것
컨테이너 삭제 시 쓰기 레이어는 함께 삭제됨
로그나 파일 등은 영구 저장 불가
--해결 방법--> Volume 사용
Volume vs Bind Mount
구분 | 설명 |
Volume | 도커가 직접 관리하는 저장소 (/var/lib/docker/volumes) docker volume create로 생성 - 컨테이너 삭제해도 볼륨은 유지된다 - 더 안전하고 격리 되어있다 - 데이터베이스, 앱 데이터, 영속성 필요한 서비스 - docker에서 백업 /복원이 쉽다 |
Bind Mount | 호스트의 특정 디렉토리를 직접 마운트예: /data/mysql - 호스트 디렉터리이기 때문에 컨테이너 삭제해도 유지 - 기존 파일 사용, 로컬 개발 시 코드 연동등에 사용된다 |
ps . Storage Driver로 생성된 쓰기 레이어는 컨테이너 삭제 시 사라진다
# volume mount 예시
docker run -v myvol:/app nginx
docker run --mount type=volume,source=myvol,target=/app nginx
# bind mount 예시
docker run -v /data/mysql:/var/lib/mysql mysql
docker run --mount type=bind,source=/data/mysql,target=/var/lib/mysql mysql
-v 옵션 --mount 옵션 둘다 사용가능한데 mount 방식이 더 직관적이라 추천된다
Storage Driver란
Docker의 layered file system을 가능하게 해주는 엔진
대표적인 스토리지 드라이버: overlay2 (가장 많이 사용됨, 기본값) , AUFS, device-mapper, btrfs, zfs 등
운영체제에 따라 자동 선택 (ex: Ubuntu는 AUFS 또는 overlay2)
Volume driver plugins
항목 | Storage Driver | Volume Driver |
역할 | 이미지 & 컨테이너 레이어 관리 | 지속적인 데이터 저장소 관리 |
기본 경로 | /var/lib/docker/... | /var/lib/docker/volumes/... |
예시 | Overlay2, AUFS 등 | local (기본), REX-Ray, Portworx 등 |
사용 대상 | 컨테이너 실행 시 생성되는 레이어 | 외부에 안전하게 보관할 데이터용 |
목적 | 이미지와 컨테이너의 레이어 저장 (읽기 전용 + 임시 쓰기) | 영구 데이터 저장소 관리 |
저장 위치 | 도커 내부 (컨테이너 계층 위) | 로컬 디스크 or 외부 스토리지 (EBS, NFS 등) |
Volume Driver는 데이터를 안전하게 저장하기 위해 사용
컨테이너가 종료되어도 데이터를 유지하고 싶을 때 사용
Volume은 Storage Driver랑 별개로 관리됨
Volume을 사용할 때는 Volume Plugin을 통해 생성됨
기본 Volume Plugin: local
도커 호스트에 있는 디스크에 저장
경로: /var/lib/docker/volumes
docker volume create myvol # 기본 local plugin 사용
서드파티 Volume Plugin
Docker는 다양한 스토리지 백엔드와 연결 가능한 드라이버를 제공
REX-Ray 같은거 사용하면 AWS EBS, S3, EMC, Google PD, OpenStack 등 지원
이 방식으로 컨테이너가 AWS EBS 같은 클라우드 볼륨을 사용할 수 있음
컨테이너가 종료되어도 데이터는 클라우드에 안전히 저장됨
Container Storage Interface(CSI)
Kubernetes가 다양한 스토리지 제공자와 연동할 수 있도록 해주는 표준 인터페이스
Kubernetes 외에도 Cloud Foundry, Mesos 등 다양한 플랫폼에서도 사용 가능
각 스토리지 벤더는 CSI 표준을 구현한 드라이버를 제공하면 된다
주요 스토리지 벤더의
AWS EBS, Azure Disk, Google PD, Portworx, 등
이들은 각자 CSI 드라이버를 제공하여 Kubernetes와 연동 가능
동작 방식
Kubernetes가 특정 볼륨 작업을 요청하면, CSI가 정의한 RPC(Remote Procedure Call) 호출
ex) CreateVolume ,DeleteVolum, MountVolume 등 다양한 RPC 정의
→ CSI 드라이버는 이런 RPC에 대해 구현 코드 제공해야 함
Volume (시험 중요)
컨테이너와 파드는 영구적이지 않다 -> 삭제되면 내부의 데이터도 같이 사라진다
데이터를 영구적으로 보존하려면 volume를 사용해야한다.
Kubernetes의 볼륨 동작 구조
Pod 정의에 volume을 정의하고
컨테이너 안에서 volumeMount로 특정 경로에 마운트
Pod 삭제 시 컨테이너는 사라지지만 볼륨에 저장된 데이터는 유지
볼륨 스토리지의 종류
HostPath
노드의 로컬 디렉토리 사용
멀티 노드에서는 노드 간 데이터 불일치 발생 할 수 있어서 단일 노드 환경에만 적합하다 (노드의 디렉토리니까)
ex) /data 같은 경로
apiVersion: v1
kind: Pod
metadata:
name: hostpath-volume-pod
spec:
containers:
- name: test-container
image: busybox
command: ["sh", "-c", "echo Hello > /data/hello.txt && sleep 3600"]
volumeMounts:
- name: host-volume
mountPath: /data #컨테이너의 /data안에 있는데이터가 host-volume과 연동
volumes:
- name: host-volume
hostPath:
path: /tmp/data
type: DirectoryOrCreate
클라우드 스토리지 / 외부 스토리지
AWS EBS, Azure Disk, GCP Persistent Disk 등 외부 스토리지 솔루션
→ 멀티 노드 환경에 적합하고 고가용성 보장 가능
apiVersion: v1
kind: Pod
metadata:
name: aws-ebs-pod
spec:
containers:
- name: test-container
image: busybox
command: ["sh", "-c", "echo Persistent Data > /mnt/data/persist.txt && sleep 3600"]
volumeMounts:
- name: ebs-volume
mountPath: /mnt/data
volumes:
- name: ebs-volume
awsElasticBlockStore:
volumeID: vol-0abcd1234efgh5678
fsType: ext4
AWS EBS 볼륨(volumeID)을 /mnt/data에 마운트
흐름 요약
Pod YAML
└─ volume 정의 (예: AWS EBS, HostPath 등)
└─ 컨테이너 내부 경로에 volumeMount
└─ 컨테이너가 데이터를 작성 → 실제 볼륨에 저장
Persistent Volumes
기존에는 Pod YAML에 직접 storage 설정이 필요했다
모든 Pod마다 storage 설정을 직접 해줘야 함 변경 시에는 모든 Pod 정의 수정 필요 하다
이에 대한 해결책이 Persistent Volume(PV) 이다
→ 클러스터 수준에서 중앙 집중식으로 저장소 관리 가능
# 기존방식
volumes:
- name: my-vol
hostPath: # ✅ 이 부분이 바로 '스토리지 구현 방식'
path: /data/...
# pv 정의
apiVersion: v1
kind: PersistentVolume
metadata:
name: pv1
spec:
capacity:
storage: 5Gi
accessModes:
- ReadWriteOnce
hostPath: # ✅ 스토리지 구현은 여기서 정의
path: /data/pv1
# pvc
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: my-claim
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 5Gi
# 이후 파드 연결
volumes:
- name: storage
persistentVolumeClaim:
claimName: my-claim # ✅ 이거만 지정하면 됨
Persistent Volume(PV)이란
관리자가 미리 생성한 스토리지 자원
사용자(Pod)는 이를 “청구”해서 사용 → Persistent Volume Claim(PVC)
pv는 노드에 있는 저장소일수도 있고 외부 스토리지일 수도 있다(하지만 멀티 환경 운영사에는 외부 스토리지 기반 추천)
구조
apiVersion: v1
kind: PersistentVolume
metadata:
name: pv-vol1
spec:
capacity:
storage: 1Gi # 스토리지 크기
accessModes:
- ReadWriteOnce # 접근 모드
hostPath:
path: /data/pv1 # 로컬 디렉토리 사용 (학습용)
AccessModes 종류
- ReadWriteOnce (RWO) : 하나의 노드에서 읽기/쓰기
- ReadOnlyMany (ROX) : 여러 노드에서 읽기만
- ReadWriteMany (RWX) : 여러 노드에서 읽기/쓰기 가능
Persistent Volume Claims
PVC 는 사용자가 스토리지를 요청하는 객체다
PV는 관리자가 미리 만들어둔 스토리지 풀
PVC를 생성하면 쿠버네티스가 조건에 맞는 PV에 자동으로 바인딩해준다
PVC와 PV의 관계
1:1 매칭만 가능하다
→ 한 PVC는 하나의 PV에만 연결되고, 남은 용량이 있어도 다른 PVC는 못 쓴다
조건 매칭 기준
- 요청한 storage (용량)
- accessModes
- volumeMode
- storageClass (선택적)
조건이 모두 맞는 PV가 없으면 Pending 상태 유지
특정 PV에 매칭
PVC 정의에 label selector를 지정해 원하는 PV만 매칭되도록 설정 가능
예시
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: myclaim
spec:
accessModes:
- ReadWriteOnce
volumeMode: Filesystem
resources:
requests:
storage: 8Gi
storageClassName: slow
selector:
matchLabels:
release: "stable"
바인딩 결과 확인
kubectl get pvc
kubectl get pv (이거하면 Reclaim plicy 확인가능)
PVC가 Bound 상태가 되면, 성공적으로 PV와 연결된 것
PVC 삭제 시 PV
reclaimPolicy 설정에 따라 행동이 달라진다
reclaimPolicy | 설명 |
Retain (기본값) | PVC는 삭제되지만 PV는 그대로 남아 있음 (사용 가능한건 아니라 수동 정리 필요) |
Delete | PVC 삭제 시 PV도 자동 삭제 |
Recycle | PV의 데이터를 지우고 자동 재사용 가능하게 만듦 (❗현재는 deprecated됨) |
Storage Class
정적 프로비저닝을 하는경우
사용자가 gcp, aws등에서 먼저 디스크를 수동으로 만들고 난 뒤
해당 디스크를 사용하는 pv를 yaml로 정의
이후 pvc를 통해서 연결하는 과정이 필요하다
-> storageClass를 이용하면 자동으로 디스크 생성 + PV 생성
(원래 pv는 직접 만들어야했던건데 storageclass는 pvc를 만들때 자동으로 pv도 동적으로 생성해주는것)
이를 동적 프로비저닝 이라고 한다
예시
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: standard-ssd
provisioner: kubernetes.io/gce-pd # GCP Persistent Disk
parameters:
type: pd-ssd # 디스크 타입
replication-type: none # 복제 여부
provisioner
AWS EBS: kubernetes.io/aws-ebs
Azure Disk/File: kubernetes.io/azure-disk
-> kubernetes.io/no-provisioner 이거 있으면 이거는 동적 볼륨 프로비저닝 안쓰는거
참고로 이것만 정의한다고 gcp에서 자동으로 디스크 생성되는건 아니고 이미 gcp에 연결되어있고 인증 설정이 완료된 상태면 가능한것
ps. kubectl get sc
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: my-pvc
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 5Gi
storageClassName: standard-ssd # ← 이게 핵심!
StorageClass의 volumeBindingMode 설정이
PVC가 언제 PV와 바인딩(연결)될지를 결정합니다.
volumeBindingMode |
설명 |
Immediate (기본값) | PVC 생성 시, 즉시 PV에 바인딩함 (Pod 없이도) |
WaitForFirstConsumer | Pod가 PVC를 사용하려고 스케줄될 때 그제서야 바인딩을 수행함 |