Kubernetes 정복기: Production-Ready 운영 완벽 정복 (Day 5)

문한성·2025년 11월 3일
0

K8S 정복하기

목록 보기
5/9

2025년 11월 3일
Job/CronJob, Network Policy, Resource Quotas, CRD, Helm Chart, etcd Backup, Cluster Upgrade까지!

들어가며

Day 4에서 Ingress, HPA, RBAC, StatefulSet, DaemonSet 등 고급 패턴을 마스터했습니다. 이제 Day 5에서는 Production 환경에서 클러스터를 안전하고 효율적으로 운영하는 방법을 학습했습니다.

오늘 배운 것:
1. Job & CronJob으로 배치 작업 자동화
2. Network Policy로 Zero Trust 네트워크 구현
3. Resource Quotas로 팀별 리소스 관리
4. CRD와 Operator Pattern 이해
5. Helm Chart로 애플리케이션 패키징 (Terraform + Terragrunt 패턴!)
6. etcd 백업 주기와 실무 전략
7. Cluster 업그레이드 절차와 Best Practices


1. Job & CronJob: 배치 작업 관리

Deployment vs Job

🤔 내가 이해한 것:

  • Deployment: 항상 실행되어야 하는 워크로드 (웹 서버, API)
  • Job: 한 번 실행 후 종료 (데이터 마이그레이션, 백업, 계산)

실습 1: 간단한 Job (π 계산)

apiVersion: batch/v1
kind: Job
metadata:
  name: pi-calculation
spec:
  template:
    spec:
      containers:
      - name: pi
        image: perl:5.34
        command: ["perl", "-Mbignum=bpi", "-wle", "print bpi(2000)"]
      restartPolicy: Never
  backoffLimit: 4

실행 결과:

$ kubectl apply -f job-pi.yaml
job.batch/pi-calculation created

$ kubectl get jobs
NAME              COMPLETIONS   DURATION   AGE
pi-calculation    1/1           8s         45s

$ kubectl get pods
NAME                    READY   STATUS      RESTARTS   AGE
pi-calculation-abc123   0/1     Completed   0          50s

$ kubectl logs pi-calculation-abc123 | head -3
3.141592653589793238462643383279502884197169399375105820974944592307816406286208998628034825342117067982148086513282306647093844609550582231725359408128481117450284102701938521105559644622948954930381964428810975665933446128475648233786783165271201909145648566923460348610454326648213393607260249141273724587006606315588174881520920962829254091715364367892590360011330530548820466521384146951941511609433057270365759591953092186117381932611793105118548074462379962749567351885752724891227938183011949129833673362440656643086021394946395224737190702179860943702770539217176293176752384674818467669405132000568127145263560827785771342757789609173637178721468440901224953430146549585371050792279689258923542019956112129021960864034418159813629774771309960518707211349999998372978049951059731732816096318595024459455346908302642522308253344685035261931188171010003137838752886587533208381420617177669147303598253490428755468731159562863882353787593751957781857780532171226806613001927876611195909216420199

2000자리 π 값 출력 성공!

실습 2: Parallel Job (병렬 처리)

apiVersion: batch/v1
kind: Job
metadata:
  name: parallel-job
spec:
  completions: 10      # 총 10번 성공해야 함
  parallelism: 3       # 동시에 3개씩 실행
  template:
    spec:
      containers:
      - name: worker
        image: busybox:1.28
        command: ["/bin/sh", "-c", "echo 'Processing task' && sleep 5 && echo 'Task completed'"]
      restartPolicy: Never

실시간 관찰 (2초마다):

# t=0초 - 3개 동시 시작!
$ kubectl get job parallel-job && kubectl get pods -l job-name=parallel-job
NAME           COMPLETIONS   DURATION   AGE
parallel-job   0/10          3s         3s

NAME                   READY   STATUS    RESTARTS   AGE
parallel-job-abc12     1/1     Running   0          3s
parallel-job-def34     1/1     Running   0          3s
parallel-job-ghi56     1/1     Running   0          3s

# t=8초 - 첫 3개 완료, 다음 3개 시작!
$ kubectl get job parallel-job && kubectl get pods -l job-name=parallel-job
NAME           COMPLETIONS   DURATION   AGE
parallel-job   3/10          11s        11s

NAME                   READY   STATUS      RESTARTS   AGE
parallel-job-abc12     0/1     Completed   0          11s
parallel-job-def34     0/1     Completed   0          11s
parallel-job-ghi56     0/1     Completed   0          11s
parallel-job-jkl78     1/1     Running     0          3s
parallel-job-mno90     1/1     Running     0          3s
parallel-job-pqr12     1/1     Running     0          3s

# t=38초 - 모두 완료!
$ kubectl get job parallel-job
NAME           COMPLETIONS   DURATION   AGE
parallel-job   10/10         38s        38s

결과: 10개 작업을 3개씩 동시 실행하여 38초 만에 완료! (순차 실행 시 50초 소요)

실습 3: CronJob (주기적 백업)

apiVersion: batch/v1
kind: CronJob
metadata:
  name: backup-job
spec:
  schedule: "*/1 * * * *"  # 매 1분마다 (테스트용)
  jobTemplate:
    spec:
      template:
        spec:
          containers:
          - name: backup
            image: busybox:1.28
            command:
            - /bin/sh
            - -c
            - |
              echo "[$(date)] Starting backup..."
              echo "Backing up data..."
              sleep 3
              echo "[$(date)] Backup completed!"
          restartPolicy: OnFailure
  successfulJobsHistoryLimit: 3
  failedJobsHistoryLimit: 1

3분 후 확인:

$ kubectl get cronjob
NAME         SCHEDULE      SUSPEND   ACTIVE   LAST SCHEDULE   AGE
backup-job   */1 * * * *   False     0        45s             3m

$ kubectl get jobs -l job-name=backup-job
NAME                    COMPLETIONS   DURATION   AGE
backup-job-29369409     1/1           5s         3m
backup-job-29369410     1/1           5s         2m
backup-job-29369411     1/1           5s         1m

$ kubectl logs backup-job-29369411-abc12
[Wed Nov 3 05:42:00 UTC 2025] Starting backup...
Backing up data...
[Wed Nov 3 05:42:03 UTC 2025] Backup completed!

자동으로 매 1분마다 Job 생성 및 실행!


2. Network Policy: Zero Trust 네트워크

🤔 내 질문: "Network Policy는 운영환경에서 모든 리소스마다 걸어두는 편인가?"

답변: 아니요, 일반적으로 20-30%의 중요 리소스에만 적용합니다:

  • 데이터베이스 (외부 접근 차단)
  • 결제 서비스 (PCI-DSS 규정)
  • 인증 서버
  • 민감 정보 처리 Pod

🤔 추가 질문: "결제서비스이면 Zero Trust를 해야겠네?"

답변: 네, 반드시 Zero Trust를 적용해야 합니다!

  • PCI-DSS 규정 준수 필수
  • Default Deny → Explicit Allow
  • 모든 통신 경로 명시적 허용

실습: Database 격리 (Backend만 접근 가능)

시나리오:

  • Frontend → Database ❌ (차단)
  • Backend → Database ✅ (허용)
  • Test Pod → Database ❌ (차단)
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: database-policy
  namespace: production
spec:
  podSelector:
    matchLabels:
      app: database
  policyTypes:
  - Ingress
  ingress:
  - from:
    - podSelector:
        matchLabels:
          app: backend
    ports:
    - protocol: TCP
      port: 5432

테스트 결과:

# Test Pod에서 접근 시도 (차단되어야 함)
$ kubectl exec -n production test-pod -- nc -zv database 5432
nc: database (10.244.5.225): Operation timed out  ❌ 차단 성공!

# Backend Pod에서 접근 시도 (허용되어야 함)
$ kubectl exec -n production backend -- nc -zv database 5432
database (10.244.5.225:5432) open  ✅ 허용 성공!

Network Policy가 정확히 작동!


3. Resource Quotas & LimitRange

ResourceQuota: Namespace 전체 리소스 제한

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

LimitRange: Pod별 기본값 및 제한

apiVersion: v1
kind: LimitRange
metadata:
  name: dev-limits
  namespace: dev
spec:
  limits:
  - max:
      cpu: "2"
      memory: 4Gi
    min:
      cpu: 100m
      memory: 128Mi
    default:
      cpu: 500m       # 기본 limit
      memory: 1Gi
    defaultRequest:
      cpu: 200m       # 기본 request
      memory: 512Mi
    type: Container

실습: 할당량 초과 테스트

1. LimitRange 위반 테스트:

$ kubectl run test-large -n dev --image=nginx \
  --limits=cpu=6  # max는 2 CPU인데 6 요청

Error from server (Forbidden): pods "test-large" is forbidden:
maximum cpu usage per Container is 2, but limit is 6

LimitRange가 먼저 차단!

2. ResourceQuota 위반 테스트:

# 이미 dev namespace에 CPU request 2.2 core 사용 중

$ kubectl run test2 -n dev --image=nginx \
  --requests=cpu=2  # 총 4.2 core가 되어 quota(4) 초과

Error from server (Forbidden): pods "test2" is forbidden:
exceeded quota: dev-quota, requested: requests.cpu=2,
used: requests.cpu=2200m, limited: requests.cpu=4

ResourceQuota가 차단!

기본값 자동 적용 확인

$ kubectl run test-default -n dev --image=nginx

$ kubectl get pod test-default -n dev -o yaml | grep -A 10 resources:
    resources:
      limits:
        cpu: 500m        # ← LimitRange의 default 자동 적용!
        memory: 1Gi
      requests:
        cpu: 200m        # ← defaultRequest 자동 적용!
        memory: 512Mi

리소스를 명시하지 않아도 자동으로 설정됨!


4. Custom Resource Definition (CRD)

🤔 내 질문: "아무리 봐도 굳이 사용하는 이유를 아직은 모르겠다. 컨테이너 이미지를 넣지도 않고 그냥 텍스트 장난으로 보이는데? 왜 쓰는거지?"

이 질문이 가장 중요했습니다!

답변: CRD 단독으로는 아무것도 하지 않습니다. CRD는 단지 데이터 구조 정의일 뿐입니다.

진짜 힘은 Operator Pattern:

CRD (데이터 구조) + Operator (Controller) = 자동화!

실제 사례: Deployment Controller (Built-in Operator)

apiVersion: apps/v1
kind: Deployment  # ← 이것도 CRD입니다! (Built-in)
metadata:
  name: nginx
spec:
  replicas: 3

Deployment를 생성하면:
1. etcd에 Deployment 정보 저장 (CRD 역할)
2. Deployment Controller가 감지 (Operator 역할)
3. ReplicaSet 자동 생성
4. ReplicaSet Controller가 감지
5. Pod 3개 자동 생성
6. Pod가 죽으면 자동으로 재생성! ← 이게 Operator의 힘!

실습: Database CRD

apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
  name: databases.mycompany.com
spec:
  group: mycompany.com
  versions:
  - name: v1
    served: true
    storage: true
    schema:
      openAPIV3Schema:
        type: object
        properties:
          spec:
            type: object
            properties:
              engine:
                type: string
                enum: ["postgres", "mysql", "mongodb"]
              version:
                type: string
              storage:
                type: string
            required:
            - engine
            - version
  scope: Namespaced
  names:
    plural: databases
    singular: database
    kind: Database
    shortNames:
    - db

Database 생성:

apiVersion: mycompany.com/v1
kind: Database
metadata:
  name: production-db
spec:
  engine: postgres
  version: "15"
  storage: 100Gi
$ kubectl apply -f database.yaml
database.mycompany.com/production-db created

$ kubectl get databases
NAME            AGE
production-db   10s
dev-db          5s

$ kubectl get db  # shortName 동작!
NAME            AGE
production-db   15s
dev-db          10s

스키마 검증 테스트 (enum 위반):

$ kubectl apply -f database-oracle.yaml  # engine: oracle

Error: Unsupported value: "oracle": supported values: "postgres", "mysql", "mongodb"

OpenAPI 스키마 검증 작동!


5. Helm Chart: Kubernetes의 Terraform

🤔 내 질문: "보통 공식으로 올라가있는 chart들은 values.yaml을 받아서 그걸로 다시 install하잖아? 마치 Terraform 모듈화를 terragrunt 환경변수로 환경별로 실행하는걸 떠올리게 한다"

완벽한 이해입니다!

TerraformHelm
Terraform 모듈Helm Chart
tfvarsvalues.yaml
terragrunt.hclvalues-dev.yaml, values-prod.yaml
terraform applyhelm install
terraform planhelm template

실습 1: 간단한 Chart 생성

$ helm create my-webapp
Creating my-webapp

$ tree my-webapp/
my-webapp/
├── Chart.yaml
├── values.yaml
├── templates/
│   ├── deployment.yaml
│   ├── service.yaml
│   ├── ingress.yaml
│   └── _helpers.tpl
└── charts/

values.yaml 수정:

replicaCount: 3

image:
  repository: nginx
  tag: "1.21"

설치:

$ helm install myapp ./my-webapp

$ kubectl get pods -l app.kubernetes.io/name=my-webapp
NAME                         READY   STATUS    RESTARTS   AGE
my-webapp-6c8b4d9f7b-abc12   1/1     Running   0          30s
my-webapp-6c8b4d9f7b-def34   1/1     Running   0          30s
my-webapp-6c8b4d9f7b-ghi56   1/1     Running   0          30s

업그레이드 (replicas 변경):

$ helm upgrade myapp ./my-webapp --set replicaCount=5

$ kubectl get pods -l app.kubernetes.io/name=my-webapp
NAME                         READY   STATUS    RESTARTS   AGE
my-webapp-6c8b4d9f7b-abc12   1/1     Running   0          2m
my-webapp-6c8b4d9f7b-def34   1/1     Running   0          2m
my-webapp-6c8b4d9f7b-ghi56   1/1     Running   0          2m
my-webapp-6c8b4d9f7b-jkl78   1/1     Running   0          5s
my-webapp-6c8b4d9f7b-mno90   1/1     Running   0          5s

롤백:

$ helm rollback myapp 1

$ kubectl get pods -l app.kubernetes.io/name=my-webapp
NAME                         READY   STATUS    RESTARTS   AGE
my-webapp-6c8b4d9f7b-abc12   1/1     Running   0          3m
my-webapp-6c8b4d9f7b-def34   1/1     Running   0          3m
my-webapp-6c8b4d9f7b-ghi56   1/1     Running   0          3m

다시 3개로 롤백!

실습 2: 환경별 Values 파일 (Terraform 패턴!)

values-dev.yaml (개발 환경):

architecture: standalone  # 단일 인스턴스

auth:
  postgresPassword: "dev-password-123"
  username: "myapp"
  password: "myapp-dev-123"
  database: "myapp_dev"

primary:
  resources:
    requests:
      memory: "256Mi"
      cpu: "250m"
    limits:
      memory: "512Mi"
      cpu: "500m"

  persistence:
    enabled: true
    storageClass: "local-path"
    size: 5Gi

backup:
  enabled: false

metrics:
  enabled: false

values-prod.yaml (프로덕션 환경):

architecture: replication  # HA 구성

auth:
  existingSecret: "postgres-prod-secret"

primary:
  resources:
    requests:
      memory: "2Gi"
      cpu: "1000m"
    limits:
      memory: "4Gi"
      cpu: "2000m"

  persistence:
    enabled: true
    storageClass: "fast-ssd"
    size: 100Gi

  podAntiAffinityPreset: hard

readReplicas:
  replicaCount: 2

  resources:
    requests:
      memory: "2Gi"
      cpu: "1000m"

backup:
  enabled: true
  cronjob:
    schedule: "0 2 * * *"

metrics:
  enabled: true
  serviceMonitor:
    enabled: true

pgpool:
  enabled: true
  numInitChildren: 32
  maxPool: 4

배포:

# 개발 환경
helm install postgres-dev bitnami/postgresql -f values-dev.yaml -n dev

# 프로덕션 환경
helm install postgres-prod bitnami/postgresql -f values-prod.yaml -n prod

결과:

  • 개발: 단일 Pod, 5GB, 백업 없음
  • 프로덕션: Primary 1개 + Replica 2개, 100GB, 자동 백업, 모니터링, Connection Pooler

완전히 Terraform + Terragrunt 패턴!


6. etcd Backup & Restore

🤔 내 질문: "실제 운영환경들은 백업 주기는?"

실제 운영환경 백업 주기

환경자동 백업 주기RTORPO보관 정책
대기업/금융매 1시간1시간1시간7년 (규정 준수)
중견기업매 6시간4시간6시간3개월
스타트업매일 1회12시간24시간1개월

가장 일반적인 패턴 (중견기업 표준):

✅ 매 6시간: 자동 백업 (S3 Standard-IA) - 48시간 보관
✅ 매일 새벽 2시: 전체 백업 (S3 Glacier) - 7일 보관
✅ 매주 일요일: 주간 백업 - 4주 보관
✅ 매월 1일: 월간 백업 - 12개월 보관
✅ 배포 직전: 수동 백업 필수!

실습: etcd 백업

$ kubectl exec -n kube-system etcd-cpu1 -- sh -c "ETCDCTL_API=3 etcdctl \
  --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 \
  snapshot save /var/lib/etcd/backup.db"

Snapshot saved at /var/lib/etcd/backup.db

$ kubectl exec -n kube-system etcd-cpu1 -- sh -c "ETCDCTL_API=3 etcdctl \
  --write-out=table \
  snapshot status /var/lib/etcd/backup.db"

+---------+----------+------------+------------+
|  HASH   | REVISION | TOTAL KEYS | TOTAL SIZE |
+---------+----------+------------+------------+
| ad8760b |  1030374 |       1760 |     7.6 MB |
+---------+----------+------------+------------+

백업 성공! 1760개 키, 7.6MB

etcd의 중요성

etcd 손실 = 클러스터 전체 손실!

etcd에 저장되는 데이터:

  • 모든 Pod, Deployment, Service 정보
  • ConfigMap, Secret
  • RBAC 권한 설정
  • Network Policy
  • 모든 Kubernetes 리소스

Production Best Practices

  1. 자동 백업: CronJob으로 6시간 또는 일 단위
  2. 원격 저장: S3, GCS 등 클라우드 저장소 필수
  3. 암호화: 백업 파일 암호화 (Secrets 포함)
  4. 3-2-1 Rule: 3개 사본, 2개 매체, 1개 오프사이트
  5. 복구 테스트: 분기별 복구 훈련 (DR Drill)
  6. 배포 전 백업: 주요 변경 전 반드시 수동 백업

7. Cluster Upgrade

현재 클러스터 상태

$ kubectl version --short
Client Version: v1.31.13
Kustomize Version: v5.4.2
Server Version: v1.31.13

$ kubectl get nodes -o wide
NAME   STATUS   ROLES           AGE     VERSION
cpu1   Ready    control-plane   6d21h   v1.31.13
cpu2   Ready    <none>          5d16h   v1.31.13
gpu1   Ready    <none>          5d16h   v1.31.13

$ sudo kubeadm upgrade plan
[upgrade/versions] Cluster version: 1.31.13
[upgrade/versions] kubeadm version: v1.31.13
[upgrade/versions] Target version: v1.31.13
[upgrade/versions] Latest version in the v1.31 series: v1.31.13

이미 최신 버전이라 실제 업그레이드는 불가능! 대신 시뮬레이션과 이론 학습을 진행했습니다.

업그레이드 규칙

  1. 한 번에 한 마이너 버전씩

    ✅ 1.30 → 1.31 → 1.32 (순차)
    ❌ 1.30 → 1.32 (건너뛰기 불가)
  2. 업그레이드 순서

    1) etcd 백업 (필수!)
    2) Control Plane 업그레이드 (kubeadm)
    3) Control Plane kubelet 업그레이드
    4) Worker Node 순차 업그레이드 (Rolling)
  3. 다운타임

    • Control Plane: 1-2분 (API 서버 재시작)
    • Worker Node: 무중단 (Rolling 방식)
    • Pod: 계속 실행 (kubectl만 잠시 불가)

업그레이드 시뮬레이션 실행

$ ./upgrade-simulation.sh

======================================
Kubernetes Cluster Upgrade Simulation
======================================

[Step 1/10] 업그레이드 전 체크리스트

현재 클러스터 버전 확인:
Client Version: v1.31.13
Kustomize Version: v5.4.2

모든 노드 상태 확인:
NAME   STATUS   ROLES           AGE     VERSION
cpu1   Ready    control-plane   6d21h   v1.31.13
cpu2   Ready    <none>          5d16h   v1.31.13
gpu1   Ready    <none>          5d16h   v1.31.13

✅ Step 1 완료

[Step 2/10] etcd 백업

kubectl exec -n kube-system etcd-cpu1 -- sh -c "ETCDCTL_API=3 etcdctl \
  --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 \
  snapshot save /var/lib/etcd/pre-upgrade-backup.db"

✅ Step 2 완료 (시뮬레이션)

[Step 3/10] 모든 리소스 백업

명령어: kubectl get all -A -o yaml > /tmp/all-resources-backup.yaml
백업 완료: /tmp/all-resources-backup.yaml (396K)

✅ Step 3 완료

[Step 4/10] API Deprecation 확인

Deprecated API 사용: 3개
⚠️  업그레이드 전 Deprecated API 수정 필요!

[Step 5/10] Control Plane 업그레이드 (시뮬레이션)
[Step 6/10] Control Plane kubelet 업그레이드 (시뮬레이션)
[Step 7/10] Worker Node 1 (cpu2) 업그레이드 (시뮬레이션)
[Step 8/10] Worker Node 2 (gpu1) 업그레이드 (시뮬레이션)

[Step 9/10] 업그레이드 검증

노드 버전 확인:
NAME   STATUS   ROLES           AGE     VERSION
cpu1   Ready    control-plane   6d21h   v1.31.13
cpu2   Ready    <none>          5d16h   v1.31.13
gpu1   Ready    <none>          5d16h   v1.31.13

[Step 10/10] 최종 확인

테스트 워크로드 배포:
pod/upgrade-test created

NAME           READY   STATUS    RESTARTS   AGE   NODE
upgrade-test   1/1     Running   0          3s    gpu1

✅ Step 10 완료

======================================
업그레이드 시뮬레이션 완료!
======================================

API Deprecation (가장 중요!)

Kubernetes는 매 버전마다 API를 Deprecate 시킵니다.

주요 Deprecation 히스토리:

  • v1.22: Ingress (extensions/v1beta1 → networking.k8s.io/v1)
  • v1.25: PodSecurityPolicy, PodDisruptionBudget (v1beta1 → v1)
  • v1.26: HorizontalPodAutoscaler (v2beta2 → v2)
  • v1.29: FlowSchema, PriorityLevelConfiguration (v1beta2 → v1)

확인 방법:

$ kubectl get --raw /metrics | grep apiserver_requested_deprecated_apis

apiserver_requested_deprecated_apis{group="",removed_release="",resource="componentstatuses",subresource="",version="v1"} 1

Production 업그레이드 전략

Blue-Green Cluster (대기업):

클러스터 2개 운영 → 트래픽 점진 전환 → 다운타임 Zero
장점: 빠른 롤백, 안전
단점: 2배 비용

Rolling Upgrade (중소기업):

노드 순차 업그레이드 → 1-2분 다운타임
장점: 추가 비용 없음
단점: Control Plane 업그레이드 시 짧은 중단

업그레이드 주기

환경주기이유
프로덕션6개월안정성 우선 (최소 2개 패치 버전 대기)
스테이징3개월프로덕션 사전 검증
개발즉시최신 기능 테스트

Kubernetes 버전 지원 정책:

Kubernetes는 최근 3개 마이너 버전만 지원

현재: v1.32 (최신)
v1.32: 지원 ✅
v1.31: 지원 ✅
v1.30: 지원 ✅
v1.29: 지원 종료 ❌ (보안 패치 없음)

결론: 최소 1년에 1-2회 업그레이드 필수!


배운 점

1. Job/CronJob은 생각보다 강력하다

  • Parallel Job으로 처리 속도 3배 향상
  • CronJob은 백업, 정리 작업에 필수
  • backoffLimit로 실패 재시도 자동화

2. Network Policy는 선택적으로

  • 20-30% 리소스에만 적용 (DB, 결제, 인증)
  • Zero Trust는 결제 서비스 필수 (PCI-DSS)
  • 과도한 적용은 운영 복잡도 증가

3. Resource Quotas는 팀 관리의 핵심

  • Namespace별 리소스 할당
  • LimitRange로 기본값 자동 적용
  • 비용 관리와 직결

4. CRD는 Operator와 함께

  • CRD 단독으로는 의미 없음
  • Operator = CRD + Controller
  • Deployment도 사실 CRD + Operator!

5. Helm은 Kubernetes의 Terraform

  • Chart = Terraform 모듈
  • values.yaml = tfvars
  • 환경별 배포 = terragrunt 패턴
  • 버전 관리, 롤백 강력

6. etcd 백업은 생명줄

  • etcd 손실 = 클러스터 전체 손실
  • 6시간 or 일 단위 자동 백업
  • 배포 전 수동 백업 필수
  • 3-2-1 Rule 준수

7. Cluster Upgrade는 계획이 90%

  • API Deprecation 확인 필수
  • etcd 백업 먼저
  • Control Plane → Worker 순서
  • 테스트 환경 먼저 검증
  • 1년 1-2회 업그레이드 필수

삽질 포인트

1. Network Policy 테스트 실패

문제: nginx 이미지에 nc (netcat) 명령어 없음

error: exec: "nc": executable file not found in $PATH

해결: busybox:1.28 이미지로 test Pod 생성

kubectl run test-pod -n production --image=busybox:1.28 \
  --labels="app=test" --command -- sleep 3600

2. ResourceQuota vs LimitRange 순서

내 착각: ResourceQuota가 먼저 체크할 줄 알았음

실제: LimitRange → ResourceQuota 순서

  • LimitRange가 Pod 생성 시점에 먼저 검증
  • ResourceQuota는 Namespace 전체 누적 검증

3. CRD 이해 부족

문제: "CRD가 왜 필요한지 모르겠다"

해결: Operator Pattern 이해로 해결

  • CRD = 데이터 구조
  • Operator = 자동화 로직
  • Deployment = Built-in CRD + Operator 예시

다음 계획 (Day 6)

Day 5에서 Production-Ready 운영을 마스터했습니다! Day 6에서는 Monitoring & Logging으로 클러스터 가시성을 확보할 예정입니다:

  1. Prometheus - 메트릭 수집 및 저장
  2. Grafana - 시각화 대시보드
  3. Prometheus Operator - CRD를 활용한 모니터링 자동화
  4. Alert Manager - 장애 알림 자동화
  5. Vector + OpenSearch Stack - 로그 수집 및 분석 (Fluentd 대비 10배 빠른 성능!)
    • Vector: Rust 기반 고성능 로그 수집기
    • OpenSearch: 완전 오픈소스 검색 엔진 (Elasticsearch 포크)
    • OpenSearch Dashboards: 로그 검색 및 시각화 (Kibana 포크)
  6. Distributed Tracing - Jaeger로 요청 추적
  7. Custom Metrics - 애플리케이션 메트릭 노출

Production 클러스터의 가시성을 완벽하게 확보합시다!


참고 자료

클러스터 환경

노드 구성:

  • cpu1 (172.30.1.43): Master + Worker (12 core, 7.5GB RAM)
  • cpu2 (172.30.1.80): Worker (8 core, 16GB RAM)
  • gpu1 (172.30.1.38): Worker (12 core, 16GB RAM)

버전:

  • Kubernetes: v1.31.13
  • CNI: Calico (VXLAN CrossSubnet)
  • Helm: v3.19.0
profile
기록하고 공유하려고 노력하는 DevOps 엔지니어

0개의 댓글