docs: A-1 — sessions survive a JGroups partition but logout invalidation does not
Cutting TCP 7800 leaves cross-node refresh working (200), confirming sessions travel through PostgreSQL rather than the cluster transport. Logout is the opposite: the database row is deleted but the other node answers from its stale local cache, so the A-0 conclusion that invalidation rides the database is corrected here. Two things the plan did not anticipate: a NetworkPolicy cannot sever an established connection because conntrack accepts it before policy evaluation, and Keycloak reports the partition through its readiness probe so the split node removes itself from the Service. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
9c3cde457e
commit
2a98ef1090
@@ -0,0 +1,13 @@
|
||||
=== [기준선 1] 클러스터 뷰 — 양쪽 파드의 마지막 ISPN000094 ===
|
||||
keycloak-0: [keycloak-1-48749(v=16.0.12)|5] (2) [keycloak-1-48749(v=16.0.12), keycloak-0-30843(v=16.0.12)]
|
||||
keycloak-1: [keycloak-1-48749(v=16.0.12)|5] (2) [keycloak-1-48749(v=16.0.12), keycloak-0-30843(v=16.0.12)]
|
||||
|
||||
=== [기준선 2] JGROUPS_PING — 디스커버리 등록 ===
|
||||
name | ip | coord | coordinated_by
|
||||
------------------+-----------------+-------+---------------------------------------------
|
||||
keycloak-0-30843 | 10.42.1.43:7800 | f | uuid://00000000-0000-0000-0000-000000000007
|
||||
keycloak-1-48749 | 10.42.0.35:7800 | t | uuid://00000000-0000-0000-0000-000000000007
|
||||
(2 rows)
|
||||
|
||||
=== [기준선 3] 기존 NetworkPolicy ===
|
||||
No resources found in keycloak-lab namespace.
|
||||
@@ -0,0 +1,17 @@
|
||||
=== [대조군] 차단 전 — keycloak-0 로그인 → keycloak-1 에서 refresh ===
|
||||
sid tAWs2gCPr6SOcD4jDR9-_CzB
|
||||
keycloak-1 에서 refresh: 200
|
||||
|
||||
=== [기준선 4] JGroups 지표 — 양쪽 노드 ===
|
||||
--- K0 (10.42.1.43) ---
|
||||
vendor_jgroups_stats_bytes_sent_total 31476.0
|
||||
vendor_jgroups_merge3_get_num_merge_events 0.0
|
||||
vendor_jgroups_merge3_get_views 0.0
|
||||
vendor_jgroups_fd_sock2_get_num_suspected_members 0.0
|
||||
vendor_jgroups_nakack2_get_xmit_table_missing_messages 0.0
|
||||
--- K1 (10.42.0.35) ---
|
||||
vendor_jgroups_merge3_get_views 0.0
|
||||
vendor_jgroups_stats_bytes_sent_total 126765.0
|
||||
vendor_jgroups_nakack2_get_xmit_table_missing_messages 0.0
|
||||
vendor_jgroups_fd_sock2_get_num_suspected_members 0.0
|
||||
vendor_jgroups_merge3_get_num_merge_events 0.0
|
||||
@@ -0,0 +1,8 @@
|
||||
=== 차단 적용 ===
|
||||
networkpolicy.networking.k8s.io/a1-block-jgroups-transport created
|
||||
a1-block-jgroups-transport map[app:keycloak]
|
||||
|
||||
적용 시각: 11:38:08
|
||||
=== FD_SOCK2 가 상대를 의심하기까지 기다린다 (15초 간격, 최대 3분) ===
|
||||
+15초 suspected(k0 k1) =
|
||||
→ 변화 감지
|
||||
@@ -0,0 +1,26 @@
|
||||
차단 경과: 11:38:51 (적용 11:38:08)
|
||||
|
||||
=== [차단 후 1] JGroups 지표 ===
|
||||
--- keycloak-0 ---
|
||||
vendor_jgroups_stats_bytes_sent_total 32857.0
|
||||
vendor_jgroups_merge3_get_num_merge_events 0.0
|
||||
vendor_jgroups_merge3_get_views 0.0
|
||||
vendor_jgroups_fd_sock2_get_num_suspected_members 0.0
|
||||
vendor_jgroups_nakack2_get_xmit_table_missing_messages 0.0
|
||||
--- keycloak-1 ---
|
||||
vendor_jgroups_merge3_get_views 0.0
|
||||
vendor_jgroups_stats_bytes_sent_total 129103.0
|
||||
vendor_jgroups_nakack2_get_xmit_table_missing_messages 0.0
|
||||
vendor_jgroups_fd_sock2_get_num_suspected_members 0.0
|
||||
vendor_jgroups_merge3_get_num_merge_events 0.0
|
||||
|
||||
=== [차단 후 2] 클러스터 뷰 — 갈라졌는가 ===
|
||||
keycloak-0:
|
||||
keycloak-1:
|
||||
=== [차단 후 3] JGROUPS_PING — 디스커버리는 살아 있는가 ===
|
||||
name | ip | coord
|
||||
------------------+-----------------+-------
|
||||
keycloak-0-30843 | 10.42.1.43:7800 | f
|
||||
keycloak-1-48749 | 10.42.0.35:7800 | t
|
||||
(2 rows)
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
=== [문제 확정] NetworkPolicy 적용 후에도 기존 연결이 conntrack 에 살아 있다 ===
|
||||
--- kc-lab-1 ---
|
||||
tcp 6 86398 ESTABLISHED src=10.42.0.35 dst=10.42.1.43 sport=40023 dport=7800 src=10.42.1.43 dst=10.42.0.35 sport=7800 dport=40023 [ASSURED] mark=0 use=1
|
||||
tcp 6 79982 ESTABLISHED src=10.42.0.35 dst=10.42.1.43 sport=50477 dport=57800 src=10.42.1.43 dst=10.42.0.35 sport=57800 dport=50477 [ASSURED] mark=0 use=1
|
||||
--- kc-lab-2 ---
|
||||
tcp 6 86398 ESTABLISHED src=10.42.0.35 dst=10.42.1.43 sport=40023 dport=7800 src=10.42.1.43 dst=10.42.0.35 sport=7800 dport=40023 [ASSURED] mark=0 use=1
|
||||
tcp 6 33 SYN_SENT src=10.42.1.58 dst=10.42.0.35 sport=34824 dport=7800 [UNREPLIED] src=10.42.0.35 dst=10.42.1.58 sport=7800 dport=34824 mark=0 use=1
|
||||
tcp 6 79982 ESTABLISHED src=10.42.0.35 dst=10.42.1.43 sport=50477 dport=57800 src=10.42.1.43 dst=10.42.0.35 sport=57800 dport=50477 [ASSURED] mark=0 use=1
|
||||
|
||||
=== [조치] 7800 흐름의 conntrack 항목을 지운다 → 다음 패킷이 정책을 다시 탄다 ===
|
||||
kc-lab-1: tcp 6 86398 ESTABLISHED src=10.42.0.35 dst=10.42.1.43 sport=40023 dport=7800 src=10.42.1.43 dst=10.42.0.35 sport=7800 dport=40023 [ASSURED] mark=0 use=1 conntrack v1.4.7 (conntrack-tools): 0 flow entries have been deleted.
|
||||
kc-lab-2: tcp 6 33 SYN_SENT src=10.42.1.58 dst=10.42.0.35 sport=34824 dport=7800 [UNREPLIED] src=10.42.0.35 dst=10.42.1.58 sport=7800 dport=34824 mark=0 use=1 conntrack v1.4.7 (conntrack-tools): 0 flow entries have been deleted.
|
||||
|
||||
=== 삭제 후 7800 conntrack ===
|
||||
kc-lab-1: 2 건
|
||||
kc-lab-2: 2 건
|
||||
@@ -0,0 +1,13 @@
|
||||
관찰 시작: 11:42:03
|
||||
+20초 suspected(k0 k1) = []
|
||||
+40초 suspected(k0 k1) = []
|
||||
+60초 suspected(k0 k1) = [0.0 0.0 0.0 0.0 ]
|
||||
+80초 suspected(k0 k1) = []
|
||||
+100초 suspected(k0 k1) = []
|
||||
+120초 suspected(k0 k1) = []
|
||||
+140초 suspected(k0 k1) = [0.0 ]
|
||||
+160초 suspected(k0 k1) = [0.0 0.0 0.0 0.0 ]
|
||||
|
||||
=== 클러스터 뷰 변화 (최근 8분) ===
|
||||
--- keycloak-0 ---
|
||||
--- keycloak-1 ---
|
||||
@@ -0,0 +1,16 @@
|
||||
=== vendor_cluster_size — 지난 25분 (차단 11:38:08, conntrack 삭제 11:41) ===
|
||||
keycloak-0:
|
||||
11:20=2 11:21=2 11:22=2 11:23=2 11:24=2 11:25=2 11:26=2 11:27=2 11:28=2 11:29=2 11:30=2 11:31=2 11:32=2 11:33=2 11:34=2 11:35=2 11:36=2 11:37=2 11:38=2 11:39=2 11:40=2 11:41=2 11:42=2 11:43=2 11:44=2 11:45=2
|
||||
keycloak-1:
|
||||
11:20=2 11:21=2 11:22=2 11:23=2 11:24=2 11:25=2 11:26=2 11:27=2 11:28=2 11:29=2 11:30=2 11:31=2 11:32=2 11:33=2 11:34=2 11:35=2 11:36=2 11:37=2 11:38=2 11:39=2 11:40=2 11:41=2 11:42=2 11:43=2 11:44=2 11:45=2
|
||||
|
||||
=== 현재 값 ===
|
||||
keycloak-1 = 2 멤버
|
||||
keycloak-0 = 2 멤버
|
||||
|
||||
=== 7800 소켓 상태 (파드 내부) ===
|
||||
keycloak-0 2
|
||||
keycloak-1 2
|
||||
=== conntrack ===
|
||||
kc-lab-1 1 건
|
||||
kc-lab-2 1 건
|
||||
@@ -0,0 +1,9 @@
|
||||
=== 정책이 걸린 상태에서 keycloak-0 을 재시작한다 → 재연결이 막힌다 ===
|
||||
재시작 시각: 11:46:07
|
||||
pod "keycloak-0" deleted from keycloak-lab namespace
|
||||
keycloak-0 false 10.42.1.67 2026-09-04T02:44:23Z
|
||||
|
||||
=== cluster_size 추이 ===
|
||||
keycloak-0: 11:45:27=1 11:45:57=1 11:46:27=1 11:46:57=1 11:47:27=1
|
||||
keycloak-0: 11:40:57=2 11:41:27=2 11:41:57=2 11:42:27=2 11:42:57=2 11:43:27=2 11:43:57=2
|
||||
keycloak-1: 11:40:57=2 11:41:27=2 11:41:57=2 11:42:27=2 11:42:57=2 11:43:27=2 11:43:57=2 11:44:27=1 11:44:57=1 11:45:27=1 11:45:57=1 11:46:27=1 11:46:57=1 11:47:27=1
|
||||
@@ -0,0 +1,16 @@
|
||||
=== keycloak-0 헬스 상태 ===
|
||||
keycloak-0 = 10.42.1.67 keycloak-1 = 10.42.0.35
|
||||
PodReadyToStartContainers=True
|
||||
Initialized=True
|
||||
Ready=False ContainersNotReady
|
||||
ContainersReady=False ContainersNotReady
|
||||
PodScheduled=True
|
||||
|
||||
=== ★ 본 시험 — 분단 상태에서 교차 노드 세션이 되는가 ===
|
||||
[1] keycloak-0 로그인 sid=nShl5TaBrZnKStDqaspjgmJB
|
||||
[2] keycloak-1 에서 refresh HTTP 200
|
||||
[3] keycloak-1 에서 로그아웃 HTTP 204
|
||||
[4] keycloak-0 에서 재갱신 시도 HTTP 200
|
||||
(400 이면 무효화가 전파된 것)
|
||||
|
||||
=== DB 세션 수 ===
|
||||
@@ -0,0 +1,33 @@
|
||||
=== 그 sid 가 DB 에 남아 있는가 ===
|
||||
user_session_id | offline_flag | last_session_refresh
|
||||
-----------------+--------------+----------------------
|
||||
(0 rows)
|
||||
|
||||
=== 전체 온라인 세션 수 ===
|
||||
1
|
||||
|
||||
=== 노드별 세션 캐시 엔트리 (Prometheus) ===
|
||||
keycloak-1 kc-lab-1 = 0
|
||||
keycloak-0 kc-lab-2 = 1
|
||||
|
||||
=== keycloak-0 이 Ready 가 아닌 이유 — 헬스 응답 ===
|
||||
{
|
||||
"status": "DOWN",
|
||||
"checks": [
|
||||
{
|
||||
"name": "Graceful Shutdown",
|
||||
"status": "UP"
|
||||
},
|
||||
{
|
||||
"name": "Keycloak cluster health check",
|
||||
"status": "DOWN",
|
||||
"data": {
|
||||
"Failing since": "2026-09-04 02:45:14,251"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "Keycloak database connections async health check",
|
||||
"status": "UP"
|
||||
},
|
||||
{
|
||||
"name": "Keycloak Initialized",
|
||||
@@ -0,0 +1,25 @@
|
||||
=== 양쪽 노드의 readiness — 둘 다 DOWN 이면 전면 장애다 ===
|
||||
Traceback (most recent call last):
|
||||
File "<string>", line 3, in <module>
|
||||
d=json.load(sys.stdin)
|
||||
File "/usr/lib/python3.14/json/__init__.py", line 298, in load
|
||||
return loads(fp.read(),
|
||||
cls=cls, object_hook=object_hook,
|
||||
parse_float=parse_float, parse_int=parse_int,
|
||||
parse_constant=parse_constant, object_pairs_hook=object_pairs_hook, **kw)
|
||||
File "/usr/lib/python3.14/json/__init__.py", line 352, in loads
|
||||
return _default_decoder.decode(s)
|
||||
~~~~~~~~~~~~~~~~~~~~~~~^^^
|
||||
File "/usr/lib/python3.14/json/decoder.py", line 348, in decode
|
||||
raise JSONDecodeError("Extra data", s, end)
|
||||
json.decoder.JSONDecodeError: Extra data: line 21 column 2 (char 446)
|
||||
|
||||
=== 파드 Ready 상태 ===
|
||||
keycloak-0 false 0
|
||||
keycloak-1 true 0
|
||||
|
||||
=== ★ Service 엔드포인트 — 트래픽을 받는 파드가 남아 있는가 ===
|
||||
ready 주소: [10.42.0.35] notReady : [10.42.1.67]
|
||||
=== ★ 외부 진입점으로 실제 로그인이 되는가 (nginx→Traefik→Service) ===
|
||||
https://auth.hyeonworks.com/realms/master HTTP 200
|
||||
토큰 발급 HTTP 200
|
||||
@@ -0,0 +1,24 @@
|
||||
=== 차단 해제 ===
|
||||
해제 시각: 11:49:58
|
||||
networkpolicy.networking.k8s.io "a1-block-jgroups-transport" deleted from keycloak-lab namespace
|
||||
|
||||
=== 자동으로 다시 붙는가 (30초 간격, 최대 4분) ===
|
||||
+30초 keycloak-0=1 keycloak-1=1 | Ready 파드 2 개
|
||||
+60초 keycloak-0=1 keycloak-1=1 | Ready 파드 2 개
|
||||
+90초 keycloak-0=2 keycloak-1=2 | Ready 파드 3 개
|
||||
→ 클러스터 재형성
|
||||
|
||||
=== 복구 로그 ===
|
||||
keycloak-0: [keycloak-0-26403(v=16.0.12)|0] (1) [keycloak-0-26403(v=16.0.12)]
|
||||
keycloak-1: [keycloak-1-48749(v=16.0.12)|6] (1) [keycloak-1-48749(v=16.0.12)]
|
||||
|
||||
=== MERGE3 가 합쳤는가 ===
|
||||
merge_events keycloak-1 = 1
|
||||
merge_events keycloak-0 = 1
|
||||
=== JGROUPS_PING — 코디네이터가 하나로 돌아왔는가 ===
|
||||
name | ip | coord
|
||||
------------------+-----------------+-------
|
||||
keycloak-0-26403 | 10.42.1.67:7800 | t
|
||||
keycloak-1-48749 | 10.42.0.35:7800 | f
|
||||
(2 rows)
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
# A-1 — JGroups 트랜스포트(7800) 차단 증거
|
||||
|
||||
2026-09-04 11:38–11:52 KST · Keycloak 26.7.0 / Infinispan 16.0.12
|
||||
해설: [`docs/experiment-a1-jgroups-transport-block.md`](../../experiment-a1-jgroups-transport-block.md)
|
||||
|
||||
| 파일 | 무엇을 보여주는가 |
|
||||
|---|---|
|
||||
| `01-baseline-cluster.txt` | 차단 전 — 양쪽이 뷰 ID 5·멤버 2로 일치, `JGROUPS_PING` 코디네이터 1명 |
|
||||
| `02-control-before-block.txt` | **대조군** — 차단 전 교차 노드 refresh `200`, JGroups 지표 전부 0 |
|
||||
| `03-block-applied.txt` | NetworkPolicy 적용. **빈 측정값을 "변화 감지"로 오판한 기록** |
|
||||
| `04-after-block-state.txt` | 차단 43초 후 — 지표 무변화, `JGROUPS_PING` 그대로 |
|
||||
| `05-conntrack-problem.txt` | **핵심 문제** — `ESTABLISHED [ASSURED]` 로 기존 연결이 살아 있음. FD_SOCK2 의 **57800** 포트도 함께 드러남 |
|
||||
| `06-partition-observed.txt` | 임시 curl 파드 폴링의 실패 — 빈 값·개수 불일치 |
|
||||
| `07-cluster-size.txt` | **`vendor_cluster_size` 가 25분 내내 2** — 분단이 일어나지 않았다는 결정적 증거 |
|
||||
| `08-restart-forced-partition.txt` | 재연결 강제 후 `2 → 1` |
|
||||
| `09-cross-node-under-partition.txt` | **본 시험** — 교차 refresh `200`(예측 적중), **로그아웃 후 재갱신 `200`(예측 빗나감)** |
|
||||
| `10-logout-not-propagated.txt` | 기제 확정 — **DB 행 0건인데 keycloak-0 캐시에 1건**, 헬스체크 `cluster health: DOWN` |
|
||||
| `11-service-impact.txt` | **분단 노드가 Service 에서 빠짐.** `ready=[10.42.0.35] notReady=[10.42.1.67]`, 외부 로그인 `200` |
|
||||
| `12-recovery.txt` | 90초 만에 자동 재형성, `merge3_get_num_merge_events = 1`, 코디네이터 재선출 |
|
||||
| `a1-cluster-size-partition-recovery.png` | Grafana — `vendor_cluster_size` 가 `2 → 1 → 2` 로 움직이는 전 구간 |
|
||||
|
||||
## 핵심 세 줄
|
||||
|
||||
1. **NetworkPolicy 만으로는 이미 붙어 있는 클러스터를 못 끊는다.** conntrack 의 ESTABLISHED 가 먼저 통과시킨다.
|
||||
2. **세션 공유는 분단을 견딘다(200).** 통념이 틀렸고 A-0 모델이 맞다.
|
||||
3. **로그아웃 무효화는 7800 을 탄다.** DB 행이 지워져도 반대편은 낡은 캐시로 200 을 준다 — A-0 의 인과 해석을 정정한다.
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 66 KiB |
Reference in New Issue
Block a user