근묵자흑
쿠버네티스 파드를 사용하는 주요 오브젝트들 본문
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으로 운영하여 대용량 데이터 파이프라인을 구축한 사례를 공유했습니다.
참고 자료
'k8s' 카테고리의 다른 글
Kubernetes API Server : 코드 레벨에서 이해하는 내부 동작 원리 (0) | 2025.07.03 |
---|---|
Kubernetes 모니터링 (0) | 2025.06.22 |
커스텀 리소스와 컨트롤러 - CR & CRD (1) | 2025.06.08 |
Kubernetes Admission Controller (1) | 2025.06.01 |
쿠버네티스 애플리케이션 배포를 위한 고급 설정: 자원 관리, 스케줄링, 그리고 배포 전략 (1) | 2025.06.01 |