면접 대비(16/23)
DevOps

K8s 운영 심화 — 면접 대비 정리

HPA, PodDisruptionBudget, Resource Request/Limit, 헬스체크, 롤링 업데이트 전략, 실무 운영 경험까지. K8s를 실제로 운영한 관점에서 정리한다.

2026-04-02
8 min read
#Kubernetes#K8s#HPA#운영#스케일링#배포

K8s 핵심 오브젝트 복습

Cluster
└── Node (물리/가상 서버)
    └── Pod (컨테이너 실행 단위)
        └── Container

Deployment → ReplicaSet → Pod 관리
Service    → Pod 그룹에 안정적인 네트워크 엔드포인트
Ingress    → 외부 HTTP 트래픽 라우팅
ConfigMap  → 설정값 저장
Secret     → 민감 정보 저장

Resource Request / Limit

containers:
- name: app
  resources:
    requests:
      cpu: "250m"      # 0.25 코어 보장
      memory: "256Mi"  # 256MB 보장
    limits:
      cpu: "500m"      # 최대 0.5 코어
      memory: "512Mi"  # 최대 512MB

Request: 스케줄링 기준. 노드에 이 만큼의 여유가 있어야 Pod가 배치된다.

Limit: 초과 방지. CPU는 초과 시 스로틀링, 메모리는 초과 시 OOMKill.

Request가 없으면: 스케줄러가 자원 계획 없이 아무 노드에 배치 → 한 노드에 몰릴 수 있음.

Limit이 없으면: 하나의 Pod가 노드 자원을 전부 먹어 다른 Pod에 영향.

실무 권장: Request = Limit으로 설정 (Guaranteed QoS). 예측 가능한 성능.


HPA (Horizontal Pod Autoscaler)

CPU/메모리 사용률에 따라 Pod 수를 자동으로 조절한다.

apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
  name: app-hpa
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: app
  minReplicas: 2
  maxReplicas: 10
  metrics:
  - type: Resource
    resource:
      name: cpu
      target:
        type: Utilization
        averageUtilization: 70   # CPU 70% 초과 시 스케일 아웃
  - type: Resource
    resource:
      name: memory
      target:
        type: Utilization
        averageUtilization: 80

주의사항:

  • HPA가 동작하려면 resources.requests가 반드시 설정되어야 한다.
  • 스케일 아웃은 빠르게, 스케일 인은 천천히 (기본 5분 안정화 기간).
  • Spring Boot 같이 시작 시간이 긴 앱은 startupProbe를 추가해 준비 전에 트래픽이 안 들어오게.

헬스체크 3종류

containers:
- name: app
  # 시작 완료 여부 (시작 중에는 다른 프로브 비활성화)
  startupProbe:
    httpGet:
      path: /actuator/health
      port: 8080
    failureThreshold: 30    # 30번 실패하면 재시작 (30 * periodSeconds)
    periodSeconds: 10       # 최대 300초 대기

  # 트래픽 받을 준비가 됐는지
  readinessProbe:
    httpGet:
      path: /actuator/health/readiness
      port: 8080
    initialDelaySeconds: 0
    periodSeconds: 5
    failureThreshold: 3     # 3번 실패하면 Service 엔드포인트에서 제외

  # 컨테이너가 살아있는지
  livenessProbe:
    httpGet:
      path: /actuator/health/liveness
      port: 8080
    initialDelaySeconds: 30
    periodSeconds: 10
    failureThreshold: 3     # 3번 실패하면 컨테이너 재시작

Spring Boot Actuator:

management:
  endpoint:
    health:
      probes:
        enabled: true  # /actuator/health/readiness, /actuator/health/liveness 활성화
  health:
    livenessstate:
      enabled: true
    readinessstate:
      enabled: true

PodDisruptionBudget (PDB)

노드 유지보수, kubectl drain 같은 자발적 중단(Voluntary Disruption) 시 최소 가용 Pod 수를 보장한다.

apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
  name: app-pdb
spec:
  minAvailable: 2       # 항상 최소 2개 유지
  # 또는
  maxUnavailable: 1     # 최대 1개까지 동시에 중단 허용
  selector:
    matchLabels:
      app: my-app

노드 업그레이드 시 PDB가 없으면 모든 Pod가 동시에 제거될 수 있다. PDB를 설정하면 순차적으로 제거되어 서비스가 유지된다.


배포 전략 상세

spec:
  strategy:
    type: RollingUpdate
    rollingUpdate:
      maxSurge: 1          # 최대 1개 추가 생성 (기존 + 1)
      maxUnavailable: 0    # 항상 desired 수 유지 (무중단)

무중단 배포 조건:

  1. maxUnavailable: 0 설정
  2. readinessProbe 설정 (준비 완료 전 트래픽 차단)
  3. 충분한 terminationGracePeriodSeconds (기본 30초)
spec:
  template:
    spec:
      terminationGracePeriodSeconds: 60  # 60초 안에 정상 종료
      containers:
      - name: app
        lifecycle:
          preStop:
            exec:
              command: ["/bin/sh", "-c", "sleep 5"]
              # Service 엔드포인트에서 제거되기 전 5초 대기
              # 이 시간 동안 새 요청은 안 들어옴

ConfigMap & Secret

# ConfigMap: 일반 설정
apiVersion: v1
kind: ConfigMap
metadata:
  name: app-config
data:
  APP_ENV: "production"
  LOG_LEVEL: "INFO"
  DB_MAX_POOL_SIZE: "10"
# Secret: 민감 정보 (Base64 인코딩)
apiVersion: v1
kind: Secret
metadata:
  name: app-secret
type: Opaque
data:
  DB_PASSWORD: cGFzc3dvcmQxMjM=   # base64("password123")
  JWT_SECRET: c2VjcmV0a2V5MTIz
# Pod에서 사용
spec:
  containers:
  - name: app
    envFrom:
    - configMapRef:
        name: app-config
    - secretRef:
        name: app-secret

실무에서는 Secret을 YAML 파일에 직접 저장하지 않는다. Helm + .Values, ExternalSecrets Operator (AWS Secrets Manager 연동), Vault 등을 사용한다.


Namespace로 환경 분리

namespace: dev      (개발)
namespace: staging  (스테이징)
namespace: prod     (운영)
kubectl get pods -n prod
kubectl apply -f deployment.yaml -n prod

ResourceQuota로 namespace별 자원 제한:

apiVersion: v1
kind: ResourceQuota
metadata:
  name: dev-quota
  namespace: dev
spec:
  hard:
    requests.cpu: "4"
    requests.memory: 4Gi
    limits.cpu: "8"
    limits.memory: 8Gi
    pods: "20"

실무 운영 경험: 레플리카 10개+ 운영

기존에는 Pod를 고정 수로 운영했다. 트래픽이 적은 새벽에도 10개가 떠있었다.

개선 내용:

1. HPA 도입

minReplicas: 2   # 최소 2개 (새벽)
maxReplicas: 12  # 최대 12개 (트래픽 피크)

피크 타임에는 자동으로 늘고, 새벽에는 2개로 줄어 인프라 비용 60% 절감.

2. readinessProbe + PDB

배포 중 트래픽 끊김 없도록 readinessProbe로 준비 완료 확인, PDB로 최소 2개 유지.

3. Resource Limit 설정

OOMKill이 간헐적으로 발생하던 문제를 Memory Limit을 적절히 올리고 Request를 맞춰 해결.


면접에서 자주 나오는 질문

Q. HPA가 동작하지 않을 때 원인은?

  1. resources.requests가 설정되지 않음 (HPA는 request 기준으로 사용률 계산).
  2. Metrics Server가 설치되지 않음.
  3. minReplicas와 현재 Pod 수가 이미 동일.

Q. readinessProbe와 livenessProbe를 다르게 설정하는 이유는?

readinessProbe 실패 시 Service에서 제외되고, 자연히 로드밸런서 대상에서도 제외된다. 컨테이너는 살아있지만 트래픽만 안 받는다. livenessProbe 실패 시 컨테이너가 재시작된다. readinessProbe를 먼저 엄격하게 설정하면 livenessProbe가 불필요하게 재시작하는 것을 방지한다.

Q. Pod가 Pending 상태일 때 원인은?

  1. 요청한 CPU/메모리를 가진 노드가 없음.
  2. 노드에 taint가 있고 Pod에 toleration이 없음.
  3. PVC가 바인딩되지 않음.
  4. 이미지 Pull 실패.

kubectl describe pod <name>으로 Events 섹션에서 원인을 확인한다.

Q. 무중단 배포를 위해 반드시 필요한 설정은?

readinessProbe (준비 완료 전 트래픽 차단), maxUnavailable: 0 (항상 desired 수 유지), preStop hook 또는 적절한 graceful shutdown (진행 중인 요청 처리 완료), 충분한 terminationGracePeriodSeconds.