삽질기 9

CronJob startingDeadlineSeconds 잘못 만졌다가 새벽 배치가 3일간 안 돌던 이야기

지난주 화요일 오전 10시쯤, 데이터팀 슬랙에 핑이 왔다. "혹시 정산 배치 어제도 안 돈 것 같은데, 뭐 바뀐 거 있어요?" 처음엔 별 생각 없이 로그부터 봤다. 어제, 그저께, 그그저께. 세 번 다 스케줄 시각에 Job이 아예 생성되지 않은 상태였다. Pod가 실패한 게 아니라 Job 자체가 없었다. 이거 뭐지, 하는 마음으로 CronJob describe를 걸었다.문제 상황문제의 CronJob은 매일 새벽 3시에 도는 정산 집계 배치였다. schedule은 0 3 * * *, concurrencyPolicy는 Forbid. 원래 잘 돌던 놈이었는데, 일주일쯤 전에 내가 매니페스트를 손댔다. 이유는 이랬다.기존 설정은 startingDeadlineSeconds가 없었다. 배치가 오래 걸리는 날에 다음..

IT/Kubernets 2026.08.23

새벽에 GHA self-hosted runner가 디스크로 뻗은 이야기

지난주 화요일 새벽 3시 반쯤, 슬랙 알림에 깼다. "CI 전체 실패 중" 이라는 온콜 메시지. 눈 비비고 노트북 열었더니 GitHub Actions 화면이 온통 빨간색이었다. 로그를 열어보니 다들 같은 메시지를 뱉고 있었다.No space left on device우리 팀은 self-hosted runner를 EC2 위에 올려서 쓴다. 처음 세팅했을 때부터 "언젠가 디스크 문제 터진다"는 거 알고 있었는데, 대응을 미루다가 결국 새벽에 얻어맞은 케이스였다. 그날 겪은 삽질을 정리해둔다.상황우리 러너 구성은 대략 이랬다.c6i.2xlarge EC2 3대, ephemeral 아니고 long-runninggp3 100GB 루트 볼륨 하나Docker in Docker로 이미지 빌드/테스트하루 평균 400개 정..

IT/CI CD 2026.08.15

Terraform state lock 삽질 노트 — DynamoDB 걷어내고 use_lockfile로 옮긴 이야기

지난주 금요일 저녁, 슬랙에서 얼굴이 화끈거리는 알림이 하나 왔다. "누가 지금 prod 계정에 terraform apply 돌리고 있어요?" 확인해 보니 아무도 돌리고 있지 않았다. 근데 state lock은 걸려 있었다. terraform-state-lock DynamoDB 테이블에 유령 락 하나가 남아 있었던 거다. 이런 걸 마주칠 때마다 매번 force-unlock으로 때웠는데, 이번엔 좀 다르게 해결해보고 싶었다. 마침 팀 내부에서 "DynamoDB 이거 언제까지 붙이고 있을 거냐"는 얘기가 몇 주째 돌고 있었고, Terraform 1.11에서 S3 네이티브 락킹이 GA로 승격된 지도 꽤 됐다.그동안 DynamoDB가 왜 있었나우리 팀 backend 설정은 5년 넘게 이 모양이었다.terrafo..

IT/IaC 2026.08.12

PgBouncer transaction pool로 갈아탄 다음 날 새벽에 벌어진 일

session pooling에서 transaction pooling으로 갈아탄 지 이틀째 되던 날 새벽 2시 반, 폰이 울렸다. 결제 API의 P99가 800ms를 넘겼다는 알람. 침대에서 노트북을 여는 순간 이미 뭐가 원인인지 짐작이 갔다. prepared statement 다.왜 갈아탔나우리 팀에서 PgBouncer는 오래전부터 session pool로 돌고 있었다. 백엔드가 Django였고, connection이 세션 내내 유지되는 게 편했다. 그런데 최근 서비스가 커지면서 DB 커넥션 수가 감당이 안 됐다. RDS 앞단 PgBouncer에 client는 3천이 넘게 붙는데 server 커넥션은 200~300 정도로 억제하고 싶었다. session pooling에서는 client 한 명이 붙어있는 ..

IT/DB 운영 2026.07.31

cert-manager 무한 재발급 루프에 걸린 새벽 이야기

cert-manager 무한 재발급 루프에 걸린 새벽 이야기새벽 3시. 폰이 울린다. 프로덕션 클러스터의 API 게이트웨이 인증서가 만료 2시간 전이라는 알림. 이상하다. cert-manager가 알아서 갱신했어야 정상인데. 눈을 비비면서 노트북을 열었다.처음엔 그냥 일시적인 문제인 줄 알았다kubectl get certificates -A 를 쳐봤다. Ready=False. kubectl describe로 이벤트를 보니 CertificateRequest가 계속 생성되고, 몇 초 뒤에 다시 실패하고, 또 다시 만들고... 초당 한 개꼴로 새 CR이 찍히고 있었다.$ kubectl get certificaterequests -n gateway | wc -l 38473847개. 헛웃음이 나왔다. 이 ..

IT/Kubernets 2026.07.19

BuildKit cache mount 하나 잘못 붙였다가 CI가 두 배 느려진 이야기

지난 주 화요일 아침에 백엔드 팀장이 슬랙 DM으로 물어봤다. "요즘 CI가 왜 이렇게 느려요? 예전에는 8분이면 됐는데 요새는 20분 넘게 걸리는 것 같은데." 확인해보니 정말 그랬다. Go 서비스 하나의 build job이 12분에서 19분으로 늘어나 있었다. 뭐 변경한 것도 없는데.내가 저 CI를 마지막으로 만진 게 6월 초였다. --mount=type=cache를 붙여서 Go module 다운로드를 캐싱하도록 바꿨다. 그때는 확실히 빨라졌다. 첫 빌드가 14분, 두 번째부터는 5분대. 팀 슬랙에 자랑까지 했다. 그런데 한 달 반이 지난 지금은 20분이 나오고 있었다.처음엔 러너 탓인 줄 알았다첫 번째 추측은 GitHub Actions runner가 지역별로 느려졌나 하는 것이었다. 요새 self-..

IT/컨테이너 2026.07.14

CoreDNS ndots:5 때문에 P99가 800ms 튀던 이야기

지난주에 진짜 며칠을 날렸다. 결론부터 말하면 범인은 ndots:5 였는데, 이 얘기 어디선가 다들 한 번씩은 들어봤을 거다. 근데 막상 우리 클러스터에서 실제로 터지니까 원인 찾는 데 이틀이 걸렸다. 이번엔 그 과정에 대한 기록이다.증상: 새벽 3시에 켜진 페이지노드 40대 규모의 EKS 클러스터에서 결제 관련 서비스 P99 레이턴시가 갑자기 튀기 시작했다. 평소엔 60ms 언저리였는데, 어느 순간부터 800ms까지 스파이크가 찍힌다. 새벽 3시에 폰이 울려서 잠이 확 깼다.처음 든 생각은 "또 RDS인가"였다. 요즘 우리 팀에선 뭐만 느려지면 일단 DB부터 의심하는 게 습관이 됐거든. 근데 커넥션 풀도, slow query 로그도, RDS CloudWatch도 다 깔끔했다. 애플리케이션 로그를 뒤져봐..

IT/Kubernets 2026.07.08

Cilium kube-proxy replacement 갈아탔다가 LoadBalancer 클라이언트 IP 다 날린 이야기

이번 분기 OKR 중에 "데이터플레인 통합" 항목이 하나 있었다. 우리는 그동안 kube-proxy(iptables 모드) + Calico CNI 조합을 써왔는데, 네트워크폴리시 관측이 약하고 iptables 룰이 노드당 8천 줄을 넘기 시작하면서 syncProxyRules 지연이 P95 기준 1.2초까지 튀는 날이 가끔 있었다. 다른 팀이 먼저 Cilium 으로 갈아탔길래, 우리도 다음 주에 도입하기로 했다.결론부터 말하면 도입은 했다. 그런데 도입 첫날 새벽 2시에 보안팀한테 "audit log 의 source IP 가 전부 노드 IP 로 찍히는데 뭔가 잘못된 거 아니냐" 라는 메시지를 받았고, 거기서부터 24시간이 좀 정신없었다.1차 시도 — kubeProxyReplacement=true 만 켜고 ..

IT/Kubernets 2026.06.30

KEDA SQS scaler 도입했다가 thrashing에 한참 데인 이야기

지난달에 SQS 기반 워커 파드를 KEDA로 옮겼다. HPA의 CPU 메트릭만으로는 큐가 쌓일 때 늦게 반응하는 게 계속 거슬려서, 큐 길이로 직접 스케일하는 게 자연스러워 보였다. KEDA는 2.19가 최근에 떨어졌고(2026-02), SQS scaler에 scaleOnDelayed 같은 옵션도 정리돼 있어서 큰 고민 없이 시작했는데, 정작 일주일 동안 새벽에 두 번 호출되고 나서야 정신을 차렸다. 그 과정 정리.시작은 정상이었다워크로드는 단순하다. 외부 이벤트 → SQS → 워커 파드(Go 단일 바이너리)가 메시지 하나씩 받아 처리. 평소엔 큐가 비어 있고, 1시간 단위로 큐가 수만 건씩 쌓이는 burst 패턴이다. 한 메시지 처리에 평균 2초, P99 8초.처음 달았던 ScaledObject는 거의..

IT/Kubernets 2026.04.29