인시던트 아카이브¶
운영·CI·파이프라인 장애의 시간 역순 아카이브(append-only — 항목은 추가만, 기존 항목 수정·삭제 금지). 각 항목: 발생 → 원인 → 복구 → 재발방지 한 블록. 진단·복구 런북·배포 절차는 version-control, 인프라 구조는 infra.
패턴: 이 팀 인시던트의 다수는 온프레/소규모 호스트의 리소스·netfilter 한계(메모리·iptables)와 외부 환경 드리프트(러너 이미지·Docker 버전)에서 온다. 코드 버그보다 환경이 원인인 경우가 많아, 진단의 핵심은 "어느 레이어에서 끊겼나"를 좁히는 것.
2026-06-01 — 온프레 the-edu-staging DB 연결 크래시루프 (~4.5h)¶
발생 apidev.d-edu.site(온프레 staging 백엔드) 부팅 → DB 연결 타임아웃 → 종료 → --restart로 무한 재시작(RestartCount=48). 19:19 KST 시작, 23:52 복구. 데이터 손실 없음.
원인 Flyway/JPA/MySQL이 아니라, WSL2 호스트의 iptables FORWARD 체인에서 Docker ACCEPT 규칙이 소실되어 theedu_backend 네트워크의 컨테이너 간 통신이 전면 차단된 것이 근본 원인(ICMP까지 100% 손실). Connect timed out(SYN 무응답=패킷 드롭)이 Connection refused/Access denied가 아니라는 신호가 네트워크 경로 문제를 가리켰다. 구조적 유발 요인은 WSL2에서 여러 docker 스택/distro가 커널 netfilter를 공유해 경합한다는 점. 정확한 트리거 명령은 휘발성·손상 journald 탓에 추적 불가(메커니즘·환경 원인은 확정).
복구 sudo systemctl restart docker → Docker가 iptables 규칙 전체 재설치 → 컨테이너 자동 재기동(볼륨 보존), health 200·RestartCount=0 확인. (임시방편: iptables -P FORWARD ACCEPT)
재발방지 ① journald 영속화(다음 트리거 포착 — 즉시·무위험) ② Flyway connect-retries=5+Hikari connection-timeout=10s 보강(브랜치 머지 대기) + 진단/복구 스크립트(diagnose-db.sh/reconnect-db-network.sh) ③ 자가복구 워치독(docker-restart-only, 승인 대기)+다운 알림(MTTR↓) ④ 근본: WSL2 멀티 docker netfilter 경합 제거(데몬 단일화 또는 비-WSL 서버 이전) ⑤ 보조 live-restore. 진단 명령·전체 분석은 version-control 런북. (계승: 옛 teams/engineering/infra/incident-2026-06-01-onprem-db-crashloop 포스트모템)
2026-02-15 — CI 통합 테스트 전면 실패 (Testcontainers)¶
발생 GitHub Actions 통합 테스트가 일제히 실패. 코드 변경 없이 갑자기 깨짐.
원인 GitHub 러너 이미지 드리프트. 02-09 이후 ubuntu24 러너 이미지가 갱신되며 Docker가 28→29로 업. Docker 29가 지원 최소 API를 1.44로 올렸는데, Testcontainers는 기본 1.32를 호출 → "could not find a valid Docker environment". 우리 코드가 아니라 실행 환경 변화가 범인.
복구·재발방지 build.gradle test 태스크에 호출 API 버전을 명시: systemProperty 'api.version', '1.44'. 교훈: 러너 이미지는 우리 통제 밖에서 바뀐다 — "코드 안 바꿨는데 깨짐"은 외부 환경 드리프트를 먼저 의심.
2026-02-05 — 개발 서버 I/O Wait 마비 (EC2, ~4h)¶
발생 17:00~21:00 KST. Disk I/O 100%, CPU I/O Wait 100%, 모든 컨테이너 응답 불가.
원인 인과 사슬: 메모리 부족(1GB로 6개 컨테이너) → Swap 32% 사용(=Disk I/O) → Loki Checkpoint 폭주(정상 10초 → 294초, 29배) → Disk I/O 100% → 전 컨테이너 전파(I/O 큐 155개, Prometheus/cAdvisor 타임아웃). 추가로, 컨테이너 전환 시 systemd에 남아있던 옛 grafana/node_exporter 서비스를 disable하지 않아 재부팅 후 중복 기동 → 메모리 압박 가중.
복구 잔존 systemd 서비스 stop+disable(grafana-server, node_exporter) → Load Average 8.34→0.39, I/O Wait 91%→19%. 단, Swap만으로는 사양 한계를 못 푼다는 결론.
재발방지 인스턴스 업그레이드(t2.micro→t3.small)·디스크 gp2→gp3(★ 비용 낮음)·Loki checkpoint 간격↑·컨테이너 메모리 제한. 교훈: Swap>10%면 증설 신호, 1GB로 6컨테이너는 불가, Loki는 소형 호스트에 부적합, container_fs_io_current=I/O 대기 큐 관측 지표.
2026-05-25~26 — 위키 파이프라인 봇 연동 디버깅 (Stage1)¶
발생 Phase 3 dry-run 중 Slack→raw→wiki 반영 파이프라인의 d-edu-ddakari 봇 chat.postMessage가 실패(channel-not-found / scope).
원인·복구 봇이 대상 채널에 미초대 + chat:write scope 미부여 → 봇 채널 invite 후 scope 보강하여 재실행, raw 반영 검증 통과. 서비스 운영 장애가 아닌 내부 자동화 파이프라인 설정 이슈. 파이프라인 구조는 infra §3.
출처 (slack 스레드 5건 — 본 인시던트로 합성): raw/slack/ ▸ 2026-05-25 Phase-3 dry-run Stage1 통신 ▸ 2026-05-26 Stage1 d-edu-ddakari 봇 init ▸ 2026-05-26 Stage1 봇 채널 add 후 ▸ 2026-05-26 Stage1 재실행 chat:write ▸ 2026-05-26 wiki에 raw 반영. (파일명이 덤프 시 일부 잘림 — 카탈로그는 raw/slack/ 디렉토리.)
신규 인시던트는 이 줄 바로 위에 시간 역순으로 prepend하고, 동일 4블록 형식을 유지한다. 원본 로그·상세는 항상
raw/...로 링크.각 인시던트 원본·백엔드 work log·운영 아카이브(대치온/이상한 과외 백업 포함) 출처는 frontmatter
source참조.