KubernetesJob을 자동으로 정리하는 기능 소개
Kubernetes는 ttlSecondsAfterFinished 필드를 통해 완료된 Job을 자동으로 정리하는 TTL(Time To Live) 메커니즘을 제공합니다.
apiVersion: batch/v1
kind: Job
metadata:
name: example-job
spec:
ttlSecondsAfterFinished: 100 # 완료 후 100초 뒤 자동 삭제
template:
spec:
containers:
- name: job-container
image: busybox
command: ["sh", "-c", "echo Hello && sleep 10"]
restartPolicy: Never
ttlSecondsAfterFinished: 0으로 설정하면 완료 즉시 삭제됩니다CronJob에서는 완료된 Job의 보관 개수를 제어할 수 있습니다.
apiVersion: batch/v1
kind: CronJob
metadata:
name: example-cronjob
spec:
schedule: "*/5 * * * *"
successfulJobsHistoryLimit: 3 # 성공한 Job 3개까지 보관
failedJobsHistoryLimit: 1 # 실패한 Job 1개까지 보관
jobTemplate:
spec:
ttlSecondsAfterFinished: 300 # 추가 안전장치
template:
spec:
containers:
- name: cronjob-container
image: busybox
command: ["sh", "-c", "date"]
restartPolicy: OnFailure
Go 클라이언트를 사용하여 Job을 생성하고 자동 정리를 설정하는 예제입니다.
package main
import (
"context"
"fmt"
batchv1 "k8s.io/api/batch/v1"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/client-go/kubernetes"
"k8s.io/client-go/rest"
)
func createJobWithTTL(clientset *kubernetes.Clientset) error {
ttlSeconds := int32(100)
job := &batchv1.Job{
ObjectMeta: metav1.ObjectMeta{
Name: "auto-cleanup-job",
Namespace: "default",
},
Spec: batchv1.JobSpec{
TTLSecondsAfterFinished: &ttlSeconds,
Template: corev1.PodTemplateSpec{
Spec: corev1.PodSpec{
Containers: []corev1.Container{
{
Name: "worker",
Image: "busybox",
Command: []string{"sh", "-c", "echo Processing && sleep 30"},
},
},
RestartPolicy: corev1.RestartPolicyNever,
},
},
},
}
result, err := clientset.BatchV1().Jobs("default").Create(
context.TODO(),
job,
metav1.CreateOptions{},
)
if err != nil {
return fmt.Errorf("failed to create job: %v", err)
}
fmt.Printf("Job created: %s\n", result.Name)
return nil
}
from kubernetes import client, config
def create_job_with_ttl():
config.load_kube_config()
batch_v1 = client.BatchV1Api()
job = client.V1Job(
api_version="batch/v1",
kind="Job",
metadata=client.V1ObjectMeta(name="python-auto-cleanup-job"),
spec=client.V1JobSpec(
ttl_seconds_after_finished=100,
template=client.V1PodTemplateSpec(
spec=client.V1PodSpec(
containers=[
client.V1Container(
name="worker",
image="busybox",
command=["sh", "-c", "echo Processing && sleep 30"]
)
],
restart_policy="Never"
)
)
)
)
batch_v1.create_namespaced_job(namespace="default", body=job)
print("Job created with TTL")
더 복잡한 정리 로직이 필요한 경우 커스텀 컨트롤러를 구현할 수 있습니다.
package main
import (
"context"
"time"
batchv1 "k8s.io/api/batch/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/client-go/kubernetes"
"k8s.io/client-go/tools/cache"
"k8s.io/client-go/util/workqueue"
)
type JobCleanupController struct {
clientset *kubernetes.Clientset
maxAge time.Duration
checkInterval time.Duration
}
func (c *JobCleanupController) Run(ctx context.Context) {
ticker := time.NewTicker(c.checkInterval)
defer ticker.Stop()
for {
select {
case <-ticker.C:
c.cleanupOldJobs(ctx)
case <-ctx.Done():
return
}
}
}
func (c *JobCleanupController) cleanupOldJobs(ctx context.Context) {
jobs, err := c.clientset.BatchV1().Jobs("").List(ctx, metav1.ListOptions{})
if err != nil {
return
}
now := time.Now()
for _, job := range jobs.Items {
// 완료된 Job 확인
if job.Status.CompletionTime == nil {
continue
}
completionTime := job.Status.CompletionTime.Time
age := now.Sub(completionTime)
// 설정된 시간보다 오래된 경우 삭제
if age > c.maxAge {
deletePolicy := metav1.DeletePropagationForeground
err := c.clientset.BatchV1().Jobs(job.Namespace).Delete(
ctx,
job.Name,
metav1.DeleteOptions{
PropagationPolicy: &deletePolicy,
},
)
if err == nil {
// 로깅
}
}
}
}
ttlSecondsAfterFinished: 300 (5분) - 빠른 정리ttlSecondsAfterFinished: 3600 (1시간) - 디버깅 가능ttlSecondsAfterFinished: 86400 (24시간) - 충분한 감사 기간apiVersion: batch/v1
kind: Job
metadata:
name: monitored-job
labels:
app: data-processor
environment: production
cleanup-policy: auto
spec:
ttlSecondsAfterFinished: 3600
template:
metadata:
labels:
app: data-processor
spec:
containers:
- name: processor
image: your-image:latest
restartPolicy: Never
클러스터에서 TTL Controller가 활성화되어 있는지 확인합니다.
# kube-controller-manager 로그 확인
kubectl logs -n kube-system kube-controller-manager-<node-name> | grep ttl
# Feature Gate 확인 (1.34에서는 GA이므로 기본 활성화)
kubectl get pod -n kube-system kube-controller-manager-<node> -o yaml | grep feature-gates
# 완료된 Job 조회
kubectl get jobs --field-selector status.successful=1
# 7일 이상 된 완료된 Job 삭제 (kubectl 1.28+)
kubectl delete jobs --field-selector status.successful=1 \
--all-namespaces \
--dry-run=client
ttlSecondsAfterFinished는 Job이 완료(Finished) 상태가 되면 작동합니다. 여기서 완료란:
apiVersion: batch/v1
kind: Job
metadata:
name: test-job
spec:
ttlSecondsAfterFinished: 100 # 아래 모든 경우에 적용
template:
spec:
containers:
- name: worker
image: busybox
command: ["sh", "-c", "exit 0"] # 또는 exit 1
restartPolicy: Never
✅ 성공 (Completed): status.succeeded > 0
✅ 실패 (Failed): status.failed > 0
✅ 수동 종료: kubectl delete로 중단된 경우도 완료로 간주
❌ 실행 중 (Running): Pod가 아직 실행 중
❌ 대기 중 (Pending): Pod가 스케줄링 대기 중
❌ Unknown: 상태를 알 수 없는 경우
# Job 생성 - 성공 케이스
cat <<EOF | kubectl apply -f -
apiVersion: batch/v1
kind: Job
metadata:
name: success-job
spec:
ttlSecondsAfterFinished: 60
template:
spec:
containers:
- name: success
image: busybox
command: ["sh", "-c", "echo Success && exit 0"]
restartPolicy: Never
EOF
# Job 생성 - 실패 케이스
cat <<EOF | kubectl apply -f -
apiVersion: batch/v1
kind: Job
metadata:
name: failed-job
spec:
ttlSecondsAfterFinished: 60
backoffLimit: 2
template:
spec:
containers:
- name: failure
image: busybox
command: ["sh", "-c", "echo Failed && exit 1"]
restartPolicy: Never
EOF
# 상태 모니터링
watch kubectl get jobs,pods
60초 후 두 Job 모두 자동으로 삭제됩니다.
문제: Job과 Pod가 무한정 누적되어 etcd 부하 증가
# TTL 없이 6개월 운영 시
$ kubectl get jobs --all-namespaces | wc -l
15847 # 😱 수만 개의 완료된 Job
# etcd 데이터베이스 크기 증가
$ ETCDCTL_API=3 etcdctl endpoint status --write-out=table
+------------------+------------------+-----------+---------+
| ENDPOINT | ID | DB SIZE | LEADER |
+------------------+------------------+-----------+---------+
| 127.0.0.1:2379 | 8e9e05c52164694d | 8.2 GB | true | # 😱
+------------------+------------------+-----------+---------+
해결: TTL로 자동 정리
# TTL 사용 시
$ kubectl get jobs --all-namespaces | wc -l
45 # ✅ 최근 24시간 내 Job만 유지
# etcd 크기 감소
DB SIZE: 2.1 GB # ✅ 70% 감소
Before (수동 정리):
# 매주 크론잡으로 수동 정리 필요
0 2 * * 0 kubectl delete jobs --field-selector status.successful=1 -A
# 또는 스크립트 작성
#!/bin/bash
for ns in $(kubectl get ns -o name | cut -d'/' -f2); do
kubectl delete jobs --field-selector status.successful=1 -n $ns
done
After (TTL 사용):
# Job 정의에 한 줄만 추가하면 끝
spec:
ttlSecondsAfterFinished: 86400 # 끝!
조직 전체 표준 정책 설정 가능:
# ArgoCD/Flux로 배포되는 모든 Job에 자동 적용
apiVersion: v1
kind: ConfigMap
metadata:
name: job-defaults
data:
ttl-dev: "300" # 개발: 5분
ttl-staging: "3600" # 스테이징: 1시간
ttl-prod: "86400" # 프로덕션: 24시간
상황에 맞게 TTL 조정:
# 프로덕션 - 충분한 디버깅 시간
apiVersion: batch/v1
kind: Job
metadata:
name: payment-processor
spec:
ttlSecondsAfterFinished: 86400 # 24시간 보관
template:
spec:
containers:
- name: processor
image: payment-app:v1
# 개발 환경 - 빠른 정리
apiVersion: batch/v1
kind: Job
metadata:
name: dev-test-job
spec:
ttlSecondsAfterFinished: 300 # 5분만 보관
# 각 팀이 자신의 정책 적용 가능
---
# Team A - 보수적
apiVersion: batch/v1
kind: Job
metadata:
name: critical-job
namespace: team-a
spec:
ttlSecondsAfterFinished: 604800 # 7일
---
# Team B - 공격적
apiVersion: batch/v1
kind: Job
metadata:
name: temp-job
namespace: team-b
spec:
ttlSecondsAfterFinished: 0 # 즉시 삭제
측정 가능한 개선:
# TTL 적용 전 - API 요청 수
$ kubectl top pods -n kube-system | grep apiserver
kube-apiserver-master-1 850m 2Gi
# TTL 적용 후 - 완료된 리소스 감소로 LIST 요청 부하 감소
kube-apiserver-master-1 620m 1.5Gi # ✅ 약 27% CPU 감소
일부 클라우드 프로바이더는 etcd 크기나 API 요청 수로 과금:
# 예: EKS 비용 절감
- etcd 스토리지: 8GB → 2GB (월 $30 절감)
- API 요청: 30% 감소 (월 $50 절감)
총 월 $80 절감 (연간 $960)
apiVersion: batch/v1
kind: CronJob
metadata:
name: daily-etl
spec:
schedule: "0 2 * * *" # 매일 새벽 2시
successfulJobsHistoryLimit: 3 # 최근 3개 성공 보관
failedJobsHistoryLimit: 1 # 최근 1개 실패 보관
jobTemplate:
spec:
ttlSecondsAfterFinished: 172800 # 추가로 2일간 보관
template:
spec:
containers:
- name: etl
image: data-pipeline:latest
restartPolicy: OnFailure
장점:
# 수천 개의 Job이 생성되는 경우
apiVersion: batch/v1
kind: Job
metadata:
name: batch-processor-{{ .ItemID }}
spec:
ttlSecondsAfterFinished: 3600 # 1시간 후 삭제
template:
spec:
containers:
- name<: worker
image: batch-worker:latest
장점:
TTL Controller 동작 모니터링:
# Prometheus 메트릭
apiVersion: v1
kind: ConfigMap
metadata:
name: prometheus-rules
data:
job-cleanup.yaml: |
groups:
- name: job_cleanup
rules:
- alert: JobTTLNotWorking
expr: count(kube_job_complete{job_ttl_seconds_after_finished!=""} == 1) > 100
for: 1h
annotations:
summary: "완료된 Job이 100개 이상 누적됨"
이처럼 TTL Controller는 운영 효율성, 리소스 관리, 자동화 측면에서 큰 장점을 제공합니다. 특히 100+ 노드 규모의 대형 클러스터에서는 필수적인 기능입니다.