관리 메뉴

근묵자흑

쿠버네티스 파드를 사용하는 주요 오브젝트들 본문

k8s

쿠버네티스 파드를 사용하는 주요 오브젝트들

Luuuuu 2025. 6. 15. 16:57

1. Job

Job은 한 번 실행하고 완료되는 작업을 위한 워크로드 오브젝트입니다. 배치 처리, 데이터 마이그레이션, 백업 등의 용도로 사용됩니다.

1.1 잡의 세부 옵션

Job의 주요 설정 옵션들을 살펴보겠습니다.

핵심 설정 필드

  • spec.backoffLimit (기본값: 6): 실패 시 재시도 횟수 제한
  • spec.completions: 성공적으로 완료되어야 하는 Pod 수
  • spec.parallelism: 동시에 실행할 수 있는 최대 Pod 수
  • spec.activeDeadlineSeconds: Job 전체 실행 시간 제한
  • spec.ttlSecondsAfterFinished: Job 완료 후 자동 삭제 시간

최신 기능 (Kubernetes 1.33)

쿠버네티스 1.33에서는 Job의 백오프 제한이 인덱스별로 설정 가능하도록 GA가 되었고, 성공 정책(SuccessPolicy)도 GA가 되어 더욱 정교한 Job 제어가 가능해졌습니다.

apiVersion: batch/v1
kind: Job
metadata:
  name: data-processing-job
spec:
  completions: 10
  parallelism: 3
  backoffLimit: 3
  activeDeadlineSeconds: 3600
  ttlSecondsAfterFinished: 1800
  template:
    spec:
      containers:
      - name: processor
        image: my-app:latest
        command: ["python", "process_data.py"]
        resources:
          requests:
            memory: "256Mi"
            cpu: "100m"
          limits:
            memory: "512Mi"
            cpu: "500m"
      restartPolicy: Never

위 예제에서는:

  • 총 10개의 Pod가 성공적으로 완료되어야 함
  • 최대 3개의 Pod가 동시에 실행
  • 실패 시 최대 3번까지 재시도
  • 전체 Job은 1시간(3600초) 내에 완료되어야 함
  • Job 완료 후 30분(1800초) 후에 자동 삭제

1.2 크론잡(CronJobs)으로 잡을 주기적으로 실행하기

CronJob은 cron 스케줄에 따라 Job을 주기적으로 실행하는 컨트롤러입니다.

주요 설정 옵션

  • schedule: Cron 표현식 (분 시 일 월 요일)
  • timeZone: 시간대 설정 (Kubernetes 1.27+)
  • concurrencyPolicy: Allow, Forbid, Replace
  • successfulJobsHistoryLimit: 성공한 Job 보관 개수
  • failedJobsHistoryLimit: 실패한 Job 보관 개수
  • startingDeadlineSeconds: Job 시작 데드라인

실무 활용 예제 - 데이터베이스 백업

apiVersion: batch/v1
kind: CronJob
metadata:
  name: postgres-backup
spec:
  schedule: "0 2 * * *"  # 매일 새벽 2시
  timeZone: "Asia/Seoul"
  concurrencyPolicy: Forbid
  successfulJobsHistoryLimit: 7
  failedJobsHistoryLimit: 3
  jobTemplate:
    spec:
      activeDeadlineSeconds: 1800  # 30분 제한
      ttlSecondsAfterFinished: 86400  # 24시간 후 삭제
      template:
        spec:
          containers:
          - name: postgres-backup
            image: postgres:15
            command: ["/bin/bash", "-c"]
            args:
            - |
              BACKUP_FILE="backup-$(date +%Y%m%d-%H%M%S).sql"
              pg_dump $PGDATABASE > /tmp/$BACKUP_FILE
              aws s3 cp /tmp/$BACKUP_FILE s3://$S3_BUCKET/backups/
            env:
            - name: PGHOST
              value: "postgres-service"
            - name: PGDATABASE
              value: "myapp"
            - name: PGUSER
              valueFrom:
                secretKeyRef:
                  name: postgres-secret
                  key: username
            - name: PGPASSWORD
              valueFrom:
                secretKeyRef:
                  name: postgres-secret
                  key: password
          restartPolicy: OnFailure

한국 기업 사례

우아한형제들 기술블로그에서는 SpringBoot Batch와 CronJob을 활용하여 주소 검색 서버의 데이터를 주기적으로 업데이트하는 사례를 소개했습니다. 매일 새벽 시간에 전국 주소 데이터를 업데이트하여 사용자에게 최신 주소 정보를 제공합니다.

2. DaemonSets

DaemonSet은 클러스터의 모든 노드(또는 특정 노드)에 파드의 복사본을 실행하는 컨트롤러입니다. 로그 수집, 모니터링, 네트워킹 등의 시스템 레벨 작업에 주로 사용됩니다.

주요 특징

  • 각 노드에 정확히 하나의 파드 실행
  • 새 노드가 추가되면 자동으로 파드 스케줄링
  • 노드가 제거되면 해당 파드도 자동 삭제
  • 테인트된 노드에도 배포 가능 (톨러레이션 설정)

실제 사용 예제 - Fluentd 로그 수집

apiVersion: apps/v1
kind: DaemonSet
metadata:
  name: fluentd-elasticsearch
  namespace: kube-system
  labels:
    k8s-app: fluentd-logging
spec:
  selector:
    matchLabels:
      name: fluentd-elasticsearch
  template:
    metadata:
      labels:
        name: fluentd-elasticsearch
    spec:
      tolerations:
      # 마스터 노드에도 배포 허용
      - key: node-role.kubernetes.io/control-plane
        operator: Exists
        effect: NoSchedule
      containers:
      - name: fluentd-elasticsearch
        image: fluent/fluentd-kubernetes-daemonset:v1-debian-elasticsearch
        env:
        - name: FLUENT_ELASTICSEARCH_HOST
          value: "elasticsearch-logging"
        - name: FLUENT_ELASTICSEARCH_PORT
          value: "9200"
        resources:
          limits:
            memory: 200Mi
          requests:
            cpu: 100m
            memory: 200Mi
        volumeMounts:
        - name: varlog
          mountPath: /var/log
          readOnly: true
        - name: varlibdockercontainers
          mountPath: /var/lib/docker/containers
          readOnly: true
      terminationGracePeriodSeconds: 30
      volumes:
      - name: varlog
        hostPath:
          path: /var/log
      - name: varlibdockercontainers
        hostPath:
          path: /var/lib/docker/containers

한국 기업 사례

카카오 기술블로그에서는 쿠버네티스 네트워크 이슈를 해결하기 위해 network-node-manager라는 DaemonSet을 개발한 사례를 소개했습니다. 이 DaemonSet은 모든 노드에서 네트워크 상태를 모니터링하고 문제가 발생했을 때 자동으로 복구 작업을 수행합니다.

3. StatefulSets

StatefulSet은 상태를 가진 애플리케이션을 관리하기 위한 워크로드 오브젝트입니다. 데이터베이스, 메시지 큐, 분산 시스템 등에서 사용됩니다.

3.1 스테이트풀셋 사용하기

Deployment와의 주요 차이점

특징 Deployment StatefulSet

Pod 이름 랜덤 해시 (app-abc123) 순서있는 인덱스 (app-0, app-1)
네트워크 식별자 불안정 안정적 (DNS 이름 유지)
스토리지 공유 또는 없음 각 Pod별 전용 PVC
배포/삭제 순서 무작위 순차적 (0→1→2, 2→1→0)
업데이트 전략 동시 롤링 업데이트 순차적 업데이트

StatefulSet 기본 예제

apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: web
spec:
  serviceName: "nginx"
  replicas: 3
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx:1.21
        ports:
        - containerPort: 80
          name: web
        volumeMounts:
        - name: www
          mountPath: /usr/share/nginx/html
  volumeClaimTemplates:
  - metadata:
      name: www
    spec:
      accessModes: ["ReadWriteOnce"]
      storageClassName: "fast-ssd"
      resources:
        requests:
          storage: 10Gi
---
apiVersion: v1
kind: Service
metadata:
  name: nginx
  labels:
    app: nginx
spec:
  ports:
  - port: 80
    name: web
  clusterIP: None  # 헤드리스 서비스
  selector:
    app: nginx

3.2 스테이트풀셋과 퍼시스턴트 볼륨

StatefulSet의 핵심 기능 중 하나는 각 Pod가 고유한 영구 스토리지를 가질 수 있다는 것입니다.

volumeClaimTemplates 작성

volumeClaimTemplates:
- metadata:
    name: data
    labels:
      app: mysql
  spec:
    accessModes: ["ReadWriteOnce"]
    storageClassName: "gp3-encrypted"
    resources:
      requests:
        storage: 100Gi

클라우드별 StorageClass 예제

AWS EBS (GP3):

apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: gp3-encrypted
provisioner: ebs.csi.aws.com
parameters:
  type: gp3
  encrypted: "true"
  throughput: "250"
  iops: "3000"
volumeBindingMode: WaitForFirstConsumer
allowVolumeExpansion: true

Google Cloud Persistent Disk:

apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: fast-ssd
provisioner: pd.csi.storage.gke.io
parameters:
  type: pd-ssd
  replication-type: regional-pd
volumeBindingMode: WaitForFirstConsumer

실제 사용 예제 - MySQL Cluster

apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: mysql
spec:
  serviceName: mysql
  replicas: 3
  selector:
    matchLabels:
      app: mysql
  template:
    metadata:
      labels:
        app: mysql
    spec:
      initContainers:
      - name: init-mysql
        image: mysql:8.0
        command:
        - bash
        - "-c"
        - |
          set -ex
          # StatefulSet ordinal에 따라 서버 ID 생성
          [[ $(hostname) =~ -([0-9]+)$ ]] || exit 1
          ordinal=${BASH_REMATCH[1]}
          echo [mysqld] > /mnt/conf.d/server-id.cnf
          echo server-id=$((100 + $ordinal)) >> /mnt/conf.d/server-id.cnf
          
          # 첫 번째 Pod는 마스터, 나머지는 슬레이브
          if [[ $ordinal -eq 0 ]]; then
            cp /mnt/config-map/primary.cnf /mnt/conf.d/
          else
            cp /mnt/config-map/replica.cnf /mnt/conf.d/
          fi
        volumeMounts:
        - name: conf
          mountPath: /mnt/conf.d
        - name: config-map
          mountPath: /mnt/config-map
      containers:
      - name: mysql
        image: mysql:8.0
        env:
        - name: MYSQL_ROOT_PASSWORD
          valueFrom:
            secretKeyRef:
              name: mysql-secret
              key: root-password
        ports:
        - name: mysql
          containerPort: 3306
        volumeMounts:
        - name: data
          mountPath: /var/lib/mysql
          subPath: mysql
        - name: conf
          mountPath: /etc/mysql/conf.d
        resources:
          requests:
            cpu: 500m
            memory: 1Gi
        livenessProbe:
          exec:
            command: ["mysqladmin", "ping"]
          initialDelaySeconds: 30
          periodSeconds: 10
          timeoutSeconds: 5
        readinessProbe:
          exec:
            command: ["mysql", "-h", "127.0.0.1", "-e", "SELECT 1"]
          initialDelaySeconds: 5
          periodSeconds: 2
          timeoutSeconds: 1
      volumes:
      - name: conf
        emptyDir: {}
      - name: config-map
        configMap:
          name: mysql
  volumeClaimTemplates:
  - metadata:
      name: data
    spec:
      accessModes: ["ReadWriteOnce"]
      storageClassName: "fast-ssd"
      resources:
        requests:
          storage: 100Gi

3.3 리소스 정리

StatefulSet을 삭제할 때는 PVC(Persistent Volume Claim)가 자동으로 삭제되지 않으므로 별도 관리가 필요합니다.

PVC 보존 정책 (Kubernetes 1.23+)

spec:
  persistentVolumeClaimRetentionPolicy:
    whenDeleted: Retain    # StatefulSet 삭제 시 PVC 보존
    whenScaled: Delete     # 스케일 다운 시 PVC 삭제

수동 정리 방법

# StatefulSet 삭제
kubectl delete statefulset mysql

# 관련 PVC 조회
kubectl get pvc -l app=mysql

# PVC 수동 삭제
kubectl delete pvc data-mysql-0 data-mysql-1 data-mysql-2

# 또는 레이블 선택자로 일괄 삭제
kubectl delete pvc -l app=mysql

# PV 상태 확인
kubectl get pv

안전한 삭제 스크립트

#!/bin/bash
STATEFULSET_NAME="mysql"
NAMESPACE="default"

echo "Scaling down StatefulSet to 0..."
kubectl scale statefulset $STATEFULSET_NAME --replicas=0 -n $NAMESPACE

echo "Waiting for pods to terminate..."
kubectl wait --for=delete pod -l app=$STATEFULSET_NAME -n $NAMESPACE --timeout=300s

echo "Deleting StatefulSet..."
kubectl delete statefulset $STATEFULSET_NAME -n $NAMESPACE

echo "Deleting PVCs..."
kubectl delete pvc -l app=$STATEFULSET_NAME -n $NAMESPACE

echo "Cleanup completed!"

한국 기업 사례

카카오톡 캐싱 시스템

카카오 기술블로그에서는 StatefulSet을 활용하여 대규모 Redis 캐시 클러스터를 운영하는 사례를 소개했습니다:

  • 메모리 크기별 타입 분리 (1GB, 4GB, 16GB)
  • OnDelete 업데이트 전략으로 안전한 배포
  • hostNetwork를 통한 네트워크 오버헤드 최소화
  • 각 Pod별 전용 로컬 SSD 스토리지 활용

라인의 데이터 파이프라인

라인 엔지니어링 블로그에서는 Confluent Platform을 쿠버네티스 환경에서 StatefulSet으로 운영하여 대용량 데이터 파이프라인을 구축한 사례를 공유했습니다.

참고 자료