Files
obsidian/history/2026-06-11-kr2-k3s-readonly-hang.md

60 lines
4.4 KiB
Markdown

---
date: 2026-06-11
topic: kr2 K3s control-plane 8h hang — Patroni failover 후 read-only DB로 kine 기동 wedge
areas:
- infra/compute/hosts/incus-kr2.md
- infra/data/postgresql-ha.md
tags: [history, incident, k3s, patroni, kine, postgresql]
---
K3s 점검 중 발견. incus-kr2 control-plane의 k3s.service가 2026-06-10 22:55부터 약 8시간 `activating` 상태로 멈춰 kr3 컨텍스트 API(100.119.109.41:6443) connection refused.
## 원인 체인
1. 2026-06-10 22:55경 **Patroni failover** 발생 (leader → postgres-2 / 10.100.3.185, incus-kr1). 전환 윈도우 동안 3개 Postgres 노드가 모두 read-only.
2. 마침 그 시점에 kr2 k3s가 (재)기동 중이었고, kine(pgx)이 설정된 노드 IP 3개(10.100.2.5 / 10.100.3.185 / 10.100.1.83)에 직접 연결 시도 → 전부 `ValidateConnect failed: read only connection`.
3. k3s가 `Configuring pgx database connection pooling` 로그 직후 **프로세스 hang** (PID 3580986, STAT=Ssl). 8시간 동안 추가 로그 0, SIGTERM 무시(systemd `deactivating`에서 대기).
4. Patroni는 이후 정상 회복(leader 선출 완료)됐지만 kr2 k3s는 자가복구 못 하고 wedge 유지.
## 영향
- 클러스터는 **kr1 control-plane으로 계속 서빙**(`/healthz` ok)되어 외부 영향 제한적. kr2만 API/스케줄 제외.
- 동반: `apisix/apisix-etcd-1`(kr1) 같은 22:55 이벤트로 etcd raft `commitTo` panic → CrashLoopBackOff. etcd quorum 2/3(hp1/hp2) 유지로 APISIX는 동작. → 씬(syn) 도메인, 별도 remediation 필요.
## 복구
```bash
ssh kaffa@100.119.109.41 # incus-kr2
sudo kill -9 3580986 # hang된 k3s PID 강제 종료 (SIGTERM 무시함)
sudo systemctl start k3s # 재기동 → 정상 leader(postgres-2)에 연결, 합류
```
- 복구 후 4노드 전부 Ready (kr1/kr2 control-plane, hp1/hp2 worker, v1.34.7+k3s1).
## 확정 근본 원인 (2026-06-11 정밀 분석)
1. 22:55:08 **Patroni가 잠깐 all-read-only** (DCS/etcd-nas 순간 블립으로 failsafe 추정. 현재 etcd-nas DCS·Patroni 모두 정상). postgres-2 patroni 로그에 명시적 failover 라인은 없음 → 클린 failover가 아니라 일시적 전원 read-only 윈도우.
2. 실행 중이던 kr2 k3s가 read-only로 **leader-election lease 갱신 실패**`leaderelection lost` → controller-manager 종료 → **k3s.service exit code=1**.
3. systemd가 재시작(Restart=always) → 아직 read-only 윈도우 → kine 기동이 `Configuring pgx pool`에서 hang.
4. **k3s.service `Type=notify` + `TimeoutStartSec=0`(무한)** 이라, 기동 중 READY 전송 전 hang하면 systemd가 **영원히 대기**(activating 8h). Restart=always는 프로세스가 안 죽으니 무용.
## 적용한 조치 (2026-06-11)
1. **k3s hang 자동복구** (핵심 재발방지): kr1·kr2에 systemd drop-in
`/etc/systemd/system/k3s.service.d/timeout-override.conf``TimeoutStartSec=300`.
→ 기동 hang 시 5분 후 systemd가 kill, Restart=always로 재시도. DB가 RW 될 때까지 5분 주기 self-heal. (8h 행 → 최대 ~5분)
- datastore-endpoint는 **변경하지 않음** (아래 주의 참조).
2. 즉시 복구: hang PID SIGKILL + `systemctl start k3s` → 4노드 Ready.
3. 동반 손상 `apisix-etcd-1`(kr1) 멤버: `etcdctl member remove` + 데이터 wipe + 파드 재기동으로 fresh 재합류(새 id `d2504d737a8e493f`). etcd 3/3 healthy 복구.
## ⚠️ 주의 — datastore를 HAProxy로 바꾸지 말 것
당초 [[2026-04-08-patroni-failover-incident]] 교훈("HAProxy 경유")을 떠올렸으나, **[[../infra/data/postgresql-ha|postgresql-ha]] 기준 HAProxy(192.168.9.1:5432)는 2026-04-16 폐기**되었고 현재 K3s kine은 **pgx multi-host(`target_session_attrs=read-write`) 3노드 직결이 정식 구성**이다(앱은 pgpool-II 경유). HAProxy는 현재 죽어있는 게 정상. → datastore-endpoint 변경은 정식 결정을 되돌리는 것이므로 금지. 문제는 endpoint가 아니라 **k3s 기동 hang 자가복구 부재**였고, 그것을 TimeoutStartSec로 해결함.
## 재발 패턴
- **3회째 동일 계열**: [[2026-04-08-patroni-failover-incident]], [[2026-05-23-kr1-k3s-stuck-cascade]], 본건.
- 남은 더 깊은 원인(선택): Patroni가 왜 순간 all-read-only가 됐는지(etcd-nas DCS 블립 빈도) — DCS 안정성 점검 시 근절 가능. 현재는 hang 자가복구로 영향(8h→5분) 최소화 완료.
## 참조
- [[../infra/data/postgresql-ha|postgresql-ha]] · [[../infra/compute/hosts/incus-kr2|incus-kr2]]