본문 바로가기
개발 가이드/Cloud Devops

[Cloud Devops] Kubernetes 심화/실무 완전 가이드

by 플로거 2026. 7. 19.

프로덕션 운영 · Kubernetes 심화 가이드

Kubernetes 심화/실무 완전 가이드

HPA·VPA 오토스케일링, RBAC 보안, StatefulSet·DaemonSet·CronJob, Java AI Agent와 GPU 기반 LLM 추론 서버 배포, 업그레이드·장애 복구·Helm 운영까지 정리합니다.

  • HPA·VPA
  • RBAC
  • StatefulSet
  • DaemonSet
  • CronJob
  • GPU·LLM
설치 없이 Kubernetes 심화 예제를 실습하세요 브라우저에서 단계별 매니페스트와 명령을 실행할 수 있는 AI DevOps Learn Kubernetes 심화 웹 IDE를 제공합니다.
Kubernetes 심화 웹 IDE 열기 →
이 글의 핵심
프로덕션 Kubernetes는 단순히 Pod를 실행하는 수준을 넘어 확장 정책, 최소 권한, 상태 저장 데이터, 노드 단위 에이전트, 배치 실행, GPU 격리와 장애 복구까지 함께 설계해야 합니다.

1. HPA와 VPA 오토스케일링

HPA는 Pod 복제본 수를 조정하고 VPA는 컨테이너 resource request를 추천하거나 변경합니다. 같은 Deployment에 CPU·메모리 기준 HPA와 자동 적용 VPA를 동시에 사용하면 충돌할 수 있으므로, VPA는 먼저 updateMode: Off로 추천값만 수집하는 방식이 안전합니다.

hpa.yaml
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.yaml
apiVersion: 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
postgres-statefulset.yaml
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 용량도 함께 맞춰야 합니다.

PVC 온라인 확장
# 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.yaml
apiVersion: 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.yaml
apiVersion: 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가 더 적합할 수도 있습니다.

Dockerfile
FROM 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을 제공해 일반 워크로드를 격리합니다.

GPU 노드 준비
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
LLM 추론 서버는 CPU 사용률보다 queue 길이, 동시 요청 수와 token 처리량이 확장 기준으로 더 적합할 수 있습니다. 모델 로딩이 끝나기 전 트래픽이 유입되지 않도록 readiness 시간을 충분히 설정하세요.

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
etcd 복구는 Control Plane 구성과 클러스터 토폴로지에 따라 절차가 달라집니다. 운영 환경에서 즉시 실행하지 말고 별도의 복구 클러스터에서 절차와 RTO를 먼저 검증하세요.

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 절차는 실제 복구 훈련으로 검증합니다.

원문: AI DevOps Korea Kubernetes 심화/실무 가이드

반응형

'개발 가이드 > Cloud Devops' 카테고리의 다른 글

[Cloud Devops] Kubernetes 완전 가이드  (0) 2026.07.19

댓글