13년차의 서버실

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

[카테고리:] k8s

  • [k8s] ArgoCD 멀티 클러스터 GitOps 배포 및 관리 전략

    [k8s] ArgoCD 멀티 클러스터 GitOps 배포 및 관리 전략

    클러스터가 하나일 때는 괜찮았는데…

    처음 쿠버네티스를 도입했을 때 저도 클러스터 하나로 시작했어요. 개발(dev), 스테이징(staging), 프로덕션(production) 환경을 네임스페이스(Namespace)로 분리해서 쓰는 방식이었는데, 솔직히 그때는 그게 맞다고 생각했거든요. 근데 조직이 커지고 팀이 늘어나면서 문제가 하나씩 터지기 시작했습니다.

    “개발팀이 실수로 프로덕션 네임스페이스에 배포했어요.” 이 한 마디에 심장이 철렁 내려앉은 적 있으신가요? 저는 있습니다 ㅎㅎ. 결국 클러스터를 분리하기로 결정했고, 그때부터 ArgoCD 멀티 클러스터 전략을 본격적으로 파고들었습니다.

    이 글에서는 ArgoCD를 허브(Hub) 클러스터에 설치하고, 여러 개의 쿠버네티스 클러스터를 GitOps 방식으로 배포 및 관리하는 전략을 단계별로 정리해 드리겠습니다. 저처럼 삽질하지 않도록요.

    ArgoCD 멀티 클러스터 Hub-and-Spoke 아키텍처 다이어그램 - 허브 클러스터에서 dev, staging, prod 클러스터로 GitOps 배포 흐름

    ▲ ArgoCD 허브 클러스터가 여러 대상 클러스터(dev, staging, prod)를 관리하는 전체 아키텍처 구조. Hub-and-Spoke 패턴의 핵심입니다.

    ArgoCD 멀티 클러스터란? 개념부터 잡고 가요

    GitOps가 뭔지 먼저 짚고 가면

    GitOps는 쉽게 말해 “Git 저장소가 인프라와 애플리케이션의 단일 진실 공급원(Single Source of Truth)이 되는 운영 방식”이에요. 배포하고 싶으면 kubectl로 직접 명령을 날리는 게 아니라, Git에 커밋하면 자동으로 클러스터에 반영되는 방식이죠.

    ArgoCD는 이 GitOps를 쿠버네티스 위에서 구현해주는 대표적인 오픈소스 도구입니다. Git 저장소를 지속적으로 감시하다가 변경이 감지되면 클러스터에 자동으로 동기화(Sync)해줘요.

    멀티 클러스터 관리, 왜 필요한가요?

    네임스페이스 분리 방식의 가장 큰 문제는 폭발 반경(Blast Radius)이 너무 넓다는 거예요. 클러스터 레벨의 장애가 모든 환경에 영향을 주고, 보안 격리도 완벽하지 않습니다. 반면 클러스터를 분리하면:

    • 환경 간 완전한 격리 — 개발 배포 실수가 프로덕션에 영향 없음
    • 클러스터별 독립적인 리소스 할당 및 스케일링
    • 규정 준수(Compliance) 요구사항 충족 용이
    • 팀별 독립적인 업그레이드 주기 관리

    그런데 클러스터가 여러 개가 되면 관리 포인트도 여러 개가 되잖아요. 각 클러스터에 ArgoCD를 따로 설치하는 방법도 있지만, 저는 Hub-and-Spoke 패턴을 선호합니다. 허브 클러스터 하나에 ArgoCD를 설치하고, 나머지 클러스터들을 원격으로 관리하는 방식이에요.

    방식 장점 단점 추천 상황
    클러스터별 ArgoCD 설치 독립성 높음, 장애 격리 관리 포인트 분산, 일관성 유지 어려움 팀/조직이 완전히 분리된 경우
    Hub-and-Spoke (중앙화) 단일 관리 포인트, 일관된 정책 적용 허브 클러스터 장애 시 배포 불가 중앙 플랫폼팀이 관리하는 경우

    ArgoCD 설치 및 멀티 클러스터 등록 — 실전으로 들어갑니다

    1단계: 허브 클러스터에 ArgoCD 설치

    저는 허브 클러스터로 별도의 관리 전용 클러스터를 사용합니다. 프로덕션 워크로드가 없는 클러스터에 ArgoCD를 올리는 게 안전하더라고요.

    # ArgoCD 네임스페이스 생성
    kubectl create namespace argocd
    
    # ArgoCD 공식 매니페스트로 설치
    kubectl apply -n argocd -f https://raw.githubusercontent.com/argoproj/argo-cd/stable/manifests/install.yaml
    
    # 설치 확인
    kubectl get pods -n argocd
    

    설치가 완료되면 argocd-server 파드가 Running 상태가 되는데, 처음엔 이미지 풀(Image Pull) 때문에 좀 기다려야 할 수도 있어요. 저도 처음에 “왜 안 되지?” 하고 5분 기다렸더니 그냥 올라왔습니다 ㅎㅎ.

    # 초기 admin 비밀번호 확인
    kubectl -n argocd get secret argocd-initial-admin-secret \
      -o jsonpath="{.data.password}" | base64 -d
    
    # ArgoCD CLI 설치 (macOS 기준)
    brew install argocd
    
    # ArgoCD 서버 포트포워딩 (로컬 접근용)
    kubectl port-forward svc/argocd-server -n argocd 8080:443
    
    # CLI 로그인
    argocd login localhost:8080 --username admin --password <위에서_확인한_비밀번호> --insecure
    

    2단계: 대상 클러스터(Spoke) 등록

    이 부분이 멀티 클러스터 설정의 핵심이에요. ArgoCD CLI로 대상 클러스터를 등록하면, ArgoCD가 해당 클러스터에 argocd-manager라는 서비스 어카운트(Service Account)를 만들고 필요한 RBAC 권한을 자동으로 설정해줍니다.

    # 현재 kubeconfig에 등록된 컨텍스트 확인
    kubectl config get-contexts
    
    # 예시 출력:
    # CURRENT   NAME            CLUSTER         AUTHINFO
    # *         hub-cluster     hub-cluster     hub-admin
    #           dev-cluster     dev-cluster     dev-admin
    #           staging-cluster staging-cluster staging-admin
    #           prod-cluster    prod-cluster    prod-admin
    
    # 개발 클러스터 등록
    argocd cluster add dev-cluster --name dev
    
    # 스테이징 클러스터 등록
    argocd cluster add staging-cluster --name staging
    
    # 프로덕션 클러스터 등록
    argocd cluster add prod-cluster --name prod
    
    # 등록된 클러스터 목록 확인
    argocd cluster list
    

    💡 팁: 클러스터 등록 시 --name 옵션으로 별칭을 지정하면 나중에 Application 설정에서 훨씬 읽기 편합니다. URL 대신 이름으로 참조할 수 있거든요.

    ArgoCD Settings Clusters 화면에서 dev, staging, prod 멀티 클러스터가 등록된 관리 UI 화면

    ▲ ArgoCD 웹 UI의 Settings > Clusters 화면. dev, staging, prod 클러스터가 각각 등록되어 연결 상태를 실시간으로 확인할 수 있습니다.

    Git 저장소 구조 설계 — 이게 진짜 중요합니다

    멀티 클러스터 GitOps에서 Git 저장소 구조를 어떻게 잡느냐가 나중에 관리 편의성을 크게 좌우해요. 제가 여러 방식을 시도해보고 정착한 구조를 공유합니다.

    모노레포(Monorepo) 방식 — 제가 선호하는 방식

    gitops-repo/
    ├── apps/                          # 애플리케이션 정의
    │   ├── base/                      # 공통 기본 설정 (Kustomize base)
    │   │   ├── my-app/
    │   │   │   ├── deployment.yaml
    │   │   │   ├── service.yaml
    │   │   │   └── kustomization.yaml
    │   └── overlays/                  # 환경별 오버레이
    │       ├── dev/
    │       │   └── my-app/
    │       │       ├── kustomization.yaml
    │       │       └── patch-replicas.yaml
    │       ├── staging/
    │       │   └── my-app/
    │       └── prod/
    │           └── my-app/
    ├── argocd/                        # ArgoCD 설정 자체도 Git으로 관리
    │   ├── projects/                  # ArgoCD Project 정의
    │   │   ├── dev-project.yaml
    │   │   ├── staging-project.yaml
    │   │   └── prod-project.yaml
    │   └── applications/              # ArgoCD Application 정의
    │       ├── dev/
    │       ├── staging/
    │       └── prod/
    └── clusters/                      # 클러스터 레벨 설정
        ├── dev/
        ├── staging/
        └── prod/
    

    이 구조의 핵심은 Kustomize(커스터마이즈)를 활용해서 base 설정을 공유하면서 환경별로 다른 값(레플리카 수, 리소스 제한, 이미지 태그 등)만 오버레이로 덮어쓰는 방식이에요.

    Kustomize 오버레이 예시

    # apps/base/my-app/deployment.yaml
    apiVersion: apps/v1
    kind: Deployment
    metadata:
      name: my-app
    spec:
      replicas: 1
      selector:
        matchLabels:
          app: my-app
      template:
        metadata:
          labels:
            app: my-app
        spec:
          containers:
          - name: my-app
            image: my-registry/my-app:latest
            resources:
              requests:
                cpu: 100m
                memory: 128Mi
    
    # apps/overlays/prod/my-app/kustomization.yaml
    apiVersion: kustomize.config.k8s.io/v1beta1
    kind: Kustomization
    resources:
      - ../../../base/my-app
    patches:
      - path: patch-replicas.yaml
    images:
      - name: my-registry/my-app
        newTag: v1.2.3  # 프로덕션은 명시적 태그 사용
    
    # apps/overlays/prod/my-app/patch-replicas.yaml
    apiVersion: apps/v1
    kind: Deployment
    metadata:
      name: my-app
    spec:
      replicas: 3  # 프로덕션은 3개로
    

    ArgoCD Application 및 AppProject 설정

    AppProject(앱 프로젝트)로 클러스터 접근 제어

    ArgoCD의 AppProject는 애플리케이션들을 논리적으로 그룹화하고, 어떤 Git 저장소에서 어떤 클러스터로 배포할 수 있는지 제한하는 역할을 합니다. 저는 이걸 환경별로 나눠서 씁니다.

    # argocd/projects/prod-project.yaml
    apiVersion: argoproj.io/v1alpha1
    kind: AppProject
    metadata:
      name: production
      namespace: argocd
    spec:
      description: Production environment project
      # 허용된 소스 저장소
      sourceRepos:
        - 'https://github.com/myorg/gitops-repo.git'
      # 배포 가능한 대상 클러스터와 네임스페이스
      destinations:
        - server: https://prod-cluster-api.example.com
          namespace: '*'
      # 클러스터 범위 리소스 생성 제한 (선택적)
      clusterResourceWhitelist:
        - group: ''
          kind: Namespace
      # RBAC 역할 정의
      roles:
        - name: prod-deployer
          description: Production deployment role
          policies:
            - p, proj:production:prod-deployer, applications, sync, production/*, allow
          groups:
            - myorg:platform-team
    

    Application 정의 — 멀티 클러스터 배포의 핵심

    # argocd/applications/prod/my-app.yaml
    apiVersion: argoproj.io/v1alpha1
    kind: Application
    metadata:
      name: my-app-prod
      namespace: argocd
      # 앱 삭제 시 리소스도 함께 삭제되지 않도록 (중요!)
      finalizers:
        - resources-finalizer.argocd.argoproj.io
    spec:
      project: production
      source:
        repoURL: https://github.com/myorg/gitops-repo.git
        targetRevision: main
        path: apps/overlays/prod/my-app  # 프로덕션 오버레이 경로
      destination:
        # 등록한 클러스터 이름 또는 API 서버 URL
        server: https://prod-cluster-api.example.com
        namespace: my-app
      syncPolicy:
        automated:
          prune: true       # Git에서 삭제된 리소스는 클러스터에서도 삭제
          selfHeal: true    # 클러스터 상태가 Git과 다르면 자동 복구
        syncOptions:
          - CreateNamespace=true  # 네임스페이스 없으면 자동 생성
          - PrunePropagationPolicy=foreground
        retry:
          limit: 5
          backoff:
            duration: 5s
            factor: 2
            maxDuration: 3m
    

    ApplicationSet으로 여러 클러스터에 한 번에 배포

    근데 여기서 더 편한 방법이 있어요. ApplicationSet(애플리케이션셋)을 쓰면 여러 클러스터에 대한 Application을 템플릿 하나로 자동 생성할 수 있거든요. 처음 이걸 알았을 때 “이게 왜 이렇게 편하지?” 싶었습니다.

    # argocd/applicationsets/my-app-all-clusters.yaml
    apiVersion: argoproj.io/v1alpha1
    kind: ApplicationSet
    metadata:
      name: my-app-all-clusters
      namespace: argocd
    spec:
      generators:
        - list:
            elements:
              - cluster: dev
                url: https://dev-cluster-api.example.com
                env: dev
                revision: HEAD
              - cluster: staging
                url: https://staging-cluster-api.example.com
                env: staging
                revision: main
              - cluster: prod
                url: https://prod-cluster-api.example.com
                env: prod
                revision: main
      template:
        metadata:
          name: 'my-app-{{env}}'
        spec:
          project: '{{env}}'
          source:
            repoURL: https://github.com/myorg/gitops-repo.git
            targetRevision: '{{revision}}'
            path: 'apps/overlays/{{env}}/my-app'
          destination:
            server: '{{url}}'
            namespace: my-app
          syncPolicy:
            automated:
              prune: true
              selfHeal: true
            syncOptions:
              - CreateNamespace=true
    

    💡 팁: ApplicationSet의 generators에는 List 방식 외에도 클러스터 레이블 기반으로 자동 감지하는 Cluster Generator도 있어요. 클러스터가 많아질수록 이쪽이 훨씬 강력합니다.

    ⚠️ 실제로 겪은 트러블슈팅 — 이거 꼭 읽으세요

    문제 1: 클러스터 등록 후 연결 끊김 (Connection Refused)

    클러스터를 등록했는데 ArgoCD UI에서 계속 “Unknown” 상태가 뜨는 경우가 있었어요. 원인을 파고들어보니 허브 클러스터에서 대상 클러스터의 API 서버로 직접 네트워크 연결이 안 되는 거였습니다. VPC 피어링이나 방화벽 규칙을 확인해야 해요.

    # 허브 클러스터에서 대상 클러스터 API 서버 연결 테스트
    kubectl run curl-test --image=curlimages/curl --rm -it --restart=Never -- \
      curl -k https://prod-cluster-api.example.com/healthz
    

    문제 2: prune 옵션으로 인한 의도치 않은 리소스 삭제

    이건 진짜 아찔했던 경험인데요. automated.prune: true를 켜놨는데, 실수로 Git에서 파일을 지웠다가 프로덕션 Deployment가 통째로 삭제된 적이 있었어요. 다행히 빠르게 복구했지만…

    이후로 저는 프로덕션 환경에는 Sync Windows(동기화 창)를 설정해서 업무 시간 외에는 자동 동기화가 안 되도록 했습니다.

    # AppProject에 Sync Window 추가
    spec:
      syncWindows:
        - kind: allow
          schedule: '0 9 * * 1-5'  # 평일 오전 9시에만
          duration: 8h
          applications:
            - '*'
          manualSync: true  # 수동 동기화는 항상 허용
    

    문제 3: Helm 차트 버전 충돌

    여러 클러스터에 같은 Helm 차트를 다른 버전으로 배포하다 보면 values 파일 구조가 버전마다 달라서 오류가 나는 경우가 있어요. 저는 이걸 해결하기 위해 클러스터별 values 파일을 명확하게 분리해서 관리하고 있습니다.

    # Helm 소스 예시 - 클러스터별 values 파일 분리
    source:
      repoURL: https://charts.example.com
      chart: my-chart
      targetRevision: 1.2.3
      helm:
        valueFiles:
          - values.yaml
          - values-prod.yaml  # 환경별 values 파일
    
    ArgoCD Applications 대시보드에서 멀티 클러스터 배포된 애플리케이션들이 모두 Synced Healthy 상태로 표시된 결과 화면

    ▲ ArgoCD 웹 UI의 Applications 화면. dev, staging, prod 클러스터에 배포된 my-app들이 모두 Synced/Healthy 상태로 표시되는 모습입니다.

    ✅ 배포 검증 및 운영 팁

    헬스 체크와 동기화 상태 모니터링

    # 전체 Application 상태 확인
    argocd app list
    
    # 특정 앱 상세 상태 확인
    argocd app get my-app-prod
    
    # 수동으로 동기화 실행
    argocd app sync my-app-prod
    
    # 동기화 상태 및 히스토리 확인
    argocd app history my-app-prod
    
    # 롤백 (이전 버전으로)
    argocd app rollback my-app-prod 
    

    Notification(알림) 설정으로 배포 상태 파악

    ArgoCD에는 argocd-notifications라는 컴포넌트가 있어서 Slack, 이메일, PagerDuty 등으로 배포 상태를 알림 받을 수 있어요. 저는 Slack 채널에 연동해서 배포 성공/실패를 실시간으로 받고 있는데, 이거 없으면 이제 못 살 것 같습니다 ㅎㅎ.

    RBAC으로 팀별 접근 제어

    # argocd-rbac-cm ConfigMap 예시
    apiVersion: v1
    kind: ConfigMap
    metadata:
      name: argocd-rbac-cm
      namespace: argocd
    data:
      policy.csv: |
        # 개발팀: dev 프로젝트만 접근 가능
        p, role:dev-team, applications, *, dev/*, allow
        p, role:dev-team, applications, sync, dev/*, allow
        
        # 플랫폼팀: 전체 접근 가능
        p, role:platform-team, applications, *, */*, allow
        p, role:platform-team, clusters, *, *, allow
        
        g, myorg:dev-team, role:dev-team
        g, myorg:platform-team, role:platform-team
      policy.default: role:readonly
    

    전략 정리 — 이것만 기억하세요

    ArgoCD 멀티 클러스터 GitOps 전략 핵심 구성요소 요약 인포그래픽 - AppProject, ApplicationSet, Kustomize, RBAC 관계도

    ▲ ArgoCD 멀티 클러스터 GitOps 전략의 핵심 요소를 한눈에 정리한 요약 다이어그램. Git 저장소 구조, ArgoCD 컴포넌트, 클러스터 관계를 보여줍니다.

    구성 요소 역할 핵심 포인트
    AppProject 배포 범위 및 권한 제한 환경별로 분리, Sync Window 설정
    Application 개별 앱 배포 정의 destination.server로 대상 클러스터 지정
    ApplicationSet 다중 클러스터 배포 자동화 템플릿 하나로 여러 클러스터에 배포
    Kustomize Overlay 환경별 설정 분리 base 공유, 환경별 차이만 patch
    RBAC 팀별 접근 제어 AppProject role + argocd-rbac-cm

    마무리하며 — 그래서 뭐가 달라졌냐면

    ArgoCD 멀티 클러스터 구성을 제대로 잡고 나서 가장 크게 달라진 건, 배포에 대한 불안감이 사라졌다는 거예요. 예전엔 “내가 지금 어떤 클러스터에 붙어있지?”를 항상 확인해야 했는데, 이제는 Git에 PR 올리고 머지하면 끝이거든요.

    물론 처음 설계할 때 Git 저장소 구조를 잘 잡는 게 제일 중요합니다. 나중에 구조를 바꾸려면 진짜 손이 많이 가거든요. 저도 한 번 갈아엎었습니다… ㅎㅎ 이 글을 보시는 분들은 처음부터 overlays 구조로 시작하시길 강력 추천합니다.

    다음 글에서는 ArgoCD Image Updater를 활용해서 컨테이너 이미지 태그 업데이트까지 완전 자동화하는 방법을 다뤄볼 예정이에요. CI 파이프라인에서 이미지 빌드되면 ArgoCD가 자동으로 Git 커밋하고 배포까지 이어지는 풀 사이클인데, 이거 진짜 편합니다.

    혹시 멀티 클러스터 구성하면서 막히는 부분 있으시면 댓글로 남겨주세요. 제가 겪어본 삽질이라면 같이 해결해 드릴 수 있을 것 같습니다 🎉

    자주 묻는 질문 (FAQ)

    Q. ArgoCD 자체가 설치된 허브 클러스터가 다운되면 어떻게 되나요?

    A. 허브 클러스터가 다운되면 새로운 배포는 불가능하지만, 이미 배포된 애플리케이션들은 각 클러스터에서 계속 정상 동작합니다. 이 때문에 허브 클러스터는 고가용성(HA) 구성으로 운영하는 것을 권장합니다.

    Q. ApplicationSet과 Application을 언제 선택해야 하나요?

    A. 동일한 앱을 여러 클러스터에 배포해야 한다면 ApplicationSet이 훨씬 효율적입니다. 클러스터마다 배포 설정이 크게 다르거나 배포 시점을 완전히 독립적으로 관리해야 한다면 개별 Application을 사용하는 게 맞습니다.

    Q. Helm과 Kustomize 중 어떤 걸 쓰는 게 좋나요?

    A. 서드파티 차트를 그대로 쓸 때는 Helm, 자체 매니페스트를 환경별로 관리할 때는 Kustomize가 더 직관적입니다. 둘을 혼합해서 쓰는 것도 가능하고, 저는 실제로 두 방식을 같이 씁니다.

  • [K8s] 쿠버네티스 Ingress Controller 비교: Nginx, Traefik, Gateway API 선택 가이드

    [K8s] 쿠버네티스 Ingress Controller 비교: Nginx, Traefik, Gateway API 선택 가이드

    쿠버네티스 Ingress Controller, 뭘 써야 할지 모르겠다면?

    쿠버네티스(Kubernetes)를 처음 도입할 때 가장 많이 막히는 부분 중 하나가 바로 Ingress Controller(인그레스 컨트롤러) 선택이에요. 저도 처음엔 “Nginx 쓰면 되는 거 아닌가?” 하고 무심코 설치했다가, 나중에 Traefik이 더 편하다는 걸 알고 갈아엎은 경험이 있거든요. 그리고 최근엔 Gateway API라는 새로운 표준까지 등장해서 선택지가 더 복잡해졌습니다.

    이 글에서는 쿠버네티스 클러스터 운영에서 가장 중요한 선택 중 하나인 Ingress Controller의 세 가지 주요 선택지를 실제 운영 경험을 바탕으로 비교해 드릴게요. 홈랩부터 프로덕션까지 다양한 환경에서 직접 써본 입장에서 솔직하게 이야기해 보겠습니다.

    쿠버네티스 Ingress Controller 전체 아키텍처 — 외부 트래픽이 인그레스 컨트롤러를 통해 내부 서비스로 라우팅되는 구조

    ▲ 쿠버네티스 클러스터에서 외부 트래픽이 Ingress Controller를 거쳐 내부 서비스로 전달되는 전체 흐름 다이어그램

    Ingress Controller가 뭔지부터 짚고 가자

    쉽게 말해서, Ingress(인그레스)는 외부에서 쿠버네티스 클러스터 안으로 들어오는 HTTP/HTTPS 트래픽을 어떻게 라우팅할지 정의하는 규칙이에요. 근데 이 규칙만 있다고 실제로 동작하는 게 아니에요. 규칙을 실제로 실행해 주는 주체가 바로 Ingress Controller(인그레스 컨트롤러)입니다.

    비유하자면, Ingress 리소스는 “1번 도메인은 A 서비스로, 2번 도메인은 B 서비스로 보내라”는 지시서고, Ingress Controller는 그 지시서를 읽고 실제로 트래픽을 처리하는 리버스 프록시(Reverse Proxy) 서버라고 보시면 됩니다.

    쿠버네티스 자체에는 Ingress Controller가 기본 내장되어 있지 않아요. 그래서 우리가 직접 선택해서 설치해야 합니다. 그리고 이 선택이 생각보다 중요한 결정이거든요.

    왜 선택이 중요한가요?

    • 운영 중에 교체하면 서비스 중단이 발생할 수 있어요
    • 각 컨트롤러마다 지원하는 기능과 설정 방식이 달라요
    • 팀의 기술 스택과 러닝 커브도 고려해야 합니다
    • 트래픽 규모와 패턴에 따라 성능 특성이 다르게 나타나요

    세 가지 선택지 한눈에 비교

    본격적인 비교 전에 전체 그림을 먼저 보시죠.

    항목 Nginx Ingress Controller Traefik Gateway API
    기반 기술 Nginx (리버스 프록시) Go 기반 자체 구현 표준 API (구현체 별도)
    설정 방식 Ingress + Annotation Ingress + CRD Gateway/HTTPRoute CRD
    자동 설정 갱신 지원 (일부 재시작 필요) ✅ 완전 동적 구현체에 따라 다름
    대시보드 별도 설치 필요 ✅ 내장 없음 (구현체 의존)
    학습 난이도 낮음 (Nginx 경험자) 중간 높음
    성숙도 매우 높음 높음 성장 중 (GA 달성)
    멀티 팀 지원 제한적 중간 ✅ 설계 목표
    커뮤니티/레퍼런스 매우 풍부 풍부 성장 중

    Nginx Ingress Controller — 검증된 베테랑

    솔직히 말씀드리면, 저도 처음 쿠버네티스 클러스터 구성할 때 고민 없이 Nginx Ingress를 선택했어요. 이유는 단순했습니다. Nginx를 이미 10년 넘게 써왔으니까요.

    Nginx Ingress Controller는 CNCF(Cloud Native Computing Foundation) 산하 프로젝트인 kubernetes/ingress-nginx와, Nginx Inc.에서 만든 nginxinc/kubernetes-ingress 두 가지가 있어요. 헷갈리시는 분들이 많은데, 커뮤니티에서 주로 쓰는 건 전자인 ingress-nginx입니다.

    Helm으로 설치하기

    # Nginx Ingress Controller Helm 설치
    helm repo add ingress-nginx https://kubernetes.github.io/ingress-nginx
    helm repo update
    
    helm install ingress-nginx ingress-nginx/ingress-nginx \
      --namespace ingress-nginx \
      --create-namespace \
      --set controller.replicaCount=2

    기본 Ingress 리소스 예시

    apiVersion: networking.k8s.io/v1
    kind: Ingress
    metadata:
      name: my-app-ingress
      annotations:
        nginx.ingress.kubernetes.io/rewrite-target: /
        nginx.ingress.kubernetes.io/ssl-redirect: "true"
    spec:
      ingressClassName: nginx
      tls:
      - hosts:
        - myapp.example.com
        secretName: myapp-tls
      rules:
      - host: myapp.example.com
        http:
          paths:
          - path: /
            pathType: Prefix
            backend:
              service:
                name: my-app-service
                port:
                  number: 80

    Nginx Ingress의 장단점

    ✅ 장점

    • 레퍼런스가 압도적으로 많아요. 구글에서 뭐든 찾을 수 있습니다
    • Nginx에 익숙한 분들은 Annotation으로 세밀한 튜닝이 가능해요
    • 안정성이 검증된 오랜 역사를 가지고 있습니다
    • 대규모 트래픽 처리에 강점이 있어요

    ⚠️ 단점

    • 설정 변경 시 nginx.conf를 reload해야 해서, 대규모 클러스터에선 부담이 될 수 있어요
    • Annotation이 너무 많아서 처음엔 뭘 써야 할지 헷갈립니다
    • 내장 대시보드가 없어서 별도로 Prometheus + Grafana를 구성해야 해요

    Traefik — 쿠버네티스 네이티브의 매력

    Traefik을 처음 접한 건 홈랩에서 Docker Compose로 운영하다가였어요. 당시에 “이거 레이블(Label) 하나 달면 자동으로 라우팅이 된다고?” 하면서 신기해했던 기억이 납니다. 쿠버네티스에서도 비슷한 경험을 할 수 있어요.

    Traefik의 핵심 철학은 동적 설정(Dynamic Configuration)이에요. 새로운 서비스가 배포되면 자동으로 감지해서 라우팅 규칙을 업데이트합니다. Nginx처럼 reload가 없어요.

    Traefik Helm 설치

    # Traefik Helm 설치
    helm repo add traefik https://helm.traefik.io/traefik
    helm repo update
    
    helm install traefik traefik/traefik \
      --namespace traefik \
      --create-namespace \
      --set dashboard.enabled=true \
      --set ingressRoute.dashboard.enabled=true

    Traefik IngressRoute CRD 예시

    Traefik은 표준 Ingress 리소스도 지원하지만, 자체 CRD인 IngressRoute를 쓰면 훨씬 풍부한 기능을 쓸 수 있어요.

    apiVersion: traefik.io/v1alpha1
    kind: IngressRoute
    metadata:
      name: my-app-ingressroute
      namespace: default
    spec:
      entryPoints:
        - websecure
      routes:
        - match: Host(`myapp.example.com`)
          kind: Rule
          services:
            - name: my-app-service
              port: 80
          middlewares:
            - name: my-ratelimit
      tls:
        certResolver: letsencrypt

    Middleware로 Rate Limiting 적용

    apiVersion: traefik.io/v1alpha1
    kind: Middleware
    metadata:
      name: my-ratelimit
    spec:
      rateLimit:
        average: 100
        burst: 50

    💡 Traefik의 진짜 강점은 Let’s Encrypt 인증서 자동 발급이 내장되어 있다는 거예요. cert-manager를 별도로 설치하지 않아도 TLS 인증서를 자동으로 관리해 줍니다. 홈랩에서 이거 하나 때문에 Traefik으로 갈아탔을 정도로 편하더라고요.

    Traefik 내장 대시보드 화면 — 라우터, 서비스, 미들웨어 현황을 한눈에 확인하는 모니터링 인터페이스

    ▲ Traefik 내장 대시보드에서 라우터, 서비스, 미들웨어 현황을 한눈에 확인할 수 있는 화면

    Traefik의 장단점

    ✅ 장점

    • 동적 설정 갱신 — 서비스 변경 시 reload 없이 즉시 반영돼요
    • 내장 대시보드로 라우팅 현황을 바로 확인할 수 있어요
    • Let’s Encrypt 자동 인증서 발급/갱신 내장
    • Middleware로 인증, Rate Limiting, 헤더 조작 등을 깔끔하게 구성 가능

    ⚠️ 단점

    • CRD 방식이 Nginx Annotation과 달라서 러닝 커브가 있어요
    • Nginx에 비해 레퍼런스가 적습니다 (그래도 요즘은 많이 늘었어요)
    • 초대형 클러스터에서의 성능 검증 사례가 Nginx보다 적은 편이에요

    Gateway API — 쿠버네티스 네트워크의 미래

    처음 Gateway API 문서를 읽었을 때 솔직히 “이게 뭐가 다른 건데?” 싶었어요. 근데 알고 보니 기존 Ingress 리소스의 한계를 근본적으로 해결하려는 시도더라고요.

    Gateway API는 SIG Network(쿠버네티스 네트워킹 특별관심그룹)에서 만든 공식 표준이에요. 2023년에 v1.0으로 GA(Generally Available, 정식 출시)를 달성했습니다. Ingress 리소스를 대체하려는 게 아니라, 더 표현력 있고 역할 기반으로 분리된 네트워크 설정을 가능하게 하는 게 목표예요.

    Gateway API의 핵심 개념

    기존 Ingress는 개발자와 인프라 관리자가 같은 리소스를 수정해야 했어요. Gateway API는 역할을 명확히 분리합니다.

    • GatewayClass: 인프라 제공자가 정의 (“이 구현체를 쓸게”)
    • Gateway: 클러스터 운영자가 정의 (“이 포트, 이 프로토콜로 받을게”)
    • HTTPRoute: 개발자가 정의 (“이 경로는 내 서비스로 보내줘”)

    Gateway API 설치 (CRD 먼저)

    # Gateway API CRD 설치
    kubectl apply -f https://github.com/kubernetes-sigs/gateway-api/releases/download/v1.1.0/standard-install.yaml
    
    # 구현체 예시: Nginx Gateway Fabric
    helm install ngf oci://ghcr.io/nginxinc/charts/nginx-gateway-fabric \
      --namespace nginx-gateway \
      --create-namespace

    Gateway와 HTTPRoute 설정 예시

    # Gateway 리소스 (운영자가 관리)
    apiVersion: gateway.networking.k8s.io/v1
    kind: Gateway
    metadata:
      name: main-gateway
      namespace: infra
    spec:
      gatewayClassName: nginx
      listeners:
      - name: https
        port: 443
        protocol: HTTPS
        tls:
          mode: Terminate
          certificateRefs:
          - name: main-tls-cert
        allowedRoutes:
          namespaces:
            from: Selector
            selector:
              matchLabels:
                gateway-access: allowed
    # HTTPRoute 리소스 (개발자가 관리)
    apiVersion: gateway.networking.k8s.io/v1
    kind: HTTPRoute
    metadata:
      name: my-app-route
      namespace: my-app-ns
    spec:
      parentRefs:
      - name: main-gateway
        namespace: infra
      hostnames:
      - myapp.example.com
      rules:
      - matches:
        - path:
            type: PathPrefix
            value: /api
        backendRefs:
        - name: api-service
          port: 8080
      - matches:
        - path:
            type: PathPrefix
            value: /
        backendRefs:
        - name: frontend-service
          port: 3000

    보이시나요? Gateway는 인프라팀이 관리하고, HTTPRoute는 각 개발팀이 자기 네임스페이스에서 독립적으로 관리할 수 있어요. 멀티 팀 환경에서 진짜 강력한 구조입니다.

    Gateway API의 장단점

    ✅ 장점

    • 역할 기반 분리로 멀티 팀 환경에 최적화되어 있어요
    • 표현력이 풍부해서 복잡한 라우팅 규칙도 명확하게 표현 가능
    • 쿠버네티스 공식 표준이라 장기적으로 생태계 지원이 확대될 거예요
    • 구현체를 교체해도 HTTPRoute 설정은 그대로 재사용 가능 (이식성)

    ⚠️ 단점

    • 개념이 많아서 처음 배우기가 쉽지 않아요. 저도 꽤 헷갈렸습니다
    • 구현체마다 지원하는 기능이 달라서 사전 확인이 필요해요
    • 소규모 팀이나 단순한 환경에선 오버엔지니어링이 될 수 있어요
    • 레퍼런스와 사례가 아직 Nginx/Traefik에 비해 적습니다

    ⚠️ 실제 트러블슈팅 경험담

    각 컨트롤러를 쓰면서 제가 직접 겪은 문제들을 공유할게요.

    Nginx Ingress — 413 Request Entity Too Large

    파일 업로드 기능이 있는 서비스에서 자꾸 413 에러가 났었어요. Nginx 기본 설정의 client_max_body_size 제한 때문이었는데, Annotation으로 해결했습니다.

    metadata:
      annotations:
        nginx.ingress.kubernetes.io/proxy-body-size: "50m"

    Traefik — 인증서가 갱신 안 되는 문제

    Let’s Encrypt 인증서를 Traefik이 자동으로 갱신해 주는 게 편한데, 어느 날 갑자기 인증서 만료 알림이 왔어요. 알고 보니 acme.json 파일이 저장된 PersistentVolume이 Pod 재시작 시 초기화되는 문제였습니다. acme.json은 반드시 영구 스토리지에 마운트해야 해요.

    # values.yaml에서 persistence 설정
    persistence:
      enabled: true
      storageClass: "longhorn"
      size: 128Mi

    Gateway API — 구현체 호환성 확인 필수

    HTTPRoute의 고급 기능(예: RequestRedirect, URLRewrite)을 사용했는데, 특정 구현체에서 지원하지 않아서 라우팅이 묵묵히 실패하는 경우가 있었어요. 구현체의 Conformance Report(적합성 보고서)를 미리 확인하는 게 중요합니다. Gateway API 공식 문서에서 구현체별 지원 기능표를 제공하고 있어요.

    쿠버네티스 Ingress Controller 모니터링 대시보드 — Prometheus와 Grafana를 활용한 요청 수, 에러율, 레이턴시 시각화

    ▲ Prometheus와 Grafana를 활용한 Ingress Controller 모니터링 대시보드 — 요청 수, 에러율, 레이턴시를 실시간으로 확인

    상황별 선택 가이드

    “그래서 뭘 써야 하나요?” 라고 물어보신다면, 상황에 따라 다르다고 답할 수밖에 없어요. 하지만 경험상 이런 기준으로 선택하시면 크게 후회는 없더라고요.

    Nginx Ingress Controller를 선택하세요, 만약…

    • 팀에 Nginx 경험자가 있고, 레퍼런스가 많은 안정적인 선택을 원할 때
    • 대규모 트래픽을 처리해야 하고 검증된 성능이 필요할 때
    • 기존 Nginx 설정을 쿠버네티스로 마이그레이션할 때
    • 복잡한 Nginx 설정(custom snippet 등)이 필요할 때

    Traefik을 선택하세요, 만약…

    • 홈랩이나 소규모 환경에서 편리하게 운영하고 싶을 때
    • Let’s Encrypt 자동 인증서 관리를 간단하게 하고 싶을 때
    • 내장 대시보드로 라우팅 현황을 빠르게 파악하고 싶을 때
    • 동적 환경에서 서비스 변경이 잦을 때

    Gateway API를 선택하세요, 만약…

    • 여러 팀이 각자의 라우팅 설정을 독립적으로 관리해야 할 때
    • 장기적으로 쿠버네티스 표준에 맞춰 가고 싶을 때
    • 복잡한 트래픽 분산(카나리 배포, A/B 테스트 등)이 필요할 때
    • 구현체 이식성을 확보하고 싶을 때
    쿠버네티스 Ingress Controller 비교 인포그래픽 — Nginx Ingress, Traefik, Gateway API 특징과 선택 기준 요약

    ▲ 환경과 요구사항에 따른 Ingress Controller 선택 가이드 요약 인포그래픽

    마무리 — 정답은 없지만, 기준은 있다

    13년 동안 인프라를 운영하면서 느낀 건, 기술 선택에 절대적인 정답은 없다는 거예요. 중요한 건 내 환경과 팀의 요구사항에 맞는 선택을 하는 겁니다.

    개인적으로는 이렇게 정리하고 싶어요.

    • 처음 시작하는 분들: Nginx Ingress로 시작하세요. 레퍼런스가 많아서 막힐 때 찾기 쉬워요
    • 홈랩이나 소규모 팀: Traefik을 강력 추천합니다. 편의성이 정말 좋아요
    • 엔터프라이즈/멀티 팀 환경: Gateway API를 진지하게 검토해 보세요. 미래 방향성이 여기 있거든요

    그리고 하나 더 — 저는 지금 홈랩에서 Traefik을 쓰고, 회사 프로덕션에서는 Nginx Ingress를 씁니다. 두 개 다 쓸 줄 알면 더 좋아요. 😄

    다음 글에서는 Traefik의 Middleware를 활용한 인증/인가(Authentication/Authorization) 설정을 더 자세히 다룰 예정이에요. cert-manager를 이용한 TLS 자동화 내용도 이전 글에서 다룬 적 있으니 참고해 보세요.

    자주 묻는 질문 (FAQ)

    Q. Nginx Ingress Controller와 Nginx Gateway Fabric은 다른 건가요?

    네, 다릅니다. ingress-nginx는 기존 Ingress API 기반이고, Nginx Gateway Fabric은 Gateway API 표준의 구현체예요. 용도와 설정 방식이 다릅니다.

    Q. 하나의 클러스터에 여러 Ingress Controller를 동시에 쓸 수 있나요?

    가능합니다. ingressClassName을 통해 어떤 컨트롤러를 사용할지 지정할 수 있어요. 다만 운영 복잡도가 올라가니 꼭 필요한 경우에만 권장합니다.

    Q. Gateway API는 Ingress를 완전히 대체하나요?

    장기적으로는 그 방향이지만, 현재는 공존하는 상태예요. 기존 Ingress 리소스가 deprecated되지는 않았고, Gateway API가 더 복잡한 요구사항을 위한 차세대 표준으로 자리잡고 있는 중입니다.

  • [k8s] kubectl 플러그인 추천: 생산성을 높이는 필수 도구 활용법

    [k8s] kubectl 플러그인 추천: 생산성을 높이는 필수 도구 활용법

    kubectl만으로는 부족했던 날들

    쿠버네티스(Kubernetes)를 쓰다 보면 kubectl이 얼마나 강력한 도구인지 금방 느끼게 됩니다. 근데 동시에 “이걸 좀 더 편하게 할 수 없나?”라는 생각도 같이 오더라고요. 저도 처음 몇 년은 그냥 기본 kubectl에 alias 좀 걸어놓고 썼는데, 팀원이 플러그인 쓰는 걸 보고 나서 세상이 달라 보였습니다. 😅

    특히 여러 클러스터를 동시에 관리하거나, 네임스페이스를 자주 전환하거나, 파드 로그를 한꺼번에 봐야 할 때 기본 kubectl만으로는 손이 너무 많이 가거든요. 오늘은 제가 실제로 쓰고 있는 kubectl 플러그인들을 소개해 드리려 합니다. 생산성이 진짜로 달라지는 것들만 골랐으니 한번 같이 살펴봐요.

    krew를 중심으로 한 kubectl 플러그인 생태계 구조 다이어그램

    kubectl 플러그인 생태계 — krew를 중심으로 다양한 플러그인이 연결되는 구조. 기본 kubectl에서 플러그인을 통해 기능이 확장되는 흐름을 보여줍니다.

    krew란 뭔가요? kubectl 플러그인 패키지 매니저

    kubectl 플러그인 얘기를 하려면 먼저 krew를 알아야 합니다. 쉽게 말해, kubectl 플러그인을 위한 apt나 brew 같은 패키지 매니저예요. kubectl은 $PATH에 kubectl-플러그인이름 형태의 실행 파일이 있으면 자동으로 서브커맨드로 인식하는 구조거든요. krew는 이 kubectl 플러그인들을 설치·업데이트·삭제하는 걸 한 곳에서 관리해 줍니다.

    공식 플러그인 인덱스에 200개가 넘는 플러그인이 등록돼 있고, 커뮤니티에서 계속 새로운 게 올라오고 있어요. 저도 처음엔 “그냥 직접 바이너리 받으면 되는 거 아닌가?” 싶었는데, 여러 플러그인을 쓰다 보니 krew 없이는 못 살겠더라고요 ㅎㅎ.

    krew 설치 방법

    macOS/Linux 기준으로 설치 방법은 아래와 같습니다. 공식 문서에서 제공하는 방법 그대로예요.

    # krew 설치 (bash/zsh 기준)
    (
      set -x; cd "$(mktemp -d)" &&
      OS="$(uname | tr '[:upper:]' '[:lower:]')" &&
      ARCH="$(uname -m | sed -e 's/x86_64/amd64/' -e 's/\(arm\)\(64\)\?.*/\1\2/' -e 's/aarch64$/arm64/')" &&
      KREW="krew-${OS}_${ARCH}" &&
      curl -fsSLO "https://github.com/kubernetes-sigs/krew/releases/latest/download/${KREW}.tar.gz" &&
      tar zxvf "${KREW}.tar.gz" &&
      ./${KREW} install krew
    )
    
    # PATH에 krew bin 추가 (~/.bashrc 또는 ~/.zshrc에 추가)
    export PATH="${KREW_ROOT:-$HOME/.krew}/bin:$PATH"
    
    # 설치 확인
    kubectl krew version

    설치 후 source ~/.zshrc 또는 터미널 재시작을 해줘야 PATH가 적용됩니다. 이 부분에서 한 번씩 헤매시는 분들이 있더라고요. ⚠️ PATH 적용 안 하면 플러그인이 인식 안 됩니다.

    꼭 써야 할 kubectl 플러그인 추천 목록

    이제 본론입니다. 제가 홈랩에서도, 실무에서도 실제로 쓰고 있는 플러그인들이에요. 설치 명령어와 기본 사용법을 같이 정리했습니다.

    1. kubectx / kubens — 컨텍스트·네임스페이스 전환의 끝판왕

    이게 없던 시절엔 어떻게 살았나 싶을 정도예요. kubectx는 클러스터 컨텍스트(context, 어느 클러스터에 연결할지 설정)를, kubens는 네임스페이스(namespace)를 빠르게 전환해 줍니다.

    # krew로 설치
    kubectl krew install ctx
    kubectl krew install ns
    
    # 컨텍스트 목록 보기 및 전환
    kubectl ctx                    # 목록 출력
    kubectl ctx my-production      # 프로덕션 클러스터로 전환
    kubectl ctx -                  # 이전 컨텍스트로 돌아가기
    
    # 네임스페이스 전환
    kubectl ns                     # 목록 출력
    kubectl ns kube-system         # kube-system으로 전환
    kubectl ns -                   # 이전 네임스페이스로 돌아가기

    특히 fzf(퍼지 검색 도구)가 설치돼 있으면 인터랙티브 선택 메뉴가 뜨는데, 이게 진짜 편합니다. 클러스터가 5개 넘어가면 이거 없이는 진짜 힘들어요.

    2. stern — 여러 파드 로그를 한 번에

    stern은 여러 파드의 로그를 동시에 스트리밍해주는 도구입니다. 기본 kubectl logs는 파드 하나밖에 못 보잖아요. 마이크로서비스 환경에서 여러 레플리카를 동시에 봐야 할 때 정말 유용해요.

    # krew로 설치
    kubectl krew install stern
    
    # 특정 레이블의 파드 로그 전부 보기
    kubectl stern -l app=my-api
    
    # 특정 네임스페이스에서 이름 패턴으로 필터링
    kubectl stern my-api -n production
    
    # 지난 1시간치 로그부터 보기
    kubectl stern my-api --since 1h
    
    # 특정 컨테이너만 보기
    kubectl stern my-api -c main-container

    색깔별로 파드를 구분해서 출력해 주기 때문에 어느 파드에서 에러가 났는지 한눈에 보입니다. 처음 써봤을 때 “이게 왜 기본이 아니지?” 싶었어요 🎉.

    3. kubectl-neat — YAML을 깔끔하게

    kubectl get pod my-pod -o yaml 해보신 적 있으시죠? 출력이 어마어마하게 길게 나오는데, 대부분은 쿠버네티스가 자동으로 붙인 메타데이터라 실제로 내가 설정한 내용 파악하기가 힘들어요. kubectl-neat는 이 불필요한 필드들을 걸러내서 깔끔한 YAML만 보여줍니다.

    # 설치
    kubectl krew install neat
    
    # 파드 YAML을 깔끔하게 출력
    kubectl get pod my-pod -o yaml | kubectl neat
    
    # 디플로이먼트도 동일하게 활용
    kubectl get deployment my-deploy -o yaml | kubectl neat
    
    # 파일로 저장해서 재활용
    kubectl get deployment my-deploy -o yaml | kubectl neat > deploy-clean.yaml

    기존 리소스를 백업하거나, 다른 환경에 옮겨 적용할 때 진짜 요긴합니다. 삽질 좀 줄여주는 고마운 플러그인이에요.

    4. kubectl-tree — 리소스 의존관계 트리로 보기

    kubectl-tree는 쿠버네티스 리소스의 오너십(ownerReference) 관계를 트리 형태로 시각화해 줍니다. 디플로이먼트 → 레플리카셋 → 파드로 이어지는 관계를 한 눈에 볼 수 있어요.

    # 설치
    kubectl krew install tree
    
    # 디플로이먼트 트리 보기
    kubectl tree deployment my-deploy
    
    # 예시 출력
    # NAMESPACE  NAME                                    READY  REASON  AGE
    # default    Deployment/my-deploy                    -              2d
    # default    └─ReplicaSet/my-deploy-7d6b9c8f5       -              2d
    # default      ├─Pod/my-deploy-7d6b9c8f5-abc12      True           2d
    # default      └─Pod/my-deploy-7d6b9c8f5-def34      True           2d

    Helm으로 배포한 차트가 어떤 리소스들을 만들었는지 파악할 때도 편리해요. 신입 엔지니어분들한테 쿠버네티스 리소스 구조 설명할 때도 이걸 보여주면 이해가 훨씬 빠르더라고요.

    5. kubectl-whoami — 현재 인증 정보 빠르게 확인

    여러 클러스터, 여러 서비스 어카운트를 쓰다 보면 “내가 지금 어느 권한으로 붙어 있지?” 헷갈릴 때가 있어요. kubectl-whoami가 딱 그럴 때 씁니다.

    # 설치
    kubectl krew install whoami
    
    # 현재 인증 주체 확인
    kubectl whoami
    # 출력 예시: system:serviceaccount:default:my-sa

    RBAC(역할 기반 접근 제어) 트러블슈팅할 때 제일 먼저 쓰는 명령어가 됐습니다. 간단하지만 정말 자주 쓰게 되더라고요.

    stern과 kubectx 등 kubectl 플러그인이 실행되는 터미널 화면

    kubectx, stern, kubectl-tree 등 주요 플러그인이 터미널에서 실행되는 모습. 각 플러그인의 출력 형식과 색상 구분이 실제 작업 환경에서 어떻게 보이는지 확인할 수 있습니다.

    6. kubectl-df-pv — PersistentVolume 용량 현황 한눈에

    kubectl-df-pv는 PersistentVolume(영구 볼륨)의 사용량을 Linux df 명령어처럼 보여줍니다. 스토리지 모니터링할 때 Grafana 대시보드까지 열기 귀찮을 때 바로 CLI에서 확인할 수 있어서 편해요.

    # 설치
    kubectl krew install df-pv
    
    # 전체 PV 사용량 확인
    kubectl df-pv
    
    # 예시 출력
    # PVC           NAMESPACE     POD           CAPACITY  USED    AVAILABLE  PERCENTUSED  IUSED  IFREE   PERCENTIUSED
    # data-pvc      production    my-db-0       50Gi      23Gi    27Gi       46%          ...    ...     ...

    데이터베이스 파드 볼륨이 가득 차서 장애 난 경험 한 번쯤 있으시죠? 이걸로 주기적으로 확인하는 습관 들이시면 좋습니다. 💡

    7. kubectl-images — 파드에서 쓰는 이미지 목록 확인

    보안 감사나 이미지 버전 관리할 때 클러스터 전체에서 어떤 컨테이너 이미지가 쓰이고 있는지 한 번에 보고 싶을 때가 있어요. kubectl-images가 딱 그 역할입니다.

    # 설치
    kubectl krew install images
    
    # 현재 네임스페이스의 모든 이미지 목록
    kubectl images
    
    # 특정 네임스페이스
    kubectl images -n production
    
    # 전체 네임스페이스
    kubectl images -A

    이미지 태그에 latest 쓴 파드 찾아내서 혼낼 때(?) 유용합니다 ㅎㅎ. 보안팀에서 취약 이미지 버전 쓰는 파드 찾을 때도 요긴하게 씁니다.

    ⚠️ 플러그인 쓸 때 주의사항과 삽질 경험

    좋은 것만 얘기하면 섭섭하죠. 실제로 겪은 문제들도 공유합니다.

    PATH 충돌 문제

    krew로 설치한 플러그인과 시스템에 직접 설치된 바이너리가 이름이 겹치면 예상치 못한 동작이 생길 수 있어요. 예를 들어 stern을 brew로도 설치하고 krew로도 설치한 경우가 그렇습니다. which kubectl-stern으로 어느 경로가 우선인지 확인하는 습관을 들이세요.

    플러그인 버전 업데이트 누락

    krew로 설치한 플러그인은 자동 업데이트가 안 됩니다. 주기적으로 아래 명령어 실행해 주세요.

    # 설치된 플러그인 목록 확인
    kubectl krew list
    
    # 전체 업데이트
    kubectl krew upgrade
    
    # krew 자체 업데이트
    kubectl krew update

    클러스터 권한 문제

    일부 플러그인은 클러스터 전체를 조회하는 ClusterRole 권한이 필요합니다. 제한된 권한의 kubeconfig로 쓰다가 에러 나는 경우가 있어요. ⚠️ 권한 오류가 나면 플러그인 문제가 아니라 RBAC 권한 문제일 가능성이 높습니다. kubectl auth can-i로 먼저 확인해보세요.

    # 특정 동작 권한 확인
    kubectl auth can-i list pods -A
    kubectl auth can-i get persistentvolumes
    krew를 통한 kubectl 플러그인 설치 및 관리 워크플로우 다이어그램

    krew를 통한 kubectl 플러그인 설치·업데이트·삭제 워크플로우. 플러그인 인덱스에서 검색하고, 설치 후 PATH를 통해 kubectl 서브커맨드로 동작하는 흐름입니다.

    플러그인 비교 정리표

    지금까지 소개한 플러그인을 한 눈에 비교해 봤습니다.

    플러그인 krew 설치명 주요 기능 추천 상황
    kubectx ctx 클러스터 컨텍스트 전환 멀티 클러스터 환경
    kubens ns 네임스페이스 전환 네임스페이스 자주 변경시
    stern stern 다중 파드 로그 스트리밍 마이크로서비스 디버깅
    kubectl-neat neat YAML 정리·간소화 리소스 백업·마이그레이션
    kubectl-tree tree 리소스 의존관계 시각화 Helm 배포 구조 파악
    kubectl-whoami whoami 현재 인증 주체 확인 RBAC 트러블슈팅
    kubectl-df-pv df-pv PV 용량 현황 조회 스토리지 모니터링
    kubectl-images images 사용 중인 이미지 목록 보안 감사·버전 관리

    ✅ 한 번에 설치하는 스크립트

    위에서 소개한 플러그인을 한 번에 설치하고 싶으시면 아래 스크립트 쓰시면 됩니다.

    #!/bin/bash
    # kubectl 필수 플러그인 일괄 설치
    
    PLUGINS=(
      ctx
      ns
      stern
      neat
      tree
      whoami
      df-pv
      images
    )
    
    # krew 인덱스 업데이트
    kubectl krew update
    
    # 플러그인 설치
    for plugin in "${PLUGINS[@]}"; do
      echo "Installing kubectl $plugin..."
      kubectl krew install "$plugin"
    done
    
    echo "\n✅ 모든 플러그인 설치 완료!"
    kubectl krew list

    자주 묻는 질문 (FAQ)

    Q. krew 없이도 플러그인 설치할 수 있나요?

    네, 가능합니다. 플러그인 바이너리를 직접 다운로드해서 kubectl-플러그인이름 형태로 이름 짓고 $PATH에 있는 디렉토리에 넣으면 됩니다. 다만 버전 관리가 번거로워서 krew를 쓰는 게 훨씬 편해요.

    Q. 플러그인이 kubectl 버전과 호환이 안 될 수도 있나요?

    드물지만 있습니다. 특히 쿠버네티스 API 버전이 크게 바뀌는 경우 플러그인이 업데이트되기 전까지 문제가 생길 수 있어요. kubectl krew upgrade로 최신 버전 유지하시고, 문제 생기면 해당 플러그인의 GitHub 이슈 트래커 확인해 보세요.

    Q. 회사 보안 정책상 외부 플러그인 설치가 어려운데요?

    이 경우엔 플러그인 소스코드를 직접 검토하고 내부 아티팩토리(Artifactory) 같은 내부 저장소를 통해 배포하는 방식을 쓰는 팀도 있어요. krew도 커스텀 인덱스를 지원하니 내부 인덱스를 구축하는 것도 방법입니다.

    kubectl 생산성 향상을 위한 필수 플러그인 8종 요약 인포그래픽

    kubectl 생산성을 높이는 필수 플러그인 8종 요약. 각 플러그인의 역할과 추천 상황을 한눈에 정리한 인포그래픽입니다.

    마무리 — CLI 환경도 투자할 가치가 있습니다

    사실 처음엔 “플러그인 설치하고 관리하는 것도 귀찮은 일 아닌가?” 싶었는데, 한 번 손에 익으면 그냥 기본 kubectl로 돌아가기가 싫어지더라고요. 특히 stern이랑 kubectx/kubens는 진짜 하루에도 수십 번씩 쓰고 있습니다.

    홈랩에서 먼저 익히고 실무에 가져가는 방식을 추천드려요. 처음부터 프로덕션 클러스터에서 새 도구 쓰다가 실수하면 곤란하니까요 😅. 오늘 소개한 플러그인들은 조회 위주라 부작용 걱정은 크게 안 하셔도 됩니다.

    다음 글에서는 kubectl 단축키 설정과 쉘 자동완성(shell completion) 세팅 방법을 다뤄볼 예정입니다. 플러그인이랑 함께 쓰면 생산성이 한 단계 더 올라가거든요. 기대해 주세요! 💡

    혹시 제가 소개 못 한 플러그인 중에 여러분이 즐겨 쓰는 게 있으면 댓글로 알려주세요. 저도 계속 배우는 중이라 좋은 도구 있으면 바로 써보고 싶습니다 🙂

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

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