상태: 실측 (2026-10-31). TT Lab 실습 VM 세 대(KubeVirt, Ubuntu 24.04) 위의 Docker 에서 잰 값입니다. 각 시험은 한 번씩 했고, VM 전원 정지는 하지 않았습니다(미실측). 구성과 시험 방법은 1편에 있습니다. 작성 규칙: 실측 데이터가 기본이고, 못 잰 것은 「미실측」, 원인을 확인하지 못한 해석은 「추정」 으로 표시합니다.
목차
- 1. 한눈에 보기
- 2. 장애 종류별 최장 쓰기 중단
- 3. 분할: 고립된 옛 주인이 쓰기를 받는가
- 4. 운영자가 꼭 알아야 할 함정
- 5. 이 측정의 한계
- 6. 정리: 2대 + 증인으로 갈 때의 점검표
1. 한눈에 보기
1편에서 데이터 노드 2대만으로는 한 대를 잃을 때 다섯 서비스가 모두 멈춘다는 것을 봤습니다. 이번에는 투표만 하는 증인 VM 을 더한 구성(데이터 노드 db1·db2 + 증인)에 세 종류의 장애를 넣었습니다.
- 고립: 노드 하나의 세션망을 iptables 로 끊었다. 노드는 살아 있지만 상대와 끊긴 분할 상태다.
- 정상 정지: 노드 하나의 컨테이너를 모두
docker stop(유예 30초)으로 내렸다. - 증인 정지: 증인의 etcd·Sentinel·OpenSearch·Keeper·SeaweedFS master 를 모두 멈췄다.
결과를 먼저 적습니다.
- 자동 전환이 됐습니다. 고립에서 PostgreSQL 은 22.6초에서 32.7초, Redis Sentinel 은 6.5초에서 7.0초 만에 새 주인을 세웠습니다. 정상 정지에서는 PostgreSQL 이 2.8초에서 3.1초 만에 넘어갔습니다.
- 확인받은 쓰기의 손실은 모든 시험, 모든 서비스에서 0건이었습니다.
- 증인을 90초 꺼도 PostgreSQL·Redis·OpenSearch 의 쓰기 실패는 0건이었습니다. 데이터 노드 둘이 살아 있으면 과반(2/3)이 남기 때문입니다.
- 그러나 「데이터 노드 2대」 라는 조건은 그대로입니다. Redis 의
min-replicas-to-write 1, ClickHouse 의insert_quorum=2, SeaweedFS 의 복제001은 모두 「상대 데이터 노드가 있어야 쓴다」 는 설정이라, 상대가 돌아올 때까지 쓰기를 막았습니다. 아래 표의 긴 값은 대부분 여기서 나왔습니다.
2. 장애 종류별 최장 쓰기 중단
최장 쓰기 중단은 성공한 쓰기와 다음 성공 사이의 가장 긴 틈(초)입니다. 괄호 안은 실패 수 / 시도 수입니다. ClickHouse 는 기본 삽입,
quorum=2 는 insert_quorum=2 로 넣은 삽입, S3 는 HAProxy 뒤 SeaweedFS S3 게이트웨이 2개입니다.
| 서비스 | db1 고립 90초 | db2 고립 90초 | db1 고립 40초 | db2 정상 정지 90초 | db1 정상 정지 90초 | 증인 정지 90초 |
|---|---|---|---|---|---|---|
| PostgreSQL | 23.7 (400/5910) | 28.3 (491/5915) | 33.7 (601/3120) | 3.4 (25/5944) | 3.7 (34/5943) | 0.09 (0/4780) |
| Redis | 105.0 (471/4364) | 105.1 (1964/5855) | 55.2 (371/2463) | 268.8 ※ (5349/5968) | 134.8 (2673/5970) | 0.09 (0/4782) |
| OpenSearch | 66.3 (16/2347) | 64.7 (15/2364) | 40.2 (9/1196) | 0.3 (0/2990) | 0.7 (0/2986) | 0.13 (0/2392) |
| ClickHouse | 5.3 (1/1476) | 5.2 (1/1476) | 5.2 (1/776) | 2.5 (2/1491) | 2.4 (2/1490) | 5.4 (1/1175) |
| ClickHouse quorum=2 | 60.6 (303/1475) | 59.9 (300/1451) | 10.0 (51/752) | 122.9 (601/1488) | 123.6 (602/1486) | 5.3 (1/1175) |
| SeaweedFS S3 | 95.7 (19/898) | 111.2 (11/955) | 61.2 (11/505) | 122.4 (11/898) | 131.1 (12/856) | 27.6 (2/1055) |
※ db2 정상 정지의 Redis 268.8초는 공식 이미지의 결함 때문에 다시 띄운 Redis 가 재시작 고리에 빠져 끝내 돌아오지 못한 값입니다(4.5절). 고친 뒤 같은 시험을 db1 에 한 것이 오른쪽에서 두 번째 열입니다. 쓰기 간격은 PostgreSQL·Redis 0.05초라 0.09초는 끊김이 없었다는 뜻입니다.
읽기(조회) 실패는 거의 없었습니다. OpenSearch _count 는 시험마다 0회에서 1회, ClickHouse·S3 는 증인 있는 구성의 모든 시험에서 0회에서 1회였습니다.
2.1 주인이 바뀌기까지
장애를 넣은 시각부터 잰 초입니다.
| db1 고립 90초 | db2 고립 90초 | db1 고립 40초 | db2 정상 정지 | db1 정상 정지 | |
|---|---|---|---|---|---|
| Patroni 새 primary | 22.6 | 27.4 | 32.7 | 3.1 | 2.8 |
| Sentinel 새 master | 6.5 | 6.6 | 7.0 | 7.9 | 7.8 |
| OpenSearch 노드 제거(yellow) | 64.8 | 65.2 | 제거 전에 복구 | 0.6 | 0.5 |
| SeaweedFS 새 master 리더 | 12.1 | 리더가 고립 노드가 아니었음 | 리더가 고립 노드가 아니었음 | 영향 없음 | 15.0 |
| 복구 뒤 합류 | PG 15.5(pg_rewind 와 재시작 포함), OpenSearch green 5.1 | PG 6.6, OpenSearch green 2.8 | PG 14.2, OpenSearch green 6.9 | OpenSearch green 23.2(시작 기준) | OpenSearch green 26.3 |
2.2 서비스별로 읽기
- PostgreSQL: 고립에서는 옛 주인의 리더 키 수명(
ttl: 30)이 끝나야 승격하므로 22초에서 34초가 걸렸습니다. 정상 정지에서는 Patroni 가 키를 스스로 내려놓아 3초 만에 넘어갔습니다. 옛 주 서버는 복구 뒤 pg_rewind 로 되감고 복제본으로 다시 붙었습니다. 고립 시간을 줄이고 싶다면ttl을 줄이는 것이 직접적인 손잡이지만, 줄인 값은 재지 않았습니다(미실측). - Redis: 전환 자체는 6.5초에서 7.9초에 끝났습니다. 그런데 새 주인도
min-replicas-to-write 1이라, 복제본(옛 주인)이 돌아올 때까지NOREPLICAS로 쓰기를 거절했습니다. 쓰기 중단은 「장애 시간 + 10초에서 15초」 였습니다. 데이터 노드가 2대뿐이면 이 설정은 「한 대가 죽으면 쓰기 정지」 와 같습니다. 대가와 선택지는 4.1절에서 봅니다. - OpenSearch: 정상 정지는 노드가 스스로 빠져나가 중단이 1초 미만이었습니다. 고립은 약 65초 동안 일부 쓰기가 멈췄습니다(4.4절).
- ClickHouse: 두 복제본이 모두 쓰기를 받으므로 기본 삽입은 HAProxy 가 죽은 쪽을 빼는 2초에서 5초만 끊겼습니다.
insert_quorum=2는 상대가 돌아올 때까지TOO_FEW_LIVE_REPLICAS로 실패했습니다. 따라잡기는 빨랐습니다. 150초 정지 뒤 복제 큐 896개를 16초에 비웠습니다. - SeaweedFS: 복제
001에서 볼륨 서버가 하나만 남으면 새 쓰기 할당이 실패합니다. 예상한 일입니다. 예상과 달랐던 것은 실패가 즉시 오지 않고 요청마다 약 10초씩 매달리다 시간 초과로 끝났다는 점입니다(4.7절). 읽기는 모든 시험에서 실패하지 않았습니다.
2.3 증인을 꺼도
| PostgreSQL | Redis | OpenSearch | ClickHouse 기본 / quorum=2 | S3 |
|---|---|---|---|---|
| 실패 0 / 4780 | 실패 0 / 4782 | 실패 0 / 2392 | 5.4초 1건 / 5.3초 1건 | 27.6초, 2건 |
ClickHouse 와 S3 의 짧은 끊김은 그 순간 Keeper 리더와 SeaweedFS master 리더가 증인에 있었기 때문입니다. 리더를 다시 뽑는 데 각각 약 5초와 12초가 걸렸고, S3 는 볼륨 서버가 새 리더에 다시 붙을 때까지 더 끊겼습니다. 증인은 「표만 던지는」 자리지만 Raft 에서는 리더가 될 수도 있다는 점을 기억해 둘 만합니다.
3. 분할: 고립된 옛 주인이 쓰기를 받는가
자동 전환이 된다고 끝이 아닙니다. 끊긴 쪽의 옛 주인이 그 사이 쓰기를 받았다가 나중에 버리면, 앱은 성공 응답을 받고도 데이터를 잃습니다. db1(그때의 주인)을 40초 고립시키고, db1 안에서 0.5초마다 자기 로컬 서비스에 직접 썼습니다. 복구 뒤 그 쓰기가 남았는지 대조했습니다.
| 서비스 | 고립된 옛 주인이 받은 쓰기 | 복구 뒤 남은 것 | 막아 준 것 |
|---|---|---|---|
| PostgreSQL | 0. 고립 직후부터 실패 | 해당 없음 | 동기 복제(복제본 확인을 기다림) + 합의 저장소를 잃으면 강등 |
| Redis | 고립 뒤 0초에서 10.7초 사이 22건 | 0건(22건 모두 소실) | min-replicas-max-lag 10 이 지난 뒤에야 NOREPLICAS. 그 10초가 소실 창 |
| OpenSearch | 100건 | 100건 | ※ 방금 만든 색인이라 복제본이 in-sync 집합에 들기 전이었다(일반 색인은 쓰기가 막혔다). 분기는 없었다 |
| ClickHouse | 0. Keeper 과반을 잃어 삽입이 매달림 | 해당 없음 | Keeper 과반 |
| SeaweedFS filer | 0. 시간 초과 | 해당 없음 | master 과반 |
과반 규칙을 그대로 따르는 PostgreSQL·ClickHouse·SeaweedFS 는 고립된 쪽이 쓰기를 거절했습니다. 손실은 Redis 에서만 나왔습니다.
4. 운영자가 꼭 알아야 할 함정
여기부터는 이번 측정에서 실제로 겪은 것입니다.
4.1 Redis min-replicas-max-lag 는 곧 분할 때의 소실 창이다
Redis 의 복제는 비동기입니다. 주인이 쓰기를 받으면 곧바로 성공을 돌려주고, 복제본에는 나중에 보냅니다. min-replicas-to-write 1 과
min-replicas-max-lag 10 은 「최근 10초 안에 응답한 복제본이 1개 이상 있을 때만 쓰기를 받는다」 는 뜻입니다. 고립된 주인은 복제본의 마지막
응답이 10초를 넘기기 전까지 쓰기를 계속 받습니다.
실측에서 그 창은 정확히 그렇게 열렸습니다. 고립된 db1 은 처음 10.7초 동안 22건을 성공으로 받았고, 그 사이 +7.0초에 Sentinel 이 db2 를 새 주인으로 세웠습니다. 복구 뒤 db1 은 db2 의 복제본이 되어 db2 의 데이터로 덮였고, 22건은 하나도 남지 않았습니다. 앱 쪽에서 보면 「성공」 응답을 받은 쓰기입니다.
선택지는 셋이고, 어느 것도 공짜가 아닙니다.
min-replicas-max-lag를 낮춥니다. 창은 줄지만 복제본이 조금만 늦어도 쓰기를 거절합니다.min-replicas-to-write를 0으로 둡니다. 데이터 노드 하나를 잃어도 쓰기는 계속되지만(2.2절의 105초 중단이 사라진다), 분할 때 옛 주인이 받는 쓰기가 제한 없이 늘어납니다.- 이 창 자체를 없애려면 Redis 가 아니라 동기 합의가 있는 저장소가 필요합니다. 캐시처럼 잃어도 되는 데이터에만 Redis 를 쓰는 것도 답입니다.
이번 구성은 첫째 설정(1, 10)이었고, 그 대가가 「분할 때 최대 약 10초의 쓰기 소실」 과 「데이터 노드 하나를 잃으면 쓰기 정지」 두 가지로 모두 측정됐습니다.
4.2 실패로 응답했는데 저장된 쓰기, 그리고 멱등 키
확인받은 쓰기의 손실은 0건이었지만, 반대 방향의 어긋남이 있었습니다. 실패 응답을 받았는데 실제로는 저장된 쓰기입니다.
| 증인 있는 구성의 시험 | OpenSearch | ClickHouse quorum=2 | S3 |
|---|---|---|---|
| db1 고립 90초 | 16 / 실패 16 | 303 / 실패 303 | 4 / 실패 19 |
| db2 고립 90초 | 15 / 실패 15 | 300 / 실패 300 | 3 / 실패 11 |
| db1 고립 40초 | 9 / 실패 9 | 51 / 실패 51 | 4 / 실패 11 |
| db2 정상 정지 90초 | 0 / 실패 0 | 601 / 실패 601 | 2 / 실패 11 |
| db1 정상 정지 90초 | 0 / 실패 0 | 601 / 실패 602 | 2 / 실패 12 |
(표의 값은 「실패 응답인데 저장된 수 / 실패 응답 수」)
- OpenSearch 는 클라이언트 시간 초과 뒤에 반영된 쓰기였습니다.
- ClickHouse
insert_quorum=2는 「정족수를 채우지 못했다」 고 답했지만 한 복제본에는 써졌고, 상대가 돌아오자 그대로 복제됐습니다. 실패 응답의 거의 전부가 이랬습니다. 「실패 = 안 써짐」 으로 다루면 안 됩니다. - Keeper 과반을 잃었을 때(Keeper 3개 중 2개를 멈춘 시험)도 같았습니다. 삽입은 오류 없이 5초 시간 초과로 매달렸고(60초 동안 13건), Keeper 를 되살리자 13건이 결국 모두 저장됐습니다.
클라이언트가 실패를 보고 다시 보내면 같은 행이 두 번 들어갑니다. 그래서 다시 보내는 쓰기에는 멱등 키가 필요합니다. OpenSearch 는 문서 _id 를
앱이 정해서 넣고, ClickHouse 는 insert_deduplication_token 을 붙입니다. 앱의 재시도 로직이 「실패했으니 없을 것」 이라고 가정하는지 먼저 확인할 일입니다.
4.3 ClickHouse Keeper 강제 복구 직후의 정족수 창
1편 7.1절에서 본 내용입니다. 2대뿐인 구성에서 Keeper 를 --force-recovery 로 단일 서버로 되살린 직후 약 20초 동안, 상대 복제본이 고립된 채였는데도
insert_quorum=2 삽입 98건이 성공으로 응답했습니다. 그 뒤에는 TOO_FEW_LIVE_REPLICAS 로 1346건이 실패했습니다. 복구된 Keeper 에 상대의 옛
「살아 있음」 표시가 세션 시간 초과(30초) 전까지 남아 있었다고 추정합니다(추정). 강제 복구 뒤에는 세션 시간이 지날 때까지 정족수 삽입의 성공을
믿지 않습니다.
4.4 OpenSearch 는 고립된 노드를 알아채는 데 65초가 걸렸다
정상 정지에서는 노드가 스스로 클러스터를 떠나므로 쓰기 중단이 1초 미만이었습니다. 고립은 달랐습니다. 노드가 빠지기까지(yellow) 64.8초와 65.2초가
걸렸고, 그동안 그 노드에 primary 샤드가 있는 색인으로 가는 쓰기가 멈췄습니다. 검색(_count)은 고립 시험마다 0회에서 1회만 실패했습니다.
OpenSearch 는 기본 장애 감지 설정(확인 한 번의 시간 제한 10초, 3회 연속 실패해야 장애로 판정)으로 노드를 지켜봅니다. 고립에서는 이 판정을 두 단계에
걸쳐 거치므로 약 65초가 된 것으로 봅니다. cluster.fault_detection.* 값을 줄이면 이 시간을 줄일 수 있지만, 줄인 값으로는 재지 않았습니다(미실측).
일시적인 네트워크 흔들림에도 노드를 빼게 되므로, 줄인다면 그 값으로 다시 재 보아야 합니다.
4.5 공식 redis:8 이미지와 Sentinel 의 재시작 고리
2절 표의 268.8초가 이것입니다. 원인은 이렇게 맞물렸습니다.
- 공식
redis:8이미지의 진입점은 모듈 폴더에 있는.so를 모두--loadmodule로 붙여 Redis 를 띄웁니다. - Sentinel 은 장애 조치 때 Redis 에
CONFIG REWRITE를 보냅니다. 그러면 실행 중인 설정이 파일에 다시 쓰이면서 그loadmodule줄들이redis.conf에 박힙니다. - 다음 시작에서 진입점이 붙인 모듈과 파일에 박힌 모듈이 겹쳐 같은 모듈을 두 번 싣다가
Can't load module ... server aborting으로 죽습니다. 컨테이너는 다시 시작되고, 다시 죽습니다.
Sentinel 을 쓰고, 장애 조치를 한 번 겪고, 그 Redis 를 다시 시작할 때에만 드러나므로 설치 직후의 시험으로는 보이지 않습니다. 모듈을 쓰지 않는다면 해결은 간단합니다. 모듈 폴더를 빈 tmpfs 로 덮어 아예 싣지 않습니다. 이미 박힌 줄은 지웁니다.
redis:
image: redis:8.8.4
command: ["redis-server", "/conf/redis.conf"]
tmpfs:
- /usr/local/lib/redis/modules # 진입점이 붙일 모듈이 없게 한다
sed -i '/^loadmodule /d' /path/to/redis.conf # 이미 박힌 줄 지우기
고친 뒤 같은 시험(db1 정상 정지)에서 Redis 는 정상으로 돌아왔고, 남은 134.8초 중단은 4.1절의 min-replicas 대가였습니다. 이 문제는 업스트림에
이미 보고되어 있고(redis/docker-library-redis#468,
redis/redis#14178), Redis 본체 쪽 고침
(redis/redis#16009)이 2026-10-29 에 병합되어 다음 판부터 풀릴 예정입니다.
같은 줄기의 작은 함정이 하나 더 있습니다. Sentinel 은 requirepass 를 ACL 해시 줄로 고쳐 씁니다. 설정 파일에서 비밀번호를 읽어 오는 헬스체크는
깨지므로, 이번에는 인증 전 응답(NOAUTH)도 「살아 있음」 으로 보게 했습니다.
4.6 증인은 세 번째 장애 영역에 둔다
이번 시험에서 증인 VM 은 db2 와 같은 물리 서버 위에 있었습니다. VM 단위 장애만 넣었기 때문에 결과에는 영향이 없었지만, 그 물리 서버가 죽으면 db2 와 증인을 함께 잃어 3표 중 2표가 사라집니다. 그러면 2대뿐인 구성과 똑같이 멈춥니다.
증인의 가치는 「데이터 노드 어느 쪽과도 함께 죽지 않는다」 는 데서 나옵니다. 운영에서는 증인을 세 번째 장애 영역, 곧 다른 랙, 다른 건물, 다른 전원과 스위치, 혹은 클라우드의 작은 VM 에 둡니다. 증인이 하는 일은 표를 던지는 것뿐이라 작은 VM 으로 충분합니다. 이번 증인은 2 vCPU·2 GiB 에서 설치 직후 1.5 GiB 를 썼습니다. 다만 2.3절에서 봤듯이 증인도 Raft 리더가 될 수 있으므로 데이터 노드와의 왕복 지연이 너무 크면 리더 재선출 동안의 끊김이 길어질 수 있습니다(미실측).
4.7 SeaweedFS: 볼륨 서버 하나로는 쓰지 못하는데, 바로 실패하지도 않는다
복제 001 에서 볼륨 서버가 하나 남으면 새 쓰기를 둘 자리가 없습니다. 여기까지는 설정대로입니다. 문제는 실패가 요청마다 약 10초씩 매달린 뒤에야
왔다는 것입니다. 두 가지가 겹쳤습니다.
- 살아남은 볼륨 서버가 죽은 옛 master 리더에게 계속 하트비트를 보내다 54초 뒤에야 새 리더에 붙었습니다. 그동안 새 리더는 등록된 용량이 0이라 「no volume server capacity registered yet」 를 냈습니다. 실제 원인과 다른 문구입니다.
- 고립됐던 master 가 돌아오면 멀쩡하던 리더를 흔들어 6초에서 19초 동안 리더가 없었습니다.
두 가지 모두 업스트림에 올렸습니다. 볼륨 서버가 새 리더를 늦게 찾는 문제는 고침 PR
(seaweedfs#12259, 재현에서 25초가 4초로)을, 돌아온 master 가 리더를 끌어내리는 문제는
이슈(seaweedfs#12260)를 냈습니다. 같은 실험에서 -raftHashicorp(PreVote 가 켜진 raft)로 돌리면
리더가 흔들리지 않았습니다. 운영에서 데이터 노드 2대로 SeaweedFS 를 쓴다면, 한 대가 빠진 동안 업로드가 바로 실패하지 않고 매달린다는
것을 앱의 시간 제한에 반영해 둡니다. 볼륨 서버 디스크를 통째로 잃은 경우는 volume.fix.replication -apply 가 30.7초에 24개 볼륨을 모두 맞췄고,
그동안 S3 쓰기 실패는 1건, 읽기 실패는 0건이었습니다. filer 하나를 60초 멈춘 시험에서는 S3 쓰기 748건 중 실패가 0건이었습니다.
4.8 짧은 함정 모음
| 함정 | 증상 | 대처 |
|---|---|---|
OpenSearch node.roles: ${OS_ROLES}(yml 안 환경 변수 치환) |
목록이 아니라 문자열 하나가 되어 노드끼리 클러스터를 만들지 못한다. DEBUG 로그에서만 보인다 | 환경 변수 node.roles=cluster_manager,data,ingest(쉼표 목록)로 준다 |
| OpenSearch 3.9 증인 힙 320m·한도 700m | OOM 재시작 고리 | 힙 384m·한도 1100m |
| SeaweedFS 이미지는 uid 1000 으로 내려가 돈다 | root 600 인 S3 설정 파일을 못 읽어 S3 가 죽는다 | 파일을 1000 소유로 |
| SeaweedFS master 한도 128m | 89 MiB 까지 오른다 | 192m |
| ClickHouse 26.8 HTTP 삽입의 기본값 | async_insert=1, wait_for_async_insert=1 로 나왔다 |
정족수 시험은 async_insert=0 으로 따로 |
| ClickHouse Keeper Raft 포트 | listen_host 를 줘도 모든 주소(9234)에서 열렸다 |
호스트 방화벽으로 바깥 인터페이스를 막는다 |
iptables -m multiport |
한 규칙에 포트 15개까지(「too many ports specified」) | 규칙을 둘로 나눈다 |
컨테이너에는 /dev/watchdog 이 없다 |
Patroni 펜싱이 합의 저장소 TTL 에만 기댄다. 주 서버가 죽지 않고 멈춘 채 TTL 을 넘기면 주인이 잠깐 둘일 수 있다 | 호스트 watchdog 을 넘기거나 softdog(미실측) |
docker stop 유예 30초 |
유예를 다 쓰는 컨테이너 때문에 노드 전체 정지가 30초 걸렸다 | 정상 정지 절차의 시간 예산에 넣는다 |
5. 이 측정의 한계
숫자를 가져다 쓰기 전에 조건을 확인해 주십시오.
- VM 위의 Docker 입니다. KubeVirt VM 안에서 Docker Compose 로 돌렸습니다. 물리 서버, 다른 하이퍼바이저, 쿠버네티스 operator 구성에서는 값이 달라질 수 있습니다. 컨테이너 메모리 한도도 운영보다 작게 잡았습니다(OpenSearch 1.4g 등).
- 각 시험은 한 번씩입니다. 고립은 노드와 길이를 바꿔 세 번, 정상 정지는 두 번(그중 한 번의 Redis 값은 4.5절의 결함 때문에 따로 봐야 한다), 증인 정지와 2대뿐인 구성의 고립·수동 승격은 각각 한 번입니다. 같은 조건을 되풀이한 분포나 중앙값이 아닙니다.
- VM 전원 정지는 하지 않았습니다(미실측). 「고립」 이 상대 쪽에서 본 전원 정지와 같다고 보고 대신했습니다. 디스크 캐시 유실처럼 전원 정지에만 있는 현상은 이 측정에 들어 있지 않습니다.
- 증인이 데이터 노드 하나와 같은 물리 서버에 있었습니다. VM 단위 장애만 넣었으므로 결과에는 영향이 없지만, 물리 서버 장애는 재지 않았습니다.
- 재지 않은 것: 되돌리기(죽었던 노드 다시 넣기), libpq 다중 호스트 접속(
target_session_attrs=read-write)으로 주 서버 찾기, Patronifailsafe_mode, OpenSearch 장애 감지 값을 줄였을 때, 2멤버로 실제 재구성한 Keeper·SeaweedFS master, watchdog 펜싱. - 보안 구성의 빈 곳: etcd·PostgreSQL·OpenSearch 는 자체 CA 의 TLS 를 썼지만, Redis·Sentinel 은 비밀번호 인증만, Patroni REST·SeaweedFS·ClickHouse HTTP 는 세션망 안에서 평문이었습니다. 운영에서는 이 셋에도 TLS 를 더해야 합니다.
- 시계 차이는 VM 사이 ±0.3초 안이라, 1초 미만의 차이는 해석하지 않았습니다.
6. 정리: 2대 + 증인으로 갈 때의 점검표
| 점검 | 이유(이 글의 실측) |
|---|---|
| 증인을 데이터 노드와 다른 장애 영역에 | 같은 곳에서 함께 죽으면 2대뿐인 구성과 같다 |
Redis 의 min-replicas-* 를 의식해서 고른다 |
(1, 10) 은 분할 때 22건 소실, 노드 하나를 90초 잃으면 105초 쓰기 정지 |
| 재시도하는 쓰기에 멱등 키 | 실패 응답인데 저장된 쓰기가 OpenSearch·ClickHouse·S3 에서 나왔다 |
| ClickHouse 강제 복구 뒤 세션 시간 동안 정족수를 믿지 않는다 | 98건이 거짓 성공 |
| OpenSearch 고립 감지 시간을 예산에 | 기본값으로 65초 |
공식 redis:8 + Sentinel 이면 모듈 폴더를 비운다 |
장애 조치 뒤 재시작 고리 |
| 수동 승격 절차를 미리 적고 펜싱부터 | 2대뿐일 때의 유일한 출구. 서비스별 0.9초에서 44.2초 |
티티 한마디. 증인 친구가 생기니 다수결이 잘 돌아가요. 그런데 짐을 드는 친구는 여전히 둘뿐이라, 한 명이 쉬면 「짝이 와야 짐을 내려놓을래」 하는 서비스가 있어요. 그리고 「실패했어요」 라는 말을 듣고 다시 보낼 땐 꼭 이름표(멱등 키)를 붙여 주세요. 같은 짐이 두 번 들어오면 곤란하거든요!