들어가며
이전 글에서 Kubernetes의 핵심 개념인 Pod, Service, Deployment를 살펴봤습니다. 하지만 실제 프로덕션 환경에서 K8s를 운영하려면 더 많은 도구와 개념이 필요합니다.
이 글에서는 패키지 매니저 Helm, HTTP 라우팅을 담당하는 Ingress, 트래픽에 따라 자동으로 스케일링하는 HPA, 그리고 안정적인 운영을 위한 Probe, Resource 관리, PDB 등을 Spring Boot 애플리케이션 배포 예제와 함께 다룹니다.
Helm - Kubernetes 패키지 매니저
Helm은 Kubernetes 애플리케이션의 패키징, 배포, 버전 관리를 위한 도구입니다. 여러 YAML 매니페스트를 하나의 Chart로 묶어 재사용 가능하게 만듭니다.
Helm Chart 디렉토리 구조
my-spring-app/
├── Chart.yaml # 차트 메타데이터
├── values.yaml # 기본 설정값
├── values-dev.yaml # 개발 환경 오버라이드
├── values-prod.yaml # 운영 환경 오버라이드
├── templates/
│ ├── _helpers.tpl # 템플릿 헬퍼 함수
│ ├── deployment.yaml
│ ├── service.yaml
│ ├── ingress.yaml
│ ├── configmap.yaml
│ ├── secret.yaml
│ ├── hpa.yaml
│ └── NOTES.txt # 설치 후 안내 메시지
└── charts/ # 의존성 차트
Chart.yaml
apiVersion: v2
name: my-spring-app
description: A Helm chart for Spring Boot application
type: application
version: 1.0.0 # Chart 버전
appVersion: "2.0.0" # 애플리케이션 버전
values.yaml - 기본 설정값
# values.yaml
replicaCount: 2
image:
repository: myregistry/spring-boot-app
tag: "2.0.0"
pullPolicy: IfNotPresent
service:
type: ClusterIP
port: 80
targetPort: 8080
ingress:
enabled: true
className: nginx
hosts:
- host: api.example.com
paths:
- path: /
pathType: Prefix
tls:
- secretName: api-tls
hosts:
- api.example.com
resources:
requests:
memory: "512Mi"
cpu: "500m"
limits:
memory: "1Gi"
cpu: "1000m"
autoscaling:
enabled: true
minReplicas: 2
maxReplicas: 10
targetCPUUtilizationPercentage: 70
spring:
profiles: production
datasource:
url: jdbc:mysql://db:3306/mydb
templates/deployment.yaml - 템플릿
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ include "my-spring-app.fullname" . }}
labels:
{{- include "my-spring-app.labels" . | nindent 4 }}
spec:
{{- if not .Values.autoscaling.enabled }}
replicas: {{ .Values.replicaCount }}
{{- end }}
selector:
matchLabels:
{{- include "my-spring-app.selectorLabels" . | nindent 6 }}
template:
metadata:
labels:
{{- include "my-spring-app.selectorLabels" . | nindent 8 }}
spec:
containers:
- name: {{ .Chart.Name }}
image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}"
imagePullPolicy: {{ .Values.image.pullPolicy }}
ports:
- containerPort: {{ .Values.service.targetPort }}
env:
- name: SPRING_PROFILES_ACTIVE
value: {{ .Values.spring.profiles | quote }}
resources:
{{- toYaml .Values.resources | nindent 12 }}
livenessProbe:
httpGet:
path: /actuator/health/liveness
port: {{ .Values.service.targetPort }}
initialDelaySeconds: 60
periodSeconds: 10
readinessProbe:
httpGet:
path: /actuator/health/readiness
port: {{ .Values.service.targetPort }}
initialDelaySeconds: 20
periodSeconds: 5
Helm 핵심 명령어
# 차트 설치
helm install my-app ./my-spring-app
# 환경별 values 파일 지정
helm install my-app ./my-spring-app -f values-prod.yaml
# 업그레이드
helm upgrade my-app ./my-spring-app -f values-prod.yaml
# 설치 또는 업그레이드 (멱등성)
helm upgrade --install my-app ./my-spring-app -f values-prod.yaml
# 릴리스 목록 조회
helm list
# 롤백
helm rollback my-app 1 # revision 1로 롤백
# 릴리스 삭제
helm uninstall my-app
# 템플릿 렌더링 결과 미리 확인
helm template my-app ./my-spring-app -f values-prod.yaml
Ingress - HTTP 라우팅과 TLS
Ingress는 클러스터 외부에서 내부 서비스로의 HTTP/HTTPS 라우팅 규칙을 정의합니다. 하나의 로드밸런서로 여러 서비스에 도메인/경로 기반 라우팅이 가능합니다.
Nginx Ingress Controller 설치
# Helm으로 Nginx Ingress Controller 설치
helm repo add ingress-nginx https://kubernetes.github.io/ingress-nginx
helm repo update
helm install ingress-nginx ingress-nginx/ingress-nginx \
--namespace ingress-nginx \
--create-namespace \
--set controller.replicaCount=2
Ingress 리소스 정의
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: backend-ingress
annotations:
nginx.ingress.kubernetes.io/rewrite-target: /
nginx.ingress.kubernetes.io/ssl-redirect: "true"
nginx.ingress.kubernetes.io/proxy-body-size: "10m"
nginx.ingress.kubernetes.io/rate-limit-connections: "5"
cert-manager.io/cluster-issuer: "letsencrypt-prod"
spec:
ingressClassName: nginx
tls:
- hosts:
- api.example.com
secretName: api-tls-secret
rules:
- host: api.example.com
http:
paths:
- path: /api/v1/users
pathType: Prefix
backend:
service:
name: user-service
port:
number: 80
- path: /api/v1/orders
pathType: Prefix
backend:
service:
name: order-service
port:
number: 80
- path: /api/v1/products
pathType: Prefix
backend:
service:
name: product-service
port:
number: 80
TLS 인증서 자동 발급 (cert-manager)
# cert-manager 설치
helm repo add jetstack https://charts.jetstack.io
helm install cert-manager jetstack/cert-manager \
--namespace cert-manager \
--create-namespace \
--set installCRDs=true
# ClusterIssuer 정의 (Let's Encrypt)
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
name: letsencrypt-prod
spec:
acme:
server: https://acme-v02.api.letsencrypt.org/directory
email: admin@example.com
privateKeySecretRef:
name: letsencrypt-prod-key
solvers:
- http01:
ingress:
class: nginx
HPA - Horizontal Pod Autoscaler
HPA는 CPU, 메모리 사용량 또는 커스텀 메트릭을 기반으로 Pod 수를 자동 조절합니다.
HPA 설정
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: spring-app-hpa
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: spring-app
minReplicas: 2
maxReplicas: 10
metrics:
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: 70
- type: Resource
resource:
name: memory
target:
type: Utilization
averageUtilization: 80
behavior:
scaleUp:
stabilizationWindowSeconds: 30
policies:
- type: Pods
value: 2
periodSeconds: 60 # 60초마다 최대 2개씩 증가
scaleDown:
stabilizationWindowSeconds: 300 # 5분 안정화 대기
policies:
- type: Percent
value: 10
periodSeconds: 60 # 60초마다 최대 10%씩 감소
HPA 동작 확인
# HPA 상태 확인
kubectl get hpa
kubectl describe hpa spring-app-hpa
# 부하 테스트로 HPA 동작 확인
kubectl run load-test --image=busybox --rm -it -- \
/bin/sh -c "while true; do wget -q -O- http://spring-app-service; done"
VPA - Vertical Pod Autoscaler
HPA가 Pod 수를 조절한다면, VPA는 개별 Pod의 리소스(CPU/메모리) 요청량을 자동 조절합니다.
apiVersion: autoscaling.k8s.io/v1
kind: VerticalPodAutoscaler
metadata:
name: spring-app-vpa
spec:
targetRef:
apiVersion: apps/v1
kind: Deployment
name: spring-app
updatePolicy:
updateMode: "Auto" # Off, Initial, Recreate, Auto
resourcePolicy:
containerPolicies:
- containerName: app
minAllowed:
cpu: 250m
memory: 256Mi
maxAllowed:
cpu: 2000m
memory: 2Gi
Resource Requests와 Limits
적절한 리소스 설정은 안정적인 K8s 운영의 핵심입니다.
Spring Boot 애플리케이션 권장 설정
resources:
requests:
memory: "512Mi" # JVM 힙 + 메타스페이스 + 여유
cpu: "500m" # 0.5 vCPU
limits:
memory: "1Gi" # OOMKilled 방지를 위한 여유
cpu: "1000m" # 1 vCPU - CPU throttling 주의
주의사항:
- Memory Limit: JVM의 -Xmx보다 충분히 크게 설정 (메타스페이스, 스레드 스택 고려)
- CPU Limit: 너무 낮으면 CPU throttling으로 응답 지연 발생
- Request = Limit으로 설정하면 QoS Class가 Guaranteed가 되어 OOM 시 마지막에 종료됨
QoS Class
| QoS Class | 조건 | OOM 우선순위 |
|---|---|---|
| Guaranteed | 모든 컨테이너의 requests = limits | 가장 마지막에 종료 |
| Burstable | requests < limits | 중간 |
| BestEffort | requests/limits 미설정 | 가장 먼저 종료 |
Liveness / Readiness / Startup Probe
Probe는 컨테이너의 상태를 주기적으로 확인하여 장애 상황에 자동 대응합니다.
Probe 종류 비교
| Probe | 목적 | 실패 시 동작 |
|---|---|---|
| Liveness | 컨테이너가 살아있는지 확인 | 컨테이너 재시작 |
| Readiness | 트래픽 수신 준비가 되었는지 확인 | Service 엔드포인트에서 제외 |
| Startup | 애플리케이션 초기 시작 완료 확인 | 시작 실패 시 컨테이너 재시작 |
Spring Boot Actuator 연동
# application.yml
management:
endpoints:
web:
exposure:
include: health,info,prometheus
endpoint:
health:
probes:
enabled: true
group:
liveness:
include: livenessState
readiness:
include: readinessState, db, redis
health:
livenessState:
enabled: true
readinessState:
enabled: true
# Kubernetes Probe 설정
startupProbe:
httpGet:
path: /actuator/health/liveness
port: 8080
failureThreshold: 30
periodSeconds: 10 # 최대 300초(5분) 대기
livenessProbe:
httpGet:
path: /actuator/health/liveness
port: 8080
periodSeconds: 10
failureThreshold: 3
readinessProbe:
httpGet:
path: /actuator/health/readiness
port: 8080
periodSeconds: 5
failureThreshold: 3
PDB - Pod Disruption Budget
PDB는 노드 드레인이나 클러스터 업그레이드 시 최소한의 Pod 가용성을 보장합니다.
apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
name: spring-app-pdb
spec:
minAvailable: 2 # 최소 2개 Pod 유지
# 또는 maxUnavailable: 1 # 최대 1개까지만 중단 허용
selector:
matchLabels:
app: spring-app
실전 종합 예제 - Spring Boot 앱 프로덕션 배포
지금까지 배운 모든 개념을 적용한 Helm values 파일입니다.
# values-prod.yaml
replicaCount: 3
image:
repository: myregistry/spring-boot-app
tag: "2.0.0"
service:
type: ClusterIP
port: 80
targetPort: 8080
ingress:
enabled: true
className: nginx
annotations:
cert-manager.io/cluster-issuer: letsencrypt-prod
nginx.ingress.kubernetes.io/ssl-redirect: "true"
nginx.ingress.kubernetes.io/proxy-read-timeout: "60"
hosts:
- host: api.myservice.com
paths:
- path: /
pathType: Prefix
tls:
- secretName: api-tls
hosts:
- api.myservice.com
resources:
requests:
memory: "512Mi"
cpu: "500m"
limits:
memory: "1Gi"
cpu: "1000m"
autoscaling:
enabled: true
minReplicas: 3
maxReplicas: 20
targetCPUUtilization: 70
targetMemoryUtilization: 80
behavior:
scaleUp:
stabilizationWindowSeconds: 30
scaleDown:
stabilizationWindowSeconds: 300
pdb:
enabled: true
minAvailable: 2
spring:
profiles: production
jvmOpts: "-Xms384m -Xmx384m -XX:+UseG1GC"
probes:
startup:
enabled: true
failureThreshold: 30
periodSeconds: 10
liveness:
enabled: true
periodSeconds: 10
failureThreshold: 3
readiness:
enabled: true
periodSeconds: 5
failureThreshold: 3
# 프로덕션 배포
helm upgrade --install my-app ./my-spring-app \
-f values-prod.yaml \
-n production \
--create-namespace \
--wait \
--timeout 5m
마치며
이번 글에서는 Kubernetes 실전 운영에 필요한 고급 도구와 개념을 다뤘습니다.
핵심 정리:
- Helm으로 K8s 매니페스트를 패키징하고 환경별 설정을 분리합니다
- Ingress로 도메인 기반 HTTP 라우팅과 TLS 종료를 처리합니다
- HPA/VPA로 트래픽 변화에 자동 대응하는 오토스케일링을 구성합니다
- Probe 설정으로 애플리케이션 장애를 자동 감지하고 복구합니다
- Resource 관리와 PDB로 안정적인 서비스 가용성을 보장합니다
Kubernetes는 학습 곡선이 가파르지만, 이러한 도구들을 하나씩 적용하다 보면 자연스럽게 안정적인 프로덕션 환경을 구축할 수 있습니다. Spring Boot Actuator와의 연동은 특히 백엔드 개발자에게 실무적으로 가장 중요한 부분이니 꼭 직접 구성해보시기 바랍니다.
'DevOps' 카테고리의 다른 글
| Prometheus + Grafana 실전 구축 - Spring Boot 모니터링 완벽 가이드 (2) | 2026.04.09 |
|---|---|
| Terraform으로 인프라 코드화 - AWS 실전 예제로 배우는 IaC (0) | 2026.04.07 |
| Kubernetes 입문 - Pod, Service, Deployment 핵심 개념 총정리 (0) | 2026.04.06 |
| Terraform으로 NCP(네이버 클라우드) 인프라 구축하기 - VPC부터 서버 배포까지 (0) | 2026.04.03 |
| GitHub Actions CI/CD 파이프라인 구축 - Spring Boot 자동 배포 (0) | 2026.03.31 |