이거 모르는 분 의외로 많더라. 어제도 사내 슬랙에서 비슷한 질문이 올라왔다. "etcd defrag CronJob 돌리는데 가끔 API 서버가 잠깐 죽어요." 그럴 만했다.
오늘은 짧게 한 가지만 정리한다. etcd defragmentation을 CronJob으로 자동화할 때 빠뜨리기 쉬운 포인트.
compaction과 defrag는 다른 거다
먼저 용어부터. 둘이 같은 줄 알고 쓰는 사람이 꽤 있다.
- compaction: 오래된 revision을 논리적으로 지운다. 디스크 파일은 안 줄어든다. 자동으로 돌릴 수 있다 (
--auto-compaction-mode=periodic --auto-compaction-retention=8h같은 식). kube-apiserver가 5분마다 자동 compaction을 호출하기도 한다. - defragmentation: 실제로 db 파일을 재작성해서 디스크 공간을 회수한다. 이건 자동이 아니다. 그리고 돌리는 동안 그 멤버는 블로킹된다.
여기서 핵심. compaction을 아무리 잘 돌려도 db 파일은 안 줄어든다. 디스크 사용량 보고 "왜 안 줄지?" 하다가 알게 되는 사람이 많다.
흔한 실수 — 동시에 다 돌린다
CronJob 하나로 모든 etcd 멤버를 한꺼번에 defrag하는 매니페스트를 종종 본다. 보통 이런 식이다.
apiVersion: batch/v1
kind: CronJob
metadata:
name: etcd-defrag
spec:
schedule: "0 3 * * 0"
jobTemplate:
spec:
template:
spec:
containers:
- name: defrag
image: quay.io/coreos/etcd:v3.5.21
command:
- /bin/sh
- -c
- |
etcdctl --endpoints=$ENDPOINTS \
--cacert=/certs/ca.crt --cert=/certs/client.crt --key=/certs/client.key \
defrag --cluster
defrag --cluster 플래그. 이게 함정이다. 모든 멤버에 순차적으로 보내긴 하는데, 각 멤버가 defrag 중에는 writes를 못 받는다. 3노드 클러스터에서 leader가 defrag 중이면 그 시간 동안 quorum이 흔들린다. db가 크면 (5GB+) 멤버 하나당 수십 초 멈출 수 있다.
운 좋으면 API 서버가 timeout만 좀 나고 끝난다. 운 나쁘면 controller-manager의 leader election이 깨지고 그 사이에 HPA나 GC가 멈춘다. 우리 팀에서도 한번 겪었다. 새벽 3시 alert에 잠 깬 게 누구였더라.
멤버별로 시간을 흩뿌리자
그래서 우리는 멤버마다 CronJob을 분리해서 시간을 5분씩 띄워뒀다. follower부터 먼저, leader는 마지막.
# member-0: 03:00, member-1: 03:10, member-2: 03:20
schedule: "0 3 * * 0" # 멤버마다 다르게
concurrencyPolicy: Forbid
successfulJobsHistoryLimit: 3
failedJobsHistoryLimit: 3
# command 부분만:
- |
set -e
# 1) 이 멤버가 leader면 step down
IS_LEADER=$(etcdctl --endpoints=$THIS_ENDPOINT endpoint status --write-out=json | jq '.[0].Status.leader == .[0].Status.header.member_id')
if [ "$IS_LEADER" = "true" ]; then
etcdctl --endpoints=$THIS_ENDPOINT move-leader $OTHER_MEMBER_ID
sleep 5
fi
# 2) defrag는 단일 endpoint로
etcdctl --endpoints=$THIS_ENDPOINT defrag
# 3) 알람 해제
etcdctl --endpoints=$THIS_ENDPOINT alarm disarm
마지막 alarm disarm도 빼먹지 말자. db quota를 넘어서 NOSPACE 알람이 떠 있는 상태라면 defrag 후에 직접 해제해야 writes가 다시 들어간다.
한 가지 더 — etcd-defrag 도구
최근에 etcd 메인테이너 중 한 명이 만든 etcd-defrag 도구가 꽤 잘 만들어졌다 (ahrtr/etcd-defrag). leader-last 순서 자동, fragmentation ratio 기반 조건부 실행 같은 게 빌트인이다. 직접 쉘 스크립트 짜는 것보다 이걸 컨테이너로 말아서 CronJob 돌리는 게 깔끔하다.
etcd-defrag \
--endpoints=... \
--cacert=... --cert=... --key=... \
--defrag-rule="dbQuotaUsage > 0.5 && dbSize - dbSizeInUse > 200*1024*1024"
위 룰은 "쿼터의 50% 이상 차 있고 단편화로 인한 낭비가 200MB 초과일 때만 실행". 매주 무지성으로 돌리는 것보단 이쪽이 안전하다.
추가로 신경 쓸 것
etcd_mvcc_db_total_size_in_bytes와 etcd_mvcc_db_total_size_in_use_in_bytes 두 지표를 Grafana에 띄워두자. 차이가 벌어지면 단편화가 쌓이고 있다는 뜻이다. quota의 70% 넘기 전에 알람.
오늘은 여기까지. 짧게만 정리하려고 했는데 또 길어졌네.
'IT > Kubernets' 카테고리의 다른 글
| ServiceAccount projected token 만료로 새벽 호출 — 1년 짜리 토큰을 캐싱한 SDK 이야기 (0) | 2026.05.19 |
|---|---|
| Pod 시작이 느릴 때, fsGroupChangePolicy 한 줄만 바꿔보자 (1) | 2026.05.19 |
| Velero로 EKS 백업/복구 자동화하기 (0) | 2026.05.16 |
| kubectl debug --target 플래그, 이거 모르는 분 꽤 많더라 (0) | 2026.05.16 |
| Cilium vs Calico — 6개월 검토하고 우리가 내린 결론 (0) | 2026.05.16 |