13년차의 서버실

서버, 인프라, 홈랩 기술 블로그

[태그:] PersistentVolume

  • [Kubernetes] StatefulSet vs Deployment: 상태 저장 애플리케이션 배포 비교

    [Kubernetes] StatefulSet vs Deployment: 상태 저장 애플리케이션 배포 비교

    [Kubernetes] Kubernetes StatefulSet vs Deployment 비교 분석

    Kubernetes StatefulSet vs Deployment를 처음 제대로 구분하게 된 건, 홈랩에서 데이터베이스를 올렸다가 볼륨이 꼬이면서 한참 삽질했을 때였습니다. 처음엔 둘 다 그냥 Pod(파드)를 여러 개 띄우는 Kubernetes 워크로드(workload, 애플리케이션 실행 단위)겠거니 싶었거든요. 근데 실제로 운영해보니까 차이가 꽤 크더라고요. 특히 상태 저장 애플리케이션(stateful application, 데이터와 식별성이 중요한 앱) 쪽은 정말 그랬습니다. 웹 API처럼 가볍게 교체되는 애플리케이션 배포는 Deployment(디플로이먼트)가 정말 편한데, MySQL이나 PostgreSQL 같은 건 접근을 잘못하면 나중에 복구할 때 진짜 식은땀이 나더라고요.

    혹시 이런 경험 있으신가요? 애플리케이션은 잘 떴는데, 재시작 후 데이터 경로가 달라지거나 Pod 이름이 바뀌면서 클러스터 구성이 꼬이는 상황 말입니다. 여기서 중요한 포인트! Kubernetes StatefulSet vs Deployment는 단순히 생성 방식만 다른 게 아니라, 애플리케이션의 정체성(identity), 저장소(storage), 배포 순서(ordering)를 다루는 철학 자체가 다릅니다.

    이번 글에서는 제가 직접 홈랩에서 써보며 정리한 기준으로, Kubernetes StatefulSet vs Deployment 차이를 실무 감각으로 풀어보겠습니다. 단순 정의만 보면 헷갈리기 쉬우니까, 실제 YAML 예제와 트러블슈팅까지 같이 보시죠. 이전 글에서 다룬 Persistent Volume(PV, 영구 볼륨)과 StorageClass(스토리지 클래스) 내용을 같이 보시면 이해가 더 빨라집니다. 다음 글에서는 Helm(헬름, 쿠버네티스 패키지 매니저)으로 상태 저장 애플리케이션 배포 자동화하는 방법도 다룰 예정입니다.

    Kubernetes StatefulSet vs Deployment 구조 비교 아키텍처 이미지

    Deployment와 StatefulSet이 Pod, Service, Volume을 어떻게 다르게 다루는지 한눈에 보는 개요 이미지입니다.

    1. 왜 Kubernetes StatefulSet vs Deployment가 중요한가

    쉽게 말해 Deployment는 언제든 교체 가능한 복제본을 잘 다루고, StatefulSet은 각 인스턴스가 자기 이름과 저장소를 유지해야 하는 경우를 잘 다룹니다.

    예를 들어 보겠습니다.

    • 웹 프론트엔드나 API 서버는 Pod가 하나 죽어도 새 Pod가 뜨면 보통 괜찮습니다.
    • 반면 데이터베이스나 메시지 큐(message queue, 비동기 메시지 처리 시스템)는 각 인스턴스가 가진 데이터와 순서가 중요거든요.
    • 캐시 서버도 단일 노드냐 클러스터 구성이냐에 따라 선택이 달라집니다.

    제가 처음엔 Redis를 무조건 Deployment로 올렸었는데요. 단일 캐시 테스트 용도에선 괜찮았는데, 나중에 persistent volume을 붙이고 노드 재스케줄링이 일어나니까 예상과 다르게 동작하는 부분이 있었어요. 그때 느낀 게, 상태 저장 애플리케이션은 살아 있는 데이터만 보는 게 아니라, 재시작 이후의 동일성까지 봐야 한다는 점이었습니다.

    2. 개념부터 쉽게 정리해보겠습니다

    2-1. Deployment란?

    Deployment는 ReplicaSet(레플리카셋, 동일 Pod 복제 관리)을 통해 여러 Pod를 선언적으로 관리하는 방식입니다. 주 목적은 무중단에 가깝게 애플리케이션 배포를 반복 가능하게 만드는 것입니다.

    • Pod 이름은 매번 바뀔 수 있습니다.
    • 특정 Pod가 죽어도 새 Pod가 대체되면 됩니다.
    • Rolling Update(롤링 업데이트, 순차 교체)가 편하죠.
    • 웹 서버, API 서버, 워커(worker, 백그라운드 작업 프로세스)에 잘 맞습니다.

    2-2. StatefulSet이란?

    StatefulSet은 이름 그대로 상태(state)를 가진 워크로드를 위한 리소스입니다. 각 Pod가 고정된 네트워크 식별자와 안정적인 스토리지 연결을 유지하도록 설계되어 있거든요.

    • Pod 이름이 ordinal(순번) 기반으로 고정됩니다. 예: db-0, db-1
    • 생성/삭제 순서가 제어됩니다.
    • 각 Pod마다 독립적인 PersistentVolumeClaim(PVC, 영구 볼륨 요청)이 붙을 수 있습니다.
    • 데이터베이스, ZooKeeper, Kafka 계열, 복제 구조가 있는 저장소에 자주 씁니다.

    저도 처음엔 이게 뭔가 싶었는데, 실제로 써보니까 StatefulSet은 Pod를 복제한다기보다, 번호가 붙은 개별 인스턴스를 관리한다는 느낌으로 이해하는 게 가장 쉽더라고요.

    3. 핵심 차이점 비교: Kubernetes StatefulSet vs Deployment

    항목 Deployment StatefulSet
    주 용도 Stateless 애플리케이션 배포 Stateful 애플리케이션 배포
    Pod 이름 가변적 고정적 순번 부여
    스토리지 공유 또는 외부 스토리지 중심 Pod별 고정 스토리지 할당 가능
    생성/종료 순서 순서 보장 약함 순차 생성, 순차 종료
    네트워크 식별성 Pod 교체 시 변경 가능 안정적인 DNS 이름 유지
    대표 사용 사례 웹, API, 배치 워커 DB, 분산 저장소, 클러스터형 메시지 시스템

    여기서 독자분들이 가장 헷갈리는 부분이 스토리지입니다. Deployment에도 PVC를 붙일 수는 있습니다. 그래서 겉보기엔 둘 차이가 없어 보일 수 있어요. 근데 StatefulSet은 각 Pod가 자기 볼륨을 안정적으로 유지해야 하는 구조를 전제로 설계되어 있거든요. 이 차이가 운영 중엔 꽤 크게 작용합니다.

    3-1. 언제 Deployment를 선택하나

    • Pod가 교체돼도 서비스만 유지되면 되는 경우
    • 세션 상태를 외부 Redis나 DB에 저장하는 경우
    • 스케일 아웃/인 빈도가 잦은 경우
    • CI/CD로 잦은 애플리케이션 배포가 필요한 경우

    3-2. 언제 StatefulSet을 선택하나

    • 각 인스턴스마다 고유 ID가 필요한 경우
    • 볼륨을 Pod별로 안정적으로 유지해야 하는 경우
    • 클러스터 합류 순서나 부팅 순서가 중요한 경우
    • 상태 저장 애플리케이션 특성상 재시작 후에도 동일한 엔드포인트가 필요한 경우

    실제로 써보니까, 애매하면 먼저 데이터의 소유권이 누구에게 붙는지를 보면 판단이 쉽습니다. 데이터가 서비스 전체에 느슨하게 연결되면 Deployment 쪽, 데이터가 특정 인스턴스에 강하게 묶이면 StatefulSet 쪽이더라고요.

    4. 실전 구현 1: Deployment로 Stateless 앱 배포

    먼저 Nginx(엔진엑스, 웹 서버)를 Deployment로 배포해보겠습니다. 이 예제는 구조를 이해하기 위한 가장 기본적인 형태입니다.

    1. Deployment 매니페스트를 작성합니다.
    2. Service(서비스, 네트워크 접근 추상화)로 노출합니다.
    3. 롤링 업데이트와 스케일링을 확인합니다.
    apiVersion: apps/v1
    kind: Deployment
    metadata:
      name: web-deployment
    spec:
      replicas: 3
      selector:
        matchLabels:
          app: web
      template:
        metadata:
          labels:
            app: web
        spec:
          containers:
            - name: nginx
              image: nginx:stable
              ports:
                - containerPort: 80
    ---
    apiVersion: v1
    kind: Service
    metadata:
      name: web-service
    spec:
      selector:
        app: web
      ports:
        - port: 80
          targetPort: 80
      type: ClusterIP
    kubectl apply -f web-deployment.yaml
    kubectl get deploy,pods,svc -o wide

    이 상태에서 Pod 하나가 내려가도 새 Pod가 올라오면 됩니다. 이름이 바뀌어도 크게 문제되지 않죠. 바로 이 특성이 Deployment의 장점입니다.

    업데이트도 간단합니다.

    kubectl set image deployment/web-deployment nginx=nginx:latest
    kubectl rollout status deployment/web-deployment

    제가 직접 해보니 테스트 환경이나 프론트엔드 계층은 거의 이 패턴으로 끝나더라고요. 단순하고, 빠르고, 운영 피로도가 낮습니다. 드디어 됐다! 싶은 순간이 자주 오는 쪽이죠.

    Kubernetes StatefulSet vs Deployment 중 Deployment 롤링 업데이트 설명 이미지

    Deployment가 ReplicaSet과 함께 Pod를 순차 교체하는 흐름을 설명하는 이미지입니다.

    5. 실전 구현 2: StatefulSet으로 상태 저장 애플리케이션 배포

    이번엔 StatefulSet 예제를 보겠습니다. 여기서는 이해를 위해 간단한 Nginx 이미지를 쓰되, 핵심은 고정된 Pod 이름과 volumeClaimTemplates 구조를 보는 데 있습니다. 실제 운영에서는 MySQL, PostgreSQL, Redis 클러스터, RabbitMQ 같은 워크로드에서 더 의미가 커요.

    StatefulSet은 보통 Headless Service(헤드리스 서비스, 개별 Pod 식별을 위한 서비스)와 함께 사용합니다.

    apiVersion: v1
    kind: Service
    metadata:
      name: web-headless
    spec:
      clusterIP: None
      selector:
        app: web-stateful
      ports:
        - port: 80
          name: http
    ---
    apiVersion: apps/v1
    kind: StatefulSet
    metadata:
      name: web-stateful
    spec:
      serviceName: web-headless
      replicas: 3
      selector:
        matchLabels:
          app: web-stateful
      template:
        metadata:
          labels:
            app: web-stateful
        spec:
          containers:
            - name: nginx
              image: nginx:stable
              ports:
                - containerPort: 80
                  name: http
              volumeMounts:
                - name: web-data
                  mountPath: /usr/share/nginx/html
      volumeClaimTemplates:
        - metadata:
            name: web-data
          spec:
            accessModes: ["ReadWriteOnce"]
            resources:
              requests:
                storage: 1Gi
    kubectl apply -f web-statefulset.yaml
    kubectl get statefulset,pods,pvc,svc

    적용 후 보면 Pod가 이런 식으로 생성됩니다.

    • web-stateful-0
    • web-stateful-1
    • web-stateful-2

    그리고 PVC도 Pod별로 따로 붙습니다. 이게 진짜 중요합니다. 예를 들어 web-data-web-stateful-0 같은 식으로 각 인스턴스가 자기 스토리지를 계속 들고 가거든요.

    여기서 중요한 포인트! StatefulSet은 삭제 후 다시 생성되어도 같은 이름 규칙과 볼륨 연결을 유지하는 데 초점이 있습니다. 이 특성 덕분에 상태 저장 애플리케이션 운영이 가능해집니다.

    kubectl delete pod web-stateful-1
    kubectl get pods -w

    이렇게 해보면 동일한 ordinal을 가진 Pod가 다시 올라옵니다. 제가 홈랩에서 PostgreSQL 실험할 때도 이 패턴 덕분에 노드 교체 후 구조를 이해하기 쉬웠습니다. 물론 데이터 정합성은 애플리케이션 레벨에서 별도로 봐야 하지만요.

    6. ⚠️ 실제로 많이 겪는 문제와 트러블슈팅

    6-1. Deployment에 데이터베이스를 올리고 나중에 후회하는 경우

    이거 진짜 자주 봅니다. 처음엔 빠르게 띄우려고 Deployment로 시작하거든요. 근데 운영 중에 Pod가 교체되고, 스토리지 붙는 방식이 예상과 다르면 문제를 마주하게 됩니다.

    • Pod 이름이 바뀌어 클러스터 노드 인식이 꼬임
    • 단일 PVC 공유 구조가 애플리케이션 특성과 맞지 않음
    • 복제본 간 데이터 소유권이 불명확해짐

    해결 방향: 데이터가 인스턴스별로 귀속되는 구조라면 StatefulSet으로 전환을 검토해야 합니다.

    6-2. Headless Service를 빼먹는 경우

    저도 처음엔 왜 서비스가 꼭 필요하지? 했었는데, StatefulSet에서 안정적인 네트워크 식별성을 얻으려면 Headless Service가 사실상 핵심이거든요.

    kubectl get svc web-headless
    kubectl describe statefulset web-stateful

    Pod 간 통신이나 클러스터 초기화가 필요한 앱이라면 이 부분을 꼭 확인하세요.

    6-3. PVC가 남는 걸 보고 당황하는 경우

    StatefulSet을 줄였는데 볼륨이 바로 안 지워져서 당황하는 경우가 있습니다. 근데 이건 오히려 안전장치에 가깝습니다. 실수로 데이터가 날아가면 더 큰일이거든요.

    주의: StatefulSet 삭제가 곧 데이터 삭제를 의미하지는 않습니다. 스토리지 정책과 reclaim policy를 같이 확인해야 합니다.

    6-4. 순서 의존성을 무시하고 병렬처럼 다루는 경우

    StatefulSet은 생성과 종료 순서가 의미가 있습니다. 특히 클러스터형 데이터베이스나 합의 기반 시스템은 더 그렇습니다. 그래서 readiness probe, startup probe 같은 헬스체크도 같이 설계해야 하죠. 저도 이걸 대충 봤다가 부팅 순서 꼬여서 한참 로그만 들여다봤습니다 ㅎㅎ

    Kubernetes StatefulSet vs Deployment 중 StatefulSet 스토리지 구조 이미지

    StatefulSet에서 각 Pod가 독립적인 영구 볼륨과 DNS 이름을 갖는 구조를 설명하는 이미지입니다.

    7. 검증 방법: 내가 만든 배포가 의도대로 동작하는지 확인

    설정이 끝났으면 꼭 검증해야 합니다. YAML만 맞다고 끝이 아니더라고요. 실제로 재시작, 스케일링, 이름 유지 여부를 봐야 합니다.

    7-1. Deployment 검증

    kubectl get deployment web-deployment
    kubectl rollout history deployment/web-deployment
    kubectl scale deployment web-deployment --replicas=5
    kubectl get pods -l app=web
    • Pod 수가 바로 조정되는지
    • 업데이트 중 서비스 중단이 없는지
    • 새 Pod 이름이 생성되어도 서비스 접근이 유지되는지

    7-2. StatefulSet 검증

    kubectl get statefulset web-stateful
    kubectl get pods -l app=web-stateful
    kubectl get pvc
    kubectl scale statefulset web-stateful --replicas=2
    kubectl get pods,pvc
    • Pod가 역순으로 종료되는지
    • 줄였다가 다시 늘렸을 때 ordinal이 유지되는지
    • PVC가 각 Pod에 맞게 보존되는지

    🎉 여기서 원하는 대로 동작하면 거의 감이 옵니다. Deployment는 교체 가능한 인스턴스 관리에 최적화되어 있고, StatefulSet은 상태와 순서를 존중하는 구조라는 점이 실제 결과에서 드러납니다.

    Kubernetes StatefulSet vs Deployment 검증 결과 대시보드 이미지

    배포 후 Pod, PVC, 롤아웃 상태를 검증하는 운영 화면 느낌의 이미지입니다.

    8. 자주 묻는 질문과 정리

    8-1. Kubernetes StatefulSet vs Deployment: PVC를 붙이면 차이가 없나요?

    비슷해 보일 수는 있지만 다릅니다. 핵심은 Pod별 고정 정체성과 순서 보장입니다. PVC 하나 붙였다고 StatefulSet의 운영 특성이 생기지는 않습니다.

    8-2. 모든 데이터베이스는 무조건 StatefulSet인가요?

    대체로 그렇지만, 실제 운영 구조에 따라 외부 매니지드 데이터베이스를 쓰면 Kubernetes 안에 직접 올리지 않을 수도 있습니다. 즉, 워크로드 선택은 애플리케이션 구조 전체를 봐야 합니다.

    8-3. 캐시는 어떤 걸 써야 하나요?

    단순 캐시, 세션 캐시처럼 날아가도 되는 구조는 Deployment가 편합니다. 반면 복제, 영속성, 노드 식별이 중요해지면 StatefulSet 쪽을 봐야 합니다.

    8-4. 결국 어떤 기준으로 결정하면 되나요?

    1. Pod가 바뀌어도 되는가?
    2. 각 인스턴스가 자기 데이터를 가져야 하는가?
    3. 이름과 네트워크 식별성이 유지되어야 하는가?
    4. 생성/종료 순서가 중요한가?

    이 네 가지에 하나라도 강하게 해당되면 StatefulSet을 우선 검토해보세요.

    정리해보겠습니다.

    • Deployment: 빠르고 유연한 애플리케이션 배포, stateless 워크로드에 적합
    • StatefulSet: 상태 저장 애플리케이션, 고정 이름, 고정 스토리지, 순서 제어에 적합

    저도 처음엔 Kubernetes StatefulSet vs Deployment를 너무 단순하게 봤었는데, 실제로 운영해보니까 선택이 잘못되면 나중에 구조를 다시 뜯어고쳐야 하더라고요. 특히 홈랩처럼 이것저것 실험하는 환경에서는 처음부터 정답을 맞히기 어렵습니다. 그래서 더더욱 워크로드의 상태 특성을 먼저 보는 습관이 중요합니다.

    💡 팁 하나 남기자면, 처음 설계할 때는 “이 Pod가 내일 사라져도 괜찮은가?”를 스스로에게 물어보세요. 괜찮으면 Deployment일 가능성이 높고, 안 괜찮으면 StatefulSet을 봐야 합니다.

    마지막으로, 다음 글에서는 StatefulSet 기반 데이터베이스를 백업/복구 관점에서 어떻게 설계하면 좋은지 다뤄보겠습니다. 그 글까지 같이 보시면 Kubernetes 워크로드 선택 감이 훨씬 또렷해지실 겁니다.

    어떤 워크로드에 Deployment를 쓰고, 어떤 경우 StatefulSet을 써야 하는지 요약한 비교 이미지입니다.

  • [k8s] 쿠버네티스 스토리지: CSI 드라이버를 활용한 영구 스토리지 관리 및 최적화

    [k8s] 쿠버네티스 스토리지: CSI 드라이버를 활용한 영구 스토리지 관리 및 최적화

    안녕하세요, 13년차의 서버실입니다!

    저는 인프라 엔지니어로 일하면서 수많은 서버실을 드나들었고, 홈랩에서도 다양한 기술을 직접 실험해보고 있거든요. 특히 쿠버네티스(Kubernetes)를 운영하면서 가장 머리 아팠던 부분 중 하나가 바로 스토리지(Storage) 문제였습니다.

    컨테이너는 Stateless(무상태)여야 한다지만, 실제 애플리케이션은 데이터가 필요하잖아요? DB나 파일 서버 같은 것들 말이죠. 컨테이너가 죽거나 재시작하면 데이터가 홀라당 날아가 버리는 경험… 혹시 해보셨나요? 제가 그랬거든요, 처음엔. 😭

    그래서 오늘은 쿠버네티스 환경에서 데이터를 안전하게 보관하고 관리하는 핵심 기술인 CSI (Container Storage Interface) 드라이버를 활용한 영구 스토리지(Persistent Storage) 관리 및 최적화 방법에 대해 제 경험을 바탕으로 솔직하게 이야기해보려고 합니다. 💡

    쿠버네티스에서 스토리지가 어떻게 연결되는지 전반적인 아키텍처를 보여주는 다이어그램입니다. CSI 드라이버가 핵심 역할을 하는 것을 볼 수 있습니다.

    1. 쿠버네티스 영구 스토리지, 왜 필요하고 어떻게 작동하나요?

    쿠버네티스에서 애플리케이션이 데이터를 영구적으로 저장하려면 몇 가지 핵심 개념을 알아야 합니다. 쉽게 말해, 컨테이너는 휘발성(Ephemeral)이라서 데이터를 저장해도 컨테이너가 사라지면 데이터도 같이 사라져요. 그래서 컨테이너 외부에 데이터를 안전하게 보관할 수 있는 공간이 필요한데, 이걸 영구 스토리지(Persistent Storage)라고 부릅니다.

    PersistentVolume (PV, 영구 볼륨): 물리적인 스토리지 자원

    PV는 실제 물리적인 스토리지 공간, 예를 들면 네트워크 파일 시스템(NFS)의 특정 디렉터리나 클라우드 제공자의 디스크(AWS EBS, Google Persistent Disk 등)를 추상화한 겁니다. 클러스터 관리자가 미리 정의해두죠. 마치 서버실의 빈 하드디스크 같은 개념이랄까요? PV는 특정 스토리지 솔루션과 연결되어 실제 데이터를 저장하는 역할을 합니다.

    PersistentVolumeClaim (PVC, 영구 볼륨 요청): 애플리케이션의 스토리지 요구

    PVC는 Pod(파드)가 사용할 스토리지의 “요구 사항”을 선언하는 겁니다. “나는 10GiB(기가바이트) 용량의 ReadWriteOnce(한 번에 한 Pod만 쓰기 가능) 모드의 스토리지가 필요해!” 하고 요청하는 거죠. 사용자가 빈 하드디스크를 “내 거”라고 찜하는 것과 비슷합니다. Pod는 PV에 직접 접근하는 대신 PVC를 통해 스토리지를 요청하고 사용합니다.

    StorageClass (스토리지 클래스): 스토리지 프로비저닝 자동화

    여기서부터 좀 더 편리해집니다. StorageClass는 스토리지의 “종류”를 정의하는 템플릿이라고 보시면 돼요. 예를 들어, “빠른 SSD 스토리지”, “저렴한 HDD 스토리지”, “백업용 스토리지” 같은 거죠. StorageClass를 사용하면 PVC가 요청할 때 PV를 자동으로 생성(Dynamic Provisioning)해줍니다. 제가 처음엔 PV를 일일이 만들었었는데, StorageClass 덕분에 삽질을 훨씬 줄일 수 있었어요. 이거 진짜 편하더라고요! ✅

    StorageClass는 어떤 CSI 드라이버를 사용할지, 어떤 볼륨 타입(SSD/HDD), 회수 정책(reclaim policy) 등을 정의합니다.

    CSI (Container Storage Interface) 드라이버: 쿠버네티스와 스토리지 연결 고리

    자, 오늘의 주인공입니다! CSI 드라이버는 쿠버네티스가 다양한 외부 스토리지 시스템(NFS, Ceph, AWS EBS, OpenEBS 등)과 통신할 수 있도록 표준화된 인터페이스를 제공합니다. 스토리지 벤더들은 이 CSI 표준에 맞춰 드라이버를 개발하고, 쿠버네티스는 이 드라이버를 통해 어떤 스토리지든 일관된 방식으로 사용할 수 있게 되는 거죠. 마치 USB 포트에 어떤 장치를 꽂아도 표준 드라이버 덕분에 작동하는 것과 비슷하다고 보시면 됩니다. 덕분에 쿠버네티스가 특정 스토리지 솔루션에 종속되지 않고 유연하게 확장될 수 있더라고요. 💡

    2. CSI 드라이버를 활용한 영구 스토리지 설정하기 (NFS CSI 드라이버 예시)

    제가 홈랩에서 가장 많이 쓰는 방식 중 하나가 바로 NFS (Network File System)를 이용한 영구 스토리지입니다. 간편하고 설정하기도 쉬워서 처음 시작하는 분들께도 추천드려요. 여기서는 NFS CSI 드라이버를 예시로 들어볼게요.

    (⚠️ 사전에 NFS 서버가 구성되어 있고, 쿠버네티스 클러스터에서 접근 가능해야 합니다.)

    2.1. NFS CSI 드라이버 배포

    먼저 NFS CSI 드라이버를 쿠버네티스 클러스터에 배포해야 합니다. 보통 Helm 차트나 manifests 파일을 통해 배포하죠. 저는 안정적인 버전의 Helm 차트를 선호하는 편입니다.

    저는 보통 프로젝트 공식 저장소에서 제공하는 Helm 차트 가이드를 따라 설치합니다. Kubernetes-CSI 프로젝트의 NFS CSI 드라이버를 설치하는 방법을 보여드릴게요.

    
    helm repo add csi-driver-nfs https://kubernetes-csi.github.io/csi-driver-nfs
    helm repo update
    helm install csi-driver-nfs csi-driver-nfs/csi-driver-nfs --namespace kube-system
    

    배포가 완료되면, 다음과 같이 CSI 드라이버 관련 Pod들이 잘 올라왔는지 확인해볼 수 있습니다.

    
    kubectl get pods -n kube-system -l app.kubernetes.io/name=csi-driver-nfs
    

    2.2. StorageClass 생성

    이제 이 CSI 드라이버를 사용할 StorageClass를 정의합니다. NFS 서버의 주소와 공유할 경로를 지정해줘야 해요.

    
    # nfs-storageclass.yaml
    apiVersion: storage.k8s.io/v1
    kind: StorageClass
    metadata:
      name: nfs-csi-storage
    provisioner: nfs.csi.k8s.io # CSI 드라이버의 이름
    parameters:
      server: 192.168.1.100 # 여러분의 NFS 서버 IP 주소로 변경하세요!
      share: /mnt/nfs_share # NFS 서버의 공유 경로로 변경하세요!
    reclaimPolicy: Delete # Pod 삭제 시 PV도 함께 삭제 (Retain으로 하면 수동 삭제 필요)
    volumeBindingMode: Immediate
    mountOptions:
      - hard
      - nfsvers=4.1
    

    이 파일을 적용하면 <code>nfs-csi-storage라는 이름의 StorageClass가 생성됩니다.

    
    kubectl apply -f nfs-storageclass.yaml
    

    StorageClass를 정의하는 YAML 파일의 예시입니다. NFS CSI 드라이버를 이용해 스토리지 클래스를 생성하는 과정을 보여줍니다.

    2.3. PersistentVolumeClaim (PVC) 생성

    이제 애플리케이션이 스토리지 1GiB를 요청할 PVC를 만들어봅시다.

    
    # my-pvc.yaml
    apiVersion: v1
    kind: PersistentVolumeClaim
    metadata:
      name: my-nfs-pvc
    spec:
      accessModes:
        - ReadWriteOnce # 한 Pod만 읽기/쓰기 가능
      storageClassName: nfs-csi-storage # 위에서 생성한 StorageClass 이름 지정
      resources:
        requests:
          storage: 1Gi # 1 기가바이트 스토리지 요청
    

    적용하면 자동으로 PV가 프로비저닝됩니다. 정말 편해졌죠?

    
    kubectl apply -f my-pvc.yaml
    kubectl get pvc my-nfs-pvc
    kubectl get pv
    

    PVC가 Bound 상태가 되고, 그에 맞는 PV가 생성된 것을 확인할 수 있을 겁니다. 🎉

    2.4. Pod에서 PVC 사용

    마지막으로, 이 PVC를 사용할 Pod를 생성합니다. 간단한 Nginx Pod를 예시로 들어볼게요.

    
    # nginx-pod-with-pvc.yaml
    apiVersion: v1
    kind: Pod
    metadata:
      name: nginx-with-nfs
    spec:
      containers:
        - name: nginx
          image: nginx:latest
          ports:
            - containerPort: 80
          volumeMounts:
            - name: nfs-storage
              mountPath: /usr/share/nginx/html # Nginx의 웹 루트에 마운트
      volumes:
        - name: nfs-storage
          persistentVolumeClaim:
            claimName: my-nfs-pvc # 위에서 생성한 PVC 이름 지정
    

    이제 이 Pod를 배포하고, /usr/share/nginx/html 경로에 파일을 생성해보세요. Pod가 재시작되어도 데이터가 유지되는 것을 확인할 수 있을 겁니다!

    
    kubectl apply -f nginx-pod-with-pvc.yaml
    kubectl exec -it nginx-with-nfs -- bash
    echo "Hello from NFS CSI!" > /usr/share/nginx/html/index.html
    exit
    # Pod를 삭제했다가 다시 만들어도 데이터가 유지되는지 확인해보세요.
    kubectl delete pod nginx-with-nfs
    kubectl apply -f nginx-pod-with-pvc.yaml
    kubectl exec -it nginx-with-nfs -- cat /usr/share/nginx/html/index.html
    

    정상적으로 “Hello from NFS CSI!”라는 문구가 보인다면 성공입니다! 드디어 됐다! 🎉

    3. 삽질 경험담: CSI 드라이버 사용 시 주의사항과 트러블슈팅

    제가 이 과정을 거치면서 몇 번이고 머리를 쥐어뜯었던 경험이 있습니다. 여러분은 저 같은 삽질을 하지 마시라고 몇 가지 팁을 드릴게요.

    3.1. ⚠️ Access Modes (접근 모드) 이해하기

    PVC를 만들 때 accessModes를 지정하는데, 이게 꽤 중요합니다.

    • ReadWriteOnce (RWO): 단일 Pod만 읽기/쓰기 가능. (가장 흔함)
    • ReadOnlyMany (ROX): 여러 Pod가 읽기만 가능.
    • ReadWriteMany (RWX): 여러 Pod가 읽기/쓰기 가능. (NFS 같은 공유 파일 시스템에서 주로 사용)

    만약 RWX가 필요한데 RWO로 설정하면, 다른 Pod에서 접근이 안 돼서 문제가 생길 수 있습니다. 특히 클라우드 볼륨(EBS 같은)은 대부분 RWO만 지원하므로, RWX가 필요하면 NFS나 CephFS 같은 공유 파일 시스템 기반 CSI 드라이버를 고려해야 해요. 제가 이 부분에서 많이 헷갈렸었죠.

    3.2. ⚠️ Reclaim Policy (회수 정책) 신중하게 설정하기

    StorageClass에 reclaimPolicy를 Delete로 설정하면, PVC가 삭제될 때 연결된 PV와 실제 스토리지 볼륨도 함께 삭제됩니다. 개발 환경에서는 편하지만, 실제 운영 환경에서는 데이터가 날아갈 수 있으니 주의해야 합니다!

    저는 처음에 이걸 모르고 테스트용 DB를 날려먹을 뻔했어요… 다행히 백업이 있었지만요. 😅

    운영 환경에서는 Retain으로 설정해서 PVC 삭제 시 PV만 삭제하고, 실제 볼륨은 수동으로 관리하는 것을 고려해봐야 합니다. 아니면 백업 정책을 철저히 해야겠죠.

    3.3. 네트워크 연결 문제 (NFS 예시)

    NFS CSI 드라이버를 사용할 경우, 쿠버네티스 노드들이 NFS 서버에 접근할 수 있는지 확인해야 합니다. 방화벽 문제나 네트워크 경로 설정 문제로 연결이 안 되는 경우가 많거든요. showmount -e [NFS 서버 IP]나 mount 명령어로 직접 마운트 테스트를 해보는 것이 가장 확실합니다.

    3.4. CSI 드라이버 설치 버전 호환성

    가끔 CSI 드라이버 버전과 쿠버네티스 클러스터 버전 간의 호환성 문제로 말썽을 일으킬 때가 있습니다. CSI 드라이버의 공식 문서를 참조하여 호환되는 버전을 사용하는 것이 중요해요. 최신 버전이 무조건 좋은 건 아니더라고요.

    4. CSI 드라이버를 통한 영구 스토리지 관리의 성과

    이렇게 CSI 드라이버를 통해 영구 스토리지를 구성하고 나면, 다음과 같은 이점을 얻을 수 있습니다.

    • 데이터 영속성 (Data Persistence) 확보: Pod가 죽거나 재시작되어도 데이터는 안전하게 유지됩니다. DB나 상태를 가지는 애플리케이션 운영에 필수적이죠.
    • 스토리지 관리의 유연성 (Flexibility): 특정 스토리지 벤더에 종속되지 않고, 다양한 스토리지 솔루션을 쿠버네티스에서 일관된 방식으로 사용할 수 있습니다.
    • 운영 효율성 (Operational Efficiency) 증대: StorageClass를 통한 동적 프로비저닝(Dynamic Provisioning) 덕분에 스토리지 할당 및 관리가 자동화되어 운영 부담이 크게 줄어듭니다. 제가 직접 PV를 일일이 만들던 시절을 생각하면 정말 격세지감이죠. 😮
    • 확장성 (Scalability): 필요한 만큼 스토리지를 손쉽게 확장하거나 축소할 수 있게 됩니다.

    실제로 제가 운영하는 서비스에서도 CSI 드라이버 덕분에 안정적으로 데이터를 관리하고, 필요에 따라 스토리지를 유연하게 변경하거나 확장할 수 있게 되었어요. K8s 스토리지 최적화의 첫걸음이라고 할 수 있습니다.

    쿠버네티스 클러스터에서 PV, PVC, StorageClass 등의 스토리지 리소스 상태를 모니터링하는 대시보드의 예시입니다.

    5. 마무리하며: 쿠버네티스 스토리지, CSI 드라이버로 마스터하기

    오늘은 쿠버네티스 환경에서 영구 스토리지를 관리하고 최적화하는 데 필수적인 CSI 드라이버에 대해 제 경험을 바탕으로 이야기해보았습니다. PersistentVolume(PV), PersistentVolumeClaim(PVC), StorageClass, 그리고 CSI 드라이버라는 핵심 개념들이 처음에는 복잡하게 느껴질 수 있지만, 몇 번 직접 구성해보면 금방 익숙해지실 거예요.

    특히 StorageClass와 CSI 드라이버를 활용한 동적 프로비저닝은 쿠버네티스 운영의 편의성을 극대화시켜주는 정말 강력한 기능이라고 생각합니다. 저의 삽질 경험담이 여러분의 K8s 스토리지 여정에 작은 도움이 되었으면 좋겠네요. 🤝

    다음번에는 CephFS나 Rook-Ceph 같은 분산 스토리지 솔루션을 CSI 드라이버와 함께 사용하는 방법에 대해서도 다뤄볼 기회가 있었으면 좋겠습니다. 궁금한 점이나 공유하고 싶은 경험이 있다면 댓글로 남겨주세요! 저는 13년차의 서버실이었습니다. 감사합니다!

    다양한 쿠버네티스 영구 스토리지 솔루션(NFS, Ceph, 클라우드 볼륨 등)의 특징과 장단점을 비교하는 인포그래픽입니다.