13년차의 서버실

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

[태그:] Kubernetes

  • [k8s] 쿠버네티스 Ingress Controller 비교: Nginx, Traefik, HAProxy 장단점 분석

    [k8s] 쿠버네티스 Ingress Controller 비교: Nginx, Traefik, HAProxy 장단점 분석

    [쿠버네티스] Ingress Controller 비교: Nginx, Traefik, HAProxy 장단점 분석

    안녕하세요, 13년차 인프라 엔지니어입니다. 쿠버네티스를 운영하면서 가장 자주 마주하는 고민 중 하나가 바로 외부 트래픽을 어떻게 효율적으로 서비스에 연결할까 하는 부분일 겁니다. 저도 처음엔 NodePort나 LoadBalancer 서비스만으로 충분하다고 생각했어요. 하지만 서비스가 많아지고, TLS(Transport Layer Security) 인증서 관리나 경로 기반 라우팅 같은 복잡한 요구사항들이 생기면서 Ingress(인그레스, 외부 트래픽 진입점)의 중요성을 절실히 깨달았습니다.

    특히 어떤 Ingress Controller를 사용해야 할지 결정하는 건 늘 어려운 선택이었어요. Nginx, Traefik, HAProxy… 저마다 장단점이 명확해서 어떤 상황에 어떤 걸 써야 좋을지 항상 고민하게 되더라고요. 제가 홈랩과 실제 프로덕션 환경에서 다양한 Ingress Controller들을 직접 써보고 겪었던 삽질 경험을 바탕으로, 오늘은 이 세 가지 주요 Ingress Controller들의 특징과 장단점을 꼼꼼하게 비교해 보려고 합니다. 혹시 어떤 Ingress Controller를 선택해야 할지 막막하셨다면, 이 글이 여러분의 고민을 덜어줄 수 있기를 바랍니다!

    그림 1: 쿠버네티스 Ingress Controller를 통한 외부 트래픽 라우팅 개요

    1. 쿠버네티스 Ingress Controller, 왜 필요한가?

    쿠버네티스 클러스터 내의 애플리케이션들은 기본적으로 클러스터 내부에서만 접근 가능합니다. 외부에서 이 서비스들에 접근하게 하려면 몇 가지 방법이 있죠. 대표적으로 NodePort나 LoadBalancer 타입의 Service를 사용하는 건데요.

    • NodePort: 모든 워커 노드의 특정 포트를 열어서 서비스에 접근하게 합니다. 간단하지만 포트 관리가 어렵고, 트래픽 분산 기능을 직접 구현해야 하는 단점이 있어요.
    • LoadBalancer: 클라우드 제공업체의 로드밸런서를 프로비저닝하여 외부 IP를 통해 서비스에 접근하게 합니다. 편리하지만, 서비스 하나당 로드밸런서가 하나씩 할당되어 비용 부담이 커질 수 있고, 복잡한 라우팅 규칙을 적용하기 어렵습니다.

    여기서 Ingress가 등장합니다. Ingress는 클러스터 외부에서 내부 서비스로 들어오는 HTTP/HTTPS 트래픽을 관리하는 API 오브젝트예요. 쉽게 말해, ‘외부 트래픽의 문지기’ 역할을 한다고 보시면 됩니다. 하나의 외부 IP와 포트를 통해 여러 서비스로 트래픽을 분산하고, 도메인 기반 라우팅, 경로 기반 라우팅, TLS 종료(Termination) 등을 처리할 수 있게 해줍니다.

    그리고 이 Ingress 리소스에 정의된 규칙들을 실제로 읽고 구현하여 트래픽을 라우팅해주는 것이 바로 Ingress Controller입니다. Ingress Controller는 클러스터 내부에서 Pod 형태로 동작하며, Ingress 리소스의 변경을 감지하고, 그 규칙에 따라 실제 로드밸런싱 설정을 동적으로 업데이트합니다. 마치 웹 서버나 리버스 프록시처럼 동작하는 셈이죠. 결국 Ingress는 ‘규칙’이고, Ingress Controller는 그 ‘규칙을 실행하는 엔진’이라고 이해하시면 편할 겁니다.

    2. 주요 Ingress Controller 심층 비교 분석

    자, 이제 본론으로 들어가서 우리가 주로 사용하는 세 가지 Ingress Controller인 Nginx, Traefik, HAProxy를 하나씩 파헤쳐 봅시다. 제가 직접 써보면서 느꼈던 장단점과 팁들을 솔직하게 공유해 드릴게요.

    2.1. Nginx Ingress Controller: 국룰에는 이유가 있죠

    Nginx Ingress Controller는 아마 쿠버네티스 환경에서 가장 널리 사용되는 Ingress Controller일 겁니다. 그만큼 안정성과 기능 면에서 검증된 솔루션이라고 할 수 있죠. 저도 처음 쿠버네티스를 도입했을 때 Nginx Ingress부터 시작했습니다.

    • ✅ 장점
      • 압도적인 안정성과 성능: Nginx 자체가 고성능 웹 서버이자 리버스 프록시로 유명하죠. Ingress Controller도 그 명성에 걸맞게 안정적이고 뛰어난 성능을 보여줍니다.
      • 풍부한 기능과 설정 옵션: HTTP/HTTPS 라우팅, URL 리라이트(Rewrite), 세션 어피니티(Session Affinity), WAF(Web Application Firewall) 연동 등 다양한 기능을 지원합니다. Nginx 설정 파일에 익숙하다면 복잡한 룰도 구현하기 쉬워요.
      • 넓은 사용자층과 커뮤니티: 워낙 많은 사람이 사용하다 보니, 문제가 생겼을 때 정보를 찾거나 도움을 받기 정말 쉽습니다. Stack Overflow나 GitHub 이슈 등 자료가 많아요.
      • 익숙함: 리눅스에서 Nginx 좀 다뤄봤다 하는 분들은 설정 방식이 익숙해서 빠르게 적응할 수 있습니다.
    • ⚠️ 단점
      • 설정의 복잡성: Nginx ConfigMap이나 Annotation을 통해 Nginx 설정을 직접 제어하는 방식이라, 처음에는 복잡하게 느껴질 수 있어요. 특히 고급 설정으로 가면 Nginx 문법을 알아야 하는 경우가 많습니다.
      • 동적 설정의 제약: Nginx의 특성상 설정 변경 시 리로드가 필요한 경우가 있는데, Ingress Controller가 내부적으로 처리하긴 하지만 완전히 실시간으로 모든 설정을 변경하기는 어렵습니다.
      • CRD(Custom Resource Definition) 미활용: 다른 Ingress Controller들이 CRD를 적극 활용하여 쿠버네티스 네이티브한 설정 경험을 제공하는 반면, Nginx는 Annotation 의존도가 높은 편입니다.

    제가 실제로 Nginx Ingress Controller를 사용하면서 느꼈던 건, “역시 국룰은 다르구나” 하는 점이었어요. 대규모 환경에서도 끄떡없이 잘 돌아갔고, 웬만한 라우팅 규칙은 다 구현할 수 있었거든요. 다만, 개발팀에서 새로운 경로를 자주 추가하거나 변경할 때마다 Ingress 리소스를 수정하고 적용하는 과정이 조금 번거롭다고 느낄 때가 있었습니다.

    2.2. Traefik Ingress Controller: 개발자를 위한 자동화 끝판왕

    Traefik Ingress Controller는 클라우드 네이티브 환경에 최적화된 HTTP 리버스 프록시 및 로드 밸런서예요. 특히 동적인 환경에서 그 진가를 발휘하는데, 저도 홈랩에서 간단한 서비스들을 올릴 때 Traefik을 정말 유용하게 쓰고 있습니다.

    • ✅ 장점
      • 뛰어난 동적 설정 기능: 쿠버네티스 Ingress 리소스뿐만 아니라 Service, Deployment 등 다양한 쿠버네티스 리소스를 감지하여 자동으로 라우팅 규칙을 생성합니다. CRD를 적극 활용해서 쿠버네티스 친화적인 설정 경험을 제공해요.
      • 자동 TLS 인증서 관리 (Let’s Encrypt): Traefik의 가장 강력한 기능 중 하나입니다. Let’s Encrypt와 연동하여 도메인에 대한 TLS 인증서를 자동으로 발급받고 갱신해줍니다. 💡 이거 진짜 편하더라고요! 제가 직접 인증서 갱신하느라 삽질했던 시간이 정말 아깝게 느껴질 정도였습니다.
      • 내장 대시보드: Traefik의 설정과 현재 라우팅되고 있는 서비스들의 상태를 한눈에 볼 수 있는 웹 대시보드를 제공해요. 디버깅이나 모니터링에 정말 유용하거든요.
      • 경량화 및 빠른 시작: Nginx 대비 가볍고 빠르게 시작할 수 있어 개발 환경이나 소규모 클러스터에 특히 적합합니다.
    • ⚠️ 단점
      • Nginx 대비 상대적으로 작은 커뮤니티: 물론 Traefik도 큰 커뮤니티를 가지고 있지만, Nginx만큼은 아닙니다. 아주 특수한 설정이나 문제 발생 시 정보 탐색이 조금 어려울 수 있어요.
      • 고급 설정 시 학습 곡선: 기본적인 설정은 쉽지만, Nginx에서 제공하는 복잡하고 미세한 튜닝 옵션들을 Traefik에서 구현하려면 Traefik의 설정 방식을 익혀야 합니다.
      • 성능: 대부분의 경우 충분하지만, 극단적인 고성능 트래픽 처리나 특정 벤치마크에서는 Nginx가 우위를 보일 수 있어요.

    저는 Traefik을 처음 써봤을 때, 자동 Let’s Encrypt 기능에 정말 감탄했습니다. 홈랩에 여러 서비스 올리면서 매번 인증서 발급받고 갱신하는 게 귀찮았거든요. Traefik은 이런 번거로움을 한 방에 해결해 줘서 개발자들이 정말 좋아할 만한 툴이라고 생각해요. 동적으로 서비스가 추가되거나 스케일 아웃될 때도 설정 변경 없이 알아서 라우팅 해주는 점도 매우 편리했습니다.

    2.3. HAProxy Ingress Controller: 성능과 유연성의 끝판왕

    HAProxy Ingress Controller는 고성능과 높은 유연성이 필요한 환경에서 빛을 발합니다. HAProxy는 이미 오랜 시간 동안 다양한 프로덕션 환경에서 검증된 강력한 로드 밸런서예요. L4(전송 계층) 및 L7(애플리케이션 계층) 로드 밸런싱 기능을 모두 지원하며, 매우 정교한 트래픽 제어가 가능하죠.

    • ✅ 장점
      • 극강의 성능과 안정성: HAProxy 자체의 강력한 성능을 그대로 가져옵니다. 대규모 트래픽 처리와 높은 동시 접속 처리에 매우 강해요.
      • 고급 로드 밸런싱 기능: 다양한 로드 밸런싱 알고리즘(Round Robin, Least Connections, Source IP Hashing 등), 세션 유지, 헬스 체크, 서버 가중치 부여 등 섬세한 제어가 가능합니다.
      • L4/L7 지원: 단순히 HTTP/HTTPS뿐만 아니라 TCP 트래픽에 대한 로드 밸런싱도 지원하여 더 넓은 범위의 애플리케이션에 적용할 수 있어요.
      • 매우 유연한 설정: HAProxy의 설정 문법을 통해 매우 복잡하고 정교한 트래픽 처리 룰을 구현할 수 있습니다.
    • ⚠️ 단점
      • 상대적으로 높은 복잡성: Nginx나 Traefik에 비해 설정 파일이 복잡하고, HAProxy 자체의 문법에 대한 이해가 필요해요. 학습 곡선이 좀 가파른 편입니다.
      • 동적 설정의 제약: Nginx와 유사하게, HAProxy의 설정을 동적으로 변경하는 데는 어느 정도 제약이 있습니다. 물론 Ingress Controller가 이를 최적화하지만, Traefik처럼 완전한 자동화를 기대하기는 어렵습니다.
      • 적은 사용자층: Nginx나 Traefik에 비해 쿠버네티스 Ingress Controller로서의 사용자층은 상대적으로 적은 편이에요.

    제가 HAProxy Ingress Controller를 써봤던 경험은, “이건 진짜 성능이 중요하거나, 표준적인 Ingress로는 부족한 복잡한 요구사항이 있을 때 쓰는 거구나” 하는 느낌이었습니다. 특히 특정 TCP 포트를 외부로 노출하면서 부하 분산을 해야 할 때 아주 유용하더라고요. 하지만 일반적인 웹 서비스 라우팅에는 Nginx나 Traefik이 더 적합하다고 느꼈어요. 굳이 HAProxy의 강력한 기능을 다 쓰지 않으면서 복잡한 설정을 감당할 필요는 없으니까요.

    그림 2: 주요 Ingress Controller들의 핵심 특징 비교

    3. 어떤 Ingress Controller를 선택해야 할까요? (선택 가이드)

    세 가지 Ingress Controller의 특징을 살펴봤으니, 이제 여러분의 환경과 요구사항에 맞춰 어떤 것을 선택해야 할지 가이드라인을 제시해 드릴게요. “정답은 없다”는 말이 식상하게 들릴 수도 있지만, 인프라 엔지니어의 세계에서는 정말 맞는 말입니다. 각자의 상황에 맞는 최적의 선택이 중요하거든요. 💡

    아래 표는 각 Ingress Controller의 주요 특성을 비교하여 선택에 도움을 주기 위한 것입니다.

    기준 Nginx Ingress Controller Traefik Ingress Controller HAProxy Ingress Controller
    성능 및 안정성 매우 우수 (검증됨) 우수 (대부분의 경우 충분) 최고 (고성능, L4/L7)
    설정 복잡도 중 (Annotation, ConfigMap 이해 필요) 하 (CRD 기반, 자동화 강점) 상 (HAProxy 문법 이해 필요)
    동적 설정 중 (리로드가 필요할 수 있음) 최상 (실시간, 자동 감지) 중 (Nginx와 유사)
    TLS 관리 수동 또는 cert-manager 연동 자동 (Let’s Encrypt 내장) 수동 또는 cert-manager 연동
    커뮤니티/자료 매우 풍부 풍부 상대적으로 적음
    주요 사용 시나리오 일반적인 웹 서비스, 레거시 시스템, 익숙함 중시 클라우드 네이티브, 개발 환경, 자동화, Let’s Encrypt 활용 극단적 고성능, 복잡한 L4/L7 규칙, 특정 TCP 서비스

    그림 3: 환경과 요구사항에 따른 Ingress Controller 선택 결정 가이드

    요약하자면:

    • 가장 보편적이고 안정적인 선택을 원한다면? ➡️ Nginx Ingress Controller. 특히 Nginx에 대한 경험이 많고, 대규모 서비스에서 검증된 안정성을 추구한다면 좋은 선택입니다.
    • 설정의 간결함과 자동화를 선호한다면? ➡️ Traefik Ingress Controller. 특히 Let’s Encrypt 자동화와 동적인 서비스 환경에 강점을 보여요. 개발팀이 빠르게 서비스를 배포하고 테스트하는 환경에 안성맞춤입니다.
    • 극단적인 성능이나 L4/L7 고급 기능이 필요하다면? ➡️ HAProxy Ingress Controller. 표준 Ingress로는 해결하기 어려운 복잡한 트래픽 제어나 특정 TCP 서비스 로드 밸런싱에 적합해요. 다만 학습 곡선과 설정 복잡도를 감수해야 합니다.

    4. ⚠️ 실제 겪은 삽질 경험과 트러블슈팅 팁

    제가 이 바닥에서 13년을 구르면서 느낀 건, 아무리 좋은 툴이라도 삽질은 피할 수 없다는 겁니다. 특히 Ingress Controller는 외부와 내부를 잇는 중요한 컴포넌트라 문제가 생기면 서비스 전체가 먹통이 될 수 있죠. 제가 겪었던 몇 가지 삽질 경험과 해결 팁을 공유해 드릴게요.

    1. Ingress Controller Pod가 Pending 상태? 권한 문제!
      처음 Ingress Controller를 배포했을 때 Pod가 계속 Pending 상태거나 CrashLoopBackOff에 빠지는 경우가 있었습니다. 확인해보니 대부분 RBAC(Role-Based Access Control) 권한 문제더라고요. Ingress Controller는 Ingress, Service, Endpoint 같은 쿠버네티스 리소스들을 읽고 변경해야 하므로, 충분한 권한을 가진 ServiceAccount, ClusterRole, ClusterRoleBinding이 제대로 설정되어 있어야 합니다. 공식 문서의 배포 YAML을 꼭 참고해서 필요한 권한들을 잘 설정해주세요.
    2. TLS 인증서 적용이 안 돼요! Secret 이름 확인 필수!
      HTTPS를 적용하려고 Ingress 리소스에 tls 섹션을 추가했는데, 브라우저에서 계속 “안전하지 않은 연결” 경고가 뜨는 겁니다. 며칠을 헤맸는데, 알고 보니 Ingress 리소스의 secretName 필드에 오타가 있었어요! 🤦‍♂️ 쿠버네티스에서 Secret은 대소문자를 구분하고, 이름 하나라도 틀리면 인식이 안 됩니다. Ingress 리소스의 tls.secretName과 실제 Secret 오브젝트의 metadata.name이 정확히 일치하는지 꼼꼼히 확인해야 합니다. cert-manager를 사용하면 이런 수동적인 오류를 많이 줄일 수 있어서 강력히 추천합니다!
    3. 외부에서 접근이 안 돼요! Service 타입이 문제?
      Ingress Controller 자체는 잘 동작하는데, 외부에서 Ingress IP로 접근이 안 되는 경우가 있었습니다. 이건 대부분 Ingress Controller Service의 타입 설정 문제였어요. 클라우드 환경에서는 보통 type: LoadBalancer를 사용해서 외부 IP를 할당받는데, 온프레미스 환경이나 특정 클러스터에서는 type: NodePort나 HostPort를 사용해야 할 수도 있습니다. 클러스터의 네트워크 구성과 환경에 맞는 Service 타입을 선택하는 것이 중요해요. MetalLB 같은 온프레미스 로드밸런서 솔루션도 고려해볼 만합니다.
    4. 동일 포트 충돌! 여러 Ingress Controller 사용 시 주의!
      간혹 하나의 클러스터에 여러 종류의 Ingress Controller를 배포하려는 경우가 있습니다. 예를 들어 Nginx와 Traefik을 같이 쓰는 거죠. 이때 주의해야 할 점은 각 Ingress Controller가 사용하는 외부 포트가 겹치지 않아야 한다는 겁니다. 예를 들어 Nginx가 80/443 포트를 NodePort로 사용하고 있는데, Traefik도 동일한 포트로 NodePort를 열려고 하면 충돌이 발생합니다. 각 Ingress Controller에 고유한 외부 포트나 별도의 LoadBalancer Service를 할당해야 합니다.

    5. Ingress Controller 동작 검증 및 결과 확인

    Ingress Controller와 Ingress 리소스를 배포하고 나면, 제대로 동작하는지 확인하는 과정이 필수입니다. 제가 주로 사용하는 몇 가지 방법을 알려드릴게요.

    1. Ingress 리소스 상태 확인
      가장 먼저 kubectl get ingress 명령어로 Ingress 리소스가 잘 생성되었는지 확인합니다. ADDRESS 컬럼에 Ingress Controller의 외부 IP 주소가 할당되었는지 봐야 해요.
    2. kubectl get ingress -n my-namespace
      # 출력 예시:
      # NAME             CLASS    HOSTS              ADDRESS          PORTS     AGE
      # my-app-ingress   nginx    myapp.example.com   192.168.1.100    80, 443   5m
      

      그리고 kubectl describe ingress <ingress-name> 명령어로 Ingress 리소스의 상세 정보를 확인합니다. Rules 섹션에 정의한 라우팅 규칙들이 제대로 표시되는지, Events 섹션에 오류는 없는지 확인해야 해요.

    3. Ingress Controller Pod 로그 확인
      Ingress Controller Pod의 로그를 확인하는 것은 문제 해결의 첫걸음입니다. kubectl logs -f <ingress-controller-pod-name> -n <ingress-controller-namespace> 명령어로 실시간 로그를 보면서 트래픽이 들어올 때 어떤 동작을 하는지, 오류 메시지는 없는지 파악할 수 있습니다.
    4. 외부에서 curl 테스트
      가장 확실한 방법은 Ingress Controller의 외부 IP(또는 도메인)로 직접 curl 명령어를 날려보는 겁니다.
    5. curl -v http://myapp.example.com/path
      curl -v https://myapp.example.com/secure-path
      

      HTTP 응답 코드(200 OK 등)와 실제 서비스의 응답이 제대로 오는지 확인하세요. 특히 HTTPS의 경우 인증서 정보가 올바르게 나오는지도 함께 확인해야 합니다.

    6. Traefik 대시보드 확인 (Traefik 사용 시)
      Traefik Ingress Controller를 사용한다면, 내장된 대시보드를 통해 현재 활성화된 라우터, 서비스, 미들웨어 등의 상태를 시각적으로 확인할 수 있어요. 대시보드 접근 설정은 Traefik 배포 시 함께 구성해야 합니다. 🎉

    6. 마무리하며: 13년차 엔지니어의 선택, 그리고 다음 단계

    오늘은 쿠버네티스 Ingress Controller의 핵심 플레이어인 Nginx, Traefik, HAProxy의 장단점을 비교하고, 어떤 상황에 어떤 컨트롤러가 적합한지 제 경험을 바탕으로 이야기해 봤습니다.

    다시 한번 정리해 보면:

    • Nginx Ingress Controller: 안정성, 성능, 범용성 측면에서 가장 검증된 선택이에요. 대부분의 웹 서비스 환경에 무난하게 적용할 수 있습니다.
    • Traefik Ingress Controller: 동적인 환경, 빠른 개발 주기, 그리고 Let’s Encrypt 자동화가 중요하다면 최고의 선택입니다. 홈랩이나 개발/테스트 환경에서 빛을 발하죠.
    • HAProxy Ingress Controller: 극강의 성능과 복잡한 L4/L7 로드 밸런싱 규칙이 필요할 때 고려해볼 만합니다. 하지만 학습 곡선이 높다는 점을 인지해야 해요.

    저 같은 13년차 엔지니어에게 “그래서 뭘 추천하냐?”고 물어보신다면… 사실 정답은 없습니다. 😅 하지만 저의 개인적인 경험으로는, 일반적인 웹 서비스라면 Nginx Ingress Controller로 시작해서 안정성을 확보하고, 개발팀의 빠른 배포와 Let’s Encrypt 자동화가 절실하다면 Traefik을 고려하는 편입니다. HAProxy는 정말 특수한 경우에만 꺼내 드는 비장의 무기 같은 느낌이랄까요?

    Ingress Controller는 쿠버네티스 클러스터의 ‘얼굴’과 같은 존재입니다. 여러분의 서비스 특성과 운영 환경을 충분히 고려해서 최적의 선택을 하시길 바랍니다. 다음 글에서는 특정 Ingress Controller를 Helm 차트를 이용해 실제로 배포하고 설정하는 과정을 좀 더 자세히 다뤄보도록 하겠습니다. 그때까지 즐거운 쿠버네티스 여정 되세요! 💪

    그림 4: Nginx, Traefik, HAProxy Ingress Controller 최종 요약 비교

  • [k8s] ArgoCD GitOps CI/CD 구축: 쿠버네티스 자동 배포 완벽 가이드

    배포가 두려운 분들께 — GitOps가 바꿔놓은 제 일상

    솔직히 말씀드리면, 저도 예전에는 배포가 제일 무서웠어요. 스테이징에서는 멀쩡하던 게 프로덕션에 올리면 왜인지 모르게 터지고, “누가 언제 뭘 바꿨지?” 하고 kubectl로 이것저것 뒤지다 보면 어느새 새벽 2시가 되어 있는 그런 날들이요. 쿠버네티스를 도입하고 나서도 한동안은 이 상황이 크게 달라지지 않았습니다. 오히려 배포 방법이 늘어나면서 혼란이 더 심해지기도 했고요.

    그러다가 ArgoCD를 알게 됐습니다. 처음엔 “또 새로운 툴이네” 하고 반신반의했는데, 막상 써보니까 진짜 다르더라고요. GitOps 방식으로 쿠버네티스 배포를 자동화하면, Git 저장소가 클러스터의 “진실의 원천(Source of Truth)”이 됩니다. 코드를 푸시하면 ArgoCD가 알아서 클러스터 상태를 맞춰주는 거예요. 오늘은 제가 홈랩과 실무에서 직접 구축하면서 겪은 경험을 바탕으로, ArgoCD를 활용한 CI/CD 파이프라인 구축 방법을 처음부터 끝까지 같이 살펴보겠습니다.

    ArgoCD GitOps 전체 아키텍처 — Git 저장소 변경이 쿠버네티스 클러스터에 자동 반영되는 흐름

    GitOps와 ArgoCD, 쉽게 이해하기

    GitOps란 뭔가요?

    GitOps를 한 문장으로 정리하면 이렇습니다. “인프라와 애플리케이션의 원하는 상태(Desired State)를 Git에 선언적으로 저장하고, 자동화 도구가 실제 상태를 그것에 맞게 유지하는 방식”이에요.

    쉽게 말해서, 예전에는 “지금 클러스터에 뭐가 떠 있지?”를 확인하려면 kubectl get 명령어를 직접 쳐봐야 했잖아요. GitOps를 쓰면 Git 저장소만 봐도 현재 클러스터 상태를 알 수 있어요. 변경 이력도 다 남고, 롤백도 git revert 한 방이면 되고요.

    기존 CI/CD 방식과 무엇이 다른가요?

    구분 기존 Push 방식 CI/CD GitOps (ArgoCD Pull 방식)
    배포 트리거 CI 파이프라인이 kubectl apply 직접 실행 ArgoCD가 Git 변경 감지 후 자동 동기화
    클러스터 접근 권한 CI 서버가 클러스터 자격증명 보유 ArgoCD만 클러스터 내부에서 관리
    상태 추적 파이프라인 로그 확인 필요 Git 커밋 히스토리 = 배포 이력
    드리프트(Drift) 감지 수동 확인 필요 ArgoCD가 실시간 감지 및 알림
    롤백 파이프라인 재실행 or 수동 Git revert + 자동 동기화

    특히 드리프트(Drift) 감지가 저한테는 정말 킬러 피처였어요. 누군가 실수로 kubectl로 직접 설정을 바꿔놔도, ArgoCD가 “어? Git이랑 다른데?” 하고 바로 잡아주거든요. 이게 얼마나 안심되는지 모릅니다.

    ArgoCD 설치 — 생각보다 간단합니다

    사전 준비

    • 쿠버네티스 클러스터 (v1.19 이상 권장)
    • kubectl 설치 및 클러스터 접근 설정 완료
    • Git 저장소 (GitHub, GitLab, Bitbucket 모두 가능)
    • ArgoCD CLI (선택사항이지만 있으면 편해요)

    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
    
    # 설치 완료 확인 (모든 Pod가 Running 상태가 될 때까지 대기)
    kubectl get pods -n argocd -w

    처음에 저도 이게 너무 간단해서 “이게 맞나?” 싶었는데, 맞습니다 ㅎㅎ. 잠시 기다리면 아래와 같이 Pod들이 뜨기 시작해요.

    NAME                                                READY   STATUS    RESTARTS   AGE
    argocd-application-controller-0                     1/1     Running   0          2m
    argocd-applicationset-controller-xxx                1/1     Running   0          2m
    argocd-dex-server-xxx                               1/1     Running   0          2m
    argocd-notifications-controller-xxx                 1/1     Running   0          2m
    argocd-redis-xxx                                    1/1     Running   0          2m
    argocd-repo-server-xxx                              1/1     Running   0          2m
    argocd-server-xxx                                   1/1     Running   0          2m

    2단계: ArgoCD UI 접근 설정

    기본적으로 ArgoCD 서버는 외부에 노출되어 있지 않아요. 로컬에서 테스트할 때는 포트 포워딩을 쓰고, 실제 운영 환경에서는 Ingress를 설정하는 게 좋습니다.

    # 로컬 테스트용 포트 포워딩
    kubectl port-forward svc/argocd-server -n argocd 8080:443
    
    # 초기 admin 비밀번호 확인
    kubectl -n argocd get secret argocd-initial-admin-secret \
      -o jsonpath="{.data.password}" | base64 -d; echo

    💡 팁: 초기 비밀번호는 반드시 첫 로그인 후 바꿔주세요. 그리고 argocd-initial-admin-secret은 비밀번호 변경 후 삭제하는 게 보안상 좋습니다.

    3단계: ArgoCD CLI 설치 (선택 권장)

    # macOS
    brew install argocd
    
    # Linux
    curl -sSL -o /usr/local/bin/argocd \
      https://github.com/argoproj/argo-cd/releases/latest/download/argocd-linux-amd64
    chmod +x /usr/local/bin/argocd
    
    # CLI로 ArgoCD 서버에 로그인
    argocd login localhost:8080 --username admin --password <위에서-확인한-비밀번호> --insecure

    ArgoCD 웹 UI 대시보드 — 애플리케이션 동기화 상태와 헬스 체크 결과를 한눈에 확인

    첫 번째 Application 등록 — 실전 GitOps 시작

    Git 저장소 구조 준비

    ArgoCD를 쓸 때 저장소 구조를 어떻게 잡느냐가 은근히 중요하더라고요. 저는 앱 코드와 쿠버네티스 매니페스트를 분리하는 방식을 선호합니다.

    my-gitops-repo/
    ├── apps/
    │   ├── dev/
    │   │   ├── deployment.yaml
    │   │   ├── service.yaml
    │   │   └── kustomization.yaml
    │   └── prod/
    │       ├── deployment.yaml
    │       ├── service.yaml
    │       └── kustomization.yaml
    └── README.md

    예시로 사용할 간단한 Deployment와 Service 매니페스트입니다.

    # apps/dev/deployment.yaml
    apiVersion: apps/v1
    kind: Deployment
    metadata:
      name: sample-app
      namespace: default
    spec:
      replicas: 2
      selector:
        matchLabels:
          app: sample-app
      template:
        metadata:
          labels:
            app: sample-app
        spec:
          containers:
          - name: sample-app
            image: nginx:1.25
            ports:
            - containerPort: 80
            resources:
              requests:
                cpu: 100m
                memory: 128Mi
              limits:
                cpu: 200m
                memory: 256Mi
    # apps/dev/service.yaml
    apiVersion: v1
    kind: Service
    metadata:
      name: sample-app-svc
      namespace: default
    spec:
      selector:
        app: sample-app
      ports:
      - protocol: TCP
        port: 80
        targetPort: 80
      type: ClusterIP

    ArgoCD Application 생성

    이제 핵심입니다. ArgoCD에게 “이 Git 저장소의 이 경로를 이 클러스터에 배포해줘”라고 알려주는 Application 리소스를 만들어야 해요. YAML로 선언적으로 만드는 걸 추천합니다 — 이것 자체도 Git으로 관리하면 더 좋고요.

    # argocd-application.yaml
    apiVersion: argoproj.io/v1alpha1
    kind: Application
    metadata:
      name: sample-app-dev
      namespace: argocd
    spec:
      project: default
      source:
        repoURL: https://github.com/your-username/my-gitops-repo.git
        targetRevision: HEAD
        path: apps/dev
      destination:
        server: https://kubernetes.default.svc
        namespace: default
      syncPolicy:
        automated:
          prune: true        # Git에서 삭제된 리소스 자동 제거
          selfHeal: true     # 드리프트 감지 시 자동 복구
        syncOptions:
        - CreateNamespace=true
    # Application 생성
    kubectl apply -f argocd-application.yaml
    
    # 동기화 상태 확인
    argocd app get sample-app-dev
    
    # 수동 동기화 (처음 한 번)
    argocd app sync sample-app-dev

    여기서 selfHeal: true 옵션이 제가 아까 말씀드린 드리프트 자동 복구 기능이에요. 누군가 실수로 kubectl로 직접 replica 수를 바꿔도, ArgoCD가 Git에 선언된 값으로 되돌려줍니다. 처음에 이게 동작하는 걸 보고 “오오…” 했던 기억이 나네요.

    Private 저장소 연결

    실무에서는 당연히 Private 저장소를 쓰죠. SSH 키나 Personal Access Token으로 연결할 수 있어요.

    # HTTPS + Personal Access Token 방식
    argocd repo add https://github.com/your-username/my-gitops-repo.git \
      --username your-username \
      --password your-personal-access-token
    
    # SSH 키 방식
    argocd repo add [email protected]:your-username/my-gitops-repo.git \
      --ssh-private-key-path ~/.ssh/id_rsa

    CI 파이프라인과 연동 — 이미지 태그 자동 업데이트

    ArgoCD는 CD(Continuous Delivery) 도구입니다. CI(Continuous Integration)는 별도 도구를 써야 해요. 저는 GitHub Actions를 주로 쓰는데, 흐름은 이렇습니다.

    1. 개발자가 앱 코드를 main 브랜치에 push
    2. GitHub Actions가 Docker 이미지를 빌드하고 레지스트리에 push
    3. GitHub Actions가 GitOps 저장소의 이미지 태그를 새 버전으로 업데이트
    4. ArgoCD가 GitOps 저장소 변경을 감지하고 자동으로 클러스터에 배포
    # .github/workflows/deploy.yaml
    name: Build and Update GitOps Repo
    
    on:
      push:
        branches: [main]
    
    jobs:
      build-and-push:
        runs-on: ubuntu-latest
        steps:
        - name: Checkout app code
          uses: actions/checkout@v3
    
        - name: Set up Docker Buildx
          uses: docker/setup-buildx-action@v2
    
        - name: Login to Container Registry
          uses: docker/login-action@v2
          with:
            registry: ghcr.io
            username: ${{ github.actor }}
            password: ${{ secrets.GITHUB_TOKEN }}
    
        - name: Build and push
          uses: docker/build-push-action@v4
          with:
            push: true
            tags: ghcr.io/${{ github.repository }}:${{ github.sha }}
    
        - name: Update GitOps repo image tag
          run: |
            git clone https://x-access-token:${{ secrets.GITOPS_TOKEN }}@github.com/your-username/my-gitops-repo.git
            cd my-gitops-repo
            # sed로 이미지 태그 업데이트
            sed -i "s|image: ghcr.io/your-username/sample-app:.*|image: ghcr.io/your-username/sample-app:${{ github.sha }}|"\
              apps/dev/deployment.yaml
            git config user.email "[email protected]"
            git config user.name "CI Bot"
            git add apps/dev/deployment.yaml
            git commit -m "chore: update image tag to ${{ github.sha }}"
            git push

    근데 여기서 주의할 점이 있어요. 앱 코드 저장소와 GitOps(매니페스트) 저장소를 분리하는 게 좋습니다. 같은 저장소에 두면 앱 코드 변경과 인프라 변경 이력이 섞여서 나중에 관리가 복잡해지더라고요. 처음에 같이 뒀다가 나중에 분리하는 삽질을 했었는데… 처음부터 분리하세요 ㅎㅎ.

    CI/CD 전체 파이프라인 흐름 — GitHub Actions가 이미지를 빌드하고 GitOps 저장소를 업데이트하면 ArgoCD가 자동 배포

    ⚠️ 삽질 모음 — 이것만 알면 시간 아낍니다

    1. OutOfSync 상태가 계속 유지되는 문제

    분명히 Git이랑 같은 내용인데 ArgoCD에서 계속 OutOfSync라고 뜨는 경우가 있어요. 대부분 리소스 정규화(normalization) 문제입니다. 쿠버네티스가 자동으로 추가하는 필드들(creationTimestamp, status 등)이 Git에 없는 거예요.

    # Application에 ignoreDifferences 추가로 해결
    spec:
      ignoreDifferences:
      - group: apps
        kind: Deployment
        jsonPointers:
        - /spec/replicas  # HPA가 관리하는 경우 replica 수 무시
      - group: ""
        kind: Service
        jsonPointers:
        - /spec/clusterIP

    2. Helm 차트 사용 시 values 파일 관리

    Helm을 ArgoCD와 함께 쓸 때 values 파일을 Git에 두면 됩니다.

    spec:
      source:
        repoURL: https://github.com/your-username/my-gitops-repo.git
        path: charts/my-app
        helm:
          valueFiles:
          - values-dev.yaml
          - values-secrets.yaml  # Sealed Secrets 등으로 암호화된 파일

    3. 시크릿(Secret) 관리 — 절대 Git에 평문으로 넣지 마세요

    이건 정말 중요해요. DB 비밀번호 같은 민감 정보를 실수로 Git에 넣는 사고가 생각보다 많이 일어납니다. 저는 Sealed Secrets나 External Secrets Operator를 씁니다.

    • Sealed Secrets: 공개키로 암호화된 SealedSecret 리소스를 Git에 저장, 클러스터 내 컨트롤러가 복호화
    • External Secrets Operator: AWS Secrets Manager, HashiCorp Vault 등 외부 시크릿 저장소와 연동

    4. ArgoCD 자체도 GitOps로 관리하기 — App of Apps 패턴

    ArgoCD Application 리소스가 많아지면 관리가 힘들어져요. 이럴 때 App of Apps 패턴을 씁니다. 루트 Application 하나가 다른 Application들을 관리하는 계층 구조예요.

    # root-application.yaml — 다른 Application들을 관리하는 루트
    apiVersion: argoproj.io/v1alpha1
    kind: Application
    metadata:
      name: root-app
      namespace: argocd
    spec:
      project: default
      source:
        repoURL: https://github.com/your-username/my-gitops-repo.git
        targetRevision: HEAD
        path: argocd-apps  # 이 폴더 안에 다른 Application YAML들이 있음
      destination:
        server: https://kubernetes.default.svc
        namespace: argocd
      syncPolicy:
        automated:
          prune: true
          selfHeal: true

    ✅ 배포 결과 확인 및 검증

    모든 설정이 끝났으면 이렇게 확인하시면 됩니다.

    # ArgoCD CLI로 앱 상태 확인
    argocd app list
    
    # 상세 상태 확인
    argocd app get sample-app-dev
    
    # 동기화 이력 확인
    argocd app history sample-app-dev
    
    # kubectl로 직접 확인
    kubectl get pods -n default
    kubectl get svc -n default

    ArgoCD UI에서 애플리케이션 상태가 Synced / Healthy로 표시되면 성공입니다. 🎉

    이제 Git 저장소에서 deployment.yaml의 이미지 태그만 바꿔서 커밋하면, 몇 분 안에 (기본 polling 주기는 3분) 클러스터에 자동으로 반영되는 걸 볼 수 있어요. 처음에 이게 자동으로 되는 걸 보고 “드디어 됐다!” 하고 혼자 좋아했던 기억이 납니다 ㅎㅎ.

    # 롤백도 이렇게 간단합니다
    argocd app rollback sample-app-dev 
    
    # 또는 Git에서 revert 후 push하면 ArgoCD가 자동으로 처리

    ArgoCD 애플리케이션 상세 화면 — Synced/Healthy 상태와 쿠버네티스 리소스 트리 시각화

    자주 묻는 질문 (FAQ)

    Q. ArgoCD는 무료인가요?

    네, ArgoCD는 오픈소스(Apache 2.0 라이선스)로 완전 무료입니다. CNCF(Cloud Native Computing Foundation) 졸업 프로젝트이기도 하고요.

    Q. Flux와 ArgoCD 중 어떤 걸 써야 하나요?

    둘 다 GitOps 도구이지만, ArgoCD는 UI가 풍부하고 직관적이라 팀에서 처음 GitOps를 도입할 때 진입 장벽이 낮습니다. Flux는 더 경량이고 CLI 친화적이에요. 저는 팀 협업 환경에서는 ArgoCD를, 단독 운영이나 자동화 중심이면 Flux를 추천합니다.

    Q. 여러 클러스터를 관리할 수 있나요?

    가능합니다. ArgoCD 하나로 여러 쿠버네티스 클러스터를 등록하고 관리할 수 있어요. argocd cluster add 명령어로 추가하면 됩니다.

    마무리 — GitOps, 한 번 맛보면 못 돌아갑니다

    ArgoCD와 GitOps를 도입하고 나서 제 일상이 꽤 달라졌어요. 배포가 무서운 이벤트에서 그냥 평범한 Git 커밋 하나로 바뀌었달까요. 팀원들도 “내가 뭘 배포했는지” Git 로그만 보면 바로 알 수 있으니 커뮤니케이션 비용도 줄었고요.

    물론 처음 셋업할 때 시크릿 관리나 멀티 클러스터 권한 설정 같은 부분에서 삽질이 좀 있긴 합니다. 근데 그 초기 투자를 하고 나면 이후에는 정말 편해요.

    오늘 다룬 내용을 정리하면 이렇습니다.

    • ✅ GitOps 개념과 기존 CI/CD 방식의 차이 이해
    • ✅ ArgoCD 설치 및 초기 설정
    • ✅ Application 리소스로 Git 저장소와 클러스터 연결
    • ✅ GitHub Actions와 연동한 완전 자동화 파이프라인 구축
    • ✅ 자주 겪는 문제와 해결법

    다음 글에서는 ArgoCD ApplicationSet을 활용해서 여러 환경(dev/staging/prod)을 템플릿 하나로 관리하는 방법을 다룰 예정입니다. App of Apps 패턴도 더 깊이 파볼 거고요. 이 글이 도움이 됐다면 댓글로 알려주세요! 궁금한 점도 편하게 물어봐 주시고요. 😊

  • [Nas] TrueNAS SCALE: Docker 및 Kubernetes 앱 배포 및 관리 완벽 가이드

    [Nas] TrueNAS SCALE: Docker 및 Kubernetes 앱 배포 및 관리 완벽 가이드

    TrueNAS SCALE: Docker 및 Kubernetes 앱 배포 및 관리 완벽 가이드

    안녕하세요, 13년차의 서버실 주인장입니다. 홈랩(HomeLab)을 운영하다 보면 NAS에 이것저것 서비스를 올려보고 싶은 욕구가 스멀스멀 올라오잖아요? 저도 처음엔 단순히 파일 저장용으로 시작했는데, Plex(미디어 서버), Nextcloud(개인 클라우드), Home Assistant(스마트 홈 허브) 같은 다양한 앱들을 돌리고 싶어서 고민이 많았습니다.

    예전에는 일일이 가상머신(Virtual Machine)을 만들어서 설치하곤 했는데, 관리도 어렵고 자원도 많이 먹더라고요. 그러다 컨테이너(Container) 기술인 Docker(도커)와 Kubernetes(쿠버네티스)를 접하게 됐고, NAS에서 이걸 더 쉽게 쓸 방법이 뭘까 고민하던 차에 TrueNAS SCALE을 만나게 됐습니다. TrueNAS SCALE은 리눅스 기반이라 Docker와 Kubernetes를 네이티브(Native)하게 지원하거든요. 특히 TrueNAS SCALE Docker 환경에서 앱을 배포하고 관리하는 경험은 정말 신세계더라고요.

    오늘은 제가 직접 TrueNAS SCALE을 활용해서 Docker와 Kubernetes 앱을 배포하고 관리하면서 겪었던 삽질 경험과 해결 과정, 그리고 효율적인 관리 팁까지 완벽하게 풀어드리려고 합니다. 홈랩에 컨테이너 기반 앱을 올려보고 싶었던 분들이라면 이 글이 큰 도움이 될 거예요!

    TrueNAS SCALE 환경에서 Docker 및 Kubernetes 앱 배포의 전체적인 아키텍처를 보여주는 다이어그램입니다.

    TrueNAS SCALE, Docker, Kubernetes 핵심 개념 이해하기

    본격적인 실전에 들어가기 전에, 우리가 다룰 핵심 기술들이 무엇인지 쉽고 간단하게 짚고 넘어갈 수 있도록, 제가 처음 공부할 때 “이게 뭔 소리야?” 했던 개념들을 멘토처럼 알려드릴게요!

    TrueNAS SCALE: 리눅스 기반의 강력한 NAS 운영체제

    • TrueNAS SCALE은 오픈소스(Open Source) NAS 운영체제인 TrueNAS의 한 버전입니다. 기존 FreeBSD 기반의 TrueNAS CORE와는 다르게 Debian Linux(데비안 리눅스)를 기반으로 하고 있어요.
    • 이 덕분에 컨테이너(Container) 기술인 Docker와 오케스트레이션(Orchestration) 도구인 Kubernetes를 기본적으로 지원하고, 훨씬 더 넓은 범위의 앱 생태계를 활용할 수 있게 됐죠.
    • 강력한 파일 시스템인 ZFS(Z File System)를 기반으로 데이터 무결성(Data Integrity)과 유연한 스토리지 관리를 제공하는 건 기본입니다.

    Docker (도커): 앱을 컨테이너에 담아 쉽게 배포!

    • Docker(도커)는 애플리케이션(Application)과 그 실행에 필요한 모든 요소를 컨테이너라는 독립적인 환경에 패키징(Packaging)하여 배포하고 실행하는 기술입니다. 쉽게 말해, 앱을 하나의 깔끔한 상자에 담아 어디서든 똑같이 실행될 수 있도록 만들어주는 거죠.
    • 장점:
      • 이식성(Portability): 어떤 환경에서든 동일하게 작동합니다. “제 컴퓨터에선 되는데?” 하는 말을 들을 일이 줄어들어요.
      • 격리성(Isolation): 각 앱이 서로 영향을 주지 않고 독립적으로 실행됩니다.
      • 효율성(Efficiency): 가상머신보다 가볍고 빠릅니다.

    Kubernetes (쿠버네티스): 컨테이너 오케스트레이션의 지휘자

    • Kubernetes(쿠버네티스, 줄여서 K8s)는 컨테이너화된 워크로드(Workload)와 서비스를 자동으로 배포하고, 스케일링(Scaling)하며, 관리해주는 오픈소스 시스템입니다. Docker 컨테이너가 많아지면 이걸 효율적으로 관리하는 게 정말 어려워지거든요. 이때 K8s가 마치 지휘자처럼 여러 컨테이너를 조율해줍니다.
    • TrueNAS SCALE Kubernetes 환경에서는 TrueNAS의 ‘Apps’ 기능을 통해 Helm Chart(헬름 차트) 기반의 Kubernetes 앱을 쉽게 배포할 수 있어요.
    • NAS 앱 배포를 자동화하고 안정성을 높이는 데 아주 효과적이죠.

    Dockge (닥지): Docker Compose를 위한 웹 UI

    • TrueNAS SCALE의 내장 Apps 기능도 훌륭하지만, Docker Compose(도커 컴포즈) 파일을 직접 관리하는 데 익숙한 분들에게는 조금 답답할 수 있어요. 이때 Dockge(닥지)가 구원투수로 등장합니다!
    • Dockge는 Docker Compose 파일을 웹 UI(User Interface)를 통해 생성, 편집, 배포, 모니터링할 수 있게 해주는 경량 도구입니다. 제가 직접 써보니 Docker Compose를 활용한 컨테이너 관리가 훨씬 편하더라고요.

    TrueNAS SCALE에서 Docker 및 Kubernetes 앱 실전 배포

    자, 이제 이론은 충분히 봤으니 직접 한번 해봐야죠! 제가 실제로 홈랩에서 앱을 배포하는 과정을 보여드릴게요. TrueNAS SCALE의 ‘Apps’ 기능을 이용하는 방법과 Dockge를 활용하는 두 가지 방법을 모두 알려드리겠습니다.

    1. TrueNAS SCALE 내장 ‘Apps’ 기능 활용하기 (Kubernetes 기반)

    TrueNAS SCALE은 자체적으로 Kubernetes 클러스터를 내장하고 있어서 ‘Apps’ 메뉴를 통해 다양한 앱을 손쉽게 배포할 수 있어요. 주로 Helm Chart(헬름 차트) 기반의 앱들이 제공됩니다.

    1. Apps 기능 활성화 및 스토리지 풀 선택:
      • TrueNAS SCALE 웹 UI에 접속합니다.
      • 좌측 메뉴에서 ‘Apps’를 클릭합니다.
      • ‘Settings’ 탭으로 이동하여 ‘Choose Pool’에서 앱 데이터를 저장할 ZFS 스토리지 풀(Storage Pool)을 선택합니다. 저는 보통 ‘ix-applications’라는 데이터셋(Dataset)이 자동으로 생성되도록 합니다.
      • ‘Save’를 눌러 설정을 저장합니다.
    2. 앱 검색 및 설치:
      • ‘Available Applications’ 탭으로 돌아와 원하는 앱을 검색해요. 예를 들어 ‘Plex’를 검색해 볼까요?
      • Plex 앱을 선택하고 ‘Install’을 클릭합니다.
      • 설치 마법사(Wizard)가 나타나는데, 여기서 앱 이름, Persistent Volume(영구 볼륨), 네트워크 설정, 환경 변수(Environment Variables) 등을 설정할 수 있어요. 특히 볼륨 설정은 앱 데이터가 저장될 위치를 지정하는 중요한 부분이니 신중하게 설정해야 합니다.
      • 필요한 설정을 마친 후 ‘Install’을 클릭하면 앱 배포가 시작됩니다.
    3. 배포된 앱 확인:
      • ‘Installed Applications’ 탭에서 배포된 앱의 상태를 확인할 수 있어요. ‘Deploying’에서 ‘Active’로 바뀌면 정상적으로 실행 중인 것입니다.
      • 앱 옆의 ‘Web UI’ 버튼을 클릭하여 해당 앱의 웹 인터페이스에 접속할 수 있습니다.

    2. Dockge를 활용하여 Docker Compose 앱 배포하기

    TrueNAS SCALE의 Apps 기능도 좋지만, 저는 Docker Compose 파일로 직접 컨테이너 설정을 제어하는 걸 더 선호하는 편입니다. 이때 Dockge TrueNAS 설치가 정말 유용하더라고요. Dockge는 Docker Compose 파일을 웹 UI에서 쉽게 관리할 수 있게 해줍니다. 먼저 Dockge부터 설치해봅시다!

    2.1. Dockge 설치

    Dockge는 Docker 컨테이너로 실행되는데, TrueNAS SCALE의 Shell(쉘)을 통해 Docker 명령어를 직접 입력하여 설치할 수 있어요.

    1. TrueNAS SCALE Shell 접속:
      • TrueNAS SCALE 웹 UI에서 ‘System Settings’ > ‘Shell’로 이동합니다.
    2. Dockge 설치 스크립트 실행:

      다음 명령어를 입력하여 Dockge를 설치합니다. 이 명령어는 Docker Compose를 이용해 Dockge 컨테이너를 실행하고, 필요한 볼륨과 네트워크를 설정해줍니다. 저는 주로 /mnt/pool_name/appdata/dockge 경로에 데이터를 저장합니다.

      mkdir -p /mnt/pool_name/appdata/dockge
      cd /mnt/pool_name/appdata/dockge
      
      curl -L https://raw.githubusercontent.com/louislam/dockge/master/compose.yaml -o compose.yaml
      
      docker compose up -d
      

      여기서 pool_name은 여러분의 ZFS 스토리지 풀 이름으로 바꿔주세요. 예를 들어 /mnt/tank/appdata/dockge 처럼요.

    3. Dockge 웹 UI 접속:

      설치가 완료되면 웹 브라우저에서 http://[TrueNAS_IP]:5001 (기본 포트)로 접속하여 Dockge 웹 UI를 확인할 수 있어요. 처음 접속 시 관리자 계정을 설정하게 됩니다.

    2.2. Dockge를 이용한 Docker Compose 앱 배포

    Dockge에 접속했다면 이제 원하는 Docker Compose 앱을 배포해봅시다. 저는 간단한 Nginx 웹 서버를 예시로 들어볼게요.

    1. 새로운 스택(Stack) 생성:
      • Dockge UI에서 ‘Stacks’ > ‘New Stack’을 클릭합니다.
      • 스택 이름을 입력하고, Docker Compose YAML 파일을 붙여넣어요.
    2. Docker Compose YAML 작성 예시 (Nginx):
      version: '3.8'
      services:
        nginx:
          image: nginx:latest
          container_name: my-nginx
          restart: unless-stopped
          ports:
            - "80:80"
          volumes:
            - /mnt/pool_name/appdata/nginx/html:/usr/share/nginx/html
            - /mnt/pool_name/appdata/nginx/conf:/etc/nginx/conf.d
      

      위 YAML 파일은 Nginx 컨테이너를 실행하고, 호스트의 80번 포트를 컨테이너의 80번 포트에 연결하며, 데이터를 저장할 볼륨을 설정합니다. 역시 pool_name은 여러분의 스토리지 풀 이름으로 바꿔주세요.

    3. 스택 배포:
      • YAML 파일을 붙여넣고 ‘Deploy’ 버튼을 클릭하면 Dockge가 Docker Compose 명령어를 실행하여 컨테이너를 배포해요.
      • 배포 상태와 로그를 실시간으로 확인할 수 있어서 문제 발생 시 디버깅(Debugging)하기 정말 편하더라고요.

    Dockge 웹 UI에서 Docker Compose 스택을 생성하고 설정하는 화면입니다. YAML 파일 편집과 배포가 한눈에 들어와서 편리하죠.

    ⚠️ 삽질 경험 & 트러블슈팅 가이드

    13년차 엔지니어 생활에서 느낀 건, 새로운 기술을 도입할 때 삽질은 숙명이라는 거예요. TrueNAS SCALE에서 컨테이너 앱을 배포하면서 저도 몇 번이나 “아, 이거 왜 안 되지?” 하면서 머리를 쥐어뜯었거든요. 그 경험을 바탕으로 여러분이 겪을 만한 문제와 해결책을 공유합니다.

    1. 스토리지 권한 문제 (Permissions Issue)

    이거 진짜 제일 많이 겪는 문제예요. 특히 Docker 컨테이너가 TrueNAS의 ZFS 데이터셋에 접근할 때 권한이 없어서 파일을 생성하거나 읽지 못하는 경우가 허다하거든요. 컨테이너 내부의 프로세스는 특정 사용자(User)와 그룹(Group)으로 실행되는데, 이 사용자에게 ZFS 데이터셋에 대한 쓰기 권한이 없어서 생기는 문제예요.

    • 해결책:
      1. ACL(Access Control List) 설정: TrueNAS 웹 UI에서 해당 데이터셋의 ‘Edit ACL’을 통해 ‘Default ACL’을 설정해주는 게 가장 깔끔합니다. ‘OPEN’ 프리셋을 적용하고, ‘Apply permissions recursively’를 체크하여 하위 폴더까지 적용해줘요.
      2. Shell에서 권한 변경: 급하게 테스트할 때는 Shell에서 chmod와 chown 명령어를 사용하기도 합니다.
        # 소유자를 root:wheel로 변경 (컨테이너가 보통 root로 실행될 때)
        sudo chown -R root:wheel /mnt/pool_name/appdata/your_app
        
        # 모든 사용자에게 읽기/쓰기/실행 권한 부여 (주의: 보안에 취약할 수 있음)
        sudo chmod -R 777 /mnt/pool_name/appdata/your_app
        

        하지만 chmod 777은 보안상 좋지 않으니, 컨테이너가 필요로 하는 최소한의 권한을 부여하는 것을 권장합니다. 보통 PUID/PGID(Process User ID/Group ID) 환경 변수를 통해 컨테이너 내부 사용자 ID를 지정하는 방식으로 해결하더라고요.

    2. 네트워크 설정 및 포트 충돌

    컨테이너 앱이 외부와 통신하려면 네트워크 설정이 중요한데, 특히 포트(Port) 충돌이 자주 발생해요.

    • 문제: 이미 TrueNAS 자체 서비스(예: SMB, SSH)나 다른 컨테이너가 사용 중인 포트를 앱이 사용하려고 할 때 생깁니다.
    • 해결책:
      • 사용 가능한 포트 확인: netstat -tulnp 명령어를 Shell에서 실행하여 현재 사용 중인 포트를 확인해요.
      • 포트 변경: 앱 설정이나 Docker Compose YAML 파일에서 호스트 포트를 다른 사용 가능한 포트로 변경합니다. 예를 들어 Nginx의 80번 포트가 충돌한다면 "8080:80"처럼 변경할 수 있어요.
      • Host Network 사용 시 주의: TrueNAS SCALE의 Apps에서는 ‘Host Network’ 옵션을 제공하는데, 이는 컨테이너가 호스트의 네트워크 스택을 직접 사용하게 하여 성능상 이점이 있지만, 포트 충돌 가능성이 더 높아집니다. 신중하게 사용해야 해요.

    3. 앱 업데이트 후 데이터 유실/오류

    컨테이너 앱을 업데이트하다가 데이터가 날아가거나 앱이 실행되지 않는 경험, 해보셨나요? 😭 저도 예전에 한번 겪고 식겁한 적이 있습니다.

    • 해결책:
      • 백업(Backup) 필수: 업데이트 전에는 항상 중요한 데이터를 백업해둬야 해요. ZFS 스냅샷(Snapshot) 기능을 활용하면 매우 편리하거든요.
      • 볼륨(Volume) 분리: 컨테이너 이미지와 데이터를 저장하는 볼륨을 분리하여 사용하면, 앱을 업데이트하거나 다시 배포해도 데이터는 안전하게 유지됩니다. Dockge의 Docker Compose YAML 예시에서도 볼륨을 명시적으로 분리했죠.
      • 업데이트 전 로그 확인: 새로운 버전의 변경 사항(Changelog)을 미리 확인하여 호환성 문제를 예측하고 대비해요.

    ✅ 배포된 앱 검증 및 결과 확인

    수많은 삽질 끝에 드디어 앱 배포에 성공했어요! 이제 앱이 제대로 작동하는지 확인해봐야죠. 저는 주로 다음 세 가지를 확인합니다.

    1. 웹 UI 접속 확인: 배포한 앱의 웹 인터페이스에 접속하여 정상적으로 화면이 뜨고 로그인, 기능 사용이 가능한지 확인해요. 예를 들어 Nginx라면 웹 브라우저에서 TrueNAS IP로 접속했을 때 기본 페이지가 보여야 하죠.
    2. 로그 확인: 앱이 제대로 작동하지 않을 때는 로그를 확인하는 게 가장 중요합니다.
      • TrueNAS SCALE Apps의 경우: ‘Installed Applications’에서 해당 앱을 선택하고 ‘Logs’ 탭을 확인해요.
      • Dockge를 통해 배포한 Docker Compose 앱의 경우: Dockge UI에서 해당 스택을 선택하면 실시간 로그를 확인할 수 있어요. 또는 TrueNAS Shell에서 docker logs [컨테이너_이름] 명령어를 사용합니다.
    3. 리소스 모니터링: TrueNAS SCALE 대시보드에서 CPU, RAM, 네트워크 사용량 등을 확인하여 앱이 과도하게 리소스를 소모하고 있지 않은지 점검해요. 특히 RAM 사용량은 컨테이너 앱이 많아질수록 중요해지거든요.

    성공적으로 배포된 Plex 미디어 서버의 대시보드 화면입니다. TrueNAS SCALE 위에서 컨테이너 앱들이 안정적으로 작동하는 것을 확인할 수 있습니다.

    🎉 마무리: TrueNAS SCALE로 홈랩을 더욱 강력하게!

    오늘은 TrueNAS SCALE에서 Docker와 Kubernetes 앱을 배포하고 관리하는 완벽 가이드를 함께 살펴봤습니다. 처음엔 좀 복잡하게 느껴질 수도 있지만, 한번 제대로 세팅해두면 정말 편리하고 강력한 홈랩 환경을 구축할 수 있어요.

    TrueNAS SCALE의 내장 ‘Apps’ 기능으로 Kubernetes 기반의 안정적인 앱 배포를 경험하고, Dockge TrueNAS 설치를 통해 Docker Compose 파일을 직접 제어하며 유연하게 컨테이너 관리를 하는 두 가지 방법을 모두 알려드렸는데요. 저는 개인적으로 Dockge를 통한 Docker Compose 관리가 훨씬 손에 익고, 제어권이 많아서 좋더라고요. 하지만 초보자분들이나 복잡한 설정 없이 바로 사용하고 싶다면 TrueNAS Apps도 훌륭한 선택지입니다.

    이런 삽질과 학습 과정을 통해 저의 NAS 앱 배포 경험치도 한 단계 올라간 것 같아요. 여러분도 저처럼 TrueNAS SCALE을 활용해서 나만의 강력한 서버실을 만들어보세요. 다음번에는 Ingress(인그레스, 외부 트래픽 진입점) 설정이나 Persistent Volume(영구 볼륨)의 고급 활용법에 대해 다뤄보면 어떨까 싶네요!

    TrueNAS SCALE에서 앱을 배포하는 두 가지 주요 방법, 내장 Apps와 Dockge를 통한 Docker Compose 활용법을 비교한 요약입니다.

  • [k8s] ArgoCD 멀티 클러스터 배포 전략: GitOps로 K8s 여러 환경을 효과적으로 관리하기

    [k8s] ArgoCD 멀티 클러스터 배포 전략: GitOps로 K8s 여러 환경을 효과적으로 관리하기

    ArgoCD 멀티 클러스터 배포 전략: GitOps로 K8s 여러 환경을 효과적으로 관리하기

    안녕하세요, 13년차의 서버실 주인장입니다. 오늘은 인프라 엔지니어라면 한 번쯤은 고민해봤을 주제, 바로 ArgoCD 멀티 클러스터 배포 전략에 대해 이야기해보려고 합니다. 개발, 스테이징, 프로덕션은 기본이고, DR(재해 복구)이나 여러 리전(Region)에 걸쳐 수많은 쿠버네티스(Kubernetes) 클러스터를 운영하다 보면, “이걸 어떻게 하면 효율적으로 관리할 수 있을까?”라는 질문에 부딪히게 되죠. 저도 예전에 수많은 클러스터들을 수동으로 관리하면서 밤샘 삽질 꽤나 했거든요. 수동 배포는 휴먼 에러의 온상이었고, 클러스터 간 설정 불일치는 늘 제 발목을 잡았습니다. 그러다 GitOps(깃옵스)라는 개념과 ArgoCD(아르고CD)를 만나면서 비로소 광명을 찾았다고 할까요? 오늘은 제가 홈랩과 실제 운영 환경에서 겪었던 경험을 바탕으로, ArgoCD를 이용해 여러 쿠버네티스 환경을 GitOps 방식으로 효과적으로 관리하는 노하우를 공유해드리겠습니다.

    그림 1: ArgoCD 멀티 클러스터 배포의 전체 아키텍처 개요

    GitOps와 ArgoCD, 그리고 멀티 클러스터 관리의 핵심

    먼저 핵심 개념부터 짚고 넘어갈까요? GitOps(깃옵스)는 쉽게 말해, Git(깃)을 인프라 및 애플리케이션 배포의 단일 진실 공급원(Single Source of Truth)으로 사용하는 운영 방식입니다. 모든 클러스터 설정, 애플리케이션 매니페스트 등을 Git 리포지토리에 저장하고, 변경 사항은 Git을 통해서만 적용하는 거죠. 이렇게 하면 누가 언제 무엇을 변경했는지 Git 기록으로 명확하게 남고, 필요하면 언제든 이전 상태로 롤백(Rollback)할 수 있습니다.

    그리고 이 GitOps를 쿠버네티스 환경에서 가장 강력하게 구현해주는 도구가 바로 ArgoCD(아르고CD)입니다. ArgoCD는 선언형 GitOps CD 도구로, Git 리포지토리에 정의된 상태와 실제 쿠버네티스 클러스터의 상태를 지속적으로 비교하고, 불일치하면 자동으로 동기화해줍니다. 마치 클러스터의 ‘자율 주행’ 시스템 같다고 할까요? 제가 직접 써보니까, 배포 파이프라인의 복잡성을 확 줄여주면서도 안정성은 훨씬 높아지더라고요.

    ArgoCD 멀티 클러스터 관리는 이를 더 확장해, 하나의 중앙 ArgoCD 인스턴스가 여러 개의 원격 쿠버네티스 클러스터(Remote Kubernetes Clusters)에 애플리케이션을 배포하고 관리하는 것을 의미합니다. 중앙 ArgoCD 서버에 원격 클러스터들을 등록하면, ArgoCD는 각 클러스터에 접근하여 Git에 정의된 애플리케이션들을 배포하고 동기화 상태를 유지합니다. 복잡한 Jenkins 파이프라인이나 스크립트 없이도, Git만 잘 관리하면 여러 클러스터를 일관된 상태로 유지할 수 있게 되는 거죠. 이거 진짜 편하더라고요!

    실전 구현: ArgoCD로 여러 쿠버네티스 환경 관리하기

    자, 그럼 이제 실제 어떻게 구성하는지 단계별로 알아볼까요? 제가 홈랩에서 여러 클러스터를 구성하면서 가장 효과적이었던 방법을 공유합니다.

    1. 중앙 ArgoCD 인스턴스 설치 (Central Cluster)

    가장 먼저, 모든 것을 관장할 중앙 쿠버네티스 클러스터에 ArgoCD를 설치해야 합니다. 저는 주로 `argocd` 네임스페이스에 설치하는데, 설치 방법은 공식 문서가 워낙 잘 되어 있으니 참고해주세요. Helm(헬름)이나 Kustomize(커스터마이즈)를 사용하면 쉽게 설치할 수 있습니다.

    kubectl create namespace argocd
    kubectl apply -n argocd -f https://raw.githubusercontent.com/argoproj/argocd/stable/manifests/install.yaml
    
    # ArgoCD CLI 설치 (macOS 예시)
    brew install argocd
    

    2. 원격 클러스터 등록하기

    이제 ArgoCD가 관리할 원격 쿠버네티스 클러스터들을 등록해야 합니다. ArgoCD는 `kubectl` 컨텍스트를 활용하여 클러스터를 추가할 수 있습니다. 각 클러스터에 접속할 수 있는 권한이 있는 상태에서 다음 명령어를 실행하면 됩니다.

    # 현재 kubectl 컨텍스트 목록 확인
    kubectl config get-contexts
    
    # 원격 클러스터 컨텍스트로 전환 (예: my-remote-cluster)
    kubectl config use-context my-remote-cluster
    
    # ArgoCD에 클러스터 등록
    argocd cluster add my-remote-cluster --name <클러스터_이름> # --name 옵션으로 ArgoCD UI에 표시될 이름 지정
    

    이 명령어를 실행하면 ArgoCD는 해당 클러스터에 argocd-manager 서비스 어카운트(Service Account)와 필요한 RBAC(Role-Based Access Control) 권한을 자동으로 생성합니다. 처음엔 권한 문제로 좀 헤맸는데, 이 서비스 어카운트 방식이 제일 깔끔하더라고요. 여러 클러스터를 등록했다면, ArgoCD UI에서 다음과 같이 등록된 클러스터 목록을 확인할 수 있습니다.

    그림 2: ArgoCD UI에 등록된 멀티 쿠버네티스 클러스터 목록

    3. Git 리포지토리 구성 전략

    GitOps의 핵심인 Git 리포지토리를 어떻게 구성하느냐가 중요합니다. 저는 주로 모노레포(Monorepo) 방식을 선호합니다. 모든 클러스터의 설정과 애플리케이션 매니페스트를 하나의 리포지토리에 모아두면 관리하기가 훨씬 수월하더라고요. 추천하는 디렉토리 구조는 다음과 같습니다.

    . 
    ├── clusters/
    │   ├── dev/
    │   │   └── cluster-config.yaml
    │   ├── stage/
    │   │   └── cluster-config.yaml
    │   └── prod/
    │       └── cluster-config.yaml
    └── applications/
        ├── my-app/
        │   ├── base/
        │   │   ├── deployment.yaml
        │   │   └── service.yaml
        │   └── overlays/
        │       ├── dev/
        │       │   └── kustomization.yaml
        │       │   └── values.yaml
        │       ├── stage/
        │       │   └── kustomization.yaml
        │       │   └── values.yaml
        │       └── prod/
        │           └── kustomization.yaml
        │           └── values.yaml
        └── another-app/
            └── ...
    

    clusters/ 디렉토리에는 각 클러스터별로 필요한 기본 설정(예: 네임스페이스 생성, RBAC 설정 등)을 담고, applications/ 디렉토리에는 배포할 애플리케이션의 매니페스트를 저장합니다. 특히 Kustomize(커스터마이즈)를 활용하여 base와 overlays 구조를 만들면, 클러스터별로 다른 설정을 유연하게 적용할 수 있습니다. 예를 들어, my-app/base에 공통적인 배포 스펙을 정의하고, my-app/overlays/prod에서 프로덕션 환경에 필요한 리소스 제한(Resource Limit)이나 레플리카(Replica) 수를 오버라이드(Override)하는 식이죠.

    4. ApplicationSet 활용: 동적인 애플리케이션 배포

    여러 클러스터에 동일한 애플리케이션을 배포해야 할 때, ArgoCD의 ApplicationSet(애플리케이션셋)은 정말 빛을 발합니다. ApplicationSet은 여러 ArgoCD Application(애플리케이션)을 동적으로 생성해주는 컨트롤러(Controller)입니다. 저는 주로 Git Generator(깃 제너레이터)를 사용하는데, 특정 Git 경로에 있는 디렉토리 목록을 읽어서 각 디렉토리마다 Application을 생성하도록 합니다.

    예를 들어, applications/my-app/overlays/ 디렉토리 아래에 dev, stage, prod 디렉토리가 있다면, ApplicationSet이 이 세 디렉토리를 각각의 클러스터에 배포할 Application으로 인식하게 만들 수 있습니다.

    apiVersion: argoproj.io/v1alpha1
    kind: ApplicationSet
    metadata:
      name: all-my-apps
    spec:
      generators:
      - git:
          repoURL: https://github.com/my-org/my-gitops-repo.git # 본인의 Git 리포지토리 URL
          revision: HEAD
          directories:
          - path: applications/*/overlays/* # 이 경로 아래의 모든 디렉토리를 찾아서 Application 생성
      template:
        metadata:
          name: '{{ path[2] }}-{{ path[4] }}' # 예: my-app-dev
          labels:
            app.kubernetes.io/part-of: '{{ path[2] }}'
        spec:
          project: default
          source:
            repoURL: https://github.com/my-org/my-gitops-repo.git
            targetRevision: HEAD
            path: '{{ path }}'
          destination:
            server: https://kubernetes.default.svc # ArgoCD에 등록된 클러스터의 API 서버 URL
            namespace: '{{ path[2] }}' # 예: my-app
          syncPolicy:
            automated:
              prune: true
              selfHeal: true
            syncOptions:
              - CreateNamespace=true
    

    위 예시에서 {{ path[2] }}와 {{ path[4] }}는 Git 경로를 기준으로 동적으로 값을 가져오는 변수입니다. 이렇게 ApplicationSet을 사용하면, 새로운 클러스터나 애플리케이션 환경이 추가될 때마다 Application 매니페스트를 일일이 만들 필요 없이, Git에 디렉토리만 추가하면 ArgoCD가 알아서 감지하고 배포해줍니다. 이 부분에서 정말 많은 시간을 절약할 수 있더라고요!

    ArgoCD 멀티 클러스터 배포 중 주의할 점들: ⚠️ 제가 겪었던 삽질 모음

    아무리 좋은 도구라도 삽질은 피할 수 없는 법이죠. 제가 ArgoCD 멀티 클러스터를 구성하면서 겪었던 주요 문제점들과 해결책을 공유합니다. 혹시 이런 경험 있으신가요?

    1. 권한 문제 (RBAC Hell): argocd cluster add 명령어가 클러스터에 argocd-manager 서비스 어카운트를 생성하지만, 때로는 특정 리소스에 대한 권한이 부족할 때가 있습니다. 특히 CRD(Custom Resource Definition)를 배포하거나 특정 오퍼레이터(Operator)를 사용할 때 권한 에러가 발생하곤 합니다. 💡 해결책: 에러 메시지를 꼼꼼히 확인하고, 필요한 권한(Role, ClusterRole)을 추가로 부여하거나, 가장 간단하게는 cluster-admin 권한을 일시적으로 부여하여 문제가 해결되는지 확인해볼 수 있습니다. 하지만 운영 환경에서는 최소 권한 원칙(Principle of Least Privilege)을 지키는 것이 중요합니다. 저도 처음엔 클러스터 등록이 자꾸 실패해서 몇 시간을 날렸는지 몰라요.

    2. 네트워크 문제: 중앙 ArgoCD 인스턴스가 원격 클러스터의 API 서버에 접근하지 못하는 경우가 있습니다. 방화벽(Firewall), 보안 그룹(Security Group), VPC(Virtual Private Cloud) 설정 등이 원인일 수 있습니다. 💡 해결책: ArgoCD 서버가 실행되는 Pod에서 원격 클러스터의 API 서버 IP 주소와 포트로 telnet이나 curl을 시도하여 연결성을 확인해보세요. 저도 한번은 사설망 연결 문제로 고생 좀 했습니다. ㅎㅎ

    3. Helm Values 오버라이드 복잡성: ApplicationSet과 Helm(헬름) 차트(Chart)를 함께 사용할 때, 클러스터별로 다른 values.yaml 파일을 관리하는 것이 복잡해질 수 있습니다. 💡 해결책: Kustomize의 patches 기능을 활용하여 Helm Chart의 특정 값을 오버라이드하거나, ApplicationSet의 parameters 기능을 이용해 클러스터별로 다른 값을 주입하는 방식을 사용해보세요. 이 과정에서 Git 리포지토리 구조를 잘 설계하는 것이 핵심입니다.

    4. Git Sync 지연 및 캐시 문제: Git 리포지토리에 변경 사항을 푸시(Push)했는데, ArgoCD가 즉시 감지하지 못하거나 오래된 캐시 데이터를 사용하는 경우가 있습니다. 💡 해결책: ArgoCD UI에서 수동으로 Sync(동기화)를 시도하거나, Git Webhook(웹훅)을 설정하여 Git 변경 이벤트 발생 시 ArgoCD가 즉시 알림을 받고 동기화를 시작하도록 구성하는 것이 좋습니다. 그래도 안 되면 ArgoCD 서버 Pod를 재시작해보는 것도 한 방법이더라고요.

    검증 및 결과: 🎉 이제 편안하게 관리해볼까요?

    모든 설정이 완료되고 나면, ArgoCD UI는 마치 잘 정돈된 지휘소처럼 느껴질 겁니다. 등록된 모든 클러스터와 그 위에 배포된 애플리케이션들의 상태를 한눈에 볼 수 있죠. Git에 코드 푸시하고 잠시 후 ArgoCD 대시보드를 보면, 모든 클러스터에 배포가 쫙~ 완료되어 있는 걸 볼 수 있습니다. 이 쾌감이란!

    새로운 클러스터에 애플리케이션을 배포해야 할 때도, ApplicationSet을 통해 정의된 Git 리포지토리 구조에 맞게 단순히 새로운 오버레이(Overlay) 디렉토리와 Kustomization 파일을 추가하고 Git에 커밋(Commit)하기만 하면 됩니다. ArgoCD가 이를 감지하고 자동으로 새로운 Application을 생성하고 배포해줍니다. 인프라 변경 관리의 일관성과 자동화 수준이 비약적으로 향상되는 순간이죠.

    그림 3: ArgoCD UI에서 확인하는 멀티 클러스터 애플리케이션 동기화 상태

    ArgoCD 멀티 클러스터 배포의 장점과 한계

    제가 13년 동안 다양한 인프라 환경을 경험하면서 느낀 ArgoCD 멀티 클러스터 배포의 장점과 한계를 정리해봤습니다.

    장점 ✅ 한계 ⚠️
    일관성 유지: 모든 클러스터가 Git에 정의된 동일한 상태를 따르므로, 환경 간 불일치를 최소화합니다. 초기 설정 복잡성: Git 리포지토리 구조, ApplicationSet 설정 등 초기 구성에 학습 곡선이 존재합니다.
    배포 자동화: Git 변경 사항이 자동으로 감지되어 배포되므로, 수동 작업으로 인한 오류를 줄입니다. GitOps 모델 이해 필요: 개발/운영 팀 모두 GitOps 워크플로우에 대한 이해와 적응이 필요합니다.
    빠른 복구 (Disaster Recovery): Git에 모든 설정이 있으므로, 장애 발생 시 새로운 클러스터에 빠르게 복구할 수 있습니다. 중앙 ArgoCD 인스턴스 의존성: 중앙 ArgoCD 서버에 장애가 발생하면 모든 클러스터 배포에 영향을 미칠 수 있습니다 (HA 구성으로 보완 가능).
    버전 관리 및 감사: 모든 변경 이력이 Git에 남아 누가 언제 무엇을 변경했는지 추적하기 용이합니다. 시크릿(Secret) 관리: Git에 민감 정보를 직접 저장하기 어려우므로, Vault(볼트) 등 별도의 시크릿 관리 솔루션과 연동해야 합니다.

    마무리: 💡 ArgoCD 멀티 클러스터 배포, 지금 시작해볼까?

    오늘은 ArgoCD를 활용한 멀티 쿠버네티스 클러스터 배포 전략에 대해 제가 직접 경험하고 삽질하며 얻은 노하우를 공유해드렸습니다. GitOps는 단순히 배포 도구를 넘어, 인프라 운영 패러다임을 혁신하는 강력한 방법론이라고 생각합니다. 13년차 엔지니어로서, 이 정도 자동화는 이제 선택이 아니라 필수라고 생각합니다. 처음엔 좀 어렵고 헷갈릴 수 있지만, 한번 구축하고 나면 정말 편안하고 안정적인 인프라를 운영할 수 있을 거예요.

    여러분도 이 글을 통해 ArgoCD 멀티 클러스터 환경 구축에 자신감을 얻으셨기를 바랍니다. 다음 글에서는 ArgoCD의 고가용성(High Availability, HA) 구성이나 외부 시크릿 관리 도구(예: HashiCorp Vault)와의 연동 등 좀 더 심화된 주제를 다뤄볼까 합니다. 궁금한 점이나 공유하고 싶은 삽질 경험이 있다면 언제든지 댓글로 남겨주세요!

    그림 4: ArgoCD 멀티 클러스터 배포의 핵심 요약 및 다음 단계

  • [k8s] Flux CD GitOps 파이프라인 구축: 안정적인 쿠버네티스 배포 자동화

    [k8s] Flux CD GitOps 파이프라인 구축: 안정적인 쿠버네티스 배포 자동화

    안녕하세요, 13년차 서버실 지킴이입니다. 오늘은 많은 인프라 엔지니어분들이 한 번쯤은 고민해봤을 주제, 바로 쿠버네티스(Kubernetes) 배포 자동화에 대해 이야기해보려고 합니다. 특히 GitOps(깃옵스) 방법론과 그 구현체 중 하나인 Flux CD(플럭스 CD)를 활용한 파이프라인 구축 경험을 공유해 드릴게요.

    혹시 이런 경험 있으신가요? 개발팀에서 “배포해주세요!” 요청이 들어오면, kubectl apply -f 명령어를 조심스럽게 입력하면서, “혹시나 다른 설정이 덮어씌워지는 건 아닐까?”, “이전 버전으로 되돌리려면 어떻게 해야 하지?” 같은 걱정을 했던 순간 말이죠. 저는 셀 수 없이 많습니다. 수동 배포는 휴먼 에러의 온상이었고, 배포하다 새벽을 맞이하는 일도 비일비재했거든요. 이런 삽질을 거듭하다가 GitOps를 만나고 나서야 비로소 안정적인 배포의 빛을 보게 되었습니다. 제 홈랩에서도 여러 서비스를 GitOps로 관리하고 있는데, 정말 편하더라고요!

    GitOps 파이프라인은 Git 저장소를 ‘진리의 단일 출처(Single Source of Truth)’로 삼아, 쿠버네티스 클러스터의 상태를 Git에 선언된 대로 유지하는 방식입니다. 간단히 말해, Git에 저장된 설정 파일이 곧 클러스터의 현재 상태여야 한다는 거죠.

    GitOps와 Flux CD, 정말 좋은 이유

    처음 GitOps라는 개념을 들었을 때는 “어차피 Git에 YAML 파일 올리는 건 똑같은데, 뭐가 다르지?” 싶었어요. 근데 실제로 써보니 이 방식이 가진 장점이 정말 많더라고요. 제가 느낀 가장 큰 장점들을 꼽아보면 이렇습니다.

    • 일관성(Consistency): 모든 설정이 Git에 있으니, 개발, 스테이징, 운영 환경 간의 차이를 최소화할 수 있어요. “제 환경에서는 잘 되는데요?” 하는 말이 확 줄어듭니다.
    • 감사 및 추적(Auditability & Traceability): Git 커밋(Commit) 기록 자체가 변경 이력이 되니까요. 누가, 언제, 무엇을 변경했는지 명확하게 알 수 있죠. 문제 발생 시 롤백(Rollback)도 Git으로 너무나 쉽습니다.
    • 안정성(Reliability): Git에 선언된 상태와 클러스터의 실제 상태가 다르면, GitOps 에이전트가 자동으로 클러스터를 Git의 상태로 되돌립니다. 마치 자가 치유(Self-healing) 능력 같달까요?
    • 생산성(Productivity): 수동 작업이 줄어들고 자동화되면서, 엔지니어는 더 중요한 일에 집중할 수 있게 됩니다.

    이런 GitOps의 철학을 구현하는 대표적인 도구가 바로 Flux CD입니다. Flux CD는 쿠버네티스 클러스터 내에서 동작하는 컨트롤러(Controller)들의 집합인데요, 주기적으로 Git 저장소를 감시하다가 변경 사항이 감지되면 자동으로 클러스터에 적용해주는 역할을 합니다. 주요 컴포넌트로는 Source Controller(소스 컨트롤러), Kustomize Controller(커스터마이즈 컨트롤러), Helm Controller(헬름 컨트롤러) 등이 있습니다.

    Flux CD GitOps 파이프라인 구축 실전 가이드

    자, 그럼 이제 제 홈랩에서 Flux CD를 어떻게 구축했는지, 단계별로 자세히 알려드릴게요. 저도 처음엔 공식 문서를 보면서 여러 번 헤맸는데, 이 가이드가 여러분의 삽질 시간을 줄여주길 바랍니다!

    1단계: 사전 준비 및 Flux CLI 설치

    먼저, 쿠버네티스 클러스터에 접근 가능한 환경과 kubectl, git이 설치되어 있어야 합니다. 저는 로컬 PC에 flux CLI(Command Line Interface)를 설치하는 것부터 시작했어요.

    # Flux CLI 설치 (macOS 기준)
    brew install fluxcd/flux/flux
    
    # 설치 확인
    flux --version
    # flux version 2.x.x (최신 버전으로 설치됩니다)
    
    # 클러스터에 Flux CD가 설치될 네임스페이스 생성 (선택 사항)
    kubectl create namespace flux-system
    

    flux-system 네임스페이스는 Flux CD 컨트롤러들이 배포될 기본 공간입니다. 따로 지정하지 않으면 기본으로 이 네임스페이스에 설치되죠.

    2단계: Git 저장소 준비

    Flux CD는 Git 저장소를 바라보며 작동하기 때문에, 쿠버네티스 설정 파일(YAML)들을 저장할 Git 저장소가 필요합니다. 저는 GitHub에 gitops-k8s-homelab이라는 프라이빗(Private) 저장소를 만들었어요. 저장소 안에는 다음과 같은 구조로 파일을 구성할 예정입니다.

    gitops-k8s-homelab/
    ├── clusters/
    │   └── my-cluster/
    │       ├── flux-system/
    │       │   └── gotk-components.yaml
    │       │   └── gotk-sync.yaml
    │       └── apps/
    │           └── my-app/
    │               └── kustomization.yaml
    ├── apps/
    │   └── my-app/
    │       ├── deployment.yaml
    │       ├── service.yaml
    │       └── ingress.yaml
    └── README.md
    

    clusters/my-cluster/flux-system 경로에는 Flux CD 자체의 설정 파일들이, clusters/my-cluster/apps 경로에는 클러스터에 배포할 애플리케이션들을 정의하는 Kustomization 파일들이 들어갈 겁니다. 실제 애플리케이션 YAML 파일들은 apps/my-app 경로에 두고요.

    3단계: Flux CD 부트스트랩 (Bootstrap)

    이제 가장 중요한 단계입니다. flux bootstrap github 명령어를 사용해서 Flux CD를 쿠버네티스 클러스터에 설치하고, Git 저장소와 연결하는 작업을 수행합니다. 이 명령 한 방으로 Flux CD가 클러스터에 필요한 모든 컴포넌트를 배포하고, 지정된 Git 저장소를 바라보도록 설정해줍니다. 저는 GitHub를 사용했으니 github 옵션을 사용했어요.

    # 환경 변수 설정 (개인 GitHub 토큰과 사용자명으로 대체하세요!)
    export GITHUB_TOKEN="YOUR_GITHUB_TOKEN" # Personal Access Token
    export GITHUB_USER="YOUR_GITHUB_USERNAME"
    
    # Flux CD 부트스트랩 실행
    flux bootstrap github \
      --owner=${GITHUB_USER} \
      --repository=gitops-k8s-homelab \
      --branch=main \
      --path=clusters/my-cluster \
      --personal
    

    이 명령을 실행하면 Flux CD가 클러스터에 설치되고, clusters/my-cluster 경로를 기준으로 Git 저장소의 내용을 동기화하기 시작합니다. --personal 옵션은 개인 저장소에 주로 사용하고, 조직(Organization) 저장소의 경우 --owner를 조직명으로 지정하고 SSH 키 방식으로 인증하는 것이 일반적입니다. 저는 홈랩이라 개인 토큰으로 편하게 작업했어요.

    부트스트랩이 완료되면, Git 저장소의 clusters/my-cluster/flux-system 경로에 Flux CD 자체의 Kustomization 파일들이 자동으로 생성되어 커밋되는 것을 볼 수 있습니다. 이게 바로 Flux CD가 스스로를 GitOps 방식으로 관리하는 모습이죠. 신기하더라고요!

    4단계: 애플리케이션 배포를 위한 Kustomization 정의

    이제 클러스터에 배포할 애플리케이션을 정의하고 Flux CD가 이를 인식하도록 설정할 차례입니다. 먼저, apps/my-app 경로에 간단한 NGINX 애플리케이션을 정의하는 YAML 파일들을 만듭니다.

    # apps/my-app/deployment.yaml
    apiVersion: apps/v1
    kind: Deployment
    metadata:
      name: my-nginx-app
      labels:
        app: my-nginx-app
    spec:
      replicas: 2
      selector:
        matchLabels:
          app: my-nginx-app
      template:
        metadata:
          labels:
            app: my-nginx-app
        spec:
          containers:
          - name: nginx
            image: nginx:latest
            ports:
            - containerPort: 80
    ---
    # apps/my-app/service.yaml
    apiVersion: v1
    kind: Service
    metadata:
      name: my-nginx-app-service
    spec:
      selector:
        app: my-nginx-app
      ports:
      - protocol: TCP
        port: 80
        targetPort: 80
      type: ClusterIP
    

    그리고 이 애플리케이션을 클러스터에 배포하도록 지시하는 Kustomization 파일을 clusters/my-cluster/apps/my-app/kustomization.yaml 경로에 생성합니다.

    # clusters/my-cluster/apps/my-app/kustomization.yaml
    apiVersion: kustomize.toolkit.fluxcd.io/v1
    kind: Kustomization
    metadata:
      name: my-nginx-app
      namespace: flux-system
    spec:
      interval: 1m0s
      sourceRef:
        kind: GitRepository
        name: flux-system
      path: ./apps/my-app
      prune: true
      validation: client
    

    이 파일들을 Git 저장소에 커밋(Commit)하고 푸시(Push)합니다.

    git add .
    git commit -m "Add my-nginx-app and kustomization"
    git push origin main
    

    Flux CD는 clusters/my-cluster/flux-system/gotk-sync.yaml 파일에 정의된 Kustomization에 의해 clusters/my-cluster 경로를 1분마다 동기화합니다. 그러면 방금 추가한 clusters/my-cluster/apps/my-app/kustomization.yaml 파일이 클러스터에 적용되고, 이 Kustomization이 다시 apps/my-app 경로의 NGINX 애플리케이션을 클러스터에 배포하게 됩니다. 이 모든 과정이 자동으로 이뤄지는 거죠!

    ⚠️ 삽질 경험 & 트러블슈팅 팁

    제가 Flux CD를 사용하면서 겪었던 몇 가지 삽질과 해결 팁을 공유해 드릴게요. 여러분은 저처럼 헤매지 마시라고요! ㅎㅎ

    • 동기화 지연 문제: Git에 변경사항을 푸시했는데, 클러스터에 바로 적용이 안 되는 경우가 있습니다. Kustomization 리소스의 interval 값이 기본 10분으로 설정되어 있거나, 네트워크 문제 등으로 Git 저장소에 접근이 안 되는 경우가 많았어요. 급할 때는 다음 명령어로 강제 동기화를 시도합니다.
      flux reconcile kustomization my-nginx-app --with-source
      

      이 명령은 해당 Kustomization과 그 소스 GitRepository를 즉시 동기화하도록 Flux CD에 지시합니다.

    • Git Repository 구조 설계: 처음에는 모든 YAML 파일을 한 폴더에 때려 넣었는데, 나중에 애플리케이션이 많아지고 환경이 복잡해지면서 관리가 힘들어지더라고요. clusters/[클러스터이름]/[환경]/apps/[앱이름] 같은 계층적인 구조나, clusters와 apps를 분리하는 구조로 가져가는 것이 훨씬 효율적입니다. 저는 지금 clusters와 apps를 분리해서 관리하고 있어요.
    • 시크릿(Secret) 관리: 데이터베이스 비밀번호 같은 민감한 정보는 Git에 평문으로 올릴 수 없죠. 저는 SOPS(Secrets OPerationS)를 사용해서 Git 저장소에 암호화된 시크릿을 저장하고, Flux CD가 배포 시 자동으로 복호화하도록 설정했습니다. EKS(Elastic Kubernetes Service) 같은 클라우드 환경에서는 KMS(Key Management Service)를 연동해서 SOPS를 사용하는 것이 일반적입니다.
    • CRD(Custom Resource Definition) 적용 순서: 때로는 특정 컨트롤러나 미들웨어의 CRD가 먼저 클러스터에 적용되어야만 해당 리소스를 배포할 수 있습니다. 예를 들어, Istio(이스티오)나 Cert-Manager(서트 매니저) 같은 도구들이 그렇죠. 이런 경우, Kustomization의 dependsOn 필드를 사용하거나, healthChecks를 설정하여 의존성을 명확히 해주는 것이 중요합니다.

    ✅ 배포 결과 확인 및 검증

    이제 모든 설정이 잘 적용되었는지 확인해볼 시간입니다. flux get kustomizations 명령어로 Flux CD가 관리하는 Kustomization 리소스들의 상태를 확인할 수 있습니다.

    flux get kustomizations
    NAME            REVISION        SUSPENDED   READY   MESSAGE                                         LAST ACTIVITY
    flux-system     main/a1b2c3d4   False       True    Kustomization reconciled successfully           2026-05-15T10:00:00Z
    my-nginx-app    main/e5f6g7h8   False       True    Kustomization reconciled successfully           2026-05-15T10:01:00Z
    

    READY 상태가 True이고 MESSAGE에 성공 메시지가 보인다면, Flux CD가 정상적으로 작동하고 있다는 뜻입니다. 이제 쿠버네티스 클러스터에 NGINX 애플리케이션이 잘 배포되었는지 확인해볼까요?

    kubectl get deployment my-nginx-app
    NAME           READY   UP-TO-DATE   AVAILABLE   AGE
    my-nginx-app   2/2     2            2           5m
    
    kubectl get service my-nginx-app-service
    NAME                     TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)   AGE
    my-nginx-app-service     ClusterIP   10.96.100.123   <none>        80/TCP    5m
    

    🎉 드디어 성공입니다! my-nginx-app 배포가 정상적으로 2개의 레플리카(Replica)로 동작하고 있는 것을 확인할 수 있네요. 이제 Git 저장소의 apps/my-app/deployment.yaml 파일에서 replicas 수를 3으로 변경하고 푸시해보세요. 잠시 후 Flux CD가 이를 감지하고 자동으로 클러스터의 레플리카 수를 3으로 업데이트할 겁니다. 이 경험을 하고 나면 GitOps의 매력에서 헤어 나올 수 없을 거예요!

    GitOps는 이렇게 Git에 대한 변경만으로 클러스터의 상태를 관리할 수 있게 해줍니다. 저는 이 편리함 덕분에 홈랩에서 새로운 서비스를 배포할 때마다 kubectl 명령어를 직접 입력할 일이 거의 없어졌어요. 모든 변경사항은 Git에 기록되니, 누가 어떤 변경을 했는지도 명확하게 알 수 있고요. 정말 안정적이고 편리한 배포 파이프라인이 구축된 거죠.

    마무리하며: GitOps와 Flux CD, 현대 인프라의 필수 도구

    오늘은 13년차 인프라 엔지니어의 경험을 바탕으로 Flux CD GitOps 파이프라인 구축에 대해 상세히 이야기해봤습니다. 쿠버네티스 배포 자동화는 현대 인프라에서 선택이 아닌 필수가 되어가고 있습니다. 특히 GitOps는 그 중심에서 안정성, 일관성, 생산성이라는 세 마리 토끼를 동시에 잡을 수 있게 해주는 강력한 방법론이라고 생각합니다.

    물론 처음에는 GitOps 개념이나 Flux CD 사용법이 낯설고 어렵게 느껴질 수 있습니다. 저도 그랬거든요. 하지만 한 번 제대로 구축하고 나면, 인프라 관리의 패러다임이 확 바뀌는 것을 경험하실 수 있을 겁니다. 마치 수동으로 서버를 한 대 한 대 설치하다가 프로비저닝(Provisioning) 자동화를 접했을 때의 충격과 비슷하다고 할까요?

    다음 글에서는 오늘 다루지 못한 Helm Chart(헬름 차트)를 Flux CD로 관리하는 방법이나, 멀티 클러스터(Multi-cluster) 환경에서의 GitOps 전략, 그리고 SOPS를 이용한 시크릿 관리 등 좀 더 심화된 주제들을 다뤄볼 예정입니다. 이 글이 여러분의 쿠버네티스 여정에 작은 도움이 되었기를 바랍니다!

    궁금한 점이나 저의 삽질 경험에 대한 질문이 있으시면 언제든지 댓글 남겨주세요! 저도 계속 배우고 성장하는 중이거든요. 😊

  • [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)에 저장하고 관리하는 방법에 대해 다뤄볼 예정이니, 기대해주세요! 😊

  • [쿠버네티스] Nginx vs Traefik Ingress Controller 선택 가이드: 13년차 엔지니어의 경험담

    [쿠버네티스] Nginx vs Traefik Ingress Controller 선택 가이드: 13년차 엔지니어의 경험담

    [쿠버네티스] Nginx vs Traefik Ingress Controller 선택 가이드: 13년차 엔지니어의 경험담

    안녕하세요, 13년차 서버실을 지키는 인프라 엔지니어입니다. 쿠버네티스(Kubernetes) 환경에서 애플리케이션을 운영하다 보면, 외부 트래픽을 어떻게 효율적으로 서비스에 연결할지가 항상 큰 고민이 됩니다. 특히 여러 서비스를 운영할수록 이 문제는 더욱 복잡해지죠. 처음엔 저도 단순히 <code>NodePort나 LoadBalancer 서비스를 사용했었는데, 점점 더 많은 도메인과 복잡한 라우팅 규칙을 관리해야 하는 상황에 부딪히더라고요. 이럴 때 필요한 것이 바로 Ingress Controller(인그레스 컨트롤러)입니다.

    오늘은 제가 홈랩(Homelab)에서 Nginx Ingress Controller와 Traefik Ingress Controller를 모두 사용해보면서 겪었던 경험과 두 솔루션의 장단점을 솔직하게 비교해드릴게요. 어떤 상황에서 어떤 Ingress Controller를 선택하는 것이 좋을지 제 경험을 토대로 멘토처럼 알려드리겠습니다. 이 글이 여러분의 쿠버네티스 트래픽 관리 고민을 덜어주면 정말 좋겠어요.

    쿠버네티스 Ingress Controller의 역할과 외부 트래픽 흐름을 보여주는 아키텍처 다이어그램

    쿠버네티스 클러스터 내에서 Ingress Controller가 외부 트래픽을 서비스로 라우팅하는 전체 아키텍처를 보여주는 다이어그램입니다. 외부 로드밸런서에서 Ingress Controller로, 다시 Ingress Rule에 따라 내부 서비스로 트래픽이 흐르는 과정을 시각화합니다.

    Ingress Controller, 대체 뭘까요?

    쿠버네티스에서 Ingress(인그레스, 외부 트래픽 진입점)는 클러스터 내부의 서비스(Service)로 외부 트래픽을 라우팅하는 규칙들을 정의하는 API 오브젝트죠. 쉽게 말해, example.com으로 들어온 요청은 web-service로 보내고, api.example.com/v1으로 들어온 요청은 api-service로 보내는 것과 같은 규칙을 정의하는 거예요. 하지만 Ingress 오브젝트 자체는 단순히 규칙을 정의할 뿐, 실제로 그 규칙에 따라 트래픽을 처리하는 역할을 하지는 않습니다. 바로 이걸 실제로 처리하는 게 Ingress Controller(인그레스 컨트롤러)입니다.

    Ingress Controller는 클러스터 내부에서 실행되는 일종의 역방향 프록시(Reverse Proxy) 또는 로드 밸런서(Load Balancer)로, Ingress 리소스에 정의된 규칙을 읽어 실제 트래픽 라우팅을 수행합니다. 대표적으로 Nginx, Traefik, HAProxy, Envoy 등의 솔루션들이 Ingress Controller로 활용될 수 있습니다. 이 중에서 가장 널리 사용되고 제가 직접 많이 써본 Nginx와 Traefik을 중심으로 이야기해볼게요.

    Nginx Ingress Controller 파헤치기

    Nginx Ingress Controller는 이름에서 알 수 있듯이, 인기 있는 웹 서버이자 역방향 프록시인 Nginx를 기반으로 만들어졌습니다. 오랫동안 인프라 엔지니어들에게 사랑받아온 Nginx의 안정성과 성능을 쿠버네티스 환경에서도 활용할 수 있다는 게 가장 큰 장점이죠. 저도 예전부터 Nginx를 많이 써왔던 터라, 처음 쿠버네티스 Ingress를 접했을 때 가장 먼저 선택하게 된 솔루션입니다.

    ✅ Nginx Ingress Controller의 주요 장점

    • 높은 안정성과 성능: Nginx 자체가 워낙 안정적이고 고성능이어서, 대규모 트래픽 처리에도 강합니다.
    • 광범위한 사용처와 커뮤니티: 가장 널리 사용되는 Ingress Controller 중 하나인 만큼, 자료가 풍부하고 문제가 생겼을 때 도움을 받기 쉽습니다.
    • 풍부한 기능: Nginx의 강력한 기능을 annotation(어노테이션)을 통해 활용할 수 있습니다. (예: 리다이렉트, 인증, 로드밸런싱 알고리즘 등)

    ⚠️ Nginx Ingress Controller의 아쉬운 점

    • 설정의 복잡성: Nginx 설정 파일(nginx.conf)을 직접 다루지 않고 ConfigMap(컨피그맵)이나 annotation으로 간접적으로 설정하다 보니, 특정 고급 기능을 사용하려면 Nginx 설정 문법을 이해하고 annotation을 정확히 알아야 합니다.
    • 동적 업데이트의 한계: 새로운 Ingress 규칙을 적용하거나 기존 규칙을 변경할 때, Nginx 프로세스를 리로드(reload)하는 과정이 필요합니다. 컨트롤러가 자동으로 처리하지만, Traefik에 비해서는 동적이라는 느낌이 덜합니다.

    간단한 Nginx Ingress 리소스 예시를 볼까요? example.com으로 들어오는 요청을 my-service로 라우팅하는 설정입니다.

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

    Traefik Ingress Controller 만나보기

    Traefik Proxy(트래픽 프록시)는 클라우드 네이티브 환경에 최적화된 최신 역방향 프록시 및 로드 밸런서입니다. 특히 쿠버네티스 Ingress Controller로서의 강점이 두드러지죠. 처음엔 이게 뭔가 싶었는데, 써보니 동적인 설정 관리가 정말 신세계더라고요.

    ✅ Traefik Ingress Controller의 주요 장점

    • 뛰어난 동적 설정: 쿠버네티스의 CRD(Custom Resource Definition, 사용자 정의 리소스 정의)를 적극 활용하여 Ingress 규칙을 실시간으로 업데이트합니다. Nginx처럼 리로딩 과정 없이 즉시 반영돼요.
    • 쉬운 설정 및 대시보드: IngressRoute와 같은 CRD를 통해 직관적인 설정이 가능하고, 웹 대시보드를 기본으로 제공하여 현재 라우팅 상태를 한눈에 파악하기 좋습니다. 저는 홈랩에서 여러 서비스를 띄울 때 이 대시보드가 정말 편하더라고요.
    • 자동 HTTPS (Let’s Encrypt): Let’s Encrypt(렛츠 인크립트)를 내장하여 도메인에 대한 HTTPS 인증서를 자동으로 발급하고 갱신해줍니다. 이거 진짜 편합니다!
    • 간결한 설정: Nginx의 복잡한 annotation 대신, 자체 CRD를 사용해 더 명확하고 간결하게 설정을 정의할 수 있습니다.

    ⚠️ Traefik Ingress Controller의 아쉬운 점

    • Nginx 대비 상대적으로 작은 커뮤니티: Nginx만큼 오랜 역사를 가진 건 아니어서, 자료나 커뮤니티 규모가 상대적으로 작을 수 있습니다. 하지만 성장세가 빠르고 공식 문서가 잘 되어 있습니다.
    • 특정 고급 기능의 복잡성: Nginx가 가진 다양한 모듈만큼의 유연성은 아닐 수 있습니다. 특정 시나리오에서는 Nginx의 annotation이 더 직관적일 때도 있습니다.

    Traefik의 IngressRoute CRD 예시입니다. Nginx Ingress와 동일하게 example.com을 my-service로 라우팅합니다.

    apiVersion: traefik.containo.us/v1alpha1
    kind: IngressRoute
    metadata:
      name: my-traefik-ingressroute
    spec:
      entryPoints:
        - web
        - websecure
      routes:
        - match: Host(`example.com`)
          kind: Rule
          services:
            - name: my-service
              port: 80
      tls:
        certResolver: myresolver # Let's Encrypt를 사용하는 경우
    

    실전 비교: Nginx vs Traefik, 어떤 걸 선택해야 할까?

    이제 두 Ingress Controller의 특징을 알았으니, 어떤 상황에서 어떤 것을 선택하는 것이 좋을지 제 경험을 토대로 비교해드릴게요. 사실 정답은 없습니다. 여러분의 환경과 요구사항에 따라 달라지는 거죠.

    Nginx Ingress Controller와 Traefik Ingress Controller의 주요 기능 및 장단점을 비교하는 표

    Nginx Ingress Controller와 Traefik Ingress Controller의 핵심적인 기능, 설정 방식, 성능, 커뮤니티 지원, 자동화 기능 등을 비교하는 표입니다. 사용자가 두 Ingress Controller 중 하나를 선택하는 데 필요한 정보를 요약하여 제공합니다.

    특징 Nginx Ingress Controller Traefik Ingress Controller
    기반 기술 Nginx 웹 서버 Golang으로 개발된 클라우드 네이티브 프록시
    설정 방식 Ingress 리소스 + Annotation, ConfigMap IngressRoute (CRD), Middlewares (CRD)
    동적 설정 변경 시 Nginx 리로드 필요 (자동화) 실시간 반영, 리로드 불필요
    HTTPS (Let’s Encrypt) 외부 Cert-Manager 등 연동 필요 내장 기능으로 자동화 용이
    관리 대시보드 별도 모니터링 툴 연동 (Prometheus, Grafana 등) 기본 제공 웹 대시보드
    커뮤니티/자료 매우 크고 풍부함 상대적으로 작지만 활발하며 성장 중
    성능 오랜 검증을 통한 고성능 클라우드 네이티브 환경에 최적화된 고성능
    사용 편의성 Nginx 지식 필요, Annotation 학습 필요 CRD 기반의 직관적인 설정, 빠른 학습 곡선

    Nginx Ingress Controller 추천하는 경우

    • 레거시 시스템과의 연동: 이미 Nginx 설정을 잘 다루는 팀이 있거나, 기존 Nginx 설정에 익숙하다면 Nginx Ingress Controller가 더 자연스럽게 느껴질 겁니다.
    • 최고의 안정성과 성능이 요구될 때: 미션 크리티컬한 서비스로, 수년간 검증된 Nginx의 안정성과 성능이 절대적으로 필요하다면 좋은 선택입니다.
    • 복잡하고 세밀한 트래픽 제어: Nginx의 다양한 모듈과 세밀한 설정을 통해 고도로 커스터마이징된 트래픽 제어가 필요할 때 유용합니다.

    Traefik Ingress Controller 추천하는 경우

    • 클라우드 네이티브 환경에 대한 높은 이해도와 선호: 쿠버네티스의 CRD를 활용한 동적인 설정에 강점이 있습니다.
    • 빠른 개발 및 배포 주기: 새로운 서비스를 자주 배포하거나 Ingress 규칙이 자주 변경되어야 하는 환경에서 실시간 업데이트는 큰 장점입니다.
    • 간편한 Let’s Encrypt 자동화: 수많은 서비스에 HTTPS를 쉽게 적용하고 싶다면 Traefik이 제공하는 자동화 기능은 정말 강력합니다. 저도 홈랩에서 이 기능 덕분에 HTTPS 적용이 너무 쉬웠어요.
    • 초보자나 소규모 환경: 대시보드와 쉬운 설정 덕분에 초보자도 빠르게 시작하고 관리할 수 있습니다.

    제가 겪었던 삽질과 해결 과정 ⚠️

    저도 처음부터 모든 걸 다 알았던 건 아니죠. 두 Ingress Controller를 사용하면서 꽤나 삽질을 많이 했습니다. 특히 몇 가지 기억에 남는 경험을 공유해볼게요.

    Nginx Ingress Controller 삽질: Annotation 지옥과 ConfigMap 캐싱

    Nginx Ingress Controller를 사용하면서 가장 많이 헷갈렸던 부분은 annotation이었습니다. Nginx의 모든 기능을 annotation으로 매핑하는 게 아니다 보니, 특정 기능(예: 커스텀 에러 페이지, 특정 헤더 추가)을 넣으려 할 때 어떤 annotation을 써야 할지, 아니면 ConfigMap을 수정해야 할지 찾는 데 시간을 많이 썼습니다. 특히 ConfigMap을 수정했을 때 바로 반영이 안 되고 캐싱 때문에 고생했던 기억도 있네요. ConfigMap을 업데이트하면 Nginx Ingress Controller Pod가 재시작되거나 설정이 리로드되는데, 이 과정에서 찰나의 순간 서비스에 영향을 줄까 봐 조마조마했던 적도 많았습니다.

    💡 해결 팁: Nginx Ingress Controller의 공식 문서에 있는 annotation 목록을 즐겨찾기에 등록해두고, ConfigMap 수정 후에는 항상 컨트롤러 로그를 확인하며 제대로 리로드되었는지 확인하는 습관을 들였습니다.

    Traefik Ingress Controller 삽질: CRD 이해와 EntryPoint 설정

    Traefik은 Nginx와는 다르게 IngressRoute라는 자체 CRD를 사용합니다. 처음엔 이 개념이 좀 낯설었어요. 특히 entryPoints와 middlewares 같은 개념들을 이해하는 데 시간이 걸렸죠. 예를 들어, 특정 포트(entryPoint)로 들어오는 요청에만 적용되는 규칙을 만들거나, 인증(middleware)을 추가하는 설정을 하려다가 몇 번이나 실패했는지 모릅니다. 특히 tls 설정에서 certResolver를 제대로 지정하지 않아서 Let’s Encrypt 자동 발급이 안 되는 문제도 겪었고요.

    # 잘못된 IngressRoute 설정 예시 (entryPoints 누락)
    apiVersion: traefik.containo.us/v1alpha1
    kind: IngressRoute
    metadata:
      name: my-broken-route
    spec:
      routes:
        - match: Host(`broken.example.com`)
          kind: Rule
          services:
            - name: my-service
              port: 80
    # 이 경우, Traefik이 어떤 entryPoint로 트래픽을 받을지 몰라 동작하지 않습니다.
    

    💡 해결 팁: Traefik 공식 문서를 정독하고, 특히 entryPoints와 middlewares 개념을 확실히 이해하는 것이 중요했습니다. 그리고 IngressRoute를 배포하기 전에 항상 Traefik 대시보드를 통해 현재 설정이 어떻게 반영될지 미리 확인하는 습관을 들였어요. 대시보드만큼 직관적인 디버깅 도구가 없더라고요!

    Traefik Ingress Controller 웹 대시보드에서 서비스 목록과 라우팅 규칙을 보여주는 화면

    Traefik Ingress Controller의 웹 대시보드 스크린샷으로, 현재 활성화된 서비스와 라우팅 규칙(IngressRoutes)이 명확하게 표시되어 트래픽 흐름을 시각적으로 파악할 수 있는 화면입니다.

    결론: 당신의 환경에 맞는 Ingress Controller는?

    제가 Nginx와 Traefik을 모두 써보면서 느낀 점은, 두 Ingress Controller 모두 훌륭한 솔루션이라는 것입니다. 다만 각자의 강점과 약점이 뚜렷하다는 것이죠. Nginx는 전통적인 인프라 환경에서 익숙한 안정성과 고성능을 제공하며, 복잡한 설정에 대한 유연성을 가집니다. 반면 Traefik은 클라우드 네이티브 환경의 동적인 특성에 완벽하게 부합하며, 자동화와 사용 편의성에서 큰 강점을 보입니다.

    저의 홈랩 환경에서는 새로운 서비스를 빠르게 올리고 내리며, Let’s Encrypt로 HTTPS를 쉽게 적용해야 하는 경우가 많아서 최종적으로는 Traefik Ingress Controller를 선택하여 운영하고 있습니다. 대시보드를 통해 현재 라우팅 상황을 한눈에 볼 수 있고, 새로운 IngressRoute를 배포하면 바로 적용되는 그 편리함이 정말 매력적이었거든요. 물론 Nginx도 여전히 중요한 역할을 하는 곳이 많습니다. 여러분의 팀 문화, 기존 인프라 스택, 그리고 프로젝트의 요구사항을 신중하게 고려하여 쿠버네티스 트래픽 관리에 최적의 선택을 하시길 바랍니다.

    Nginx Ingress Controller와 Traefik Ingress Controller의 핵심 차이점을 요약한 인포그래픽

    Nginx Ingress Controller와 Traefik Ingress Controller의 주요 특징, 장단점, 추천 사용 사례를 한눈에 비교할 수 있도록 시각적으로 요약한 인포그래픽입니다.

    마무리하며

    쿠버네티스 환경에서 Ingress Controller는 외부와 내부를 연결하는 중요한 다리 역할을 합니다. Nginx와 Traefik 모두 각자의 방식으로 이 역할을 훌륭하게 수행하죠. 어떤 것을 선택하든, 중요한 것은 해당 솔루션의 작동 방식을 이해하고 여러분의 환경에 맞게 최적화하는 것입니다. 제가 겪었던 삽질 경험들이 여러분의 시간과 노력을 아끼는 데 조금이나마 도움이 되었기를 바랍니다.

    다음 글에서는 Traefik을 이용한 Let’s Encrypt 자동화 설정에 대해 좀 더 자세히 다뤄볼까 합니다. 긴 글 읽어주셔서 감사합니다! 궁금한 점이 있다면 언제든지 댓글로 남겨주세요. 제가 아는 선에서는 성심성의껏 답변해드리겠습니다. 🎉

  • [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가 더 복잡한 요구사항을 위한 차세대 표준으로 자리잡고 있는 중입니다.