13년차의 서버실

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

[태그:] 쿠버네티스 관리

  • [k8s] Rancher 2.x에서 차세대로 마이그레이션: 실제 경험과 주요 변경점

    [k8s] Rancher 2.x에서 차세대로 마이그레이션: 실제 경험과 주요 변경점

    안녕하세요, 13년차 서버실 지킴이입니다. 오늘은 많은 분들이 고민하고 계실 법한 주제, 바로 Rancher 2.x 환경을 차세대 쿠버네티스 관리 아키텍처로 전환하는 경험에 대해 이야기해볼게요. 제목에는 ‘Rancher 3.x’라는 표현을 썼지만, 사실 직접적인 ‘Rancher 3.x’라는 버전이 명확하게 출시된 것은 아닙니다. 대신, Rancher를 활용한 쿠버네티스 관리 방식이 점차 진화하면서, 기존 2.x 시절의 방식과는 크게 달라진 미래 지향적인 아키텍처로의 전환 과정을 ‘3.x 마이그레이션’이라는 개념으로 풀어보려고 해요. 즉, 단순히 버전을 올리는 것을 넘어, 더 효율적이고 안정적인 쿠버네티스 운영을 위한 근본적인 변화를 고민하는 시간이라고 보시면 되겠습니다.

    저도 홈랩에서부터 시작해서 프로덕션 환경까지 다양한 규모의 쿠버네티스 클러스터를 Rancher 2.x로 관리해왔어요. 처음에는 하나의 Rancher 서버로 모든 클러스터를 중앙에서 관리하는 방식이 정말 편하더라고요. 하지만 클러스터의 수가 늘어나고, 요구사항이 복잡해지면서 중앙 집중형 관리의 한계를 느끼게 됐습니다. 특히, Rancher 서버 자체의 안정성이나 업그레이드 부담, 그리고 GitOps(깃옵스)와 같은 최신 트렌드를 통합하는 데 대한 고민이 많았거든요. 그래서 ‘이제는 좀 더 분산되고 선언적인 방식으로 가야 하지 않을까?’ 하는 생각을 하게 됐습니다. 이런 고민을 하고 계신 분들이라면 오늘 제 이야기가 작은 팁이라도 될 수 있을 겁니다. 제가 직접 삽질해가며 얻은 경험들을 솔직하게 공유해볼게요!

    개념 설명: Rancher 2.x와 차세대 아키텍처의 차이

    먼저, 우리가 이야기하는 Rancher 2.x는 보통 RKE1(Rancher Kubernetes Engine 1) 기반의 쿠버네티스 클러스터들을 Rancher Management Server(랜처 관리 서버)가 중앙에서 프로비저닝하고 관리하는 형태를 의미합니다. 웹 UI를 통해 클러스터를 생성하고, 워크로드를 배포하며, 모니터링까지 한곳에서 할 수 있었죠. 정말 편리하고 직관적입니다. 하지만 단점도 명확해요.

    • 중앙 집중형 의존성: Rancher Management Server에 장애가 발생하면 모든 하위 클러스터 관리에 문제가 생길 수 있습니다.
    • 관리 복잡성 증가: 클러스터가 많아질수록 관리 서버 자체의 부담도 커집니다.
    • GitOps 통합의 어려움: UI 기반의 작업이 많아 GitOps 파이프라인과 완벽하게 통합하기 쉽지 않은 부분이 있었습니다.

    그렇다면 여기서 말하는 ‘차세대 아키텍처’ 혹은 ‘3.x스러운’ 접근 방식은 뭘까요? 저는 주로 RKE2(Rancher Kubernetes Engine 2)나 K3s(케이쓰리s)와 같은 경량화되거나 보안이 강화된 쿠버네티스 배포판을 활용하고, GitOps 원칙을 적극적으로 도입하여 선언적(Declarative)으로 클러스터와 애플리케이션을 관리하는 방식을 의미한다고 봐요. 이제 더 이상 Rancher Management Server가 클러스터의 모든 것을 직접 제어하기보다는, 클러스터 자체의 견고함과 Git을 통한 선언적 관리에 초점을 맞추는 거죠. Rancher는 이런 클러스터들을 통합적으로 보여주고 관리하는 ‘플랫폼’으로서의 역할에 더 집중하게 됩니다.

    Rancher 2.x 중앙 집중형 관리와 차세대 분산형 GitOps 아키텍처 비교 다이어그램

    Rancher 2.x의 중앙 집중형 관리 방식과 차세대 분산형 GitOps 아키텍처의 주요 차이점을 시각적으로 보여주는 다이어그램입니다.

    실전 구현: 차세대 클러스터 환경 구축 전략

    기존 Rancher 2.x 환경을 ‘마이그레이션’하는 것은 단순히 버전을 올리는 것이 아니라, 새로운 클러스터를 구축하고 워크로드를 이전하는 과정에 가깝습니다. 제가 홈랩에서 여러 번 시도해본 결과, 크게 두 가지 접근 방식이 있더라고요.

    1. 새로운 RKE2/K3s 클러스터 프로비저닝

    먼저, 기존 Rancher 2.x 관리 서버에서 새로운 RKE2나 K3s 클러스터를 프로비저닝하는 방법을 소개할게요. Rancher 2.6 버전부터는 RKE2/K3s 클러스터 생성을 공식적으로 지원해서 훨씬 수월해졌습니다.

    1. Rancher UI 접속: 기존 Rancher Management Server의 웹 UI에 접속합니다.
    2. 클러스터 생성 메뉴 진입: ‘클러스터’ 탭에서 ‘클러스터 생성’ 버튼을 클릭합니다.
    3. RKE2/K3s 선택: ‘Rancher Kubernetes Engine 2 (RKE2)’ 또는 ‘K3s’를 선택합니다. 저는 보안과 성능을 고려해 RKE2를 선호하는 편입니다.
    4. 노드 구성 및 옵션 설정: 컨트롤 플레인(Control Plane), etcd, 워커(Worker) 노드를 구성하고 필요한 네트워크, CNI(Container Network Interface, 컨테이너 네트워크 인터페이스) (예: Canal, Calico 등) 옵션을 설정합니다. 이때, 클러스터의 고가용성(High Availability, HA)을 위해 컨트롤 플레인 노드를 최소 3개 이상으로 구성하는 것이 중요합니다.
    5. 클러스터 생성: 모든 설정을 마치고 클러스터를 생성하면, Rancher가 백그라운드에서 노드에 에이전트를 설치하고 RKE2/K3s를 배포합니다.

    다음은 RKE2 클러스터를 생성할 때 필요한 기본적인 설정 예시입니다.

    # 클러스터 생성 시 RKE2/K3s 설정 예시 (Rancher UI에서 구성)
    kubernetes_version: v1.27.x-rke2r1 # 원하는 RKE2 버전 선택
    cni: canal # CNI 플러그인 선택 (calico, cilium 등)
    cloud_provider: 
      name: aws # 클라우드 환경에 맞춰 설정 (baremetal, azure, vsphere 등)
    agent_options:
      node_taints:
        - "CriticalAddonsOnly=true:NoExecute"
      labels:
        - "node-role.kubernetes.io/control-plane=true"
        - "node-role.kubernetes.io/etcd=true"
    # 노드 추가는 Rancher UI에서 직접 진행하거나, Machine Group을 통해 자동화 가능
    

    이렇게 새 클러스터를 만들고 나면, 이제 기존 워크로드들을 새로운 클러스터로 이전할 준비가 된 겁니다. 이게 생각보다 쉽지 않아요. 애플리케이션 의존성부터 데이터 마이그레이션까지 고려할 게 많거든요. 제가 초반에 이걸 간과해서 밤샘 삽질을 좀 했습니다. 😅

    Rancher UI에서 RKE2 클러스터를 생성하는 화면 예시

    Rancher UI를 통해 새로운 RKE2 클러스터를 생성하는 화면 예시입니다. 여기서 다양한 옵션을 설정할 수 있습니다.

    2. 워크로드 및 데이터 마이그레이션

    가장 중요한 단계입니다. 워크로드 마이그레이션은 서비스 중단 시간을 최소화하는 방향으로 계획해야 해요. 몇 가지 팁을 드리자면:

    • 상태 없는(Stateless) 애플리케이션 먼저: 웹 서버, API 게이트웨이 등 상태를 저장하지 않는 애플리케이션부터 이전하여 위험을 줄입니다. Helm(헬름)이나 GitOps 툴(예: Argo CD, Flux CD)을 활용하면 배포를 선언적으로 관리하고 쉽게 이전할 수 있어요.
    • 상태 있는(Stateful) 애플리케이션: 데이터베이스와 같은 상태 있는 애플리케이션은 velero(벨레로)와 같은 백업 및 복구 툴을 사용하거나, 클라우드 제공업체의 볼륨 스냅샷 기능을 활용하여 데이터를 이전합니다. 이 과정에서 네트워크 지연(Latency)이나 데이터 정합성(Data Consistency)에 문제가 없는지 꼼꼼히 확인해야 해요.
    • 네트워크 설정: Ingress(인그레스, 외부 트래픽 진입점) 컨트롤러, Service(서비스) 타입, ExternalDNS(외부 DNS) 설정 등을 새 클러스터에 맞게 재구성해야 합니다. 특히 DNS 레코드를 변경할 때는 TTL(Time-To-Live, 캐시 유지 시간)을 짧게 설정하여 롤백에 대비하는 것이 좋습니다.
    # Argo CD를 이용한 GitOps 배포 예시
    # 새 클러스터에 Argo CD 설치 후, Git 리포지토리 연결
    
    # 1. Argo CD 설치 (새 클러스터에)
    kubectl create namespace argocd
    kubectl apply -n argocd -f https://raw.githubusercontent.com/argoproj/argo-cd/stable/manifests/install.yaml
    
    # 2. Argo CD CLI 설치 및 초기 비밀번호 확인
    # (생략)
    
    # 3. 애플리케이션 정의 (예: my-app.yaml)
    # applications/my-app.yaml 파일 생성
    # apiVersion: argoproj.io/v1alpha1
    # kind: Application
    # metadata:
    #   name: my-webapp
    #   namespace: argocd
    # spec:
    #   project: default
    #   source:
    #     repoURL: https://github.com/my-org/my-app-configs.git # 애플리케이션 설정이 있는 Git 레포지토리
    #     targetRevision: HEAD
    #     path: dev # Git 레포지토리 내 경로
    #   destination:
    #     server: https://kubernetes.default.svc
    #     namespace: my-webapp-prod # 배포할 네임스페이스
    #   syncPolicy:
    #     automated:
    #       prune: true
    #       selfHeal: true
    
    # 4. Argo CD에 애플리케이션 등록
    argocd app create my-webapp --repo https://github.com/my-org/my-app-configs.git --path dev --dest-server https://kubernetes.default.svc --dest-namespace my-webapp-prod
    

    이런 GitOps 툴을 활용하면, 기존 클러스터에서 사용하던 매니페스트(Manifest) 파일들을 Git 레포지토리에 올리고, 새 클러스터에 Argo CD를 설치하여 동기화하는 방식으로 쉽게 워크로드를 이전할 수 있어요. 코드로 인프라를 관리하는(Infrastructure as Code, IaC) 경험은 정말 혁신적입니다!

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

    제가 이 ‘차세대 전환’을 하면서 겪었던 몇 가지 삽질과 해결책들을 공유해볼게요.

    1. RKE1과 RKE2/K3s의 설정 차이: RKE1은 cluster.yml 파일을 기반으로 클러스터를 구성했지만, RKE2/K3s는 /etc/rancher/rke2/config.yaml (또는 k3s) 파일을 사용하거나 Helm 차트 등을 통해 더 세분화된 설정을 합니다. 기존 RKE1의 커스텀 설정(예: 추가 컨테이너 런타임 옵션, CNI 플러그인 설정)을 그대로 옮기려다 호환성 문제로 고생 좀 했어요. 새로운 배포판의 문서(Documentation)를 꼼꼼히 확인하는 것이 정말 중요합니다.
    2. StorageClass(스토리지 클래스) 호환성: 기존 클러스터에서 사용하던 PersistentVolume(영구 볼륨)과 PersistentVolumeClaim(영구 볼륨 클레임)은 StorageClass에 따라 다르게 동작할 수 있습니다. 특히 클라우드 환경이 변경되거나 스토리지 솔루션이 달라지면, 새로운 StorageClass를 정의하고 기존 PV/PVC를 마이그레이션해야 해요. 저는 홈랩에서 Longhorn(롱혼)을 사용하는데, 새 클러스터에 Longhorn을 재설치하고 데이터를 옮기는 과정에서 네트워크 설정 문제로 볼륨이 마운트되지 않아 애먹었습니다. 😅
    3. Cilium(실리움) CNI 도입 시 주의사항: 최근 Cilium이 성능과 보안 면에서 각광받고 있어서 저도 도입을 고려했는데, 기존 CNI(예: Canal)에서 Cilium으로 변경할 때는 네트워크 정책(Network Policy)과의 호환성, kube-proxy(쿠베 프록시) 없이 동작하는 모드(Direct Routing) 설정 등 고려할 사항이 많아요. 잘못하면 클러스터 내부 통신이 마비될 수 있으니 충분한 테스트 환경에서 검증해야 합니다.
    4. Rancher Management Server의 역할 변화: 기존에는 Rancher 서버가 모든 것을 다 해주는 느낌이었다면, 이제는 ‘관측성(Observability)’과 ‘정책 관리(Policy Management)’, 그리고 ‘클러스터 수명 주기 관리(Cluster Lifecycle Management)’에 더 집중하게 됩니다. 즉, 클러스터 내부의 복잡한 운영은 GitOps 툴과 같은 다른 도구들에게 맡기고, Rancher는 그 위에서 통합된 뷰를 제공하는 역할로 전환되는 거죠. 이 역할을 이해하지 못하면 ‘왜 Rancher가 예전처럼 클러스터 설정을 다 해주지 않지?’ 하고 헤맬 수 있습니다.

    검증 및 결과: 안정적인 차세대 환경 확인

    모든 워크로드 마이그레이션이 완료되었다면, 새로운 클러스터가 제대로 동작하는지 꼼꼼히 검증해야 합니다. 다음은 제가 주로 확인하는 항목들입니다.

    • 애플리케이션 정상 동작 확인: 모든 서비스의 엔드포인트에 접속하여 기능이 정상적으로 동작하는지 확인합니다. 특히 로깅(Logging)과 모니터링(Monitoring) 시스템이 제대로 연동되는지 확인하는 것이 중요해요.
    • 리소스 사용량 모니터링: Grafana(그라파나), Prometheus(프로메테우스) 등의 툴을 통해 CPU, 메모리, 네트워크, 디스크 I/O 등 클러스터 리소스 사용량을 면밀히 모니터링합니다. 기존 클러스터와 비교하여 비정상적인 패턴이 없는지 확인해야 합니다.
    • 클러스터 컴포넌트 상태: kubectl get pods -A 명령어로 모든 네임스페이스의 파드(Pod) 상태를 확인하고, kubectl get nodes로 노드 상태를 확인하여 문제가 없는지 점검합니다.
    • 데이터 정합성 검증: 상태 있는 애플리케이션의 경우, 이전된 데이터가 정확한지, 쓰기/읽기 작업이 문제없이 이루어지는지 반드시 확인해야 합니다.

    이 모든 검증을 마치고 나면, 드디어 새로운 차세대 Rancher 환경이 안정적으로 운영되는 것을 확인할 수 있어요. 이 뿌듯함이란! 🎉

    새롭게 마이그레이션된 Rancher 환경의 클러스터 대시보드

    새롭게 마이그레이션된 Rancher 환경에서 클러스터의 전반적인 상태를 보여주는 대시보드 화면입니다.

    마무리: 배운 점과 다음 단계

    Rancher 2.x에서 차세대 쿠버네티스 관리 환경으로의 전환은 단순히 소프트웨어 버전을 올리는 작업이 아니었습니다. 이는 클러스터 아키텍처, 운영 방식, 그리고 인프라 엔지니어의 역할에 대한 근본적인 재정의 과정이라고 생각해요. 저도 이 과정을 거치면서 많은 것을 배우고 삽질도 정말 많이 했습니다. 하지만 그 덕분에 GitOps의 중요성, RKE2/K3s의 장점, 그리고 Rancher가 나아가야 할 방향에 대해 더 깊이 이해하게 되었죠.

    Rancher 환경 전환 시 고려해야 할 핵심 사항 요약 인포그래픽

    Rancher 환경 전환 시 고려해야 할 핵심 사항들을 요약한 인포그래픽입니다.

    이번 마이그레이션을 통해 얻은 주요 교훈은 다음과 같습니다.

    • 계획의 중요성: 충분한 사전 계획 없이는 성공적인 마이그레이션은 불가능해요. 의존성 파악, 백업/복구 전략, 롤백 계획 등 모든 것을 미리 준비해야 합니다.
    • GitOps의 힘: 선언적인 코드로 인프라와 애플리케이션을 관리하는 GitOps는 복잡한 마이그레이션을 훨씬 수월하게 만들고, 향후 운영의 안정성을 높여줍니다.
    • 문서화의 중요성: 모든 변경 사항과 결정 사항을 문서화하여 팀원들과 공유하고, 향후 트러블슈팅에 활용해야 합니다.

    이제 새로운 클러스터에서 더 안정적이고 효율적인 쿠버네티스 운영을 할 수 있게 되었어요. 다음 단계로는 클러스터의 보안 강화(Security Hardening), 자동화된 재해 복구(Automated Disaster Recovery) 시스템 구축, 그리고 멀티 클러스터 환경에서의 서비스 메시(Service Mesh) 도입 등을 고민해볼 예정입니다. 여러분도 혹시 비슷한 고민을 하고 계시다면, 주저하지 말고 새로운 아키텍처로의 전환을 시도해보세요. 물론 삽질은 필수겠지만, 그만큼 얻는 것도 많을 겁니다! 궁금한 점이 있다면 언제든 댓글 남겨주세요. 다음 포스팅에서 또 재미있는 이야기로 찾아뵙겠습니다!

  • [k8s] Helm 차트 개발 및 배포 모범 사례: 효율적인 쿠버네티스 애플리케이션 관리

    [k8s] Helm 차트 개발 및 배포 모범 사례: 효율적인 쿠버네티스 애플리케이션 관리

    Helm 차트 개발 및 배포 모범 사례: 효율적인 쿠버네티스 애플리케이션 관리

    안녕하세요, 13년차의 서버실입니다. 오늘은 쿠버네티스(Kubernetes) 환경에서 애플리케이션을 효율적으로 관리하는 핵심 도구인 Helm(헬름) 차트 개발과 배포 모범 사례에 대해 이야기해보려 합니다. 혹시 쿠버네티스에 배포할 애플리케이션이 늘어나면서 수많은 YAML 파일을 일일이 관리하는 데 어려움을 겪고 계신가요? 저도 처음엔 Deployment, Service, Ingress(인그레스, 외부 트래픽 진입점) 등 수많은 YAML 파일을 만들고, 애플리케이션을 업데이트할 때마다 모든 파일을 수정하느라 삽질 좀 했습니다 ㅎㅎ. Helm은 이런 복잡성을 해결해주는 정말 강력한 패키지 매니저(Package Manager)거든요. 마치 리눅스에서 APT나 YUM으로 소프트웨어를 설치하듯이, 쿠버네티스에서는 Helm으로 애플리케이션을 손쉽게 배포하고 관리할 수 있으니까요.

    이번 글을 통해 Helm 차트 개발의 기초부터 실제 운영에서 제가 겪었던 경험과 팁까지 모두 알려드릴게요. 드디어 YAML 지옥에서 벗어날 때가 온 거죠!

    Helm의 기본적인 작동 방식을 보여주는 아키텍처 다이어그램입니다. Helm 3부터는 Tiller가 사라져 더 간소화되었죠.

    Helm, 왜 필요할까요? (개념 설명)

    Helm은 쿠버네티스용 패키지 매니저라고 쉽게 생각하시면 됩니다. 애플리케이션을 구성하는 모든 쿠버네티스 리소스(Resource)들을 차트(Chart)라는 하나의 묶음으로 정의하고, 이 차트를 통해 배포, 업그레이드, 롤백(Rollback) 등 라이프사이클(Lifecycle) 관리를 자동화하죠. 쉽게 말해, 복잡한 쿠버네티스 애플리케이션을 마치 하나의 설치 파일처럼 다룰 수 있게 해주는 거예요.

    Helm의 주요 구성 요소

    • Chart (차트): 하나의 쿠버네티스 애플리케이션을 정의하는 파일들의 묶음입니다. Docker 이미지, 환경 변수, 서비스, 인그레스 등 모든 구성 요소를 담고 있죠.
    • Release (릴리즈): 쿠버네티스 클러스터에 배포된 차트의 인스턴스를 말합니다. 하나의 차트로 여러 개의 릴리즈를 생성할 수 있습니다. 예를 들어, Nginx 차트 하나로 개발 환경용 Nginx와 운영 환경용 Nginx를 각각 다른 릴리즈로 배포할 수 있거든요.
    • Repository (레포지토리): 차트들을 저장하고 공유하는 공간입니다. Artifact Hub나 개인 S3 버킷 등을 활용할 수 있습니다.

    Helm 차트의 핵심 구성 요소

    Helm 차트는 여러 파일과 디렉토리로 구성되지만, 특히 중요한 몇 가지가 있습니다.

    구성 요소 설명
    Chart.yaml 차트의 이름, 버전, 설명 등 메타데이터(Metadata)를 정의합니다. 차트의 ‘신분증’ 같은 거죠.
    values.yaml 차트 템플릿에 주입할 기본 설정 값들을 정의합니다. 배포 환경에 따라 달라지는 값들을 외부에서 쉽게 변경할 수 있게 해줍니다. 제가 제일 많이 만지는 파일이기도 해요.
    templates/ 쿠버네티스 리소스 정의(Deployment, Service 등) 파일들이 들어있는 디렉토리거든요. Go Template 문법을 사용해서 values.yaml의 값들을 동적으로 주입합니다.
    _helpers.tpl 재사용 가능한 템플릿 조각이나 함수들을 정의하는 파일입니다. 차트가 복잡해질수록 코드 중복을 줄이고 가독성을 높이는 데 정말 유용하더라고요.

    실전! Helm 차트 개발 및 배포

    이제 실제로 간단한 Nginx 애플리케이션을 배포하는 Helm 차트를 만들어보겠습니다. 제가 직접 해보니 이렇게 단계별로 따라 하는 게 가장 이해하기 쉽더라고요.

    1단계: 차트 초기화

    Helm CLI를 사용하면 기본적인 차트 구조를 자동으로 생성해줍니다. 정말 편하죠!

    helm create my-nginx-chart

    이 명령어를 실행하면 my-nginx-chart라는 디렉토리 안에 기본적인 차트 파일들이 생성됩니다. 이 구조를 기반으로 우리 애플리케이션에 맞게 수정하는 거죠.

    2단계: values.yaml 설정

    생성된 my-nginx-chart/values.yaml 파일을 열어 Nginx 이미지와 서비스 포트 등을 설정해봅시다. 기본적으로 생성된 내용이 많지만, 간단하게 Nginx 관련 부분만 수정할게요.

    # my-nginx-chart/values.yaml
    replicaCount: 1
    
    image:
      repository: nginx
      pullPolicy: IfNotPresent
      # Overrides the image tag whose default is the chart appVersion.
      tag: "latest"
    
    service:
      type: ClusterIP
      port: 80
    
    # ... (나머지 부분은 기본값 유지 또는 주석 처리)
    

    여기서 image.repository와 image.tag를 Nginx 이미지로 설정하고, service.port를 80으로 지정했습니다. 나중에 배포할 때 이 값들을 변경하고 싶으면 helm install이나 helm upgrade 명령어에서 --set 옵션을 사용하면 돼요.

    3단계: 템플릿 수정 (Deployment, Service)

    templates/ 디렉토리 안에는 deployment.yaml, service.yaml 등이 있습니다. 이 파일들을 values.yaml에 정의한 값들을 사용하도록 수정해야 합니다. {{ .Values.image.repository }}와 같은 Go Template 문법으로 값을 가져올 수 있거든요.

    my-nginx-chart/templates/deployment.yaml 파일의 image 부분을 이렇게 수정합니다.

    # my-nginx-chart/templates/deployment.yaml
    apiVersion: apps/v1
    kind: Deployment
    metadata:
      name: {{ include "my-nginx-chart.fullname" . }}
      labels:
        {{- include "my-nginx-chart.labels" . | nindent 4 }}
    spec:
      replicas: {{ .Values.replicaCount }}
      selector:
        matchLabels:
          {{- include "my-nginx-chart.selectorLabels" . | nindent 6 }}
      template:
        metadata:
          {{- with .Values.podAnnotations }}
          annotations:
            {{- toYaml . | nindent 8 }}
          {{- end }}
        labels:
          {{- include "my-nginx-chart.selectorLabels" . | nindent 6 }}
        spec:
          containers:
            - name: {{ .Chart.Name }}
              image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}"
              imagePullPolicy: {{ .Values.image.pullPolicy }}
              ports:
                - name: http
                  containerPort: 80
                  protocol: TCP
              # ... (나머지 부분은 기본값 유지)
    

    그리고 my-nginx-chart/templates/service.yaml 파일의 port 부분을 이렇게 수정합니다.

    # my-nginx-chart/templates/service.yaml
    apiVersion: v1
    kind: Service
    metadata:
      name: {{ include "my-nginx-chart.fullname" . }}
      labels:
        {{- include "my-nginx-chart.labels" . | nindent 4 }}
    spec:
      type: {{ .Values.service.type }}
      ports:
        - port: {{ .Values.service.port }}
          targetPort: http
          protocol: TCP
          name: http
      selector:
        {{- include "my-nginx-chart.selectorLabels" . | nindent 4 }}
    

    이렇게 하면 values.yaml에서 정의한 이미지와 포트가 Deployment와 Service에 동적으로 적용됩니다. 코드를 보면 {{ include "my-nginx-chart.fullname" . }} 같은 구문이 보이는데, 이건 _helpers.tpl에 정의된 재사용 가능한 템플릿 함수거든요. 이런 식으로 차트의 가독성과 유지보수성을 높일 수 있더라고요.

    Helm 차트의 기본적인 구조와 개발, 배포 과정을 한눈에 볼 수 있는 워크플로우입니다.

    4단계: 차트 배포

    이제 우리가 만든 차트를 쿠버네티스 클러스터에 배포해봅시다. helm install 명령어를 사용합니다.

    helm install my-nginx ./my-nginx-chart
    • my-nginx는 우리가 배포하는 릴리즈(Release)의 이름입니다.
    • ./my-nginx-chart는 우리가 개발한 차트가 있는 로컬 경로를 의미합니다.

    명령어를 실행하면 Helm이 차트 템플릿을 렌더링(Rendering)하여 쿠버네티스 API 서버에 리소스들을 생성합니다. 🎉 드디어 Nginx 애플리케이션이 클러스터에 배포된 거예요!

    5단계: 차트 업데이트

    만약 Nginx 버전을 바꾸거나 Replica 수를 늘리고 싶다면 어떻게 할까요? values.yaml을 수정하고 helm upgrade 명령어를 사용하면 돼요.

    예를 들어, my-nginx-chart/values.yaml에서 replicaCount: 3으로 변경한 후:

    helm upgrade my-nginx ./my-nginx-chart

    이렇게 하면 Helm이 변경된 내용만 감지하여 기존 릴리즈를 안전하게 업데이트합니다. 정말 편리하죠? 롤백(Rollback)도 쉽게 할 수 있어서 운영 환경에서 장애 발생 시 빠르게 이전 버전으로 돌아갈 수 있습니다.

    ⚠️ 삽질 방지! Helm 차트 트러블슈팅 팁

    제가 직접 Helm 차트를 개발하면서 가장 많이 겪었던 삽질 중 하나는 바로 values.yaml과 templates 간의 변수 매핑(Mapping) 오류였습니다. YAML 문법은 들여쓰기(Indentation) 하나만 잘못돼도 바로 에러를 뿜어내거든요. 특히 복잡한 구조의 값을 참조할 때 경로를 잘못 지정하거나, 타입(Type)이 맞지 않아 문제가 생기곤 했습니다.

    주요 트러블슈팅 팁

    • helm template --debug --dry-run . 활용: 이 명령어는 실제로 배포하지 않고 차트가 렌더링(Rendering)된 최종 YAML 파일을 보여줍니다. 어디서 어떤 값이 잘못 들어갔는지, 템플릿 문법 오류는 없는지 확인하는 데 정말 큰 도움이 돼요. 제가 가장 많이 쓰는 디버깅(Debugging) 도구입니다.
    • --set 옵션으로 값 오버라이딩(Override): 배포 전에 특정 values.yaml 값을 테스트하고 싶을 때, helm install/upgrade --set image.tag=1.21 my-nginx ./my-nginx-chart 처럼 명령줄에서 값을 직접 오버라이딩해서 테스트해볼 수 있습니다.
    • YAML 문법 검사기 사용: Visual Studio Code 같은 IDE에서 YAML 린터(Linter) 확장을 사용하면 문법 오류를 실시간으로 잡을 수 있거든요.
    • _helpers.tpl 디버깅: _helpers.tpl에 복잡한 로직을 넣었다면, 해당 헬퍼를 사용하는 템플릿 파일에 임시로 {{ include "my-chart.myHelper" . | toYaml }}처럼 넣어서 출력 결과를 확인해보세요.

    배포 확인 및 결과 검증

    차트 배포가 성공적으로 완료되었다면, 이제 쿠버네티스 클러스터에서 Nginx 애플리케이션이 잘 실행되고 있는지 확인해야겠죠?

    배포된 릴리즈 확인

    helm list 명령어로 현재 클러스터에 배포된 모든 Helm 릴리즈를 확인할 수 있습니다.

    helm list
    NAME        NAMESPACE   REVISION    UPDATED                               STATUS      CHART               APP VERSION
    my-nginx    default     1           2023-10-27 10:00:00.123456 +0900 KST deployed    my-nginx-chart-0.1.0 1.25.1
    

    쿠버네티스 리소스 확인

    kubectl 명령어를 사용해서 실제 쿠버네티스 리소스들이 잘 생성되었는지 확인합니다.

    kubectl get pods -l app.kubernetes.io/instance=my-nginx
    NAME                          READY   STATUS    RESTARTS   AGE
    my-nginx-my-nginx-chart-xyz   1/1     Running   0          5m
    
    kubectl get svc -l app.kubernetes.io/instance=my-nginx
    NAME                TYPE        CLUSTER-IP   EXTERNAL-IP   PORT(S)   AGE
    my-nginx-my-nginx   ClusterIP   10.X.X.X     <none>        80/TCP    5m
    

    이렇게 Nginx Pod와 Service가 정상적으로 실행되는 것을 볼 수 있습니다. 만약 Ingress를 설정했다면 외부에서 접속도 가능하겠죠!

    쿠버네티스 대시보드에서 my-nginx 릴리즈로 배포된 Nginx 애플리케이션의 Pod와 Service 상태를 확인하는 모습입니다.

    Helm 차트 개발 및 배포 모범 사례

    Helm 차트를 효과적으로 사용하려면 몇 가지 모범 사례(Best Practices)를 따르는 것이 좋습니다. 제가 13년 동안 인프라를 만지면서 느낀 점은, 잘 만들어진 템플릿 하나가 수많은 야근을 줄여준다는 거예요 ㅎㅎ. 다음은 제가 꼭 지키려고 노력하는 모범 사례들입니다.

    • values.yaml을 통한 설정 외부화: 환경별로 달라지는 값들은 반드시 values.yaml에 정의하고, 템플릿에서는 이 값들을 참조하도록 만듭니다. 하드코딩(Hard-coding)은 피해야 합니다.
    • 템플릿 분리 및 재사용: 복잡한 템플릿은 _helpers.tpl에 함수 형태로 분리하여 재사용성을 높이고 가독성을 확보합니다.
    • Semantic Versioning(시맨틱 버저닝): Chart.yaml에 정의된 차트 버전은 MAJOR.MINOR.PATCH 규칙을 따르는 것이 좋습니다. 변경 사항을 명확히 하고 롤백 시 혼란을 줄일 수 있거든요.
    • Liveness/Readiness Probes (활성/준비 프로브) 설정: 애플리케이션의 헬스 체크(Health Check)를 위한 프로브를 반드시 설정하여 안정적인 서비스 운영을 돕습니다.
    • Resource Limits (리소스 제한) 명시: Pod가 사용할 CPU, 메모리 리소스를 제한하여 클러스터의 안정성을 확보합니다. 의도치 않은 리소스 고갈을 방지할 수 있거든요.
    • README.md 문서화: 차트의 사용법, 설정 옵션, 예시 등을 README.md 파일에 상세히 작성하여 다른 사용자들이 쉽게 이해하고 활용할 수 있도록 합니다.
    • 보안 고려: 민감한 정보는 Secret(시크릿)으로 관리하고, RBAC(Role-Based Access Control) 설정을 통해 최소 권한 원칙을 지킵니다.

    Helm 차트 개발 및 배포 시 고려해야 할 핵심 모범 사례들을 인포그래픽으로 정리해봤습니다.

    마무리하며: 효율적인 쿠버네티스 관리의 시작

    오늘은 Helm 차트 개발부터 배포, 그리고 몇 가지 모범 사례까지 쭉 훑어봤습니다. Helm은 쿠버네티스 애플리케이션 배포와 관리를 정말 쉽고 효율적으로 만들어주는 강력한 도구입니다. 처음엔 낯설 수 있지만, 한 번 익숙해지면 수많은 YAML 파일 지옥에서 벗어날 수 있을 거예요. 저도 처음엔 이게 뭔가 싶었는데, 지금은 없으면 허전할 정도로 잘 쓰고 있습니다.

    Helm을 잘 활용하면 CI/CD 파이프라인(Pipeline)과도 쉽게 통합하여 배포 자동화를 구축할 수 있습니다. 여러분의 쿠버네티스 환경이 더욱 스마트해지고 안정적으로 운영되는 데 Helm이 큰 역할을 할 거라고 확신합니다. 다음 글에서는 Helm 차트를 OCI 레지스트리(Registry)에 저장하고 관리하는 방법에 대해 다뤄볼 예정이니, 기대해주세요! 😊