13년차의 서버실

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

[태그:] 배포 자동화

  • [Cloud] GitLab CI 마이그레이션 회고: 전환 결정 기준

    [Cloud] GitLab CI 마이그레이션 회고: 전환 결정 기준

    GitLab CI 마이그레이션 회고: 전환 결정 기준

    GitLab CI 마이그레이션, YAML 변환보다 먼저 바뀌는 것

    GitLab CI 마이그레이션을 몇 번 해보면 초반 착각이 거의 비슷합니다. 기존 Jenkinsfile이나 사내 CI 스크립트를 .gitlab-ci.yml로 옮기면 끝날 것 같지만, 실제로 흔들리는 지점은 YAML 문법이 아니라 실행 환경, 권한 경계, 캐시 수명, 배포 승인 습관입니다.

    제가 제일 경계하는 실패는 빨간 파이프라인이 아닙니다. 차라리 실패는 빨리 보이거든요. 더 위험한 건 성공처럼 보이는데 산출물이 예전과 다른 경우입니다. 예를 들어 기존 빌드 서버에는 전역 패키지, 로컬 캐시, SSH known_hosts, 사내 CA 인증서가 이미 깔려 있었는데 GitLab Runner의 Docker Executor에서는 전부 사라질 수 있습니다.

    그래서 저는 전환을 시작할 때 ‘CI 도구를 바꾼다’고 보지 않습니다. 빌드 지식을 어디에 둘 것인가, 운영 배포 권한을 누가 어떤 조건에서 행사할 것인가, 실패 로그를 누가 재현 가능한 방식으로 읽을 것인가를 다시 정하는 작업으로 봅니다. 결국 GitLab CI는 도구 선택보다 팀의 배포 습관을 저장소 중심으로 다시 쓸 준비가 되어 있는지의 문제에 가깝더라고요.

    GitLab CI 마이그레이션 전체 흐름 아키텍처 다이어그램

    기존 CI 도구에서 GitLab CI로 넘어갈 때 함께 이동하는 요소를 한눈에 보는 개요 다이어그램입니다.

    GitLab CI 마이그레이션이 바꾸는 운영 경계

    GitLab CI/CD의 핵심은 저장소 안의 .gitlab-ci.yml을 기준으로 Job, Stage, Pipeline을 선언하는 데 있습니다. 그런데 실무에서 중요한 변화는 정의 파일의 위치가 아니라 책임의 위치입니다. 예전에는 빌드 서버 안에 있던 지식이 저장소로 들어오고, 운영팀 개인 계정에 묶여 있던 배포 절차가 Job과 Environment로 드러납니다.

    저는 전환 전에 기존 CI를 다음 네 덩어리로 분해합니다. 이 과정을 생략하면 나중에 ‘왜 Jenkins에서는 됐는데 GitLab에서는 안 되죠?’라는 질문만 반복됩니다.

    • 실행 환경: OS 패키지, 런타임 버전, Docker-in-Docker 사용 여부, 사내 인증서, DNS, 프록시 설정입니다.
    • 상태 저장 지점: 캐시, 아티팩트, 빌드 번호, 릴리스 노트, 컨테이너 이미지 태그가 어디에 남는지입니다.
    • 권한 경계: 배포 토큰, 레지스트리 인증, 클라우드 IAM, SSH 키, protected branch/tag 조건입니다.
    • 사람의 개입: 운영 배포 승인, 장애 시 재실행 기준, 롤백 명령을 누가 실행하는지입니다.

    이 네 가지가 정리되어 있으면 GitLab CI 문법은 금방 따라옵니다. 반대로 이게 흐릿하면 문법을 아무리 예쁘게 써도 파이프라인은 오래 못 갑니다. 관련해서는 이전에 정리한 CI/CD 체크리스트 글과 함께 보면 결정 기준을 더 빨리 잡을 수 있습니다.

    마이그레이션 결정 요소: 저는 이 표를 먼저 채웁니다

    도구 비교표보다 먼저 보는 건 팀의 현재 배포 습관입니다. 같은 GitLab CI라도 저장소가 이미 GitLab에 있는 팀과, Jenkins 플러그인에 배포 지식이 잔뜩 들어 있는 팀의 난이도는 완전히 다릅니다.

    결정 요소 이럴 땐 전환 우선 이럴 땐 보류 또는 Shadow 운영 실패 모드
    저장소 위치 코드와 MR 리뷰가 이미 GitLab 중심입니다. 여러 SCM에 코드가 흩어져 있고 미러링 정책이 없습니다. 커밋 기준과 빌드 기준이 달라 추적성이 깨집니다.
    Runner 운영 Docker 또는 Kubernetes 기반 격리 실행을 운영할 사람이 있습니다. 빌드 서버가 한 대뿐이고 전역 설치 도구에 강하게 의존합니다. 특정 서버에서만 되는 빌드가 됩니다.
    Secret 관리 토큰 목록과 소유자가 파악되어 있고 변수 범위를 재설계할 수 있습니다. 개인 계정 SSH 키, 오래된 API 토큰, 스크립트 내 평문 값이 섞여 있습니다. Job은 성공하지만 과도한 권한으로 배포됩니다.
    배포 승인 운영 배포 전 승인자와 브랜치 정책이 명확합니다. main push가 곧 운영 반영인데 롤백 절차가 문서화되어 있지 않습니다. 마이그레이션 첫 주에 자동 배포 사고가 납니다.
    외부 연동 컨테이너 레지스트리, 패키지 저장소, 클라우드 계정 접근 방식이 표준화되어 있습니다. Jenkins 플러그인이나 사내 스크립트가 인증을 대신 처리합니다. YAML에는 문제가 없는데 인증 단계에서 계속 pending 또는 401이 납니다.

    제 기준은 단순합니다. GitLab CI로 옮겼을 때 배포 이력과 권한이 더 잘 보이면 전환 가치가 큽니다. 반대로 기존 CI가 레거시 시스템의 접착제 역할을 하고 있다면, 전환이 아니라 먼저 분해가 필요합니다.

    실전 구현: 기존 파이프라인을 작게 쪼개 옮기는 순서

    처음부터 운영 배포까지 한 번에 옮기면 원인 추적이 지옥이 됩니다. 저는 테스트 전용 파이프라인, 산출물 생성, 수동 배포, 기존 CI 제거 순서로 갑니다. 느려 보이지만 장애 복구 시간을 줄여줍니다.

    1. 기존 CI 작업을 빌드, 테스트, 패키징, 이미지 빌드, 배포, 알림으로 나눕니다.
    2. 각 단계가 읽는 파일, 쓰는 산출물, 필요한 환경 변수를 표로 만듭니다.
    3. Runner Executor를 정합니다. 서버 상태 의존을 줄이고 싶으면 Docker Executor를 먼저 봅니다.
    4. 배포 없는 테스트 Job부터 GitLab CI에 붙입니다.
    5. 아티팩트와 캐시를 분리합니다. 결과물은 artifacts, 재사용 의존성은 cache입니다.
    6. 운영 배포는 rules, when: manual, protected branch/tag를 함께 설계합니다.

    아래 예시는 Node.js 프로젝트를 전제로 한 출발점입니다. 의도적으로 only 대신 rules를 썼습니다. GitLab 문서에서도 새 조건 설계는 rules 사용을 권장하고, only/except는 deprecated 키워드로 안내합니다.

    stages:
      - test
      - build
      - deploy
    
    default:
      image: node:20
      interruptible: true
      before_script:
        - node --version
        - npm --version
    
    cache:
      key:
        files:
          - package-lock.json
      paths:
        - .npm/
      policy: pull-push
    
    test:
      stage: test
      script:
        - npm ci --cache .npm --prefer-offline
        - npm test
      artifacts:
        when: always
        paths:
          - junit.xml
        reports:
          junit: junit.xml
        expire_in: 1 week
    
    build:
      stage: build
      script:
        - npm ci --cache .npm --prefer-offline
        - npm run build
      artifacts:
        paths:
          - dist/
        expire_in: 1 week
      needs:
        - job: test
          artifacts: false
    
    deploy_production:
      stage: deploy
      image: alpine:latest
      needs:
        - job: build
          artifacts: true
      script:
        - echo 'deploy script goes here'
      environment:
        name: production
      rules:
        - if: '$CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH'
          when: manual
        - when: never

    여기서 needs는 단순한 보기 좋은 문법이 아닙니다. Stage 순서를 무작정 기다리지 않고 필요한 Job 관계를 명시합니다. 다만 남발하면 의존 그래프가 복잡해져 장애 때 읽기 어려워집니다. 빌드 시간이 길고 독립 테스트가 많은 저장소에는 적극적으로 쓰고, 작은 서비스에는 Stage만으로 단순하게 유지하는 편이 낫더라고요.

    GitLab CI 마이그레이션에서 Runner와 설정 파일 구성 관계도

    .gitlab-ci.yml, Runner, Job, Artifact, Cache가 어떻게 연결되는지 보여주는 구성 다이어그램입니다.

    GitLab CI Runner 등록과 검증: 토큰 방식부터 확인하세요

    Self-managed Runner를 쓴다면 등록 단계에서 예전 블로그 글을 그대로 따라 하면 막힐 수 있습니다. Runner registration token 방식은 deprecated 상태이며, 현재 GitLab Runner 등록 흐름은 GitLab UI나 API에서 Runner를 만든 뒤 발급되는 runner authentication token을 사용하는 쪽이 기준입니다. 명령어 예시도 --registration-token이 아니라 --token을 기준으로 잡는 게 안전합니다.

    sudo gitlab-runner register \
      --url https://gitlab.example.com \
      --token glrt-REPLACE_WITH_RUNNER_AUTH_TOKEN \
      --executor docker \
      --docker-image alpine:latest \
      --description docker-runner-01

    Runner 태그와 protected 설정은 GitLab UI에서 관리되는 경우가 많습니다. 등록 명령만 성공했다고 Job이 실행되는 건 아닙니다. Pipeline이 계속 pending이면 저는 아래 순서로 봅니다.

    sudo gitlab-runner status
    sudo gitlab-runner verify
    sudo gitlab-runner list
    sudo gitlab-runner --debug run

    verify가 통과하면 GitLab 서버와 Runner의 통신은 일단 됩니다. 그래도 Job이 안 잡히면 네트워크보다 태그 매칭, protected branch/tag, Runner scope, locked 설정을 먼저 봅니다. Job에 tags가 있는데 Runner에 같은 태그가 없으면 정상적으로 대기합니다. 이건 장애가 아니라 스케줄링 조건 불일치입니다.

    test_with_tagged_runner:
      stage: test
      tags:
        - docker
        - linux
      image: alpine:latest
      script:
        - echo 'runner tag matched'
      rules:
        - if: '$CI_PIPELINE_SOURCE == "merge_request_event"'
        - if: '$CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH'

    Executor 선택도 비용과 안정성에 직접 영향을 줍니다. Shell Executor는 빠르고 단순하지만 호스트 오염 위험이 큽니다. Docker Executor는 격리가 좋아 마이그레이션 검증에 유리하지만 이미지 pull, 캐시, Docker 데몬 접근 정책을 설계해야 합니다. Kubernetes Executor는 확장성은 좋지만 클러스터 운영 역량이 없으면 CI 문제가 곧 플랫폼 문제가 됩니다.

    트러블슈팅: 겉으로는 CI 문제, 뿌리는 운영 습관 문제

    로컬에서는 되는데 CI에서 깨지는 상황은 대부분 암묵적 환경 때문입니다. 제가 실제로 본 문제들은 문법 오류보다 환경 차이, 권한 범위, 캐시 오염이 훨씬 많았습니다. 이거 진짜 한 번 겪으면, 다음 마이그레이션부터는 환경 목록부터 보게 됩니다.

    1. 변수 이름은 같지만 범위가 달랐습니다

    기존 CI에서 전역 토큰 하나를 모든 프로젝트가 공유하던 팀이 있었습니다. GitLab으로 옮기면서 프로젝트 변수에 넣었는데, 실제 배포 Job은 하위 템플릿 프로젝트에서 실행되고 있었습니다. 이름은 같은데 스코프가 달라 값이 비어 있었던 겁니다. 해결은 Group Variables로 올릴 값과 Project Variables로 제한할 값을 분리하고, 운영 배포용 변수는 protected branch/tag에서만 노출되도록 하는 것이었습니다.

    git grep -n -E 'DEPLOY|TOKEN|PASSWORD|SECRET|PRIVATE_KEY' -- . ':!node_modules' ':!dist'
    git grep -n -E 'ssh|scp|rsync|kubectl|helm|aws|gcloud|az' -- . ':!node_modules' ':!dist'

    이 명령은 마이그레이션 전에 꼭 돌려봅니다. 결과가 많이 나온다는 건 나쁜 게 아닙니다. 숨어 있던 배포 지식을 드러낸 겁니다. 다만 평문 Secret이 나오면 즉시 회수, 재발급, 변수화까지 같이 해야 합니다. 단순히 YAML에서 지우는 건 보안 조치가 아닙니다.

    2. 캐시가 실패를 숨겼습니다

    node_modules를 통째로 캐시하면 처음엔 빨라 보입니다. 그런데 lock file이 바뀌었는데도 오래된 의존성이 남거나, Runner OS/아키텍처가 바뀌며 네이티브 모듈이 깨지는 경우가 있습니다. 그래서 저는 패키지 매니저 캐시 디렉터리를 캐시하고, 설치 결과 디렉터리를 산출물처럼 다루지 않습니다. npm이면 .npm/, pip이면 wheel/cache 계열처럼 재다운로드 비용을 줄이는 쪽이 낫습니다.

    3. 아티팩트와 캐시를 섞었습니다

    캐시는 다음 파이프라인에도 재사용될 수 있는 가속 장치입니다. 아티팩트는 이번 파이프라인의 결과물입니다. 배포에 필요한 dist/, 패키지 파일, 테스트 리포트는 cache가 아니라 artifacts에 둬야 합니다. 반대로 의존성 다운로드 캐시는 artifacts에 넣으면 보관 비용과 다운로드 시간이 불필요하게 커집니다.

    4. 배포 자동화를 너무 빨리 켰습니다

    마이그레이션 첫 주에는 운영 배포를 when: manual로 둡니다. 저는 이 기간에 로그 포맷, 배포 산출물, 롤백 명령, 승인자 권한을 확인합니다. 자동화는 마지막에 켜도 늦지 않습니다. 검증 안 된 자동 배포는 속도가 아니라 부채입니다.

    GitLab CI 마이그레이션 검증 기준

    마이그레이션 완료 기준을 ‘성공 표시가 떴다’로 잡으면 위험합니다. 저는 같은 커밋에서 같은 산출물이 나오는지, 누가 배포를 눌렀는지, 실패 로그만 보고 원인을 좁힐 수 있는지를 봅니다.

    검증 항목 확인 방법 통과 기준
    재현성 같은 commit SHA로 기존 CI와 GitLab CI 산출물 파일 목록을 비교합니다. 파일 누락, 이름 규칙, 환경별 설정 차이가 설명 가능합니다.
    추적성 Pipeline, Job, Environment, Artifact 링크로 배포 이력을 따라갑니다. 운영 반영 커밋과 실행자를 역추적할 수 있습니다.
    권한 분리 protected branch/tag, protected variables, manual job 권한을 확인합니다. 일반 개발자가 운영 Secret을 읽거나 배포하지 못합니다.
    실패 해석 실패 명령 바로 위의 환경 출력, 버전, 인증 로그를 봅니다. 네트워크, 인증, 의존성, 스크립트 오류 중 하나로 빠르게 좁힙니다.

    로그를 볼 때는 마지막 줄만 보면 안 됩니다. npm ci가 실패했다면 registry 인증, lock file, 네트워크, Node 버전 차이를 봅니다. docker build가 실패했다면 Dockerfile 단계와 build context를 확인합니다. kubectl apply가 실패했다면 kubeconfig 권한, namespace, server-side validation을 분리해서 봅니다.

    git diff --name-only origin/main...HEAD
    find dist -type f | sort > dist-files.txt
    sha256sum package-lock.json dist-files.txt
    printenv | sort | grep -E '^CI_|^GITLAB_|^NODE_|^NPM_'

    로컬에서 Runner를 흉내 내는 방식은 빠른 확인에는 도움이 되지만, GitLab 서버의 모든 동작을 완전히 재현한다고 믿으면 안 됩니다. 특히 protected variables, merge request pipeline 조건, 환경 승인, Runner 스케줄링은 실제 GitLab Pipeline에서 확인해야 합니다.

    GitLab CI 파이프라인 검증과 로그 분석 화면

    파이프라인 성공 여부, 실패 로그, Artifact 확인 흐름을 보여주는 검증 화면 예시입니다.

    CI/CD 전환 전략: 병행 운영이 느린 게 아니라 싸게 먹힙니다

    제가 가장 효과를 본 방식은 기존 CI를 바로 끄지 않는 겁니다. GitLab CI를 먼저 읽기 전용으로 붙이고, 같은 커밋에 대해 테스트와 산출물을 비교합니다. 이 기간은 낭비가 아닙니다. 기존 자동화에 숨어 있던 전역 의존성, 권한 누수, 오래된 배포 스크립트를 찾는 보험입니다.

    1. Inventory 단계: 기존 Job, Secret, 산출물, 외부 연동을 목록화합니다.
    2. Read-only 단계: GitLab CI에서 테스트만 돌리고 배포는 하지 않습니다.
    3. Shadow 단계: 기존 CI와 GitLab CI를 같은 커밋에서 같이 돌립니다.
    4. Manual Deploy 단계: 운영 배포를 수동 승인 Job으로 연결합니다.
    5. Primary 단계: GitLab CI를 기본 파이프라인으로 바꾸고 기존 CI 트리거를 중지합니다.
    6. Cleanup 단계: 기존 토큰, 빌드 서버 권한, 스케줄러, 웹훅을 제거합니다.

    성능과 비용을 볼 때도 단순히 ‘GitLab CI가 빠른가’로 보면 답이 흐립니다. 비용을 좌우하는 건 Runner 대수보다 실패 재실행 횟수, 이미지 pull 정책, 캐시 설계, 병렬화된 Job의 대기 시간입니다. 작은 서비스는 단순한 Stage 기반 파이프라인이 더 안정적이고, 큰 모노레포는 rules:changes, needs, child pipeline을 검토할 가치가 있습니다. 다만 child pipeline은 관측 포인트가 늘어나므로 팀이 로그를 읽을 준비가 된 뒤에 넣는 편이 좋습니다.

    자주 묻는 질문

    GitLab CI 마이그레이션은 언제 하는 게 좋나요?

    저는 코드가 GitLab에 있고, Merge Request 중심으로 리뷰하며, 빌드와 배포 이력을 커밋 기준으로 추적하고 싶을 때 추천합니다. 특히 운영 배포 승인과 결과물을 GitLab 안에서 보고 싶은 팀은 전환 효과가 큽니다.

    Jenkins에서 바로 GitLab CI로 옮겨도 괜찮나요?

    가능하지만 Jenkinsfile을 줄 단위로 번역하면 실패하기 쉽습니다. 먼저 stage 구조, 플러그인 의존성, credential 사용 위치, workspace에 남는 파일을 분해해야 합니다. Jenkins 플러그인이 해주던 일을 GitLab CI에서는 YAML, Runner 설정, 외부 CLI, protected variables 조합으로 다시 설계하는 경우가 많습니다.

    가장 먼저 확인할 설정은 뭔가요?

    Runner 태그, protected branch/tag, CI/CD Variables 범위, artifacts 경로, cache key를 먼저 봅니다. 제 경험상 초반 문제의 상당수는 이 다섯 가지에서 나왔습니다.

    Shell Executor와 Docker Executor 중 무엇을 골라야 하나요?

    기존 빌드 서버의 상태를 그대로 써야 하고 격리 요구가 낮으면 Shell Executor가 빠른 출발점이 될 수 있습니다. 하지만 마이그레이션 품질을 높이고 싶다면 Docker Executor가 낫습니다. 매번 비슷한 컨테이너 환경에서 실행되기 때문에 ‘그 서버에만 깔린 무언가’를 빨리 찾아냅니다.

    GitLab CI 마이그레이션 결정 기준 요약 인포그래픽

    전환 여부를 판단하기 위한 저장소, Runner, 보안, 배포 승인 기준을 요약한 인포그래픽입니다.

    마무리: GitLab CI 마이그레이션은 언제 밀어붙일까

    GitLab CI 마이그레이션은 파이프라인 파일 하나를 만드는 작업이 아닙니다. 빌드 지식, 배포 권한, 장애 대응 루틴을 저장소 중심으로 다시 쓰는 일입니다. 그래서 저는 도구의 인기보다 운영 방식이 더 투명해지는지를 기준으로 봅니다.

    GitLab에 코드가 있고, MR 리뷰가 일상이고, 운영 배포 이력까지 한곳에서 보고 싶다면 GitLab CI로 옮기세요. 이 경우에는 테스트 전용 파이프라인부터 시작해 manual deploy까지 붙이는 방식이 가장 무난합니다. 반대로 Jenkins 플러그인, 개인 SSH 키, 사내 배포 서버의 전역 상태에 강하게 묶여 있다면 바로 전환하지 마세요. 먼저 Shadow Pipeline으로 기존 결과와 GitLab CI 결과를 비교하고, Secret과 배포 권한을 정리한 뒤에 주 파이프라인으로 승격하는 편이 안전합니다.

    제 경험상 성공적인 전환의 신호는 ‘파이프라인이 빨라졌다’보다 ‘실패했을 때 누구나 같은 방식으로 원인을 좁힐 수 있다’에 가깝습니다. 속도는 그다음에 옵니다. 파이프라인은 옮기는 게 아니라, 팀의 배포 습관을 코드로 다시 쓰는 일입니다.

    참고한 1차 문서: GitLab Runner 등록 문서, GitLab CI deprecated keywords, GitLab CI caching 문서

  • [DevOps] Argo CD vs Spinnaker: CI/CD 파이프라인 구축 비교 분석

    [DevOps] Argo CD vs Spinnaker: CI/CD 파이프라인 구축 비교 분석

    [DevOps] Argo CD vs Spinnaker: CI/CD 파이프라인 구축 비교 분석

    제가 현업이랑 홈랩에서 이것저것 굴려보면서 느낀 게 하나 있습니다. Argo CD Spinnaker 비교는 단순히 “어느 툴이 더 좋냐”의 문제가 아니더라고요. 팀이 Kubernetes를 어디까지 표준으로 쓰고 있는지, 배포 승인을 얼마나 엄격하게 가져가는지, 그리고 운영자가 원하는 가시성(visibility)이 어느 정도인지에 따라 답이 꽤 달라집니다. CI/CD 파이프라인을 처음 설계할 때 이 부분을 대충 잡고 들어가면, 나중에 배포 흐름이 꼬여서 삽질 좀 하게 됩니다 ㅎㅎ

    특히 지속적 배포(Continuous Delivery)를 도입하려는 팀이라면 더 그렇습니다. 처음엔 저도 “둘 다 배포 툴 아닌가?” 싶었는데, 실제로 써보니까 운영 철학이 꽤 다르더라고요. 오늘은 클라우드 네이티브 환경에서 Argo CD와 Spinnaker를 어떻게 봐야 하는지, 어디서 갈리는지, 실전에서는 어떻게 접근하면 되는지를 경험 섞어서 정리해보겠습니다.

    Argo CD Spinnaker 비교 아키텍처 다이어그램

    Argo CD의 GitOps 흐름과 Spinnaker의 파이프라인 중심 배포 흐름을 한 장으로 비교하는 개요 이미지입니다.

    1. 왜 Argo CD vs Spinnaker 비교가 중요한가

    배포 자동화는 이제 선택이 아니라 기본이죠. 그런데 자동화라고 다 같은 자동화가 아닙니다. 어떤 팀은 Git 저장소만 바꾸면 자동 반영되는 구조가 편하고, 어떤 팀은 승인 단계, 카나리 배포(일부 트래픽만 먼저 보내는 배포), 멀티 클라우드 전략이 더 중요하거든요.

    여기서 중요한 포인트가 있습니다. Argo CD는 GitOps(Git을 단일 진실 원본으로 삼는 운영 방식)에 굉장히 강한 도구이고, Spinnaker는 복잡한 배포 파이프라인과 멀티 클라우드 전달 흐름에 강한 플랫폼이라는 점입니다. 이 차이를 모르고 고르면, 도입 초반엔 쉬워 보여도 운영이 무거워지거나 반대로 필요한 통제가 안 붙는 상황이 생깁니다.

    • Git 변경 기반 자동 동기화가 중요하다면 Argo CD 쪽이 잘 맞습니다.
    • 복수 환경 승인, 배포 전략, 외부 시스템 연동이 많다면 Spinnaker가 더 자연스럽습니다.
    • Kubernetes 중심인지, 여러 배포 타깃이 섞여 있는지도 판단 기준입니다.

    2. 개념부터 쉽게: Argo CD와 Spinnaker는 어떻게 다를까

    2-1. Argo CD: 선언형 배포와 GitOps 중심

    쉽게 말해 Argo CD는 “클러스터 상태는 Git에 적어둔 그대로여야 한다”는 철학으로 움직입니다. Git 저장소 안의 YAML 매니페스트(manifest, 리소스 정의 파일)나 Helm 차트(Chart, 쿠버네티스 패키지)를 보고, 실제 클러스터 상태와 비교한 뒤 차이가 있으면 맞춰주는 방식이죠.

    제가 직접 해보니 이 방식의 장점은 정말 명확했습니다. 배포 이력이 Git 커밋으로 남고, 누가 뭘 바꿨는지 추적하기가 편합니다. 드리프트(drift, 선언한 상태와 실제 상태가 어긋나는 현상)도 눈에 잘 보이고요. 대신 애플리케이션 전달 과정 전체를 설계하는 엔진이라기보다는, Kubernetes 배포 상태를 Git 기준으로 맞추는 데 최적화된 도구에 가깝습니다.

    2-2. Spinnaker: 파이프라인 오케스트레이션 중심

    Spinnaker는 접근이 조금 다릅니다. 이쪽은 “어떤 조건에서 어떤 순서로 배포를 진행할지”를 세밀하게 다루는 데 강합니다. 예를 들면 빌드 완료 후 테스트 실행, 승인, 스테이징 배포, 카나리 분석, 운영 반영 같은 흐름을 하나의 파이프라인으로 묶는 식이죠.

    처음엔 이게 뭔가 싶었는데, 실제로 써보니까 대규모 환경이나 규정이 많은 조직에서 왜 Spinnaker를 선호하는지 이해가 되더라고요. 반면 구성 요소가 많고 운영 복잡도도 꽤 있습니다. 작은 팀이 가볍게 시작하기엔 다소 무겁다고 느낄 수 있습니다.

    2-3. 핵심 차이 한눈에 보기

    항목 Argo CD Spinnaker
    핵심 철학 GitOps 기반 선언형 동기화 파이프라인 기반 지속적 배포
    주요 타깃 Kubernetes 중심 멀티 클라우드 및 다양한 배포 전략
    운영 복잡도 상대적으로 단순 상대적으로 높음
    강점 상태 일치, 변경 추적, Git 연동 승인 흐름, 카나리, 복합 파이프라인
    적합한 팀 플랫폼 표준이 Kubernetes인 팀 배포 정책과 절차가 복잡한 팀

    3. 어떤 팀에 어떤 도구가 맞는가

    이 부분은 제가 후배들한테도 자주 이야기하는데요, 도구를 고를 때 기능 목록보다 운영 모델을 먼저 봐야 합니다.

    • Argo CD가 잘 맞는 경우: Git 기반 운영을 표준화하고 싶을 때, Kubernetes 리소스가 배포의 중심일 때, 운영 단순성이 중요할 때
    • Spinnaker가 잘 맞는 경우: 승인 단계가 많을 때, 여러 클라우드나 배포 전략을 함께 다룰 때, 파이프라인 오케스트레이션이 핵심일 때
    • 둘을 함께 보는 경우: CI는 다른 툴에서 처리하고, CD만 역할 분리해서 설계할 때

    사실 Argo CD Spinnaker 비교에서 제일 많이 놓치는 지점이 여기입니다. 둘은 완전히 같은 문제를 푸는 경쟁 제품이라기보다, 겹치는 영역이 있으면서도 중심축이 다른 도구라고 보는 게 더 정확합니다.

    4. 실전 구현: Argo CD로 GitOps형 CI/CD 파이프라인 구성

    먼저 Argo CD 쪽부터 보겠습니다. 홈랩에서 테스트할 때 저는 보통 “애플리케이션 매니페스트를 Git에 넣고, Argo CD가 자동 동기화하게 만드는 흐름”으로 검증합니다. 이 구조는 이해도 쉽고, 실무 전환도 빠르거든요.

    4-1. Argo CD 설치

    1. 전용 네임스페이스(namespace, Kubernetes 논리 분리 공간)를 만듭니다.
    2. 공식 설치 매니페스트를 적용합니다.
    3. 초기 관리자 비밀번호를 확인하고 UI에 접속합니다.
    kubectl create namespace argocd
    kubectl apply -n argocd -f https://raw.githubusercontent.com/argoproj/argo-cd/stable/manifests/install.yaml
    kubectl get pods -n argocd
    kubectl port-forward svc/argocd-server -n argocd 8080:443

    여기서 저는 처음에 포트포워딩(port-forwarding, 로컬 포트를 클러스터 서비스에 연결)만 열어놓고 왜 접속이 불안정하지 싶었는데, 로컬 브라우저 인증서 경고를 그냥 넘기지 않아서 그런 경우가 있더라고요. 사소한데 은근 많이 막힙니다.

    4-2. Git 저장소에 애플리케이션 매니페스트 준비

    Argo CD의 핵심은 Git이기 때문에, 배포 대상 매니페스트가 먼저 있어야 합니다. 가장 단순한 Deployment(디플로이먼트, 파드 복제와 롤링 업데이트 관리)와 Service(서비스, 네트워크 노출 객체) 예시는 아래처럼 둘 수 있습니다.

    apiVersion: apps/v1
    kind: Deployment
    metadata:
      name: demo-nginx
      namespace: demo
    spec:
      replicas: 2
      selector:
        matchLabels:
          app: demo-nginx
      template:
        metadata:
          labels:
            app: demo-nginx
        spec:
          containers:
            - name: nginx
              image: nginx:stable
              ports:
                - containerPort: 80
    ---
    apiVersion: v1
    kind: Service
    metadata:
      name: demo-nginx
      namespace: demo
    spec:
      selector:
        app: demo-nginx
      ports:
        - port: 80
          targetPort: 80

    4-3. Argo CD Application 리소스 생성

    이제 Argo CD에 “어느 Git 저장소의 어느 경로를 어느 클러스터 네임스페이스에 반영할지” 알려주면 됩니다. 이 리소스가 사실상 배포 선언문 역할을 합니다.

    apiVersion: argoproj.io/v1alpha1
    kind: Application
    metadata:
      name: demo-nginx
      namespace: argocd
    spec:
      project: default
      source:
        repoURL: https://github.com/example-org/example-manifests.git
        targetRevision: main
        path: apps/demo-nginx
      destination:
        server: https://kubernetes.default.svc
        namespace: demo
      syncPolicy:
        automated:
          prune: true
          selfHeal: true
        syncOptions:
          - CreateNamespace=true
    kubectl apply -f application.yaml
    kubectl get applications -n argocd
    Argo CD Spinnaker 비교 중 Argo CD 동기화 설정 화면

    Git 저장소 경로, 대상 네임스페이스, 자동 동기화 옵션이 설정된 Argo CD Application 화면을 보여주는 위치입니다.

    여기서 prune은 Git에서 제거된 리소스를 클러스터에서도 정리하는 옵션이고, selfHeal은 누군가 클러스터에서 수동 변경해도 Git 기준으로 다시 되돌리는 기능입니다. 이거 진짜 편하더라고요. 운영자가 많아질수록 체감됩니다.

    5. 실전 구현: Spinnaker로 파이프라인 중심 배포 설계

    이번엔 Spinnaker 관점입니다. Spinnaker는 단순히 매니페스트를 맞추는 느낌보다, 배포 절차를 단계적으로 묶는 쪽에 가깝습니다. 그래서 예시도 “배포 흐름 설계” 관점으로 보는 게 이해가 쉽습니다.

    5-1. 추천 파이프라인 흐름

    1. CI 도구에서 이미지 빌드 및 레지스트리 푸시
    2. Spinnaker가 새 이미지 태그 감지
    3. 스테이징 환경 배포
    4. 수동 승인(Manual Judgment)
    5. 운영 환경 반영

    Spinnaker의 장점은 바로 이 지점입니다. 예를 들어 조직 정책상 운영 배포 전에 승인 절차가 꼭 필요하다면, Argo CD만으로는 별도 설계가 필요했던 부분을 Spinnaker는 비교적 자연스럽게 파이프라인에 녹일 수 있습니다.

    5-2. 파이프라인 단계 예시

    단계 설명 운영 포인트
    Trigger 이미지 변경 또는 이벤트 감지 CI와 연결 구조를 명확히 해야 함
    Deploy to Staging 스테이징 환경 우선 배포 운영과 최대한 동일한 조건 유지
    Manual Judgment 사람 승인 후 다음 단계 진행 승인 기준을 문서화해야 혼선이 적음
    Deploy to Prod 운영 환경 반영 롤백 기준과 모니터링 연동 중요

    실제로 써보니까 Spinnaker는 “배포 파이프라인을 플랫폼 차원에서 관리하고 싶다”는 팀에 꽤 매력적입니다. 대신 구성요소가 많아서 관리 비용이 올라갑니다. 작은 팀이면 이 장점이 부담으로 바뀌기도 하더라고요.

    6. ⚠️ 주의사항과 트러블슈팅: 제가 실제로 많이 부딪힌 포인트

    6-1. Argo CD에서 자주 겪는 문제

    • 드리프트 오해: 운영자가 클러스터에서 급한 수정 후 Git 반영을 빼먹으면 Argo CD가 다시 덮어씁니다.
    • 권한 문제: 네임스페이스 생성이나 특정 리소스 적용 시 RBAC(Role-Based Access Control, 역할 기반 권한 제어) 때문에 막히는 경우가 많습니다.
    • Helm 값 충돌: values 파일과 환경별 override가 섞이면 실제 반영값 추적이 어려워집니다.

    저도 처음엔 selfHeal이 멋져 보여서 다 켜놨었는데, 운영자가 수동 조치한 내용을 바로 되돌려버려서 당황한 적이 있습니다. 그래서 지금은 긴급 변경은 Git에 먼저 반영이라는 팀 규칙을 꼭 같이 둡니다.

    6-2. Spinnaker에서 자주 겪는 문제

    • 구성 복잡도: 서비스 수와 설정 포인트가 많아 초반 진입장벽이 있습니다.
    • 파이프라인 관리 비용: 애플리케이션이 늘어나면 표준화하지 않은 파이프라인이 금방 복잡해집니다.
    • 운영 관찰성 확보: 어느 단계에서 실패했는지 추적하려면 로그와 모니터링 체계를 같이 잡아야 합니다.

    근데 여기서 중요한 포인트! Spinnaker 자체가 나쁘다는 뜻이 아니라, 요구사항보다 플랫폼이 더 커지면 운영 피로도가 급격히 올라간다는 이야기입니다. 특히 팀 규모가 작을수록요.

    Argo CD Spinnaker 비교를 위한 배포 파이프라인 승인 흐름 이미지

    스테이징 배포, 수동 승인, 운영 반영으로 이어지는 Spinnaker 스타일의 파이프라인 흐름을 설명하는 이미지입니다.

    7. 검증과 결과: 어떤 선택이 더 현실적이었나

    제가 여러 환경에서 비교해보니 결과는 꽤 분명했습니다. Kubernetes가 배포 표준이고, Git 중심 운영 문화를 만들고 싶다면 Argo CD가 훨씬 빠르게 자리 잡습니다. 반대로 배포 절차가 길고 승인, 검증, 단계별 제어가 중요하다면 Spinnaker가 더 잘 맞습니다.

    검증할 때는 보통 아래 항목을 봤습니다.

    1. Git 변경 후 실제 반영까지 흐름이 단순한가
    2. 실패했을 때 원인 추적이 쉬운가
    3. 롤백(rollback, 이전 정상 상태로 되돌리기)이 명확한가
    4. 운영자 수가 늘어도 관리 규칙이 유지되는가

    Argo CD는 상태 비교가 직관적이라 운영 중 안심이 되더라고요. 반면 Spinnaker는 파이프라인 단계가 많을수록 통제력은 좋지만, 초반 설계 퀄리티가 정말 중요했습니다. 이건 진짜 경험 차이입니다.

    kubectl get applications -n argocd
    kubectl get pods -n demo
    kubectl rollout status deployment/demo-nginx -n demo

    위 명령으로 Argo CD 애플리케이션 상태, 실제 파드 상태, 롤아웃 완료 여부를 확인할 수 있습니다. 지속적 배포 환경에서는 “배포했다”보다 “원하는 상태로 수렴했는가”를 보는 게 더 중요하거든요.

    Argo CD Spinnaker 비교 결과를 보여주는 배포 검증 대시보드

    애플리케이션이 Synced, Healthy 상태인지와 실제 배포 결과를 함께 보여주는 검증용 이미지 자리입니다.

    8. Argo CD vs Spinnaker 최종 정리

    질문 추천
    우리는 Kubernetes 중심으로 단순하고 강한 CD가 필요한가? Argo CD
    GitOps 운영 모델을 팀 표준으로 만들고 싶은가? Argo CD
    승인, 카나리, 복합 배포 절차가 핵심인가? Spinnaker
    멀티 환경과 복잡한 전달 흐름을 한 플랫폼에서 통제하고 싶은가? Spinnaker

    짧게 정리하면 이렇습니다. Argo CD는 “원하는 상태를 Git에 적고 맞춰나가는 도구”이고, Spinnaker는 “배포 절차를 정교하게 설계하고 흘려보내는 플랫폼”입니다. 둘 다 훌륭하지만, 잘 맞는 환경이 다릅니다.

    혹시 이런 경험 있으신가요? 배포 자동화를 시작했는데, CI/CD 파이프라인이 오히려 더 복잡해져서 손이 더 많이 가는 상황이요. 저도 처음엔 헷갈렸는데, 결국 답은 기능 수보다 운영 방식에 있었습니다.

    Argo CD Spinnaker 비교 선택 기준 요약 인포그래픽

    팀 규모, Kubernetes 의존도, 승인 절차 복잡도에 따라 어떤 도구가 맞는지 요약한 비교 인포그래픽입니다.

    9. 마무리: 지금 시작한다면 저는 이렇게 고릅니다

    만약 지금 새로 시작하는 팀이고, 이미 Kubernetes를 기본 플랫폼으로 쓰고 있다면 저는 Argo CD부터 검토할 것 같습니다. 이유는 분명합니다. 도입 속도가 빠르고, GitOps 문화를 만들기 좋고, 운영 단순성도 꽤 좋거든요.

    반대로 조직 규모가 크고, 배포 승인과 전달 절차가 중요한 팀이라면 Spinnaker 쪽이 더 설득력 있습니다. 다만 이 경우엔 플랫폼 운영 책임까지 함께 고려해야 합니다. 단순히 배포 기능만 보고 들어가면 나중에 힘들 수 있습니다.

    다음 글에서는 Argo CD 기반으로 CI와 CD를 분리해서 운영하는 패턴, 예를 들어 GitHub Actions 같은 CI 도구와 연결하는 방식도 다뤄볼 예정입니다. 이전 글에서 다뤘던 Kubernetes 배포 기본 흐름과 함께 보시면 더 이해가 잘 되실 겁니다.

    자주 묻는 질문

    Argo CD가 CI까지 대체하나요?

    보통은 아닙니다. Argo CD는 CD에 더 가깝고, 빌드와 테스트는 별도 CI 도구가 맡는 경우가 많습니다.

    Spinnaker는 Kubernetes 환경에서만 쓰나요?

    아닙니다. 다만 이 글에서는 클라우드 네이티브와 Kubernetes 중심 관점에서 설명했습니다.

    둘 중 하나만 꼭 골라야 하나요?

    반드시 그렇진 않습니다. 팀 구조와 기존 플랫폼에 따라 역할을 나눠 설계하는 경우도 있습니다.

  • [Cloud] Vercel에서 GitLab CI/CD로 마이그레이션: 실제 경험과 고려사항

    [Cloud] Vercel에서 GitLab CI/CD로 마이그레이션: 실제 경험과 고려사항

    [DevOps] Vercel GitLab CI/CD 마이그레이션 실제 경험과 고려사항

    프론트엔드 배포를 빠르게 시작할 때는 Vercel이 정말 편합니다. 저도 처음엔 Git push만 하면 미리보기 배포(Preview Deployment, 변경사항 확인용 임시 배포)가 바로 올라오는 흐름이 너무 좋아서 한동안 만족하면서 썼거든요. 그런데 서비스가 조금씩 커지고, 백엔드와 인프라 정책까지 같이 맞춰야 하다 보니 Vercel GitLab CI/CD 마이그레이션을 진지하게 검토하게 되더라고요. 특히 팀에서 이미 GitLab을 중심으로 이슈, 머지 리퀘스트(Merge Request, 코드 리뷰 요청), 배포 이력을 관리하고 있다면 CI/CD 전환 자체가 단순한 툴 변경이 아니라 DevOps 워크플로우를 정리하는 작업이 됩니다. 이번 글에서는 제가 직접 정리하면서 겪었던 판단 포인트, 삽질했던 부분, 그리고 안정적으로 옮기는 방법을 차근차근 풀어보겠습니다.

    Vercel GitLab CI/CD 마이그레이션 전체 아키텍처 다이어그램

    Vercel 중심 배포에서 GitLab CI/CD 중심 배포로 흐름이 바뀌는 전체 구조를 보여주는 이미지입니다.

    1. 왜 Vercel에서 GitLab CI/CD로 옮기게 됐는가

    쉽게 말해, Vercel은 프론트엔드 배포 경험을 극도로 단순화해주는 플랫폼이고, GitLab CI/CD는 배포 과정을 내가 더 많이 통제할 수 있게 해주는 자동화 파이프라인입니다. 둘 중 뭐가 절대적으로 낫다기보다는, 팀 상황에 따라 기준이 달라집니다.

    제가 마이그레이션을 고민한 가장 큰 이유는 세 가지였습니다.

    • 배포 흐름 통합: 프론트엔드만 따로 Vercel에 있으면, 백엔드 배포와 인프라 변경 이력이 분리되기 쉽습니다.
    • 권한과 정책 일원화: GitLab 프로젝트 권한, 브랜치 보호(Protected Branch), 승인 규칙을 한 곳에서 다루는 게 편하더라고요.
    • 비용 절감: 서비스 규모와 팀 사용 방식에 따라 별도 플랫폼 비용보다 기존 GitLab Runner(러너, 작업 실행기)를 활용하는 쪽이 더 맞을 때가 있습니다.

    여기서 중요한 포인트! CI/CD 전환은 단순히 배포 속도만 보는 게 아닙니다. 로그를 어디서 볼지, 실패 시 누가 복구할지, 시크릿(Secret, 비밀값)을 어디서 관리할지까지 같이 봐야 합니다.

    2. Vercel과 GitLab CI/CD의 차이, 쉽게 설명해보면

    저도 처음엔 이게 뭔가 싶었는데, 아주 단순하게 비유하면 이렇습니다.

    • Vercel: 잘 차려진 배포 전문 주방입니다. 재료만 넣으면 빠르게 요리가 나옵니다.
    • GitLab CI/CD: 주방을 직접 설계할 수 있습니다. 대신 가스불, 조리 순서, 청소 방식까지 내가 정해야 합니다.

    그래서 소규모 프로젝트나 정적 사이트(Static Site, 서버 렌더링 없이 빌드 결과물만 배포하는 형태)는 Vercel이 아주 매력적입니다. 반대로 프론트엔드 빌드, 테스트, 컨테이너 이미지(Container Image), 쿠버네티스(Kubernetes, 컨테이너 오케스트레이션) 배포까지 한 줄로 이어야 한다면 GitLab CI/CD 쪽이 더 자연스러울 수 있습니다.

    항목 Vercel GitLab CI/CD
    초기 설정 매우 간단 직접 설계 필요
    프론트엔드 미리보기 강점 직접 구현 필요
    배포 통제력 플랫폼 기준 높음
    조직 내 표준화 분리 운영 가능성 GitLab 중심 통합 유리
    확장성 프론트엔드 친화적 전체 파이프라인 확장 유리

    3. 마이그레이션 전에 먼저 체크한 항목

    실제로 써보니까, 코드를 옮기는 것보다 현재 Vercel이 대신 해주던 것을 목록화하는 게 훨씬 중요했습니다. 이걸 빼먹으면 나중에 꼭 터집니다 ㅎㅎ

    1. 빌드 명령: 예를 들어 npm run build 또는 pnpm build가 정확히 무엇을 수행하는지 확인합니다.
    2. 출력 디렉터리: 정적 결과물이 dist, build, out 중 어디에 생성되는지 봅니다.
    3. 환경 변수(Environment Variable, 실행 환경별 설정값): API URL, 토큰, 공개 키 등을 개발/스테이징/운영으로 나눠 정리합니다.
    4. 리라이트/리다이렉트: Vercel 설정에 있던 경로 재작성(Rewrite) 규칙이 있다면 웹 서버나 인그레스에서 다시 구현해야 합니다.
    5. 프리뷰 배포 전략: 머지 리퀘스트마다 미리보기 URL이 꼭 필요한지 결정합니다.
    6. 도메인과 TLS: 커스텀 도메인(Custom Domain)과 인증서(TLS Certificate) 종료 지점이 어디인지 확인합니다.

    이 단계에서 제가 한 실수는, 빌드만 되면 끝이라고 생각한 거였습니다. 근데 실제 운영은 빌드보다 배포 후 라우팅과 환경 변수 관리에서 더 많이 흔들리더라고요.

    4. GitLab CI/CD로 기본 파이프라인 구성하기

    이제 실전입니다. 여기서는 가장 보편적인 방식으로, 프론트엔드 앱을 빌드한 뒤 정적 파일을 서버나 스토리지로 배포하는 흐름을 예시로 들겠습니다. 프레임워크는 Next.js, React, Vue 등 무엇이든 응용 가능하지만, 설정은 프로젝트 성격에 맞게 조정하셔야 합니다.

    4-1. 기본 디렉터리와 환경 준비

    먼저 프로젝트 루트에 .gitlab-ci.yml 파일을 둡니다. GitLab Runner가 Node.js 환경에서 의존성을 설치하고, 테스트 후 빌드를 수행하게 만들 겁니다.

    stages:
      - install
      - test
      - build
      - deploy
    
    variables:
      NODE_ENV: production
      npm_config_cache: .npm
    
    cache:
      paths:
        - .npm/
        - node_modules/
    
    install:
      stage: install
      image: node:20
      script:
        - npm ci
    
    unit_test:
      stage: test
      image: node:20
      script:
        - npm ci
        - npm run test -- --runInBand
      rules:
        - if: '$CI_COMMIT_BRANCH'
    
    build_app:
      stage: build
      image: node:20
      script:
        - npm ci
        - npm run build
      artifacts:
        paths:
          - dist/
        expire_in: 1 day
    
    deploy_production:
      stage: deploy
      image: alpine:latest
      script:
        - echo "Deploy step runs here"
      rules:
        - if: '$CI_COMMIT_BRANCH == "main"'

    위 예시는 아주 기본 골격입니다. 핵심은 install – test – build – deploy 단계를 분리해서 실패 지점을 명확하게 보는 겁니다. 나중에 장애가 나도 어디서 깨졌는지 바로 보이거든요.

    Vercel GitLab CI/CD 마이그레이션 파이프라인 구성 이미지

    설치, 테스트, 빌드, 배포 단계가 GitLab Runner에서 어떻게 순차 실행되는지 보여주는 이미지입니다.

    4-2. 정적 파일 서버로 배포하는 예시

    만약 Nginx(엔진엑스, 웹 서버)로 정적 파일을 서빙한다면 이런 식으로 배포 스크립트를 둘 수 있습니다.

    #!/usr/bin/env bash
    set -euo pipefail
    
    TARGET_DIR="/var/www/my-frontend"
    
    rm -rf "${TARGET_DIR:?}"/*
    cp -r dist/* "$TARGET_DIR"/
    
    echo "deploy completed"

    그리고 GitLab CI에서는 SSH(Secure Shell, 원격 접속 프로토콜)로 원격 서버에 접속해 배포할 수 있습니다.

    deploy_production:
      stage: deploy
      image: alpine:latest
      before_script:
        - apk add --no-cache openssh-client rsync
        - eval $(ssh-agent -s)
        - echo "$SSH_PRIVATE_KEY" | tr -d '\r' | ssh-add -
        - mkdir -p ~/.ssh
        - chmod 700 ~/.ssh
      script:
        - rsync -avz --delete dist/ deploy@your-server:/var/www/my-frontend/
        - ssh deploy@your-server "sudo systemctl reload nginx"
      rules:
        - if: '$CI_COMMIT_BRANCH == "main"'

    여기서 중요한 포인트는 시크릿을 코드에 넣지 않는 겁니다. SSH 키, API 토큰, 배포 대상 주소는 GitLab CI/CD Variables에 넣어야 합니다.

    5. 프리뷰 배포와 브랜치 전략은 어떻게 바꿨는가

    Vercel을 쓰다가 GitLab으로 넘어오면 가장 아쉬운 부분 중 하나가 프리뷰 배포입니다. 저도 이 부분이 꽤 컸습니다. Vercel은 이 경험이 워낙 매끄럽거든요. 근데 GitLab에서도 포기할 필요는 없습니다.

    • 옵션 1: 스테이징 환경 하나를 두고, 머지 전 검증은 공용 URL에서 진행
    • 옵션 2: 브랜치별 또는 머지 리퀘스트별 임시 환경을 생성
    • 옵션 3: 정적 아티팩트만 확인하고 실제 프리뷰 URL은 운영하지 않음

    제가 해보니 팀 규모가 크지 않다면, 처음부터 브랜치별 임시 환경을 만들기보다 스테이징 하나를 안정적으로 운영하는 쪽이 훨씬 덜 피곤했습니다. 특히 프론트엔드 배포만 있는 게 아니라 API, 인증, CORS(Cross-Origin Resource Sharing, 교차 출처 요청 정책)까지 얽혀 있으면 프리뷰 환경 증식이 오히려 운영 복잡도를 키우더라고요.

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

    이 섹션은 꼭 넣고 싶었습니다. 마이그레이션 자체보다 여기서 시간을 더 썼거든요.

    6-1. SPA 라우팅이 깨지는 문제

    React Router 같은 클라이언트 라우팅(Client-side Routing, 브라우저에서 경로 처리)을 쓰는 앱은 새로고침 시 404가 날 수 있습니다. Vercel에서는 비교적 자연스럽게 처리되던 부분이, Nginx에서는 별도 설정이 필요합니다.

    location / {
      try_files $uri $uri/ /index.html;
    }

    처음엔 정적 파일만 복사하면 끝인 줄 알았는데, 이 설정 빠져서 새로고침할 때마다 페이지가 죽더라고요. 여기서 좀 삽질했습니다 ㅎㅎ

    6-2. 환경 변수 이름이 달라서 빌드가 실패한 문제

    Vercel에 등록해 둔 환경 변수와 GitLab Variables 이름이 다르면 빌드는 되는데 런타임에서 깨지기도 하고, 아예 빌드 시점에 실패하기도 합니다. 그래서 저는 아래처럼 체크리스트를 따로 뒀습니다.

    • NODE_ENV
    • PUBLIC_* 또는 프레임워크별 공개 변수 prefix
    • API 엔드포인트 URL
    • 서드파티 인증 키

    6-3. 캐시 때문에 이전 빌드 결과가 섞이는 문제

    CI 캐시는 속도에는 도움이 되지만, 설정이 애매하면 오히려 독이 됩니다. 의존성 캐시와 빌드 산출물 캐시는 분리해서 보시는 걸 추천드립니다. 저는 한 번은 캐시를 과하게 잡아놔서, 수정했는데도 예전 결과물이 살아남는 바람에 한참 헷갈렸습니다.

    6-4. 배포는 성공인데 서비스는 실패한 문제

    이거 많이 놓칩니다. 파이프라인이 초록불이라고 서비스가 정상이라는 뜻은 아니거든요. 배포 후 헬스 체크(Health Check, 상태 확인)나 간단한 smoke test(스모크 테스트, 핵심 기능 점검)를 꼭 넣어야 합니다.

    Vercel GitLab CI/CD 마이그레이션 트러블슈팅 로그 이미지

    배포 로그에서 자주 만나는 오류와 원인 분석 포인트를 시각적으로 정리한 이미지입니다.

    7. 검증은 이렇게 했습니다

    마이그레이션 후에는 감으로 보면 안 됩니다. 저는 최소한 아래 순서로 확인했습니다.

    1. 빌드 재현성: 같은 커밋에서 동일한 결과가 나오는지 확인
    2. 정적 자산 로딩: JS, CSS, 이미지 경로가 깨지지 않는지 확인
    3. 라우팅: 직접 URL 접근과 새로고침 테스트
    4. 환경별 설정: 개발/스테이징/운영에서 API 호출 대상이 올바른지 확인
    5. 롤백 가능성: 이전 결과물로 빠르게 되돌릴 수 있는지 점검

    여기서 제가 특히 중요하게 본 건 롤백 전략이었습니다. Vercel은 비교적 되돌리기 경험이 좋았는데, GitLab 기반으로 직접 운영할 때는 내가 롤백 절차를 설계해야 하거든요. 배포 디렉터리를 버전별로 보관하거나, 아티팩트를 일정 기간 유지하는 방식이 실무적으로 꽤 유용했습니다.

    curl -I https://your-service.example.com/
    curl -s https://your-service.example.com/health
    

    아주 단순한 명령이지만, 배포 직후 이 두 개만 자동으로 돌려도 문제를 빨리 찾는 데 도움이 됩니다.

    Vercel GitLab CI/CD 마이그레이션 결과 검증 대시보드 이미지

    파이프라인 성공 상태와 서비스 헬스 체크 결과를 함께 보여주는 검증 이미지입니다.

    8. 그래서 누구에게 적합한가: 선택 기준 정리

    결론적으로 Vercel GitLab CI/CD 마이그레이션은 모든 팀에 정답은 아닙니다. 다만 아래에 가깝다면 꽤 의미가 있습니다.

    • 프론트엔드, 백엔드, 인프라 배포를 한 플랫폼에서 관리하고 싶은 팀
    • 머지 리퀘스트 승인과 배포 이력을 강하게 연결하고 싶은 팀
    • 러너 운영이 가능하고, YAML 기반 파이프라인 관리에 거부감이 없는 팀
    • 배포 자동화와 권한 모델을 조직 표준에 맞추고 싶은 팀

    반대로, 빠른 배포 경험과 프리뷰 환경이 최우선이고 운영 인력이 많지 않다면 Vercel 유지가 더 좋은 선택일 수도 있습니다. 사실 이건 기술 우열보다 운영 철학 차이에 가깝습니다.

    상황 추천 방향
    작은 팀, 프론트엔드 중심, 빠른 배포 우선 Vercel 유지 검토
    조직 표준 CI/CD 필요, 배포 통합 필요 GitLab CI/CD 전환 검토
    스테이징/운영 분리와 승인 규칙 중요 GitLab CI/CD 유리
    프리뷰 경험이 가장 중요 Vercel 강점 큼
    Vercel GitLab CI/CD 마이그레이션 선택 기준 비교 인포그래픽

    도입 난이도, 통제력, 프리뷰 배포, 운영 표준화 관점에서 두 방식을 비교한 요약 이미지입니다.

    9. 마무리: 배포 도구보다 중요한 건 운영 기준입니다

    이번에 정리하면서 다시 느낀 건, 도구를 바꾸는 것보다 배포 기준을 문서화하는 일이 더 중요하다는 점이었습니다. 제가 직접 해보니 GitLab CI/CD로 옮긴 뒤 얻은 가장 큰 장점은 화려한 기능보다도, 누가 봐도 같은 절차로 배포할 수 있는 상태가 됐다는 거였습니다. 이거 진짜 편하더라고요.

    물론 처음엔 귀찮습니다. YAML도 손봐야 하고, 시크릿도 다시 넣어야 하고, 웹 서버 설정도 만져야 하거든요. 근데 한 번 기준을 잡아두면 이후에는 프론트엔드 배포뿐 아니라 배치 작업, 백엔드, 운영 스크립트까지 같은 방식으로 확장하기가 좋습니다. 이게 결국 장기적으로는 비용 절감과 운영 안정성으로 이어집니다.

    혹시 지금 CI/CD 전환을 고민하고 계신다면, 먼저 현재 Vercel이 대신 해주고 있는 기능부터 목록으로 적어보세요. 그다음 GitLab CI/CD에서 반드시 재현해야 할 항목과, 과감히 포기해도 되는 항목을 나누는 게 좋습니다. 다음 글에서는 GitLab Runner를 Docker Executor(도커 실행기)로 운영할 때 주의할 점도 다뤄볼 예정입니다. 이전 글에서 다뤘던 Nginx 리버스 프록시 구성과 같이 보시면 흐름 잡는 데 더 도움이 되실 겁니다.

    자주 묻는 질문

    Q1. Vercel에서 GitLab CI/CD로 옮기면 무조건 비용 절감이 되나요?

    꼭 그렇지는 않습니다. Runner 운영 비용, 저장소, 로그 관리, 엔지니어링 시간까지 같이 봐야 합니다. 다만 기존 GitLab 중심 운영 체계가 이미 있다면 중복 도구 비용을 줄이는 효과는 기대할 수 있습니다.

    Q2. 프리뷰 배포가 꼭 필요하면 GitLab CI/CD는 불리한가요?

    기본 경험은 Vercel이 더 좋다고 느끼는 경우가 많습니다. 대신 GitLab에서도 머지 리퀘스트 기반 임시 환경을 설계할 수 있으니, 팀이 감당할 운영 복잡도와 맞는지 보는 게 중요합니다.

    Q3. 가장 먼저 검증해야 할 한 가지는 뭔가요?

    저는 라우팅과 환경 변수라고 봅니다. 빌드는 됐는데 실제 서비스가 안 뜨는 경우가 대부분 여기서 나왔습니다. 특히 SPA 라우팅과 공개 환경 변수 prefix는 꼭 확인해보세요.

  • [k8s] ArgoCD 도입 실패 사례 분석: GitOps 전환 시 흔한 실수와 방지 전략

    [k8s] ArgoCD 도입 실패 사례 분석: GitOps 전환 시 흔한 실수와 방지 전략

    [DevOps] ArgoCD 도입 실패 사례 분석과 GitOps 실수 방지

    ArgoCD 도입 실패 이야기는 생각보다 흔합니다. 저도 처음엔 GitOps(깃옵스, Git을 단일 진실 공급원으로 삼는 운영 방식)만 붙이면 배포가 깔끔해질 줄 알았거든요. 그런데 실제로 해보니, ArgoCD(아르고CD, Kubernetes 선언형 배포 도구)를 넣는 순간 오히려 배포가 더 복잡해지는 팀도 많았습니다. 특히 CI/CD 전환 사례를 보면, 기술 문제가 아니라 역할 분리, 저장소 구조, 승인 흐름 같은 운영 설계에서 먼저 무너지는 경우가 많더라고요. 이번 글은 제가 현업과 홈랩에서 반복해서 본 ArgoCD 도입 실패 패턴을 중심으로, 왜 그런 일이 생기는지와 어떻게 피해야 하는지를 정리해보겠습니다.

    혹시 이런 경험 있으신가요? 배포 자동화는 넣었는데 누가 어떤 값을 바꿨는지 더 헷갈리고, 장애가 나면 Git이 문제인지 클러스터가 문제인지부터 추적하게 되는 상황 말입니다. 여기서 중요한 포인트는, ArgoCD 자체가 문제라기보다 GitOps 실수가 누적되면서 운영 복잡도가 폭발한다는 점입니다.

    ArgoCD 도입 실패와 GitOps 전환 흐름을 설명하는 아키텍처 이미지

    Git 저장소, CI 파이프라인, ArgoCD, Kubernetes 클러스터 사이의 흐름과 실패 포인트를 한눈에 보여주는 개요 이미지입니다.

    1. 왜 ArgoCD 도입 실패가 반복될까요?

    쉽게 말해 ArgoCD는 배포 버튼을 없애는 도구가 아니라, 변경 관리 방식 자체를 바꾸는 도구입니다. 그래서 예전 CI/CD에서는 잘 통하던 습관이 GitOps로 오면 바로 문제를 만듭니다. 예를 들면 이런 식입니다.

    • 운영값을 Git이 아니라 사람 손으로 클러스터에서 직접 수정함
    • 애플리케이션 코드 저장소와 배포 매니페스트 저장소의 책임이 불명확함
    • 자동 동기화(auto-sync)를 켰는데 승인 절차는 그대로 수동 운영을 기대함
    • Helm(헬름, Kubernetes 패키지 관리 도구) 값 파일이 환경별로 제멋대로 늘어남
    • 장애가 나도 누가 마지막 변경을 만들었는지 추적이 어려움

    저도 처음엔 이게 뭔가 싶었는데, 결국 핵심은 하나였습니다. ArgoCD는 배포 도구이면서 동시에 운영 규율을 강제하는 도구라는 점입니다. 팀이 그 규율을 합의하지 않은 상태에서 도입하면, ArgoCD 문제점처럼 보이는 현상이 사실은 프로세스 문제로 터집니다.

    2. GitOps와 ArgoCD를 아주 쉽게 설명해보면

    GitOps는 “실제 운영 상태는 Git에 적힌 선언대로 맞춘다”는 철학입니다. ArgoCD는 그 선언과 클러스터 상태를 계속 비교해서 drift(드리프트, 선언과 실제 상태가 어긋난 상태)를 감지하고 맞춰주는 역할을 하죠.

    예전 방식은 대체로 이랬습니다. CI 서버가 빌드하고, 누군가 kubectl(쿠버네티스 CLI)이나 Helm으로 운영 클러스터에 직접 배포합니다. 반면 GitOps 방식은 배포 변경도 Pull Request(PR, 변경 제안 요청)로 남고, 승인 후 Git이 바뀌면 ArgoCD가 그걸 따라갑니다. 감사 추적(audit trail, 변경 이력 추적)이 되는 대신, 우회 수정이 어려워집니다. 이 점이 장점이자 초반 저항 포인트입니다.

    항목 기존 CI/CD GitOps + ArgoCD
    배포 트리거 파이프라인 또는 운영자 수동 실행 Git 변경 반영
    변경 이력 파이프라인 로그 중심 Git 커밋과 PR 중심
    긴급 수정 클러스터에서 직접 수정 가능 직접 수정 시 drift 발생
    권한 통제 클러스터 권한 중심 저장소 권한과 승인 흐름 중요
    실패 원인 스크립트 불안정, 환경 차이 저장소 구조, 책임 분리 실패

    그래서 ArgoCD 도입 실패를 줄이려면 설치보다 운영 모델을 먼저 설계해야 합니다. 이걸 건너뛰면 나중에 진짜 많이 돌아갑니다. 저도 삽질 좀 했습니다 ㅎㅎ

    3. 실제로 많이 본 실패 사례 4가지

    3-1. 저장소 구조를 너무 늦게 정한 경우

    가장 흔한 실수입니다. 앱 소스와 배포 설정이 한 저장소에 섞여 있거나, 반대로 너무 잘게 쪼개져서 어디를 기준으로 봐야 하는지 모호한 경우죠. 처음엔 편해 보였는데, 팀이 커질수록 충돌이 심해집니다. 누가 이미지 태그를 관리하고, 누가 replica 수를 바꾸고, 누가 Ingress(인그레스, 외부 트래픽 진입점)를 수정하는지 경계가 안 잡히거든요.

    3-2. 자동 동기화만 켜고 승인 체계를 안 만든 경우

    auto-sync는 정말 편합니다. 드디어 됐다! 싶은 순간이 오거든요. 근데 여기서 바로 사고가 납니다. 운영 반영 전 검토가 필요한 팀인데도 자동 배포를 먼저 켜버리면, 잘못된 값 하나가 바로 프로덕션으로 갑니다. 도구는 빨라졌는데 프로세스는 그대로인 상태죠.

    3-3. Secret 관리 방식을 정하지 않은 경우

    GitOps 실수에서 빠지지 않는 항목입니다. 민감 정보(Secret)를 평문으로 저장하면 안 되고, 그렇다고 운영자가 클러스터에만 몰래 만들어두면 Git과 실제 상태가 분리됩니다. 저는 이 부분에서 팀마다 가장 오래 멈추는 걸 많이 봤습니다. Sealed Secrets(시일드 시크릿)나 External Secrets Operator 같은 접근을 검토하되, 핵심은 “Git에 무엇을 남기고 실제 값은 어디서 주입할지”를 팀 단위로 합의하는 겁니다.

    3-4. Drift를 장애로 볼지, 운영 유연성으로 볼지 합의가 없는 경우

    운영자가 급한 패치를 직접 넣는 문화가 남아 있으면 ArgoCD는 계속 OutOfSync(아웃오브싱크) 상태를 만들 겁니다. 이걸 경고로 볼지, 바로 되돌릴지, 특정 리소스는 예외로 둘지 정하지 않으면 경보만 쌓이고 신뢰가 깨집니다. 이 지점이 대표적인 ArgoCD 문제점처럼 보이는데, 사실은 정책 부재에 가깝습니다.

    4. 실전 구현: 실패 확률을 낮추는 최소 전환 절차

    제가 직접 해보니, 처음부터 전 서비스에 GitOps를 거는 것보다 작은 서비스 하나로 운영 규칙을 검증하는 게 훨씬 낫더라고요. 아래는 많이 무리하지 않는 최소 절차입니다.

    1. 배포 대상 네임스페이스(namespace) 하나를 파일럿으로 고릅니다.
    2. 애플리케이션 코드 저장소와 배포 매니페스트 저장소의 책임을 분리합니다.
    3. ArgoCD 프로젝트(AppProject)로 허용 대상 클러스터/네임스페이스를 제한합니다.
    4. 자동 동기화는 바로 켜지 말고, 먼저 수동 sync로 운영 흐름을 익힙니다.
    5. 변경 승인 기준을 PR 템플릿과 리뷰 규칙으로 문서화합니다.
    6. Drift 발생 시 대응 절차를 정합니다.

    예시로는 아래처럼 시작하면 무난합니다.

    kubectl create namespace argocd
    kubectl apply -n argocd -f https://raw.githubusercontent.com/argoproj/argo-cd/stable/manifests/install.yaml
    kubectl get pods -n argocd

    설치 자체보다 더 중요한 건 프로젝트 경계를 먼저 두는 일입니다.

    apiVersion: argoproj.io/v1alpha1
    kind: AppProject
    metadata:
      name: sample-project
      namespace: argocd
    spec:
      description: sample project for controlled GitOps rollout
      sourceRepos:
        - 'https://github.com/example/platform-manifests.git'
      destinations:
        - namespace: sample-app
          server: 'https://kubernetes.default.svc'
      clusterResourceWhitelist:
        - group: '*'
          kind: '*'

    그 다음 애플리케이션은 이렇게 붙일 수 있습니다.

    apiVersion: argoproj.io/v1alpha1
    kind: Application
    metadata:
      name: sample-app
      namespace: argocd
    spec:
      project: sample-project
      source:
        repoURL: 'https://github.com/example/platform-manifests.git'
        targetRevision: main
        path: apps/sample-app/overlays/prod
      destination:
        server: 'https://kubernetes.default.svc'
        namespace: sample-app
      syncPolicy: {}

    여기서 일부러 자동 동기화 설정을 비워둔 것이 포인트입니다. 처음엔 수동 sync로 팀이 변경 흐름을 이해하게 만드는 게 좋습니다. 자동화는 익숙해진 뒤에 켜도 늦지 않습니다.

    ArgoCD 도입 실패를 줄이기 위한 AppProject와 저장소 구조 구성 이미지

    AppProject 경계, Application 연결, PR 승인 후 반영되는 흐름을 시각적으로 설명하는 이미지입니다.

    5. CI/CD 전환 사례에서 특히 조심해야 할 체크포인트

    기존 파이프라인에서 이미지 빌드까지는 잘 돌아가는데 GitOps로 넘기면서 꼬이는 경우가 많습니다. 보통 CI는 artifact(아티팩트, 배포 가능한 결과물)를 만들고, CD는 배포를 실행합니다. GitOps에선 이 경계가 바뀝니다. CI가 직접 배포하지 않고, 이미지 태그나 차트 값을 Git에 반영하는 식으로 역할이 이동하죠.

    예를 들면 이런 방식입니다.

    # CI job example
    export IMAGE_TAG=${GIT_COMMIT_SHA}
    sed -i "s/tag: .*/tag: ${IMAGE_TAG}/" apps/sample-app/overlays/prod/values.yaml
    git add apps/sample-app/overlays/prod/values.yaml
    git commit -m "chore: deploy sample-app ${IMAGE_TAG}"
    git push origin main

    이 방식은 단순하지만, 바로 main 브랜치에 반영하면 위험합니다. 제가 추천하는 건 별도 배포 브랜치나 PR 자동 생성 방식입니다. 사람이 마지막으로 한 번 더 보고 머지하는 흐름이 초반 안정화에 꽤 도움이 됩니다.

    • 프로덕션 반영은 PR 승인 후에만 가능하게 설정
    • 리뷰어를 플랫폼 담당자와 서비스 담당자로 분리
    • 롤백 기준을 Git revert 중심으로 문서화
    • 수동 kubectl 적용은 예외 상황에서만 허용

    이런 기본선만 있어도 CI/CD 전환 사례에서 실패 확률이 꽤 내려갑니다.

    6. ⚠️ 트러블슈팅: 제가 실제로 많이 본 문제와 해결법

    여기서부터는 정말 많이 부딪히는 부분들입니다. 처음엔 저도 “왜 sync는 성공인데 앱은 안 뜨지?” 같은 상황을 자주 만났거든요.

    증상 원인 해결 방향
    OutOfSync가 계속 발생 운영자가 클러스터에서 직접 수정 직접 수정 금지 원칙 정리, 예외 리소스만 ignore 설정 검토
    Sync 성공인데 서비스 장애 매니페스트 문법은 맞지만 런타임 의존성 누락 readinessProbe, Secret 참조, ConfigMap 값 검증 강화
    배포가 너무 자주 일어남 이미지 태그나 공통 값 파일이 과도하게 변경됨 환경별 경로 분리, 변경 범위 최소화
    리뷰 병목 발생 모든 변경이 한 저장소에 몰림 팀 단위 경계 재설계, CODEOWNERS 활용
    롤백이 헷갈림 Git revert와 수동 핫픽스가 섞임 롤백 절차를 Git 기준으로 단일화

    6-1. ignoreDifferences 남용

    ArgoCD에는 특정 필드 차이를 무시하는 기능이 있습니다. 편하긴 한데, 이걸 남용하면 drift 감지 의미가 사라집니다. 정말 컨트롤러가 자동으로 바꾸는 필드처럼 불가피한 경우에만 제한적으로 쓰는 게 맞습니다.

    6-2. Helm 값 파일 난립

    환경이 늘수록 values 파일이 너무 많아집니다. dev, stage, prod는 그렇다 쳐도 팀별 패치, 긴급 패치, 지역별 패치가 늘어나면 나중에 아무도 구조를 설명 못 합니다. 저도 홈랩에서 비슷하게 풀었다가, 결국 Kustomize(커스터마이즈, 오버레이 기반 설정 관리)와 역할을 분리하면서 정리했었습니다.

    6-3. 알림 없이 운영

    Sync 실패나 Health degraded(헬스 저하) 이벤트를 아무도 못 받으면, ArgoCD UI를 매번 열어보기 전까지 장애를 놓치게 됩니다. 알림 체계는 초반부터 붙이는 게 좋습니다. 최소한 운영 채널로 실패 이벤트는 전달되게 구성해두세요.

    7. 검증: 전환이 잘 되고 있는지 어떻게 확인할까

    성공 기준도 미리 정의해야 합니다. 그냥 “ArgoCD가 떴다”로 끝내면 안 됩니다. 저는 보통 아래 항목으로 봅니다.

    1. 배포 변경이 모두 PR과 커밋으로 추적되는가
    2. 직접 클러스터 수정 비율이 줄고 있는가
    3. 장애 시 마지막 변경점 확인 시간이 짧아졌는가
    4. 롤백 절차가 Git revert 기준으로 일관되게 동작하는가
    5. 서비스별 소유권(owner)이 저장소 구조와 일치하는가

    ArgoCD UI에서 Health, Sync 상태를 보는 것도 좋지만, 그것만으론 부족합니다. 진짜 중요한 건 운영팀과 개발팀이 같은 변경 이력을 보고 같은 언어로 이야기하게 됐는지입니다. 이게 되면 도입이 절반은 성공한 겁니다. 실제로 써보니까 배포 속도보다 커뮤니케이션 비용이 더 크게 줄더라고요.

    ArgoCD 도입 실패 검증을 위한 Sync와 Health 상태 대시보드 이미지

    배포 상태 확인, 이상 징후 탐지, 롤백 판단 포인트를 한눈에 보여주는 결과 검증 이미지입니다.

    8. 정리 FAQ: 많이 받는 질문

    Q1. ArgoCD는 무조건 자동 동기화로 써야 하나요?

    아닙니다. 초반에는 수동 sync가 오히려 안전합니다. 팀이 GitOps 흐름에 익숙해지고 승인 절차가 자리 잡으면 그때 auto-sync를 검토해도 됩니다.

    Q2. 기존 CI/CD를 전부 버려야 하나요?

    그건 아닙니다. 빌드와 테스트는 CI가 계속 담당하고, 배포 실행만 Git 기반으로 넘기는 식이 일반적입니다.

    Q3. ArgoCD 문제점은 결국 도구 한계 아닌가요?

    일부는 맞지만, 현장에서 보이는 대부분은 정책과 구조 문제였습니다. 특히 저장소 책임 분리와 승인 체계가 약하면 같은 문제가 반복됩니다.

    Q4. 작은 팀도 GitOps가 필요할까요?

    작은 팀도 필요할 수 있습니다. 다만 처음부터 무겁게 가지 말고, 단일 서비스와 단순한 저장소 구조로 시작하는 편이 좋습니다.

    ArgoCD 도입 실패 방지 전략과 GitOps 실수 비교 요약 이미지

    도입 전후 비교, 실패 패턴, 예방 전략을 요약한 인포그래픽 형태의 정리 이미지입니다.

    9. 마무리: ArgoCD 도입 실패를 줄이는 진짜 핵심

    ArgoCD 도입 실패는 대개 설치 실패가 아니라 운영 모델 설계 실패입니다. GitOps는 도구를 붙이는 프로젝트가 아니라, 변경을 기록하고 승인하고 되돌리는 방식을 다시 정의하는 작업이거든요. 저도 처음엔 UI가 예쁘고 sync가 자동으로 돌아가니까 금방 안정화될 줄 알았는데, 실제론 저장소 구조와 팀 규칙을 먼저 잡아야 효과가 났습니다.

    정리하면 이렇습니다. GitOps 실수를 줄이려면 작은 범위로 시작하고, 수동 sync로 흐름을 익히고, 저장소 책임과 승인 규칙을 먼저 고정해야 합니다. 그리고 drift, Secret, 롤백 기준을 문서로 남겨야 합니다. 이 4가지만 해도 현장에서 체감 차이가 큽니다.

    다음 글에서는 App of Apps 패턴과 멀티 클러스터 운영에서 어디까지 표준화해야 하는지 다뤄볼 예정입니다. 이전 글에서 다뤘던 Kubernetes 배포 기본기와 함께 보시면 흐름이 더 잘 잡히실 겁니다. 혹시 지금 ArgoCD 도입 실패를 겪고 계시다면, 설치 로그보다 먼저 운영 규칙부터 점검해보세요. 그게 생각보다 훨씬 빠른 지름길입니다. 🎉

  • [k8s] ArgoCD로 멀티 클러스터 GitOps 배포 자동화 완벽 가이드

    [k8s] ArgoCD로 멀티 클러스터 GitOps 배포 자동화 완벽 가이드

    ArgoCD로 멀티 클러스터 GitOps 배포 자동화 완벽 가이드

    안녕하세요, 13년차 서버실 지킴이입니다. 오늘도 어김없이 여러분의 인프라 여정에 도움이 될 만한 꿀팁을 들고 왔습니다. 혹시 여러 쿠버네티스 클러스터를 운영하면서 배포 때문에 머리 아팠던 경험 있으신가요? 개발, 스테이징, 운영 환경이 각각 다른 클러스터에 존재하거나, 온프레미스와 클라우드를 넘나드는 하이브리드 환경에서 일관된 배포를 유지하는 게 정말 쉽지 않거든요. 저도 처음엔 수동 배포 지옥에서 헤매던 시절이 있었죠. 클러스터마다 kubectl 명령어를 치고, YAML 파일을 수정하고, 휴먼 에러로 인해 서비스 장애를 겪었던 아찔한 순간들도 많았고요. 😱

    하지만 GitOps(깃옵스)를 만나고, 특히 ArgoCD(아르고CD)를 활용하면서 이런 배포 스트레스가 확 줄었습니다. 오늘은 저처럼 멀티 클러스터 환경에서 배포 자동화에 목마른 분들을 위해, ArgoCD로 GitOps를 구현하고 여러 클러스터에 애플리케이션을 자동으로 배포하는 멀티 클러스터 GitOps 배포 자동화 방법을 완벽하게 가이드해 드리려고 합니다. 제가 직접 삽질하며 터득한 경험들을 솔직하게 공유할 테니, 끝까지 따라오시면 분명 큰 도움이 될 겁니다! 자, 그럼 시작해 볼까요?

    ArgoCD를 활용한 멀티 클러스터 GitOps 배포 아키텍처는 위와 같이 구성될 수 있습니다. 하나의 ArgoCD 인스턴스가 여러 쿠버네티스 클러스터에 애플리케이션을 배포하고 관리하는 모습이죠. Git을 중심으로 모든 클러스터의 상태를 동기화하는 핵심 아이디어를 담고 있습니다.

    1. GitOps와 ArgoCD, 그리고 멀티 클러스터 배포, 이게 뭔가요?

    본격적인 실전에 앞서, 핵심 개념들을 짚고 넘어가는 게 중요하겠죠? 제가 늘 강조하는 부분인데, 도구를 잘 쓰는 것도 중요하지만, 그 도구가 왜 필요하고 어떤 철학을 담고 있는지 이해하는 게 진짜 실력입니다.

    • GitOps (깃옵스): 쉽게 말해, Git을 유일한 진실의 원천(Single Source of Truth)으로 삼아 인프라와 애플리케이션 배포를 자동화하는 운영 방식이에요. 모든 설정과 배포 상태를 Git 리포지토리에 코드로 관리하고, Git에 변경사항이 푸시되면 자동으로 인프라에 반영되도록 하는 거죠. 개발자들이 코드 관리하듯이 인프라를 관리한다고 생각하시면 됩니다. 일관성, 버전 관리, 감사 추적(Audit Trail)이 가능하다는 엄청난 장점이 있어요.

    • ArgoCD (아르고CD): 이 친구는 GitOps를 쿠버네티스(Kubernetes) 환경에서 구현해주는 선언적(Declarative) GitOps 지속적 배포(Continuous Delivery) 툴입니다. Git 리포지토리에 정의된 원하는 상태(Desired State)와 실제 쿠버네티스 클러스터의 현재 상태(Live State)를 끊임없이 비교하고, 만약 다르면 Git에 정의된 상태로 클러스터를 동기화(Sync)시켜 줍니다. 마치 클러스터 상태를 감시하는 파수꾼 같다고 할까요? 정말 든든한 친구더라고요.

    • 멀티 클러스터 배포 (Multi-Cluster Deployment): 여러 개의 쿠버네티스 클러스터에 동일하거나 다른 애플리케이션을 배포하고 관리하는 시나리오를 말합니다. 개발, 스테이징, 운영 환경을 분리하거나, 재해 복구(DR)를 위해 지리적으로 분산된 클러스터를 운영할 때 주로 사용하죠. ArgoCD는 하나의 중앙 인스턴스에서 여러 클러스터를 관리할 수 있어서, 멀티 클러스터 환경에서 배포를 중앙 집중화하고 자동화하는 데 아주 탁월한 솔루션입니다.

    2. 실전 구현: ArgoCD로 멀티 클러스터 GitOps 환경 구축하기

    자, 이제 이론은 충분히 봤으니, 저와 함께 직접 만들어 볼 시간입니다. 제가 홈랩에서 여러 번 시도하면서 가장 효율적이라고 생각했던 방법으로 안내해 드릴게요. 따라만 하시면 됩니다! 💡

    2.1. 사전 준비물 (Prerequisites)

    시작하기 전에 몇 가지 준비물이 필요합니다.

    • 쿠버네티스 클러스터 2개 이상: 하나는 ArgoCD를 설치할 ‘컨트롤 플레인 클러스터’로, 나머지는 애플리케이션을 배포할 ‘타겟 클러스터’로 사용할 겁니다. (저는 KIND나 K3s로 쉽게 구성했어요.)
    • kubectl: 쿠버네티스 클러스터를 제어하는 CLI 도구.
    • Helm (선택 사항): 쿠버네티스 패키지 매니저. ArgoCD 설치에 Helm을 사용하진 않지만, 애플리케이션 배포에 유용할 수 있습니다.
    • Git 리포지토리: 배포할 애플리케이션의 YAML 파일들을 저장할 공간 (GitHub, GitLab, Bitbucket 등).

    2.2. 단계 1: ArgoCD 설치 (컨트롤 플레인 클러스터)

    먼저 ArgoCD를 설치할 클러스터에 ArgoCD를 배포합니다. 이 클러스터가 모든 배포의 중앙 통제실 역할을 하게 됩니다.

    1. ArgoCD 네임스페이스 생성

      kubectl create namespace argocd
    2. ArgoCD 설치 YAML 적용

      kubectl apply -n argocd -f https://raw.githubusercontent.com/argoproj/argo-cd/stable/manifests/install.yaml

      이 명령은 ArgoCD의 모든 컴포넌트(API 서버, 컨트롤러, 레포 서버 등)를 설치합니다. 잠시 기다리면 파드들이 정상적으로 올라올 거예요.

    3. ArgoCD CLI 설치 (선택 사항이지만 강력 추천!)

      # macOS (Homebrew)
      brew install argocd
      
      # Linux (직접 다운로드)
      curl -sSL -o argocd-linux-amd64 https://github.com/argoproj/argo-cd/releases/latest/download/argocd-linux-amd64
      sudo install -m 555 argocd-linux-amd64 /usr/local/bin/argocd
      rm argocd-linux-amd64
    4. 초기 비밀번호 확인 및 UI 접근

      ArgoCD API 서버의 초기 비밀번호는 컨트롤 플레인 클러스터의 argocd-initial-admin-secret 시크릿에 저장되어 있습니다.

      kubectl -n argocd get secret argocd-initial-admin-secret -o jsonpath="{.data.password}" | base64 -d; echo

      UI에 접근하려면 포트 포워딩(Port Forwarding)을 해줘야 합니다.

      kubectl port-forward svc/argocd-server -n argocd 8080:443

      이제 웹 브라우저에서 https://localhost:8080으로 접속한 후, 사용자명 admin과 위에서 확인한 비밀번호로 로그인하세요. 첫 로그인 후 비밀번호를 변경하는 것을 추천합니다.

    2.3. 단계 2: Git Repository 준비

    배포할 애플리케이션의 YAML 파일들을 Git 리포지토리에 준비해야 합니다. 저는 간단한 Nginx 배포 파일을 예시로 들어볼게요.

    my-gitops-repo/apps/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:1.14.2
            ports:
            - containerPort: 80

    my-gitops-repo/apps/nginx/service.yaml

    apiVersion: v1
    kind: Service
    metadata:
      name: nginx-service
    spec:
      selector:
        app: nginx
      ports:
        - protocol: TCP
          port: 80
          targetPort: 80
      type: LoadBalancer # 또는 ClusterIP, NodePort

    이 파일들을 Git 리포지토리에 푸시해 주세요. (예: https://github.com/your-org/my-gitops-repo.git)

    2.4. 단계 3: 타겟 클러스터 등록 (ArgoCD에 연결)

    이제 ArgoCD가 애플리케이션을 배포할 타겟 클러스터들을 ArgoCD에 등록해야 합니다. ArgoCD CLI를 사용하면 아주 쉽게 등록할 수 있어요.

    1. ArgoCD CLI 로그인

      argocd login localhost:8080

      초기 비밀번호로 로그인합니다.

    2. 타겟 클러스터 등록

      현재 kubeconfig 파일에 정의된 클러스터 컨텍스트(Context)를 사용해서 등록합니다. 등록할 클러스터의 컨텍스트로 kubectl config use-context <TARGET_CLUSTER_CONTEXT> 명령어를 실행한 후, 아래 명령어를 실행하세요.

      argocd cluster add <TARGET_CLUSTER_CONTEXT>

      예를 들어, dev-cluster와 prod-cluster라는 컨텍스트가 있다면 각각 등록해 줍니다.

      이 명령은 ArgoCD가 타겟 클러스터에 접근할 수 있도록 필요한 RBAC 설정과 시크릿을 자동으로 생성해 줍니다. ⚠️ 권한 문제로 많이들 삽질하시는데, 이 단계에서 정확한 컨텍스트를 선택했는지 꼭 확인하세요.

    3. 등록된 클러스터 확인

      argocd cluster list

      등록된 클러스터 목록이 보이면 성공입니다! ArgoCD UI에서도 ‘Clusters’ 메뉴에서 확인할 수 있습니다.

    ArgoCD UI에서 클러스터가 성공적으로 등록되고 ApplicationSet이 여러 클러스터에 배포되는 모습을 시각적으로 확인할 수 있습니다. 각 클러스터별로 애플리케이션 상태를 한눈에 파악할 수 있죠.

    2.5. 단계 4: ApplicationSet으로 멀티 클러스터 배포 자동화

    이제 이 글의 핵심인 ApplicationSet(애플리케이션셋)을 활용해서 여러 클러스터에 Nginx 애플리케이션을 배포해 볼 시간입니다. ApplicationSet은 여러 개의 ArgoCD Application 리소스를 동적으로 생성해주는 컨트롤러입니다. 특히 멀티 클러스터 환경에서 빛을 발하죠!

    ApplicationSet을 사용하면, 등록된 모든 클러스터에 동일한 애플리케이션을 배포하거나, 클러스터별로 약간 다른 설정을 적용하여 배포할 수 있습니다. 여기서는 Cluster Generator를 사용해서 ArgoCD에 등록된 모든 클러스터에 Nginx를 배포하도록 해볼게요.

    my-gitops-repo/applicationsets/nginx-applicationset.yaml

    apiVersion: argoproj.io/v1alpha1
    kind: ApplicationSet
    metadata:
      name: multi-cluster-nginx
      namespace: argocd
    spec:
      generators:
      - clusters: {}
      template:
        metadata:
          name: '{{name}}-nginx' # 클러스터 이름 기반으로 Application 이름 생성
          namespace: argocd
        spec:
          project: default
          source:
            repoURL: https://github.com/your-org/my-gitops-repo.git # 본인의 Git 리포지토리 URL로 변경
            targetRevision: HEAD
            path: apps/nginx
          destination:
            server: '{{server}}'
            namespace: default
          syncPolicy:
            automated:
              prune: true
              selfHeal: true
            syncOptions:
              - CreateNamespace=true # 대상 클러스터에 네임스페이스가 없으면 생성

    위 YAML 파일을 Git 리포지토리에 푸시한 후, ArgoCD가 설치된 컨트롤 플레인 클러스터에 적용합니다.

    kubectl apply -n argocd -f my-gitops-repo/applicationsets/nginx-applicationset.yaml

    잠시 후 ArgoCD UI의 ‘Applications’ 메뉴로 이동해 보세요. 등록된 클러스터 개수만큼 <클러스터이름>-nginx 형태의 애플리케이션이 자동으로 생성되고, 배포가 시작되는 것을 확인할 수 있을 겁니다. 정말 신기하더라고요! 🎉

    3. 주의사항 및 트러블슈팅: 저의 삽질 경험담 ⚠️

    제가 13년간 인프라 엔지니어로 일하면서 깨달은 건, 새로운 기술을 도입할 때 늘 예상치 못한 문제가 터진다는 겁니다. ArgoCD도 예외는 아니었죠. 제가 겪었던 주요 삽질 경험과 해결 팁을 공유해 드립니다.

    • RBAC (Role-Based Access Control) 문제: 타겟 클러스터 등록 시 ArgoCD가 해당 클러스터에 접근할 권한이 없어서 배포가 실패하는 경우가 많았습니다. argocd cluster add 명령이 자동으로 RBAC를 설정해주지만, 때로는 특정 리소스에 대한 추가 권한이 필요할 수 있어요. ArgoCD 서버의 서비스 어카운트(Service Account)에 필요한 ClusterRoleBinding이 제대로 되어있는지 확인하고, 필요하다면 수동으로 추가해줘야 합니다.

      # 예시: ArgoCD 서비스 어카운트에 Cluster-Admin 권한 부여 (실제 운영에서는 최소 권한 원칙 준수)
      apiVersion: rbac.authorization.k8s.io/v1
      kind: ClusterRoleBinding
      metadata:
        name: argocd-manager-role-binding
      subjects:
      - kind: ServiceAccount
        name: argocd-argocd-application-controller
        namespace: argocd
      roleRef:
        apiGroup: rbac.authorization.k8s.io
        kind: ClusterRole
        name: cluster-admin # or a more specific role
    • Kubeconfig 파일 접근 권한: ArgoCD가 타겟 클러스터에 접근하려면 컨트롤 플레인 클러스터에서 타겟 클러스터의 API 서버에 네트워크적으로 연결이 가능해야 합니다. 방화벽, VPC 설정 등을 꼼꼼히 확인하세요. 특히 온프레미스와 클라우드 하이브리드 환경에서는 네트워크 경로 설정이 복잡해질 수 있습니다.

    • Sync Policy 설정: automated: prune: true와 selfHeal: true 옵션은 배포를 매우 편리하게 해주지만, 자칫 잘못하면 예상치 못한 변경이 즉시 반영될 수 있습니다. 특히 운영 환경에서는 신중하게 접근하고, 처음에는 수동 동기화(Manual Sync)로 시작하여 동작을 충분히 이해한 후 자동화하는 것을 추천합니다.

    • ApplicationSet Generator 문제: Cluster Generator를 쓸 때, Git 리포지토리의 경로(path)나 targetRevision, 그리고 destination의 server와 namespace 설정에 오타가 없는지 여러 번 확인해야 합니다. 변수({{name}}, {{server}}) 사용법도 헷갈리기 쉽더라고요.

    4. 검증 및 결과: 드디어 됐다! ✅

    이제 ArgoCD UI에서 모든 애플리케이션들이 Synced 상태인지 확인해 보세요. 각 클러스터에 배포된 Nginx 파드들도 kubectl get pods -n default 명령으로 확인하면 정상적으로 실행되고 있을 겁니다. 드디어 모든 클러스터에 배포가 완료됐네요! 이 맛에 GitOps 하는 거 아니겠습니까? 🎉

    여기서 끝이 아닙니다! GitOps의 진정한 가치는 Git 변경 사항이 자동으로 반영되는 데 있거든요. my-gitops-repo/apps/nginx/deployment.yaml 파일의 replicas 수를 2에서 3으로 변경하고 Git에 푸시해 보세요. ArgoCD가 변경 사항을 감지하고, 몇 초 내에 모든 타겟 클러스터의 Nginx 파드 개수가 3개로 자동으로 업데이트되는 것을 볼 수 있을 겁니다. 정말 편하더라고요!

    ArgoCD 대시보드에서 여러 클러스터에 배포된 애플리케이션들의 실시간 상태를 모니터링하는 화면입니다. 모든 애플리케이션이 정상적으로 동기화(Synced)된 것을 확인할 수 있습니다.

    5. 마무리: GitOps로 한 단계 더 성장하기

    오늘은 ArgoCD를 활용하여 멀티 클러스터 GitOps 배포 자동화를 구현하는 과정을 저의 경험을 바탕으로 상세하게 설명해 드렸습니다. 어떠셨나요? 처음엔 복잡해 보이지만, 한 번 구축해 놓으면 배포의 일관성, 속도, 안정성이 비약적으로 향상되는 것을 체감하실 수 있을 겁니다.

    멀티 클러스터 환경에서 ArgoCD와 GitOps는 정말 강력한 조합입니다. 수동 작업에서 벗어나 휴먼 에러를 줄이고, Git을 통한 완벽한 버전 관리와 감사 추적이 가능해지거든요. 무엇보다 인프라 엔지니어가 더 중요하고 전략적인 업무에 집중할 수 있도록 도와주는 멋진 도구라고 생각합니다.

    물론 초기 설정에 시간과 노력이 필요하고, GitOps 문화에 적응하는 과정도 필요합니다. 하지만 그 투자 가치는 충분하다고 제가 직접 보증합니다. 제 홈랩에서도 이 방식으로 다양한 서비스를 배포하고 있거든요. 😉

    다음번에는 오늘 배운 ArgoCD와 GitOps 개념을 확장해서, Helm Chart 통합, Kustomize 활용, 그리고 Argo Rollouts(아르고 롤아웃)를 이용한 블루/그린, 카나리 배포 전략까지 자동화하는 경험담을 들려드릴게요! 이 글이 여러분의 인프라 여정에 작은 이정표가 되었기를 바랍니다. 다음에 또 만나요!

    GitOps와 기존 배포 방식, 그리고 ArgoCD의 멀티 클러스터 관리 장점을 요약한 인포그래픽입니다. 어떤 점들이 개선되었는지 한눈에 파악할 수 있습니다.

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

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

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

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

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

    GitOps와 Flux CD, 정말 좋은 이유

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

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

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

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

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

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

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

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

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

    2단계: Git 저장소 준비

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    ✅ 배포 결과 확인 및 검증

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    Helm의 주요 구성 요소

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

    Helm 차트의 핵심 구성 요소

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

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

    실전! Helm 차트 개발 및 배포

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

    1단계: 차트 초기화

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

    helm create my-nginx-chart

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

    2단계: values.yaml 설정

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

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

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

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

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

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

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

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

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

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

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

    4단계: 차트 배포

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

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

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

    5단계: 차트 업데이트

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

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

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

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

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

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

    주요 트러블슈팅 팁

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

    배포 확인 및 결과 검증

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

    배포된 릴리즈 확인

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

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

    쿠버네티스 리소스 확인

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

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

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

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

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

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

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

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

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

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

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