프로덕션 운영 · Kubernetes 심화 가이드
Kubernetes 심화/실무 완전 가이드
HPA·VPA 오토스케일링, RBAC 보안, StatefulSet·DaemonSet·CronJob, Java AI Agent와 GPU 기반 LLM 추론 서버 배포, 업그레이드·장애 복구·Helm 운영까지 정리합니다.
- HPA·VPA
- RBAC
- StatefulSet
- DaemonSet
- CronJob
- GPU·LLM
Kubernetes 심화 웹 IDE 열기 →
프로덕션 Kubernetes는 단순히 Pod를 실행하는 수준을 넘어 확장 정책, 최소 권한, 상태 저장 데이터, 노드 단위 에이전트, 배치 실행, GPU 격리와 장애 복구까지 함께 설계해야 합니다.

1. HPA와 VPA 오토스케일링
HPA는 Pod 복제본 수를 조정하고 VPA는 컨테이너 resource request를 추천하거나 변경합니다. 같은 Deployment에 CPU·메모리 기준 HPA와 자동 적용 VPA를 동시에 사용하면 충돌할 수 있으므로, VPA는 먼저 updateMode: Off로 추천값만 수집하는 방식이 안전합니다.
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: myapp-hpa
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: myapp
minReplicas: 2
maxReplicas: 20
metrics:
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: 60
- type: Resource
resource:
name: memory
target:
type: AverageValue
averageValue: 400Mi
behavior:
scaleDown:
stabilizationWindowSeconds: 300
policies:
- type: Pods
value: 1
periodSeconds: 60
scaleUp:
policies:
- type: Percent
value: 100
periodSeconds: 15
vpa.yaml
apiVersion: autoscaling.k8s.io/v1
kind: VerticalPodAutoscaler
metadata:
name: myapp-vpa
spec:
targetRef:
apiVersion: apps/v1
kind: Deployment
name: myapp
updatePolicy:
updateMode: "Off"
resourcePolicy:
containerPolicies:
- containerName: "*"
minAllowed:
cpu: 50m
memory: 64Mi
maxAllowed:
cpu: "2"
memory: 4Gi
| 오토스케일러 | 조정 대상 | 주의점 |
|---|---|---|
| HPA | Pod 복제본 수 | resource request와 안정화 시간이 필요합니다. |
| VPA | requests와 limits | Auto 모드는 Pod 재시작을 일으킬 수 있습니다. |
| Cluster Autoscaler | Worker Node 수 | Pending Pod가 있어야 노드 증설이 트리거됩니다. |
2. RBAC와 보안
ServiceAccount는 Pod 내부 프로세스의 API 권한을, Role과 ClusterRole은 사용자·팀·CI/CD 시스템의 접근 범위를 관리합니다. 운영에서는 필요한 resource와 verb만 허용하는 최소 권한 정책을 적용해야 합니다.
team-rbac.yamlapiVersion: v1
kind: ServiceAccount
metadata:
name: dev-team-reader
namespace: production
---
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: read-only
namespace: production
rules:
- apiGroups: [""]
resources:
- pods
- services
- endpoints
- configmaps
- pods/log
verbs: ["get", "list", "watch"]
- apiGroups: ["apps"]
resources:
- deployments
- replicasets
- statefulsets
verbs: ["get", "list", "watch"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: dev-team-binding
namespace: production
subjects:
- kind: ServiceAccount
name: dev-team-reader
namespace: production
roleRef:
kind: Role
name: read-only
apiGroup: rbac.authorization.k8s.io
권한 확인
kubectl auth can-i get pods \
--as=system:serviceaccount:production:dev-team-reader \
-n production
kubectl auth can-i delete deployments \
--as=system:serviceaccount:production:dev-team-reader \
-n production
apiGroups: ["*"], resources: ["*"], verbs: ["*"] 조합은 사실상 관리자 권한입니다. CI/CD 시스템에도 Deployment 업데이트 등 필요한 권한만 제공하세요.3. StatefulSet과 상태 저장 워크로드
| 구분 | Deployment | StatefulSet |
|---|---|---|
| Pod 이름 | 랜덤 suffix | app-0, app-1처럼 순번 고정 |
| 생성·삭제 | 순서 없이 병렬 처리 | 기본적으로 순서 보장 |
| 네트워크 | Service IP 중심 | Headless Service 기반 고정 DNS |
| 스토리지 | 별도 PVC 구성 | volumeClaimTemplates로 Pod별 PVC 생성 |
| 주요 용도 | API, 웹, 일반 추론 서버 | DB, Kafka, Redis Cluster |
apiVersion: v1
kind: Service
metadata:
name: postgres-svc
spec:
clusterIP: None
selector:
app: postgres
ports:
- name: postgres
port: 5432
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: postgres
spec:
serviceName: postgres-svc
replicas: 3
selector:
matchLabels:
app: postgres
template:
metadata:
labels:
app: postgres
spec:
containers:
- name: postgres
image: postgres:16-alpine
ports:
- containerPort: 5432
env:
- name: POSTGRES_PASSWORD
valueFrom:
secretKeyRef:
name: postgres-secret
key: password
- name: PGDATA
value: /var/lib/postgresql/data/pgdata
resources:
requests:
cpu: 250m
memory: 512Mi
limits:
cpu: "1"
memory: 2Gi
volumeMounts:
- name: postgres-data
mountPath: /var/lib/postgresql/data
readinessProbe:
exec:
command: ["pg_isready", "-U", "postgres"]
periodSeconds: 10
volumeClaimTemplates:
- metadata:
name: postgres-data
spec:
accessModes: ["ReadWriteOnce"]
storageClassName: fast-ssd
resources:
requests:
storage: 10Gi
StatefulSet 운영 명령
kubectl get pods -l app=postgres -w
# Pod별 고정 DNS
kubectl exec -it postgres-0 -- \
psql -U postgres -c "SELECT inet_server_addr();"
# 스케일
kubectl scale statefulset postgres --replicas=5
kubectl scale statefulset postgres --replicas=3
# 롤링 업데이트
kubectl set image statefulset/postgres \
postgres=postgres:17-alpine
kubectl rollout status statefulset/postgres
# PVC는 Pod 삭제 후에도 유지
kubectl delete pod postgres-1
kubectl get pvc
4. 볼륨 확장과 스냅샷
StatefulSet의 volumeClaimTemplates는 생성 후 직접 수정하기 어렵습니다. 기존 PVC는 개별 patch로 확장하고, 신규 replica용 template 용량도 함께 맞춰야 합니다.
# StorageClass에 allowVolumeExpansion: true 필요
for i in 0 1 2; do
kubectl patch pvc postgres-data-postgres-$i \
-n database \
-p '{"spec":{"resources":{"requests":{"storage":"50Gi"}}}}'
done
kubectl get pvc -n database -w
VolumeSnapshot
apiVersion: snapshot.storage.k8s.io/v1
kind: VolumeSnapshotClass
metadata:
name: csi-snapclass
driver: ebs.csi.aws.com
deletionPolicy: Retain
---
apiVersion: snapshot.storage.k8s.io/v1
kind: VolumeSnapshot
metadata:
name: postgres-before-upgrade
namespace: database
spec:
volumeSnapshotClassName: csi-snapclass
source:
persistentVolumeClaimName: postgres-data-postgres-0
5. DaemonSet
DaemonSet은 대상 노드마다 정확히 하나의 Pod를 배치합니다. 로그 수집기, Node Exporter, CNI, CSI, NVIDIA device plugin처럼 노드 기능을 제공하는 에이전트에 적합합니다.
fluent-bit-daemonset.yamlapiVersion: apps/v1
kind: DaemonSet
metadata:
name: fluent-bit
namespace: logging
spec:
selector:
matchLabels:
app: fluent-bit
template:
metadata:
labels:
app: fluent-bit
spec:
tolerations:
- key: node-role.kubernetes.io/control-plane
effect: NoSchedule
containers:
- name: fluent-bit
image: fluent/fluent-bit:3.0
resources:
requests:
cpu: 100m
memory: 128Mi
limits:
cpu: 300m
memory: 256Mi
volumeMounts:
- name: varlog
mountPath: /var/log
readOnly: true
volumes:
- name: varlog
hostPath:
path: /var/log
DaemonSet 운영
kubectl get daemonset fluent-bit -n logging
kubectl get pods -n logging \
-l app=fluent-bit \
-o wide
kubectl set image daemonset/fluent-bit \
fluent-bit=fluent/fluent-bit:3.1 \
-n logging
kubectl rollout status daemonset/fluent-bit -n logging
6. Job과 CronJob
Job은 완료가 필요한 일회성 작업을, CronJob은 정해진 시간마다 Job을 생성하는 배치 작업을 담당합니다. 재시도 횟수, 최대 실행 시간과 동시 실행 정책을 반드시 제한해야 합니다.
db-migration-job.yamlapiVersion: batch/v1
kind: Job
metadata:
name: db-migration-v2
namespace: production
spec:
backoffLimit: 3
activeDeadlineSeconds: 600
ttlSecondsAfterFinished: 3600
template:
spec:
restartPolicy: Never
containers:
- name: migrate
image: myapp-migrator:2.1.0
command:
- ./migrate.sh
- --target=v2
envFrom:
- secretRef:
name: db-credentials
nightly-backup-cronjob.yaml
apiVersion: batch/v1
kind: CronJob
metadata:
name: postgres-nightly-backup
namespace: database
spec:
schedule: "0 3 * * *"
timeZone: "Asia/Seoul"
concurrencyPolicy: Forbid
startingDeadlineSeconds: 300
successfulJobsHistoryLimit: 3
failedJobsHistoryLimit: 3
jobTemplate:
spec:
backoffLimit: 2
template:
spec:
restartPolicy: Never
containers:
- name: backup
image: postgres:16-alpine
command:
- /bin/sh
- -c
- pg_dump -h postgres-svc -U postgres appdb > /backup/appdb.sql
7. Spring Boot Java AI Agent StatefulSet 배포
상태와 벡터 인덱스를 로컬에 유지하는 Java AI Agent는 상태 저장 PVC와 감사 로그 PVC를 분리해 관리할 수 있습니다. 외부 DB와 벡터 DB를 사용하는 구조라면 Deployment가 더 적합할 수도 있습니다.
DockerfileFROM eclipse-temurin:21-jdk-alpine AS builder
WORKDIR /app
COPY gradlew build.gradle.kts settings.gradle.kts ./
COPY gradle ./gradle
COPY src ./src
RUN ./gradlew bootJar --no-daemon -q
FROM eclipse-temurin:21-jre-alpine
WORKDIR /app
RUN mkdir -p /app/data /app/logs && \
addgroup -S agent && \
adduser -S agent -G agent && \
chown -R agent:agent /app
COPY --from=builder /app/build/libs/*.jar agent.jar
USER agent
EXPOSE 8080
ENTRYPOINT [
"java",
"-XX:MaxRAMPercentage=75.0",
"-XX:+UseG1GC",
"-Dspring.profiles.active=prod",
"-Dlogging.file.path=/app/logs",
"-jar",
"agent.jar"
]
StatefulSet 핵심 구조
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: java-agent
namespace: ai-agent
spec:
serviceName: java-agent-headless
replicas: 2
selector:
matchLabels:
app: java-agent
template:
metadata:
labels:
app: java-agent
spec:
containers:
- name: java-agent
image: registry.example.com/java-agent:1.0.0
ports:
- name: http
containerPort: 8080
envFrom:
- secretRef:
name: java-agent-secret
volumeMounts:
- name: agent-state
mountPath: /app/data
- name: agent-logs
mountPath: /app/logs
readinessProbe:
httpGet:
path: /actuator/health/readiness
port: 8080
volumeClaimTemplates:
- metadata:
name: agent-state
spec:
accessModes: ["ReadWriteOnce"]
storageClassName: fast-ssd
resources:
requests:
storage: 20Gi
- metadata:
name: agent-logs
spec:
accessModes: ["ReadWriteOnce"]
storageClassName: nfs-client
resources:
requests:
storage: 10Gi
8. GPU 워크로드와 LLM 추론 서버
NVIDIA device plugin이 GPU를 nvidia.com/gpu 리소스로 노출하면 GPU Pod가 정수 단위로 GPU를 요청할 수 있습니다. GPU 노드에는 taint를 적용하고 GPU Pod에만 toleration을 제공해 일반 워크로드를 격리합니다.
kubectl create -f \
https://raw.githubusercontent.com/NVIDIA/k8s-device-plugin/main/deployments/static/nvidia-device-plugin.yml
kubectl taint nodes gpu-node-1 \
nvidia.com/gpu=present:NoSchedule
kubectl label node gpu-node-1 gpu-type=a10g
llm-inference.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: llm-inference
namespace: ai-agent
spec:
replicas: 2
selector:
matchLabels:
app: llm-inference
template:
metadata:
labels:
app: llm-inference
spec:
nodeSelector:
gpu-type: a10g
tolerations:
- key: nvidia.com/gpu
operator: Equal
value: present
effect: NoSchedule
containers:
- name: vllm
image: vllm/vllm-openai:latest
args:
- --model
- /models/llama-3-8b
- --gpu-memory-utilization
- "0.9"
resources:
requests:
cpu: "4"
memory: 24Gi
nvidia.com/gpu: "1"
limits:
cpu: "8"
memory: 32Gi
nvidia.com/gpu: "1"
readinessProbe:
httpGet:
path: /health
port: 8000
initialDelaySeconds: 120
periodSeconds: 10
9. 운영과 노드 유지보수
kubectl 운영 치트시트kubectl get pods -n production -o wide
kubectl top nodes
kubectl top pods -n production --sort-by=cpu
kubectl logs <pod> --previous -n production
kubectl describe pod <pod> -n production
kubectl debug <pod> -it --image=busybox
kubectl get events -n production \
--sort-by='.lastTimestamp'
kubectl rollout restart deployment/myapp \
-n production
노드 유지보수
# 새 Pod 스케줄 차단
kubectl cordon k8s-worker-01
# 기존 Pod 이동
kubectl drain k8s-worker-01 \
--ignore-daemonsets \
--delete-emptydir-data \
--grace-period=30 \
--timeout=300s
# 유지보수 후 복귀
kubectl uncordon k8s-worker-01
10. etcd 백업과 복구
etcd는 Kubernetes 클러스터의 모든 상태를 저장합니다. 백업 파일 생성뿐 아니라 스냅샷 검증과 실제 복구 훈련까지 수행해야 합니다.
etcd 백업BACKUP_DIR=/backup/etcd/$(date +%Y%m%d-%H%M%S)
mkdir -p "$BACKUP_DIR"
ETCDCTL_API=3 etcdctl snapshot save \
"$BACKUP_DIR/snapshot.db" \
--endpoints=https://127.0.0.1:2379 \
--cacert=/etc/kubernetes/pki/etcd/ca.crt \
--cert=/etc/kubernetes/pki/etcd/server.crt \
--key=/etc/kubernetes/pki/etcd/server.key
ETCDCTL_API=3 etcdctl snapshot status \
"$BACKUP_DIR/snapshot.db" \
--write-out=table
11. 장애 진단 패턴
| 상태 | 우선 확인 | 대표 원인 |
|---|---|---|
| Pending | describe Pod와 Events | 자원 부족, taint, PVC, affinity 조건 |
| CrashLoopBackOff | 이전 로그와 종료 코드 | 애플리케이션 오류, OOM, 잘못된 설정 |
| ImagePullBackOff | image 이름과 imagePullSecret | 레지스트리 인증, tag 오류 |
| Service 연결 실패 | selector와 EndpointSlice | label 불일치, readiness 실패 |
| Ingress 404·502 | Ingress, Service, Endpoint | path, port, backend 또는 controller 문제 |
kubectl describe pod <pod> -n <namespace>
kubectl logs <pod> --previous -n <namespace>
kubectl get service,endpoints,endpointslice \
-n <namespace>
kubectl get pvc,pv,storageclass
kubectl get events -A --sort-by='.lastTimestamp'
12. Helm 차트 운영
심화 환경에서는 Helm Chart를 배포 패키지로 사용하고 values 파일, Secret, resource, autoscaling과 workload 유형을 환경별로 분리합니다.
Helm 검증과 배포helm lint ./chart
helm template myapp ./chart \
-f values-prod.yaml \
> rendered.yaml
helm upgrade --install myapp ./chart \
-f values-prod.yaml \
--namespace production \
--create-namespace \
--atomic \
--timeout 10m
helm history myapp -n production
helm rollback myapp 2 -n production
마무리
Kubernetes 심화 운영에서는 오토스케일링, RBAC, StatefulSet과 GPU 설정을 개별 기능으로만 이해해서는 부족합니다. 서비스 가용성, 데이터 보존, 배포·업그레이드와 장애 복구 목표를 먼저 정의하고 각 리소스를 하나의 운영 체계로 연결해야 합니다.
- HPA, VPA와 Cluster Autoscaler의 책임을 분리합니다.
- RBAC과 ServiceAccount는 최소 권한으로 구성합니다.
- 상태 저장 서비스는 PVC, PDB, backup과 복구 절차를 함께 준비합니다.
- DaemonSet과 CronJob은 노드·배치 특성에 맞는 제한값을 설정합니다.
- GPU Pod는 전용 노드와 taint·toleration으로 격리합니다.
- etcd 백업과 노드 drain 절차는 실제 복구 훈련으로 검증합니다.
'개발 가이드 > Cloud Devops' 카테고리의 다른 글
| [Cloud Devops] Kubernetes 완전 가이드 (0) | 2026.07.19 |
|---|
댓글