StatefulSet 파드 안에서 주기 작업을 돌려야 할 때 사이드카로 cron 데몬을 넣는 방식과 별도 CronJob 리소스를 쓰는 방식 가운데 하나를 고른다. 기본값은 CronJob 이다. 사이드카 cron 은 작업이 파드의 수명 주기 또는 그 파드에만 붙어 있는 PVC 와 강하게 묶여 있을 때만 정당화된다.
| 작업 성격 | 적합한 방식 |
|---|---|
| DB 유지보수(VACUUM, 통계 갱신)처럼 클러스터 전체에 한 번 | CronJob |
| 파드 로컬 캐시·임시 파일 정리처럼 replica 마다 따로 | 사이드카 cron |
| 전역 배치·리포트 생성 | CronJob |
| replica 중 하나만 해야 하는 작업 | 사이드카 cron + 리더 선출 또는 ordinal 제한 |
CronJob 은 스케줄링 · 재시도 · 실행 이력 · 동시 실행 정책(concurrencyPolicy) · 데드라인(startingDeadlineSeconds) 을 Kubernetes 가 관리해 준다. 사이드카 cron 은 이 모든 것을 직접 구현해야 한다. 그래서 "파드 안의 로컬 상태를 반드시 건드려야 한다"는 근거가 없으면 CronJob 을 쓴다.
스크립트는 ConfigMap 으로 주입하고, cron 데몬은 포그라운드로 띄운다. 백그라운드로 띄우면 컨테이너의 PID 1 이 즉시 끝나면서 파드가 재시작 루프에 빠진다.
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: example-sts
spec:
serviceName: example
replicas: 3
selector:
matchLabels:
app: example
template:
metadata:
labels:
app: example
spec:
volumes:
- name: cron-script
configMap:
name: cron-script
defaultMode: 0755
containers:
- name: app
image: nginx
- name: cron-sidecar
image: alpine:3.20
command: ["/bin/sh", "-c"]
args:
- |
echo "*/1 * * * * /scripts/job.sh >> /proc/1/fd/1 2>&1" > /etc/crontabs/root
exec crond -f -l 2
resources:
limits:
cpu: 100m
memory: 64Mi
volumeMounts:
- name: cron-script
mountPath: /scripts
apiVersion: v1
kind: ConfigMap
metadata:
name: cron-script
data:
job.sh: |
#!/bin/sh
date
replica 가 셋이면 cron 도 셋이 돈다. 전역에 한 번만 실행돼야 하는 작업이면 반드시 걸러야 한다. StatefulSet 은 파드 이름이 <name>-<ordinal> 로 고정되므로 ordinal 0 만 실행하는 방식이 가장 단순하다.
[ "${HOSTNAME##*-}" = "0" ] || exit 0
다만 이 방식은 ordinal 0 파드가 내려가 있으면 그 주기를 통째로 건너뛴다. 실행 누락이 허용되지 않으면 Lease 기반 리더 선출을 쓰거나 CronJob 으로 옮긴다.
로그는 파일 대신 표준 출력으로 보낸다. cron 은 기본적으로 자식 출력을 메일로 보내려 하므로 위 예처럼 PID 1 의 stdout 으로 리다이렉트해야 kubectl logs 에 잡힌다. 파일로 남기면 그 파일을 위한 PVC 와 로테이션이 또 필요해진다.
사이드카 컨테이너가 죽으면 파드 전체가 재시작 정책을 따르므로 본 서비스의 가용성에 영향을 준다. 반드시 CPU·메모리 제한을 걸고, 무거운 작업은 사이드카에 두지 않는다.