Soomin Hwang
DevOps / 클라우드 인프라

커밋에서 프로덕션까지: Jenkins + K3s + ArgoCD GitOps 파이프라인

역할
DevOps 엔지니어 (단독)
기간
2024년 1월 – 현재
기술 스택
JenkinsDockerGitHub Container RegistryK3sArgoCDGitOpsNginxTailscale
공개 범위
공개 저장소 →
배포
수동 SSH5분 이내 자동완전 자동화
인프라
단일 VPSK3s 클러스터고가용성

배경

Next14-R3F 포트폴리오 프로젝트(별도 케이스 스터디 참고)에는 프로덕션 등급의 배포 파이프라인이 필요했습니다. 흔한 "SSH 접속 → pull → 빌드 → 재시작" 방식 대신, 완전한 GitOps 파이프라인을 구축했습니다:

개발자 push → GitHub → Jenkins 빌드 & push → GitOps 저장소 업데이트 → ArgoCD sync → K3s 클러스터

GitHub: Lajancia/Next14-R3F (Jenkinsfile 포함)

파이프라인 아키텍처

GitHub → Jenkins 트리거

GitHub에 설정된 웹훅이 push 이벤트를 Jenkins 서버로 보냅니다. main 브랜치로의 push를 받으면 파이프라인이 자동으로 시작됩니다.

1단계: Checkout

Jenkins가 push된 커밋 기준으로 저장소를 체크아웃해서, 빌드가 개발자가 의도한 내용과 정확히 일치하도록 보장합니다.

2단계: Docker 빌드 & push

stage('Docker Build & Push') {
    steps {
        script {
            def shortCommit = sh(returnStdout: true,
              script: 'git rev-parse --short HEAD').trim()
            sh "docker build -f dockerfile -t ${GHCR_IMAGE}:${shortCommit} ."
            sh "docker tag ${GHCR_IMAGE}:${shortCommit} ${GHCR_IMAGE}:latest"
            sh "docker push ${GHCR_IMAGE}:${shortCommit}"
            sh "docker push ${GHCR_IMAGE}:latest"
        }
    }
}

멀티 스테이지 Docker 빌드는 의존성 관리에 pnpm을 씁니다:

FROM node:20-alpine AS base
RUN corepack enable && corepack prepare [email protected] --activate

FROM base AS deps
WORKDIR /app
COPY package.json pnpm-lock.yaml ./
RUN pnpm install --no-frozen-lockfile

FROM base AS builder
WORKDIR /app
COPY --from=deps /app/node_modules ./node_modules
COPY . .
RUN pnpm panda && pnpm build

FROM base AS runner
WORKDIR /app
COPY --from=builder /app/.next/standalone ./
COPY --from=builder /app/.next/static ./.next/static
COPY --from=builder /app/public ./public

핵심 최적화 포인트:

  • pnpm — npm 대비 빠른 설치와 디스크 효율
  • output: 'standalone' — 최소 Docker 이미지(러너 스테이지 약 110MB)
  • 멀티 스테이지 빌드 — deps 캐시 레이어 덕분에 코드만 바뀐 경우 재설치 불필요

3단계: GitOps 저장소 업데이트

서버에 SSH로 접속하는 대신, Jenkins가 별도의 GitOps 저장소를 업데이트합니다:

stage('Update GitOps Repo') {
    steps {
        script {
            def shortCommit = sh(...)
            sh """
                git clone https://github.com/<gitops-org>/next-r3f-ops.git
                sed -i 's|image: ...|image: ${GHCR_IMAGE}:${shortCommit}|' next-r3f.yaml
                git commit -m "chore: update image tag to ${shortCommit}"
                git push
            """
        }
    }
}

GitOps 저장소에는 ArgoCD가 감시하는 쿠버네티스 매니페스트(next-r3f.yaml) 하나가 들어 있습니다.

4단계: ArgoCD sync

K3s 클러스터에서 돌고 있는 ArgoCD가 3분마다 GitOps 저장소를 폴링합니다. 이미지 태그 변경을 감지하면 새 매니페스트를 클러스터에 자동으로 동기화합니다 — 무중단으로 롤링 업데이트됩니다.

인프라 다이어그램

배포 경로와 서빙 경로는 서로 별개입니다. 배포는 main에 머지될 때마다 한 방향으로만 흐릅니다:

GitHub push
     ↓
PR 리뷰 후 main에 머지
     ↓
Jenkins webhook
     ↓
Docker 이미지 빌드 → GHCR push
     ↓
GitOps 저장소 업데이트 (이미지 태그 교체)
     ↓
ArgoCD sync
     ↓
K3s 클러스터 배포 (롤링 업데이트)

서빙 경로는 위 흐름과 무관합니다. Nginx가 SSL을 종료하고 K3s 위에서 돌고 있는 Next.js 애플리케이션으로 요청을 프록시합니다. Tailscale은 클러스터 관리용 접근 경로로, 두 경로 어느 쪽에도 끌어오지 않습니다.

인프라 세부 사항

K3s 클러스터

  • 단일 VPS 배포에 적합한 경량 쿠버네티스 배포판
  • Nginx 리버스 프록시 뒤에서 VPS 위에 실행
  • 관리용 접근은 Tailscale VPN을 통해서만 노출

Nginx

  • SSL 종료(Let's Encrypt)를 처리하는 리버스 프록시
  • K3s 위에서 실행되는 Next.js 애플리케이션으로 트래픽 라우팅
  • 성능을 위한 정적 자산 캐싱 헤더 설정

Tailscale

  • 보안 클러스터 접근을 위한 WireGuard 기반 메시 VPN
  • K3s 노드에 열린 포트 없음 — 모든 트래픽이 Tailscale을 경유
  • 관리자 대시보드와 모니터링은 VPN을 통해서만 접근 가능

결과

지표이전이후
배포 방식수동 SSHGitOps (완전 자동화)
배포 시간~20분< 5분
롤백수동git revert + push
접근공개 포트Tailscale VPN + Nginx

직접 검증하는 방법

  1. Jenkinsfile 확인: Lajancia/Next14-R3F/blob/main/Jenkinsfile
  2. Docker 멀티 스테이지 빌드 확인: Lajancia/Next14-R3F/blob/main/dockerfile

관련 케이스 스터디

이 파이프라인이 배포하는 애플리케이션에 대해서는 3D 포트폴리오 케이스 스터디를 참고하세요.

이 케이스 스터디가 흥미로우셨나요?

가벼운 커피챗도 언제든 환영합니다. 편하게 연락 주세요.