근묵자흑
Kubernetes Pattern: 선언적 배포(Declarative Deployment) + FluxCD 본문
k8s/kubernetes-pattern
Kubernetes Pattern: 선언적 배포(Declarative Deployment) + FluxCD
Luuuuu 2025. 8. 9. 19:25왜 선언적 배포인가?
여러분이 개발한 애플리케이션을 Kubernetes에 배포한다고 상상해보세요. 새 버전을 출시할 때마다 이런 고민을 하게 됩니다.
- "서비스 중단 없이 업데이트할 수 있을까?"
- "문제가 생기면 빠르게 롤백할 수 있을까?"
- "일부 사용자에게만 먼저 테스트해볼 수 없을까?"
이 모든 질문의 답이 바로 선언적 배포 패턴에 있습니다.
명령형 vs 선언적: 패러다임의 전환
# 명령형 방식 (Imperative) - "어떻게" 할지 일일이 지시
kubectl create deployment my-app --image=nginx:1.0
kubectl scale deployment my-app --replicas=3
kubectl set image deployment/my-app nginx=nginx:2.0
kubectl rollout status deployment/my-app
# 선언적 방식 (Declarative) - "무엇"을 원하는지만 선언
kubectl apply -f deployment.yaml
선언적 방식의 핵심은 원하는 상태(Desired State)를 정의하면, Kubernetes가 알아서 현재 상태를 원하는 상태로 만들어준다는 것입니다.
4가지 배포 전략 심층 분석
이번장의 실습 코드는 GitHub 저장소에서 확인할 수 있습니다.
1.1 롤링 업데이트 (Rolling Update) - 안정적인 점진적 교체
롤링 업데이트는 구 버전의 파드를 하나씩 새 버전으로 교체하는 방식입니다. 마치 달리는 기차의 바퀴를 하나씩 교체하는 것과 같습니다.
실제 구현 코드 분석
# apps/rolling-update/deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: random-generator
namespace: rolling-demo
spec:
replicas: 3 # 총 3개의 파드 운영
strategy:
type: RollingUpdate
rollingUpdate:
maxSurge: 1 # 업데이트 중 추가로 생성 가능한 파드 수
maxUnavailable: 1 # 업데이트 중 사용 불가능한 파드 수
template:
spec:
containers:
- name: random-generator
image: k8spatterns/random-generator:1.0
livenessProbe: # 파드가 살아있는지 확인
httpGet:
path: /actuator/health
port: 8080
initialDelaySeconds: 30
readinessProbe: # 트래픽을 받을 준비가 되었는지 확인
httpGet:
path: /actuator/health
port: 8080
initialDelaySeconds: 5
핵심 파라미터 설명:
maxSurge: 1
: 업데이트 중 최대 4개(3+1)까지 파드 운영 가능maxUnavailable: 1
: 최소 2개(3-1)의 파드는 항상 서비스 제공livenessProbe
: 파드 헬스체크 실패 시 재시작readinessProbe
: 준비되지 않은 파드로는 트래픽 안 보냄
실제 테스트 결과
# 테스트 실행
./scripts/test-rolling-update.sh
# 결과: 3개 파드가 순차적으로 업데이트
NAME READY STATUS AGE
random-generator-5c7d8f9b4f-xyz123 1/1 Running 10s # 새 버전
random-generator-5c7d8f9b4f-abc456 1/1 Running 20s # 새 버전
random-generator-6b8d7f5c9d-def789 1/1 Terminating 5m # 구 버전 종료 중
장점:
- 무중단 배포
- 리소스 효율적 (110-125% 사용)
- 자동 롤백 가능
단점:
- 배포 중 신/구 버전 혼재
- 데이터베이스 스키마 변경 시 주의 필요
1.2 블루-그린 배포 - 즉각적인 전환과 롤백
블루-그린은 두 개의 완전히 동일한 환경을 운영하며, 트래픽을 한 번에 전환하는 방식입니다.
실제 구현 코드 분석
# apps/blue-green/deployment-blue.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: random-generator-blue
labels:
version: blue # Blue 버전 식별자
spec:
replicas: 3
selector:
matchLabels:
app: random-generator
version: blue
template:
metadata:
labels:
app: random-generator
version: blue # 파드 레이블
spec:
containers:
- name: random-generator
image: k8spatterns/random-generator:1.0
env:
- name: VERSION
value: "blue"
# apps/blue-green/service.yaml
apiVersion: v1
kind: Service
metadata:
name: random-generator
spec:
selector:
app: random-generator
version: blue # 초기: Blue로 트래픽 라우팅
ports:
- port: 80
targetPort: 8080
---
# Preview 서비스 - Green 테스트용
apiVersion: v1
kind: Service
metadata:
name: random-generator-preview
spec:
selector:
app: random-generator
version: green # 항상 Green을 가리킴
트래픽 전환 메커니즘
# Blue에서 Green으로 전환 (Service의 selector 변경)
kubectl patch service random-generator \
-p '{"spec":{"selector":{"version":"green"}}}'
# 문제 발생 시 Blue로 즉시 롤백
kubectl patch service random-generator \
-p '{"spec":{"selector":{"version":"blue"}}}'
실제 테스트 결과:
# 전환 전: Blue 버전 서비스 중
$ curl http://localhost:8080/actuator/info
{"pattern": "Blue-Green (Blue Version)"}
# 전환 명령 실행 (1초 이내 완료)
$ kubectl patch service random-generator -p '{"spec":{"selector":{"version":"green"}}}'
# 전환 후: Green 버전 서비스 중
$ curl http://localhost:8080/actuator/info
{"pattern": "Blue-Green (Green Version)"}
장점:
- 즉각적인 전환 (1초 이내)
- 간단한 롤백
- 전체 환경 테스트 가능
단점:
- 리소스 2배 필요
- 데이터베이스 동기화 복잡
- 스테이트풀 앱 적용 어려움
1.3 카나리 배포 - 위험을 최소화하는 점진적 릴리스
카나리 배포는 새 버전을 일부 사용자에게만 먼저 배포하여 위험을 최소화합니다. 위험을 먼저 감지하는 역할을 수행합니다.
Flagger를 활용한 자동화된 카나리 배포
# apps/canary/canary.yaml
apiVersion: flagger.app/v1beta1
kind: Canary
metadata:
name: random-generator
spec:
targetRef:
apiVersion: apps/v1
kind: Deployment
name: random-generator
# 단계적 트래픽 증가 설정
analysis:
interval: 30s # 분석 주기
threshold: 5 # 실패 허용 횟수
maxWeight: 50 # 최대 카나리 트래픽
stepWeight: 10 # 단계별 증가율
# 메트릭 기반 자동 판단
metrics:
- name: request-success-rate
thresholdRange:
min: 99 # 99% 이상 성공률 유지
interval: 30s
- name: request-duration
thresholdRange:
max: 500 # 500ms 이하 응답시간
interval: 30s
# 부하 테스트 자동 실행
webhooks:
- name: load-test
type: rollout
url: http://flagger-loadtester/
metadata:
cmd: "hey -z 2m -q 10 -c 2 http://random-generator/"
카나리 배포 진행 과정
# 1. 초기 상태: Stable 버전만 운영
Status: Initialized | Canary Weight: 0% | Iterations: 0
# 2. 새 버전 배포 시작
$ kubectl set image deployment/random-generator \
random-generator=k8spatterns/random-generator:2.0
# 3. Flagger가 자동으로 진행 상황 관리
Status: Progressing | Canary Weight: 10% | Iterations: 1
# → 10% 트래픽을 새 버전으로, 메트릭 확인
Status: Progressing | Canary Weight: 20% | Iterations: 2
# → 메트릭 정상, 20%로 증가
Status: Progressing | Canary Weight: 30% | Iterations: 3
# → 계속 증가...
# 4-1. 성공 시나리오
Status: Succeeded | Canary Weight: 0% | Iterations: 5
# → 100% 전환 완료, 구 버전 제거
# 4-2. 실패 시나리오 (에러율 증가 감지)
Status: Failed | Canary Weight: 0% | Iterations: 3
# → 자동 롤백, 구 버전 유지
장점:
- 위험 최소화
- 실사용자 피드백
- 자동 롤백
- A/B 테스트 가능
단점:
- 복잡한 설정
- 모니터링 인프라 필요
- 배포 시간 길어짐
GitOps with FluxCD
GitOps: 선언적 배포의 진화
GitOps는 Git을 단일 진실의 원천(Single Source of Truth)으로 사용하는 운영 모델입니다.
FluxCD 아키텍처와 동작 원리
# fluxcd-system/kustomization.yaml
apiVersion: kustomize.toolkit.fluxcd.io/v1
kind: Kustomization
metadata:
name: apps
namespace: flux-system
spec:
interval: 1m # Git 동기화 주기
path: ./apps # 모니터링할 경로
prune: true # Git에서 삭제되면 클러스터에서도 삭제
sourceRef:
kind: GitRepository
name: flux-system
validation: client # 적용 전 검증
healthChecks: # 배포 후 상태 확인
- apiVersion: apps/v1
kind: Deployment
name: my-app
namespace: default
FluxCD 핵심 컴포넌트:
- Source Controller: Git/Helm/OCI 저장소 모니터링
- Kustomize Controller: Kustomization 리소스 적용
- Helm Controller: Helm 차트 관리
- Notification Controller: 알림 전송
실전 구현 및 테스트
환경 구축: Minikube + FluxCD
#!/bin/bash
# scripts/setup-minikube.sh 핵심 부분
# 1. Minikube 시작 (충분한 리소스 할당)
minikube start \
--cpus=4 \
--memory=8192 \
--kubernetes-version=v1.28.0
# 2. 필수 애드온 활성화
minikube addons enable metrics-server # 메트릭 수집
minikube addons enable ingress # 인그레스 컨트롤러
# 3. FluxCD 설치
flux install \
--namespace=flux-system \
--network-policy=false \
--components=source-controller,kustomize-controller,helm-controller
실제 테스트 수행 및 결과 분석
테스트 1: 롤링 업데이트 성능 측정
# 부하 생성하며 업데이트
while true; do
curl -s http://localhost:8080/ > /dev/null
echo -n "."
done &
# 업데이트 실행
kubectl set image deployment/random-generator \
random-generator=k8spatterns/random-generator:2.0
# 결과: 0% 에러율, 평균 응답시간 50ms 유지
테스트 2: 블루-그린 전환 시간 측정
# 전환 시간 측정 스크립트
START=$(date +%s%N)
kubectl patch service random-generator \
-p '{"spec":{"selector":{"version":"green"}}}'
END=$(date +%s%N)
echo "전환 시간: $(( ($END - $START) / 1000000 ))ms"
# 결과: 평균 230ms (네트워크 지연 포함)
테스트 3: 카나리 배포 메트릭 분석
# Flagger 메트릭 확인
kubectl -n canary-demo get canary random-generator -o json | \
jq '.status.conditions'
# 결과:
[
{
"type": "Promoted",
"status": "True",
"reason": "Succeeded",
"message": "Canary analysis completed successfully"
}
]
비교 분석 및 의사결정 가이드
배포 전략 선택 기준
시나리오 | 추천 전략 | 이유 |
---|---|---|
일반 웹 애플리케이션 | 롤링 업데이트 | 간단하고 효율적 |
금융 거래 시스템 | 블루-그린 | 즉시 롤백 필요 |
ML 모델 배포 | 카나리 | A/B 테스트 및 점진적 검증 |
마이크로서비스 (100+) | 롤링 + 카나리 혼합 | 서비스별 중요도 차별화 |
스타트업 MVP | 롤링 업데이트 | 리소스 효율성 |
트러블슈팅
자주 발생하는 문제와 해결법
문제 1: ImagePullBackOff
# 진단
kubectl describe pod <pod-name>
# 해결
# 1. 이미지 태그 확인
# 2. 레지스트리 인증 설정
kubectl create secret docker-registry regcred \
--docker-server=<registry> \
--docker-username=<username> \
--docker-password=<password>
문제 2: 롤링 업데이트 중단
# 진단
kubectl rollout status deployment/<name>
# 해결
# 1. 헬스체크 타임아웃 조정
livenessProbe:
initialDelaySeconds: 60 # 증가
periodSeconds: 20 # 증가
# 2. 강제 재시작
kubectl rollout restart deployment/<name>
문제 3: FluxCD 동기화 실패
# 진단
flux get all
flux logs --level=error
# 해결
# 1. 수동 조정
flux reconcile source git flux-system
# 2. 캐시 삭제
kubectl -n flux-system delete pod -l app=source-controller
참고 자료
'k8s > kubernetes-pattern' 카테고리의 다른 글
Kubernetes Pattern: Managed Lifecycle (1) | 2025.08.23 |
---|---|
Kubernetes Pattern: Health Probe Pattern(정상상태 점검 패턴) (2) | 2025.08.16 |
Kubernetes Pattern: 예측 가능한 요구사항(Predictable Demands) (7) | 2025.08.02 |