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] Kubernetes Longhorn 스토리지: 설치부터 운영까지 완벽 가이드

    [k8s] Kubernetes Longhorn 스토리지: 설치부터 운영까지 완벽 가이드

    안녕하세요, 13년차 서버실 지킴이입니다. 🤓

    오늘은 제가 홈랩에서 Kubernetes(쿠버네티스) 클러스터를 운영하면서 겪었던 스토리지 고민과 그 해결책, 바로 Longhorn(롱혼)에 대해 이야기해보려고 합니다. 쿠버네티스에서 컨테이너 애플리케이션을 운영하다 보면, 데이터 영속성(Persistence) 확보가 정말 중요한데요. 특히 분산 스토리지(Distributed Storage)는 고가용성(High Availability)과 데이터 안정성을 위해 필수적이죠. 처음엔 로컬 스토리지를 PersistentVolume(PV)으로 직접 연결해보기도 하고, NFS(Network File System)를 써보기도 했는데, 이게 또 생각보다 관리하기가 만만치 않더라고요.

    그러다 우연히 Longhorn을 알게 되었고, 직접 설치하고 운영해보니 “아, 이거다!” 싶었습니다. 설치도 비교적 간단하고, 웹 UI로 볼륨 관리도 직관적이라 삽질 좀 덜 수 있었거든요. 오늘은 저의 경험을 바탕으로 Longhorn을 처음 접하시는 분들도 쉽게 따라할 수 있도록 설치부터 운영까지 완벽 가이드를 준비해봤습니다. 함께 시작해볼까요?

    쿠버네티스 클러스터 내 롱혼 분산 스토리지 아키텍처 개요

    이미지 캡션: 쿠버네티스 클러스터 내 롱혼 분산 스토리지 아키텍처 개요. 여러 노드에 걸쳐 데이터가 복제되고 관리되는 모습을 보여줍니다.

    💡 Longhorn, 넌 누구냐? (핵심 개념 설명)

    자, 그럼 먼저 Longhorn이 뭔지부터 알아봐야겠죠? Longhorn은 CNCF(Cloud Native Computing Foundation) 프로젝트 중 하나로, 쿠버네티스를 위한 분산 블록 스토리지 시스템입니다. 쉽게 말해, 쿠버네티스 클러스터 내의 여러 노드에 있는 디스크들을 모아서 하나의 거대한 가상 스토리지 풀을 만들고, 이 풀에서 PersistentVolume(영구 볼륨)을 생성하여 파드(Pod)에 제공해주는 역할을 해요.

    • 분산 스토리지 (Distributed Storage): 여러 노드의 저장 공간을 하나로 묶어 사용하며, 데이터가 여러 노드에 복제되어 저장되기 때문에 특정 노드에 문제가 생겨도 데이터 유실 걱정을 덜 수 있습니다.
    • CSI (Container Storage Interface, 컨테이너 스토리지 인터페이스) 호환: 쿠버네티스의 표준 스토리지 인터페이스인 CSI를 완벽하게 지원하기 때문에, 쿠버네티스에서 제공하는 StorageClass(스토리지 클래스), PersistentVolumeClaim(PVC, 영구 볼륨 클레임) 등의 기능을 문제없이 쓸 수 있습니다.
    • 스냅샷 및 백업/복구: 볼륨의 스냅샷을 찍고, S3와 같은 오브젝트 스토리지(Object Storage)나 NFS로 백업 및 복구를 쉽게 할 수 있어요. 제가 써보니 이 기능이 정말 편하더라고요!

    사실 처음엔 이런 분산 스토리지가 복잡하고 어렵게 느껴졌는데, Longhorn은 웹 UI가 워낙 직관적이라 금방 익숙해질 수 있었습니다. 덕분에 홈랩에서 여러 스테이트풀(Stateful) 애플리케이션들을 안정적으로 운영할 수 있게 되었죠. 예를 들어, 제 홈랩의 Prometheus(프로메테우스)나 Grafana(그라파나) 같은 모니터링 툴의 데이터도 Longhorn 볼륨에 저장해서 쓰고 있거든요.

    🛠️ 실전 구현: Longhorn 설치부터 PersistentVolume 사용까지

    이제 본격적으로 Longhorn을 설치하고 사용하는 방법을 알아볼 차례입니다. 저는 Helm(헬름)을 이용해서 설치하는 방법을 선호합니다. 배포가 간편하고 버전 관리가 용이하거든요. 시작하기 전에 몇 가지 준비물이 필요합니다.

    ✅ 준비물 확인

    1. Kubernetes 클러스터: 최소 3개 이상의 노드(Node)를 권장합니다. 각 노드에 충분한 여유 디스크 공간이 필요합니다.
    2. iSCSI initiator 설치: Longhorn은 iSCSI 프로토콜을 사용합니다. 각 노드에 <code>iscsi-initiator-utils (CentOS/RHEL) 또는 open-iscsi (Ubuntu/Debian) 패키지가 설치되어 있어야 합니다. 제가 처음 이걸 몰라서 삽질 좀 했습니다. 꼭 확인하세요!
      
      # CentOS/RHEL 계열
      sudo yum install -y iscsi-initiator-utils
      sudo systemctl enable --now iscsid
      
      # Ubuntu/Debian 계열
      sudo apt update
      sudo apt install -y open-iscsi
      sudo systemctl enable --now iscsid
      
    3. Helm 설치: 클러스터에 Helm이 설치되어 있어야 합니다.

    🚀 Longhorn 설치하기

    Helm을 이용하면 Longhorn 설치는 정말 간단합니다. 다음 명령어를 순서대로 실행해주세요.

    
    # 1. Longhorn Helm 레포지토리 추가
    helm repo add longhorn https://charts.longhorn.io
    helm repo update
    
    # 2. Longhorn 네임스페이스 생성 (선택 사항이지만 권장)
    kubectl create namespace longhorn-system
    
    # 3. Longhorn 설치 (기본 설정으로 충분합니다)
    helm install longhorn longhorn/longhorn --namespace longhorn-system --version 1.5.3 # 버전은 최신 안정 버전을 확인하세요!
    

    설치가 완료되면, 잠시 기다리면 모든 Longhorn 컴포넌트 파드들이 longhorn-system 네임스페이스에 배포되고 실행될 겁니다. kubectl get pods -n longhorn-system 명령으로 확인해보세요. 모든 파드가 Running 상태인지 확인하는 것이 중요합니다.

    🌐 Longhorn UI 접속하기

    Longhorn은 웹 UI를 제공해서 볼륨 상태, 노드 상태 등을 직관적으로 확인할 수 있습니다. UI에 접속하려면 보통 Ingress(인그레스, 외부 트래픽 진입점)를 설정하거나 NodePort(노드포트) 서비스를 생성하는 방법이 있습니다. 저는 간단하게 포트 포워딩(Port Forwarding)으로 접속해보겠습니다.

    
    # Longhorn UI 서비스 찾기
    kubectl get svc -n longhorn-system
    
    # UI 서비스 포트 포워딩 (예: longhorn-frontend 서비스)
    kubectl -n longhorn-system port-forward svc/longhorn-frontend 8080:80
    

    이제 웹 브라우저에서 http://localhost:8080으로 접속하면 Longhorn 대시보드를 볼 수 있습니다. 🎉 드디어 Longhorn의 세상으로 들어온 것을 환영합니다!

    롱혼 웹 UI 대시보드 화면. 클러스터 노드 스토리지 상태 및 볼륨 목록 확인

    이미지 캡션: Longhorn 웹 UI 대시보드 화면. 클러스터 내 노드들의 스토리지 상태와 생성된 볼륨 목록을 한눈에 확인할 수 있습니다.

    ⚙️ StorageClass 생성 및 PersistentVolumeClaim 사용

    Longhorn이 설치되었으니, 이제 쿠버네티스에서 스토리지를 사용할 차례입니다. Longhorn은 기본 StorageClass(스토리지 클래스)를 자동으로 생성하지만, 필요에 따라 커스터마이징할 수 있습니다. 저는 기본 StorageClass를 사용하되, 예시로 PVC를 만들어보겠습니다.

    
    apiVersion: v1
    kind: PersistentVolumeClaim
    metadata:
      name: my-longhorn-pvc
    spec:
      accessModes:
        - ReadWriteOnce # 하나의 파드에서만 읽기/쓰기 가능
      storageClassName: longhorn # Longhorn이 생성한 기본 StorageClass 이름
      resources:
        requests:
          storage: 5Gi # 5GB 볼륨 요청
    

    위 YAML 파일을 pvc.yaml로 저장하고 적용합니다.

    
    kubectl apply -f pvc.yaml
    kubectl get pvc my-longhorn-pvc
    

    PVC가 Pending에서 Bound 상태로 바뀌면 성공입니다. 이제 이 PVC를 사용하는 파드를 배포해봅시다. 간단한 Nginx(엔진엑스) 파드를 예시로 들어보겠습니다.

    
    apiVersion: apps/v1
    kind: Deployment
    metadata:
      name: nginx-with-longhorn
    spec:
      selector:
        matchLabels:
          app: nginx
      replicas: 1
      template:
        metadata:
          labels:
            app: nginx
        spec:
          containers:
            - name: nginx
              image: nginx:latest
              ports:
                - containerPort: 80
              volumeMounts:
                - name: longhorn-volume
                  mountPath: /usr/share/nginx/html # Nginx 웹 페이지 경로
          volumes:
            - name: longhorn-volume
              persistentVolumeClaim:
                claimName: my-longhorn-pvc # 위에서 생성한 PVC 이름
    

    위 YAML 파일을 nginx-deployment.yaml로 저장하고 적용하면, Nginx 파드가 Longhorn 볼륨을 마운트하여 시작됩니다. 이제 /usr/share/nginx/html 경로에 데이터를 써보면, 파드가 재시작되거나 다른 노드로 옮겨가도 데이터가 그대로 유지되는 것을 확인할 수 있습니다. 💡

    ⚠️ 삽질 경험: 주의사항 및 트러블슈팅

    제가 Longhorn을 운영하면서 겪었던 몇 가지 삽질 경험과 주의사항을 공유해드릴게요. 독자분들은 저처럼 고생하지 마시라고요! ㅎㅎ

    • iSCSI initiator 문제: 가장 흔한 문제 중 하나입니다. 노드에 iscsi-initiator-utils나 open-iscsi가 설치되어 있지 않거나, iscsid 서비스가 실행 중이 아니라서 볼륨 마운트가 실패하는 경우가 많습니다. 꼭 설치 여부와 서비스 상태를 확인해주세요!
    • 네트워크 대역폭: Longhorn은 데이터를 여러 노드에 복제하기 때문에, 노드 간 네트워크 대역폭이 충분해야 합니다. 네트워크가 느리면 볼륨 성능 저하로 이어질 수 있습니다. 특히 홈랩 환경에서는 유선 네트워크를 사용하는 것이 좋습니다.
    • 디스크 공간 관리: Longhorn은 노드의 가용 디스크 공간을 사용합니다. 볼륨을 너무 많이 생성하거나 스냅샷을 자주 찍으면 노드의 디스크 공간이 부족해질 수 있습니다. Longhorn UI에서 각 노드의 디스크 사용량을 주기적으로 확인하고, 불필요한 스냅샷이나 백업은 정리하는 습관을 들이는 것이 좋습니다.
    • 노드 드레인(Drain) 시 주의: 쿠버네티스 노드를 유지보수할 때 kubectl drain 명령을 사용하죠. Longhorn 볼륨이 마운트된 파드가 있는 노드를 드레인할 때는 Longhorn이 해당 볼륨을 다른 노드로 안전하게 옮길 수 있도록 충분한 시간을 주어야 합니다. 급하게 드레인하면 볼륨이 Faulted 상태가 될 수도 있더라고요.

    ✅ 검증 및 결과: Longhorn 볼륨 확인하기

    모든 설치와 설정이 끝났으니, 이제 제대로 작동하는지 확인해볼 차례입니다. 저는 Nginx 파드에 접속해서 간단한 HTML 파일을 만들어보고, 파드를 삭제 후 다시 생성하여 데이터 영속성을 확인하는 방법을 즐겨 씁니다.

    
    # Nginx 파드 이름 확인
    kubectl get pods -l app=nginx
    
    # Nginx 파드 내부로 접속하여 파일 생성
    kubectl exec -it <nginx-pod-name> -- bash
    echo "Hello from Longhorn!" > /usr/share/nginx/html/index.html
    exit
    
    # Nginx 서비스 포트 포워딩 (테스트용)
    kubectl port-forward svc/<nginx-service-name> 8080:80 # Nginx 서비스가 없다면 배포해야 합니다.
    
    # 웹 브라우저에서 http://localhost:8080 접속하여 내용 확인
    

    이제 Nginx 파드를 삭제했다가 다시 생성해보세요. 그리고 다시 접속해보면, 이전에 작성했던 “Hello from Longhorn!” 메시지가 그대로 남아있는 것을 확인할 수 있을 겁니다. 🎉 드디어 영구적인 스토리지를 성공적으로 구축한 거죠!

    Nginx 파드에 마운트된 Longhorn 볼륨과 데이터 영속성 확인 화면

    이미지 캡션: Nginx 파드에 성공적으로 마운트된 Longhorn 볼륨과 데이터 영속성을 확인하는 모습. 웹 브라우저를 통해 저장된 내용을 확인하고 있습니다.

    마무리: 13년차 서버실 지킴이의 한마디

    오늘은 Kubernetes Longhorn(쿠버네티스 롱혼) 스토리지의 설치부터 운영까지 제가 직접 경험한 내용을 바탕으로 자세히 설명해드렸습니다. Longhorn은 쿠버네티스 환경에서 영구 볼륨(Persistent Volume)을 안정적이고 유연하게 제공하는 훌륭한 분산 스토리지 솔루션이라고 생각합니다. 특히 홈랩이나 소규모 클러스터 환경에서는 초기 구축 비용이나 복잡성 측면에서 다른 엔터프라이즈 솔루션보다 훨씬 매력적이죠. 저는 Longhorn 덕분에 홈랩에서 다양한 스테이트풀 애플리케이션들을 걱정 없이 운영하고 있습니다. 백업과 스냅샷 기능도 정말 유용하구요!

    물론 Longhorn도 만능은 아닙니다. 대규모 엔터프라이즈 환경에서는 Ceph(세프)나 NetApp(넷앱) 같은 더욱 강력하고 복잡한 솔루션들이 필요할 수도 있습니다. 하지만 “간단하게 시작해서 점진적으로 확장하고 싶다”는 분들에게는 Longhorn이 정말 좋은 선택지가 될 거라고 확신합니다. 제 경험상, 중요한 건 어떤 툴을 쓰느냐보다, 그 툴을 얼마나 잘 이해하고 자신의 환경에 맞게 활용하느냐인 것 같아요. 저도 아직 배워야 할 게 많지만, 계속해서 새로운 기술들을 실험하고 삽질하면서 여러분께 유용한 정보를 공유해드리겠습니다.

    다음 글에서는 Longhorn의 백업 및 복구 기능에 대해 더 자세히 다뤄보거나, Longhorn 볼륨의 성능 튜닝 방법에 대해 이야기해볼까 합니다. 궁금한 점이 있다면 언제든지 댓글로 남겨주세요! 다음에 또 유익한 정보로 찾아뵙겠습니다. 감사합니다! 😊

    Longhorn 주요 특징, 장단점 및 다른 쿠버네티스 스토리지 솔루션 비교 인포그래픽

    이미지 캡션: Longhorn의 주요 특징과 장단점을 요약한 인포그래픽. 쿠버네티스 스토리지 선택에 있어 Longhorn의 위치를 보여줍니다.

  • [k8s] 쿠버네티스 영구 스토리지: Longhorn vs Rook Ceph 비교 및 선택 가이드

    쿠버네티스 영구 스토리지, 왜 이렇게 어렵냐고요

    쿠버네티스(Kubernetes)로 처음 스테이트풀(Stateful) 애플리케이션을 올려본 분들이라면 공감하실 텐데요. 컨테이너는 죽었다 살아나는 게 당연한 세계인데, 데이터는 절대 사라지면 안 되잖아요. 데이터베이스, 메시지 큐, 로그 수집기… 이런 것들을 올리려면 결국 쿠버네티스 영구 스토리지(Persistent Storage) 문제를 해결해야 합니다.

    저도 처음 홈랩에서 쿠버네티스 클러스터를 구성할 때 이 부분에서 꽤 오래 고민했거든요. NFS 마운트로 버티다가 결국 제대로 된 분산 스토리지를 써야겠다 싶어서 Longhorn이랑 Rook Ceph를 둘 다 실제로 구성해봤습니다. 오늘은 그 경험을 바탕으로 두 솔루션을 비교하고, 어떤 상황에서 뭘 선택해야 하는지 정리해 드릴게요.

    ▲ 쿠버네티스 영구 스토리지의 전체 구조 — PVC(Persistent Volume Claim)가 어떻게 실제 스토리지 백엔드와 연결되는지 보여주는 아키텍처 다이어그램

    먼저 개념부터: PV, PVC, CSI가 뭔가요?

    비교 얘기 전에 기본 개념을 짚고 넘어가겠습니다. 저도 처음엔 이 용어들이 다 비슷비슷해 보여서 헷갈렸거든요 ㅎㅎ

    • PV (Persistent Volume, 영구 볼륨): 클러스터 관리자가 프로비저닝한 실제 스토리지 리소스입니다. 쉽게 말해 “실제 저장 공간”이에요.
    • PVC (Persistent Volume Claim, 영구 볼륨 클레임): 개발자(또는 파드)가 스토리지를 요청하는 오브젝트입니다. “나 10GB짜리 저장 공간 필요해요”라고 요청하는 거죠.
    • StorageClass (스토리지 클래스): 스토리지의 종류와 프로비저닝 방식을 정의합니다. PVC가 요청하면 자동으로 PV를 만들어주는 동적 프로비저닝(Dynamic Provisioning)을 가능하게 해줘요.
    • CSI (Container Storage Interface, 컨테이너 스토리지 인터페이스): 쿠버네티스가 다양한 스토리지 벤더와 통신하기 위한 표준 인터페이스입니다. Longhorn도, Rook Ceph도 이 CSI 드라이버를 통해 쿠버네티스와 연동돼요.

    💡 핵심 포인트: 개발자는 PVC만 선언하면 되고, 실제 스토리지가 어디에 있는지는 신경 안 써도 됩니다. 그 복잡한 연결을 StorageClass와 CSI 드라이버가 처리해주거든요.

    Longhorn 소개: CNCF가 인정한 경량 분산 스토리지

    Longhorn은 Rancher(현재 SUSE 산하)에서 개발한 쿠버네티스 전용 분산 블록 스토리지입니다. CNCF(Cloud Native Computing Foundation) 졸업 프로젝트로 등록되어 있고, 오픈소스이면서 완전히 쿠버네티스 네이티브하게 설계됐어요.

    Longhorn의 주요 특징

    • 쿠버네티스 위에서 동작하는 컨트롤러 기반 아키텍처
    • 볼륨 복제본(Replica)을 여러 노드에 자동 분산
    • 내장 UI 대시보드 — 볼륨 상태를 시각적으로 확인 가능
    • 스냅샷(Snapshot) 및 백업 기능 내장 (S3, NFS 등으로 백업 가능)
    • 볼륨 확장(Volume Expansion) 지원
    • RWO(ReadWriteOnce) 지원, RWX(ReadWriteMany)는 NFS 게이트웨이를 통해 제한적으로 지원

    Longhorn 설치 — Helm으로 빠르게

    실제로 설치해보겠습니다. 저는 k3s 기반 3노드 클러스터에서 테스트했어요.

    # 사전 요구사항 확인 (iscsi 패키지 필요)
    sudo apt-get install -y open-iscsi
    sudo systemctl enable --now iscsid
    
    # Helm 저장소 추가
    helm repo add longhorn https://charts.longhorn.io
    helm repo update
    
    # longhorn 네임스페이스 생성 및 설치
    helm install longhorn longhorn/longhorn \
      --namespace longhorn-system \
      --create-namespace \
      --set defaultSettings.defaultReplicaCount=3

    설치가 완료되면 longhorn-system 네임스페이스에 여러 파드가 뜨는 걸 확인할 수 있어요.

    kubectl get pods -n longhorn-system
    # NAME                                        READY   STATUS    RESTARTS
    # longhorn-manager-xxxxx                      1/1     Running   0
    # longhorn-driver-deployer-xxxxx              1/1     Running   0
    # longhorn-ui-xxxxx                           1/1     Running   0
    # engine-image-ei-xxxxx-xxxxx                 1/1     Running   0

    Longhorn UI에 접근하려면 포트 포워딩이나 인그레스(Ingress, 외부 트래픽 진입점)를 설정해야 합니다.

    kubectl port-forward -n longhorn-system svc/longhorn-frontend 8080:80

    브라우저에서 http://localhost:8080으로 접속하면 볼륨 상태, 노드 디스크 현황을 한눈에 볼 수 있는 대시보드가 나옵니다. 이거 처음 봤을 때 진짜 편하다 싶었어요.

    Longhorn StorageClass 및 PVC 생성 예시

    apiVersion: storage.k8s.io/v1
    kind: StorageClass
    metadata:
      name: longhorn
    provisioner: driver.longhorn.io
    allowVolumeExpansion: true
    parameters:
      numberOfReplicas: "3"
      staleReplicaTimeout: "2880"
      fromBackup: ""
      fsType: "ext4"
    ---
    apiVersion: v1
    kind: PersistentVolumeClaim
    metadata:
      name: my-longhorn-pvc
    spec:
      accessModes:
        - ReadWriteOnce
      storageClassName: longhorn
      resources:
        requests:
          storage: 10Gi

    Rook Ceph 소개: 엔터프라이즈급 분산 스토리지

    Rook은 Ceph를 쿠버네티스에서 오퍼레이터(Operator) 패턴으로 운영할 수 있게 해주는 프레임워크입니다. Ceph 자체는 수십 년의 역사를 가진 검증된 분산 스토리지 시스템이고, Rook은 그 복잡한 Ceph를 쿠버네티스 위에서 쉽게(?) 운영하게 해주죠.

    솔직히 말하면 “쉽게”라는 말에 따옴표를 붙인 이유가 있어요. 처음 Rook Ceph 설치할 때 삽질을 꽤 했거든요 ㅎㅎ

    Rook Ceph의 주요 특징

    • 블록 스토리지(RBD), 파일시스템(CephFS), 오브젝트 스토리지(S3 호환 RGW) 모두 지원
    • CephFS를 통한 RWX(ReadWriteMany) 네이티브 지원 — 여러 파드가 동시에 같은 볼륨 마운트 가능
    • PG(Placement Group), CRUSH Map 등 고급 튜닝 옵션
    • 대규모 클러스터에서 검증된 안정성
    • Ceph Dashboard 내장
    • 최소 3개 OSD(Object Storage Daemon) 노드 권장

    Rook Ceph 설치 — 조금 더 복잡합니다

    # Rook 오퍼레이터 설치
    git clone --single-branch --branch v1.13.0 https://github.com/rook/rook.git
    cd rook/deploy/examples
    
    # CRD 및 공통 리소스 먼저 설치
    kubectl create -f crds.yaml
    kubectl create -f common.yaml
    
    # 오퍼레이터 배포
    kubectl create -f operator.yaml
    
    # 오퍼레이터 파드 확인
    kubectl get pods -n rook-ceph
    # NAME                                  READY   STATUS    RESTARTS
    # rook-ceph-operator-xxxxx              1/1     Running   0

    오퍼레이터가 Running 상태가 되면 Ceph 클러스터를 생성합니다. 아래는 기본 클러스터 설정이에요.

    apiVersion: ceph.rook.io/v1
    kind: CephCluster
    metadata:
      name: rook-ceph
      namespace: rook-ceph
    spec:
      cephVersion:
        image: quay.io/ceph/ceph:v18.2.0
      dataDirHostPath: /var/lib/rook
      mon:
        count: 3
        allowMultiplePerNode: false
      mgr:
        count: 2
      dashboard:
        enabled: true
        ssl: true
      storage:
        useAllNodes: true
        useAllDevices: false
        deviceFilter: "^sd[b-z]"
      resources:
        mgr:
          requests:
            cpu: "500m"
            memory: "512Mi"

    ⚠️ 주의사항: deviceFilter 설정이 중요합니다. 잘못 설정하면 OS 디스크까지 Ceph OSD로 사용하려고 할 수 있어요. 저도 이 부분에서 한 번 아찔한 경험을 했습니다. 반드시 데이터 디스크만 지정하세요.

    # Ceph 블록 스토리지 풀 및 StorageClass 생성
    kubectl create -f csi/rbd/storageclass.yaml
    
    # CephFS (RWX 지원) StorageClass 생성
    kubectl create -f filesystem.yaml
    kubectl create -f csi/cephfs/storageclass.yaml

    ▲ Rook Ceph 아키텍처 — MON(모니터), MGR(매니저), OSD(오브젝트 스토리지 데몬)의 구성과 CSI 드라이버를 통한 쿠버네티스 연동 구조

    Longhorn vs Rook Ceph: 직접 써본 비교

    두 솔루션을 실제로 운영해보면서 느낀 점들을 솔직하게 비교해 드릴게요.

    항목 Longhorn Rook Ceph
    설치 난이도 ⭐⭐ (쉬움) ⭐⭐⭐⭐ (복잡함)
    최소 노드 수 1개 (복제본 설정에 따라) 3개 (OSD 최소 3개 권장)
    리소스 사용량 가벼움 무거움 (MON, MGR, OSD 등 다수)
    RWX 지원 제한적 (NFS 게이트웨이 필요) 네이티브 지원 (CephFS)
    오브젝트 스토리지 미지원 지원 (S3 호환 RGW)
    UI/관리 편의성 직관적인 내장 UI Ceph Dashboard (기능 풍부)
    백업 기능 내장 (S3, NFS 백업) 별도 구성 필요
    성숙도/안정성 CNCF 졸업 프로젝트 CNCF 졸업 프로젝트 (Ceph는 업계 표준)
    운영 복잡도 낮음 높음 (PG 튜닝, CRUSH Map 등)
    적합한 규모 소~중형 클러스터 중~대형 클러스터

    실제로 겪은 트러블슈팅 사례들

    Longhorn에서 겪은 문제: 노드 한 대를 재부팅했더니 해당 노드의 복제본이 degraded 상태가 됐어요. 근데 이건 Longhorn이 자동으로 다른 노드에 복제본을 재빌드해주더라고요. 시간이 좀 걸리긴 했지만 데이터 손실은 없었습니다. ✅

    Rook Ceph에서 겪은 문제: OSD 디스크 하나에 파티션이 남아있었는데, Rook이 해당 디스크를 인식 못 하는 문제가 있었어요. 이게 처음엔 왜 안 되는지 몰라서 꽤 삽질했습니다.

    # OSD 디스크 초기화 — 기존 파티션 제거
    sgdisk --zap-all /dev/sdb
    dd if=/dev/zero of=/dev/sdb bs=1M count=100 oflag=direct
    blkdiscard /dev/sdb
    
    # 파티션 확인
    lsblk /dev/sdb

    ⚠️ 경고: 위 명령어는 해당 디스크의 모든 데이터를 완전히 삭제합니다. 반드시 올바른 디스크를 지정했는지 확인하고 실행하세요.

    또 Ceph 클러스터 상태가 HEALTH_WARN으로 뜰 때 원인 파악하는 방법도 알아두면 좋아요.

    # Ceph 상태 확인
    kubectl exec -n rook-ceph -it deploy/rook-ceph-tools -- ceph status
    kubectl exec -n rook-ceph -it deploy/rook-ceph-tools -- ceph health detail
    
    # OSD 상태 확인
    kubectl exec -n rook-ceph -it deploy/rook-ceph-tools -- ceph osd status

    ▲ Longhorn 대시보드 — 볼륨 상태, 노드별 복제본 분산 현황, 백업 상태를 한눈에 모니터링하는 화면

    어떤 상황에서 뭘 선택해야 할까요?

    이게 결국 핵심 질문이잖아요. 저는 이렇게 기준을 잡아드리고 싶어요.

    Longhorn을 선택하세요, 만약…

    • ✅ 홈랩이나 소규모 클러스터(노드 3~5개 이하)를 운영한다면
    • ✅ 쿠버네티스 스토리지를 처음 도입하는 팀이라면
    • ✅ 운영 인력이 부족하고 관리 부담을 최소화하고 싶다면
    • ✅ 블록 스토리지(RWO) 위주로만 사용한다면
    • ✅ 백업/복구 기능을 간편하게 쓰고 싶다면
    • ✅ 빠르게 구성해서 바로 써야 하는 상황이라면

    Rook Ceph를 선택하세요, 만약…

    • ✅ 중대형 클러스터(노드 5개 이상)를 운영한다면
    • ✅ RWX(ReadWriteMany) 볼륨이 반드시 필요하다면
    • ✅ S3 호환 오브젝트 스토리지도 함께 필요하다면
    • ✅ 전담 인프라 운영 인력이 있다면
    • ✅ 엔터프라이즈 환경에서 검증된 기술 스택이 필요하다면
    • ✅ 고가용성과 세밀한 스토리지 정책 제어가 필요하다면

    둘 다 쓰는 경우도 있어요

    실제로 일부 팀에서는 일반적인 파드 스토리지는 Longhorn으로, RWX가 필요한 공유 파일시스템이나 오브젝트 스토리지는 Rook Ceph로 나눠서 운영하기도 합니다. 복잡해지긴 하지만 각각의 장점을 활용할 수 있는 방법이기도 해요.

    결과 검증: 실제로 제대로 동작하는지 확인하기

    설치만 하면 끝이 아니죠. 실제로 데이터가 잘 보존되는지 확인해봐야 합니다. 아래는 간단한 테스트 시나리오예요.

    # 테스트용 파드 생성
    apiVersion: v1
    kind: Pod
    metadata:
      name: storage-test
    spec:
      containers:
      - name: test
        image: busybox
        command: ["/bin/sh", "-c", "while true; do date >> /data/test.log; sleep 5; done"]
        volumeMounts:
        - mountPath: /data
          name: test-volume
      volumes:
      - name: test-volume
        persistentVolumeClaim:
          claimName: my-longhorn-pvc
    # 파드 실행 후 데이터 확인
    kubectl exec storage-test -- cat /data/test.log
    
    # 파드를 강제 삭제
    kubectl delete pod storage-test
    
    # 새 파드로 다시 마운트해서 데이터가 남아있는지 확인
    kubectl apply -f storage-test.yaml
    kubectl exec storage-test -- cat /data/test.log
    # 이전 로그가 그대로 남아있으면 성공! 🎉

    🎉 데이터가 살아있는 걸 확인했을 때의 그 안도감… 처음엔 진짜 설레더라고요. 쿠버네티스에서 영구 스토리지가 제대로 동작한다는 걸 눈으로 확인한 순간이었습니다.

    추가로 볼륨 상태도 확인해두면 좋아요.

    # PVC 상태 확인
    kubectl get pvc
    # NAME               STATUS   VOLUME                                     CAPACITY   ACCESS MODES   STORAGECLASS   AGE
    # my-longhorn-pvc    Bound    pvc-xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx   10Gi       RWO            longhorn       5m
    
    # PV 상세 정보
    kubectl describe pv pvc-xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx

    ▲ Longhorn vs Rook Ceph 선택 가이드 요약 — 클러스터 규모, 기능 요구사항, 운영 복잡도를 기준으로 한 비교 인포그래픽

    자주 묻는 질문 (FAQ)

    Q. Longhorn은 프로덕션 환경에서 써도 되나요?

    네, CNCF 졸업 프로젝트이고 실제로 많은 기업에서 프로덕션에 사용하고 있습니다. 다만 대규모 클러스터나 높은 I/O가 필요한 환경에서는 Ceph 대비 한계가 있을 수 있어요.

    Q. Rook Ceph 최소 사양이 어떻게 되나요?

    OSD 노드 최소 3개를 권장합니다. 각 OSD 노드에는 전용 디스크가 필요하고, MON(모니터) 데몬 운영을 위한 CPU와 메모리도 여유 있게 확보해야 해요. 리소스가 빠듯한 환경에서는 Longhorn이 훨씬 현실적입니다.

    Q. 기존 NFS 스토리지에서 마이그레이션이 가능한가요?

    직접적인 마이그레이션 도구는 없고, 보통 데이터를 백업 후 새 PVC에 복원하는 방식을 사용합니다. Velero(벨레로) 같은 쿠버네티스 백업 도구를 활용하면 좀 더 체계적으로 진행할 수 있어요. 이 내용은 다음 글에서 다룰 예정입니다.

    Q. 홈랩에서는 뭘 추천하세요?

    저는 홈랩에서 Longhorn을 쓰고 있어요. 설치가 간단하고 UI가 직관적이어서 관리하기 편합니다. 노드 3대 이하 환경이라면 Longhorn이 압도적으로 편해요.

    마무리: 결국 “상황에 맞는” 선택이 정답입니다

    13년 동안 인프라 일을 하면서 느낀 건데요. 기술 선택에 “절대적인 정답”은 없더라고요. Longhorn이 좋냐, Rook Ceph가 좋냐가 아니라 내 환경에 뭐가 맞냐가 핵심이에요.

    소규모 클러스터에서 빠르게 쿠버네티스 영구 스토리지를 도입하고 싶다면 Longhorn으로 시작하세요. 운영하다가 규모가 커지고 RWX나 오브젝트 스토리지가 필요해지면 그때 Rook Ceph로 전환하거나 병행하는 것도 방법이에요.

    중요한 건 일단 시작하는 거라고 생각해요. NFS로 버티다가 나중에 대규모 마이그레이션 하는 것보다, 처음부터 제대로 된 CSI 드라이버 기반 스토리지를 도입하는 게 훨씬 낫거든요.

    다음 글에서는 Velero를 활용한 쿠버네티스 백업 전략에 대해 다룰 예정입니다. Longhorn 내장 백업과 Velero를 조합하면 꽤 탄탄한 백업 체계를 만들 수 있거든요. 기대해 주세요! 🎉

    혹시 설치하다가 막히는 부분이 있으시면 댓글로 남겨주세요. 제가 겪었던 삽질 경험이 도움이 될 수도 있으니까요 ㅎㅎ

  • [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 설정과 함께 구성하면 완성도 높은 홈랩 환경이 만들어집니다.

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