커밋에서 프로덕션까지: Jenkins + K3s + ArgoCD GitOps 파이프라인
배경
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을 통해서만 접근 가능
결과
| 지표 | 이전 | 이후 |
|---|---|---|
| 배포 방식 | 수동 SSH | GitOps (완전 자동화) |
| 배포 시간 | ~20분 | < 5분 |
| 롤백 | 수동 | git revert + push |
| 접근 | 공개 포트 | Tailscale VPN + Nginx |
직접 검증하는 방법
- Jenkinsfile 확인: Lajancia/Next14-R3F/blob/main/Jenkinsfile
- Docker 멀티 스테이지 빌드 확인: Lajancia/Next14-R3F/blob/main/dockerfile
관련 케이스 스터디
이 파이프라인이 배포하는 애플리케이션에 대해서는 3D 포트폴리오 케이스 스터디를 참고하세요.
이 케이스 스터디가 흥미로우셨나요?
가벼운 커피챗도 언제든 환영합니다. 편하게 연락 주세요.