13년차의 서버실

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

[태그:] 영구 스토리지

  • [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, 클라우드 볼륨 등)의 특징과 장단점을 비교하는 인포그래픽입니다.

  • [k8s] Longhorn 쿠버네티스 영구 스토리지 완벽 가이드: 설치부터 PV/PVC까지

    [k8s] Longhorn 쿠버네티스 영구 스토리지 완벽 가이드: 설치부터 PV/PVC까지

    목차

    파드가 죽으면 데이터도 같이 사라진다고요? 😱

    쿠버네티스 공부하다가 처음으로 맞닥뜨리는 벽 중 하나가 바로 스토리지(Storage) 문제거든요. 저도 처음에 홈랩에 k3s 클러스터 꾸려놓고 MySQL 파드 띄웠다가, 파드 재시작하니까 데이터가 싹 날아간 경험을 했었는데요. 그때 진짜 멘붕이었죠 ㅎㅎ

    쿠버네티스에서 파드(Pod)는 기본적으로 ephemeral(일시적인) 존재입니다. 파드가 죽고 다시 살아나면 컨테이너 내부 파일시스템은 초기화돼요. 그래서 데이터베이스나 파일 서버처럼 상태를 유지해야 하는 워크로드에는 반드시 영구 스토리지(Persistent Storage)가 필요하죠.

    근데 쿠버네티스 스토리지 설정이 처음엔 진짜 복잡하게 느껴지거든요. PV, PVC, StorageClass… 용어만 해도 헷갈리는데, 여기에 분산 스토리지까지 얹으면 더 막막하죠. 오늘은 제가 홈랩에서 직접 운영하면서 가장 만족스럽게 쓰고 있는 Longhorn을 소개해드리려고 합니다. Longhorn 설치부터 PV/PVC 관리까지 한 번에 정리해볼게요.

    Longhorn 분산 스토리지 쿠버네티스 아키텍처 다이어그램

    ▲ Longhorn의 전체 아키텍처: 쿠버네티스 클러스터 내에서 각 노드의 디스크를 묶어 분산 스토리지를 구성하는 방식을 보여줍니다.

    Longhorn이 뭔가요? — 쉽게 말하면 이런 겁니다

    Longhorn 핵심 개념 정리

    Longhorn은 CNCF(Cloud Native Computing Foundation) 프로젝트로 편입된 쿠버네티스 전용 분산 블록 스토리지(Distributed Block Storage) 솔루션이에요. Rancher Labs(현 SUSE)에서 만들었고, 오픈소스로 무료로 사용할 수 있습니다.

    쉽게 말해서, 클러스터에 있는 여러 노드의 디스크를 하나로 묶어서 마치 네트워크 스토리지처럼 쓸 수 있게 해주는 거예요. 그리고 데이터를 여러 노드에 복제(Replication)해서 노드 하나가 죽어도 데이터가 안전하게 보존되죠.

    제가 Longhorn을 선택한 이유는 딱 세 가지였어요:

    • 💡 웹 UI가 있다 — 대시보드에서 볼륨 상태를 한눈에 볼 수 있어요. 이게 진짜 편하더라고요.
    • 💡 설치가 간단하다 — Helm 차트나 kubectl 한 방으로 설치 가능
    • 💡 스냅샷/백업 기능 — Longhorn 볼륨 스냅샷이나 S3 백업 연동이 기본 탑재

    PV, PVC, StorageClass — 헷갈리는 개념 한번에 정리

    저도 처음엔 이 세 개 개념이 너무 헷갈렸는데요. 비유로 설명하면 이해가 쉬워요.

    개념 풀네임 비유 설명
    PV PersistentVolume 실제 창고 공간 관리자가 미리 만들어둔 실제 스토리지 리소스
    PVC PersistentVolumeClaim 창고 사용 신청서 사용자(파드)가 필요한 스토리지를 요청하는 오브젝트
    StorageClass StorageClass 창고 종류/등급 PV를 동적으로 생성하는 방법을 정의한 템플릿

    Longhorn을 설치하면 longhorn이라는 StorageClass가 자동으로 생성되고, PVC를 만들면 그에 맞는 PV가 자동으로 프로비저닝(Dynamic Provisioning)돼요. 직접 PV를 만들 필요가 없어서 진짜 편합니다.

    사전 준비 — 설치 전에 꼭 확인하세요 ⚠️

    노드 요구사항 체크리스트

    Longhorn 설치 전에 반드시 확인해야 할 것들이 있어요. 저도 처음에 이걸 빠뜨려서 삽질을 좀 했거든요 ㅎㅎ

    1. open-iscsi 패키지 설치 — 각 노드에 반드시 필요합니다
    2. NFSv4 클라이언트 — 백업 기능 사용 시 필요
    3. curl, findmnt, grep, awk, blkid, lsblk — 기본 유틸리티 확인
    4. 마운트 전파(Mount Propagation) — 컨테이너 런타임 설정 확인

    Longhorn 공식에서는 사전 체크 스크립트를 제공해줘요. 이걸 먼저 돌려보는 게 좋습니다:

    # Longhorn 환경 체크 스크립트 실행
    curl -sSfL https://raw.githubusercontent.com/longhorn/longhorn/master/scripts/environment_check.sh | bash

    Ubuntu/Debian 계열 노드라면 open-iscsi를 이렇게 설치해요:

    # 각 노드에서 실행 (모든 워커 노드에 적용)
    sudo apt-get update
    sudo apt-get install -y open-iscsi
    sudo systemctl enable iscsid
    sudo systemctl start iscsid
    
    # 상태 확인
    sudo systemctl status iscsid

    RHEL/CentOS 계열이라면:

    sudo yum install -y iscsi-initiator-utils
    sudo systemctl enable iscsid
    sudo systemctl start iscsid

    Longhorn 설치하기 — 3가지 방법

    방법 1: kubectl로 한 방에 설치 (가장 간단)

    가장 빠른 방법이에요. 테스트 환경이나 홈랩이라면 이걸로 충분합니다:

    # Longhorn 설치 (공식 manifest 사용)
    kubectl apply -f https://raw.githubusercontent.com/longhorn/longhorn/v1.6.0/deploy/longhorn.yaml
    
    # 설치 진행 상황 확인
    kubectl get pods --namespace longhorn-system --watch
    
    # 모든 파드가 Running 상태가 될 때까지 기다립니다
    # 보통 2~3분 정도 걸려요

    방법 2: Helm으로 설치 (권장 — 프로덕션 환경)

    커스터마이징이 필요하거나 GitOps로 관리하고 싶다면 Helm이 훨씬 나아요. 저도 실제 환경에서는 Helm을 써요:

    # Helm 레포지토리 추가
    helm repo add longhorn https://charts.longhorn.io
    helm repo update
    
    # longhorn-system 네임스페이스 생성
    kubectl create namespace longhorn-system
    
    # 기본 설치
    helm install longhorn longhorn/longhorn \
      --namespace longhorn-system \
      --set defaultSettings.defaultReplicaCount=2
    
    # 설치 확인
    kubectl -n longhorn-system get pods

    💡 팁: defaultReplicaCount는 볼륨 복제본 개수예요. 노드가 3개 이상이면 3으로 설정하는 게 좋고, 홈랩처럼 노드가 적으면 2로 줄이세요.

    방법 3: values.yaml로 세부 설정

    프로덕션 환경에서는 values 파일로 관리하는 게 좋아요:

    # longhorn-values.yaml
    defaultSettings:
      # 기본 복제본 수
      defaultReplicaCount: 3
      # 스토리지 예약 비율 (각 노드 디스크의 25% 예약)
      storageReservedPercentageForDefaultDisk: 25
      # 백업 타겟 (S3나 NFS 경로)
      # backupTarget: s3://my-bucket@us-east-1/
      
    ingress:
      enabled: true
      host: longhorn.yourdomain.com
      # 인증 설정 (Basic Auth 권장)
      annotations:
        nginx.ingress.kubernetes.io/auth-type: basic
        nginx.ingress.kubernetes.io/auth-secret: basic-auth
    
    persistence:
      # 기본 StorageClass로 설정
      defaultClass: true
      defaultClassReplicaCount: 3
      reclaimPolicy: Retain
    # values 파일로 설치
    helm install longhorn longhorn/longhorn \
      --namespace longhorn-system \
      --values longhorn-values.yaml
    Longhorn 웹 UI 대시보드 볼륨 관리 화면

    ▲ Longhorn 웹 UI 대시보드: 볼륨 상태, 노드별 디스크 사용량, 복제본 상태를 한눈에 확인할 수 있습니다.

    실전: PVC 만들고 파드에 마운트하기

    StorageClass 확인

    설치가 완료됐으면 StorageClass가 잘 생성됐는지 먼저 확인해요:

    kubectl get storageclass
    
    # 출력 예시
    # NAME                 PROVISIONER          RECLAIMPOLICY   VOLUMEBINDINGMODE   ALLOWVOLUMEEXPANSION
    # longhorn (default)   driver.longhorn.io   Delete          Immediate           true

    (default) 표시가 붙어있으면 PVC 만들 때 StorageClass를 따로 지정 안 해도 Longhorn이 자동으로 사용돼요.

    PVC 생성하기

    이제 실제로 PVC를 만들어볼게요. 예시로 MySQL용 스토리지를 만들어보겠습니다:

    # mysql-pvc.yaml
    apiVersion: v1
    kind: PersistentVolumeClaim
    metadata:
      name: mysql-data-pvc
      namespace: default
    spec:
      accessModes:
        - ReadWriteOnce   # RWO: 하나의 노드에서만 읽기/쓰기
      storageClassName: longhorn
      resources:
        requests:
          storage: 10Gi   # 10GB 요청
    # PVC 생성
    kubectl apply -f mysql-pvc.yaml
    
    # PVC 상태 확인
    kubectl get pvc
    
    # 출력 예시
    # NAME             STATUS   VOLUME                                     CAPACITY   ACCESS MODES
    # mysql-data-pvc   Bound    pvc-a1b2c3d4-...                          10Gi       RWO

    STATUS가 Bound로 바뀌면 PV가 자동 생성되고 연결된 거예요. 드디어 됐다! 🎉

    파드에 볼륨 마운트하기

    이제 이 PVC를 실제 파드에 붙여볼게요:

    # mysql-deployment.yaml
    apiVersion: apps/v1
    kind: Deployment
    metadata:
      name: mysql
      namespace: default
    spec:
      replicas: 1
      selector:
        matchLabels:
          app: mysql
      template:
        metadata:
          labels:
            app: mysql
        spec:
          containers:
            - name: mysql
              image: mysql:8.0
              env:
                - name: MYSQL_ROOT_PASSWORD
                  value: "your-password"
              ports:
                - containerPort: 3306
              volumeMounts:
                - name: mysql-storage
                  mountPath: /var/lib/mysql   # 컨테이너 내부 마운트 경로
          volumes:
            - name: mysql-storage
              persistentVolumeClaim:
                claimName: mysql-data-pvc    # 위에서 만든 PVC 이름
    # 배포
    kubectl apply -f mysql-deployment.yaml
    
    # 파드 상태 확인
    kubectl get pods -l app=mysql
    
    # 볼륨 마운트 확인
    kubectl describe pod <파드이름> | grep -A 5 Volumes

    볼륨 확장(Expand)하기

    나중에 스토리지가 부족해지면 PVC를 확장할 수 있어요. 이거 진짜 편한 기능이거든요:

    # PVC 수정으로 볼륨 확장
    kubectl patch pvc mysql-data-pvc -p '{"spec":{"resources":{"requests":{"storage":"20Gi"}}}}'
    
    # 확장 상태 확인
    kubectl get pvc mysql-data-pvc
    
    # Conditions 항목에서 확장 진행 상황 확인
    kubectl describe pvc mysql-data-pvc

    ⚠️ 주의: Longhorn 볼륨 확장은 가능하지만 축소는 지원하지 않습니다. 처음부터 넉넉하게 잡기보다는 필요할 때 늘리는 방식이 좋아요.

    Longhorn 고급 기능 — 스냅샷과 백업

    볼륨 스냅샷 설정

    Longhorn의 숨겨진 킬러 기능 중 하나가 바로 스냅샷이에요. 쿠버네티스 VolumeSnapshot API와 연동해서 사용할 수 있습니다:

    # VolumeSnapshotClass 생성
    apiVersion: snapshot.storage.k8s.io/v1
    kind: VolumeSnapshotClass
    metadata:
      name: longhorn-snapshot-class
    driver: driver.longhorn.io
    deletionPolicy: Delete
    # 스냅샷 생성
    apiVersion: snapshot.storage.k8s.io/v1
    kind: VolumeSnapshot
    metadata:
      name: mysql-snapshot-20240101
    spec:
      volumeSnapshotClassName: longhorn-snapshot-class
      source:
        persistentVolumeClaimName: mysql-data-pvc
    # 스냅샷 확인
    kubectl get volumesnapshot
    
    # 스냅샷에서 PVC 복원
    # source 항목에 dataSource를 지정하면 됩니다

    RecurringJob으로 자동 스냅샷

    Longhorn에는 RecurringJob이라는 기능이 있어서 스냅샷을 자동으로 주기적으로 찍을 수 있어요. 이거 설정해두면 정말 마음이 편해지더라고요:

    # 매일 새벽 2시에 스냅샷, 최대 7개 보관
    apiVersion: longhorn.io/v1beta2
    kind: RecurringJob
    metadata:
      name: daily-snapshot
      namespace: longhorn-system
    spec:
      cron: "0 2 * * *"       # 크론 표현식
      task: snapshot
      groups:
        - default
      retain: 7               # 최대 7개 보관
      concurrency: 2
      labels:
        interval: daily

    ⚠️ 삽질 모음 — 트러블슈팅 가이드

    문제 1: PVC가 Pending 상태에서 안 넘어가요

    이거 정말 자주 겪는 문제예요. 저도 처음에 한참 헤맸거든요.

    # PVC 이벤트 확인
    kubectl describe pvc <pvc-name>
    
    # Longhorn 관련 파드 상태 확인
    kubectl -n longhorn-system get pods
    
    # 특정 파드 로그 확인
    kubectl -n longhorn-system logs -l app=longhorn-manager --tail=50

    대부분의 원인은:

    • 노드에 open-iscsi가 설치 안 됨 → 각 노드에 설치 후 iscsid 서비스 재시작
    • 디스크 여유 공간 부족 → kubectl -n longhorn-system get nodes.longhorn.io로 확인
    • Longhorn 파드 중 하나가 비정상 → 해당 파드 재시작

    문제 2: 볼륨이 Degraded(저하됨) 상태예요

    복제본 중 하나가 정상적으로 동기화 안 됐을 때 나타나요. Longhorn UI에서 확인하면 어느 노드의 복제본이 문제인지 바로 보여서 편합니다.

    # 볼륨 상태 확인
    kubectl -n longhorn-system get volumes.longhorn.io
    
    # 특정 볼륨 상세 확인
    kubectl -n longhorn-system describe volume.longhorn.io <volume-name>

    보통은 자동으로 복구되는데, 안 된다면 UI에서 해당 복제본을 삭제하고 재생성하면 해결돼요.

    문제 3: 노드 추가 후 스토리지가 인식이 안 돼요

    새 노드 추가 후 Longhorn이 디스크를 자동으로 추가하지 않을 때가 있어요. UI의 Node 탭에서 해당 노드 클릭 → Add Disk로 수동 추가하거나, 노드에 node.longhorn.io/create-default-disk: config 어노테이션을 추가하면 돼요.

    Longhorn 볼륨 복제본 분산 및 Degraded 상태 시각화

    ▲ Longhorn 볼륨 복제본 분산 현황: 각 노드에 복제본이 어떻게 분산되어 있는지, Degraded 상태 발생 시 어떤 노드에 문제가 있는지 확인할 수 있습니다.

    ✅ 설치 완료 후 검증하기

    전체 상태 한번에 확인하는 명령어

    # Longhorn 시스템 파드 전체 상태
    kubectl -n longhorn-system get pods
    
    # StorageClass 확인
    kubectl get storageclass
    
    # 현재 PV/PVC 목록
    kubectl get pv,pvc --all-namespaces
    
    # Longhorn 볼륨 목록
    kubectl -n longhorn-system get volumes.longhorn.io
    
    # 노드별 디스크 상태
    kubectl -n longhorn-system get nodes.longhorn.io

    실제 데이터 보존 테스트

    진짜 제대로 되는지 확인하려면 직접 테스트해보는 게 최고예요:

    # 파드에 접속해서 테스트 파일 생성
    kubectl exec -it <mysql-pod-name> -- bash
    # 컨테이너 내부에서
    echo "Longhorn 스토리지 테스트!" > /var/lib/mysql/test.txt
    exit
    
    # 파드 강제 삭제
    kubectl delete pod <mysql-pod-name>
    
    # 새 파드가 자동으로 올라옴
    kubectl get pods -w
    
    # 새 파드에서 파일 확인
    kubectl exec -it <새-mysql-pod-name> -- cat /var/lib/mysql/test.txt
    # "Longhorn 스토리지 테스트!" 가 출력되면 성공! 🎉

    이게 출력되면 진짜 영구 스토리지가 제대로 동작하는 거예요. 처음 이 테스트 통과했을 때 얼마나 뿌듯했던지 ㅎㅎ

    Longhorn vs 다른 쿠버네티스 스토리지 솔루션 비교

    솔루션 특징 장점 단점 추천 환경
    Longhorn 쿠버네티스 전용 분산 블록 스토리지 웹 UI, 쉬운 설치, 스냅샷/백업 성능이 Ceph보단 낮음 홈랩, 중소규모 클러스터
    Rook-Ceph Ceph 기반 엔터프라이즈급 스토리지 높은 성능, 다양한 스토리지 타입 복잡한 설치/운영, 리소스 많이 사용 대규모 프로덕션
    NFS Provisioner NFS 서버 기반 동적 프로비저닝 간단, 기존 NFS 서버 활용 가능 HA 미지원, 성능 한계 소규모, 테스트 환경
    OpenEBS 경량 분산 스토리지 다양한 엔진 선택 가능 엔진별 기능 차이 있음 엣지, 경량 환경

    홈랩이나 중소규모 환경이라면 Longhorn이 가성비 최고라고 생각해요. 운영 복잡도 대비 기능이 충실하거든요.

    쿠버네티스 스토리지 솔루션 Longhorn Rook-Ceph NFS 비교 인포그래픽

    ▲ 쿠버네티스 스토리지 솔루션 비교: Longhorn, Rook-Ceph, NFS Provisioner의 사용 환경과 특성을 한눈에 비교한 인포그래픽입니다.

    자주 묻는 질문 (FAQ)

    Q. 노드가 2개밖에 없는데 Longhorn 써도 되나요?

    A. 네, 됩니다. 다만 defaultReplicaCount를 2로 설정하세요. 노드 수보다 복제본 수가 많으면 Longhorn 볼륨이 Degraded 상태가 됩니다.

    Q. ReadWriteMany(RWX) 지원하나요?

    A. Longhorn v1.1부터 NFS 기반으로 RWX(여러 노드에서 동시 읽기/쓰기)를 지원합니다. 단, 블록 스토리지 기반 RWX보다 성능이 낮을 수 있어요.

    Q. 기존 로컬 PV 데이터를 Longhorn으로 마이그레이션할 수 있나요?

    A. 직접 마이그레이션 기능은 없어요. 보통 애플리케이션 레벨에서 데이터를 백업 후 새 Longhorn PVC에 복원하는 방식을 사용합니다.

    Q. Longhorn UI에 외부에서 접근하려면 어떻게 하나요?

    A. Ingress를 설정하면 돼요. 단, 반드시 인증(Basic Auth 등)을 걸어두세요. 외부에 무방비로 열면 안 됩니다.

    마무리 — 이제 데이터 걱정 없이 쿠버네티스 쓰세요 🎉

    오늘 Longhorn 설치부터 PV/PVC 관리, 스냅샷, 트러블슈팅까지 쭉 훑어봤는데요. 처음 보면 복잡해 보이지만 한번 설치해놓으면 이후 운영은 생각보다 편합니다.

    제가 정리한 핵심 포인트:

    • ✅ 설치 전 open-iscsi 반드시 설치 — 이거 빠뜨리면 PVC가 Pending에서 안 풀림
    • ✅ 복제본 수는 노드 수에 맞게 — 노드 2개면 replica 2, 3개면 3
    • ✅ RecurringJob으로 자동 스냅샷 — 데이터 보험 필수
    • ✅ UI 접근에 인증 설정 — 보안 필수
    • ✅ 볼륨 확장은 가능, 축소는 불가 — 처음부터 너무 크게 잡지 말 것

    다음 글에서는 Longhorn 백업을 S3 호환 오브젝트 스토리지(MinIO)와 연동하는 방법을 다뤄볼 예정이에요. 백업까지 자동화하면 진짜 마음 편하거든요. 이전 글에서 다룬 MetalLB + Ingress 설정과 함께 구성하면 완성도 높은 홈랩 환경이 만들어집니다.

    혹시 설치하다가 막히는 부분 있으면 댓글로 남겨주세요. 제가 겪은 삽질 경험을 바탕으로 같이 해결해봐요! 😄