자격증/CKA

Storage

sanglog 2025. 4. 14. 01:57

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를 사용하려고 스케줄될 때 그제서야 바인딩을 수행함