K8s 실전 #6 운영 체크리스트 — 업그레이드 / 백업,복구 / 비용 / 보안

12 분 소요

K8s 실전 시리즈의 마지막 글입니다. #1~#5까지 myshop-api를 처음부터 띄우고, 배포,DB,CI/CD,모니터링까지 한 흐름을 자동화했습니다. 이 시점에서 클러스터는 잘 굴러가는 상태이지만, 한 해 단위로 안전하게 운영하는 일은 다른 결의 작업입니다. K8s는 1년에 마이너 버전이 3개씩 나오고, AWS는 분기마다 새 인스턴스 타입을 내고, RDS는 정기적으로 maintenance window가 잡힙니다. 이번 글은 그 정기 운영 사이클을 정리하겠습니다 — EKS 업그레이드, 백업,복구, 비용, 보안의 네 차원입니다. 시리즈의 마지막 글이므로 실전 6편과 K8s 트랙 전체 26편의 회고도 함께 담겠습니다.

이번 시리즈는 K8s 실전 6편입니다.

EKS 업그레이드 — 1년에 한 번은 마이너 버전 #

K8s 자체의 버전 정책은 명확합니다.

  • 마이너 버전 (1.30 → 1.31 → 1.32) — 약 4개월 주기로 릴리스
  • 각 마이너 버전의 지원 기간 — 출시 후 약 14개월
  • EKS의 지원 기간 — 표준 지원 14개월 + 확장 지원 추가 12개월(유료)

운영 클러스터가 표준 지원 안에 있도록 유지하려면 1년에 최소 한 번의 마이너 업그레이드가 필요합니다. 분기별 업데이트 캘린더로 관리하는 것이 표준입니다.

업그레이드의 표준 흐름 #

EKS 마이너 업그레이드의 한 사이클
1. 릴리스 노트 검토 — 새 버전의 deprecation, removed API
2. dev 클러스터에서 먼저 업그레이드
3. 일주일~이주일 dev에서 굴려 보기
4. 매니페스트의 deprecated API 정리
5. 업그레이드 점검 도구 실행 (pluto, kubent)
6. prod 컨트롤 플레인 업그레이드
7. prod 노드 그룹 업그레이드 (rolling)
8. 애드온 업그레이드 (vpc-cni, coredns, kube-proxy, ebs-csi)

각 단계에서 시간이 가장 많이 드는 곳은 4단계 — deprecated API 정리입니다. K8s가 마이너 버전마다 일부 API를 제거하므로, 매니페스트에 옛 API가 들어 있으면 새 클러스터에서 거부됩니다.

점검 도구 — pluto와 kubent #

pluto — 매니페스트의 deprecated API 점검
pluto detect-files -d charts/ --target-versions k8s=v1.31
kubent — 클러스터에 살아 있는 deprecated API 점검
kubent --target-version 1.31

두 도구가 합쳐져 “내 매니페스트와 내 클러스터 둘 다에서” deprecated API를 찾아 줍니다. 운영 클러스터의 업그레이드 표준 절차의 일부입니다.

컨트롤 플레인 업그레이드 — Terraform 한 줄 #

terraform — cluster_version만 바꿔서 apply
module "eks" {
  # ...
  cluster_version = "1.31"   # 1.30 → 1.31
}

terraform apply가 EKS 컨트롤 플레인의 마이너 업그레이드를 트리거합니다. EKS는 컨트롤 플레인을 무중단으로 업그레이드합니다 — 사용자 워크로드는 영향받지 않고, kubectl도 계속 작동합니다. 약 30분~1시간 정도 걸립니다.

노드 그룹 업그레이드 — Rolling vs Blue-green #

노드 그룹의 업그레이드는 두 패턴이 있습니다.

패턴모델
In-place rolling같은 Managed Node Group 안의 노드를 한 대씩 새 AMI로 교체
Blue-green새 Managed Node Group을 만들어 워크로드 이전 후 옛 그룹 삭제

EKS Managed Node Group의 기본은 in-place rolling입니다. EKS가 자동으로 Pod를 cordon → drain → 새 노드 띄우기 → Pod를 새 노드로 → 옛 노드 삭제의 사이클을 돕니다. #2에서 만든 PodDisruptionBudget이 이 시점에서 결정적입니다 — PDB가 없으면 같은 워크로드의 Pod가 동시에 다 내려갈 수 있고, 사용자에게 다운타임으로 보입니다.

노드 그룹 업그레이드 트리거 (Terraform 또는 콘솔)
# 컨트롤 플레인이 1.31로 올라간 뒤
aws eks update-nodegroup-version \
  --cluster-name myshop-prod \
  --nodegroup-name general \
  --region ap-northeast-2

대규모 클러스터에서 업그레이드의 영향이 우려되면 blue-green 패턴이 안전합니다 — 새 노드 그룹을 만들고 고급 #4 Karpenter의 disruption 제어로 워크로드를 점진 이전한 뒤 옛 그룹을 비우는 흐름입니다.

애드온 업그레이드 #

컨트롤 플레인과 노드가 새 버전이 된 뒤 애드온도 함께 올립니다.

terraform — 애드온 버전 업데이트
cluster_addons = {
  vpc-cni = {
    most_recent = true   # 또는 명시적 버전
  }
  coredns = {
    most_recent = true
  }
  # ...
}

most_recent = true로 두면 Terraform이 K8s 버전과 호환되는 최신 애드온 버전으로 자동 업그레이드합니다. 명시적 버전 핀이 필요한 경우 addon_version 필드를 씁니다.

백업과 복구 — RDS의 PITR이 1차 방어선 #

myshop의 데이터는 거의 전부 RDS에 있습니다. K8s 클러스터 자체는 매니페스트가 git에 있으므로 무너져도 다시 구성할 수 있지만, RDS 데이터는 한 번 잃으면 복구가 어렵습니다.

자동 스냅샷 + PITR #

#3의 Terraform에서 backup_retention_period = 30을 적은 한 줄이 다음을 만들어 냈습니다.

  • 매일 자동 스냅샷backup_window(03:00-04:00 UTC) 시간에 자동 백업
  • PITR (Point-in-Time Recovery) — 지난 30일 안의 임의 시각으로 1초 단위 복구 가능

PITR은 RDS의 가장 강력한 백업 기능입니다. 사용자가 실수로 DELETE FROM orders;를 prod에 돌렸을 때, 그 직전 시각으로 5분 만에 새 RDS 인스턴스를 복구해 데이터를 비교할 수 있습니다.

PITR로 30분 전 시점 복구 — 새 인스턴스로
aws rds restore-db-instance-to-point-in-time \
  --source-db-instance-identifier myshop-prod \
  --target-db-instance-identifier myshop-prod-recovery \
  --restore-time 2026-05-11T08:30:00Z \
  --db-subnet-group-name myshop-prod \
  --vpc-security-group-ids sg-xxxxx

이 명령이 30분 전 시점의 데이터를 가진 새 인스턴스를 만듭니다. 그 인스턴스에서 손상되지 않은 데이터를 SELECT로 가져와 prod에 복원하는 흐름이 표준 사고 대응입니다.

복구 훈련 — 분기별 점검 #

자동 백업이 굴러가는 것을 보고 안심하는 것과 실제로 복구가 가능한 것은 다릅니다. 분기에 한 번씩 복구를 시뮬레이션해 보는 게 표준입니다.

분기 복구 훈련 절차
1. PITR로 새 인스턴스 복구 (위 명령)
2. 새 인스턴스의 데이터를 표본 확인 (row count, 최근 트랜잭션)
3. dev 환경의 myshop-api를 새 인스턴스로 가리키도록 설정 변경
4. 기능 시험 (가상 주문 생성, 조회)
5. 새 인스턴스 삭제 + 결과 문서화

이 훈련에서 한 번이라도 실패가 나오면 운영 우선순위 1번으로 잡아야 하는 신호입니다. “백업이 있다"가 아니라 “복구가 검증되었다"가 운영의 표준입니다.

클러스터 복구 — Velero #

K8s 클러스터 자체의 복구는 GitOps repo가 1차 방어선이지만, etcd 자체에 있는 동적 상태(예: 사용자가 만든 PVC, ConfigMap의 자동 갱신)까지 보존하려면 Velero가 표준 도구입니다.

Velero — S3로 클러스터 백업
velero install \
  --provider aws \
  --bucket myshop-velero-backups \
  --region ap-northeast-2 \
  --plugins velero/velero-plugin-for-aws:v1.10.0 \
  --backup-location-config region=ap-northeast-2

# 매일 자동 백업
velero schedule create daily --schedule="0 2 * * *" --ttl 720h

Velero가 etcd에서 K8s 객체 + EBS 볼륨 스냅샷을 S3로 정기 백업합니다. 클러스터를 통째로 잃었을 때 새 클러스터에서 velero restore로 복구할 수 있습니다.

비용 — 가장 빠르게 새는 영역 #

K8s 클러스터의 비용은 의도하지 않으면 빠르게 부풀어 오릅니다. 운영 클러스터의 비용 점검 표준 항목들을 짚어 두겠습니다.

1. Karpenter + Spot의 결합 #

#1에서 짧게 언급한 Karpenter가 비용 측면에서 가장 큰 절감을 만듭니다.

Karpenter NodePool — Spot 우선
spec:
  template:
    spec:
      requirements:
        - key: karpenter.sh/capacity-type
          operator: In
          values: ["spot", "on-demand"]
        - key: node.kubernetes.io/instance-type
          operator: NotIn
          values: ["m5.metal"]   # 너무 큰 인스턴스 제외
  disruption:
    consolidationPolicy: WhenEmptyOrUnderutilized
    consolidateAfter: 30s

Karpenter가 워크로드의 자원 요구에 정확히 맞는 인스턴스를 Spot 가격으로 띄웁니다. ON_DEMAND 대비 약 50~70% 절감이 일반적이고, consolidation으로 사용량이 낮은 노드를 자동 통합합니다.

2. RDS 인스턴스 right-sizing #

RDS 비용은 인스턴스 클래스 + 스토리지 + IOPS의 합입니다. 인스턴스 클래스가 가장 큰 비중을 차지하므로 분기별 right-sizing 검토가 표준입니다.

RDS Performance Insights에서 보는 시그널
- CPU 사용률 평균이 30% 미만 → 한 단계 작은 클래스 검토
- max_connections 사용률 50% 미만 → 풀 크기 / 인스턴스 모두 검토
- 스토리지 IOPS의 max가 baseline의 50% 미만 → gp3로 다운사이징 검토

매니지드 서비스라 인스턴스 클래스 변경이 한 번의 reboot로 끝납니다. 분기 단위 right-sizing이 가장 큰 비용 절감 항목 중 하나입니다.

3. NAT Gateway 데이터 전송 #

#1에서 짧게 짚었던 NAT Gateway 비용은 시간당 + GB당 데이터 전송 요금입니다. private 서브넷의 워크로드가 외부와 통신할 때마다 비용이 쌓이고, 의외로 큰 비중을 차지하는 경우가 많습니다.

NAT 데이터 전송 절감 패턴
- VPC Endpoint 도입 — S3 / ECR / DynamoDB 같은 AWS 서비스는 VPC Endpoint로 NAT 우회
- 같은 region의 RDS는 VPC 내부 라우팅이라 NAT 우회 (VPC peering / Transit Gateway)
- 외부 API를 자주 부른다면 캐싱 레이어 도입

월 $100~$500 NAT 비용이 VPC Endpoint 도입으로 그 절반 이하로 떨어지는 일이 흔합니다.

4. EBS 스냅샷과 unused 자원 #

정기 점검할 unused 자원
- 옛 EBS 스냅샷 (RDS 자동 스냅샷 외에 수동으로 만든 것)
- 옛 AMI
- 분리된 EBS 볼륨 (옛 노드 그룹의 잔재)
- 사용되지 않는 ALB / NLB (옛 Ingress의 잔재)
- ECR의 옛 이미지 (lifecycle policy로 자동 삭제 권장)

ECR lifecycle policy는 Terraform 한 장으로 정리됩니다.

terraform — ECR lifecycle
resource "aws_ecr_lifecycle_policy" "myshop_api" {
  repository = aws_ecr_repository.myshop_api.name

  policy = jsonencode({
    rules = [
      {
        rulePriority = 1
        description  = "Keep last 30 production tags"
        selection = {
          tagStatus     = "tagged"
          tagPrefixList = ["v"]
          countType     = "imageCountMoreThan"
          countNumber   = 30
        }
        action = { type = "expire" }
      },
      {
        rulePriority = 2
        description  = "Expire untagged after 7 days"
        selection = {
          tagStatus   = "untagged"
          countType   = "sinceImagePushed"
          countUnit   = "days"
          countNumber = 7
        }
        action = { type = "expire" }
      }
    ]
  })
}

5. 비용 가시화 — Kubecost / OpenCost #

OpenCost (오픈소스) 설치
helm repo add opencost https://opencost.github.io/opencost-helm-chart
helm install opencost opencost/opencost \
  -n opencost --create-namespace

OpenCost가 Prometheus 메트릭과 클라우드 비용 API를 결합해 네임스페이스,워크로드 단위 비용을 보여줍니다. “myshop 네임스페이스가 한 달에 얼마를 쓰는가, 어느 워크로드가 대부분인가"가 한눈에 보입니다. 비용 책임의 분배가 팀 단위로 떨어지는 환경에서는 거의 표준입니다.

보안 — 정기 점검의 표준 항목 #

운영 클러스터의 보안은 한 번의 셋업이 아니라 정기 점검의 누적입니다. 표준 항목 셋을 짚어 두겠습니다.

1. CIS Benchmark — kube-bench #

kube-bench — CIS Benchmark 점검
kubectl apply -f https://raw.githubusercontent.com/aquasecurity/kube-bench/main/job-eks.yaml
kubectl logs -l job-name=kube-bench

kube-bench가 CIS Kubernetes Benchmark의 항목을 자동 점검합니다. EKS는 컨트롤 플레인이 매니지드라 점검 항목이 노드 + 워크로드 매니페스트에 한정됩니다. 분기별로 실행하고 FAIL 항목을 정리하는 것이 표준입니다.

2. 컨테이너 이미지 스캔 — Trivy #

.github/workflows/build.yml — 이미지 스캔 단계 추가
- name: Trivy 이미지 스캔
  uses: aquasecurity/trivy-action@master
  with:
    image-ref: 123456789012.dkr.ecr.ap-northeast-2.amazonaws.com/myshop-api:${{ steps.meta.outputs.tag }}
    format: sarif
    severity: CRITICAL,HIGH
    exit-code: 1   # CRITICAL 발견 시 빌드 실패
    output: trivy-results.sarif

- name: SARIF를 GitHub Security 탭에 업로드
  uses: github/codeql-action/upload-sarif@v3
  with:
    sarif_file: trivy-results.sarif

Trivy가 베이스 이미지의 OS 패키지 + 애플리케이션 의존성의 알려진 취약점(CVE)을 스캔합니다. CRITICAL이 발견되면 빌드 실패로 막아 새 이미지가 ECR에 들어가지 못하게 합니다. ECR Enhanced Scanning(유료)도 같은 영역을 채우는 매니지드 옵션입니다.

3. RBAC 권한 audit #

권한 사용 현황 점검
# 모든 ClusterRoleBinding 보기
kubectl get clusterrolebindings -o wide

# 특정 권한이 부여된 주체 찾기
kubectl auth can-i --list --as=system:serviceaccount:myshop:myshop-api

# 외부 도구 — krane (Salesforce)
# 또는 rbac-tool by InsightCloudSec

분기별로 RBAC를 audit해서 “사용되지 않는 권한”, “과도한 권한”, “옛 ServiceAccount의 잔재"를 정리하는 흐름이 표준입니다. 고급 #2에서 다룬 kubectl auth can-i가 일상 점검의 도구입니다.

4. 정책 엔진 — Kyverno로 admission 강제 #

고급 #3에서 다룬 Kyverno를 도입하면 다음 같은 정책이 admission 단계에서 강제됩니다.

kyverno-policies.yaml — 운영 표준 정책 셋
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
  name: require-image-from-ecr
spec:
  validationFailureAction: Enforce
  rules:
    - name: ecr-only
      match:
        resources:
          kinds: [Pod]
      validate:
        message: "Images must come from our ECR registry."
        pattern:
          spec:
            containers:
              - image: "123456789012.dkr.ecr.*"

이런 정책 5~10개가 운영 클러스터의 표준 가드레일입니다. 다른 사람이 외부 이미지나 limits 없는 컨테이너를 실수로 배포해도 admission 단계에서 거부됩니다.

정기 운영 캘린더 #

위 항목을 캘린더로 정리하면 다음과 같습니다.

주기작업
매일알람 리뷰, Grafana 대시보드 점검
매주새 보안 패치 검토, ECR Trivy 스캔 결과 리뷰
매월비용 리뷰 (OpenCost), unused 자원 정리, SLI/SLO 리포트
분기EKS 마이너 업그레이드, RDS right-sizing, RBAC audit, 복구 훈련, kube-bench
반기보안 점검 종합 (외부 감사 / pentest), DR 시뮬레이션
클러스터 아키텍처 리뷰, 매니페스트 현대화

이 캘린더가 한 페이지에 정리되어 있고 담당자가 정해져 있는 것이 운영 팀의 도착점입니다. 한 번 셋업한 뒤 “잘 굴러가니까 잊고 산다"가 가장 위험한 패턴입니다.

시리즈 회고 — K8s 실전 6편으로 손에 들어온 것 #

마지막 글이므로 6편을 한번 짚어 두겠습니다.

  • #1EKS 클러스터 셋업. Terraform으로 VPC,EKS,노드 그룹,IRSA,표준 애드온까지를 한 코드베이스로. eksctl 비교, Karpenter 예고.
  • #2앱 배포 골격. Deployment + Service + Ingress + ConfigMap + Secret + HPA + PDB의 표준 9묶음. AWS Load Balancer Controller로 ALB 자동 프로비저닝. Helm 차트로 dev / prod 환경별 values.
  • #3DB 연동. RDS Terraform, Secrets Manager, External Secrets Operator로 비밀 동기화. PgBouncer 커넥션 풀. Helm hook 기반 마이그레이션 Job. RDS IAM 인증의 더 진보된 결.
  • #4CI/CD 파이프라인. GitHub Actions OIDC로 정적 키 없이 ECR push. 매니페스트 repo 자동 commit. ArgoCD App of Apps. Argo Rollouts 카나리.
  • #5모니터링,알람. kube-prometheus-stack 한 번에 설치. ServiceMonitor + PrometheusRule. 4 golden signals. Alertmanager severity 분기. Loki + CloudWatch.
  • #6 — 운영 체크리스트. EKS 업그레이드, PITR 백업,복구, Karpenter Spot 비용 절감, kube-bench / Trivy / Kyverno 보안.

시리즈 첫 글에서 잡았던 가상 시나리오(myshop-api)가 6편을 거치며 실제 운영 클러스터의 한 사이클로 완성됐습니다. 추상이 아닌 구체로 K8s를 도입,운영하는 시야가 손에 들어오는 단계입니다.

K8s 트랙 전체 회고 — 26편 #

기초 7편 + 중급 7편 + 고급 6편 + 실전 6편을 더하면 26편이고, 도커 기초 6편까지 포함하면 32편입니다. 이 숫자가 K8s 트랙과 주변 도커 트랙의 규모를 한 번에 보여줍니다.

K8s 트랙의 큰 그림
[기초 7편]    매니페스트 한 장의 모델 — kubectl apply 한 번의 흐름
[중급 7편]    그 매니페스트가 운영 클러스터에서 굴러가는 깊이
[고급 6편]    그 위에 얹히는 정책,확장,관측,동기화의 결
[실전 6편]    EKS 위 진짜 서비스 한 사이클 — myshop-api

각 트랙이 다음 트랙의 입력이 되는 구조이고, 26편 다 따라온 시점이라면 다음 단계가 명확해집니다.

  • 매니페스트 한 장의 의도를 한 줄로 읽을 수 있음
  • 새 클러스터의 셋업,확장,운영의 결정이 손에 들어옴
  • 사고 발생 시 어디부터 봐야 할지 즉시 짚을 수 있음
  • 비용,보안,업그레이드의 정기 사이클이 머릿속에 캘린더로 잡힘

그다음 — K8s 트랙 너머 #

이 트랙이 도착점은 아닙니다. K8s 위에 얹는 더 깊은 주제들이 남아 있습니다.

  • Service Mesh — Istio / Linkerd. mTLS,세밀한 트래픽 라우팅,observability mesh.
  • MLOps on K8s — Kubeflow, KServe, Argo Workflows. ML 모델 학습,배포,서빙의 전용 스택.
  • 멀티 클러스터 — 단일 클러스터의 한계를 넘는 패턴. 클러스터 페더레이션, 멀티 region, ArgoCD ApplicationSet.
  • EBPF 깊이 — Cilium 너머의 영역. 보안 / 옵저버빌리티 / 네트워킹의 다음 세대.
  • eks-anywhere / on-prem K8s — 매니지드를 벗어난 클러스터 운영의 결.

이 주제들은 별도 시리즈로 다룰 가치가 있고, 30편의 K8s 트랙이 그 출발점에 서 있는 시야를 만들어 줍니다.

마무리 #

K8s 실전 시리즈 6편을 마무리하고, K8s 트랙 전체 26편을 마무리하겠습니다. 이번 글에서는 클러스터를 한 해 단위로 안전하게 굴리는 정기 사이클을 따라갔습니다 — EKS 마이너 업그레이드, RDS PITR과 분기 복구 훈련, Karpenter + Spot으로 비용 절감, kube-bench / Trivy / Kyverno로 보안 정기 점검까지가 운영 사이클의 표준 골격입니다. 이 트랙의 도착점은 단순한 한 문장으로 줄여집니다 — “클러스터가 잘 굴러간다"고 말할 수 있는 시야가 손에 들어왔는가. 매니페스트 한 장의 모델부터 분기 캘린더의 운영 항목까지가 한 사람의 머릿속에 자연스럽게 묶이는 시점이 K8s 트랙의 마지막 단계입니다.

X