13년차의 서버실

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

[카테고리:] k8s

  • [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 패턴도 더 깊이 파볼 거고요. 이 글이 도움이 됐다면 댓글로 알려주세요! 궁금한 점도 편하게 물어봐 주시고요. 😊

  • [k8s] k3s와 MicroK8s 비교: 경량 쿠버네티스 환경 선택 가이드

    경량 쿠버네티스, 왜 지금 이 선택이 중요한가요?

    홈랩을 운영하다 보면 꼭 한 번씩 마주치는 고민이 있어요. “쿠버네티스(Kubernetes)는 써보고 싶은데, 풀스택 k8s를 올리기엔 서버가 너무 작다…” 저도 딱 그랬거든요. 라즈베리파이(Raspberry Pi) 클러스터 위에 뭔가 올려보려고 했는데, 일반 쿠버네티스는 메모리만 수 GB를 잡아먹으니 엄두가 안 나더라고요.

    그래서 찾게 된 게 경량 쿠버네티스(Lightweight Kubernetes)입니다. 그중에서도 현재 가장 많이 언급되는 두 가지가 바로 k3s와 MicroK8s예요. 이 둘, 뭐가 다른지 처음엔 저도 진짜 헷갈렸습니다. 이름도 비슷하고 목적도 비슷한 것 같고… 근데 실제로 써보면 꽤 다르거든요.

    이번 글에서는 제가 직접 두 솔루션을 홈랩에서 돌려보면서 느낀 점, 그리고 어떤 상황에서 어떤 걸 선택해야 하는지 정리해 드릴게요. 엣지 컴퓨팅(Edge Computing) 환경이나 라즈베리파이 쿠버네티스 구성을 고민 중이신 분들께 특히 도움이 될 거예요.

    k3s와 MicroK8s의 전체 아키텍처 구조를 한눈에 비교한 다이어그램. 각각의 구성 요소와 설계 철학의 차이를 보여줍니다.


    k3s와 MicroK8s, 각각 어떤 녀석인가요?

    k3s — “진짜 가볍게 만들어 버렸다”

    k3s는 Rancher Labs(현재 SUSE)에서 만든 경량 쿠버네티스 배포판입니다. 공식 CNCF(Cloud Native Computing Foundation) 인증 프로젝트이기도 해요. 핵심 철학은 딱 하나예요. “싱글 바이너리(single binary)로 모든 걸 해결하자.”

    쉽게 말해, etcd(이티시디, 쿠버네티스의 상태 저장소) 대신 기본적으로 SQLite를 쓰고, 불필요한 컴포넌트를 싹 쳐냈어요. 덕분에 바이너리 하나가 약 100MB 남짓이고, 메모리도 정말 적게 씁니다. 라즈베리파이 같은 저사양 ARM 기기에서도 잘 돌아가는 이유가 여기 있어요.

    • 단일 바이너리로 설치 및 실행
    • 기본 데이터스토어: SQLite (고가용성 구성 시 etcd 또는 외부 DB 사용 가능)
    • ARM 아키텍처 공식 지원 (라즈베리파이 최적)
    • Flannel(플란넬) CNI 기본 내장
    • Traefik(트래픽) Ingress Controller 기본 포함
    • CNCF 공식 인증 쿠버네티스

    MicroK8s — “Ubuntu 생태계에서 편하게”

    MicroK8s는 Canonical(캐노니컬, Ubuntu 만드는 회사)에서 만든 경량 쿠버네티스예요. 설치 방식이 좀 독특한데, Snap(스냅) 패키지 매니저를 통해 설치합니다. Ubuntu 환경이라면 진짜 설치가 간단해요.

    k3s와 달리 MicroK8s는 애드온(addon) 시스템이 핵심이에요. 기본 설치는 가볍게 하고, 필요한 기능(DNS, 대시보드, Ingress, 스토리지 등)을 명령어 하나로 활성화하는 방식이거든요. 개발 환경이나 Ubuntu 서버에서 빠르게 쿠버네티스를 시험해보고 싶을 때 특히 편합니다.

    • Snap 패키지로 설치 (Ubuntu/Linux 최적화)
    • 애드온 시스템으로 필요한 기능만 활성화
    • 단일 노드 개발 환경에 강점
    • Canonical의 지원 및 업스트림 쿠버네티스와 빠른 동기화
    • ARM 지원 (단, k3s 대비 리소스 사용량이 다소 높음)

    k3s vs MicroK8s 핵심 스펙 비교

    말로만 하면 헷갈리니까 표로 정리해 봤어요. 제가 직접 테스트하면서 확인한 내용 위주입니다.

    항목 k3s MicroK8s
    개발사 Rancher Labs / SUSE Canonical (Ubuntu)
    설치 방식 curl 스크립트 / 단일 바이너리 Snap 패키지
    기본 데이터스토어 SQLite (소규모) / etcd 선택 가능 dqlite (분산 SQLite)
    기본 CNI Flannel Calico (기본 활성화)
    Ingress 기본 포함 Traefik (기본 활성화) nginx (애드온으로 활성화)
    ARM 지원 매우 우수 (라즈베리파이 최적) 지원 (단, x86 최적화)
    멀티노드 클러스터 매우 간단 가능 (설정 다소 복잡)
    CNCF 인증 ✅ 인증 ✅ 인증
    주요 사용 환경 엣지, IoT, 라즈베리파이, 프로덕션 경량화 개발/테스트, Ubuntu 서버, 단일 노드
    OS 의존성 낮음 (대부분의 Linux 지원) 높음 (Snap 지원 OS 필요)

    💡 핵심 포인트: 둘 다 CNCF 공식 인증을 받은 쿠버네티스예요. 즉, 표준 kubectl 명령어와 YAML 매니페스트가 그대로 동작합니다. “이거 진짜 쿠버네티스 맞아?” 걱정 안 하셔도 돼요.


    k3s 설치: 진짜 이렇게 간단해도 되나?

    처음 k3s를 설치했을 때 솔직히 당황했어요. 너무 간단해서요. “이게 다야?” 싶었거든요. 라즈베리파이 OS나 Ubuntu 기준으로 설명드릴게요.

    마스터 노드(서버) 설치

    # k3s 서버(마스터 노드) 설치 — 딱 한 줄입니다
    curl -sfL https://get.k3s.io | sh -
    
    # 설치 완료 후 상태 확인
    sudo systemctl status k3s
    
    # 노드 상태 확인
    sudo kubectl get nodes

    이게 전부예요. 진짜로요. 스크립트가 k3s 바이너리를 받아서 systemd 서비스로 등록까지 해줍니다. 설치 후 1~2분 기다리면 노드가 Ready 상태가 돼요.

    워커 노드(에이전트) 추가

    # 마스터 노드에서 토큰 확인
    sudo cat /var/lib/rancher/k3s/server/node-token
    
    # 워커 노드에서 실행 (K3S_TOKEN과 서버 IP를 바꿔주세요)
    curl -sfL https://get.k3s.io | K3S_URL=https://마스터노드IP:6443 \
      K3S_TOKEN=여기에토큰값 sh -

    라즈베리파이 3대로 클러스터 구성할 때 이 명령어로 10분 만에 끝냈어요. 삽질 없이요. 😄

    kubeconfig 설정 (로컬 PC에서 접속)

    # 마스터 노드에서 kubeconfig 복사
    sudo cat /etc/rancher/k3s/k3s.yaml
    
    # 로컬 PC의 ~/.kube/config 에 붙여넣기
    # server: https://127.0.0.1:6443 부분을 실제 서버 IP로 변경
    # 예: server: https://192.168.1.100:6443

    ⚠️ 주의: k3s.yaml 파일의 server 주소가 기본적으로 127.0.0.1로 돼 있어요. 원격 접속할 때 반드시 실제 IP로 바꿔줘야 합니다. 이거 놓치면 “connection refused” 보면서 한참 헤맬 수 있어요. 저도 한 30분 고생했었습니다 ㅎㅎ


    MicroK8s 설치: Ubuntu라면 이게 편할 수도

    MicroK8s는 Ubuntu 환경이라면 진짜 편해요. Snap으로 설치하니까요. 다만 Snap이 없는 OS에서는 좀 번거로울 수 있습니다.

    설치 및 기본 설정

    # MicroK8s 설치 (Ubuntu 기준)
    sudo snap install microk8s --classic
    
    # 현재 사용자를 microk8s 그룹에 추가 (sudo 없이 사용하려면 필수)
    sudo usermod -aG microk8s $USER
    newgrp microk8s
    
    # 상태 확인
    microk8s status --wait-ready
    
    # kubectl 사용 (microk8s.kubectl 또는 alias 설정)
    microk8s kubectl get nodes
    
    # 편하게 쓰려면 alias 등록
    alias kubectl='microk8s kubectl'

    애드온 활성화 — 이게 MicroK8s의 핵심이에요

    # DNS 활성화 (거의 필수)
    microk8s enable dns
    
    # 대시보드 활성화
    microk8s enable dashboard
    
    # Ingress 컨트롤러 활성화
    microk8s enable ingress
    
    # 스토리지(hostpath) 활성화
    microk8s enable hostpath-storage
    
    # 여러 개 한 번에 활성화
    microk8s enable dns dashboard ingress hostpath-storage
    
    # 활성화된 애드온 확인
    microk8s status

    MicroK8s의 애드온 시스템과 k3s 멀티노드 클러스터 구성 화면. 각각의 설치 및 설정 방식의 차이를 보여줍니다.

    MicroK8s 멀티노드 클러스터 구성

    # 마스터 노드에서 조인 토큰 생성
    microk8s add-node
    
    # 출력된 명령어를 워커 노드에서 실행 (예시)
    # microk8s join 192.168.1.100:25000/토큰값/해시값

    k3s보다 조금 더 손이 가긴 하지만, 출력된 명령어를 그대로 복붙하면 돼서 어렵진 않아요.


    ⚠️ 실제로 써보면서 만난 문제들

    둘 다 써보면서 “아, 이거 주의해야 하는구나” 싶었던 것들 솔직하게 공유할게요.

    k3s에서 자주 만나는 문제들

    • 라즈베리파이 cgroup 설정: 라즈베리파이 OS에서 k3s 실행 시 cgroup(컨트롤 그룹, 리소스 제어 메커니즘) 설정이 안 돼 있으면 노드가 NotReady 상태로 뜹니다. /boot/cmdline.txt (또는 /boot/firmware/cmdline.txt)에 cgroup_enable=cpuset cgroup_enable=memory cgroup_memory=1를 추가하고 재부팅해야 해요.
    • Traefik vs 내 Ingress 충돌: k3s는 Traefik을 기본으로 깔아줘요. 근데 nginx Ingress를 따로 쓰고 싶다면 k3s 설치 시 --disable traefik 옵션을 줘야 합니다. 나중에 충돌 나면 진짜 원인 찾기 어렵거든요.
    • 방화벽 포트: 멀티노드 구성 시 6443(API 서버), 8472(Flannel VXLAN), 10250(kubelet) 포트가 열려있어야 해요. UFW 켜놓고 왜 에이전트가 안 붙나 한참 봤던 경험이 있어요.

    MicroK8s에서 자주 만나는 문제들

    • Snap 업데이트 자동 재시작: Snap 패키지 특성상 자동 업데이트가 되면서 MicroK8s가 재시작되는 경우가 있어요. 프로덕션에서 쓴다면 sudo snap refresh --hold microk8s로 자동 업데이트를 잠시 홀드해두는 게 좋습니다.
    • Snap 미지원 OS: CentOS나 일부 최소화 배포판에서는 Snap 자체가 없어서 설치가 번거로워요. 이런 환경에선 k3s가 훨씬 편합니다.
    • DNS 애드온 활성화 순서: 다른 애드온보다 DNS를 먼저 활성화해야 해요. 순서 안 지키면 파드 간 통신이 안 되는 경우가 있더라고요.

    💡 공통 팁: 두 솔루션 모두 kubectl 명령어는 동일하게 사용할 수 있어요. 한쪽에서 작성한 YAML 매니페스트를 다른 쪽에서 그대로 apply해도 됩니다. 그 점은 진짜 편하더라고요.


    간단한 애플리케이션 배포로 검증해보기

    설치만 하고 끝내면 아쉽죠. 간단한 nginx 파드를 배포해서 정상 동작을 확인해 봅시다. 두 솔루션 모두 동일한 YAML로 테스트할 수 있어요.

    # nginx-test.yaml
    apiVersion: apps/v1
    kind: Deployment
    metadata:
      name: nginx-test
      labels:
        app: nginx-test
    spec:
      replicas: 2
      selector:
        matchLabels:
          app: nginx-test
      template:
        metadata:
          labels:
            app: nginx-test
        spec:
          containers:
          - name: nginx
            image: nginx:stable
            ports:
            - containerPort: 80
    ---
    apiVersion: v1
    kind: Service
    metadata:
      name: nginx-test-svc
    spec:
      selector:
        app: nginx-test
      ports:
      - port: 80
        targetPort: 80
      type: NodePort
    # 배포
    kubectl apply -f nginx-test.yaml
    
    # 파드 상태 확인 (Running이 되어야 정상)
    kubectl get pods -o wide
    
    # 서비스 확인 (NodePort 번호 확인)
    kubectl get svc nginx-test-svc
    
    # 접속 테스트 (NodePort 번호는 위에서 확인한 번호로)
    curl http://노드IP:NodePort번호

    “Welcome to nginx!” 가 뜨면 성공입니다! 🎉 이 YAML은 k3s든 MicroK8s든 동일하게 동작해요.

    kubectl get nodes와 get pods 명령어로 확인한 클러스터 정상 동작 결과. 두 노드 모두 Ready 상태이며 파드가 Running 중인 것을 확인할 수 있습니다.


    결국 뭘 골라야 할까? — 상황별 선택 가이드

    “그래서 뭐 써요?”라는 질문을 제일 많이 받아요. 솔직히 말씀드리면, 상황에 따라 달라요. 제가 정리한 선택 기준 공유해 드릴게요.

    k3s를 선택해야 하는 경우

    • ✅ 라즈베리파이나 ARM 기기를 사용하는 경우 — k3s가 압도적으로 유리해요
    • ✅ 엣지 컴퓨팅, IoT 환경 — 리소스가 제한적인 환경에서의 프로덕션 배포
    • ✅ 멀티노드 클러스터를 빠르게 구성해야 할 때
    • ✅ Ubuntu 외 OS(CentOS, Debian, Alpine 등)를 사용하는 경우
    • ✅ CI/CD 파이프라인에 경량 쿠버네티스 환경이 필요할 때
    • ✅ Snap을 쓰기 싫거나 쓸 수 없는 환경

    MicroK8s를 선택해야 하는 경우

    • ✅ Ubuntu 서버/데스크탑이 주 환경인 경우 — 설치와 관리가 정말 편해요
    • ✅ 로컬 개발/테스트 환경 — 단일 노드에서 빠르게 시험해볼 때
    • ✅ 애드온 시스템이 매력적인 경우 — 필요한 기능을 명령어 하나로 켜고 끄고 싶을 때
    • ✅ Canonical 생태계와 통합이 필요한 경우 (Ubuntu Pro, Landscape 등)
    • ✅ 업스트림 쿠버네티스 최신 버전을 빠르게 따라가고 싶을 때

    제 개인적인 결론

    저는 홈랩에서 라즈베리파이 클러스터는 k3s, x86 Ubuntu 서버에서 빠른 테스트는 MicroK8s를 씁니다. 둘 다 훌륭한 도구예요. 어느 쪽을 선택하든 “진짜 쿠버네티스”를 배울 수 있고, 나중에 풀스택 k8s로 넘어가도 배운 게 그대로 써먹힙니다.

    k3s와 MicroK8s 상황별 선택 가이드 요약. 사용 환경, 목적, OS에 따른 최적의 선택을 한눈에 정리한 인포그래픽입니다.


    자주 묻는 질문 (FAQ)

    Q. k3s와 MicroK8s는 실제 쿠버네티스랑 호환되나요?

    네, 둘 다 CNCF 공식 인증을 받은 쿠버네티스입니다. 표준 kubectl 명령어와 YAML 매니페스트가 그대로 동작하고, Helm 차트도 사용할 수 있어요.

    Q. 라즈베리파이 4에서 어느 쪽이 더 잘 돌아가나요?

    k3s가 더 유리합니다. ARM 아키텍처 최적화가 잘 돼 있고, 메모리 사용량도 더 적어요. MicroK8s도 ARM을 지원하지만, 라즈베리파이 환경에서는 k3s가 훨씬 검증이 잘 돼 있습니다.

    Q. 나중에 풀스택 쿠버네티스로 마이그레이션하기 어렵나요?

    애플리케이션 레벨(YAML 매니페스트, Helm 차트)은 그대로 이전할 수 있어요. 다만 스토리지 클래스나 네트워크 플러그인 설정은 환경에 맞게 다시 잡아줘야 합니다. 어렵다기보다는 손이 좀 가는 작업이에요.

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

    k3s는 실제로 엣지 컴퓨팅, 리테일, 제조업 현장 등 다양한 프로덕션 환경에서 사용되고 있습니다. 고가용성(HA) 구성도 지원해요. MicroK8s도 프로덕션 사용이 가능하지만, 일반적으로 개발/테스트 환경에서 더 많이 쓰이는 경향이 있습니다.


    마무리: 일단 설치해 보세요

    k3s와 MicroK8s 비교, 어떠셨나요? 사실 이 글을 쓰면서 다시 한번 느낀 건데, 이 두 도구 모두 “쿠버네티스 진입 장벽을 낮추자”는 목표 하나는 정말 잘 달성했어요. 예전엔 쿠버네티스 클러스터 하나 올리는 데 반나절씩 걸렸는데, 지금은 30분이면 충분하니까요.

    제 추천은 간단해요. 라즈베리파이나 ARM 기기, 또는 멀티노드 엣지 환경이라면 k3s, Ubuntu 서버에서 빠르게 개발/테스트하고 싶다면 MicroK8s. 고민이 길어지면 일단 k3s로 시작해보세요. 단일 명령어 설치에 문서도 풍부하거든요.

    다음 글에서는 k3s 위에 Helm(헬름, 쿠버네티스 패키지 매니저)과 ArgoCD(아르고시디, GitOps 배포 도구)를 올려서 홈랩 GitOps 파이프라인을 구성하는 내용을 다뤄볼 예정입니다. 경량 쿠버네티스를 설치했으면, 이제 그 위에 뭔가를 올려야죠! 🚀

    혹시 설치하다가 막히는 부분이 있으시면 댓글로 남겨주세요. 제가 겪어본 삽질이라면 같이 풀어드릴 수 있을 것 같아요. 😄

  • [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] 쿠버네티스 서비스 메시: Istio vs Linkerd 심층 비교 및 선택 가이드

    [k8s] 쿠버네티스 서비스 메시: Istio vs Linkerd 심층 비교 및 선택 가이드

    안녕하세요, 13년차의 서버실 주인장입니다. 😎

    마이크로서비스 아키텍처(MSA)가 대세가 되면서 애플리케이션 개발은 훨씬 유연해졌지만, 그만큼 인프라 운영의 복잡도는 기하급수적으로 늘어나더라고요. 수많은 서비스 간의 통신, 트래픽 관리, 보안, 그리고 장애 발생 시 원인 추적까지… 저도 처음엔 이게 뭔가 싶어서 삽질 좀 많이 했었죠. 특히 쿠버네티스(Kubernetes) 환경에서 이런 고민은 더 커지더라고요.

    그래서 등장한 개념이 바로 서비스 메시(Service Mesh)입니다. 서비스 메시는 이런 복잡한 문제들을 깔끔하게 해결해 줄 수 있는 강력한 도구인데요, 그중에서도 가장 많이 언급되는 두 가지가 바로 Istio와 Linkerd입니다. 오늘은 이 두 서비스 메시 솔루션을 심층적으로 비교해 보고, 여러분의 환경에 어떤 것이 더 적합할지 함께 고민해 보는 시간을 가져볼까 합니다. 제가 홈랩에서 직접 써보면서 느꼈던 점들을 솔직하게 공유해 드릴게요!

    쿠버네티스 환경에서 서비스 메시가 어떻게 동작하는지 보여주는 개념도입니다. 데이터 플레인과 컨트롤 플레인의 역할을 시각적으로 나타냅니다.

    서비스 메시(Service Mesh)란 무엇인가요?

    쉽게 말해 서비스 메시는 마이크로서비스 간의 통신을 관리하고 제어하는 인프라 계층입니다. 애플리케이션 코드 변경 없이 트래픽 관리, 보안, 관측 가능성(Observability) 기능을 제공하거든요. 마치 서비스들 사이에 ‘스마트한 프록시’를 두는 것과 같다고 생각하시면 편합니다.

    • 데이터 플레인(Data Plane): 실제 서비스 간 트래픽을 가로채고 처리하는 부분입니다. 보통 사이드카(Sidecar) 프록시 형태로 각 서비스 파드(Pod) 옆에 배포됩니다. Istio는 Envoy를, Linkerd는 자체 개발한 Rust 기반 프록시를 사용합니다.
    • 컨트롤 플레인(Control Plane): 데이터 플레인의 프록시들을 제어하고 구성하는 부분입니다. 트래픽 라우팅 규칙, 정책, 인증서 등을 관리합니다.

    이런 분리 덕분에 개발자는 비즈니스 로직에만 집중하고, 인프라 엔지니어는 서비스 메시를 통해 네트워크 관련 문제들을 중앙에서 효율적으로 관리할 수 있게 되더라고요. 이거 진짜 편하더라고요! 처음엔 설치하고 설정하는 게 좀 번거로웠지만, 한 번 구축해두니 마이크로서비스 운영이 훨씬 수월해졌습니다. 🎉

    Istio 깊게 파보기: 강력함과 유연함의 상징

    Istio(이스티오)는 Google, IBM, Lyft가 함께 개발한 오픈소스 서비스 메시 프로젝트입니다. 가장 강력하고 기능이 풍부하다는 평을 받죠. 저도 처음엔 Istio의 방대한 기능에 압도당했었는데, 하나하나 파고들수록 그 유연함에 감탄했었습니다.

    Istio의 주요 특징

    • Envoy 프록시(Proxy): 데이터 플레인으로 업계 표준에 가까운 Envoy 프록시를 사용합니다. 높은 성능과 확장성을 제공하죠.
    • 강력한 트래픽 관리(Traffic Management): A/B 테스팅, 카나리 배포(Canary Deployment), 서킷 브레이킹(Circuit Breaking), 타임아웃, 재시도 등 고급 트래픽 제어 기능을 제공합니다. 제가 특정 서비스의 트래픽을 10%만 신규 버전으로 보내보고 싶을 때 정말 유용하게 썼습니다.
    • 정책 시행(Policy Enforcement): 쿼터(Quota), 속도 제한(Rate Limiting) 등 다양한 정책을 정의하고 적용할 수 있습니다.
    • 보안(Security): 서비스 간 상호 TLS(mTLS)를 기본으로 제공하며, 강력한 인증(Authentication) 및 권한 부여(Authorization) 정책을 설정할 수 있습니다.
    • 관측 가능성(Observability): Prometheus, Grafana, Jaeger 등 다양한 도구와 통합되어 풍부한 메트릭(Metrics), 로그(Logs), 트레이스(Traces)를 수집합니다.

    Istio는 엔터프라이즈 환경이나 매우 복잡한 마이크로서비스 아키텍처를 운영하는 팀에 딱 맞아떨어지더라고요. 기능이 많은 만큼 학습 곡선이 높고, 리소스 소모도 Linkerd에 비해 큰 편입니다. 저도 처음에 설정 파일 보면서 ‘이걸 다 알아야 하나’ 싶었는데, 핵심 기능 위주로 접근하니 그래도 괜찮더라고요.

    Linkerd 깊게 파보기: 심플함과 성능의 미학

    Linkerd(링커디)는 CNCF(Cloud Native Computing Foundation)에서 졸업(Graduated)한 프로젝트로, Buoyant가 주도하고 있습니다. Istio에 비해 기능은 적지만, 가볍고 빠르며 사용하기 쉽다는 게 장점이더라고요. 제 홈랩처럼 리소스가 제한적인 환경에서는 Linkerd의 매력이 상당했습니다.

    Linkerd의 주요 특징

    • Rust 기반 프록시: 데이터 플레인에 경량화된 Rust 기반의 프록시를 사용합니다. 메모리 사용량과 지연 시간(Latency)이 매우 낮아 성능이 뛰어납니다.
    • 간결한 기능(Simplicity): Istio처럼 모든 기능을 제공하기보다는, 핵심적인 트래픽 관리, 관측 가능성, 보안 기능에 집중합니다. 복잡한 설정 없이도 빠르게 시작할 수 있습니다.
    • 자동 mTLS(Automatic mTLS): 서비스 간 통신을 자동으로 암호화하여 보안을 강화합니다. 별다른 설정 없이도 적용되는 점이 좋더라고요.
    • 대시보드(Dashboard): Linkerd CLI와 함께 제공되는 대시보드는 서비스의 상태, 트래픽 흐름, 지연 시간 등을 직관적으로 보여줍니다. 처음 써봤을 때 ‘와, 한눈에 다 보이네!’ 싶었습니다.

    Linkerd는 빠르고 가벼우며, 비교적 작은 규모의 팀이나 복잡한 설정보다는 핵심 기능에 집중하고 싶은 프로젝트에 이상적입니다. 특히 성능이 중요한 환경이라면 Linkerd가 좋은 선택이 될 수 있습니다. 하지만 Istio처럼 세밀한 트래픽 제어가 필요한 경우에는 아쉬울 수 있습니다.

    Istio와 Linkerd의 주요 구성 요소와 기능을 비교하여 보여주는 다이어그램입니다. 각 서비스 메시의 강점이 시각적으로 강조됩니다.

    Istio vs Linkerd 심층 비교 테이블

    이제 두 서비스 메시의 핵심적인 차이점을 한눈에 볼 수 있도록 표로 정리해볼게요. 제가 직접 써보면서 느낀 점들도 함께 녹여냈으니, 여러분의 선택에 도움이 되었으면 좋겠습니다.

    구분 Istio Linkerd
    개발 주체 Google, IBM, Lyft (커뮤니티 중심) Buoyant (CNCF 졸업 프로젝트)
    데이터 플레인 프록시 Envoy Proxy Rust 기반 경량 프록시
    복잡도 & 학습 곡선 높음 (다양한 기능, 세밀한 설정) 낮음 (간결한 기능, 쉬운 시작)
    성능 & 리소스 리소스 소모 상대적으로 높음 (기능이 많아서) 매우 낮음 (경량화, 고성능)
    트래픽 관리 매우 강력하고 세밀함 (A/B, 카나리, 서킷 브레이커 등) 필수적인 기능 위주 (mTLS, HTTP/gRPC 리트라이 등)
    보안 mTLS, 인증/권한 부여 정책, JWT 검증 등 자동 mTLS 기본 제공, 정책은 Istio보다 적음
    관측 가능성 Prometheus, Grafana, Jaeger 등 통합 (풍부한 메트릭/트레이스) 내장 대시보드, Grafana 통합 (핵심 메트릭)
    확장성 Mixer(구), WASM 등 플러그인 확장 용이 비교적 제한적 (핵심 기능에 집중)
    주요 사용 사례 대규모 엔터프라이즈, 복잡한 마이크로서비스 아키텍처, 세밀한 제어 필요 성능 중시, 빠른 시작, 리소스 제약 환경, 심플한 관리 선호

    어떤 서비스 메시를 선택해야 할까요? 선택 가이드 및 실전 팁

    결론부터 말하자면 ‘정답은 없다’는 거죠. 여러분의 프로젝트 요구사항, 팀의 역량, 그리고 인프라 환경에 따라 최적의 선택이 달라질 수 있습니다.

    1. 복잡한 트래픽 제어가 필수라면 Istio: A/B 테스팅, 카나리 배포, 폴트 인젝션(Fault Injection) 등 매우 세밀하고 다양한 트래픽 제어 전략이 필요하다면 Istio가 좋은 선택입니다. 초반 학습 비용은 들겠지만, 그만큼 강력한 기능을 제공하거든요.
    2. 심플함과 성능이 최우선이라면 Linkerd: 서비스 메시의 핵심 기능(mTLS, 기본적인 트래픽 관리, 관측 가능성)만으로 충분하고, 리소스 효율성과 낮은 지연 시간이 중요하다면 Linkerd가 탁월합니다. 제가 홈랩에서 가볍게 실험할 때는 Linkerd가 정말 편하더라고요. 설치도 쉽고, 금방 결과물을 볼 수 있었어요.
    3. 팀의 역량과 학습 곡선 고려: Istio는 강력한 만큼 설정할 것이 많고 복잡합니다. 팀에 서비스 메시 전문가가 있거나, 학습에 충분한 시간을 투자할 여력이 있다면 Istio를 고려해볼 만합니다. Linkerd는 비교적 쉽게 시작하고 운영할 수 있어서, 서비스 메시를 처음 도입하는 팀에 부담이 덜할 수 있습니다.
    4. 기존 에코시스템 통합: 이미 Prometheus, Grafana, Jaeger 같은 특정 도구들을 깊게 사용하고 있다면, 해당 도구들과의 통합이 얼마나 쉬운지도 고려해야 합니다. 두 솔루션 모두 잘 통합되지만, Istio는 더 넓은 스펙트럼의 통합을 지원하는 경향이 있습니다.

    제가 실제로 여러 프로젝트에서 두 가지를 모두 사용해보니, ‘작은 규모부터 시작해서 점진적으로 확장할 계획이라면 Linkerd로 가볍게 시작하고, 나중에 필요하다면 Istio로 마이그레이션을 고려하는 것도 한 방법’이라는 생각을 하게 되었습니다. 💡

    ⚠️ 주의사항 및 트러블슈팅: 삽질은 나의 힘!

    서비스 메시를 도입하면 모든 문제가 해결될 것 같지만, 사실 새로운 종류의 삽질이 시작되기도 합니다. 저도 처음엔 많이 헤맸거든요.

    • 리소스 오버헤드(Resource Overhead): 서비스 메시의 사이드카 프록시는 각 파드에 추가되므로, 네트워크 오버헤드와 함께 CPU, 메모리 사용량이 증가합니다. 특히 Istio는 이 부분이 Linkerd보다 더 두드러질 수 있어요. 항상 리소스 모니터링을 철저히 해야 합니다.
    • 설정 복잡도: 특히 Istio의 VirtualService, DestinationRule, Gateway 같은 CRD(Custom Resource Definition)들은 처음 접할 때 매우 혼란스러울 수 있습니다. YAML 파일을 작성할 때 인덴트(Indent) 하나만 잘못돼도 에러가 나기 일쑤였죠. 😅 공식 문서와 예제를 꼼꼼히 보면서 익숙해지는 수밖에 없습니다.
    • 디버깅의 어려움: 서비스 메시 계층에서 문제가 발생하면, 트래픽이 애플리케이션에 도달하기 전에 차단되거나 잘못 라우팅될 수 있습니다. 이때는 프록시의 로그를 확인하거나, Istio의 istioctl analyze, Linkerd의 linkerd check 같은 진단 도구를 적극 활용해야 합니다. 제가 한번은 특정 서비스로 요청이 계속 실패해서 몇 시간을 날렸는데, 알고 보니 서비스 메시 정책 설정이 잘못되어 외부 트래픽을 아예 막고 있었더라고요. 🤦‍♂️

    이런 삽질을 줄이려면, 도입 전에 작은 스케일로 PoC(개념 증명)를 충분히 해보고, 팀원들과 함께 학습하는 시간을 가지는 것이 중요하다고 생각합니다. 그리고 문서화! 정말 중요합니다.

    서비스 메시 도입 후 Prometheus와 Grafana를 통해 수집된 트래픽, 에러율, 지연 시간 등의 메트릭을 시각적으로 보여주는 대시보드 예시입니다.

    결과 및 검증: 잘 동작하고 있나?

    서비스 메시를 성공적으로 배포했다면, 이제 제대로 동작하는지 확인해야겠죠? 저는 주로 다음과 같은 방법으로 검증했습니다.

    1. 대시보드 확인: Linkerd는 자체 대시보드를 제공하고, Istio는 Kiali 같은 도구와 통합하여 서비스 간 트래픽 흐름, 오류율, 지연 시간 등을 시각적으로 보여줍니다. 여기서 이상 징후를 빠르게 파악할 수 있어요.
    2. 메트릭 및 로그 분석: Prometheus와 Grafana를 통해 수집되는 메트릭을 확인하여 서비스 메시가 트래픽을 정상적으로 처리하고 있는지, 리소스 사용량에 문제는 없는지 주기적으로 모니터링합니다.
    3. 트레이싱(Tracing): Jaeger 같은 분산 트레이싱 도구를 사용하여 특정 요청이 여러 서비스를 거쳐 어떻게 처리되는지 엔드투엔드(End-to-End)로 추적할 수 있습니다. 마이크로서비스 환경에서 문제 발생 시 원인 서비스를 특정하는 데 정말 큰 도움이 되더라고요.
    4. 정책 테스트: 설정한 트래픽 라우팅 규칙이나 보안 정책이 의도한 대로 동작하는지 직접 테스트 요청을 보내 확인합니다. 예를 들어, 특정 버전으로 10% 트래픽을 라우팅하는 카나리 배포를 했다면, 실제로 그 비율대로 트래픽이 분산되는지 확인하는 거죠.

    이런 검증 과정을 통해 서비스 메시가 안정적으로 운영될 수 있도록 지속적으로 관리해야 합니다. 처음엔 낯설겠지만, 익숙해지면 엄청난 효율성을 가져다줄 거예요.

    Istio와 Linkerd 중 하나를 선택하기 위한 의사결정 흐름을 보여주는 인포그래픽입니다. 프로젝트 규모, 기능 요구사항, 팀 역량 등을 기준으로 선택 과정을 요약합니다.

    마무리: 나에게 맞는 서비스 메시 찾기

    오늘은 쿠버네티스 서비스 메시의 양대 산맥인 Istio와 Linkerd에 대해 깊이 있게 알아보고 비교해 봤습니다. 두 솔루션 모두 마이크로서비스 운영의 복잡도를 줄여주고, 트래픽 관리, 보안, 관측 가능성을 향상시키는 강력한 도구라는 점은 변함이 없습니다.

    핵심은 여러분의 프로젝트가 무엇을 더 중요하게 생각하는가입니다. 강력한 기능과 세밀한 제어가 필요하다면 Istio를, 심플함, 경량성, 그리고 빠른 도입이 중요하다면 Linkerd를 고려해 보세요. 저도 처음엔 무조건 기능이 많은 Istio가 최고인 줄 알았는데, 막상 홈랩에서는 Linkerd의 가벼움에 반했거든요. ㅎㅎ

    어떤 솔루션을 선택하든, 충분한 PoC와 학습, 그리고 지속적인 모니터링이 필수입니다. 서비스 메시 도입은 한 번의 설정으로 끝나는 것이 아니라, 마이크로서비스 운영 철학의 변화를 가져오는 과정이라고 생각합니다.

    혹시 Istio나 Linkerd를 사용하면서 겪었던 재미있는 삽질 경험이나 꿀팁이 있다면 댓글로 공유해 주세요! 다음번에는 서비스 메시를 실제 쿠버네티스 클러스터에 배포하고 운영하는 더 구체적인 방법을 다뤄볼까 합니다. 기대해 주세요! 😊

  • [k8s] k3s로 경량 쿠버네티스 클러스터 구축: 홈랩과 엣지 완벽 가이드

    [k8s] k3s로 경량 쿠버네티스 클러스터 구축: 홈랩과 엣지 완벽 가이드

    k3s로 경량 쿠버네티스 클러스터 구축하기: 13년차 엔지니어의 삽질 완전 정복

    안녕하세요, 13년차의 서버실 주인장입니다. 오늘은 제가 최근 홈랩에서 직접 구축해보고, 엣지 컴퓨팅 환경에서도 정말 유용하겠다 싶었던 기술, k3s(케이쓰리 에스)에 대해 이야기해보려고 해요. 기존 쿠버네티스(Kubernetes)는 아무래도 무겁고 복잡해서 홈랩이나 리소스가 제한적인 엣지 환경에 도입하기가 쉽지 않았거든요. 근데 k3s를 만나고 나서 생각이 확 바뀌었습니다. 가볍고 빠르면서도 쿠버네티스의 강력한 기능을 고스란히 쓸 수 있다는 게 정말 매력적이더라고요!

    저도 처음엔 이게 뭔가 싶었는데, 실제로 써보니까 ‘아, 이거 진짜 물건이다!’ 싶었습니다. 제 경험을 바탕으로 k3s 경량 쿠버네티스를 활용해서 클러스터를 어떻게 구축하는지, 그리고 어떤 삽질을 겪었고 어떻게 해결했는지 솔직하게 공유해볼게요. 특히 엣지 컴퓨팅(Edge Computing)이나 저처럼 홈랩(Home Lab)을 운영하는 분들께 큰 도움이 될 거라 생각합니다.

    💡 k3s, 왜 선택했을까? 경량 쿠버네티스의 진짜 매력

    혹시 이런 경험 있으신가요? 작은 라즈베리 파이(Raspberry Pi)나 오래된 미니 PC에 k3s를 포함한 쿠버네티스를 올려보고 싶었는데, 리소스 문제나 복잡한 설치 과정 때문에 포기했던 경험 말이죠. 저도 그랬거든요. 일반적인 쿠버네티스 배포판은 마스터 노드(Master Node) 하나만 해도 꽤 많은 메모리와 CPU를 요구합니다. 그런데 k3s는 이런 제약을 확 낮춰줍니다. 말 그대로 ‘경량(Lightweight)’ 쿠버네티스죠.

    k3s는 Rancher Labs에서 개발한 CNCF(Cloud Native Computing Foundation) 샌드박스 프로젝트로, 리소스가 제한적인 환경, 예를 들면 IoT 디바이스, 엣지 서버, 그리고 저의 사랑스러운 홈랩 같은 곳에 최적화되어 있습니다. 바이너리 파일 하나로 설치가 끝나고, 필요 없는 기능들을 쳐내서 메모리 사용량도 훨씬 적어요. 제가 직접 해보니, 512MB RAM 이상이면 마스터 노드를 구동할 수 있더라고요! 정말 놀랍지 않습니까? 🎉

    k3s 경량 쿠버네티스 클러스터의 간소화된 아키텍처 다이어그램: 마스터 노드와 여러 워커 노드가 연결되어 엣지 및 홈랩 환경에서 효율적으로 동작하는 모습을 보여줍니다.

    k3s 기본 이해: 경량 쿠버네티스의 핵심 개념

    쉽게 말해 k3s는 쿠버네티스(Kubernetes)의 모든 핵심 기능을 제공하면서도, 배포판을 경량화한 버전입니다. k3s는 기존 쿠버네티스가 제공하는 API Server, Controller Manager, Scheduler, etcd 같은 핵심 컴포넌트들을 모두 포함하고 있어요. 하지만 몇 가지 차이점이 있습니다.

    • 단일 바이너리(Single Binary): 설치 파일이 하나로 되어 있어 배포가 정말 간편합니다.
    • SQLite3 내장(Embedded SQLite3): 기본적으로 외부 etcd 대신 SQLite3를 데이터 저장소로 사용합니다. 물론 PostgreSQL, MySQL, etcd 등 외부 DB도 연결할 수 있어요.
    • 불필요한 기능 제거: 레거시(Legacy) 코드나 클라우드 제공업체(Cloud Provider) 관련 기능을 제거하여 경량화했습니다.
    • 컨테이너 런타임(Container Runtime) 변경: 도커(Docker) 대신 containerd(컨테이너디)를 기본 컨테이너 런타임으로 사용합니다.

    이런 특징들 덕분에 k3s는 일반적인 쿠버네티스에 비해 설치도 빠르고 리소스 소모도 적어서, 소규모 프로젝트나 학습용, 그리고 저처럼 홈랩에서 다양한 실험을 해보고 싶을 때 최적의 선택이 됩니다. 컨테이너 오케스트레이션(Container Orchestration)이라는 쿠버네티스의 핵심 개념은 그대로 가져가면서도, 훨씬 쉽게 접근할 수 있게 해주는 거죠.

    실전! k3s 경량 쿠버네티스 클러스터 구축 스텝별 가이드

    자, 이제 직접 k3s 클러스터를 구축해볼 시간입니다. 저는 라즈베리 파이 4B 두 대와 오래된 미니 PC 한 대를 이용해서 클러스터를 만들어봤어요. OS는 모두 Ubuntu Server 22.04 LTS를 사용했습니다. 여러분도 리눅스(Linux) 기반의 어떤 장비든 준비하시면 됩니다.

    1. k3s 마스터 노드(Server) 설치

    가장 먼저 클러스터의 ‘뇌’ 역할을 할 마스터 노드를 설치합니다. k3s는 정말 설치가 간단해서 명령어 한 줄이면 끝나버려요. 💡 팁: 실제 운영 환경에서는 스크립트 내용을 확인하고 사용하는 것이 좋습니다.

    
    curl -sfL https://get.k3s.io | sh -
    

    이 명령어를 실행하면 k3s가 자동으로 설치되고 서비스로 등록되어 실행됩니다. 설치가 끝나면 제대로 작동하는지 확인해볼까요?

    
    sudo k3s kubectl get nodes
    

    기본적으로 k3s는 자체적으로 제공하는 k3s kubectl 명령어를 사용하도록 설정되어 있어요. 처음엔 조금 어색하지만 익숙해지니 괜찮더라고요. k3s가 정상적으로 설치되었다면 마스터 노드가 Ready 상태로 보일 겁니다. 예를 들면 이런 식이죠:

    
    NAME         STATUS   ROLES                  AGE   VERSION
    master-node   Ready    control-plane,master   2m    v1.28.3+k3s1
    

    2. k3s 워커 노드(Agent) 추가

    이제 마스터 노드에 연결할 워커 노드(Agent Node)를 추가해봅시다. 워커 노드 역시 명령어 한 줄로 설치되는데, 다만 마스터 노드와 연결하기 위한 토큰(Token)이 필요해요. 이 토큰은 마스터 노드에서 확인할 수 있습니다.

    
    sudo cat /var/lib/rancher/k3s/server/node-token
    

    이 명령어를 실행하면 긴 문자열이 나올 텐데, 이게 바로 워커 노드를 연결할 때 필요한 토큰입니다. 이 토큰과 마스터 노드의 IP 주소를 알고 있다면, 워커 노드에서 다음 명령어를 실행해주세요. <master-ip> 부분은 마스터 노드의 실제 IP 주소로, <token> 부분은 위에서 확인한 토큰으로 바꿔주셔야 합니다.

    
    curl -sfL https://get.k3s.io | K3S_URL=https://<master-ip>:6443 K3S_TOKEN=<token> sh -
    

    워커 노드 설치 후, 다시 마스터 노드에서 sudo k3s kubectl get nodes 명령어를 실행해보세요. 몇 분 뒤에 워커 노드가 Ready 상태로 보인다면 성공입니다! 🎉

    k3s 마스터 및 워커 노드 구성과 kubectl 환경 설정이 완료된 명령줄 인터페이스 스크린샷입니다.

    3. kubectl 환경 설정으로 편하게 사용하기

    매번 sudo k3s kubectl이라고 치는 게 귀찮으시죠? 저도 그랬거든요. 일반적인 kubectl 명령어를 사용할 수 있도록 환경 설정을 해봅시다. 마스터 노드에서 다음 명령어를 실행하여 kubeconfig 파일을 홈 디렉토리로 복사하고 환경 변수를 설정합니다.

    
    mkdir -p ~/.kube
    sudo cp /etc/rancher/k3s/k3s.yaml ~/.kube/config
    sudo chown $(id -u):$(id -g) ~/.kube/config
    chmod 600 ~/.kube/config
    export KUBECONFIG=~/.kube/config
    

    마지막 export 명령어는 현재 세션에만 적용되므로, 로그인할 때마다 자동으로 적용되게 하려면 .bashrc나 .zshrc 파일에 추가하는 것이 좋습니다. 이제 kubectl get nodes 명령어를 실행해보세요. 잘 동작할 겁니다!

    ⚠️ k3s 설치 중 만난 난관들 & 해결법

    제가 13년차 엔지니어라고 해도 삽질은 피할 수 없죠! k3s 설치 중 저를 괴롭혔던 몇 가지 문제와 해결법을 공유합니다. 혹시 여러분도 비슷한 문제를 겪는다면 참고가 되셨으면 좋겠어요.

    1. 워커 노드가 연결되지 않는 문제 (방화벽!)

      처음엔 마스터 노드와 워커 노드 간 통신이 안 되는 거예요. kubectl get nodes를 하면 워커 노드가 NotReady 상태로 계속 머물러 있었죠. 알고 보니 방화벽(Firewall) 문제였습니다. Ubuntu Server는 기본적으로 ufw(Uncomplicated Firewall)가 활성화되어 있는 경우가 많거든요. k3s는 기본적으로 6443/tcp 포트를 사용하는데, 이 포트가 막혀있었던 겁니다. 🤯

      해결법: 마스터 노드와 워커 노드 모두에서 필요한 포트를 열어줬습니다.

      
      sudo ufw allow 6443/tcp  # k3s API Server
      sudo ufw allow 10250/tcp # Kubelet (필수)
      sudo ufw reload
      

      특히 마스터 노드는 워커 노드의 Kubelet과 통신해야 하므로 10250 포트도 열어주는 것이 좋습니다. 이 문제를 해결하고 나니 워커 노드가 바로 Ready 상태로 바뀌더라고요. 역시 네트워크 문제는 언제나 기본부터 체크해야 합니다.

    2. 토큰(Token) 불일치로 인한 연결 실패

      간혹 워커 노드를 추가할 때 K3S_TOKEN 값을 잘못 입력하는 경우가 있어요. 아니면 복사 붙여넣기 할 때 공백이 들어간다거나요. 그럼 당연히 워커 노드가 마스터에 연결되지 않습니다.

      해결법: 워커 노드에서 k3s 서비스를 중지하고 삭제한 후, 정확한 토큰 값으로 다시 설치했습니다.

      
      sudo systemctl stop k3s-agent
      sudo systemctl disable k3s-agent
      sudo k3s-uninstall.sh # 워커 노드에 설치된 k3s 삭제 스크립트
      # 정확한 토큰으로 다시 설치
      curl -sfL https://get.k3s.io | K3S_URL=https://<master-ip>:6443 K3S_TOKEN=<correct-token> sh -
      
    3. 로그 확인의 중요성

      어떤 문제가 발생하든, 가장 먼저 해야 할 일은 로그를 확인하는 것입니다. k3s 서비스의 로그는 journalctl 명령어를 통해 볼 수 있어요.

      
      sudo journalctl -u k3s --since "10 minutes ago" -f
      # 워커 노드는
      sudo journalctl -u k3s-agent --since "10 minutes ago" -f
      

      로그를 보면 에러 메시지나 경고 메시지를 통해 문제의 원인을 파악할 수 있는 경우가 많습니다. 저는 방화벽 문제도 로그에서 connection refused 같은 메시지를 보고 눈치챘거든요.

    🎉 완성! k3s 클러스터에 애플리케이션 배포하기

    클러스터가 성공적으로 구축되었다면, 이제 간단한 애플리케이션을 배포해서 잘 동작하는지 확인해봐야죠. 저는 웹 서버로 유명한 Nginx(엔진엑스)를 배포해볼게요. 다음 YAML 파일을 nginx-deployment.yaml로 저장합니다.

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

    저장한 파일을 클러스터에 배포합니다.

    
    kubectl apply -f nginx-deployment.yaml
    

    배포가 성공적으로 완료되었다면, 파드(Pod)와 서비스(Service)가 잘 생성되었는지 확인해봅시다.

    
    kubectl get pods -o wide
    kubectl get svc
    

    kubectl get svc 결과에서 nginx-service의 NodePort를 확인해보세요. 예를 들어 30000번대 포트가 할당될 겁니다. 이제 워커 노드의 IP 주소와 할당된 NodePort를 이용하여 웹 브라우저에서 Nginx 웹 페이지에 접속할 수 있습니다. 예를 들어, http://<워커-노드-IP>:<NodePort> 이런 식으로요. 드디어 제 홈랩 k3s 클러스터에서 Nginx가 동작하는 걸 보니 정말 뿌듯하더라고요! 🎉

    k3s 클러스터에 Nginx 애플리케이션 배포 후 웹 브라우저 접속 화면 및 kubectl 명령 결과를 보여주는 스크린샷입니다.

    🚀 13년차 엔지니어의 제언: k3s 경량 쿠버네티스 활용처

    k3s를 직접 구축하고 사용해보니, 이 경량 쿠버네티스가 얼마나 유용한지 다시 한번 깨달았습니다. 제가 생각하는 k3s의 주요 활용처는 다음과 같습니다.

    활용 분야 k3s의 장점 예시
    홈랩 (Home Lab) 저렴한 비용으로 쿠버네티스 학습 및 개인 서비스 운영 라즈베리 파이 클러스터, 개인 웹서버, 미디어 서버
    엣지 컴퓨팅 (Edge Computing) 제한된 리소스 환경에서 컨테이너 애플리케이션 배포 및 관리 스마트 팩토리, 스마트 시티 IoT 게이트웨이, 지점 서버
    개발/테스트 환경 로컬 머신이나 가상 머신에 빠르고 가볍게 k3s 클러스터 구축 CI/CD 파이프라인 테스트, 개발자 개인 환경
    IoT (사물 인터넷) 수많은 IoT 디바이스의 애플리케이션 배포 및 업데이트 관리 자율주행 차량, 스마트 농장 센서 데이터 처리

    k3s는 쿠버네티스의 복잡성을 줄여주면서도 강력한 기능을 제공하기 때문에, 위와 같은 환경에서 컨테이너 기반 애플리케이션을 효율적으로 배포하고 관리하는 데 탁월한 선택이 될 수 있습니다. 저처럼 인프라를 직접 만져보고 실험하는 걸 좋아하는 분들에게는 정말 최고의 장난감이 될 거예요. 💡

    물론 k3s가 모든 상황에 완벽한 만능 해결책은 아니에요. 대규모 엔터프라이즈 환경에서는 여전히 표준 쿠버네티스 배포판이 더 적합할 수 있습니다. 하지만 소규모, 리소스 제한 환경에서는 k3s가 제공하는 가치는 엄청나다고 생각합니다. 저도 처음엔 ‘이 작은 걸로 뭘 할 수 있을까?’ 싶었는데, 지금은 제 홈랩의 핵심 인프라가 되었습니다. 다음 글에서는 k3s 위에 Helm(헬름)으로 애플리케이션을 배포하거나 Persistent Storage(영구 스토리지)를 구성하는 방법에 대해서도 다뤄볼 예정이니 기대해주세요!

    k3s의 주요 장점과 활용 사례를 간결하게 요약한 인포그래픽입니다.

    오늘 제가 공유한 삽질 경험과 해결 과정이 여러분의 k3s 경량 쿠버네티스 클러스터 구축에 조금이나마 도움이 되었기를 바랍니다. 혹시 궁금한 점이나 또 다른 삽질 경험이 있다면 언제든지 댓글로 남겨주세요! 13년차의 서버실은 언제나 열려있습니다. 다음 글에서 또 만나요! 👋

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

  • [k8s] 쿠버네티스 보안 강화: RBAC와 네트워크 정책 실전 설정 가이드

    [k8s] 쿠버네티스 보안 강화: RBAC와 네트워크 정책 실전 설정 가이드

    쿠버네티스 보안 강화: RBAC, 네트워크 정책 설정 실전 가이드

    안녕하세요, 13년차 서버실 지킴이, 인프라 엔지니어 “13년차의 서버실”입니다. 오늘도 여러분의 튼튼한 인프라를 위한 이야기를 들고 왔습니다.

    도입부: 왜 쿠버네티스 보안이 중요할까요?

    요즘 클라우드 환경에서 쿠버네티스(Kubernetes, k8s)는 정말 대세 중의 대세죠. 저도 홈랩에서 다양한 애플리케이션들을 쿠버네티스 위에 올려서 실험하고 운영하고 있거든요. 그런데 이렇게 편리하고 강력한 도구일수록 보안(Security)은 더욱 중요해집니다.

    솔직히 처음엔 저도 쿠버네티스 보안? 그냥 방화벽 잘 치고, SSL/TLS 잘 적용하면 되는 거 아냐? 라고 쉽게 생각했었어요. 근데 이게 웬걸, 클러스터 내부의 파드(Pod)나 서비스(Service) 간의 통신, 그리고 누가 어떤 리소스(Resource)에 접근할 수 있는지 같은 복잡한 문제들이 산적해 있더라고요. 클라우드 환경은 기존 온프레미스(On-premise)와는 또 다른 공격 표면(Attack Surface)을 가지거든요. 특히나 클러스터 보안(Cluster Security)은 한번 뚫리면 전체 시스템이 위험해질 수 있어서 각별히 신경 써야 합니다.

    그래서 오늘은 쿠버네티스 보안을 강화하기 위한 핵심 두 가지, 바로 RBAC(Role-Based Access Control, 역할 기반 접근 제어)와 네트워크 정책(Network Policy) 설정에 대한 실전 가이드를 준비해 봤습니다. 끊임없이 변화하는 클라우드 환경에서 항상 최신 보안 트렌드를 반영하는 방법을 익혀두면 좋겠죠? 제가 직접 삽질하며 배운 내용들을 아낌없이 공유해 드릴게요!

    쿠버네티스 클러스터는 여러 보안 계층으로 보호됩니다. RBAC는 ‘누가 무엇을 할 수 있는가’를, 네트워크 정책은 ‘누가 누구와 통신할 수 있는가’를 제어하며 핵심적인 역할을 합니다.

    쿠버네티스 RBAC, 왜 필요할까요? (개념 설명)

    RBAC(Role-Based Access Control)는 말 그대로 ‘역할 기반 접근 제어’입니다. 쉽게 말해, “누가 쿠버네티스 클러스터 내에서 무엇을 할 수 있는가?”를 정의하는 메커니즘이에요. 예를 들어, 개발팀은 자기네 네임스페이스(Namespace)에 파드만 배포할 수 있고, 운영팀은 클러스터 전체의 모든 리소스를 관리할 수 있도록 권한을 나누는 거죠.

    RBAC의 주요 구성 요소는 다음과 같습니다:

    • Role (역할): 특정 네임스페이스 내에서 허용되는 권한 집합을 정의합니다. (예: ‘default’ 네임스페이스에서 파드를 조회, 생성할 수 있는 권한)
    • ClusterRole (클러스터 역할): 클러스터 전체에 걸쳐 허용되는 권한 집합을 정의합니다. 네임스페이스에 종속되지 않는 리소스(노드, 퍼시스턴트 볼륨 등)나 모든 네임스페이스에 대한 권한을 부여할 때 사용합니다.
    • RoleBinding (역할 바인딩): Role을 특정 사용자(User), 그룹(Group), 또는 서비스 계정(ServiceAccount)에 연결하여 해당 네임스페이스 내에서 권한을 부여합니다.
    • ClusterRoleBinding (클러스터 역할 바인딩): ClusterRole을 사용자, 그룹, 또는 서비스 계정에 연결하여 클러스터 전체에 걸쳐 권한을 부여합니다.

    이걸 왜 써야 하냐고요? 권한을 너무 많이 주면 보안 사고로 이어지기 쉽고, 너무 적게 주면 개발이나 운영이 힘들어지거든요. 최소 권한 원칙(Principle of Least Privilege)을 지키면서 효율적인 협업 환경을 만드는 데 RBAC가 필수적입니다.

    RBAC 설정, 저도 처음엔 삽질 좀 했습니다 (실전 구현)

    자, 이제 RBAC를 직접 설정해볼까요? 제가 홈랩에서 개발팀과 운영팀의 권한을 분리했던 경험을 바탕으로 설명해 드릴게요. 처음엔 Role과 ClusterRole, Binding의 차이가 헷갈려서 삽질 좀 했습니다 ㅎㅎ.

    1. 개발팀 전용 네임스페이스 생성

    먼저 개발팀이 사용할 네임스페이스를 만듭니다. 여기서는 <code>dev-team이라는 네임스페이스를 사용할게요.

    kubectl create namespace dev-team
    

    2. 개발팀 역할(Role) 정의

    dev-team 네임스페이스 내에서 파드와 디플로이먼트(Deployment)를 조회, 생성, 업데이트, 삭제할 수 있는 권한을 부여하는 Role을 만듭니다. 이 Role은 dev-team 네임스페이스에만 적용됩니다.

    # dev-team-role.yaml
    apiVersion: rbac.authorization.k8s.io/v1
    kind: Role
    metadata:
      name: dev-team-pod-deployment-manager
      namespace: dev-team
    rules:
    - apiGroups: ["", "apps"]
      resources: ["pods", "deployments"]
      verbs: ["get", "list", "watch", "create", "update", "patch", "delete"]
    - apiGroups: [""]
      resources: ["pods/log"]
      verbs: ["get"]
    

    파일을 저장하고 적용합니다.

    kubectl apply -f dev-team-role.yaml
    

    3. 개발팀 서비스 계정(ServiceAccount) 생성 및 RoleBinding

    실제 사용자 대신 서비스 계정(ServiceAccount)을 만들어서 권한을 부여하는 게 일반적입니다. 여기서는 dev-user-sa라는 서비스 계정을 만들고, 위에서 정의한 Role을 이 서비스 계정에 바인딩(Binding)합니다.

    # dev-team-sa.yaml
    apiVersion: v1
    kind: ServiceAccount
    metadata:
      name: dev-user-sa
      namespace: dev-team
    ---
    # dev-team-rolebinding.yaml
    apiVersion: rbac.authorization.k8s.io/v1
    kind: RoleBinding
    metadata:
      name: dev-user-pod-deployment-binding
      namespace: dev-team
    subjects:
    - kind: ServiceAccount
      name: dev-user-sa
      namespace: dev-team
    roleRef:
      kind: Role
      name: dev-team-pod-deployment-manager
      apiGroup: rbac.authorization.k8s.io
    

    파일을 저장하고 적용합니다.

    kubectl apply -f dev-team-sa.yaml
    

    이제 dev-user-sa 서비스 계정은 dev-team 네임스페이스에서 파드와 디플로이먼트를 관리할 수 있게 됩니다. 이 서비스 계정의 토큰을 활용해 개발자는 해당 권한으로 클러스터에 접근할 수 있어요. 💡 팁: 실제 사용자에게는 OIDC(OpenID Connect) 연동을 통해 사용자 인증 후 RoleBinding을 연결하는 경우가 많습니다.

    쿠버네티스 RBAC는 사용자/서비스 계정, Role/ClusterRole, 그리고 이들을 연결하는 RoleBinding/ClusterRoleBinding으로 구성되어 권한 흐름을 제어합니다.

    네트워크 정책(Network Policy), 통제된 소통의 시작 (개념 설명)

    RBAC가 “누가 무엇을 할 수 있는가”를 정의한다면, 네트워크 정책(Network Policy)은 “쿠버네티스 파드(Pod) 간에 누가 누구와 통신할 수 있는가”를 정의하는 기능입니다. 기본적으로 쿠버네티스 클러스터 내의 모든 파드는 서로 아무런 제약 없이 통신할 수 있어요. 하지만 프로덕션 환경에서는 이게 너무 위험할 수 있겠죠?

    예를 들어, 웹 프론트엔드 파드가 데이터베이스 파드에 직접 접근할 필요는 없을 거예요. API 백엔드 파드만 DB에 접근해야 하고요. 이럴 때 네트워크 정책을 사용해서 파드 간의 통신을 세밀하게 제어할 수 있습니다.

    네트워크 정책은 특정 파드(podSelector)에 적용되며, 해당 파드로 들어오는 트래픽(Ingress)과 나가는 트래픽(Egress)을 정의할 수 있습니다. 한 번 정책이 적용되면, 명시적으로 허용된 트래픽 외에는 모두 차단됩니다. 이게 핵심이에요!

    Network Policy 적용, 드디어 통신이 막히네요! (실전 구현)

    이번에는 간단한 웹 서비스 환경에서 네트워크 정책을 적용해 볼게요. 프론트엔드, 백엔드, 데이터베이스 파드가 있다고 가정해 봅시다. 목표는 다음과 같습니다:

    • 프론트엔드는 백엔드에만 접근 가능해야 합니다.
    • 백엔드는 프론트엔드와 데이터베이스에만 접근 가능해야 합니다.
    • 데이터베이스는 그 어떤 파드로부터의 Egress 트래픽도 허용하지 않습니다 (오직 백엔드로부터의 Ingress만 허용).

    먼저 예시 파드들을 생성합니다. 각각 app: frontend, app: backend, app: database 라벨을 가지고 있다고 가정할게요.

    1. 기본 차단 정책(Default Deny) (선택적)

    특정 네임스페이스의 모든 Ingress/Egress 트래픽을 기본적으로 차단하는 정책입니다. 이렇게 해두면 명시적으로 허용한 트래픽만 통과하게 되므로 보안을 강화할 수 있어요. 저는 보통 이걸 먼저 적용하고 시작합니다.

    # default-deny.yaml
    apiVersion: networking.k8s.io/v1
    kind: NetworkPolicy
    metadata:
      name: default-deny-all
      namespace: default
    spec:
      podSelector: {}
      policyTypes:
      - Ingress
      - Egress
    
    kubectl apply -f default-deny.yaml
    

    ⚠️ 주의: 이 정책을 적용하면 해당 네임스페이스의 모든 파드 통신이 끊기므로, 이후 허용 정책을 바로 적용해야 합니다!

    2. 백엔드 접근 허용 정책

    프론트엔드 파드(app: frontend)가 백엔드 파드(app: backend)에 접근하도록 허용하는 정책입니다. 백엔드 파드 입장에서는 프론트엔드로부터의 Ingress를 허용해야겠죠?

    # backend-allow-frontend.yaml
    apiVersion: networking.k8s.io/v1
    kind: NetworkPolicy
    metadata:
      name: backend-allow-frontend
      namespace: default
    spec:
      podSelector:
        matchLabels:
          app: backend
      policyTypes:
      - Ingress
      ingress:
      - from:
        - podSelector:
            matchLabels:
              app: frontend
        ports:
        - protocol: TCP
          port: 8080 # 백엔드 서비스 포트
    
    kubectl apply -f backend-allow-frontend.yaml
    

    3. 데이터베이스 접근 허용 정책

    백엔드 파드(app: backend)가 데이터베이스 파드(app: database)에 접근하도록 허용하는 정책입니다. 데이터베이스 파드 입장에서는 백엔드로부터의 Ingress를 허용합니다.

    # database-allow-backend.yaml
    apiVersion: networking.k8s.io/v1
    kind: NetworkPolicy
    metadata:
      name: database-allow-backend
      namespace: default
    spec:
      podSelector:
        matchLabels:
          app: database
      policyTypes:
      - Ingress
      ingress:
      - from:
        - podSelector:
            matchLabels:
              app: backend
        ports:
        - protocol: TCP
          port: 5432 # 데이터베이스 포트 (예: PostgreSQL)
    
    kubectl apply -f database-allow-backend.yaml
    

    이제 app: backend 파드는 app: frontend 파드와 app: database 파드에만 접근할 수 있게 됩니다. app: frontend 파드는 app: backend 파드에만 접근할 수 있고요. 다른 파드들과의 통신은 모두 차단됩니다. 드디어 통제된 소통이 시작되는 거죠!

    네트워크 정책을 통해 프론트엔드, 백엔드, 데이터베이스 파드 간의 통신 흐름을 세밀하게 제어하여 불필요한 접근을 차단하는 모습입니다.

    ⚠️ RBAC & Network Policy 설정 시 주의할 점 (트러블슈팅)

    제가 직접 겪었던 삽질 경험들을 바탕으로 몇 가지 주의사항을 알려드릴게요.

    RBAC 관련 주의사항

    • 최소 권한 원칙(Principle of Least Privilege): 항상 필요한 최소한의 권한만 부여해야 합니다. *(모든 권한)을 남발하면 안 돼요. 저도 처음에 귀찮아서 *로 줬다가 나중에 감사(Audit) 때 식은땀 좀 흘렸습니다 😅.
    • ClusterRole 오남용 금지: ClusterRole은 클러스터 전체에 영향을 미치기 때문에 정말 신중하게 사용해야 합니다. 웬만하면 Role과 RoleBinding으로 네임스페이스 단위로 권한을 관리하는 것이 좋습니다.
    • ServiceAccount 권한 관리: 파드 내부에서 쿠버네티스 API와 통신할 때 ServiceAccount가 사용됩니다. 각 파드에 적절한 ServiceAccount를 할당하고, 해당 ServiceAccount에 최소한의 권한을 가진 RoleBinding을 연결해야 합니다.
    • kubectl auth can-i 활용: 특정 서비스 계정이나 사용자가 어떤 작업을 할 수 있는지 확인하는 데 이 명령어가 정말 유용합니다. 설정 후 꼭 확인해 보세요!

    Network Policy 관련 주의사항

    • Default Deny 정책의 영향: 네임스페이스에 podSelector: {}와 policyTypes: [Ingress, Egress]가 포함된 Network Policy를 적용하면 해당 네임스페이스의 모든 파드 통신이 기본적으로 차단됩니다. 이때 시스템 파드나 kube-dns 같은 핵심 파드의 통신까지 막힐 수 있으니, 적용 전에 충분히 테스트하고 필요한 허용 정책을 바로 이어서 적용해야 합니다. 저도 이걸 모르고 적용했다가 클러스터가 먹통이 돼서 밤샘 삽질 좀 했습니다…
    • 라벨링(Labeling)의 중요성: Network Policy는 파드의 라벨을 기반으로 동작합니다. 따라서 일관성 있고 의미 있는 라벨을 파드에 잘 붙이는 것이 매우 중요합니다. 라벨링이 엉망이면 정책 적용이 어렵거나 잘못 적용될 수 있어요.
    • CNI 플러그인 확인: 모든 CNI(Container Network Interface) 플러그인이 Network Policy를 지원하는 것은 아닙니다. Calico, Cilium, Weave Net 등 대부분의 인기 있는 CNI는 지원하지만, 사용 중인 CNI가 지원하는지 확인해야 합니다.

    확인하고 넘어가시죠! (검증/결과)

    설정만 하고 끝내면 안 되겠죠? 제대로 적용되었는지 꼭 확인해야 합니다.

    RBAC 검증

    특정 서비스 계정이 특정 리소스에 대해 어떤 권한을 가지고 있는지 확인하려면 kubectl auth can-i 명령어를 사용합니다.

    # dev-team 네임스페이스에서 dev-user-sa 서비스 계정이 파드를 생성할 수 있는지 확인
    kubectl auth can-i create pods --as=system:serviceaccount:dev-team:dev-user-sa -n dev-team
    
    # dev-team 네임스페이스에서 dev-user-sa 서비스 계정이 노드를 조회할 수 있는지 확인 (권한 없음 예상)
    kubectl auth can-i get nodes --as=system:serviceaccount:dev-team:dev-user-sa -n dev-team
    

    RoleBinding이나 ClusterRoleBinding의 상세 정보를 조회하여 어떤 Role이 누구에게 바인딩되었는지도 확인할 수 있습니다.

    kubectl describe rolebinding dev-user-pod-deployment-binding -n dev-team
    

    Network Policy 검증

    먼저 적용된 네트워크 정책 목록을 확인합니다.

    kubectl get netpol -n default
    

    그리고 실제 파드에 접속해서 curl 등으로 통신 테스트를 해보는 것이 가장 확실합니다. 예를 들어, 프론트엔드 파드에서 데이터베이스 파드로 접속을 시도하면 실패해야 합니다.

    # 프론트엔드 파드에서 백엔드 파드로 요청 (성공 예상)
    k exec -it <frontend-pod-name> -- curl backend-service:8080
    
    # 프론트엔드 파드에서 데이터베이스 파드로 요청 (실패 예상)
    k exec -it <frontend-pod-name> -- curl database-service:5432
    

    이런 식으로 실제 시나리오를 만들어 검증해보면 됩니다. 🎉 드디어 됐다! 라고 외칠 때의 그 쾌감은 정말 최고죠!

    RBAC는 ‘누가 무엇을 할 수 있는가’를, 네트워크 정책은 ‘누가 누구와 통신할 수 있는가’를 제어하는 핵심적인 쿠버네티스 보안 메커니즘입니다.

    마무리하며: 안전한 쿠버네티스 클러스터를 향한 여정

    오늘은 쿠버네티스 보안(Kubernetes Security)을 강화하기 위한 핵심 요소인 RBAC 설정(Role-Based Access Control)과 네트워크 정책(Network Policy)에 대해 자세히 알아봤습니다. RBAC는 인적 오류나 악의적인 접근으로부터 클러스터 리소스를 보호하고, 네트워크 정책은 파드 간의 불필요한 통신을 차단하여 내부 공격 표면을 줄이는 데 큰 역할을 합니다.

    이 두 가지는 k8s 보안 가이드(k8s Security Guide)의 가장 기본적인 출발점이라고 할 수 있습니다. 물론 쿠버네티스 보안은 이것이 다가 아닙니다. 시크릿(Secret) 관리, Pod Security Admission(PSA), 컨테이너 이미지 보안 스캐닝, 그리고 클러스터 로깅 및 모니터링 등 고려해야 할 부분이 정말 많아요.

    하지만 오늘 다룬 RBAC와 네트워크 정책만 제대로 적용해도 여러분의 클러스터 보안(Cluster Security) 수준은 한 단계 높아질 겁니다. 제가 직접 겪어보니, 처음에는 어렵고 복잡해 보여도 하나하나 적용해 보면서 얻는 경험이 가장 중요하더라고요. 여러분도 이 가이드를 바탕으로 안전하고 튼튼한 쿠버네티스 클러스터를 만들어 가시길 응원합니다!

    다음 글에서는 쿠버네티스 시크릿 관리나 Pod Security Admission에 대해 더 깊이 다뤄볼 예정이니, 많은 관심 부탁드립니다!

  • [k8s] kubectl 고급 활용법: Pod, Service, Deployment 관리 팁

    [k8s] kubectl 고급 활용법: Pod, Service, Deployment 관리 팁

    안녕하세요, 13년차 서버실 지킴이입니다. 오늘도 서버실의 삐걱거리는 소리와 함께 하루를 시작하네요. 😊

    인프라 엔지니어로 일하다 보면, 매일같이 손에서 놓지 못하는 도구들이 몇 가지 있습니다. 그중에서도 Kubernetes(쿠버네티스) 클러스터를 운영한다면 kubectl(큐브컨트롤)은 정말 없어서는 안 될 존재거든요. <code>kubectl은 쿠버네티스 클러스터와 상호작용하는 CLI(명령줄 인터페이스) 도구로, 파드(Pod), 서비스(Service), 디플로이먼트(Deployment) 등 모든 리소스(resource)를 관리할 수 있게 해줍니다.

    근데 이거, 진짜 제대로 쓰고 있나? 하는 의문, 혹시 여러분도 해보신 적 있으신가요? 저도 처음엔 kubectl get pod, kubectl apply -f 같은 기본적인 명령어만으로 충분하다고 생각했거든요. 하지만 클러스터가 커지고 문제 상황이 복잡해질수록, 더 깊고 강력한 kubectl 활용법이 필요하다는 걸 깨달았습니다. 덕분에 삽질 좀 했습니다만, 그만큼 얻은 꿀팁들도 많았죠. ㅎㅎ

    오늘은 13년차 인프라 엔지니어인 제가 직접 현업과 홈랩에서 써보면서 얻은 kubectl 고급 활용법들을 여러분께 알려드리려고 합니다. 특히 Pod, Service, Deployment를 효과적으로 관리하는 팁과 함께, 제가 겪었던 삽질 경험과 트러블슈팅 노하우도 솔직하게 공유해 드릴게요. 이 글을 통해 여러분의 쿠버네티스 운영 생산성이 한층 더 높아지기를 바랍니다!

    쿠버네티스 Pod, Service, Deployment는 서로 유기적으로 연결되어 동작합니다. (이미지: 쿠버네티스 핵심 컴포넌트 아키텍처)

    핵심 개념 다시 보기: Pod, Service, Deployment

    본격적인 팁을 알려드리기 전에, 오늘 다룰 세 가지 핵심 리소스에 대해 간단히 짚고 넘어갈게요. 이미 잘 알고 계시겠지만, 다시 한번 머릿속에 정리하는 시간이라고 생각하시면 좋습니다.

    • Pod (파드): 쿠버네티스에서 가장 작고 기본적인 배포 단위입니다. 하나 이상의 컨테이너(Container)를 포함할 수 있으며, 같은 파드 내의 컨테이너들은 네트워크와 스토리지를 공유해요. 쉽게 말해, ‘작업을 수행하는 최소한의 묶음’이라고 보시면 됩니다.
    • Service (서비스): 여러 파드에 대한 안정적인 네트워크 접근을 제공하는 추상화 계층이죠. 파드는 언제든 생성되고 삭제될 수 있는데, 서비스는 이런 파드의 동적인 변화와 관계없이 일정한 IP 주소와 DNS 이름을 통해 파드 그룹에 접근할 수 있도록 해줍니다. 마치 변동하는 직원들에게 고정된 부서 전화번호를 할당해주는 것과 같죠.
    • Deployment (디플로이먼트): 파드와 ReplicaSet(레플리카셋)을 선언적으로 관리하는 상위 리소스입니다. 애플리케이션의 버전을 관리하고, 원하는 수의 파드를 항상 유지하며, 롤링 업데이트(Rolling Update)나 롤백(Rollback) 같은 배포 전략을 손쉽게 구현할 수 있어요. ‘애플리케이션의 배포와 생명주기를 책임지는 관리자’라고 생각하시면 편합니다.

    kubectl 고급 활용법: Pod 관리 팁

    파드는 쿠버네티스에서 가장 많이 다루는 리소스 중 하나일 겁니다. 저도 매일 파드 상태를 확인하고, 로그를 보고, 문제가 생기면 파드 안으로 들어가 보곤 하거든요. 몇 가지 유용한 명령어들을 소개합니다.

    1. 로그 실시간 확인 (Log Streaming)

    애플리케이션 개발이나 트러블슈팅 시 가장 기본이 되는 작업이죠. kubectl logs 명령어로 특정 파드의 로그를 확인할 수 있습니다. 특히 -f (follow) 옵션은 로그를 실시간으로 스트리밍해주기 때문에, 마치 tail -f처럼 동작해서 에러를 잡을 때 정말 유용해요.

    # 특정 파드의 로그 실시간 스트리밍
    kubectl logs -f my-app-pod-abcdefg
    
    # 특정 컨테이너의 로그만 보기 (파드에 여러 컨테이너가 있을 경우)
    kubectl logs -f my-app-pod-abcdefg -c my-container-name
    
    # 마지막 100줄만 보고 싶을 때
    kubectl logs --tail=100 my-app-pod-abcdefg
    
    # 특정 시간 이후 로그만 보고 싶을 때
    kubectl logs --since=1h my-app-pod-abcdefg # 1시간 이내 로그
    kubectl logs --since=2023-01-01T00:00:00Z my-app-pod-abcdefg # 특정 시각 이후 로그

    제가 개발 중 에러를 잡을 때 정말 유용하게 썼던 기능입니다. 특히 컨테이너가 갑자기 재시작(restart)될 때, -f 옵션으로 로그를 계속 보고 있으면 어떤 이유로 죽었는지 금방 파악할 수 있더라고요. 💡

    2. 컨테이너 내부 접속 (Exec into Container)

    파드 안에서 직접 명령어를 실행하거나 파일 시스템을 확인해야 할 때가 있습니다. 마치 SSH로 서버에 접속하는 것처럼, kubectl exec 명령어로 컨테이너 내부에 접속할 수 있어요.

    # 특정 파드의 컨테이너 내부로 접속 (bash 셸 사용)
    kubectl exec -it my-app-pod-abcdefg -- bash
    
    # 특정 컨테이너 내부로 접속 (파드에 여러 컨테이너가 있을 경우)
    kubectl exec -it my-app-pod-abcdefg -c my-container-name -- sh # sh 셸 사용 예시

    터미널이 없으면 답답하잖아요? 문제가 생겼을 때 바로 파드 내부로 들어가서 설정 파일을 확인하거나, 특정 명령어를 실행해서 상황을 진단해볼 수 있어서 정말 편하더라고요. ⚠️ 중요한 건 -- 뒤에 실행할 명령어를 붙여야 한다는 점입니다.

    3. 자원 사용량 확인 (Resource Usage)

    파드가 갑자기 죽거나 성능이 저하되는 원인 중 하나는 CPU나 메모리 같은 리소스 부족인 경우가 많습니다. kubectl top pod 명령어를 사용하면 파드별 리소스 사용량을 한눈에 볼 수 있어요. 다만, 이 기능을 사용하려면 클러스터에 Metrics Server(메트릭스 서버)가 설치되어 있어야 합니다.

    # 모든 파드의 CPU 및 메모리 사용량 확인
    kubectl top pod
    
    # 특정 네임스페이스의 파드만 확인
    kubectl top pod -n my-namespace
    
    # 파드를 CPU 사용량 기준으로 정렬
    kubectl top pod --sort-by=cpu

    파드가 갑자기 죽는다? 대부분 CPU나 메모리를 너무 많이 쓰는 경우가 많더라고요. kubectl top pod로 확인해서 리소스 제한(resource limit)을 조정해주곤 합니다. 💡

    4. Pod 이벤트 확인 (Event Check)

    파드가 생성되지 않거나, Pending(대기) 상태에 머무르거나, 예상치 못하게 재시작될 때 그 원인을 알려주는 것이 바로 Event(이벤트)입니다. kubectl describe pod 명령의 하단에 이벤트 정보가 자세히 나옵니다.

    # 특정 파드의 상세 정보 및 이벤트 확인
    kubectl describe pod my-app-pod-abcdefg

    왜 내 파드가 Pending에 머물러 있을까? 답은 Event에 있습니다. 예를 들어, 필요한 볼륨(Volume)이 마운트(mount)되지 않았거나, 노드의 리소스가 부족하거나, 컨테이너 이미지를 풀(pull)하지 못하는 등 다양한 원인이 이벤트로 기록돼요. 저도 처음에 이거 때문에 밤샜습니다. 결국 이벤트 로그에 답이 있더라고요!

    kubectl 고급 활용법: Service 관리 팁

    서비스는 외부 트래픽을 파드로 연결해주는 중요한 역할을 합니다. 서비스 관련 문제가 생기면 애플리케이션 전체가 먹통이 될 수 있죠. 서비스 관리 팁들을 알아볼게요.

    1. 서비스 상세 정보 (Service Details)

    서비스의 IP 주소, 포트 매핑, 연결된 파드 정보 등 상세 정보를 확인할 때 kubectl describe service 명령이 유용해요.

    # 특정 서비스의 상세 정보 확인
    kubectl describe service my-app-service

    외부에서 접근이 안 될 때, 제일 먼저 살펴보는 곳입니다. Cluster IP(클러스터 IP), External IP(외부 IP), Port(포트) 매핑 정보가 정확한지 확인하곤 합니다. 특히 Type이 NodePort나 LoadBalancer일 경우, 외부 접근이 가능해야 하는데 안 된다면 여기서 단서를 찾을 수 있죠.

    2. 서비스 포트 포워딩 (Port Forwarding)

    클러스터 내부의 서비스에 로컬 머신에서 직접 접속해야 할 때가 있습니다. 예를 들어, 개발 중인 로컬 환경에서 클러스터 내부의 데이터베이스 서비스에 연결해야 할 때 유용해요. kubectl port-forward 명령으로 로컬 포트를 서비스 포트에 연결할 수 있습니다.

    # 특정 서비스의 80포트를 로컬 8080포트로 포워딩
    kubectl port-forward service/my-app-service 8080:80
    
    # 특정 파드의 80포트를 로컬 8080포트로 포워딩 (서비스 대신 파드에 직접 연결)
    kubectl port-forward my-app-pod-abcdefg 8080:80

    홈랩에서 외부 IP 없이 내부 서비스 테스트할 때 이거 진짜 꿀팁입니다! 로컬에서 개발한 프론트엔드(frontend)가 백엔드(backend) 서비스에 잘 연결되는지 확인할 때 아주 유용하게 썼어요. 🎉

    3. 엔드포인트 확인 (Endpoint Verification)

    서비스는 파드들의 IP 주소를 모아놓은 Endpoint(엔드포인트)를 기반으로 트래픽을 라우팅합니다. 서비스는 있는데 왜 연결이 안 되지? 싶을 때는 엔드포인트가 제대로 생성되었는지 확인해야 해요. 파드가 정상적으로 Running(실행) 상태여야 엔드포인트에 등록됩니다.

    # 특정 서비스에 연결된 엔드포인트 확인
    kubectl get endpoints my-app-service
    
    # 모든 엔드포인트 확인
    kubectl get ep

    서비스는 잘 있는데 왜 연결이 안 되지? 알고 보니 엔드포인트가 비어있을 때가 있더라고요. 파드가 죽었거나, readinessProbe(레디니스 프로브)가 실패해서 엔드포인트에서 제외된 경우였습니다. ⚠️

    kubectl 고급 활용법: Deployment 관리 팁

    디플로이먼트는 애플리케이션 배포의 핵심입니다. 새로운 버전을 배포하고, 문제가 생기면 이전 버전으로 되돌리고, 트래픽 변화에 따라 파드 개수를 조절하는 등 많은 작업을 디플로이먼트를 통해 하게 됩니다.

    다양한 kubectl 명령어를 활용해 Pod, Service, Deployment의 상태를 실시간으로 모니터링하는 모습입니다. (이미지: kubectl 모니터링)

    1. 롤링 업데이트 & 롤백 (Rolling Update & Rollback)

    새로운 버전의 애플리케이션을 배포할 때, kubectl apply 명령을 사용하면 디플로이먼트가 롤링 업데이트(Rolling Update)를 수행합니다. 이 과정에서 업데이트 상태를 확인하고, 문제가 생겼을 때 이전 버전으로 롤백(Rollback)하는 것이 중요해요.

    # 디플로이먼트 롤아웃(rollout) 상태 확인
    kubectl rollout status deployment/my-app-deployment
    
    # 디플로이먼트 롤아웃 히스토리(history) 확인
    kubectl rollout history deployment/my-app-deployment
    
    # 이전 버전으로 롤백 (이전 리비전으로 되돌리기)
    kubectl rollout undo deployment/my-app-deployment
    
    # 특정 리비전으로 롤백
    kubectl rollout undo deployment/my-app-deployment --to-revision=2

    실수로 잘못된 이미지를 배포했을 때, kubectl rollout undo 이거 한 방이면 되돌릴 수 있어서 얼마나 든든한지 모릅니다. 롤아웃 상태를 보면서 파드가 하나씩 교체되는 걸 지켜보는 것도 꽤 재미있고요. 😅

    2. 스케일링 (Scaling)

    애플리케이션에 트래픽이 몰리거나 반대로 줄어들 때, 파드의 개수를 동적으로 조절해야 합니다. kubectl scale 명령어를 사용하면 디플로이먼트의 파드 개수를 쉽게 늘리거나 줄일 수 있어요.

    # 디플로이먼트의 파드 개수를 5개로 스케일링
    kubectl scale deployment/my-app-deployment --replicas=5
    
    # 오토스케일러(Horizontal Pod Autoscaler, HPA) 설정
    kubectl autoscale deployment my-app-deployment --cpu-percent=50 --min=2 --max=10

    갑자기 트래픽이 몰릴 때, 빠르게 파드를 늘려줄 수 있죠. 물론 HPA를 설정하면 자동으로 스케일링되겠지만, 수동으로 빠르게 대응해야 할 때 이 명령어가 아주 유용해요.

    3. 환경 변수/시크릿 확인 (Env/Secret Check)

    애플리케이션이 제대로 동작하지 않을 때, 환경 변수(Environment Variable)나 시크릿(Secret) 설정이 잘못되었을 가능성도 있습니다. 디플로이먼트의 상세 정보를 통해 이를 확인할 수 있어요.

    # 특정 디플로이먼트의 상세 정보 확인
    kubectl describe deployment my-app-deployment

    describe 명령의 출력에서 Environment 섹션과 Volumes 섹션을 확인하면, 어떤 환경 변수가 설정되었고 어떤 시크릿이나 컨피그맵(ConfigMap)이 마운트(mount)되었는지 알 수 있습니다. 간혹 환경 변수 오타 때문에 앱이 안 뜨는 경우도 있더라고요. 디스크라이브로 확인하면 됩니다.

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

    제가 13년 동안 인프라 엔지니어로 일하면서 수없이 겪었던 삽질 경험들 중, kubectl과 관련하여 특히 기억에 남는 몇 가지를 공유합니다. 여러분은 저처럼 밤새지 마세요! 😅

    Problem 1: 파드가 계속 Pending 상태인 경우

    새로운 디플로이먼트를 배포했는데, 파드가 Running 상태가 되지 않고 계속 Pending(대기) 상태에 머무는 경우가 있습니다. 저도 처음엔 이게 뭔가 싶어서 계속 파드를 삭제하고 다시 만들고 했었죠.

    • 원인:
      • 노드의 리소스(CPU, 메모리) 부족
      • 이미지 풀(Image Pull) 실패 (예: 잘못된 이미지 이름, 프라이빗 레지스트리 인증 실패)
      • 볼륨(PersistentVolume, PersistentVolumeClaim) 바인딩 실패
      • 노드 셀렉터(Node Selector)나 테인트/톨러레이션(Taints/Tolerations) 설정 문제로 스케줄링할 노드를 찾지 못함
    • 해결:
      # 파드 상세 정보 확인 (가장 중요!)
      kubectl describe pod <pod-name>
      # 이벤트 섹션에서 FailedScheduling, Failed to pull image 등의 에러 메시지를 찾습니다.
      
      # 노드 리소스 확인
      kubectl top nodes
      
      # 이미지 레지스트리 인증 정보 확인 (Secret)
      kubectl get secret <image-pull-secret-name> -o yaml

      결국 답은 kubectl describe pod의 Events 섹션에 있습니다. 어떤 이유로 파드가 스케줄링되지 못했는지, 이미지를 가져오지 못했는지 자세히 알려주거든요. 저도 이거 때문에 밤샜습니다. 결국 이벤트 로그에 답이 있더라고요! 💡

    Problem 2: 서비스가 외부에서 접근 안 되는 경우

    분명히 서비스를 만들었고 파드도 잘 돌고 있는데, 외부에서 접속이 안 되는 경우가 있습니다. 특히 클라우드 환경에서 많이 겪었던 문제인데요.

    • 원인:
      • 서비스 타입(Service Type) 오설정 (ClusterIP는 외부 접근 불가)
      • 로드밸런서(LoadBalancer)나 인그레스(Ingress) 컨트롤러 문제
      • 클라우드 프로바이더(Cloud Provider)의 방화벽(Firewall) 설정
      • 서비스의 selector와 파드의 labels가 일치하지 않아 엔드포인트가 비어있음
    • 해결:
      # 서비스 타입 및 외부 IP 확인
      kubectl get svc <service-name> -o wide
      
      # 엔드포인트 확인 (파드가 서비스에 제대로 연결되었는지)
      kubectl get ep <service-name>
      
      # 서비스 상세 정보 확인
      kubectl describe svc <service-name>

      로드밸런서 타입으로 만들었는데 왜 안 돼? 알고 보니 클라우드 provider 설정 문제였던 적도 있었어요. 예를 들어, GCP나 AWS에서 로드밸런서가 프로비저닝(provisioning)되지 않았거나, 보안 그룹(Security Group) 설정이 잘못되어 있었죠. 😅 항상 엔드포인트와 서비스 타입을 먼저 확인하는 습관을 들이세요!

    Problem 3: Deployment 롤아웃이 멈춘 경우

    새로운 버전으로 디플로이먼트를 업데이트했는데, 롤아웃이 중간에 멈춰버리고 진행되지 않는 경험, 다들 있으시죠?

    • 원인:
      • 새로 생성된 파드가 CrashLoopBackOff 상태로 계속 죽음
      • 파드의 readinessProbe(레디니스 프로브)가 실패하여 새 파드가 Ready 상태가 되지 못함
      • 리소스 부족으로 새 파드가 생성되지 못함
    • 해결:
      # 롤아웃 상태 확인
      kubectl rollout status deployment/<deployment-name>
      
      # 새로운 파드의 이름 확인 (롤아웃 중 생성된 파드)
      kubectl get pod -l app=<app-label> --sort-by=.metadata.creationTimestamp
      
      # 문제의 파드 로그 및 이벤트 확인
      kubectl logs <new-pod-name>
      kubectl describe pod <new-pod-name>

      새로운 이미지가 문제가 있어서 기존 파드도 못 죽고 롤아웃이 멈춰버린 경험, 다들 있으시죠? 저도 몇 번 겪었습니다. 이럴 때는 새로 생성되는 파드의 로그와 이벤트를 확인해서 왜 Ready 상태가 되지 못하는지 파악하는 것이 중요해요. 그리고 문제가 파악되면 kubectl rollout undo로 빠르게 이전 버전으로 롤백하는 것이 현명한 방법입니다!

    쿠버네티스 환경에서 흔히 겪을 수 있는 트러블슈팅 상황을 한눈에 볼 수 있습니다. (이미지: 쿠버네티스 트러블슈팅)

    검증 및 결과 확인

    위에서 배운 명령어들을 활용하여 변경 사항이 잘 적용되었는지, 클러스터의 상태는 정상적인지 확인하는 것이 중요합니다. 주로 사용하는 명령어는 다음과 같아요.

    # 모든 네임스페이스의 모든 리소스 확인 (초보자에게 추천!)
    kubectl get all --all-namespaces
    
    # 특정 네임스페이스의 주요 리소스 확인 (파드, 서비스, 디플로이먼트, 레플리카셋)
    kubectl get deploy,svc,pod,rs -n my-namespace
    
    # 특정 리소스의 자세한 정보 확인 (IP 주소, 노드 정보 등)
    kubectl get deploy,svc,pod -o wide -n my-namespace

    이 명령어들로 제가 고친 설정이 잘 반영되었는지, 파드들이 정상적으로 돌고 있는지, 외부 IP는 제대로 할당되었는지 확인하곤 합니다. 특히 -o wide 옵션은 더 많은 정보를 보여줘서 매우 편리해요. ✅

    오늘 다룬 kubectl 고급 활용 팁들을 한 장으로 요약한 인포그래픽입니다. (이미지: kubectl 팁 요약)

    마무리하며: `kubectl` 마스터가 되는 그날까지!

    오늘은 13년차 인프라 엔지니어의 경험을 담아 kubectl 고급 활용법에 대해 알아봤습니다. Pod, Service, Deployment를 관리할 때 유용하게 사용할 수 있는 명령어들과 함께, 제가 직접 겪었던 삽질 경험과 트러블슈팅 노하우까지 솔직하게 공유해 드렸네요.

    사실 kubectl은 알면 알수록 강력한 도구거든요. 기본적인 명령어만 쓰기엔 아까운 기능들이 너무 많아요. 클러스터의 모든 것을 kubectl 하나로 제어할 수 있다고 해도 과언이 아닙니다. 저도 처음엔 헷갈렸지만, 꾸준히 사용하고 궁금한 점은 직접 찾아보면서 익혔습니다. 여러분도 저처럼 삽질하면서 얻은 팁들을 활용해서 kubectl 마스터가 되시길 바랍니다! 🎉

    다음에는 kubectl 플러그인이나 K9s 같은 터미널 UI 도구들에 대해서도 다뤄보면 좋겠네요. 쿠버네티스 운영에 또 다른 재미를 더해줄 도구들이거든요. 긴 글 읽어주셔서 감사합니다. 다음에도 더 유익한 정보로 찾아오겠습니다! 😉

  • [k8s] kubectl 명령어 완벽 가이드: 클러스터 관리 필수 명령어

    kubectl 명령어, 13년 경력자도 매일 찾아보는 이유

    쿠버네티스(Kubernetes)를 처음 배울 때 가장 먼저 맞닥뜨리는 게 바로 kubectl 명령어입니다. 저도 처음엔 이게 뭔가 싶었거든요. 도커(Docker)는 어느 정도 익숙해졌는데, kubectl 앞에서는 또 새로 공부해야 하나 싶은 막막함이 있었어요.

    근데 솔직히 말씀드리면, 13년 경력인 저도 지금도 자주 찾아봅니다. 워낙 옵션이 많고, 상황마다 쓰는 명령어가 다르거든요. 그래서 이번엔 제가 실무에서 진짜 자주 쓰는 명령어들, 그리고 “이게 왜 안 되지?”하고 삽질했던 경험까지 싹 정리해봤습니다.

    kubectl 사용법을 제대로 익혀두면 쿠버네티스 클러스터 관리가 훨씬 수월해집니다. 이 글에서는 클러스터 조회부터 디버깅, 리소스 관리까지 실무에서 바로 쓸 수 있는 필수 명령어들을 카테고리별로 정리해드릴게요.

    kubectl 명령어는 크게 조회, 생성/수정, 삭제, 디버깅, 클러스터 관리 카테고리로 나뉩니다. 각 카테고리별로 필수 명령어를 익혀두면 쿠버네티스 클러스터 관리가 훨씬 쉬워집니다.


    kubectl이 뭔지 잠깐만 짚고 가요

    쉽게 말해, kubectl은 쿠버네티스 클러스터에 명령을 내리는 CLI(Command Line Interface, 명령줄 인터페이스) 도구입니다. 쿠버네티스 API 서버와 통신하면서 파드(Pod), 서비스(Service), 디플로이먼트(Deployment) 같은 리소스들을 만들고, 조회하고, 수정하고, 삭제하는 역할을 하죠.

    기본 구조는 이렇습니다:

    kubectl [명령어] [리소스 타입] [리소스 이름] [옵션]

    예를 들어 kubectl get pods my-app이라고 하면, “파드(pods) 중에서 my-app이라는 걸 조회(get)해줘”라는 뜻이에요. 구조 자체는 단순한데, 옵션이 어마어마하게 많아서 처음엔 좀 헷갈립니다 ㅎㅎ.


    1. 클러스터 기본 정보 조회 명령어

    가장 먼저 클러스터 상태부터 확인해야죠. 제가 새 환경에 접속하면 제일 먼저 치는 명령어들입니다.

    1-1. 클러스터 정보 확인

    # 클러스터 기본 정보
    kubectl cluster-info
    
    # 현재 컨텍스트(context, 연결된 클러스터) 확인
    kubectl config current-context
    
    # 모든 컨텍스트 목록
    kubectl config get-contexts
    
    # 컨텍스트 전환 (멀티 클러스터 관리 시 필수!)
    kubectl config use-context [컨텍스트명]
    
    # API 서버 버전 확인
    kubectl version --short

    💡 팁: 멀티 클러스터 환경에서 컨텍스트 전환을 자주 하다 보면 실수로 운영 클러스터에 명령어를 날리는 경우가 생깁니다. 저도 한 번 그랬거든요… 그래서 저는 프롬프트에 현재 컨텍스트를 항상 표시해두는 편이에요.

    1-2. 노드(Node) 조회

    # 노드 목록 조회
    kubectl get nodes
    
    # 노드 상세 정보 (IP, OS, 역할 포함)
    kubectl get nodes -o wide
    
    # 특정 노드 상세 정보
    kubectl describe node [노드명]
    
    # 노드 리소스 사용량 (metrics-server 설치 필요)
    kubectl top nodes

    2. 파드(Pod) 관련 필수 명령어

    실무에서 가장 많이 쓰는 건 역시 파드 관련 명령어입니다. 파드는 쿠버네티스에서 컨테이너가 실행되는 가장 작은 단위거든요.

    2-1. 파드 조회

    # 현재 네임스페이스의 파드 목록
    kubectl get pods
    
    # 모든 네임스페이스의 파드 조회 (전체 현황 파악 시 유용)
    kubectl get pods --all-namespaces
    # 또는 축약형
    kubectl get pods -A
    
    # 특정 네임스페이스의 파드 조회
    kubectl get pods -n [네임스페이스명]
    
    # IP, 노드 정보 포함해서 조회
    kubectl get pods -o wide
    
    # 실시간 상태 변화 감시 (watch)
    kubectl get pods -w
    
    # 특정 라벨(label)로 필터링
    kubectl get pods -l app=my-app

    2-2. 파드 상세 정보 및 로그

    # 파드 상세 정보 (Events 섹션이 트러블슈팅의 핵심!)
    kubectl describe pod [파드명]
    
    # 파드 로그 조회
    kubectl logs [파드명]
    
    # 실시간 로그 스트리밍 (tail -f 같은 느낌)
    kubectl logs -f [파드명]
    
    # 최근 100줄만 조회
    kubectl logs --tail=100 [파드명]
    
    # 멀티 컨테이너 파드에서 특정 컨테이너 로그
    kubectl logs [파드명] -c [컨테이너명]
    
    # 이전에 크래시된 컨테이너 로그 (재시작 원인 파악 시 필수)
    kubectl logs [파드명] --previous

    ⚠️ 주의: --previous 옵션은 파드가 재시작된 경우에만 동작합니다. 파드가 삭제되고 새로 생성된 경우엔 이전 로그를 볼 수 없어요. 로그 영구 보존이 필요하면 Loki나 Elasticsearch 같은 로그 수집 시스템을 별도로 구축해야 합니다.

    2-3. 파드 내부 접속 및 실행

    # 파드 내부에 bash 셸로 접속
    kubectl exec -it [파드명] -- /bin/bash
    
    # bash가 없는 경우 (alpine 기반 이미지 등)
    kubectl exec -it [파드명] -- /bin/sh
    
    # 파드 안에서 단일 명령어 실행
    kubectl exec [파드명] -- env
    
    # 특정 컨테이너에 접속
    kubectl exec -it [파드명] -c [컨테이너명] -- /bin/bash

    처음엔 -it 옵션이 뭔가 싶었는데, -i는 interactive(대화형), -t는 tty(터미널 할당)입니다. 셸로 접속할 때는 둘 다 붙여줘야 제대로 동작해요.


    3. 리소스 생성 및 수정 명령어

    조회만 할 줄 알면 반쪽짜리죠. 실제로 리소스를 만들고 수정하는 kubectl 명령어도 알아야 합니다.

    3-1. 리소스 생성

    # YAML 파일로 리소스 생성
    kubectl apply -f [파일명.yaml]
    
    # 디렉토리 내 모든 YAML 적용
    kubectl apply -f ./manifests/
    
    # URL에서 직접 적용 (공식 예제 실습 시 자주 씀)
    kubectl apply -f https://example.com/manifest.yaml
    
    # 명령형으로 디플로이먼트 생성 (빠른 테스트용)
    kubectl create deployment my-app --image=nginx:1.21
    
    # 네임스페이스 생성
    kubectl create namespace my-namespace

    💡 apply vs create 차이: create는 이미 존재하면 에러가 나고, apply는 없으면 생성, 있으면 업데이트합니다. 실무에서는 apply를 훨씬 많이 씁니다. GitOps 방식으로 쿠버네티스 클러스터를 관리할 때 특히 유용하더라고요.

    3-2. 리소스 수정

    # 리소스를 에디터로 직접 수정
    kubectl edit deployment [디플로이먼트명]
    
    # 이미지 변경 (롤링 업데이트 트리거)
    kubectl set image deployment/[디플로이먼트명] [컨테이너명]=[새이미지:태그]
    
    # 레플리카(replica, 복제본) 수 조정
    kubectl scale deployment [디플로이먼트명] --replicas=3
    
    # 특정 필드를 JSON Patch로 수정
    kubectl patch deployment [디플로이먼트명] -p '{"spec":{"replicas":5}}'

    kubectl apply 명령어가 실행되면 API 서버를 통해 etcd에 저장되고, 컨트롤러가 원하는 상태로 파드를 조정하는 전체 흐름을 나타낸 다이어그램입니다.

    3-3. 롤아웃(Rollout, 배포) 관리

    # 배포 상태 확인
    kubectl rollout status deployment/[디플로이먼트명]
    
    # 배포 이력 조회
    kubectl rollout history deployment/[디플로이먼트명]
    
    # 이전 버전으로 롤백 (장애 시 생명줄!)
    kubectl rollout undo deployment/[디플로이먼트명]
    
    # 특정 버전으로 롤백
    kubectl rollout undo deployment/[디플로이먼트명] --to-revision=2
    
    # 배포 일시 중지/재개
    kubectl rollout pause deployment/[디플로이먼트명]
    kubectl rollout resume deployment/[디플로이먼트명]

    롤백 명령어는 진짜 실무에서 몇 번 써봤는데, 쓸 때마다 심장이 쫄깃해집니다 ㅎㅎ. rollout undo 치고 배포 상태 보면서 “제발 됩시다…”하는 그 느낌 있잖아요.


    4. 서비스 및 네트워크 관련 명령어

    4-1. 서비스 조회 및 포트 포워딩

    # 서비스 목록 조회
    kubectl get services
    # 축약형
    kubectl get svc
    
    # 서비스 상세 정보
    kubectl describe svc [서비스명]
    
    # 포트 포워딩 (로컬에서 클러스터 내부 서비스 접근 시)
    kubectl port-forward svc/[서비스명] 8080:80
    
    # 파드에 직접 포트 포워딩
    kubectl port-forward pod/[파드명] 8080:80
    
    # 인그레스(Ingress) 조회
    kubectl get ingress
    kubectl get ing

    포트 포워딩은 개발/디버깅 환경에서 정말 자주 씁니다. 로드밸런서 없이 로컬에서 서비스를 바로 테스트할 수 있거든요. 실무에서 운영 환경 직접 접근이 막혀있을 때 이만큼 편한 게 없더라고요.


    5. kubectl 디버깅 필수 명령어

    이 섹션이 사실 제일 중요합니다. 쿠버네티스 클러스터 관리하다 보면 문제가 안 생기는 날이 없거든요. kubectl 디버깅 명령어를 제대로 알아두면 장애 시간을 확 줄일 수 있습니다.

    5-1. 이벤트 조회

    # 클러스터 이벤트 전체 조회 (문제 파악의 시작)
    kubectl get events
    
    # 특정 네임스페이스 이벤트
    kubectl get events -n [네임스페이스]
    
    # 시간순 정렬
    kubectl get events --sort-by='.lastTimestamp'
    
    # Warning 이벤트만 필터링
    kubectl get events --field-selector type=Warning

    5-2. 리소스 사용량 및 상태 확인

    # 파드별 CPU/메모리 사용량 (metrics-server 필요)
    kubectl top pods
    kubectl top pods -A
    
    # 특정 네임스페이스
    kubectl top pods -n [네임스페이스]
    
    # 리소스 할당량 확인
    kubectl describe resourcequota -n [네임스페이스]
    
    # PVC(PersistentVolumeClaim, 영구 볼륨 요청) 상태
    kubectl get pvc
    kubectl get pv

    5-3. 임시 디버그 파드 실행

    # 네트워크 디버깅용 임시 파드 실행
    kubectl run debug-pod --image=busybox --rm -it --restart=Never -- sh
    
    # curl이 있는 이미지로 HTTP 테스트
    kubectl run curl-test --image=curlimages/curl --rm -it --restart=Never -- sh
    
    # 특정 노드에 디버그 파드 배치
    kubectl run debug-pod --image=busybox --rm -it --restart=Never \
      --overrides='{"spec":{"nodeName":"[노드명]"}}' -- sh