13년차의 서버실

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

[카테고리:] k8s

  • [k8s] Backstage 운영 후기: 개발자 포털 1년 회고와 정착 과정

    [k8s] Backstage 운영 후기: 개발자 포털 1년 회고와 정착 과정

    Backstage 운영 후기: 개발자 포털 1년 회고와 정착 과정

    Backstage 운영 후기를 정리해보려고 합니다. 개발팀이 커질수록 문서는 여기저기 흩어지고, 서비스는 늘어나고, 누구에게 뭘 물어봐야 하는지도 점점 모호해지더라고요. 저도 인프라 일을 오래 하면서 이런 상황을 정말 많이 봤습니다. 처음엔 위키만 잘 정리하면 되겠지 싶었는데, 실제로 써보니 위키만으로는 서비스 카탈로그(Service Catalog, 서비스 목록 체계), 템플릿(Template, 표준 생성 양식), 권한 관리(Permission, 접근 제어)까지 한 번에 풀기 어렵더라고요. 그래서 선택한 게 바로 Backstage였습니다.

    이 글은 화려한 성공담보다는, 실제에 가까운 Backstage 도입 사례 기록입니다. 도입 검토부터 초기 구축, 팀 정착, 그리고 1년 운영하면서 느낀 한계까지 솔직하게 적어보겠습니다. 지금 개발자 포털 구축을 고민 중이라면 시행착오를 줄이는 데 도움이 될 겁니다.

    Backstage 운영 후기를 설명하는 개발자 포털 전체 아키텍처 이미지

    Backstage를 중심으로 Git 저장소, CI/CD, 문서, 모니터링 도구가 연결되는 전체 구조를 한눈에 보여주는 이미지입니다.

    Backstage란 무엇이고 왜 도입했나

    쉽게 말해 Backstage는 개발자 포털을 구축할 때 쓰는 오픈소스 프레임워크입니다. 서비스 목록을 모아두는 화면이기도 하고, 팀 표준을 배포하는 플랫폼이기도 하고, 신규 서비스 생성 흐름을 자동화하는 입구이기도 하죠. 처음엔 “이거 그냥 내부 위키랑 뭐가 다르지?” 싶었는데, 막상 운영해보니 차이가 꽤 컸습니다.

    제가 체감한 가장 큰 차이는 세 가지였습니다.

    • 서비스를 문서가 아니라 엔티티(Entity, 관리 대상 객체)로 다룬다: 팀, 시스템, API, 컴포넌트를 관계로 연결할 수 있습니다.
    • 소유권(Ownership, 담당 주체)이 드러난다: 장애가 났을 때 누가 관리하는지 찾는 시간이 줄었습니다.
    • 표준화가 강제된다: 새 저장소를 만들 때부터 템플릿으로 기본 구조를 맞출 수 있거든요.

    특히 플랫폼 엔지니어링(Platform Engineering, 개발 생산성 플랫폼 설계) 관점에서 좋았던 건, 운영팀이 “지침”만 주는 게 아니라 “실행 가능한 기본값”을 제공할 수 있다는 점이었습니다. 말로만 표준을 외치면 잘 안 지켜집니다. 그런데 템플릿에 녹여두면 생각보다 잘 따라오더라고요. 이거 진짜 편했습니다.

    참고로 Backstage는 CNCF 인큐베이팅 프로젝트이기도 합니다. 그래서 CNCF Backstage라는 표현을 쓰더라도, 제품명이라기보다 CNCF 생태계에 속한 Backstage를 가리키는 말로 이해하는 편이 자연스럽습니다.

    Backstage 도입 전에 먼저 정한 기준

    Backstage는 기능이 많지만, 처음부터 다 하려 들면 거의 반드시 무너집니다. 저도 처음엔 카탈로그, 문서, 템플릿, 플러그인(Plugin, 확장 기능), 권한까지 한 번에 붙이려다가 시행착오를 꽤 겪었습니다. 그래서 기준을 다시 잡았습니다.

    1. 첫 3개월 목표는 검색성과 소유권 정리

    처음부터 모든 자동화를 노리진 않았습니다. 가장 먼저 해결한 건 “우리 서비스가 몇 개인지”, “이 서비스 담당 팀이 누구인지”, “배포 파이프라인이 어디 있는지”를 한 화면에서 보이게 하는 것이었습니다.

    2. 카탈로그 품질이 화면보다 먼저다

    Backstage는 화면보다 데이터가 중요합니다. <code>catalog-info.yaml이 엉망이면 나중에 검색도, 관계도, 문서 연결도 다 지저분해집니다. 그래서 초기에 메타데이터 규칙부터 정했습니다.

    3. 운영팀이 다 입력하지 않는다

    플랫폼은 중앙집중형으로 굴리면 오래 못 갑니다. 각 팀이 자기 서비스 메타데이터를 유지하게 하고, 운영팀은 템플릿과 검증 규칙을 관리하는 식으로 분리했습니다.

    항목 초기 목표 1년 뒤 목표
    서비스 카탈로그 핵심 서비스 등록 전 팀 기본 등록
    소유권 표시 팀 단위 매핑 온콜/문서 링크까지 연결
    소프트웨어 템플릿 1~2개 표준 템플릿 언어/런타임별 확장
    TechDocs 중요 서비스만 문서 작성 습관 정착
    권한 관리 최소 권한 팀/역할 기반 세분화

    Backstage 개발자 포털 구축: 첫 배포까지

    개발자 포털 구축 자체는 생각보다 빨리 됩니다. 문제는 그 다음부터입니다. 기본 앱을 만들고, 로컬에서 띄우고, 카탈로그 엔티티를 등록해보는 것까지는 공식 흐름이 꽤 잘 되어 있습니다.

    1. 새 Backstage 앱 생성
    2. 기본 실행 확인
    3. 카탈로그 엔티티 등록
    4. 인증과 조직 구조 연결
    5. 템플릿과 문서 기능 추가

    저는 초기에 아주 단순한 구조로 시작했습니다. 로컬에서 먼저 확인하고, 이후 컨테이너(Container, 실행 격리 환경)로 배포하는 흐름이었습니다. 현재 공식 시작 가이드 기준으로는 아래처럼 앱을 만든 뒤 yarn start로 프런트엔드와 백엔드를 함께 띄우는 방식이 가장 기본입니다.

    npx @backstage/create-app@latest
    cd my-backstage-app
    yarn install
    yarn start

    처음 화면이 뜨면 “생각보다 금방 되네?” 싶습니다. 그런데 여기서 중요한 포인트가 있습니다. 로컬 실행 성공은 시작일 뿐입니다. 실제 운영에서 중요한 건 인증, 카탈로그 입력 규칙, 저장소 구조, 문서 관리 방식입니다.

    서비스 등록은 아래처럼 아주 기본적인 엔티티 파일부터 시작했습니다.

    apiVersion: backstage.io/v1alpha1
    kind: Component
    metadata:
      name: payment-api
      description: Payment service for internal platform
      tags:
        - backend
        - critical
    spec:
      type: service
      lifecycle: production
      owner: team-platform
      system: commerce-platform

    이 단계에서 중요한 건 멋진 화면이 아니라 명명 규칙(Naming Convention, 이름 규칙)입니다. 이름이 제각각이면 검색이 바로 망가집니다. 실제로 써보니까 team-, svc- 같은 접두사(prefix, 앞부분 식별자)를 과하게 쓰는 것도 오히려 헷갈리더라고요. 적당히 단순해야 오래 갑니다.

    Backstage 운영 후기의 핵심인 서비스 카탈로그와 엔티티 관계 구성 이미지

    Component, API, System, Group 엔티티가 서로 연결된 카탈로그 구조를 보여주는 이미지입니다.

    Backstage 운영 후기에서 편해진 지점: 카탈로그, 템플릿, 문서

    카탈로그(Catalog, 서비스 자산 목록)

    Backstage 운영 후기에서 빼놓기 어려운 핵심은 카탈로그입니다. 저는 처음에 이걸 “서비스 목록 화면” 정도로 생각했었는데, 1년 써보니 사실상 운영 기준점이더라고요. 서비스가 장애를 냈을 때 저장소와 문서, 대시보드, 소유 팀을 한 번에 찾을 수 있다는 게 큽니다.

    소프트웨어 템플릿(Software Templates, 표준 생성 템플릿)

    이 기능은 팀 표준을 퍼뜨릴 때 정말 강력했습니다. 신규 서비스 생성 시 README, 기본 디렉터리 구조, CI 설정, 카탈로그 파일까지 자동으로 넣어주게 만들면 편차가 많이 줄어듭니다.

    apiVersion: scaffolder.backstage.io/v1beta3
    kind: Template
    metadata:
      name: simple-service-template
      title: Simple Service Template
    spec:
      owner: team-platform
      type: service
      parameters:
        - title: Service Info
          required:
            - name
            - owner
          properties:
            name:
              type: string
              title: Service Name
            owner:
              type: string
              title: Owner Team
      steps:
        - id: fetch-base
          name: Fetch Base
          action: fetch:template
          input:
            url: ./template
            values:
              name: ${{ parameters.name }}
              owner: ${{ parameters.owner }}

    처음엔 템플릿을 많이 만들수록 좋다고 생각했는데, 오히려 반대였습니다. 템플릿 종류가 많아지면 사용자가 뭘 선택해야 하는지 모르거든요. 그래서 1년 운영 후 기준은 명확해졌습니다. “가장 많이 쓰는 패턴부터 적게 만든다.” 이게 맞았습니다.

    TechDocs(테크독스, 문서 자동 게시)

    문서도 생각보다 영향이 컸습니다. 개발자 포털 구축에서 문서가 분리되어 있으면 결국 다시 검색 지옥으로 돌아갑니다. Backstage 안에서 문서가 보이게 만들면 적어도 “어디에 있는지”를 찾는 시간은 줄어듭니다. 다만 문서 품질 자체는 도구가 해결해주지 않습니다. 이건 운영 문화의 문제더라고요.

    1년 운영하면서 겪은 문제들

    여기부터가 진짜 운영 후기입니다. 설치보다 운영이 훨씬 어렵습니다.

    1. 카탈로그 등록률이 생각보다 안 올라갑니다

    플랫폼팀이 좋다고 생각하는 것과 현업팀이 귀찮다고 느끼는 건 늘 다릅니다. 특히 초기에는 “왜 이걸 또 적어야 하죠?”라는 반응이 있었습니다. 저도 처음엔 안내 문서를 길게 써놨었는데, 효과가 크지 않더라고요.

    해결은 단순했습니다.

    • 템플릿 생성 시 catalog-info.yaml 자동 포함
    • 필수 필드 최소화
    • 리뷰 체크리스트에 소유권 항목 추가
    • 등록 안 된 서비스는 운영 지표에서 제외

    강제와 편의의 균형이 중요합니다. 너무 세게 밀면 반감이 생기고, 너무 느슨하면 아무도 안 합니다.

    2. 조직도와 실제 책임 구조가 다릅니다

    Group(그룹, 팀 조직 엔티티)를 예쁘게 모델링해도 현실은 늘 예외가 있습니다. 명목상 팀과 실제 운영 담당이 다를 때가 있거든요. 그래서 저는 조직도 그대로만 넣지 않고, 서비스 기준 소유권을 별도로 정리했습니다. 여기서 중요한 건 HR 기준이 아니라 장애 대응 기준입니다.

    3. 플러그인 욕심이 커집니다

    Backstage는 플러그인이 많고 확장성이 좋아서, 운영하다 보면 이것저것 붙이고 싶어집니다. 그런데 여기서 많이 흔들립니다. 저도 한동안 대시보드, 품질 지표, 배포 이력, API 문서, 비용 정보까지 한 화면에 다 넣어보려 했는데요. 결과적으로는 정보 밀도가 너무 높아져서 오히려 안 보게 되더라고요.

    정말 자주 보는 정보만 전면에 두고, 나머지는 링크로 넘기는 구조가 오래 갑니다.

    4. 권한 관리가 늦어지면 나중에 더 아픕니다

    초기엔 내부 도구니까 대충 열어두자 싶을 수 있습니다. 저도 솔직히 그랬습니다. 그런데 문서, 운영 대시보드, 템플릿 액션이 늘어나면 접근 제어가 갑자기 중요해집니다. 특히 프로덕션 관련 링크나 자동화 액션이 연결되기 시작하면 더 그렇습니다.

    backend:
      auth:
        keys:
          - secret: ${BACKEND_SECRET}
    permission:
      enabled: true

    다만 실제 운영에서는 설정만 넣는다고 끝나지 않습니다. 현재 공식 문서 기준으로는 app-config.yaml의 permission.enabled: true 설정과 함께, 백엔드에 permission policy를 연결하는 작업이 같이 필요합니다. 여기서는 방향만 보여드리는 예시로 봐주시면 됩니다.

    개발자 포털 구축 과정에서 인증과 템플릿 자동화 흐름을 보여주는 이미지

    SSO 로그인 이후 템플릿 실행, 저장소 생성, 카탈로그 등록으로 이어지는 운영 자동화 흐름을 보여주는 이미지입니다.

    검증: 무엇이 실제로 달라졌나

    정량 지표를 과하게 꾸미고 싶진 않습니다. 다만 1년 운영하면서 체감 변화는 분명했습니다.

    • 신규 입사자 온보딩 시 서비스 위치 파악이 빨라졌습니다.
    • 운영 중 “이 서비스 누가 보나요?”라는 질문이 줄었습니다.
    • 표준 저장소 구조가 어느 정도 정착됐습니다.
    • 문서 링크와 대시보드 링크를 찾는 시간이 줄었습니다.

    특히 장애 대응 때 차이가 컸습니다. 예전에는 메신저 검색부터 했거든요. 이제는 포털에서 서비스를 보고, 담당 팀을 보고, 관련 문서와 대시보드로 넘어가는 흐름이 꽤 자연스러워졌습니다. 이럴 때는 “아, 이거 도입하길 잘했네” 싶더라고요.

    반대로 기대보다 덜 바뀐 것도 있습니다.

    • 문서 최신화는 도구만으로 해결되지 않았습니다.
    • 카탈로그 품질은 각 팀의 습관에 크게 좌우됐습니다.
    • 포털 접속 빈도는 팀마다 차이가 컸습니다.

    그래서 CNCF Backstage를 만능 해결책으로 보면 실망할 수 있습니다. 이건 포털 소프트웨어이면서 동시에 운영 문화 도구입니다. 플랫폼 엔지니어링의 일부이지, 전부는 아니더라고요.

    Backstage 운영 후기 결과를 보여주는 대시보드와 성과 시각화 이미지

    서비스 소유권 가시성, 온보딩 속도 개선, 문서 연결 상태 등을 대시보드 형태로 요약한 이미지입니다.

    Backstage 도입 사례로 정리하는 운영 팁

    중요한 포인트만 추리면 이렇습니다.

    1. 카탈로그부터 시작하세요. 검색성과 소유권이 먼저입니다.
    2. 템플릿은 적게 만드세요. 제일 많이 쓰는 패턴 한두 개가 효과적입니다.
    3. 문서 품질 책임은 각 팀에 남겨두세요. 플랫폼팀이 대신 다 맡기는 어렵습니다.
    4. 권한 모델을 초기에 잡으세요. 뒤늦게 손보면 연결 범위가 너무 넓어집니다.
    5. 플러그인은 신중하게 늘리세요. 첫 화면은 단순해야 계속 보게 됩니다.
    상황 추천 접근 피해야 할 접근
    초기 도입 카탈로그 중심 MVP 모든 기능 동시 도입
    템플릿 설계 반복 패턴 우선 팀별 전용 템플릿 난립
    문서 운영 작성 책임 분산 플랫폼팀 단독 관리
    권한 관리 초기 정책 정의 운영 후반 일괄 정리
    플러그인 확장 핵심 정보만 노출 한 화면에 모든 정보 집약

    정리와 다음 단계

    Backstage 운영 후기를 한 줄로 줄이면 이렇습니다. “설치는 빠른데, 정착은 결국 운영 설계의 문제다.” 저도 처음엔 도구만 잘 깔면 해결될 줄 알았는데, 실제로 써보니 카탈로그 품질, 조직 책임, 템플릿 설계, 권한 정책 같은 기본기가 훨씬 중요했습니다.

    Backstage 도입 사례를 찾고 있다면 너무 크게 시작하지 않는 편이 낫습니다. 개발자 포털은 예쁜 화면이 아니라 팀의 작업 흐름을 바꾸는 도구입니다. 그래서 작게 시작해서, 자주 쓰는 흐름부터 붙이는 쪽이 훨씬 잘 되더라고요. 이전 글에서 다룬 홈랩 기반 GitOps 실험도 함께 읽어보시면 도입 배경을 더 자연스럽게 이어서 보실 수 있습니다.

    다음 글에서는 TechDocs 운영 방식이나 소프트웨어 템플릿 표준화 전략을 더 깊게 다뤄볼 예정입니다.

    도입 전후 차이, 추천 시작 순서, 운영 시 주의할 점을 한 장으로 정리한 요약 인포그래픽입니다.

    자주 묻는 질문

    Backstage는 작은 팀에도 필요할까요?

    작은 팀이라면 반드시 필요하다고 보긴 어렵습니다. 다만 서비스 수가 늘고, 팀이 분화되고, 온보딩 비용이 커지기 시작하면 가치가 확실히 보입니다.

    개발자 포털 구축에서 가장 먼저 해야 할 일은 뭔가요?

    서비스 목록과 소유권 정리입니다. 화려한 자동화보다 먼저, 누가 어떤 서비스를 책임지는지 보여야 합니다.

    플랫폼 엔지니어링과 Backstage는 같은 말인가요?

    같지는 않습니다. Backstage는 플랫폼 엔지니어링을 구현할 때 쓰는 여러 도구 중 하나에 가깝습니다. 운영 방식과 조직 합의가 더 중요합니다.

    CNCF Backstage를 도입하면 문서 문제가 해결되나요?

    문서 위치 문제는 꽤 좋아집니다. 다만 문서 최신화 문제까지 자동으로 해결되진 않습니다. 그건 팀 습관과 리뷰 문화가 같이 따라와야 합니다.

  • [k8s] Pod Security Standards 마이그레이션: Kubernetes PSP에서 안전하게 전환하기

    [k8s] Pod Security Standards 마이그레이션: Kubernetes PSP에서 안전하게 전환하기

    Pod Security Standards 마이그레이션: Kubernetes PSP에서 안전하게 전환하기

    Kubernetes 클러스터를 오래 운영했다면 PSP(PodSecurityPolicy) 때문에 한 번쯤 머리가 아팠을 겁니다. 저도 그랬습니다. 그런데 PSP는 Kubernetes 1.21에서 deprecated 되었고, Kubernetes 1.25에서 완전히 제거됐습니다. 그래서 Pod Security Standards 마이그레이션은 이제 선택이 아니라 업그레이드와 운영 안정성을 위해 꼭 챙겨야 할 작업이 됐습니다. 이번 글에서는 Kubernetes 보안 관점에서 PSP 대체 수단인 PSS(Pod Security Standards)와 Pod Security Admission으로 어떻게 안전하게 전환하는지, 실무 기준으로 차근차근 정리해보겠습니다.

    처음엔 저도 PSS가 너무 단순해 보여서 오히려 불안했습니다. PSP처럼 세부 필드를 다 적는 방식에 익숙하면 더 그렇게 느껴지거든요. 그런데 실제로 운영해보니 팀 공통 보안 기준을 맞추는 데는 훨씬 현실적이더라고요. 대신 전환 순서를 잘못 잡으면 배포가 갑자기 막힐 수 있습니다. 여기서 핵심은 처음부터 enforce로 가지 말고 warn, audit로 먼저 관찰하는 것입니다.

    Pod Security Standards 마이그레이션 개요를 보여주는 Kubernetes 보안 아키텍처 이미지

    PSP에서 PSS와 Pod Security Admission으로 넘어가는 흐름을 한눈에 보여주는 개요 이미지입니다.

    왜 Pod Security Standards 마이그레이션이 중요한가

    예전 방식인 PSP는 표현력은 강했지만 운영 피로도도 높았습니다. 반면 PSS는 Privileged, Baseline, Restricted라는 공통 보안 레벨을 제공해서 팀 간 기준을 맞추기 훨씬 쉽습니다. 여러 네임스페이스를 운영하는 환경에서는 긴 정책 객체를 계속 유지하는 것보다, 보안 수준을 레벨로 선언하는 편이 관리가 훨씬 단순합니다.

    제가 직접 겪어보니 Pod Security Standards 마이그레이션의 장점은 분명했습니다. 신규 팀원에게 설명하기 쉽고, 클러스터 업그레이드 때 정책 호환성 이슈가 크게 줄어듭니다. 다만 PSP처럼 아주 세밀한 예외 제어를 기대하면 아쉬울 수 있습니다. 그런 경우에는 OPA Gatekeeper나 Kyverno 같은 정책 엔진을 함께 쓰는 구성이 더 잘 맞습니다.

    Pod Security Standards 마이그레이션 기준: PSP와 PSS는 무엇이 다를까

    처음 보면 이름만 바뀐 것처럼 느껴질 수 있는데 구조는 꽤 다릅니다. PSP는 정책 리소스를 만들고 RBAC으로 사용자나 서비스어카운트와 연결하는 방식이었습니다. 반면 PSS는 네임스페이스에 보안 수준을 라벨로 선언하고, Pod Security Admission이 그 기준으로 파드를 검사합니다.

    항목 PSP PSS + Pod Security Admission
    정책 방식 세부 필드 기반 정책 객체 사전 정의된 보안 표준 레벨
    적용 범위 RBAC과 연결해 사용 네임스페이스 라벨 기반 적용
    운영 난이도 높은 편 상대적으로 단순
    이식성 클러스터별 편차 큼 표준화하기 좋음
    세밀한 예외 처리 강점 제한적, 필요 시 별도 정책 엔진 보완

    PSS의 핵심 레벨은 아래처럼 이해하면 편합니다.

    • Privileged: 사실상 제한이 거의 없는 수준입니다. 시스템 워크로드나 인프라성 컴포넌트에 가깝습니다.
    • Baseline: 일반 애플리케이션에 적용하기 좋은 현실적인 최소 보호선입니다.
    • Restricted: 현재 권장되는 강한 하드닝 기준입니다. 대신 기존 애플리케이션은 여기서 많이 걸릴 수 있습니다.

    Pod Security Admission 적용 방법: 전환 전에 꼭 확인할 것

    Pod Security Admission은 파드 생성 시점에 PSS 기준을 검사하는 내장 Admission Controller입니다. 다만 여기서 하나 짚고 갈 점이 있습니다. warn과 audit는 Deployment 같은 워크로드 리소스에도 경고를 보여주지만, enforce는 최종적으로 생성되는 Pod에 적용됩니다. 이 차이를 알고 있어야 배포 시 메시지를 덜 헷갈립니다.

    1. enforce: 정책을 어기면 Pod 생성이 거부됩니다.
    2. audit: 생성은 허용되지만 감사 로그에 위반 정보가 남습니다.
    3. warn: 생성은 허용되지만 클라이언트에 경고가 표시됩니다.

    실무에서 자주 하는 실수가 바로 여기입니다. 바로 enforce부터 켜버리면 그동안 관성적으로 돌아가던 워크로드가 한꺼번에 실패할 수 있거든요. 저도 테스트 환경에서 ingress controller가 안 떠서 꽤 오래 본 적이 있습니다. 알고 보니 hostNetwork와 securityContext 설정이 기준에 안 맞았더라고요. 이런 경험을 한 번 하면 warn, audit부터 가야 하는 이유가 확실히 보입니다.

    전환 전 체크리스트

    • 현재 클러스터에서 PSP를 실제로 쓰는 네임스페이스가 어디인지 확인합니다.
    • 권한 상승 허용 여부를 점검합니다.
    • root 실행, hostPath 마운트, hostNetwork 사용 여부를 확인합니다.
    • DaemonSet, CSI, 모니터링 에이전트처럼 예외가 필요한 워크로드를 분리합니다.
    • 애플리케이션 네임스페이스와 시스템 네임스페이스를 같은 기준으로 묶지 않습니다.

    실전 구현: Pod Security Standards 마이그레이션 순서

    여기서는 제가 실제로 권장하는 순서를 정리해보겠습니다. 핵심은 단순합니다. 관찰 먼저, 강제는 나중. 이 순서만 지켜도 사고 확률이 확실히 줄어듭니다.

    1. 현재 네임스페이스 라벨 상태 확인

    kubectl get ns --show-labels

    기존에 이미 pod-security 관련 라벨이 붙어 있는지 먼저 확인합니다. 다른 운영자가 먼저 설정해둔 경우도 생각보다 자주 있습니다. 이 상태를 모르고 덮어쓰면 원인 파악이 더 어려워집니다.

    2. 먼저 warn, audit 모드로 Baseline 적용

    kubectl label ns my-app \
      pod-security.kubernetes.io/warn=baseline \
      pod-security.kubernetes.io/audit=baseline \
      --overwrite

    처음부터 Restricted로 가고 싶어도 일단 Baseline부터 보는 편이 안전합니다. 여기서 어떤 워크로드가 경고를 내는지 파악해야 다음 단계가 보입니다. 이 단계가 생각보다 중요하더라고요.

    3. 버전 라벨도 함께 명시해 드리프트 줄이기

    kubectl label ns my-app \
      pod-security.kubernetes.io/warn-version=v1.36 \
      pod-security.kubernetes.io/audit-version=v1.36 \
      --overwrite

    버전 라벨은 선택 사항이지만 운영에선 거의 필수에 가깝습니다. PSS 해석 기준은 Kubernetes 마이너 버전에 따라 달라질 수 있어서, 테스트와 운영 클러스터의 결과가 어긋나는 일을 줄여주거든요. 다만 예시 값을 그대로 복붙하기보다는 현재 클러스터의 마이너 버전에 맞춰 적는 게 맞습니다.

    4. 경고 메시지로 문제 워크로드 찾기

    kubectl apply -f deployment.yaml

    배포 시 warn 메시지가 뜨면 그 내용을 기준으로 수정합니다. warn과 audit는 Deployment 같은 워크로드 리소스에도 적용되기 때문에, 이 단계에서 꽤 많은 힌트를 얻을 수 있습니다. 흔히 손보게 되는 포인트는 아래와 같습니다.

    • runAsNonRoot: non-root 실행 강제
    • allowPrivilegeEscalation: false 설정
    • seccompProfile: RuntimeDefault 적용
    • capabilities: 불필요한 Linux capability 제거
    Pod Security Admission과 네임스페이스 라벨 기반 PSS 적용 방법을 설명하는 이미지

    네임스페이스에 warn, audit, enforce 라벨을 붙이는 방식과 적용 흐름을 보여주는 이미지입니다.

    5. 매니페스트 보안 컨텍스트 정리

    아래 예시는 Restricted로 가기 전에 가장 자주 쓰는 기본 골격입니다.

    apiVersion: apps/v1
    kind: Deployment
    metadata:
      name: web-app
      namespace: my-app
    spec:
      replicas: 2
      selector:
        matchLabels:
          app: web-app
      template:
        metadata:
          labels:
            app: web-app
        spec:
          securityContext:
            runAsNonRoot: true
            seccompProfile:
              type: RuntimeDefault
          containers:
            - name: web-app
              image: nginx:stable
              securityContext:
                allowPrivilegeEscalation: false
                capabilities:
                  drop:
                    - ALL
              ports:
                - containerPort: 80

    이 예시는 아주 화려하지는 않지만, 실제로 많이 걸리는 조건을 담고 있습니다. 특히 runAsNonRoot는 이미지 자체가 non-root 실행을 지원해야 제대로 동작합니다. 오래된 이미지라면 매니페스트만 고쳐서는 해결이 안 되고, 컨테이너 이미지 베이스부터 손봐야 할 때가 있습니다.

    6. 문제 없으면 enforce 전환

    kubectl label ns my-app \
      pod-security.kubernetes.io/enforce=baseline \
      pod-security.kubernetes.io/enforce-version=v1.36 \
      --overwrite

    여기서도 한 번에 모든 네임스페이스를 바꾸는 건 추천하지 않습니다. 서비스 영향이 낮은 곳부터 묶어서 적용하고, 모니터링으로 이상이 없는지 확인한 뒤 확대하는 방식이 제일 덜 아픕니다. 이거 진짜 차이가 큽니다.

    Restricted까지 가려면 어떤 점을 더 봐야 하나

    Pod Security Standards 마이그레이션을 시작하면 많은 팀이 최종 목표를 Restricted로 잡습니다. 방향은 맞습니다. 다만 현실적으로 모든 워크로드가 바로 들어가지는 않더라고요. 특히 아래 항목은 자주 발목을 잡습니다.

    1. 레거시 이미지가 root 실행을 전제로 만들어져 있음
    2. initContainer에서 과한 권한을 사용함
    3. 모니터링, 스토리지, 네트워크 에이전트가 호스트 접근을 필요로 함
    4. hostPath 사용이 습관처럼 들어가 있음

    그래서 저는 보통 네임스페이스를 이렇게 나눕니다.

    네임스페이스 유형 권장 레벨 비고
    일반 애플리케이션 Baseline 또는 Restricted 신규 서비스는 Restricted 목표
    운영 도구 Baseline 필요 권한 확인 후 점진 강화
    시스템 워크로드 Privileged 또는 별도 예외 관리 CNI, CSI, 일부 에이전트 주의

    트러블슈팅: Pod Security Standards 마이그레이션에서 자주 막히는 지점

    문서만 보면 금방 끝날 것 같지만, 운영 환경에 붙이면 여기서 한 번씩 다 막힙니다. 저도 몇 번은 꽤 돌아갔습니다. 그래서 아래 포인트는 미리 보고 들어가는 편이 좋습니다.

    1. 배포가 갑자기 거부될 때

    enforce가 켜진 네임스페이스에 정책 위반 Pod가 들어가면 바로 거부됩니다. 이 경우 이벤트와 매니페스트를 같이 봐야 합니다.

    kubectl describe pod <pod-name> -n my-app
    kubectl get events -n my-app --sort-by=.lastTimestamp

    경고 문구만 보고 대충 고치면 같은 문제를 반복하기 쉽습니다. 어떤 필드가 위반인지 정확히 확인하고, Pod 템플릿 기준으로 수정해야 합니다.

    2. 시스템 네임스페이스까지 묶어서 막힐 때

    이 부분은 정말 조심해야 합니다. kube-system 같은 곳에 일반 앱과 같은 Restricted를 걸면 예상치 못한 장애가 날 수 있습니다. 네트워크 플러그인, 스토리지 드라이버, 노드 에이전트가 영향을 받는 경우가 대표적입니다. 실제 운영에선 시스템성 워크로드를 별도 기준으로 다루는 게 훨씬 안전합니다.

    3. warn은 뜨는데 배포가 되니까 그냥 넘길 때

    처음엔 “배포는 되네” 하고 넘기기 쉽습니다. 그런데 나중에 enforce로 바꾸는 순간 한꺼번에 터집니다. 그래서 warn 단계에서 바로 티켓을 만들고, 팀별 수정 일정을 잡아두는 편이 훨씬 낫습니다.

    4. PSP 기능과 1:1 대응을 기대할 때

    PSP 대체라고 해서 완전히 같은 경험을 기대하면 실망할 수 있습니다. PSS는 표준 보안 레벨을 제공하는 방식이고, 세밀한 조직별 규칙은 별도 정책 도구가 더 적합합니다. 이 차이를 이해하고 설계해야 전환 이후 불만도 줄어듭니다.

    검증: Pod Security Admission이 제대로 동작하는지 확인하는 방법

    적용 후에는 라벨만 붙였다고 끝이 아닙니다. 검증이 꼭 필요합니다. 제가 보통 확인하는 항목은 아래와 같습니다.

    1. 네임스페이스 라벨이 의도한 값으로 들어갔는지 확인
    2. warn 메시지가 줄거나 사라졌는지 확인
    3. 문제 Pod 없이 롤링 업데이트가 완료되는지 확인
    4. 시스템 워크로드가 영향 없이 유지되는지 확인
    kubectl get ns my-app --show-labels
    kubectl rollout status deploy/web-app -n my-app
    kubectl get pods -n my-app

    조금 더 확실히 보려면 의도적으로 정책 위반 Pod를 하나 던져보는 것도 방법입니다. 다만 이 테스트는 운영 네임스페이스가 아니라 테스트 네임스페이스에서만 하는 편이 안전합니다.

    apiVersion: v1
    kind: Pod
    metadata:
      name: pss-test
      namespace: my-app
    spec:
      hostNetwork: true
      containers:
        - name: busybox
          image: busybox:1.36
          command: ["sh", "-c", "sleep 3600"]
          securityContext:
            allowPrivilegeEscalation: true

    Baseline이나 Restricted enforce가 걸려 있다면 이런 Pod는 거부되는 게 정상입니다. hostNetwork는 Baseline부터 금지되고, allowPrivilegeEscalation은 Restricted에서 금지되기 때문에 테스트용으로 확인하기 좋습니다.

    Pod Security Standards 마이그레이션 검증 결과를 보여주는 Kubernetes 보안 이미지

    정책 위반 테스트와 정상 배포가 어떻게 다르게 보이는지 검증 결과를 시각화한 이미지입니다.

    운영 팁: 네임스페이스 전략을 먼저 정하면 훨씬 편합니다

    제가 여러 번 해보니, PSS 적용 방법에서 제일 중요한 건 YAML 문법보다 운영 기준이었습니다. 다시 말해 “어떤 네임스페이스를 어떤 신뢰 수준으로 볼 것인가”를 먼저 정해야 합니다. 이 기준이 없으면 팀마다 예외 요청이 쌓이고, 결국 정책이 누더기가 되더라고요.

    • 신규 서비스는 기본적으로 Baseline 또는 Restricted로 시작합니다.
    • 예외가 필요한 시스템성 워크로드는 별도 네임스페이스로 분리합니다.
    • warn과 audit 결과를 주기적으로 점검해 enforce 후보를 올립니다.
    • 정책 예외는 문서화하고 만료 시점을 정합니다.

    이런 방식으로 가면 Kubernetes 보안이 특정 담당자 감에 의존하지 않고 팀 표준으로 자리 잡습니다. 이전에 정리한 RBAC 글이 있다면 같이 연결해두는 것도 좋습니다. 내부 링크 흐름이 생기면 독자 입장에서도 이해가 더 빨라지고, SEO 측면에서도 분명 도움이 됩니다.

    정리: PSP에서 PSS로 갈아탈 때 기억할 것

    오늘 내용을 짧게 정리하면 이렇습니다. Pod Security Standards 마이그레이션은 단순한 기능 교체가 아니라, 보안 운영 방식을 표준화하는 작업입니다. PSP보다 세밀한 제어는 줄었지만, 네임스페이스 단위로 기준을 맞추기 쉬워졌고, Pod Security Admission으로 warn, audit, enforce를 단계적으로 운영할 수 있게 됐습니다.

    저도 처음엔 이 구조가 너무 단순한 것 아닌가 싶었습니다. 그런데 실제로는 팀 단위 운영에 더 잘 맞더라고요. 다만 무작정 enforce부터 걸면 안 됩니다. 꼭 warn, audit으로 현재 상태를 먼저 파악하고, Restricted는 목표로 두되 시스템 워크로드 예외를 현실적으로 분리해 가져가면 훨씬 수월합니다.

    지금 클러스터 업그레이드를 앞두고 있다면 이번 주에라도 테스트 네임스페이스 하나 만들어 Baseline warn부터 붙여보세요. 생각보다 빨리 문제가 보입니다. 그리고 이전에 다뤘던 RBAC 정리 글도 함께 보면 흐름이 더 잘 잡힐 겁니다. 다음 글에서는 Kyverno나 Gatekeeper로 PSS의 빈틈을 어떻게 보완하는지도 이어서 정리해보겠습니다.

    PSP 대체와 Pod Security Standards 보안 레벨 비교를 정리한 인포그래픽 이미지

    PSP와 PSS의 차이, 적용 순서, 운영 체크포인트를 한 장으로 정리한 요약 인포그래픽입니다.

    자주 묻는 질문

    PSS만으로 PSP를 완전히 대체할 수 있나요?

    기본적인 표준화 목적에는 충분한 경우가 많습니다. 다만 세밀한 조건 제어나 예외 검증이 많다면 Kyverno나 Gatekeeper 같은 정책 엔진을 함께 검토하는 편이 좋습니다.

    처음부터 Restricted로 가도 되나요?

    신규 서비스만 있고 이미지와 매니페스트를 처음부터 통제할 수 있는 환경이라면 가능합니다. 하지만 기존 워크로드가 있다면 Baseline warn, audit부터 시작하는 편이 훨씬 안전합니다.

    Pod Security Admission은 어디에 적용되나요?

    네임스페이스 라벨 기준으로 해당 네임스페이스의 Pod 생성에 영향을 줍니다. 또 warn과 audit는 Deployment 같은 워크로드 리소스에도 힌트를 주기 때문에, 네임스페이스 전략과 배포 검증 루틴을 함께 가져가는 게 중요합니다.

  • [k8s] Karpenter 오토스케일로 EKS 비용 절감 검증하는 방법

    [k8s] Karpenter 오토스케일로 EKS 비용 절감 검증하는 방법

    Karpenter 오토스케일로 EKS 비용 절감 검증하는 방법

    Karpenter 오토스케일을 검토하시는 분들은 비슷한 지점에서 막히곤 합니다. HPA로 파드 수는 잘 늘고 줄어드는데, 월말 청구서를 보면 노드가 생각보다 오래 남아 있어서 클라우드 비용 절감 체감이 약하거든요. 특히 AWS EKS를 운영하다 보면 파드 오토스케일링과 노드 비용 최적화가 완전히 같은 문제가 아니라는 점을 금방 느끼게 됩니다.

    저도 초반에는 “HPA만 잘 잡으면 끝나는 거 아닌가?” 싶었는데, 실제로는 노드 단위 빈 공간이 더 큰 병목이었습니다. 이럴 때 Karpenter 오토스케일은 파드 요구사항에 맞춰 노드를 더 유연하게 만들고, 유휴 노드를 정리하는 데 강점이 있습니다. 이번 글에서는 Kubernetes 오토스케일링 관점에서 Karpenter가 왜 비용 최적화에 유리한지, 그리고 AWS EKS 비용 최적화 효과를 어떤 지표로 검증해야 하는지 정리해보겠습니다.

    Karpenter 오토스케일 기반 EKS 아키텍처 개요 이미지

    Karpenter가 스케줄링되지 못한 파드를 감지하고 적절한 EC2 노드를 만들고, 유휴 노드를 정리하는 흐름을 보여주는 이미지입니다.

    Karpenter 오토스케일이 비용 절감에 유리한 이유

    쉽게 말해 Karpenter는 파드가 실제로 필요로 하는 리소스에 맞춰 노드를 바로 만들어주는 도구입니다. 기존 Cluster Autoscaler도 노드 수를 늘리고 줄일 수 있지만, 보통 미리 정의한 노드 그룹을 중심으로 확장하다 보니 리소스가 남는 구간이 생기기 쉽습니다.

    반면 Karpenter는 Pending 상태의 파드를 보고 CPU, 메모리, 아키텍처, 용량 유형(온디맨드/스팟) 같은 조건에 맞는 인스턴스를 더 유연하게 고릅니다. 운영에서 보면 이 차이가 꽤 큽니다. 특정 인스턴스 몇 개만 반복해서 늘리는 대신, 워크로드 특성에 맞춰 c 계열, m 계열, r 계열을 섞고 스팟까지 활용하기 쉬워지거든요.

    항목 Cluster Autoscaler Karpenter
    확장 기준 기존 노드 그룹 중심 파드 요구사항 중심
    인스턴스 선택 유연성 상대적으로 제한적 높음
    빈 리소스 최소화 노드 그룹 크기에 영향 받음 상황별 최적화에 유리
    스팟 활용 구성 복잡도 있음 정책 설계가 비교적 직관적
    비용 절감 포인트 노드 수 축소 노드 크기와 종류까지 최적화

    핵심은 단순히 노드를 빨리 만드는 데 있지 않습니다. Karpenter 오토스케일의 진짜 장점은 불필요하게 큰 노드를 오래 붙잡고 있지 않게 만드는 구조에 있습니다. 결국 비용은 시간당 단가와 실행 시간의 곱이라서, 둘을 같이 줄여야 체감이 나더라고요.

    Karpenter 오토스케일 도입 전에 먼저 봐야 할 비용 지표

    Karpenter를 붙였는데도 “그래서 얼마가 절감됐지?”를 설명하지 못하면 운영팀 설득이 어렵습니다. 감으로 보면 꼭 해석이 꼬입니다. 최소한 아래 지표는 같은 기간 기준으로 같이 보는 편이 좋습니다.

    • 노드 평균 사용률: CPU와 메모리 요청량 대비 실제 사용률
    • 유휴 시간: 야간 또는 비업무 시간에 노드가 얼마나 오래 남는지
    • 파드 Pending 빈도: 확장 지연이 발생하는지
    • 인스턴스 타입 다양성: 특정 타입에 과도하게 묶여 있는지
    • 스팟 비중: 안정성과 비용 균형이 맞는지

    메트릭은 CloudWatch, Prometheus, Grafana 조합으로 많이 보고, 비용은 AWS Cost Explorer 또는 CUR로 맞춰보면 됩니다. 포인트는 하나입니다. 메트릭과 비용 데이터를 반드시 같은 기간으로 맞춰서 비교해야 한다는 점입니다.

    Kubernetes 오토스케일링 구성, 이렇게 잡으면 시작이 편합니다

    실전 구현 자체는 아주 복잡하지 않습니다. 다만 EKS 클러스터가 이미 있어야 하고, IRSA 또는 EKS Pod Identity 같은 AWS 권한 구성이 가능해야 합니다. 그리고 Karpenter가 파드 요청값을 기준으로 판단하므로 워크로드의 resources.requests가 비어 있으면 기대한 최적화가 잘 나오지 않습니다. 이 부분은 정말 중요합니다.

    1. EKS 클러스터 이름과 리전을 환경 변수로 설정합니다.
    2. Karpenter 컨트롤러용 IAM 역할과 중단 이벤트용 큐를 준비합니다.
    3. Helm으로 Karpenter를 설치합니다.
    4. NodePool과 EC2NodeClass를 정의합니다.
    5. 워크로드의 requests/limits를 다시 점검합니다.
    export CLUSTER_NAME=my-eks
    export AWS_DEFAULT_REGION=ap-northeast-2
    export KARPENTER_NAMESPACE=karpenter
    export KARPENTER_VERSION=1.14.0
    export KARPENTER_IAM_ROLE_ARN=arn:aws:iam::123456789012:role/KarpenterControllerRole
    
    aws eks update-kubeconfig --name $CLUSTER_NAME --region $AWS_DEFAULT_REGION
    kubectl config current-context
    kubectl get nodes
    

    클러스터 연결부터 먼저 확인해두세요. 여기서 컨텍스트가 꼬이면 다른 클러스터에 배포하는 사고가 의외로 자주 납니다. 한 번만 겪어도 이 단계가 왜 중요한지 바로 느껴지더라고요.

    Karpenter 설치 예시

    helm upgrade --install karpenter oci://public.ecr.aws/karpenter/karpenter \
      --version $KARPENTER_VERSION \
      --namespace $KARPENTER_NAMESPACE \
      --create-namespace \
      --set settings.clusterName=$CLUSTER_NAME \
      --set settings.interruptionQueue=$CLUSTER_NAME \
      --set serviceAccount.annotations.eks\.amazonaws\.com/role-arn=$KARPENTER_IAM_ROLE_ARN \
      --wait
    
    kubectl -n $KARPENTER_NAMESPACE get pods
    

    설치가 끝나면 컨트롤러 파드가 정상 기동하는지 먼저 확인합니다. 여기서 CrashLoopBackOff가 보이면 IAM 권한, 서브넷 태그, 보안 그룹 태그, 이벤트 큐 설정부터 보는 게 빠릅니다.

    Karpenter 오토스케일 설정과 NodePool 연결 구조 이미지

    Karpenter 컨트롤러, NodePool, EC2NodeClass, 워크로드 파드가 어떻게 연결되는지 보여주는 구성 다이어그램입니다.

    NodePool과 EC2NodeClass 예시

    apiVersion: karpenter.sh/v1
    kind: NodePool
    metadata:
      name: default
    spec:
      template:
        spec:
          nodeClassRef:
            group: karpenter.k8s.aws
            kind: EC2NodeClass
            name: default
          requirements:
            - key: kubernetes.io/arch
              operator: In
              values: ["amd64"]
            - key: kubernetes.io/os
              operator: In
              values: ["linux"]
            - key: karpenter.sh/capacity-type
              operator: In
              values: ["spot", "on-demand"]
            - key: node.kubernetes.io/instance-type
              operator: In
              values: ["c6i.large", "m6i.large", "r6i.large"]
          expireAfter: 720h
      disruption:
        consolidationPolicy: WhenEmptyOrUnderutilized
        consolidateAfter: 5m
      limits:
        cpu: "200"
    ---
    apiVersion: karpenter.k8s.aws/v1
    kind: EC2NodeClass
    metadata:
      name: default
    spec:
      role: KarpenterNodeRole-my-eks
      amiSelectorTerms:
        - alias: al2023@latest
      subnetSelectorTerms:
        - tags:
            karpenter.sh/discovery: my-eks
      securityGroupSelectorTerms:
        - tags:
            karpenter.sh/discovery: my-eks
    

    여기서 핵심은 세 가지입니다. 첫째, NodePool에는 nodeClassRef가 필요합니다. 둘째, 최근 EKS 기준으로는 AL2보다 AL2023 계열 AMI 선택이 더 자연스럽습니다. 셋째, consolidation을 켜야 유휴 노드 정리가 제대로 일어납니다. 실제 비용 절감은 스케일 아웃보다 이 정리 단계에서 더 크게 체감되는 경우가 많습니다.

    테스트용 워크로드 배포

    apiVersion: apps/v1
    kind: Deployment
    metadata:
      name: inflate
    spec:
      replicas: 0
      selector:
        matchLabels:
          app: inflate
      template:
        metadata:
          labels:
            app: inflate
        spec:
          terminationGracePeriodSeconds: 0
          containers:
            - name: inflate
              image: public.ecr.aws/eks-distro/kubernetes/pause:3.7
              resources:
                requests:
                  cpu: 1
    
    kubectl apply -f inflate.yaml
    kubectl scale deployment inflate --replicas 5
    kubectl get pods -w
    

    이렇게 테스트하면 Pending 상태였던 파드 수요를 Karpenter가 보고 새 노드를 띄우는 흐름을 확인할 수 있습니다. 이후 replicas를 다시 줄이거나 배포를 삭제해보면 노드 통합 정리까지 관찰할 수 있습니다. 작은 워크로드부터 붙여보는 방식이 운영 리스크도 낮고 결과도 해석하기 편합니다.

    AWS EKS 비용 최적화를 위한 운영 포인트

    이 섹션은 꼭 짚고 넘어가야 합니다. Karpenter를 도입했다고 자동으로 클라우드 비용 절감이 되진 않습니다. 정책을 어떻게 잡느냐에 따라 결과가 꽤 다르게 나옵니다.

    • requests를 현실적으로 작성: 과장된 요청값은 큰 노드를 계속 부릅니다.
    • 스팟과 온디맨드 혼합: 서비스 성격에 따라 풀을 나누는 편이 안정적입니다.
    • 워크로드 분리: API 서버 계열과 배치 계열을 같은 NodePool에 몰지 않는 게 좋습니다.
    • 스케일 인 여유 시간 확인: 너무 공격적으로 줄이면 재기동이 잦아질 수 있습니다.
    • PodDisruptionBudget 검토: 축소가 막혀 노드가 안 내려가는 경우가 생각보다 많습니다.

    처음부터 모든 워크로드를 하나의 NodePool에 넣으면 정책 충돌이 생기기 쉽습니다. 최소한 안정성 우선 풀과 비용 우선 풀 정도는 분리해두면 운영이 훨씬 편합니다. 이거 진짜 편하더라고요.

    ⚠️ 실제로 많이 겪는 문제와 해결법

    문서만 보면 금방 될 것 같아도 운영 환경에서는 자잘한 함정이 많습니다. 특히 권한, 태그, 요청값 같은 기본 설정에서 많이 막힙니다.

    1. 노드가 아예 생성되지 않는 경우

    대부분은 IAM 또는 태그 문제입니다. 서브넷과 보안 그룹에 karpenter.sh/discovery 태그가 빠져 있으면 인프라 리소스를 찾지 못합니다.

    kubectl describe nodepool default
    kubectl -n karpenter logs deploy/karpenter
    

    로그에서 subnet, security group, instance profile, access denied 관련 메시지를 먼저 확인하면 원인이 빨리 보입니다.

    2. 노드는 생기는데 비용이 생각보다 안 줄어드는 경우

    이 경우도 자주 나옵니다. 원인은 대개 세 가지입니다.

    • 파드 requests가 과하게 큼
    • PodDisruptionBudget 때문에 빈 노드를 못 비움
    • 스팟 허용 범위가 너무 좁음

    Karpenter가 덜 똑똑해서가 아니라 입력값이 비효율적인 경우가 많습니다. 결국 스케줄러와 오토스케일러도 선언된 요청값을 기준으로 움직이니까요.

    3. 스팟 중단 대응이 불안한 경우

    스팟은 비용 면에서 매력적이지만 서비스 성격에 따라 조심해야 합니다. 중단 알림을 받았을 때 드레이닝과 재스케줄링이 자연스럽게 되도록 준비가 필요합니다. 중요한 서비스는 무조건 스팟으로 몰기보다 온디맨드와 섞는 편이 안전합니다.

    Karpenter 오토스케일 비용 절감 결과 대시보드 이미지

    파드 수 증가에 따라 노드가 늘고, 유휴 시간대에 통합 정리로 노드 수가 줄어드는 메트릭 대시보드 예시입니다.

    Karpenter 오토스케일 비용 절감 효과, 어떻게 검증해야 할까

    여기서 중요한 건 “노드 수가 줄었다”로 끝내지 않는 겁니다. 노드 수는 줄었는데 더 비싼 타입을 오래 썼다면 의미가 없거든요. 저는 아래 순서로 비교하면 결과 해석이 가장 깔끔했습니다.

    1. 도입 전 2주와 도입 후 2주를 비교합니다.
    2. 같은 요일, 같은 시간대 기준으로 워크로드 패턴을 맞춥니다.
    3. EC2 비용, 노드 평균 개수, 평균 vCPU 사용률을 함께 봅니다.
    4. 배포 실패, Pending 시간, 재스케줄링 지연이 늘지 않았는지 확인합니다.
    5. 스팟 비중 증가가 장애로 이어지지 않았는지 점검합니다.

    특히 비용 / 요청 처리량 같은 지표가 설명력이 좋습니다. 같은 트래픽을 처리하는데 필요한 EC2 비용이 줄었는지 보면, 단순 총액보다 훨씬 설득력이 있습니다. 팀 내 공유 문서에도 이 지표를 같이 적어두면 의사결정이 빨라집니다.

    kubectl top nodes
    kubectl top pods -A
    aws ce get-cost-and-usage \
      --time-period Start=2026-07-01,End=2026-07-15 \
      --granularity DAILY \
      --metrics UnblendedCost \
      --group-by Type=DIMENSION,Key=SERVICE
    

    위 명령은 비용 추세를 보는 기본 예시입니다. 참고로 Cost Explorer의 종료일은 보통 마지막 날짜 다음 날로 잡아야 비교가 깔끔합니다. 운영에서는 CUR를 Athena로 조회하거나 Grafana에 연결해 시계열로 보는 쪽이 더 편합니다.

    해석할 때 주의할 점

    • 도입 직후 며칠은 캐시, 배포 주기, 스팟 수급 영향으로 흔들릴 수 있습니다.
    • 트래픽 자체가 줄어서 비용이 감소한 것을 Karpenter 효과로 착각하면 안 됩니다.
    • RI나 Savings Plans 영향도 함께 봐야 합니다.

    결국 비교 기준은 같은 부하 조건이어야 합니다. 이 기준만 지켜도 해석 오류가 꽤 줄어듭니다.

    정리: 이런 환경이라면 Karpenter 도입 가치가 큽니다

    다음 조건에 해당하면 Karpenter 도입 효과가 비교적 잘 나옵니다.

    • 트래픽 변동폭이 크다
    • 워크로드 종류가 다양하다
    • EKS에서 인스턴스 타입 선택 폭을 넓게 가져갈 수 있다
    • 스팟 활용 여지가 있다
    • 현재 노드 유휴 시간이 길다

    반대로 워크로드가 매우 단순하고 부하가 거의 고정되어 있다면 체감 효과가 크지 않을 수도 있습니다. 그래서 무조건 도입부터 하기보다, 현재 비효율이 어디서 생기는지 먼저 확인하는 편이 맞습니다. 그다음 작은 서비스에 시범 적용하고 결과를 비교하면 훨씬 덜 위험합니다.

    Karpenter 오토스케일과 기존 오토스케일링 비교 인포그래픽

    노드 그룹 기반 확장과 파드 요구사항 기반 확장의 차이를 한눈에 요약한 비교 이미지입니다.

    마무리

    이번 글의 핵심은 간단합니다. Karpenter 오토스케일은 단순한 증설 도구라기보다, AWS EKS 비용 최적화를 운영 레벨에서 밀어주는 도구에 가깝습니다. 다만 효과는 설치 여부보다 requests 품질, NodePool 정책, 스팟 전략, 검증 방식에 더 크게 좌우됩니다.

    지금 EKS 비용이 애매하게 새고 있다고 느끼신다면, 전면 도입부터 하기보다 작은 워크로드에 먼저 붙여보세요. 그리고 이전 글의 HPA/VPA 튜닝 가이드도 함께 보시면 파드 스케일링과 노드 스케일링을 한 흐름으로 이해하는 데 도움이 됩니다. 다음 글에서는 스팟과 온디맨드 혼합 정책을 어떻게 설계해야 장애 없이 비용을 줄일 수 있는지 이어서 다뤄보겠습니다.

    자주 묻는 질문

    Karpenter만 도입하면 비용이 바로 줄어드나요?

    아닙니다. requests 값, NodePool 정책, 스팟 허용 범위가 같이 맞아야 의미 있는 절감이 나옵니다.

    Cluster Autoscaler를 꼭 버려야 하나요?

    꼭 그렇진 않습니다. 다만 인스턴스 선택 유연성과 운영 단순성이 중요하다면 Karpenter가 더 잘 맞는 환경이 많습니다.

    비용 절감 효과는 무엇으로 확인하나요?

    총 EC2 비용만 보지 말고, 같은 트래픽 대비 비용, 노드 평균 사용률, 유휴 시간, Pending 감소 여부를 함께 보시면 됩니다.

  • [k8s] MicroK8s 벤치마크: K3s와 엣지 환경 성능 비교 [실전 가이드]

    [k8s] MicroK8s 벤치마크: K3s와 엣지 환경 성능 비교 [실전 가이드]

    [인프라] MicroK8s 벤치마크: K3s와 엣지 환경 리소스 사용량 비교

    홈랩을 굴리다 보면 결국 한 번은 부딪히는 주제가 있습니다. 바로 MicroK8s 벤치마크를 어떻게 봐야 하느냐는 점이거든요. 저도 ARM 서버를 만지면서 처음엔 “둘 다 경량 쿠버네티스인데 뭐가 그렇게 다르지?” 싶었는데, 실제로 써보니까 꽤 차이가 있더라고요. 특히 엣지 컴퓨팅이나 소형 ARM 보드, 미니 PC처럼 자원이 넉넉하지 않은 환경에서는 이런 차이가 더 크게 체감됩니다.

    이번 글에서는 MicroK8s와 K3s 성능 비교를 단순한 스펙 나열이 아니라, 실제로 어떤 항목을 벤치마크해야 의미가 있는지 중심으로 정리해보겠습니다. 제가 직접 홈랩에서 테스트할 때 썼던 방식, 삽질했던 포인트, 그리고 해석할 때 조심해야 할 점까지 같이 적어볼게요.

    MicroK8s 벤치마크를 위한 엣지 환경 ARM 서버 아키텍처 다이어그램

    엣지 환경에서 경량 쿠버네티스를 비교하는 전체 구성을 한눈에 보여주는 이미지입니다.

    1. 왜 엣지 환경에서는 MicroK8s 벤치마크가 중요할까

    데이터센터에서는 CPU랑 메모리를 조금 더 쓰더라도 관리 편의성이 좋으면 넘어가는 경우가 많습니다. 근데 엣지에서는 얘기가 달라집니다. 1~2GB 메모리 차이, 디스크 I/O 초기화 시간, 컨트롤 플레인(control plane, 클러스터 제어 영역) 기동 속도 같은 게 꽤 민감하게 다가오거든요.

    쉽게 말해, 같은 Kubernetes(쿠버네티스, 컨테이너 오케스트레이션 플랫폼) 계열이라도 무엇을 기본으로 포함하느냐, 어떤 런타임을 묶어 배포하느냐, 초기 설치와 운영 자동화가 어디까지 되어 있느냐에 따라 체감 리소스 사용량이 달라집니다. 그래서 MicroK8s 벤치마크를 볼 때는 숫자 하나보다도 “어떤 조건에서 잰 숫자인가?”를 먼저 보셔야 합니다.

    2. MicroK8s와 K3s, 쉽게 말해 뭐가 다를까

    저도 처음엔 둘 다 그냥 작은 경량 쿠버네티스 배포판 정도로 이해했었는데요, 실제로는 운영 철학이 꽤 다릅니다.

    항목 MicroK8s K3s
    배포 성격 Canonical에서 제공하는 경량 쿠버네티스 배포판 Rancher 진영에서 시작된 경량 쿠버네티스 배포판
    설치 방식 snap 기반 설치가 대표적 단일 바이너리 중심 설치 경험이 간단한 편
    체감 특징 애드온(add-on, 부가 기능) 관리가 편함 가볍고 빠르게 올리기 좋음
    엣지 적합성 기능 일체감이 좋음 최소 자원 환경에서 선호되는 경우가 많음

    여기서 중요한 포인트! 경량 쿠버네티스라고 해서 무조건 가장 적은 메모리만 쓰는 제품을 고르면 끝은 아닙니다. 예를 들어 Ingress(인그레스, 외부 트래픽 진입점), DNS, StorageClass(스토리지 클래스, 동적 볼륨 정책) 같은 기본 기능을 나중에 붙이기 시작하면, 처음의 “가벼움”이 생각보다 금방 상쇄되거든요.

    3. MicroK8s 벤치마크는 무엇을 비교해야 의미가 있을까

    MicroK8s 벤치마크라고 검색하면 CPU, 메모리 그래프 하나 딱 보여주는 자료가 많은데요, 실무에서는 그걸로 판단하기 어렵습니다. 제가 실제로 비교할 때는 아래 항목을 기준으로 잡았습니다.

    • Idle resource usage(유휴 리소스 사용량): 아무 워크로드 없을 때 CPU와 메모리 사용량
    • Cold start time(콜드 스타트 시간): 설치 후 API 응답 가능 상태까지 걸리는 시간
    • Pod scheduling latency(파드 스케줄링 지연): 테스트 파드가 실제 Running 상태가 되기까지의 시간
    • Add-on overhead(부가 기능 오버헤드): DNS, Ingress, Metrics 추가 후 증가 폭
    • Disk footprint(디스크 점유): 설치 후 데이터 디렉터리 증가량

    사실 이 다섯 개만 잡아도 꽤 감이 옵니다. 특히 ARM 서버에서는 디스크 성능이 병목이 되는 경우가 많아서, 메모리만 보면 놓치는 게 많더라고요.

    4. 제가 홈랩에서 잡은 테스트 조건

    벤치마크는 조건 통제가 핵심입니다. 같은 하드웨어, 같은 OS 계열, 같은 컨테이너 이미지, 같은 측정 횟수로 맞춰야 비교가 그나마 공정해집니다. 저는 아래처럼 맞춰서 보는 편입니다.

    1. 테스트 대상 노드는 동일한 ARM 기반 장비 또는 동일 사양 미니 PC로 구성합니다.
    2. 운영체제는 같은 Ubuntu 계열로 맞춥니다.
    3. 백그라운드 서비스와 자동 업데이트는 측정 전 최대한 정리합니다.
    4. MicroK8s와 K3s는 각각 단일 노드로 먼저 비교합니다.
    5. 기본 상태와 add-on 활성화 상태를 따로 측정합니다.
    6. 각 측정은 3회 이상 반복해서 편차를 확인합니다.

    이 부분을 안 맞추면 진짜 헷갈립니다. 저도 처음엔 결과가 들쭉날쭉해서 한참 헤맸는데, 알고 보니 한쪽은 메트릭 수집기가 떠 있었고 다른 쪽은 아니더라고요. 삽질 좀 했습니다 ㅎㅎ

    5. 실전 구현: MicroK8s와 K3s 벤치마크 준비

    5-1. MicroK8s 설치와 기본 확인

    sudo snap install microk8s --classic
    sudo usermod -a -G microk8s $USER
    newgrp microk8s
    microk8s status --wait-ready
    microk8s kubectl get nodes -o wide

    MicroK8s는 snap 기반이라 설치 경험이 꽤 일관적입니다. 대신 snapd 상태나 채널(channel, 배포 트랙)에 따라 초기 준비 시간이 다르게 느껴질 수 있습니다.

    5-2. K3s 설치와 기본 확인

    curl -sfL https://get.k3s.io | sh -
    sudo kubectl get nodes -o wide
    sudo systemctl status k3s --no-pager

    K3s는 설치가 정말 간단합니다. 처음 써보면 “이렇게 빨리 올라온다고?” 싶은 느낌이 있어요. 엣지 환경에서 자주 언급되는 이유가 괜히 있는 게 아니더라고요.

    MicroK8s 벤치마크와 K3s 성능 비교를 위한 단일 노드 설치 상태 이미지

    단일 노드 환경에서 두 배포판의 설치 직후 상태를 비교하는 장면을 보여주는 이미지입니다.

    5-3. 공통 워크로드 배포

    apiVersion: apps/v1
    kind: Deployment
    metadata:
      name: nginx-bench
    spec:
      replicas: 3
      selector:
        matchLabels:
          app: nginx-bench
      template:
        metadata:
          labels:
            app: nginx-bench
        spec:
          containers:
          - name: nginx
            image: nginx:stable
            ports:
            - containerPort: 80
    ---
    kubectl apply -f nginx-bench.yaml
    kubectl get pods -w

    여기서는 복잡한 앱보다 단순한 워크로드가 낫습니다. 이유는 비교 대상을 경량 쿠버네티스 배포판 자체로 최대한 좁혀야 하기 때문입니다.

    5-4. 유휴 리소스와 파드 상태 측정

    free -h
    vmstat 1 5
    ps aux --sort=-%mem | head
    kubectl get pods -A
    kubectl top nodes
    kubectl top pods -A

    Metrics Server(메트릭 서버, 리소스 수집 컴포넌트)가 없는 상태라면 kubectl top이 바로 안 될 수 있습니다. 이때는 OS 레벨 지표와 함께 보셔야 합니다.

    6. ⚠️ 실제로 겪었던 문제와 트러블슈팅

    6-1. 애드온 차이 때문에 공정 비교가 안 되는 문제

    이게 제일 흔합니다. MicroK8s는 add-on 활성화가 쉽고, K3s는 기본 구성이 상대적으로 간결한 편이라 시작점이 다를 수 있거든요. 그래서 기본 설치 상태와 필수 기능 포함 상태를 따로 비교해야 합니다.

    microk8s enable dns ingress metrics-server
    kubectl get pods -A

    혹시 이런 경험 있으신가요? “왜 MicroK8s가 더 무겁지?” 하고 봤더니 사실 기능이 더 많이 켜져 있었던 겁니다.

    6-2. ARM 서버에서 스토리지 지연 때문에 체감이 달라지는 문제

    특히 SD 카드나 느린 eMMC 기반 장비에서는 API 응답보다 이미지 풀(image pull, 컨테이너 이미지 다운로드)과 파일 시스템 동기화가 더 큰 변수였습니다. 그래서 이미지를 미리 받아둔 상태와 처음 내려받는 상태를 분리해서 봐야 해요.

    6-3. 첫 부팅과 재부팅 후 상태가 다른 문제

    처음엔 이게 뭔가 싶었는데, 재부팅 후 서비스 재기동 순서와 캐시 영향으로 결과가 다르게 나오더라고요. 그래서 가능하면 아래처럼 측정 구간을 분리하는 게 좋습니다.

    • 설치 직후 측정
    • 재부팅 후 안정화 뒤 측정
    • 워크로드 배포 후 측정

    7. 검증과 결과 해석: 숫자보다 패턴을 보셔야 합니다

    제가 여러 번 비교해보면서 느낀 패턴은 이렇습니다. K3s는 초기 설치와 기동이 간결하게 느껴지고, MicroK8s는 기능 통합과 관리 편의성이 좋다는 점입니다. 그리고 K3s 성능 비교 자료를 볼 때 자주 나오는 “더 가볍다”는 평은, 대체로 최소 구성 기준에서 이해하면 맞습니다.

    반대로 실서비스에 가까운 구성으로 DNS, Ingress, 모니터링 관련 요소를 하나씩 붙이기 시작하면, 단순 메모리 숫자만으로는 승부가 잘 안 나기도 합니다. 여기서 중요한 건 운영 편의성 대비 리소스 비용입니다. CPU 몇 퍼센트, 메모리 몇백 MB보다도 내가 관리하면서 덜 고생하는 쪽이 전체 비용은 더 낮을 수 있거든요.

    MicroK8s 벤치마크 결과를 보여주는 CPU 메모리 대시보드 이미지

    유휴 상태와 워크로드 배포 후 상태를 비교하는 벤치마크 대시보드 이미지입니다.

    비교 관점 MicroK8s에서 보기 좋은 점 K3s에서 보기 좋은 점
    초기 셋업 애드온 중심 운영이 편함 빠르고 단순하게 시작 가능
    리소스 민감 환경 기능 포함 시 구성 일관성 확보 최소 구성에서 부담이 적은 편
    실험/홈랩 여러 기능을 켜보며 배우기 좋음 가볍게 여러 노드 실험하기 좋음
    운영 판단 기준 기능 통합성 경량성과 단순성

    즉, MicroK8s 벤치마크 결과를 해석할 때는 “기본 상태 비교인지”, “실제 운영 기능 포함 비교인지”를 꼭 구분하셔야 합니다.

    8. 어떤 환경에 무엇을 고르면 좋을까

    • 초소형 엣지 노드나 아주 타이트한 리소스 환경이면 K3s 쪽이 출발이 편할 가능성이 큽니다.
    • 학습, 홈랩, 기능 실험을 자주 하신다면 MicroK8s의 add-on 경험이 꽤 편합니다.
    • ARM 서버를 여러 대 운영하면서 자동화까지 보실 거라면 설치 이후 운영 스크립트와 모니터링 방식까지 같이 검토하셔야 합니다.

    저는 개인적으로 “최소 자원 + 빠른 프로비저닝”이 우선이면 K3s를 먼저 보고, “기능 포함 상태에서 일관된 운영 경험”이 중요하면 MicroK8s를 먼저 봅니다. 둘 중 하나가 절대적으로 우월하다기보다, 우선순위가 다르다고 보는 게 맞더라고요.

    9. 정리와 다음 단계

    정리해보면, MicroK8s 벤치마크는 단순히 누가 더 메모리를 덜 먹느냐로 끝나지 않습니다. 엣지 환경에서는 설치 방식, 애드온 구조, 스토리지 특성, 재부팅 후 복구 속도, 그리고 운영 중 손이 얼마나 가는지가 다 같이 중요합니다. 제가 직접 해보니 숫자 하나보다 패턴이 더 중요했고, 특히 비교 조건을 통일하는 게 절반이더라고요.

    다음 글에서는 실제 홈랩 기준으로 Prometheus(프로메테우스, 메트릭 수집 시스템)와 Grafana(그라파나, 시각화 도구)를 붙여서 MicroK8s와 K3s의 장기 모니터링 방법도 다뤄볼 예정입니다. 이전 글에서 다뤘던 홈랩 네트워크 설계 내용과도 연결해서 보시면 더 이해가 쉬우실 겁니다.

    MicroK8s 벤치마크와 K3s 선택 기준을 요약한 인포그래픽

    어떤 조건에서 어떤 배포판이 더 잘 맞는지 한 장으로 정리한 요약 이미지입니다.

    자주 묻는 질문

    Q1. MicroK8s와 K3s 중 뭐가 무조건 더 빠른가요?

    무조건이라고 말하긴 어렵습니다. 최소 구성에서는 K3s가 가볍게 느껴지는 경우가 많지만, 운영 기능을 붙인 뒤에는 비교 조건에 따라 달라집니다.

    Q2. 엣지 컴퓨팅에서는 무엇을 먼저 봐야 하나요?

    CPU보다도 메모리, 디스크 I/O, 재기동 안정성을 먼저 보시는 걸 권장합니다. 특히 느린 저장장치에서는 체감 차이가 크게 납니다.

    Q3. 홈랩 입문자는 무엇으로 시작하면 좋을까요?

    기능을 빨리 배우고 싶으면 MicroK8s, 아주 가볍게 여러 노드를 올려보고 싶으면 K3s가 편할 수 있습니다.

  • [인프라] Istio 프로덕션 도입 1년: 서비스 메시 운영에서 얻은 교훈과 팁

    [인프라] Istio 프로덕션 도입 1년: 서비스 메시 운영에서 얻은 교훈과 팁

    [인프라] Istio 프로덕션 도입 1년: 서비스 메시 운영에서 얻은 교훈과 팁

    Istio 프로덕션 운영을 1년 정도 해보니, 처음 기대했던 장점과 실제 운영에서 마주치는 현실은 꽤 다르더라고요. 쿠버네티스 서비스 메시를 도입하면 트래픽 제어, 보안 정책, 관측성(Observability, 시스템 상태를 관찰하는 능력)이 한 번에 정리될 것 같았는데, 막상 운영에 들어가니 설정은 늘어나고, 장애 포인트도 새로 생기고, 팀의 이해도 차이도 크게 느껴졌습니다. 저도 처음엔 이게 뭔가 싶었는데요. 그래도 지나고 보니 Istio 도입 사례에서 중요한 건 기능 자체보다, 어디까지 맡기고 어디서 멈출지 경계를 정하는 일이었습니다.

    이번 글에서는 제가 직접 겪은 Istio 프로덕션 운영 경험을 바탕으로, 도입 초기에 놓치기 쉬운 부분과 서비스 메시 운영 노하우를 정리해보겠습니다. 특히 Istio 장애 해결 과정에서 배운 점, 운영 기준을 어떻게 세웠는지, 그리고 어떤 팀에 정말 잘 맞는지 현실적으로 풀어볼게요.

    Istio 프로덕션 운영 아키텍처를 보여주는 쿠버네티스 서비스 메시 다이어그램

    프로덕션 환경에서 Ingress(인그레스, 외부 트래픽 진입점), Envoy 프록시, 서비스 간 통신 흐름을 한눈에 보여주는 구조 이미지입니다.

    1. 왜 Istio 프로덕션 운영이 생각보다 어렵냐면

    쉽게 말해 Istio는 애플리케이션 바깥에서 네트워크 정책을 통제하게 해주는 계층이거든요. 애플리케이션 코드를 크게 바꾸지 않고도 mTLS(mutual TLS, 상호 인증 암호화), Traffic Split(트래픽 분할), Retry(재시도), Timeout(타임아웃), Access Control(접근 제어)을 걸 수 있거든요. 듣기만 하면 정말 좋죠.

    근데 여기서 중요한 포인트! 기능이 많다는 건 운영자가 알아야 할 상태값도 많아진다는 뜻입니다. 실제로 써보니까, 애플리케이션 장애인지, 쿠버네티스 네트워크 문제인지, Envoy 설정 문제인지, Istiod 제어 평면(Control Plane, 정책 배포를 담당하는 핵심 컴포넌트) 문제인지 구분하는 데 시간이 꽤 걸렸습니다. 특히 밤에 장애 나면 정신없습니다 ㅎㅎ

    • 좋았던 점: 일관된 트래픽 정책, 보안 정책 표준화, 메트릭 수집 편의성
    • 어려웠던 점: 사이드카(Sidecar, 애플리케이션 옆에서 붙는 프록시) 리소스 오버헤드, 디버깅 복잡도, 운영 지식 요구치 상승
    • 결론: 기능보다 운영 모델을 먼저 정해야 합니다

    2. 서비스 메시를 쉽게 말하면 이런 개념입니다

    서비스 메시(Service Mesh, 마이크로서비스 간 통신 제어 계층)는 애플리케이션끼리 주고받는 네트워크 요청을 공통 프록시가 대신 관리하는 구조거든요. 개발팀은 비즈니스 로직에 집중하고, 플랫폼팀이나 인프라팀은 통신 정책을 중앙에서 제어하는 식이죠.

    항목 서비스 메시 없이 Istio 사용 시
    재시도 정책 애플리케이션 코드마다 개별 구현 VirtualService(가상 서비스 규칙)로 일관 적용
    서비스 간 암호화 앱별 구현 편차 큼 mTLS 정책으로 표준화 가능
    트래픽 분할 배포 파이프라인에 강하게 의존 Canary(카나리, 점진 배포) 구성 수월
    관측성 로그/메트릭 포맷 제각각 프록시 기준으로 공통 지표 확보

    제가 멘토링할 때 자주 드리는 말이 있는데요. Istio는 네트워크 운영체제처럼 접근해야 한다는 겁니다. 단순히 설치하고 끝나는 애드온(add-on)이 아니거든요. 정책, 배포, 인증서, 모니터링이 다 얽혀 있습니다.

    3. 제가 실제로 잡았던 Istio 도입 범위와 기준

    처음부터 클러스터 전체에 강하게 적용하면 사고 납니다. 저도 초반에 의욕이 앞서서 네임스페이스(namespace, 쿠버네티스 논리 격리 단위) 여러 개에 한 번에 사이드카 주입을 걸었다가, 예상 못 한 헬스체크 경로와 내부 호출 정책 때문에 삽질 좀 했습니다.

    그래서 운영 기준을 이렇게 다시 잡았습니다.

    1. 외부 공개 API와 내부 핵심 서비스만 우선 적용
    2. 관측성과 mTLS부터 도입
    3. 복잡한 Fault Injection(장애 주입)이나 고급 라우팅은 나중으로 미룸
    4. 기본 Retry, Timeout, Circuit Breaker(회로 차단) 정책만 표준화
    5. 온보딩 문서와 디버깅 명령어를 먼저 팀에 공유

    이 접근이 좋았던 이유는 명확합니다. Istio 도입 사례를 보면 성공한 팀들은 대부분 단계적으로 갑니다. 반대로, 기능을 다 켜놓고 팀이 따라오길 기대하면 운영 피로도가 급격히 올라갑니다.

    4. 실전 구현: 최소한의 프로덕션 시작 구성

    아래 예시는 과하게 복잡하지 않으면서도, 프로덕션에서 바로 의미가 있는 패턴입니다. Ingress Gateway(인그레스 게이트웨이), DestinationRule(대상 규칙), VirtualService를 기본으로 잡고, Timeout과 Retry를 명시해두는 방식이죠.

    4-1. 네임스페이스에 사이드카 자동 주입

    kubectl create namespace prod-app
    kubectl label namespace prod-app istio-injection=enabled
    kubectl get namespace prod-app --show-labels

    여기서 라벨(label)이 붙었다고 끝이 아닙니다. 기존 파드(Pod, 쿠버네티스 실행 단위)는 재생성이 필요합니다. 저도 이걸 놓쳐서 “왜 프록시가 안 붙지?” 하고 한참 봤었네요.

    4-2. 기본 라우팅 정책 정의

    apiVersion: networking.istio.io/v1beta1
    kind: VirtualService
    metadata:
      name: app-service
      namespace: prod-app
    spec:
      hosts:
        - app-service
      http:
        - timeout: 3s
          retries:
            attempts: 2
            perTryTimeout: 1s
          route:
            - destination:
                host: app-service
                subset: stable

    4-3. 서브셋과 mTLS 정책 정의

    apiVersion: networking.istio.io/v1beta1
    kind: DestinationRule
    metadata:
      name: app-service
      namespace: prod-app
    spec:
      host: app-service
      trafficPolicy:
        tls:
          mode: ISTIO_MUTUAL
      subsets:
        - name: stable
          labels:
            version: v1

    실제로 써보니까 Timeout을 명시하지 않은 서비스가 생각보다 많았습니다. 서비스 메시가 들어오면 느린 응답이 더 잘 보이거든요. 이전에는 그냥 “가끔 느리네” 하고 넘어가던 게, 이제는 메트릭으로 드러납니다. 이건 불편한 게 아니라, 원래 있던 문제가 보이기 시작한 겁니다.

    Istio 프로덕션 운영에서 VirtualService와 DestinationRule 트래픽 제어를 설명하는 이미지

    VirtualService, DestinationRule, mTLS 정책이 어떻게 연결되어 실제 요청 흐름을 제어하는지 설명하는 구성 이미지입니다.

    4-4. 점검 명령어는 꼭 표준화하세요

    kubectl get pod -n prod-app
    kubectl describe pod <pod-name> -n prod-app
    istioctl proxy-status
    istioctl analyze
    kubectl logs <pod-name> -c istio-proxy -n prod-app

    여기서 istioctl analyze는 진짜 자주 씁니다. 리소스 참조가 꼬였을 때 생각보다 빠르게 힌트를 주더라고요.

    5. ⚠️ 실제 겪었던 문제와 Istio 장애 해결 팁

    Istio 장애 해결은 “증상”보다 “레이어”를 먼저 구분해야 빨라집니다. 저도 초반에는 애플리케이션 로그만 봤었는데, 나중엔 아래 순서로 보는 습관이 생겼습니다.

    1. 애플리케이션 컨테이너가 느린 건지 확인
    2. istio-proxy 컨테이너 로그 확인
    3. VirtualService, DestinationRule, PeerAuthentication 적용 범위 확인
    4. Ingress와 내부 서비스 호출 경로 분리해서 테스트
    5. mTLS 강제 여부와 예외 대상 확인

    5-1. 사이드카 주입 누락

    가장 흔했습니다. 특히 배치 잡(Job)이나 운영성 파드에서 자주 빠졌어요. 네임스페이스 라벨만 믿지 말고, 실제 파드에 프록시 컨테이너가 붙었는지 확인해야 합니다.

    5-2. mTLS 적용 후 일부 통신 실패

    이건 정말 많이 겪습니다. Plain Text(평문 통신)를 쓰던 워크로드가 남아 있거나, 외부 연동 경로가 예외 처리되지 않으면 통신 실패가 납니다. 저도 처음엔 애플리케이션 버그인 줄 알았는데 아니더라고요. 정책 범위를 좁혀서 단계적으로 적용하는 게 답이었습니다.

    5-3. Retry 때문에 지연이 더 커진 경우

    Retry는 만능이 아닙니다. 다운스트림 서비스가 이미 과부하인데 재시도를 추가하면 요청 폭주가 더 심해질 수 있거든요. 그래서 저는 모든 서비스에 같은 재시도 정책을 넣지 않고, 읽기 요청과 쓰기 요청을 구분해서 적용했습니다.

    • 읽기 요청: 제한적 Retry 허용
    • 쓰기 요청: 중복 처리 위험 때문에 신중 적용
    • 긴 작업: Timeout 기준부터 재설계

    5-4. 리소스 사용량 증가

    사이드카가 붙으면 메모리와 CPU 사용량은 당연히 늘어납니다. 이건 피할 수 없는 비용입니다. 중요한 건 놀라지 않는 거예요. 처음 용량 계획(Capacity Planning, 자원 수용량 계획)을 할 때부터 프록시 오버헤드를 포함해야 합니다.

    6. 검증은 이렇게 했습니다: 운영 지표와 확인 루틴

    프로덕션 반영 후에는 “설치됐다”보다 “통제되고 있다”를 확인해야 합니다. 저는 검증 기준을 크게 네 가지로 잡았습니다.

    • 서비스 간 호출 성공률이 안정적인가
    • 에러율이 특정 경로에서 급증하지 않는가
    • 배포 후 트래픽 분할이 의도대로 동작하는가
    • 프록시 로그와 애플리케이션 로그를 함께 볼 수 있는가
    kubectl get virtualservice -A
    kubectl get destinationrule -A
    kubectl get peerauthentication -A
    istioctl proxy-config routes <pod-name> -n prod-app

    특히 proxy-config routes로 실제 라우팅 결과를 확인하는 습관이 중요했습니다. YAML은 맞아 보여도 프록시에 반영된 상태가 기대와 다를 때가 있거든요. 처음엔 좀 귀찮은데, 이 루틴 덕분에 배포 직후 이상 동작을 빨리 잡았습니다.

    Istio 프로덕션 운영 검증을 위한 트래픽 모니터링 대시보드 이미지

    성공률, 지연 시간, 에러율, 서비스 간 호출 관계를 한 화면에서 확인하는 운영 대시보드 예시 이미지입니다.

    7. 1년 운영 후 정리한 서비스 메시 운영 노하우

    여기서는 좀 현실적인 이야기를 드릴게요. 쿠버네티스 서비스 메시가 모든 팀에 정답은 아닙니다. 서비스 수가 적고, 트래픽 정책도 단순하고, 팀이 네트워크 추상화에 익숙하지 않다면 오히려 복잡도만 늘 수 있습니다.

    반대로 아래 조건이면 Istio 프로덕션 운영의 효과가 꽤 큽니다.

    잘 맞는 경우 조심해야 하는 경우
    마이크로서비스 수가 많음 서비스 수가 적고 구조가 단순함
    팀별 통신 정책을 표준화해야 함 운영 인력이 매우 적음
    보안 정책과 암호화 요구가 큼 장애 분석 도구나 관측 체계가 약함
    카나리 배포와 세밀한 라우팅이 중요함 플랫폼 운영 문서가 없는 상태

    제가 얻은 핵심 교훈은 세 가지입니다.

    1. 작게 시작할 것: 전체 적용보다 핵심 서비스 우선
    2. 운영 명령어를 표준화할 것: 장애 대응 속도가 달라집니다
    3. 정책 소유권을 분명히 할 것: 누가 어떤 라우팅, 보안 정책을 관리하는지 애매하면 반드시 꼬입니다

    이 부분은 다음 글에서 다룰 예정인데요. 특히 Gateway API와 기존 Ingress 운영을 어떻게 나눠 가져갈지, 그리고 멀티 클러스터까지 확장할 때 무엇이 달라지는지는 따로 정리해보겠습니다. 이전 글에서 다뤘던 쿠버네티스 리소스 요청/제한(Request/Limit) 설계와도 연결되는 주제라 같이 보시면 흐름이 더 잘 잡히실 겁니다.

    Istio 프로덕션 운영 도입 전후를 비교한 서비스 메시 운영 요약 이미지

    도입 전후의 운영 방식 차이와 핵심 체크포인트를 직관적으로 비교하는 요약 이미지입니다.

    8. 마무리: Istio는 기능보다 운영 습관이 더 중요했습니다

    1년 정도 돌려보니, Istio 도입 사례에서 진짜 중요한 건 화려한 기능이 아니라 운영 습관이더라고요. YAML 몇 개 잘 쓴다고 끝나는 게 아니었습니다. 누가 정책을 만들고, 누가 검증하고, 장애가 났을 때 어느 레이어부터 볼지 팀이 합의되어 있어야 합니다.

    혹시 지금 Istio 도입을 고민하고 계시다면, 제 경험상 가장 먼저 할 일은 기능 체크리스트가 아닙니다. 우리 팀이 이 복잡도를 감당할 준비가 되었는지를 보는 겁니다. 준비가 되어 있다면 Istio 프로덕션 운영은 분명히 강력한 무기가 됩니다. 다만 준비 없이 들어가면, 편해지려고 넣은 도구가 가장 큰 장애 포인트가 되기도 합니다.

    정리하면 이렇습니다.

    • Istio는 강력하지만 운영 난도가 낮지는 않습니다
    • 서비스 메시 운영 노하우의 핵심은 단계적 도입과 표준화입니다
    • Istio 장애 해결은 로그보다 레이어 구분이 먼저입니다
    • 쿠버네티스 서비스 메시의 가치는 규모와 표준화 요구가 클수록 올라갑니다

    저도 처음엔 헷갈렸는데, 한 번 기준이 잡히고 나니까 훨씬 수월해졌습니다. 여러분도 처음부터 완벽하게 하려 하지 마시고, 작은 범위에서 검증하면서 가져가보세요. 그게 결국 제일 덜 아프고, 제일 오래 갑니다. 🎉

    9. 자주 묻는 질문 정리

    Istio는 모든 쿠버네티스 환경에 필요한가요?

    아닙니다. 서비스 수가 적고 통신 정책이 단순하다면 오버엔지니어링이 될 수 있습니다.

    처음 도입할 때 가장 먼저 볼 것은 뭔가요?

    mTLS와 관측성부터입니다. 그다음에 라우팅 정책을 최소 단위로 붙여보는 게 좋습니다.

    Istio 장애 해결에서 가장 자주 놓치는 부분은 뭔가요?

    사이드카 주입 여부, 정책 적용 범위, 그리고 실제 프록시에 반영된 설정 확인입니다.

  • [K8s] Longhorn 1년 운영기: 안정성과 성능 정리

    [K8s] Longhorn 1년 운영기: 안정성과 성능 정리

    [K8s] 프로덕션 Longhorn 1년 운영기: 안정성과 성능 정리

    프로덕션에서 k8s longhorn 운영을 1년 정도 해보면, 처음 기대했던 편의성과 실제 운영 난이도 사이의 간격이 꽤 선명하게 보입니다. 저도 처음엔 ‘쿠버네티스(Kubernetes, 컨테이너 오케스트레이션) 위에서 스토리지까지 한 번에 정리되면 정말 편하겠다’ 싶었거든요. 실제로 써보니까 편한 점도 분명했고, 반대로 방심하면 장애를 키우는 포인트도 있었습니다. 이번 글은 제품 소개보다는 longhorn 사례에 가깝습니다. 무엇이 안정적이었고, 어디서 삽질했는지, 그리고 지금 다시 구성한다면 무엇을 다르게 할지 정리해보겠습니다.

    특히 상태 저장 워크로드(Stateful Workload, 데이터가 남아야 하는 애플리케이션)를 k8s에서 돌리고 계신 분이라면, 스토리지 선택이 결국 운영 스트레스를 좌우한다는 걸 이미 느끼셨을 겁니다. 혹시 PVC(PersistentVolumeClaim, 영구 볼륨 요청)만 붙이면 끝이라고 생각하셨다면, 여기서 중요한 포인트가 있습니다. 스토리지는 선언형만으로 끝나지 않고 운영 습관까지 같이 설계해야 하더라고요.

    프로덕션 클러스터에서 Longhorn이 각 노드 디스크를 묶어 분산 블록 스토리지로 구성되는 전체 흐름을 보여주는 이미지입니다.

    1. 왜 k8s에서 Longhorn을 고민하게 되는가

    쉽게 말해 Longhorn은 쿠버네티스 친화적인 분산 블록 스토리지(Distributed Block Storage)입니다. 각 노드의 디스크를 활용해서 볼륨을 만들고, 복제본(Replica, 데이터 사본)을 여러 노드에 분산해 두는 방식이죠. 관리 포인트가 늘어나는 건 맞는데, 대신 클러스터 안에서 스토리지를 비교적 일관된 방식으로 다룰 수 있습니다.

    제가 Longhorn을 선택했던 이유는 세 가지였습니다.

    • 노드 로컬 디스크를 활용할 수 있다는 점
    • UI 기반 운영 가시성이 괜찮다는 점
    • 백업(Backup)과 스냅샷(Snapshot) 흐름을 잡기 쉬웠다는 점

    반대로 처음부터 알았어야 했던 것도 있습니다. Longhorn은 마법이 아닙니다. underlying disk, 네트워크, 워크로드 I/O 패턴이 좋지 않으면 그대로 티가 납니다. 즉 k8s 스토리지 문제를 Longhorn이 가려주지는 않더라고요. 오히려 더 빨리 드러내 줍니다. 이건 단점 같지만, 운영자 입장에선 장점이기도 합니다.

    2. Longhorn 개념, 쉽게 말해 어떻게 동작하나

    처음엔 이게 뭔가 싶었는데, 구조를 한 번 그림으로 이해하면 꽤 단순합니다.

    1. 파드(Pod, 컨테이너 실행 단위)가 PVC를 요청합니다.
    2. 스토리지클래스(StorageClass, 동적 볼륨 정책)에 따라 Longhorn 볼륨이 생성됩니다.
    3. Longhorn이 여러 노드에 복제본을 배치합니다.
    4. 특정 노드에서 파드가 볼륨을 마운트(Mount, 연결)해서 사용합니다.
    5. 노드 하나가 내려가도 복제본이 남아 있으면 복구 여지가 생깁니다.

    여기서 핵심은 두 가지입니다. 첫째, 복제본 수는 안정성과 비용의 균형입니다. 둘째, 성능은 결국 네트워크와 디스크 특성의 영향을 크게 받습니다. 그래서 Longhorn을 볼 때는 단순히 설치 성공 여부보다, 복제 전략과 워크로드 특성을 먼저 맞춰보는 게 중요합니다.

    항목 Longhorn에서 보는 포인트 운영 시 체감
    안정성 복제본 분산, 노드 장애 대응 단일 노드 장애에는 강한 편
    성능 디스크 IOPS, 네트워크 지연 랜덤 I/O 많은 DB는 민감함
    운영 편의 UI, 볼륨 관리, 스냅샷 초기 러닝커브 이후 꽤 편함
    주의점 디스크 여유 공간, 재빌드 트래픽 장애 후 재동기화 시 부담 큼

    3. 제가 실제로 잡았던 운영 원칙

    longhorn 안정성은 제품 자체보다 운영 원칙에서 많이 갈렸습니다. 제가 1년 돌리면서 끝까지 가져간 기준은 아래와 같았습니다.

    • 컨트롤 플레인(Control Plane, 제어 영역)과 스토리지 부담을 가능한 분리
    • 복제본은 최소한 장애 도메인(Failure Domain, 함께 망가질 수 있는 범위)을 고려해서 배치
    • 디스크 사용률 임계치(Threshold, 경계값)를 보수적으로 설정
    • 스냅샷은 남발하지 않고 백업 정책과 구분
    • DB처럼 I/O 민감한 워크로드는 사전 검증 후 배치

    이 중에서 가장 중요했던 건 디스크 여유 공간입니다. 처음엔 여유가 좀 있으니 괜찮겠지 했었는데, 재빌드(Rebuild, 복제본 재생성)가 걸리는 순간 생각보다 빠르게 압박이 오더라고요. 스토리지 시스템은 평상시보다 장애 직후가 더 무겁습니다. 이건 꼭 기억하셔야 합니다.

    4. 실전 구현: k8s Longhorn 운영 시작할 때 제가 한 순서

    설치는 환경마다 조금씩 다르지만, 흐름은 크게 다르지 않습니다. 아래는 제가 홈랩과 실서비스 검증 환경에서 자주 쓰는 형태입니다.

    1. 노드 디스크 상태와 마운트 경로를 먼저 확인합니다.
    2. Longhorn 시스템용 네임스페이스(namespace)를 분리합니다.
    3. 기본 StorageClass를 만들되, 복제본 수를 환경에 맞게 조정합니다.
    4. 테스트용 PVC와 파드를 붙여 실제 쓰기/읽기를 확인합니다.
    5. 운영 전 스냅샷과 백업 경로를 반드시 따로 검증합니다.
    kubectl create namespace longhorn-system
    kubectl get nodes -o wide
    kubectl get sc
    kubectl get pods -n longhorn-system
    

    스토리지클래스는 아래처럼 시작하면 이해가 쉽습니다. 값 자체는 환경마다 다르게 잡으셔야 합니다.

    apiVersion: storage.k8s.io/v1
    kind: StorageClass
    metadata:
      name: longhorn-replicated
    provisioner: driver.longhorn.io
    allowVolumeExpansion: true
    reclaimPolicy: Delete
    volumeBindingMode: Immediate
    parameters:
      numberOfReplicas: "3"
      staleReplicaTimeout: "30"
      fsType: "ext4"
    

    그리고 바로 PVC를 붙여봅니다.

    apiVersion: v1
    kind: PersistentVolumeClaim
    metadata:
      name: demo-pvc
    spec:
      accessModes:
        - ReadWriteOnce
      storageClassName: longhorn-replicated
      resources:
        requests:
          storage: 20Gi
    ---
    apiVersion: v1
    kind: Pod
    metadata:
      name: demo-writer
    spec:
      containers:
        - name: writer
          image: busybox
          command: ["sh", "-c", "while true; do date >> /data/out.log; sleep 5; done"]
          volumeMounts:
            - name: data
              mountPath: /data
      volumes:
        - name: data
          persistentVolumeClaim:
            claimName: demo-pvc
    

    처음엔 단순한 쓰기 테스트가 무슨 의미가 있나 싶었는데, 여기서 마운트 지연이나 attach 문제를 초기에 꽤 많이 잡았습니다. 운영 들어가고 나서 알면 늦거든요.

    k8s longhorn 운영 설정과 PVC 연결 흐름 이미지

    StorageClass 생성부터 PVC 연결, 파드 마운트까지 이어지는 실제 구성 흐름을 설명하는 이미지입니다.

    5. 운영 중 자주 본 이슈와 트러블슈팅

    이 섹션이 사실 제일 중요합니다. 예쁜 소개보다, 실제로 어디서 무너지는지를 아는 게 훨씬 도움이 되거든요.

    5-1. ⚠️ 복제본 재빌드가 길어지면서 성능이 흔들릴 때

    노드 하나를 점검하느라 내렸다가 다시 올리면, 복제본 재동기화가 시작됩니다. 이때 네트워크와 디스크에 동시에 부담이 옵니다. longhorn 성능이 평소엔 괜찮다가도 이 구간에서 갑자기 느려질 수 있습니다.

    제가 했던 대응은 단순했습니다.

    • 업무 시간 중 대규모 재스케줄링을 피했습니다.
    • 용량이 큰 볼륨은 변경 작업 전에 백업 상태를 먼저 확인했습니다.
    • 재빌드가 오래 걸리는 볼륨은 워크로드 I/O 패턴을 따로 봤습니다.

    5-2. ⚠️ 디스크는 남았는데 스케줄링이 꼬이는 경우

    이건 꽤 헷갈립니다. df로 보면 공간이 남아 있는데, Longhorn 쪽에서는 새 복제본 배치를 보수적으로 판단하는 경우가 있습니다. 저도 처음엔 버그인가 싶었는데, 실제론 예약 공간과 최소 여유 공간 정책을 같이 봐야 하더라고요.

    이럴 때는 아래처럼 확인했습니다.

    kubectl get pvc
    kubectl describe pvc demo-pvc
    kubectl get volumes.longhorn.io -n longhorn-system
    kubectl get replicas.longhorn.io -n longhorn-system
    

    5-3. ⚠️ 스냅샷이 백업을 대체한다고 착각하기

    이건 정말 많이 나오는 실수입니다. 스냅샷(Snapshot, 시점 복사본)은 같은 스토리지 계층 안에 남는 경우가 많아서, 근본적인 백업 전략과는 다릅니다. 저도 초반엔 스냅샷이 있으니 어느 정도 안심했었는데, 사고 시나리오를 따져보면 그걸로는 부족했습니다. 스냅샷은 운영 편의 기능이고, 백업은 재해 복구 전략입니다. 완전히 다릅니다.

    6. 검증 방법: 설치보다 중요한 운영 확인 절차

    프로덕션에선 ‘붙었다’보다 ‘망가졌을 때 어떻게 보이는가’를 확인해야 합니다. 저는 최소한 아래는 반복해서 체크했습니다.

    1. 파드 재시작 후 볼륨 재연결이 정상인지 확인
    2. 노드 drain 이후 워크로드 복구 흐름 확인
    3. 스냅샷 생성 및 삭제 후 공간 회수 패턴 확인
    4. 백업 저장소 접근 실패 시 경고 동작 확인
    5. 복제본 손실 상황에서 재생성 시간이 과하게 길지 않은지 확인

    실제로 써보니까 UI에서 상태가 보여주는 정보가 꽤 유용했습니다. 다만 UI만 믿고 있으면 늦습니다. 이벤트(Event), PVC 상태, 파드 재시작 로그를 같이 봐야 원인이 보입니다.

    longhorn 성능과 안정성 확인용 운영 대시보드 이미지

    복제본 상태, 볼륨 attach 여부, 재빌드 진행 상황을 한눈에 확인하는 운영 관점의 대시보드 이미지입니다.

    7. 1년 운영 후 느낀 안정성과 성능 평가

    결론부터 말하면, Longhorn은 잘 설계된 환경에서는 충분히 실전적인 선택지였습니다. 특히 여러 팀이 PVC를 빠르게 써야 하고, 외부 스토리지 어플라이언스에 강하게 의존하고 싶지 않을 때 장점이 컸습니다. 제가 본 longhorn 사례 기준으로는 다음과 같이 정리할 수 있었습니다.

    구분 좋았던 점 아쉬웠던 점
    안정성 노드 단위 장애 대응이 비교적 명확함 디스크/네트워크 품질이 낮으면 불안정성이 바로 드러남
    성능 일반적인 앱 데이터, 로그성 워크로드는 무난 고성능 DB, 지연 민감 워크로드는 사전 검증 필수
    운영 가시성과 자동화 포인트가 좋음 재빌드와 용량 계획은 생각보다 더 보수적으로 해야 함

    드디어 됐다 싶었던 순간도 많았습니다. 예전엔 스토리지 붙이는 작업 자체가 팀 내 병목이었는데, Longhorn 이후엔 표준화가 쉬워졌거든요. 반대로 ‘이거 진짜 편하더라고요’라고 말할 수 있으려면, 초반 설계와 검증을 꽤 꼼꼼하게 해야 합니다. 그냥 설치만 해놓고 운영에 들어가면 언젠가 비용을 치릅니다.

    8. 정리와 다음 단계: 이런 분께 특히 맞습니다

    k8s longhorn 운영을 1년 해보면서 제가 내린 판단은 이렇습니다.

    • 온프레미스(On-premise, 자체 인프라)나 홈랩에서 쿠버네티스 스토리지를 일관되게 운영하고 싶은 분
    • 외부 스토리지 장비보다 클러스터 내부 표준화를 우선하는 팀
    • 운영자가 디스크, 네트워크, 장애 복구 흐름을 직접 이해하고 관리할 준비가 된 환경

    반대로, 아주 빡빡한 성능 요구가 있는 데이터베이스를 바로 얹을 계획이라면 먼저 작은 범위에서 검증해보시는 걸 권합니다. 저도 처음엔 자신 있게 올렸다가, I/O 패턴 때문에 조정 많이 했습니다. 결국 중요한 건 제품 이름보다 워크로드 적합성입니다.

    다음 글에서는 백업 저장소(Backup Target) 설계나, 스냅샷 정리 정책을 어떻게 가져가면 운영이 덜 꼬이는지 다뤄볼 예정입니다. 이전 글에서 다뤘던 Ingress(인그레스, 외부 트래픽 진입점)와 모니터링 구성과도 연결되는 부분이 많아서, 같이 보시면 흐름이 더 잘 잡히실 겁니다.

    k8s longhorn 운영 장단점 요약 인포그래픽

    안정성, 성능, 운영 난이도, 추천 환경을 한 장으로 요약한 비교형 인포그래픽 이미지입니다.

    9. 자주 묻는 질문 FAQ

    Q1. Longhorn은 모든 워크로드에 무난한가요?

    아닙니다. 일반적인 상태 저장 앱에는 잘 맞을 수 있지만, 초저지연이 중요한 워크로드는 별도 검증이 필요합니다.

    Q2. 복제본 수는 무조건 많을수록 좋은가요?

    그렇지 않습니다. 안정성은 좋아질 수 있지만, 용량과 재빌드 비용도 같이 늘어납니다. 장애 도메인 기준으로 잡는 게 현실적입니다.

    Q3. 스냅샷만 잘 찍어두면 백업은 안 해도 되나요?

    안 됩니다. 스냅샷과 백업은 역할이 다릅니다. 이건 꼭 분리해서 생각하셔야 합니다.

    정리하면, longhorn 안정성은 꽤 인상적이었고, longhorn 성능은 환경과 워크로드 영향을 솔직하게 받았습니다. 그래서 더 좋았습니다. 숨기지 않거든요. 문제를 빨리 보여주는 시스템은, 운영자가 준비만 되어 있으면 꽤 믿을 만합니다. k8s 스토리지 때문에 계속 고민 중이셨다면, 작은 클러스터에서부터 검증해보세요. 그 과정에서 배우는 게 정말 많습니다.

  • [Kubernetes] Ingress Controller 비용 분석: 효율적인 선택 가이드

    [Kubernetes] Ingress Controller 비용 분석: 효율적인 선택 가이드

    [Kubernetes] Ingress Controller 비용 효율 분석

    쿠버네티스(Kubernetes) 운영에서 Ingress Controller 비용은 생각보다 늦게 체감되는 항목입니다. 처음엔 워크로드만 잘 뜨면 끝인 줄 알았는데, 실제로 운영해보면 외부 트래픽을 받는 구조 하나 때문에 Load Balancer(로드밸런서, 트래픽 분산 장비) 비용, Node(노드, 서버 인스턴스) 점유, 인증서 관리, 로그 저장 비용이 같이 따라오거든요. 저도 홈랩(Home Lab, 개인 실험 환경)과 작은 운영 환경을 굴리면서 처음엔 “Nginx면 다 되는 거 아닌가?” 싶었는데, 막상 비용 구조를 뜯어보니 선택 기준이 꽤 달랐어요.

    이번 글은 특정 제품을 무조건 추천하려는 글은 아닙니다. Nginx Ingress 비용, Traefik 비용, 그리고 전체적인 Kubernetes 비용 최적화 관점에서 어떤 항목을 봐야 하는지 정리해보려는 글이에요. 쉽게 말해, 라이선스 가격표만 보지 말고 운영비까지 같이 보자는 이야기거든요.

    Ingress Controller 비용 구조를 보여주는 쿠버네티스 개요 다이어그램

    Ingress Controller 비용을 구성하는 요소를 한눈에 보여주는 개요 이미지가 들어갈 자리입니다.

    1. 왜 Ingress Controller 비용을 따로 봐야 할까요?

    Ingress(인그레스, 외부 트래픽 진입점)는 단순히 “HTTP 들어오는 문” 정도로 생각하기 쉬워요. 근데 여기서 중요한 포인트! 실제 비용은 소프트웨어 자체보다 주변 인프라에서 많이 발생합니다.

    • 클라우드 Load Balancer를 몇 개 붙이느냐
    • 고가용성(HA, High Availability)을 위해 Pod(파드, 실행 단위)를 몇 개 두느냐
    • TLS 종료(TLS termination, HTTPS 복호화 처리)를 어디서 하느냐
    • 접근 로그와 메트릭을 어디까지 남기느냐
    • 설정 복잡도 때문에 운영 시간이 얼마나 드느냐

    제가 직접 해보니, 오픈소스(Open Source, 공개 소프트웨어)라서 공짜라고 끝이 아니더라고요. 컨트롤러 자체는 무료여도, 잘못 설계하면 외부 Load Balancer를 여러 개 만들고, 로그도 과하게 쌓고, 장애 대응 시간도 길어진다니까요. 그러면 결국 사람 시간까지 비용이 돼 버립니다.

    2. 쉽게 이해하는 Ingress Controller 비용 구조

    쉽게 말해 Ingress Controller는 “트래픽 정리실” 같은 역할입니다. 사용자가 들어오면 어떤 서비스(Service, 쿠버네티스 내부 네트워크 대상)로 보낼지 결정하는 거죠. 그런데 그 정리실을 운영하려면 다음 네 가지 비용 축을 같이 봐야 합니다.

    2-1. 직접 비용(Direct Cost)

    • 클라우드 Load Balancer 사용 비용
    • 추가 노드 자원 사용량(CPU, Memory)
    • 상용 기능 또는 엔터프라이즈 지원 계약이 필요한 경우의 라이선스 비용

    2-2. 간접 비용(Indirect Cost)

    • 설정 난이도 때문에 생기는 운영 시간
    • 장애 분석에 드는 로그 수집 및 관찰성(Observability, 관측성) 비용
    • 보안 설정 실수로 인한 재작업 비용

    2-3. 확장 비용(Scale Cost)

    처음에는 서비스 2~3개라 괜찮습니다. 근데 서비스가 늘어나면 경로(path), 호스트(host), 인증서, 리다이렉트 정책이 같이 늘어나거든요. 이때 단일 Ingress Controller로 묶을지, 팀별로 분리할지에 따라 비용 구조가 달라집니다.

    2-4. 장애 비용(Incident Cost)

    이 부분은 숫자로 계산하기 애매하지만 정말 커요. 설정이 단순하면 장애 복구가 빠르고, 복잡하면 새벽에 삽질이 시작되거든요. ㅎㅎ 저도 처음엔 annotation(애노테이션, 리소스에 붙이는 추가 설정) 몇 줄이면 끝날 줄 알았는데, 컨트롤러별 동작 차이 때문에 꽤 헤맸습니다.

    3. Nginx Ingress 비용 vs Traefik 비용, 무엇이 다른가요?

    둘 다 널리 쓰이는 선택지입니다. 여기서는 제품 스펙 경쟁보다 운영비 관점으로 보겠습니다. 특히 Nginx Ingress 비용과 Traefik 비용을 볼 때는 “소프트웨어 가격”보다 “내 환경에서 관리가 쉬운가”를 먼저 봐야 합니다.

    항목 Nginx Ingress Traefik
    초기 진입 난이도 문서와 사례가 많아 참고가 쉬운 편 개념이 깔끔해서 빠르게 익숙해지는 경우가 많음
    설정 방식 Annotation 중심 구성이 자주 보임 동적 설정과 라우팅 개념이 비교적 직관적
    운영 복잡도 세부 옵션이 많아 유연하지만 관리 포인트도 늘어날 수 있음 구조를 잘 잡으면 단순하게 유지하기 좋음
    관찰성 연동 로그/메트릭 수집 패턴이 널리 알려져 있음 대시보드와 라우팅 확인이 편하다고 느끼는 경우가 있음
    비용 관점 핵심 운영자 숙련도에 따라 관리 시간 차이가 큼 단순한 구조에서는 운영 시간 절감 효과를 보기 쉬움

    실제로 써보니까 Nginx 계열은 레퍼런스가 많아서 문제를 검색해 해결하기 좋았어요. 반면 Traefik은 라우팅 구조를 이해하면 구성 자체가 꽤 편하더라고요. 다만 어느 쪽이 더 싸냐는 질문에는 항상 “환경에 따라 다릅니다”가 정답입니다. 예를 들어 팀에 Nginx 경험자가 많으면 그 자체가 비용 절감이거든요. 반대로 새로 시작하는 팀이라면 단순한 설정 흐름이 더 큰 절감 포인트가 될 수 있습니다.

    4. Kubernetes 비용 최적화를 위한 설계 기준

    Kubernetes 비용 최적화는 Ingress Controller 하나만 바꾼다고 끝나지 않습니다. 저는 아래 네 가지를 같이 보시는 걸 추천해요.

    1. Load Balancer 수를 최소화합니다. 가능하면 여러 서비스가 하나의 Ingress 계층을 공유하도록 설계합니다.
    2. 컨트롤러 분리 기준을 명확히 합니다. 내부용과 외부용, 운영계와 개발계를 목적에 따라 나눕니다.
    3. 로그는 필요한 수준만 남깁니다. Access Log(액세스 로그, 요청 기록)를 무조건 장기간 보관하면 저장 비용이 금방 커져요.
    4. TLS와 인증서 자동화를 붙입니다. 수동 갱신은 사람 시간을 태우는 대표 항목입니다.

    혹시 이런 경험 있으신가요? 서비스마다 Load Balancer를 하나씩 붙였다가, 나중에 청구서 보고 뒤늦게 구조를 합치는 경우 말이에요. 저도 비슷한 삽질을 했었습니다. 처음 설계할 때 “분리 = 안전”이라고만 생각했는데, 사실 분리 기준이 명확하지 않으면 비용만 늘어나는 경우가 많더라고요.

    Nginx Ingress 비용과 Traefik 비용 비교를 위한 구성 다이어그램

    Nginx Ingress와 Traefik의 구성 방식 차이를 직관적으로 보여주는 비교 다이어그램 자리입니다.

    5. 실전 구현: 비용을 아끼는 기본 배치 예시

    이번 예시는 외부용 Ingress Controller 하나를 공용으로 두고, 여러 서비스가 호스트 기반 라우팅(host-based routing)을 공유하는 구조입니다. 가장 단순하면서도 비용을 줄이기 쉬운 패턴이거든요.

    5-1. Nginx Ingress 배포 예시

    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

    여기서는 replicaCount(복제 수)를 2로 둬서 최소한의 고가용성을 확보합니다. 무조건 많이 띄운다고 좋은 게 아니고, 트래픽 규모에 맞춰 조정하셔야 해요.

    5-2. Traefik 배포 예시

    helm repo add traefik https://traefik.github.io/charts
    helm repo update
    
    helm install traefik traefik/traefik \
      --namespace traefik \
      --create-namespace \
      --set deployment.replicas=2

    Traefik도 비슷합니다. 핵심은 어떤 컨트롤러를 쓰든 외부 진입 계층을 불필요하게 여러 벌 두지 않는 것입니다.

    5-3. 공용 Ingress 리소스 예시

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

    이 구조의 장점은 명확합니다. 서비스 두 개를 위해 외부 진입점 두 개를 만드는 대신, 하나의 Ingress 계층에서 정리하는 거죠. 단, 보안 요구사항이 다르면 무조건 합치면 안 됩니다. 예를 들어 공개 서비스와 사내 전용 관리 페이지는 분리하는 게 맞으니까요.

    5-4. 리소스 요청값(Resources Requests)도 꼭 보세요

    controller:
      resources:
        requests:
          cpu: 100m
          memory: 128Mi
        limits:
          cpu: 500m
          memory: 512Mi

    이 값은 예시입니다. 중요한 건 “기본값이라 괜찮겠지” 하지 말고 실제 트래픽에 맞게 조정하는 거예요. Ingress Controller가 과하게 큰 리소스를 먹고 있으면 그것도 꾸준한 비용이 됩니다.

    6. ⚠️ 제가 실제로 겪었던 비용 함정과 트러블슈팅

    이 섹션은 진짜 중요합니다. 기능은 되는데 비용이 새는 경우가 꽤 많거든요.

    • 문제 1: 서비스마다 Load Balancer 생성
      처음엔 팀별 독립성을 준다고 Service type LoadBalancer를 여기저기 만들었는데, 나중에 보니 공용 Ingress 하나로 충분한 서비스가 많았어요. 해결은 단순했습니다. HTTP/HTTPS 트래픽은 Ingress로 통합하고, 정말 필요한 TCP/UDP만 별도 노출했습니다.
    • 문제 2: 액세스 로그 과다 수집
      처음엔 다 남겨야 안심되더라고요. 근데 보존 기간이 길어질수록 저장소 비용과 분석 비용이 올라가니까요. 해결은 로그 샘플링(sampling) 또는 보존 기간 분리였습니다.
    • 문제 3: 인증서 갱신 수동 처리
      한두 개일 땐 버텼는데, 도메인이 늘어나니까 관리가 바로 번거로워졌어요. cert-manager 같은 자동화 도구를 붙이는 순간 운영 시간이 확 줄었습니다.
    • 문제 4: 개발/운영 환경을 같은 Ingress에 섞음
      테스트용 설정이 운영 계층에 섞이면서 규칙이 복잡해졌습니다. 결국 운영용과 비운영용 ingressClass를 분리해서 정리했어요.

    처음엔 이게 뭔가 싶었는데, 비용 최적화는 사실 설정 최적화와 거의 같은 말이더라고요. 구조가 단순하면 장애도 줄고, 장애가 줄면 사람 시간도 줄고, 이게 다 비용입니다.

    Ingress Controller 비용 절감을 위한 공용 인그레스 배포와 리소스 최적화 이미지

    Helm 배포, 리소스 요청값, 공용 Ingress 구성을 시각적으로 정리한 이미지 자리입니다.

    7. 검증: 무엇을 확인해야 “정말 아꼈다”고 말할 수 있을까

    구성을 바꿨다고 바로 비용 최적화가 끝난 건 아닙니다. 저는 아래 항목을 꼭 확인합니다.

    1. Load Balancer 개수가 줄었는지 확인합니다.
    2. Ingress Controller Pod 자원 사용량이 안정적인지 봅니다.
    3. 에러율(4xx, 5xx)이 증가하지 않았는지 확인합니다.
    4. TLS 인증서 갱신이 자동으로 도는지 점검합니다.
    5. 로그 저장량이 예상 범위인지 봅니다.

    Prometheus(프로메테우스, 메트릭 수집)나 Grafana(그라파나, 시각화 도구)를 쓰고 계시면 대시보드 하나 만들어두는 걸 추천해요. 드디어 됐다! 싶은 순간이, 숫자로도 확인돼야 진짜 끝이거든요.

    kubectl get ingress -A
    kubectl get svc -A
    kubectl top pod -n ingress-nginx
    kubectl describe ingress -n web shared-web-ingress

    이 정도만 봐도 현재 구조가 과하게 비대해졌는지 감이 옵니다. 특히 서비스 수에 비해 외부 노출 지점이 너무 많다면 다시 설계를 보셔야 합니다.

    Kubernetes 비용 최적화 후 Ingress Controller 비용 변화를 보여주는 대시보드 이미지

    비용 절감 전후의 로드밸런서 수, 자원 사용량, 로그 저장량 변화를 보여주는 대시보드 이미지 자리입니다.

    8. 정리: 어떤 환경에서 어떤 선택이 더 유리할까요?

    짧게 정리하면 이렇습니다.

    • 팀에 Nginx 운영 경험이 많다: Nginx Ingress가 더 낮은 운영비로 이어질 가능성이 커요.
    • 처음부터 단순한 라우팅 구조를 선호한다: Traefik이 관리 시간을 줄여줄 수 있습니다.
    • 서비스 수가 적고 빠르게 시작해야 한다: 공용 Ingress 1계층으로 시작하고, 나중에 분리 기준이 생기면 나누는 편이 낫습니다.
    • 보안 요구사항이 다르다: 비용보다 분리가 우선입니다. 이건 아끼면 안 되는 영역입니다.

    결국 Ingress Controller 비용은 도구 이름보다 구조와 운영 방식에 더 크게 좌우됩니다. 제가 여러 번 겪어보니, 제일 싼 선택은 “공짜 소프트웨어”가 아니라 “운영자가 실수하지 않는 구조”였어요. 이거 진짜 편하더라고요. 장애 대응도 쉬워지고요.

    다음 글에서는 Ingress와 Gateway API(게이트웨이 API, 차세대 트래픽 관리 표준)를 비용과 운영 복잡도 관점에서 이어서 다뤄볼 예정입니다. 관련해서 예전 글의 Service 타입 정리도 같이 보시면 흐름을 잡는 데 도움이 돼요.

    Ingress Controller 비용과 선택 기준을 요약한 인포그래픽

    Nginx Ingress 비용, Traefik 비용, Kubernetes 비용 최적화 포인트를 한 장으로 요약한 인포그래픽 자리입니다.

    9. 자주 묻는 질문(FAQ)

    Q1. 오픈소스 Ingress Controller면 비용 걱정 안 해도 되나요?

    아닙니다. 소프트웨어 사용료와 운영 비용은 별개입니다. 외부 Load Balancer, 노드 자원, 로그 저장, 사람 시간이 같이 들어가거든요.

    Q2. Nginx Ingress 비용과 Traefik 비용 중 어느 쪽이 무조건 더 싼가요?

    무조건 한쪽이 더 싸다고 보긴 어렵습니다. 팀 숙련도, 라우팅 복잡도, 관찰성 구성에 따라 총비용이 달라져요.

    Q3. Kubernetes 비용 최적화에서 가장 먼저 손댈 곳은 어디인가요?

    대부분은 외부 노출 구조 정리부터입니다. 중복된 Load Balancer를 줄이고, 공용 Ingress 계층을 만들면 효과를 체감하기 쉽습니다.

    Q4. 모든 서비스를 하나의 Ingress로 합쳐도 될까요?

    아닙니다. 보안 경계가 다르거나 운영 책임 주체가 다르면 분리해야 해요. 비용 최적화보다 안전한 분리가 먼저입니다.

  • [k8s] Helm Argo CD 마이그레이션: GitOps 전환 전략과 실제 사례

    [k8s] Helm Argo CD 마이그레이션: GitOps 전환 전략과 실제 사례

    Helm Argo CD 마이그레이션: GitOps 전환 전략과 실제 사례

    Helm Argo CD 마이그레이션 이야기는 생각보다 많은 팀이 한 번쯤 부딪히는 주제입니다. 처음엔 Helm(헬름, 쿠버네티스 패키지 매니저)만으로도 배포가 꽤 잘 돌아가거든요. 그런데 서비스가 늘어나고, 환경이 dev/stage/prod로 나뉘고, 누가 언제 어떤 값을 바꿨는지 추적해야 하는 순간부터 슬슬 복잡해집니다. 저도 홈랩과 실무 환경에서 비슷한 과정을 겪었는데요. 처음엔 CI에서 helm upgrade --install만 잘 돌면 끝인 줄 알았는데, 실제로 써보니까 배포 이력 관리, 드리프트(drift, 선언 상태와 실제 상태의 불일치) 감지, 롤백 기준 정리가 점점 더 중요해지더라고요.

    특히 GitOps 전환, Kubernetes CI/CD, Argo CD 도입 사례를 찾는 분들이 공통으로 묻는 게 있습니다. “기존 Helm 차트(chart, 배포 템플릿 묶음)를 버려야 하나요?”인데요. 결론부터 말씀드리면, 꼭 그렇지는 않습니다. 제가 직접 해보니 Helm을 유지한 채 Argo CD(아르고 CD, Git 기반 쿠버네티스 지속 배포 도구)로 제어 레이어만 바꾸는 방식이 가장 현실적이었습니다. 오늘은 그 Helm Argo CD 마이그레이션 전략을 단계별로 풀어보겠습니다.

    Helm 기반 배포에서 Argo CD로 전환되는 전체 GitOps 아키텍처 다이어그램

    기존 CI 중심 배포 흐름에서 GitOps 기반 선언형 운영으로 바뀌는 구조를 한눈에 보여주는 아키텍처 이미지입니다.

    왜 Helm Argo CD 마이그레이션을 고민하게 될까요?

    쉽게 말해 Helm은 배포를 잘하게 해주는 도구이고, Argo CD는 원하는 상태를 계속 맞춰주는 운영 도구에 가깝습니다. 둘은 경쟁 관계라기보다 역할이 다릅니다. 여기서 중요한 포인트! Helm만 쓸 때는 보통 CI 파이프라인이 배포를 직접 실행합니다. 예를 들어 GitHub Actions나 Jenkins에서 이미지 빌드 후 Helm으로 클러스터에 적용하는 식이죠.

    문제는 운영이 길어질수록 이런 질문이 생깁니다.

    • 지금 클러스터 상태가 Git에 있는 선언과 정말 같은가요?
    • 누가 수동으로 kubectl edit 했는지 추적 가능한가요?
    • 운영 환경 값이 언제 바뀌었는지 리뷰 가능한가요?
    • 동일한 앱을 여러 클러스터에 일관되게 배포하고 있나요?

    저도 처음엔 이게 뭔가 싶었는데, 결국 핵심은 배포 자동화보다 운영 상태의 일관성이었습니다. GitOps는 Git 저장소를 단일 진실 공급원(single source of truth, 기준 원본)으로 두고, 클러스터가 그 상태를 따라가게 만드는 방식입니다. 이 구조로 바꾸면 “배포 명령을 누가 실행했는가”보다 “Git에 무엇이 머지되었는가”가 기준이 됩니다.

    Helm과 Argo CD의 역할 차이부터 정리해보겠습니다

    이 부분을 헷갈리면 Helm Argo CD 마이그레이션이 꼬입니다. 저도 초반에 Helm 차트 구조부터 뒤엎으려다가 삽질 좀 했습니다 ㅎㅎ 사실 그렇게까지 할 필요는 없더라고요.

    항목 Helm Argo CD
    주요 역할 애플리케이션 패키징과 템플릿 렌더링 Git 기반 선언 상태 동기화
    배포 방식 명령 실행 중심 상태 감시 및 자동 동기화
    변경 추적 릴리스 이력 중심 Git 커밋과 머지 이력 중심
    운영 적합성 단순 배포에 강함 멀티 환경, 멀티 클러스터 운영에 강함
    함께 사용 여부 가능 Helm 소스를 그대로 사용할 수 있음

    즉, Helm Argo CD 마이그레이션은 “Helm을 버리고 Argo CD로 갈아탄다”가 아니라, “Helm 차트를 유지하면서 Argo CD가 그 차트를 배포하게 만든다”에 더 가깝습니다. 이 관점으로 접근하면 난이도가 확 내려갑니다.

    GitOps 전환 전에 먼저 점검할 것들

    실전 들어가기 전에 아래는 꼭 확인하시는 걸 권장드립니다. 이거 안 보고 들어가면 중간에 엉킵니다.

    1. 현재 Helm 차트 구조: 공용 차트인지, 앱별 차트인지 확인합니다.
    2. values 분리 수준: 환경별 values-dev.yaml, values-prod.yaml 같은 파일이 정리되어 있는지 봅니다.
    3. 시크릿 관리 방식: Secret(시크릿, 민감 정보 리소스)을 Git에 어떻게 둘지 결정해야 합니다.
    4. 배포 주체: 기존 CI가 배포까지 하는지, 아니면 빌드만 하는지 분리합니다.
    5. 클러스터 접근 권한: Argo CD가 대상 네임스페이스(namespace, 논리적 격리 영역)에 배포 가능한지 확인합니다.

    제가 실제로 써보니까 가장 많이 막히는 건 시크릿과 값 파일 구조였습니다. 차트가 지저분해도 일단 굴러가긴 하는데, GitOps 전환 시점에는 그 지저분함이 그대로 운영 리스크로 드러나거든요.

    실전 구현: Helm 기반 배포를 Argo CD로 옮기는 단계

    1. 저장소 구조를 먼저 정리합니다

    보통은 애플리케이션 코드 저장소와 배포 선언 저장소를 분리하거나, 최소한 배포 디렉터리를 명확히 나눕니다. 저는 운영 가시성을 위해 배포 저장소를 분리하는 편을 선호합니다.

    repo/
      charts/
        myapp/
          Chart.yaml
          templates/
          values.yaml
      environments/
        dev-values.yaml
        prod-values.yaml
      argocd/
        myapp-dev.yaml
        myapp-prod.yaml

    기존에 CI에서 직접 실행하던 명령은 대략 이런 형태였을 겁니다.

    helm upgrade --install myapp ./charts/myapp \
      --namespace myapp \
      -f environments/prod-values.yaml

    이제부터는 이 실행 책임을 Argo CD에 넘길 겁니다.

    2. Argo CD Application 리소스를 만듭니다

    Argo CD에서는 Application(애플리케이션, 배포 대상 정의) 리소스로 어떤 Git 경로를 어떤 클러스터와 네임스페이스에 동기화할지 선언합니다.

    apiVersion: argoproj.io/v1alpha1
    kind: Application
    metadata:
      name: myapp-prod
      namespace: argocd
    spec:
      project: default
      source:
        repoURL: https://github.com/example/repo.git
        targetRevision: main
        path: charts/myapp
        helm:
          valueFiles:
            - ../../environments/prod-values.yaml
      destination:
        server: https://kubernetes.default.svc
        namespace: myapp
      syncPolicy:
        automated:
          prune: true
          selfHeal: true
        syncOptions:
          - CreateNamespace=true

    여기서 prune은 Git에서 제거된 리소스를 클러스터에서도 정리해 주는 옵션이고, selfHeal은 누군가 수동 변경한 상태를 다시 선언 상태로 맞춰주는 기능입니다. 드디어 됐다! 싶은 순간이 바로 이 self-heal이 처음 동작할 때였어요. 수동 핫픽스가 다시 원래 선언값으로 돌아오는 걸 보니, 운영 기준이 확실히 잡히더라고요.

    Argo CD Application 리소스와 Helm values 파일 연결 관계를 보여주는 구성 다이어그램

    Argo CD Application이 Git 저장소의 Helm 차트와 환경별 values 파일을 어떻게 참조하는지 설명하는 구성 이미지입니다.

    3. CI/CD 책임을 분리합니다

    Kubernetes CI/CD를 GitOps 스타일로 바꿀 때 중요한 건 CI가 더 이상 클러스터에 직접 배포하지 않도록 하는 겁니다. CI는 이미지 빌드와 테스트, 이미지 태깅(tagging, 버전 표기), 그리고 필요한 경우 values 파일 업데이트까지만 담당하게 만드는 게 깔끔합니다.

    예를 들어 이런 흐름이 됩니다.

    1. 애플리케이션 이미지 빌드
    2. 컨테이너 레지스트리(registry, 이미지 저장소)에 푸시
    3. 배포 저장소의 이미지 태그 업데이트
    4. Git 머지
    5. Argo CD가 변경 감지 후 동기화

    이 구조가 익숙해지면, “배포 버튼”보다 “리뷰된 Git 변경”이 훨씬 신뢰할 만한 기준이 됩니다.

    4. 환경별 선언을 분리합니다

    dev와 prod를 하나의 values 파일로 억지로 버티면 나중에 꼭 터집니다. 환경별로 분리하고, 차이가 큰 값은 명시적으로 드러내는 편이 좋습니다.

    image:
      repository: ghcr.io/example/myapp
      tag: "1.2.3"
    
    replicaCount: 3
    
    ingress:
      enabled: true
      host: myapp.example.com

    여기서 중요한 건 애플리케이션 코드는 이미지로, 운영 설정은 Git 선언으로 나누는 감각입니다. 이게 정리되면 배포 사고가 꽤 줄어듭니다.

    ⚠️ Helm Argo CD 마이그레이션 중 자주 겪는 문제와 해결법

    이 섹션은 정말 경험담 위주입니다. 책처럼 깔끔하게 흘러가지 않더라고요.

    문제 1. 기존 Helm 릴리스와 Argo CD가 충돌합니다

    처음 Argo CD가 같은 리소스를 관리하려고 들어오면 annotation(어노테이션, 부가 메타데이터)이나 라벨 기준이 달라 충돌하는 경우가 있습니다. 기존 릴리스를 갑자기 지우기보다, 먼저 현재 리소스와 선언 상태가 최대한 같도록 맞춘 뒤 전환하는 게 안전합니다.

    • 기존 Helm values와 Argo CD가 참조할 values를 동일하게 맞춥니다.
    • 리소스 이름 규칙이 달라지지 않도록 차트 값을 점검합니다.
    • 전환 시점에는 변경 폭을 최소화합니다.

    문제 2. 값 파일 경로가 꼬입니다

    Helm CLI에서는 되던 상대 경로가 Argo CD에서 안 먹는 경우가 있습니다. 저장소 구조를 너무 복잡하게 잡으면 이런 문제가 잦아집니다. 저도 처음엔 공용 차트, 서브모듈, 환경 저장소 분리까지 한 번에 밀어붙였다가 경로 지옥을 봤습니다. 실제로는 처음 전환은 단순한 구조가 정답이더라고요.

    문제 3. Secret 관리가 애매합니다

    이건 정말 팀 표준이 필요합니다. GitOps 전환을 한다고 해서 Secret 평문을 Git에 넣으면 안 되겠죠. 외부 시크릿 연동이나 별도 암호화 워크플로를 먼저 정하는 편이 좋습니다. 확실하지 않은 구성은 전환 범위에서 잠시 분리하는 것도 방법입니다.

    문제 4. 자동 동기화가 부담스럽습니다

    prod에서 바로 automated sync를 켜는 게 불안할 수 있습니다. 저도 그랬습니다. 그래서 처음엔 dev만 자동 동기화하고, prod는 수동 동기화로 시작한 뒤 점진적으로 넘겼습니다. 이런 식의 단계적 도입이 Argo CD 도입 사례에서 꽤 현실적인 패턴입니다.

    검증: 전환이 제대로 됐는지 어떻게 확인할까요?

    Helm Argo CD 마이그레이션이 끝났다고 말하려면 최소한 아래는 확인해야 합니다.

    1. Git 변경이 Argo CD에서 감지되는지 확인합니다.
    2. Application 상태가 Synced와 Healthy로 보이는지 확인합니다.
    3. 수동 변경을 넣었을 때 self-heal이 동작하는지 봅니다.
    4. 불필요한 리소스 삭제 시 prune이 예상대로 동작하는지 확인합니다.
    kubectl get applications -n argocd
    kubectl get pods -n myapp
    kubectl describe application myapp-prod -n argocd

    실제로 써보니까 여기서 가장 만족도가 높았던 건 운영 가시성이었습니다. 예전에는 “배포는 된 것 같은데 왜 상태가 이렇지?” 하고 CI 로그, Helm 이력, 클러스터 상태를 따로 봐야 했거든요. Argo CD로 넘어오니 적어도 선언 기준과 실제 상태를 같은 화면에서 보는 감각이 생겼습니다. 이거 진짜 편하더라고요.

    Argo CD 대시보드에서 Synced와 Healthy 상태를 확인하는 운영 화면 스타일의 일러스트

    Git 변경 후 애플리케이션이 Synced, Healthy 상태로 안정화된 모습을 보여주는 검증용 이미지입니다.

    전환 결과: 무엇이 좋아졌고, 무엇은 여전히 남을까요?

    좋아진 점은 명확합니다.

    • 배포 기준이 Git으로 통일됩니다.
    • 운영 변경 이력이 Pull Request 리뷰와 함께 남습니다.
    • 드리프트 감지가 쉬워집니다.
    • 멀티 환경 운영이 예측 가능해집니다.

    반대로 남는 과제도 있습니다.

    • 차트 품질이 나쁘면 GitOps로 옮겨도 그대로 드러납니다.
    • 시크릿 전략은 별도로 정교하게 설계해야 합니다.
    • 운영팀과 개발팀이 Git 변경 책임을 어떻게 나눌지 합의가 필요합니다.

    즉, GitOps 전환은 마법이 아니라 운영 원칙을 코드와 프로세스로 고정하는 작업에 가깝습니다. 그래서 도구 도입보다 팀 합의가 더 중요할 때도 많습니다.

    실무에서 추천하는 Helm Argo CD 마이그레이션 전략

    혹시 이런 경험 있으신가요? 한 번에 다 바꾸려다가 더 복잡해지는 경우요. 저는 그래서 아래 순서를 추천드립니다.

    1. 기존 Helm 차트와 values 파일부터 정리합니다.
    2. dev 환경에만 Argo CD를 먼저 붙입니다.
    3. CI에서 배포 단계를 제거하고 선언 업데이트만 남깁니다.
    4. 운영 검증 후 prod는 수동 sync로 시작합니다.
    5. 문제 없으면 자동 동기화 범위를 넓힙니다.

    이 접근은 보수적이지만 실패 비용이 낮습니다. 특히 Kubernetes CI/CD를 운영 중인 팀이라면, “배포 명령을 옮긴다”가 아니라 “운영 제어권을 Git으로 옮긴다”는 관점이 중요합니다.

    정리와 FAQ

    정리하면, Helm Argo CD 마이그레이션의 핵심은 기존 Helm 자산을 최대한 살리면서 GitOps 전환을 달성하는 겁니다. 저도 처음엔 Helm을 걷어내야 하나 고민했었는데, 실제로는 Helm을 잘 유지하는 쪽이 훨씬 현실적이었습니다. Argo CD 도입 사례를 보면 멋진 아키텍처보다도, 작은 범위에서 안전하게 시작한 팀이 오래 갑니다.

    다음 글에서는 ApplicationSet(ApplicationSet, 여러 Application을 템플릿으로 관리하는 기능)이나 멀티 클러스터 운영 패턴도 다뤄볼 예정입니다. 이전 글에서 다룬 Ingress(인그레스, 외부 트래픽 진입점)와 네임스페이스 분리 전략을 같이 보시면 더 이해가 쉬우실 겁니다.

    자주 묻는 질문

    • Q. Helm을 완전히 버려야 하나요?
      A. 아닙니다. Helm 차트를 그대로 두고 Argo CD가 이를 배포하게 만드는 방식이 일반적입니다.
    • Q. 자동 동기화는 바로 켜도 될까요?
      A. dev부터 켜고 prod는 수동으로 시작하는 편이 안전했습니다.
    • Q. GitOps 전환의 첫 번째 우선순위는 뭔가요?
      A. values 구조 정리와 시크릿 관리 기준 수립입니다.
    Helm 단독 배포와 Argo CD 기반 GitOps 운영의 차이를 요약한 비교 인포그래픽

    Helm 중심 배포와 Argo CD 기반 GitOps 운영의 차이, 장점, 도입 순서를 한 장으로 정리한 요약 이미지입니다.

    마지막으로 한 줄만 덧붙이면, GitOps는 도구를 바꾸는 프로젝트가 아니라 운영 습관을 바꾸는 작업입니다. 그래서 천천히, 하지만 기준은 단단하게 잡고 가는 게 맞습니다. 저도 그렇게 돌고 돌아서 정착했습니다.

  • [k8s] OpenTelemetry K8s 마이그레이션: Prometheus 공존 전략과 함정

    [k8s] OpenTelemetry K8s 마이그레이션: Prometheus 공존 전략과 함정

    OpenTelemetry K8s 마이그레이션: Prometheus 공존 전략과 함정

    요즘 Prometheus에서 OpenTelemetry K8s로 마이그레이션을 고민하는 분들이 정말 많아요. 저도 홈랩이랑 사내 Kubernetes(쿠버네티스, 컨테이너 오케스트레이션) 환경을 굴리면서 비슷한 고민을 꽤 오래 했거든요. 기존 Prometheus(프로메테우스, 메트릭 수집 시스템)는 익숙하고 강력한데, 로그와 트레이스(trace, 분산 추적)까지 한 방향으로 정리하려고 보면 슬슬 한계가 보이더라고요. 특히 K8s 모니터링이 메트릭 중심으로만 굳어져 있으면 장애 원인 추적이 길어지고, 팀마다 도구가 갈라지는 순간 운영 피로도가 확 올라갑니다.

    처음엔 저도 “Prometheus 잘 쓰고 있는데 굳이 바꿔야 하나?” 싶었어요. 근데 실제로 OpenTelemetry(오픈텔레메트리, 통합 관측성 표준)를 붙여보니, 이건 단순히 수집기 하나 바꾸는 작업이 아니더군요. 옵저버빌리티 전환의 기준을 다시 세우는 일에 가깝습니다. 그래서 이번 글에서는 OpenTelemetry K8s 마이그레이션을 할 때 어떤 순서로 접근해야 덜 망가지는지, 어디서 많이 삽질하는지, 그리고 Prometheus를 완전히 버리기보다 어떻게 현실적으로 공존시킬지 제 경험 기준으로 풀어볼게요.

    Prometheus 중심 구조에서 OpenTelemetry Collector 중심 구조로 전환되는 전체 흐름을 한눈에 보여주는 이미지입니다.

    왜 지금 OpenTelemetry K8s 마이그레이션 이야기가 많을까

    쉽게 말해, 예전에는 메트릭(metrics, 수치형 운영 데이터)만 잘 봐도 운영이 됐어요. CPU, 메모리, 요청 수, 에러율 정도만 봐도 웬만한 장애는 잡혔거든요. 그런데 마이크로서비스가 늘어나고, 서비스 간 호출이 복잡해지면 그걸로는 부족합니다. 에러율이 올라간 건 알겠는데 어디서부터 꼬였는지를 찾는 시간이 너무 오래 걸려요.

    OpenTelemetry는 바로 이 지점을 건드립니다. 메트릭, 로그(logs, 로그 데이터), 트레이스까지 한 모델로 가져가려고 하죠. 여기서 중요한 포인트가 하나 있습니다. OpenTelemetry는 Prometheus의 완전한 대체재라기보다, 수집과 전송의 표준화 계층으로 이해하는 게 맞습니다. 저도 처음엔 이걸 헷갈려서 설계를 잘못 잡았었는데, Prometheus가 잘하던 영역과 OpenTelemetry가 잘하는 영역이 미묘하게 달라요.

    • Prometheus: pull 기반 메트릭 수집, PromQL(프로메스큐엘, Prometheus 질의 언어), 알림 규칙에 강합니다.
    • OpenTelemetry: 메트릭, 로그, 트레이스의 표준화된 수집과 변환, 다양한 백엔드 전송에 강해요.
    • Collector: 수집기이자 중간 파이프라인입니다. 여기서 필터링, 배치, 리소스 태깅을 처리하죠.

    그래서 Prometheus에서 OpenTelemetry K8s로 마이그레이션은 보통 “Prometheus 삭제”가 아니라, “수집 구조를 Collector 중심으로 재편하고 필요한 부분만 점진 전환”으로 가야 안전합니다.

    개념 먼저 정리: Prometheus와 OpenTelemetry는 어떻게 다른가

    저도 처음엔 용어가 너무 많아서 헷갈렸어요. Operator(오퍼레이터, 쿠버네티스 앱 관리 자동화), Exporter(익스포터, 특정 시스템 메트릭 노출기), Receiver(리시버, 수집 입력), Processor(프로세서, 데이터 가공기), Exporter는 또 OpenTelemetry 안에서도 다른 뜻으로 쓰이거든요. 여기서 한 번 깔끔하게 정리해볼게요.

    항목 Prometheus 중심 구조 OpenTelemetry 중심 구조
    수집 방식 주로 pull pull + push 혼합 가능
    주요 대상 메트릭 메트릭, 로그, 트레이스
    표준화 도구 중심 벤더 중립 표준
    가공 처리 제한적 Collector에서 풍부하게 처리
    전환 난이도 익숙함 설계 이해가 필요해요

    쉽게 말해 이런 느낌입니다.

    1. Prometheus는 메트릭 수집과 조회에 특화된 도구예요.
    2. OpenTelemetry는 데이터를 어떻게 모으고, 어떤 속성(attribute, 속성값)을 붙이고, 어디로 보낼지 표준화합니다.
    3. Kubernetes 환경에서는 OpenTelemetry Collector가 사실상 핵심이죠.

    여기서 독자분들이 가장 많이 오해하는 부분이 있어요. OpenTelemetry를 도입한다고 해서 PromQL 기반 대시보드나 알림을 바로 버릴 필요는 없습니다. 저도 이 부분을 무리하게 한 번에 바꾸려다가 롤백했었어요. 기존 운영 체계를 존중하면서 단계별로 옮겨야 합니다.

    마이그레이션 전에 먼저 결정해야 할 4가지

    실제로 써보니까, 기술보다 먼저 정해야 하는 건 운영 기준이었어요. 이걸 안 정하면 설정은 돌아가도 팀이 힘들어집니다.

    1. 최종 백엔드가 무엇인지 정하기

    Collector는 중간 계층이죠. 결국 메트릭과 트레이스를 어디에 저장하고 조회할지 정해야 해요. 기존 Prometheus를 유지할지, OTLP(OTLP, OpenTelemetry Protocol) 수신 가능한 백엔드를 붙일지, 로그 저장소와 어떻게 나눌지 먼저 결정해야 합니다.

    2. 어떤 신호(signal)부터 옮길지 정하기

    메트릭부터 갈지, 애플리케이션 트레이스부터 붙일지, 노드/파드 수준 K8s 모니터링부터 정리할지 우선순위를 잡아야 해요. 제 경험상 가장 무난한 순서는 이렇습니다.

    1. 인프라 메트릭 유지
    2. Collector 배포
    3. 애플리케이션 트레이스 추가
    4. 메트릭 라우팅 정리
    5. 로그 연계 검토

    3. 라벨(label)과 리소스 속성(attribute) 기준 통일

    이게 진짜 중요합니다. Prometheus 쪽 label과 OpenTelemetry 쪽 resource attribute가 섞이면 쿼리와 대시보드가 다 꼬여요. namespace, pod, service, cluster 같은 공통 키를 먼저 합의하세요.

    4. 샘플링(sampling, 일부만 수집) 전략 정하기

    트레이스는 전부 모으면 부담이 커져요. 특히 K8s에서 서비스 수가 많아지면 수집량이 금방 늘어요. 처음부터 100% 수집을 고집하기보다, 핵심 서비스 기준으로 시작하는 게 현실적입니다.

    OpenTelemetry K8s 마이그레이션에서 Collector 구성도

    DaemonSet 또는 Deployment 형태의 Collector가 어떤 데이터를 받고 어디로 보내는지 설명하는 구성 이미지입니다.

    실전 구현: Kubernetes에 OpenTelemetry Collector 배포하기

    이제 실제 구성을 봐볼게요. 여기서는 가장 많이 쓰는 패턴인 Collector를 Kubernetes에 배포하고, Prometheus scrape 대상 일부를 Collector로 옮기는 방식을 기준으로 설명하겠습니다. 특정 배포 도구를 강제하진 않을게요. Helm(헬름, 쿠버네티스 패키지 매니저)을 쓰든 매니페스트를 쓰든 핵심 구조는 비슷하거든요.

    1. 기본 Collector 설정 예시

    아래 예시는 개념 설명용으로 단순화한 구성입니다. 실제 운영에서는 인증, 리소스 제한, 백엔드 주소, 필터링 규칙을 환경에 맞춰 넣어야 해요.

    receivers:
      otlp:
        protocols:
          grpc:
          http:
      prometheus:
        config:
          scrape_configs:
            - job_name: 'kubernetes-pods'
              kubernetes_sd_configs:
                - role: pod
              relabel_configs:
                - source_labels: [__meta_kubernetes_pod_annotation_prometheus_io_scrape]
                  action: keep
                  regex: true
    
    processors:
      batch:
      memory_limiter:
        check_interval: 1s
        limit_percentage: 75
        spike_limit_percentage: 15
      resource:
        attributes:
          - key: deployment.environment
            value: homelab
            action: upsert
    
    exporters:
      debug: {}
      otlp:
        endpoint: otel-backend.example.local:4317
        tls:
          insecure: true
    
    service:
      pipelines:
        metrics:
          receivers: [prometheus, otlp]
          processors: [memory_limiter, batch, resource]
          exporters: [debug, otlp]
        traces:
          receivers: [otlp]
          processors: [memory_limiter, batch, resource]
          exporters: [debug, otlp]

    여기서 핵심은 세 가지예요.

    • receivers: 데이터를 어디서 받을지 정의합니다.
    • processors: 배치 처리, 메모리 제한, 공통 속성 추가를 담당해요.
    • exporters: 어디로 보낼지 정의합니다.

    처음엔 debug exporter를 꼭 켜두세요. 저도 이걸 안 켜고 한참 헤맸어요. 데이터가 안 보일 때 수집이 안 되는 건지, 가공이 잘못된 건지, 전송이 막힌 건지 구분이 안 되거든요.

    2. Kubernetes 리소스 예시

    apiVersion: apps/v1
    kind: Deployment
    metadata:
      name: otel-collector
      namespace: observability
    spec:
      replicas: 2
      selector:
        matchLabels:
          app: otel-collector
      template:
        metadata:
          labels:
            app: otel-collector
        spec:
          containers:
            - name: otel-collector
              image: otel/opentelemetry-collector:latest
              args:
                - "--config=/conf/otel-collector-config.yaml"
              ports:
                - containerPort: 4317
                - containerPort: 4318
              volumeMounts:
                - name: config
                  mountPath: /conf
          volumes:
            - name: config
              configMap:
                name: otel-collector-config

    실무에서는 DaemonSet(데몬셋, 노드마다 하나씩 배포)도 많이 써요. 노드 단위 수집이나 에이전트 성격이 강하면 DaemonSet이 자연스럽고, 중앙 수집기면 Deployment가 편합니다. 둘 중 뭐가 정답이다, 이건 없어요. 수집 대상과 트래픽 경로를 보고 정해야 합니다.

    3. 애플리케이션에서 OTLP로 보내기

    애플리케이션이 OpenTelemetry SDK를 지원한다면 OTLP endpoint를 Collector로 맞추면 돼요. 언어별 설정은 다르지만 원리는 비슷합니다.

    export OTEL_EXPORTER_OTLP_ENDPOINT=http://otel-collector.observability.svc.cluster.local:4318
    export OTEL_SERVICE_NAME=my-api
    export OTEL_RESOURCE_ATTRIBUTES=deployment.environment=prod,k8s.namespace.name=default

    여기서 중요한 포인트! 애플리케이션마다 서비스 이름 규칙이 제각각이면 나중에 대시보드가 정말 난장판이 돼요. 저도 처음엔 서비스명이 코드 저장소 이름, 배포명, 제품명으로 다 달라서 정리하느라 고생했네요 ㅎㅎ

    Prometheus를 바로 걷어내지 말고 공존 기간을 두세요

    이건 경험에서 나온 조언이에요. 옵저버빌리티 전환을 할 때 가장 위험한 패턴이 “이번 분기 안에 다 바꾸자”예요. 겉보기엔 속도가 나는데, 운영팀만 죽어납니다. 알람 공백이 생기고, 대시보드 신뢰도가 떨어지고, 결국 새 체계가 욕을 먹게 돼요.

    제가 추천하는 건 다음과 같은 병행 운영이에요.

    1. 기존 Prometheus 알림과 대시보드는 유지합니다.
    2. OpenTelemetry Collector로 동일한 메트릭 일부를 병행 수집해요.
    3. 핵심 서비스에만 트레이스를 먼저 붙입니다.
    4. 장애 대응 시 새 데이터가 실제로 도움이 되는지 검증해요.
    5. 검증이 끝난 뒤에만 대시보드와 알림을 전환합니다.

    이 방식이 느려 보이지만, 실제로는 제일 빨아요. 왜냐면 롤백 비용이 낮거든요.

    ⚠️ 실제로 많이 겪는 함정과 트러블슈팅

    여기부터는 제가 직접 해보니 자주 부딪히는 문제들이에요. OpenTelemetry K8s 마이그레이션에서 대부분 여기서 시간 써요.

    1. 라벨과 속성 이름이 서로 달라서 쿼리가 깨짐

    Prometheus의 `job`, `instance`, `namespace`와 OpenTelemetry resource attribute가 1:1로 안 맞는 경우가 많아요. 그 결과 대시보드가 비어 보이거나, 같은 서비스가 여러 이름으로 나뉘어 보이죠.

    해결: 공통 키 매핑 표를 먼저 만드세요. 운영팀, 개발팀이 같이 보는 문서가 있어야 해요.

    2. Collector가 만능인 줄 알고 모든 변환을 몰아넣음

    처음엔 저도 그랬어요. 필터링, 이름 변경, 태깅, 라우팅, 샘플링을 Collector 하나에 계속 추가하다 보면 설정이 금방 복잡해져요. 그러면 장애 시 디버깅이 힘들어요.

    해결: Processor는 최소한으로 시작하고, 꼭 필요한 것만 추가하세요. 먼저 배치, 메모리 제한, 공통 속성 정도로 출발하는 게 좋아요.

    3. 트레이스 수집량 폭증

    마이크로서비스 호출이 많으면 생각보다 데이터가 빠르게 늘어나요. 특히 개발 환경에서 무심코 다 켜두면 저장소와 네트워크 부담이 커질 수 있어요.

    해결: 전면 수집보다 핵심 API부터 시작하세요. 에러 구간 위주로 샘플링하거나, 특정 네임스페이스부터 적용하는 식이 현실적입니다.

    4. Prometheus scrape와 OTLP push를 동시에 붙였는데 중복 메트릭 발생

    이거 꽤 흔해요. 같은 애플리케이션 메트릭이 Prometheus scrape 경로와 OTLP 경로 둘 다 들어와서 그래프가 이상해질 수 있어요.

    해결: 신호별 소유권을 정하세요. 예를 들어 애플리케이션 메트릭은 OTLP, 인프라 메트릭은 기존 scrape처럼 명확히 나누는 거예요.

    5. Kubernetes 메타데이터가 기대만큼 안 붙음

    pod, node, namespace 정보가 자동으로 다 붙을 거라 기대했는데, 실제로는 배포 방식이나 권한 설정에 따라 부족할 수 있어요.

    해결: 서비스 계정 권한, 리소스 탐지 방식, Collector 배치 위치를 같이 점검하세요. 특히 K8s 메타데이터 enrich(강화)가 안 되면 트러블슈팅 난이도가 확 올라가요.

    OpenTelemetry K8s 마이그레이션 함정과 트러블슈팅 이미지

    실제 마이그레이션에서 자주 발생하는 문제 지점을 강조한 트러블슈팅 시각 자료입니다.

    검증은 이렇게 하시면 돼요

    구성이 적용됐다고 끝이 아니에요. 드디어 됐다! 싶었는데 실제로는 일부 서비스만 보이고, 속성 누락이 있고, 중복 수집이 있는 경우가 많거든요. 저는 아래 순서로 검증해요.

    1. Collector 로그에서 수신과 전송 여부를 확인합니다.
    2. 기존 Prometheus 수치와 새 파이프라인 수치를 큰 흐름 기준으로 비교해요.
    3. 트레이스 한 건을 선택해서 서비스 간 호출 연결이 보이는지 확인합니다.
    4. namespace, pod, service 이름이 일관되게 붙는지 확인해요.
    5. 장애 상황을 일부러 만들어서 실제 분석 시간이 줄어드는지 봅니다.

    간단한 확인 명령 예시는 이런 식이에요.

    kubectl get pods -n observability
    kubectl logs -n observability deploy/otel-collector
    kubectl port-forward -n observability deploy/otel-collector 4318:4318

    그리고 Prometheus 메트릭을 계속 보는 환경이라면, 병행 구간에서는 아래 체크리스트가 유용해요.

    • 기존 알림 규칙이 그대로 동작하는가
    • OpenTelemetry 경로로 들어온 메트릭의 이름과 단위가 일관적인가
    • 트레이스에서 서비스 호출 병목 구간이 눈에 띄는가
    • 운영자가 새 쿼리 모델을 이해할 수 있는가

    이 검증을 해보면 단순히 “데이터가 들어온다”보다 중요한 게 보여요. 문제를 더 빨리 찾을 수 있느냐가 진짜 기준이거든요.

    OpenTelemetry K8s 마이그레이션 결과 대시보드 이미지

    메트릭 그래프와 분산 추적 결과가 함께 보이는 검증 단계의 대시보드 이미지를 배치할 위치입니다.

    결과적으로 무엇이 좋아졌나

    제가 느낀 가장 큰 변화는 장애 대응 대화가 바뀌었다는 점이에요. 예전에는 “CPU는 정상인데 왜 느리지?”에서 한참 머물렀다면, OpenTelemetry 도입 뒤에는 “어느 서비스 호출에서 지연이 시작됐는지”를 더 빨리 볼 수 있었어요. 이거 진짜 편하더라고요.

    물론 Prometheus 자체의 가치가 줄어든 건 아니에요. 여전히 K8s 모니터링에서 메트릭은 기본 중의 기본이고, 알림과 시계열 분석은 아주 중요합니다. 다만 옵저버빌리티 전환 관점에서는 메트릭 중심 운영에서 컨텍스트 중심 운영으로 넘어가는 느낌이 분명히 있어요.

    비교 항목 전환 전 전환 후
    장애 감지 메트릭 경보 중심 메트릭 + 트레이스 조합
    원인 추적 로그를 따로 뒤짐 서비스 호출 흐름 기준 탐색
    운영 표준 도구별 설정 분산 Collector 중심 파이프라인 정리
    확장성 메트릭 중심 다양한 신호 통합 가능

    정리: 성공 전략은 “점진 전환”입니다

    이번 글의 핵심을 한 줄로 줄이면 이거예요. Prometheus에서 OpenTelemetry K8s로 마이그레이션할 때는 도구 교체가 아니라 운영 모델 전환으로 접근해야 해요. 저도 처음엔 설정 파일만 맞추면 끝날 줄 알았는데, 실제로는 라벨 체계, 샘플링 기준, 공존 기간 설계가 훨씬 중요했어요.

    정리해보면 성공 전략은 이래요.

    • Prometheus를 바로 버리지 말고 공존 기간을 둬요.
    • 메트릭보다 먼저 데이터 모델과 속성 체계를 정리합니다.
    • Collector는 단순하게 시작하고 점진적으로 확장해요.
    • 트레이스는 핵심 서비스부터 붙입니다.
    • 검증 기준은 “수집 여부”가 아니라 “문제 해결 속도”로 잡으세요.

    혹시 지금 OpenTelemetry 도입을 검토 중인데 너무 복잡해 보여서 멈춰 계셨다면, 저라면 제일 먼저 Collector 하나 띄우고, 가장 중요한 서비스 한 개에만 트레이스 붙여보는 것부터 권할게요. 그 한 번이 전체 방향을 많이 보여주거든요.

    다음 글에서는 Kubernetes 환경에서 OpenTelemetry Collector를 DaemonSet과 Deployment 중 어떤 패턴으로 나누는 게 좋은지, 그리고 운영 중 설정이 비대해질 때 어떻게 분리하는지 다뤄볼 거예요. 이전 글에서 다룬 Prometheus 알림 설계 원칙과 같이 보시면 훨씬 연결이 잘 될 거라고 생각해요.

    Prometheus와 OpenTelemetry 점진 전환 전략 요약 이미지

    마이그레이션 단계, 주의사항, 검증 포인트를 한 장으로 정리한 요약 이미지가 들어갈 위치입니다.

    자주 묻는 질문

    Q1. OpenTelemetry를 도입하면 Prometheus는 없어도 되나요?

    반드시 그렇진 않아요. 특히 기존 PromQL 쿼리, 대시보드, 알림 체계가 잘 돌아간다면 당분간 공존하는 편이 안전합니다.

    Q2. K8s 모니터링은 메트릭부터 옮겨야 하나요?

    상황에 따라 다르지만, 보통은 인프라 메트릭은 유지하고 애플리케이션 트레이스부터 붙여보는 방식이 체감 효과가 커요.

    Q3. 가장 큰 함정은 무엇인가요?

    제가 보기엔 라벨과 속성 체계를 통일하지 않고 시작하는 거예요. 나중에 대시보드와 검색 조건이 전부 어긋나거든요.

    Q4. 옵저버빌리티 전환의 성공 기준은 뭔가요?

    새 도구가 예뻐 보이는지가 아니라, 장애가 났을 때 원인 찾는 시간이 줄었는지가 핵심이에요. 결국 운영은 그걸로 평가받거든요.

  • [Kubernetes] Cilium vs Calico 네트워킹 성능 실측 비교

    [Kubernetes] Cilium vs Calico 네트워킹 성능 실측 비교

    [Kubernetes] Cilium vs Calico 네트워킹 성능 실측 비교

    Cilium vs Calico 이야기는 Kubernetes CNI를 운영하는 분들이 한 번쯤 꼭 부딪히는 주제입니다. 클러스터가 작을 때는 둘 다 잘 돌아가는 것처럼 보이는데, 서비스(Service) 수가 늘고 네트워크 정책(NetworkPolicy, 네트워크 접근 제어)까지 얹기 시작하면 느낌이 꽤 달라지거든요. 저도 처음엔 ‘어차피 Pod to Pod 통신만 되면 되는 거 아닌가?’ 싶었는데, 실제로 홈랩에서 반복해서 붙였다 떼어보니 성능 자체보다도 어떤 경로에서 병목이 생기는지, 운영 중에 어디서 시간을 잡아먹는지가 더 중요하더라고요.

    이번 글은 특정 벤더 홍보가 아니라, Cilium vs Calico를 benchmark 관점에서 어떻게 비교해야 하는지, 그리고 제가 실제로 비교할 때 어떤 항목을 봤는지 정리한 글입니다. 숫자를 지어내서 화려하게 쓰는 대신, 실제로 재현 가능한 측정 방법과 해석 포인트 위주로 풀어보겠습니다. 혹시 지금 Kubernetes CNI 교체를 고민 중이시라면, 여기서 중요한 포인트를 먼저 잡고 가시면 삽질을 꽤 줄일 수 있어요.

    홈랩 Kubernetes 클러스터에서 Cilium과 Calico의 데이터 경로를 비교하는 전체 개요 이미지입니다.

    Kubernetes CNI에서 Cilium과 Calico를 왜 따로 봐야 할까요

    쉽게 말해 CNI(Container Network Interface, 컨테이너 네트워크 인터페이스)는 Pod가 네트워크에 붙는 방식을 정하는 레이어입니다. 그런데 운영해보면 단순히 IP 붙이는 수준에서 끝나지 않아요. 서비스 라우팅, 네트워크 정책, 트래픽 가시성, 디버깅 도구, 커널 의존성까지 같이 따라옵니다.

    Calico는 오래전부터 많이 써온 선택지라 운영 경험치가 쌓여 있고, 라우팅과 정책 측면에서 익숙한 분들이 많습니다. 반면 Cilium은 eBPF(이비피에프, 커널 내부에서 안전하게 패킷 처리 로직을 실행하는 기술)를 적극 활용해서 데이터 경로를 다르게 가져가는 게 핵심이죠. 처음엔 이게 뭔가 싶었는데, 관찰성(Observability, 트래픽을 들여다보는 능력) 쪽은 확실히 인상적이었어요.

    여기서 오해하면 안 되는 게 하나 있습니다. “무조건 Cilium이 빠르다” 혹은 “Calico는 오래돼서 느리다” 같은 식으로 단정하면 안 됩니다. 실제 Kubernetes CNI 성능 비교는 트래픽 패턴에 따라 결과가 달라집니다. Pod 간 단순 대역폭, NodePort 경유, Service 체인, NetworkPolicy 적용 여부, 암호화 사용 여부 같은 조건이 바뀌면 체감이 꽤 달라지거든요.

    Cilium vs Calico 핵심 차이 정리

    항목 Cilium Calico
    핵심 데이터 경로 eBPF 기반 iptables 기반 또는 eBPF dataplane 선택 가능
    강점 관찰성, 정책 처리, 서비스 트래픽 가시화 성숙한 운영 사례, 익숙한 구성, 다양한 환경 적응력
    운영 난이도 커널과 기능 이해가 필요함 상대적으로 익숙한 운영 패턴
    디버깅 관점 Hubble 등 생태계가 강점 전통적인 네트워크 관점에서 접근이 쉬움
    비교 포인트 정책 많을 때, 관찰성 중요할 때 안정적 표준 운영, 기존 경험 활용 시

    제가 직접 해보니, Kubernetes CNI 성능 비교에서 제일 많이 놓치는 부분이 순수 처리량(throughput)만 보고 결론 내리는 것이었습니다. 실제 운영에서는 지연(latency), 정책 적용 후 변화, 장애 났을 때 추적 가능성까지 같이 봐야 하더라고요. 특히 멀티테넌트 비슷하게 네임스페이스를 나눠 쓰는 환경이면 더 그렇습니다.

    비교 전에 먼저 정해야 할 질문

    • 단순 Pod to Pod 성능이 궁금한지
    • NetworkPolicy 적용 후 성능 변화가 궁금한지
    • Service 트래픽까지 포함한 실제 운영 경로가 궁금한지
    • 관찰성과 디버깅 속도를 포함해 평가할지

    이 질문부터 정리하고 들어가면 benchmark 해석이 훨씬 깔끔해집니다.

    실전 비교 환경 구성: 같은 조건으로 맞추는 게 먼저입니다

    여기서 제일 중요한 건 두 CNI를 최대한 같은 조건에서 비교하는 겁니다. CPU, 메모리, 커널, Kubernetes 버전, MTU, 노드 수, Pod 수가 다르면 결과가 섞여버립니다. 저도 초반에는 클러스터를 급하게 갈아엎다가 조건이 달라져서 다시 측정했었어요. 삽질 꽤 했습니다 ㅎㅎ

    1. 동일한 노드 사양으로 테스트 클러스터를 준비합니다.
    2. 한 번에 하나의 CNI만 설치합니다.
    3. 테스트 워크로드는 똑같이 유지합니다.
    4. 측정 항목을 미리 고정합니다. 예: 대역폭, 지연, 정책 적용 전후, Service 경유 성능.

    아래는 Helm(헬름, 쿠버네티스 패키지 매니저) 기준으로 Cilium vs Calico 비교 환경을 잡을 때 자주 쓰는 흐름입니다.

    helm repo add cilium https://helm.cilium.io/
    helm repo update
    
    helm install cilium cilium/cilium \
      --namespace kube-system \
      --set kubeProxyReplacement=false

    ⚠️ 주의: 위 설정의 kubeProxyReplacement=false는 kube-proxy를 계속 사용한다는 뜻입니다. Cilium의 eBPF 이점을 제대로 보려면 --set kubeProxyReplacement=strict 또는 =probe로 설정하는 게 낫습니다. 비교 목적이라면 두 CNI 모두 같은 서비스 경로 구성을 유지해야 한다는 점을 잊지 마세요.

    helm repo add projectcalico https://docs.tigera.io/calico/charts
    helm repo update
    
    helm install calico projectcalico/tigera-operator \
      --namespace tigera-operator \
      --create-namespace

    환경에 따라 설치 방식은 달라질 수 있습니다. 중요한 건 설치 명령보다도 비교 조건을 고정하는 습관입니다.

    동일 조건의 Kubernetes CNI 테스트 환경에서 Cilium vs Calico를 비교하는 구성 이미지

    동일한 노드 조건에서 Cilium과 Calico를 각각 분리해 테스트하는 구성 다이어그램입니다.

    Benchmark 워크로드 만들기: iperf3와 정책 테스트를 같이 봐야 합니다

    Kubernetes CNI 성능 비교를 할 때 저는 보통 iperf3(아이퍼프3, 네트워크 처리량 측정 도구)부터 올립니다. 이유는 단순해요. 가장 빠르게 Pod 간 대역폭과 TCP/UDP 흐름을 볼 수 있거든요. 다만 이것만 보면 반쪽짜리입니다. 실제 운영은 Service, DNS, NetworkPolicy가 얹히니까요.

    기본 테스트 Pod 배포

    apiVersion: v1
    kind: Pod
    metadata:
      name: iperf3-server
      labels:
        app: iperf3-server
    spec:
      containers:
        - name: iperf3
          image: networkstatic/iperf3
          args: ["-s"]
    ---
    apiVersion: v1
    kind: Pod
    metadata:
      name: iperf3-client
    spec:
      containers:
        - name: iperf3
          image: networkstatic/iperf3
          command: ["sleep", "3600"]
    kubectl apply -f iperf3.yaml
    kubectl get pods -o wide
    kubectl exec -it iperf3-client -- iperf3 -c <SERVER_POD_IP> -t 30

    여기서 중요한 포인트! 같은 노드에 붙은 경우와 다른 노드에 붙은 경우를 분리해서 봐야 합니다. 같은 노드면 오버레이(overlay) 영향을 덜 받고, 다른 노드면 실제 네트워크 경로가 더 잘 드러나거든요.

    Service 경유 테스트도 꼭 해보세요

    apiVersion: v1
    kind: Service
    metadata:
      name: iperf3-service
    spec:
      selector:
        app: iperf3-server
      ports:
        - protocol: TCP
          port: 5201
          targetPort: 5201
    kubectl apply -f iperf3-service.yaml
    kubectl exec -it iperf3-client -- iperf3 -c iperf3-service.default.svc.cluster.local -t 30

    이렇게 보면 직접 Pod IP로 붙었을 때와 Service를 경유했을 때 차이를 비교할 수 있어요. 실제로 써보니까 이 구간에서 체감 차이를 보는 경우가 꽤 있더라고요.

    NetworkPolicy 적용 후도 따로 측정

    apiVersion: networking.k8s.io/v1
    kind: NetworkPolicy
    metadata:
      name: allow-iperf3
    spec:
      podSelector:
        matchLabels:
          app: iperf3-server
      policyTypes:
        - Ingress
      ingress:
        - from:
            - podSelector: {}
          ports:
            - protocol: TCP
              port: 5201

    정책 없을 때 한 번, 정책 적용 후 한 번. 최소 이 두 번은 돌려보셔야 합니다. 정책이 많아질수록 Cilium vs Calico의 dataplane 특성이 드러나는 경우가 있거든요.

    제가 실제로 체크한 관찰 포인트

    단순히 결과 숫자만 기록하면 나중에 해석이 안 됩니다. 그래서 저는 아래 항목을 같이 봤어요.

    • Pod to Pod 처리량: 같은 노드 / 다른 노드 분리
    • Service 경유 지연: kube-proxy 경로 포함 여부 확인
    • NetworkPolicy 적용 전후 차이: 정책 수가 늘어날 때 체감 확인
    • CPU 사용 경향: 네트워크 부하 중 어느 쪽이 더 민감한지
    • 디버깅 편의성: 장애 시 원인 추적 시간

    특히 마지막 항목은 문서만 보면 잘 안 보입니다. 성능 수치가 약간 좋아도, 운영 중 원인 파악이 너무 오래 걸리면 결국 전체 효율은 떨어지더라고요. 저는 예전에 정책 충돌 때문에 Pod 통신이 막힌 적이 있었는데, 그때부터는 관찰성도 성능의 일부라고 생각하게 됐습니다.

    Kubernetes 네트워킹 성능 비교를 위해 Cilium vs Calico benchmark를 수행하는 시각화 이미지

    iperf3 실행과 정책 테스트를 병행하면서 측정하는 과정을 보여주는 시각 자료입니다.

    ⚠️ 주의사항과 트러블슈팅: 여기서 많이 막힙니다

    이 섹션은 진짜 중요합니다. Kubernetes CNI benchmark가 이상하게 나오면 대개 CNI 자체보다 테스트 환경 변수가 문제인 경우가 많거든요.

    1. MTU 불일치

    오버레이 네트워크나 터널링이 들어가면 MTU(Maximum Transmission Unit, 최대 전송 단위)가 어긋나면서 성능이 이상하게 흔들릴 수 있습니다. 겉보기엔 통신이 되는데, 처리량이 들쑥날쑥하거나 재전송이 늘어나는 식이죠.

    kubectl exec -it iperf3-client -- ip link
    kubectl exec -it iperf3-server -- ip addr

    MTU 값이 환경 전체에서 일관적인지 먼저 확인해보세요.

    2. 같은 노드에만 스케줄링되는 문제

    처음엔 결과가 너무 좋게 나와서 ‘와, 이거 엄청 빠른데?’ 했었는데, 알고 보니 서버와 클라이언트 Pod가 같은 노드에 올라가 있었던 적이 있어요. 이러면 진짜 비교가 안 됩니다.

    kubectl get pods -o wide

    노드 분산이 필요하면 nodeSelector나 podAntiAffinity도 같이 고려해보세요.

    3. 정책 테스트인데 DNS를 빼먹는 문제

    Service 이름으로 붙는 테스트를 할 때 DNS 허용 정책을 빼먹으면 결과가 꼬여요. 통신 자체가 안 되는 걸 성능 문제로 오해하기 쉽거든요.

    4. 관찰 도구가 없는 상태에서 디버깅 시작

    Calico든 Cilium이든 로그와 패킷 경로를 볼 수단을 먼저 확보해두는 게 좋습니다. 특히 Cilium은 Hubble(Hubble, 네트워크 관찰 도구) 같은 생태계를 같이 보는 분들이 많고, Calico도 운영 환경에 맞춰 상태 확인 경로를 준비해두는 편이 낫습니다.

    💡 팁: benchmark 전에 무부하 상태 baseline을 한 번 기록해두세요. 나중에 결과가 흔들려도 비교 기준점이 생깁니다.

    검증과 결과 해석: 숫자보다 패턴을 보셔야 합니다

    이제 제일 궁금한 부분이죠. 그래서 뭐가 더 빨랐냐고요? 제가 홈랩과 소규모 테스트 클러스터에서 여러 번 Cilium vs Calico를 비교해본 느낌은 이랬습니다.

    • 단순 Pod to Pod 처리량만 보면 둘 다 충분히 빠른 편이라, 환경에 따라 큰 차이가 안 보일 때도 많았어요.
    • Service 경유, 정책 적용, 관찰성 포함으로 범위를 넓히면 체감 차이가 생겼습니다.
    • Cilium은 eBPF 기반 흐름과 관찰성 덕분에 문제를 추적하는 시간이 줄어드는 느낌이 있었어요.
    • Calico는 운영 경험이 이미 쌓인 팀이라면 구성과 장애 대응이 익숙해서 안정감이 있더라고요.

    즉, Kubernetes CNI benchmark 결과는 단순히 ‘누가 더 빠르다’로 끝내면 아쉬워요. 운영 모델까지 포함한 성능 비교가 맞습니다. 특히 네트워크 정책을 많이 쓰고 서비스 메시에 가까운 복잡한 흐름이 있다면 Cilium이 주는 장점이 더 잘 보일 수 있습니다. 반대로 단순하고 예측 가능한 구조, 기존 팀 역량 활용이 중요하다면 Calico도 여전히 아주 현실적인 선택입니다.

    테스트 항목 해석 포인트 제가 본 체감
    Pod to Pod 순수 데이터 경로 성능 환경 따라 큰 차이 없을 수 있음
    Service 경유 서비스 라우팅 오버헤드 구성 차이가 드러날 수 있음
    Policy 적용 후 정책 엔진 영향 워크로드 구조에 따라 차이 발생
    운영 디버깅 문제 추적 시간 Cilium 관찰성이 인상적이었음
    Kubernetes CNI에서 Cilium vs Calico benchmark 결과 해석을 요약한 비교 이미지

    Cilium vs Calico benchmark 결과를 숫자보다 해석 포인트 중심으로 요약한 인포그래픽입니다.

    어떤 환경에 Cilium, 어떤 환경에 Calico가 맞을까요

    정리하면 이렇습니다.

    • Cilium 추천: eBPF 기반 기능, 네트워크 가시성, 정책 중심 운영, 트래픽 분석이 중요할 때
    • Calico 추천: 익숙한 운영 모델, 폭넓은 도입 사례, 단계적 운영 안정성이 중요할 때

    여기서 중요한 포인트! 팀의 운영 역량도 반드시 포함해서 판단해야 합니다. 도구가 아무리 좋아도, 팀이 이해하지 못하면 장애 대응 시간은 길어집니다. 저도 처음에는 기능표만 보고 혹했었는데, 결국 오래 남는 건 운영 난이도와 추적 가능성이더라고요.

    정리와 다음 단계

    이번 글에서는 Cilium vs Calico를 Kubernetes CNI와 네트워킹 성능 비교 관점에서 풀어봤습니다. 핵심은 간단해요. Benchmark는 같은 조건에서 측정하고, 결과는 처리량만이 아니라 정책/서비스/디버깅까지 함께 해석해야 한다는 점입니다. 제가 직접 해보니 결국 ‘더 빠른 CNI’보다 ‘내 환경에서 더 잘 설명되는 CNI’를 고르는 쪽이 맞았습니다.

    ✅ 지금 바로 해보실 일은 세 가지입니다.

    1. 동일 조건의 테스트 클러스터를 준비합니다.
    2. Pod to Pod, Service, NetworkPolicy 세 가지 축으로 나눠 측정합니다.
    3. 결과 표에 숫자뿐 아니라 장애 추적 난이도까지 같이 적습니다.

    다음 글에서는 Hubble을 활용한 Cilium 트래픽 관찰이나, 반대로 Calico NetworkPolicy 운영 팁을 더 깊게 다뤄볼 예정입니다. Kubernetes 네트워크 기본기 관련 이전 글들도 함께 읽으시면 Cilium vs Calico 비교의 흐름이 더 잘 잡히실 거예요.

    자주 묻는 질문

    Cilium이 항상 Calico보다 빠른가요?

    아니에요. 트래픽 패턴, 정책 수, 서비스 경로, 커널 환경에 따라 달라집니다. 단순 benchmark 하나로 일반화하면 위험합니다.

    Calico도 eBPF를 쓰나요?

    Calico는 전통적인 방식으로 많이 알려져 있지만, eBPF dataplane 관련 선택지도 존재합니다. 다만 실제 비교에서는 어떤 dataplane을 쓰는지를 명확히 맞춰야 합니다.

    Kubernetes CNI 비교에서 제일 중요한 항목은 뭔가요?

    제 기준으로는 정책 적용 후의 변화와 운영 중 추적 가능성입니다. 실서비스는 그 구간에서 차이가 크게 느껴지거든요.

    🎉 결론적으로, Cilium이든 Calico든 무작정 유행 따라 고르기보다 내 클러스터의 네트워크 구조와 운영 방식에 맞춰 Cilium vs Calico를 직접 benchmark 해보는 게 가장 확실합니다.