프로젝트

일반

사용자 정보

실행

태스크 #13724

볼륨 사용량 한계 측정

오 지호님이 19일 전에 추가함. 6일 전에 수정됨.

상태:
해결
우선 순위:
보통
담당자:
목표 버전:
시작 시간:
2026/09/10
완료 기한:
2026/09/17 (12일 지연)
진척도:

100%

추정 시간:
32:00 시간
발견 버전:
반영 버전:
난이도:
보통
중요도:
하
조력자:
회사:
연락처:
점수:
3.50

설명

ASE에서 볼륨 리소스를 몇개까지 생성해서 동작해도 버틸 수 있는지 테스트

실행 #1

오 지호님이 18일 전에 변경

스토리지 멀티패스 안정화 (테스트 선행 작업)

한계 측정에 앞서 ASE4 백엔드(HUAWEI S2600T, 1TB LUN 10개, HA 2노드)의 스토리지 쓰기가 ~3.2 MB/s로 측정되어 GMS thinpool 생성이 계속 실패했음. 원인을 추적해 보니 성능 문제와 무관한 구조 결함 두 가지가 겹쳐 있었고 둘 다 수정하여 측정 가능한 상태로 만들었음.

발견 1: FC 링크가 한쪽 컨트롤러에만 연결

  • 확인 결과 4개 FC 링크(노드당 2개) 전부가 ENG0.A 포트에만 연결돼 있었음 (VPD83 target port group designator로 전 경로의 소속 TPG를 확인)
  • ENG0.B가 소유한 5개 LUN은 모든 IO가 A->B 컨트롤러 프록시로 동작
  • 컨트롤러 이중화도 사실상 없었음: A 장애 시 양 노드의 전 경로가 동시에 소멸

조치: 노드당 1경로씩 ENG0.B 포트로 재배선

발견 2: 구형 firmware의 TPGS 거짓 보고로 ALUA 우선순위가 무효화

재배선 후에도 prio alua가 동작하지 않아 추적한 결과:

  • S2600T는 표준 INQUIRY에 TPGS=0(ALUA 미지원)으로 보고하지만 실제로는 implicit ALUA가 정상 동작(RTPG 질의에 정상 응답, VPD83에 TPG designator 존재)
  • multipathd는 prioritizer 이름이 alua인 경우 TPGS 비트가 0이면 getprio 호출 없이 const로 교체해 버림(multipath-tools propsel.c)
  • 결과적으로 경로가 하나의 round-robin 그룹에 섞여 IO의 절반이 비최적화(ghost) 경로 = 컨트롤러 프록시로 흐르게 됨
  • 커널의 scsi_dh_alua 핸들러도 같은 TPGS 비트로 거부(alua: not supported)

조치: 배포 패키지의 libprioalua.so를 이름만 바꿔 복사한 libprioaluaforce.so를 양 노드 /lib64/multipath/에 배포.

cp /lib64/multipath/libprioalua.so /lib64/multipath/libprioaluaforce.so

multipathd가 prioritizer를 파일명(dlopen 경로)과 getprio 심볼로만 찾기 때문에 빌드나 등록 절차 없이 복사만으로 동작하며, prio 이름이 alua가 아니면 위의 선제 교체 로직이 작동하지 않아 정상적으로 경로당 우선순위(최적화 50 / 비최적화 10)가 계산됨.

멀티패스 설정(양 노드 동일):

defaults {
        user_friendly_names     "yes"
        find_multipaths         "yes"
        path_selector           "round-robin 0"
        path_grouping_policy    "group_by_prio"
        prio                    "aluaforce"
        failback                "immediate"
}

결과

  • 전 LUN에서 prio=50(active, 최적화 경로로 IO 전담) + prio=10(enabled, 비최적화 경로는 장애 시에만 사용) 그룹 분리 확인
  • 프록시 IO 제거, 컨트롤러 장애 시 자동 전환 구조 확보
  • multipathd의 emergency fallback 로그 0건

유지보수 주의

  • device-mapper-multipath 패키지 업그레이드 시 libprioaluaforce.so도 갱신 필요(struct path ABI) - 위 cp 한 줄로 해결
  • conf의 prio 이름은 aluaforce로 유지해야 함. alua로 쓰면 TPGS 게이트에 걸려 다시 무효화됨

참고

상단부터 fc host online 주소

  • 1번: 2100f4c7aa0617ce, 2100f4c7aa0617cf
  • 2번: 21000024ff1e495e, 21000024ff1e495f
  • 3번: 2100001b329fac49, 2101001b32bfac49
  • 4번: 2100001b329f7749, 2101001b32bf7749
실행 #2

오 지호님이 14일 전에 변경

InfiniBand 카드 확인 (테스트 환경 점검)

한계 측정 환경을 보다가 IB 카드가 올라오지 않는 걸 발견해서 확인한 내용. 하드웨어 문제는 아니었고 OS 쪽 원인이었음.

장착된 카드 - 4노드 전부 동일

상단 두 노드와 하단 두 노드 모두 같은 카드가 꽂혀 있음. 펌웨어까지 일치.

구분 PCI ID Subsystem HCA type board_id Firmware 포트
상단 1노드 15b3:1003 15b3:0050 MT4099 MT_1090120019 2.42.5000 2, auto (ib)
상단 2노드 15b3:1003 15b3:0050 MT4099 MT_1090120019 2.42.5000 2, auto (ib)
하단 1노드 15b3:1003 15b3:0050 MT4099 MT_1090120019 2.42.5000 2, auto (ib)
하단 2노드 15b3:1003 15b3:0050 MT4099 MT_1090120019 2.42.5000 2, auto (ib)

board_id(PSID 대응 보드 식별자)까지 4노드 전부 일치함. OEM 커스텀 펌웨어가 들어간 카드라면 이 값에서 갈리는데 갈리지 않음. node_guid만 장비별로 다르고 나머지는 동일. PCIe 링크도 4노드 모두 8.0 GT/s x8.

상단 두 노드 - 드라이버가 아예 없었음

Rocky 10.2, 커널 6.12.0-211.16.1.el10_2.0.1.

lspci에는 카드가 보이는데 Kernel driver in use가 비어 있고 lsmod에 mlx4 관련 모듈이 하나도 없었음. /sys/class/infiniband 디렉토리 자체가 생성되지 않아 ibstat도 빈 출력.

# modinfo mlx4_core
modinfo: ERROR: Module mlx4_core not found.

원인은 RHEL 10에서 CONFIG_MLX4_*를 꺼버린 것. 소스는 커널 트리에 그대로 있는데 빌드만 안 하고 있음. /lib/modules/.../mellanox/ 아래에 mlx5만 있고 mlx4 계열이 통째로 없는 상태.

# CONFIG_MLX4_EN is not set
# CONFIG_MLX4_INFINIBAND is not set
CONFIG_MLX5_INFINIBAND=m

에러 메시지가 없어서 하드웨어 고장으로 오인하기 쉬웠음.

조치 - kmod 패키지로 분리

커널에 들어 있는 mlx4 소스를 그대로 out-of-tree 빌드. 결과물은 350 KB 정도이고 mlx4_core, mlx4_en, mlx4_ib 세 개가 들어감.

kABI 안정 심볼 밖의 심볼을 22개 쓰기 때문에 커널 하나당 패키지 하나로 가야 함. 커널을 올리면 다시 빌드해야 하고 패키지 이름에 커널 버전을 넣어서 여러 커널용이 공존하도록 했음. 이름을 공유하면 RPM이 업그레이드로 처리해 버려서 지금 돌고 있는 커널의 모듈이 지워짐.

커널 config를 켜는 방식은 안 됨 (실제 확인)

커스텀 커널을 어차피 쓰니 CONFIG_MLX4_*만 켜서 빌드하면 되지 않느냐는 접근이 자연스러운데, 실제로 해보니 동작하지 않음. 성공한 것처럼 보이면서 실패해서 더 나쁨.

ase4 커널 소스에서 SOURCES/kernel-x86_64-rhel.config의 세 줄을 켜고 전체 빌드를 돌림. 빌드는 깨끗하게 통과했고 모듈 세 개가 kernel-modules / kernel-modules-core에 들어감. Rocky 커널 서명 키로 서명까지 붙음. 상단 첫 번째 노드에 설치해서 부팅했더니 모듈은 다 로드됐는데 카드가 안 붙음.

Warning: Disabled Hardware is detected: mlx4_core:1003:15B3 @ 0000:05:00.0
         is no longer enabled in this release.
mlx4_core 0000:05:00.0: probe with driver mlx4_core failed with error -13

RHEL은 config와 별개로 PCI ID 차단 목록을 커널에 박아뒀음. kernel/rh_messages.h의 rh_disabled_pci_devices[]에 우리 카드가 그대로 있음.

{ 0x15B3, 0x1003, PCI_ANY_ID, PCI_ANY_ID },

검사는 local_pci_probe()에서 pci_rh_check_status()를 통해 돌아감.

if (pci_dev->driver->driver.owner != NULL) {
    if (!test_bit(TAINT_OOT_MODULE, &pci_dev->driver->driver.owner->taints)) {
        ...
        return pci_hw_disabled(pci_dev);
    }
}
return false;   // out-of-tree 모듈은 검사를 건너뜀

out-of-tree 모듈이면 검사를 통째로 건너뜀. kmod 패키지가 동작하는 이유가 정확히 이것이었음. modpost는 커널 트리 안에서 빌드된 모듈에만 intree: Y를 붙이기 때문에 M=$PWD로 빌드한 모듈은 TAINT_OOT_MODULE이 찍히고 차단 목록을 그냥 지나감. 같은 소스, 같은 config, 같은 컴파일러인데 빌드 위치만 다름.

config로 가려면 rh_messages.h까지 패치해야 하고, 그러면 순정 소스가 아니게 되면서 Secure Boot 이점도 같이 사라짐. 결국 kmod 패키지가 더 나은 선택이 아니라 유일하게 동작하는 선택. 테스트 커널은 확인 후 장비에서 제거했음.

확인 결과

상단 두 노드를 ase4 커널로 재부팅해서 확인.

  • 부팅만으로 자동 로드. modprobe 따로 필요 없음
  • 4개 포트 전부 Active / LinkUp / 56 Gb/s (4X FDR)
  • IPoIB ping 0.12ms 내외, packet loss 0%
  • RDMA ib_write_bw 단일 QP 43.56 ~ 50.47 Gb/s
  • 순정 커널용과 ase4 커널용을 같이 설치한 상태로 재부팅해서 공존 확인
  • GMS, ledisdb 서비스 정상

배선은 스위치 없이 상단 두 노드 직결이고 포트1끼리 포트2끼리 물려 있음. PCIe는 8.0 GT/s x8로 63 Gb/s 확보되어 있어 FDR 대역폭에는 여유가 있음.

opensm 관련 - 이거 없으면 통신 안 됨

IB는 Subnet Manager가 LID를 할당해주지 않으면 링크가 물리적으로 붙어도 통신이 안 됨. sminfo가 timeout 나는 걸로 봐서 패브릭에 SM이 없는 상태였고 스위치도 없어서 호스트 중 하나가 돌려야 함.

직결이라 포트마다 별개의 subnet이 되는데 opensm 하나에 GUID를 여러 개 주는 방식은 동작하지 않음. ibstat상으로는 두 포트 다 Active로 보이는데 IPoIB broadcast multicast group이 한쪽에만 생성돼서 실제로는 통신이 안 됨. dmesg에 multicast join failed ... status -22가 찍히고 carrier=0 상태로 남음.

포트별로 나눠서 띄워야 정상 동작함.

opensm -g 0x<port1_guid> --daemon --log_file /var/log/opensm.p1.log
opensm -g 0x<port2_guid> --daemon --log_file /var/log/opensm.p2.log

각 로그에 SUBNET UP이 찍히면 정상.

현재 장비 상태

opensm과 IPoIB IP는 확인용으로만 잡아둔 거라 재부팅하면 사라짐. 모듈 로드와 링크는 재부팅해도 유지됨.

항목 재부팅 후
kmod 모듈 로드 유지
IB 링크 LinkUp 유지
opensm 사라짐 (unit이 disabled 상태)
IPoIB IP 사라짐 (NM 프로파일 없음)

opensm이 없으면 포트가 Initializing에서 멈춤. 링크는 붙어 있는데 통신만 안 되는 상태라 헷갈리기 쉬움. 재부팅 후 복구 절차는 레포 README에 적어둠.

하단 두 노드

CentOS 7.9.2009, 커널 3.10.0-1160.119.1.ase3.1.

카드는 상단과 동일하고 현재는 mlx4_core가 정상 바인딩되어 있음. CentOS 7 커널에는 mlx4가 기본 포함이라 문제가 없음. 이쪽은 모델 확인만 했고 포트 상태나 링크는 보지 않았음.

Rocky 10으로 올리면 상단과 똑같은 증상이 발생함. 카드가 동일 모델에 동일 펌웨어라 위에서 만든 kmod로 그대로 커버됨.

남은 것

  • Secure Boot - 현재 4노드 다 disabled라 문제없지만 켜진 환경에서는 모듈에 서명이 없어 로드되지 않음. in-tree 빌드로 커널 서명 키를 받는 우회는 위 이유로 불가능하고 MOK 등록이 필요함
  • ase4 repo 업로드와 CI 연결
  • opensm 서비스 영구 등록 (반복 측정할 거면 필요)
실행 #3

오 지호님이 14일 전에 변경

  • 문서 항목에 kmod-mlx4.tar 값이 추가되었습니다.
실행 #4

오 지호님이 14일 전에 변경

kmod-mlx4 첨부파일 받아서 README.md 확인

커널 버전에 맞는 rpm 설치하고 opensm infiniband-diags 설치해서 사용하기

kmod-mlx4 gitlab repo

실행 #5

오 지호님이 13일 전에 변경

  • 완료 기한 항목을 지정했습니다. (2026/09/16)
  • 상태 항목을 변경했습니다 (신규 => 진행)
  • 담당자 항목을 지정했습니다. (오 지호)
  • 시작 시간 항목을 지정했습니다. (2026/09/10)
  • 추정 시간 항목을 지정했습니다. (32:00 시간)
  • 난이도 항목을 지정했습니다. (보통)
  • 점수 항목을 변경했습니다 (0.00 => 7.12)
실행 #6

오 지호님이 12일 전에 변경

  • 파일 항목에 gms-hang.txt 값이 추가되었습니다.
  • 파일 항목에 gms-wedge3-bt.txt 값이 추가되었습니다.
  • 파일 항목에 gms-wedge4-bt.txt 값이 추가되었습니다.
  • 파일 항목에 gms-drop-repro-13724.txt 값이 추가되었습니다.
  • 파일 항목에 gms-ledis-stale-13724.txt 값이 추가되었습니다.
  • 파일 항목에 failover-downscale-13724.txt 값이 추가되었습니다.
  • 파일 항목에 failover-B30-clean.txt 값이 추가되었습니다.
  • 파일 항목에 failover-B40-confirm.txt 값이 추가되었습니다.
  • 파일 항목에 soak-metrics.log 값이 추가되었습니다.
  • 점수 항목을 변경했습니다 (7.12 => 5.50)
실행 #7

오 지호님이 12일 전에 변경

  • 파일 항목에 gms-crash-strace-window.txt 값이 추가되었습니다.
실행 #8

오 지호님이 12일 전에 변경

  • 파일 항목을 지웠습니다. (gms-hang.txt)
실행 #9

오 지호님이 12일 전에 변경

  • 파일 항목을 지웠습니다. (gms-wedge3-bt.txt)
실행 #10

오 지호님이 12일 전에 변경

  • 파일 항목을 지웠습니다. (gms-wedge4-bt.txt)
실행 #11

오 지호님이 12일 전에 변경

  • 파일 항목을 지웠습니다. (gms-drop-repro-13724.txt)
실행 #12

오 지호님이 12일 전에 변경

  • 파일 항목을 지웠습니다. (gms-ledis-stale-13724.txt)
실행 #13

오 지호님이 12일 전에 변경

  • 파일 항목을 지웠습니다. (failover-downscale-13724.txt)
실행 #14

오 지호님이 12일 전에 변경

  • 파일 항목을 지웠습니다. (gms-crash-strace-window.txt)
실행 #15

오 지호님이 12일 전에 변경

  • 파일 항목을 지웠습니다. (failover-B30-clean.txt)
실행 #16

오 지호님이 12일 전에 변경

  • 파일 항목을 지웠습니다. (failover-B40-confirm.txt)
실행 #17

오 지호님이 12일 전에 변경

  • 파일 항목을 지웠습니다. (soak-metrics.log)
실행 #18

오 지호님이 11일 전에 변경

볼륨 사용량 한계 측정 결과

환경

  • ASE4 노드 2대 HA(pacemaker + stonith/IPMI). 노드당 16코어 / 64 GiB, 16 Gbit FC로 연결된 SAN 10 LUN -> VG1 / thin pool 95 GiB
  • 데이터 경로(NFS VIP)는 1 GbE다. 장착된 10/25 GbE 포트는 이번 캠페인에 쓰이지 않았다(처리량 천장 해석에 중요, "지속 부하" 절 참조)
  • Manila 드라이버는 8 vCPU / 16 GB 가상장비에서 구동. 단계 간 비교가 무효화되지 않도록 캠페인 내내 VM 스펙/구성은 동결
  • 공유 1개 = thin LV + XFS + NFS export + pacemaker 리소스 2개 (Filesystem + ShareCtl) + 제약 세트, /etc/exports.d 1파일
  • Manila VM에서 manila API로 1 GiB 공유를 연속 생성/삭제하며 10개 간격으로 메트릭 스냅샷 + 전 공유 IO 버스트(dsync 덮어쓰기) 측정

결과 요약

항목 값
운영 권장 규모 공유 30개 - 변형 비용/운영 여유 기준 (failover는 제약이 아님, "운영 시사점" 참조)
생성 도달 한계 ~60개 (백엔드 붕괴 아님, 원인은 아래)
데이터플레인 처리량 개수 무관 - soak 10-60 전 구간 63-73 MiB/s 평탄, 62개 버스트 83 MiB/s(최고)
생성/삭제 시간 생성 ~70s + 3.6s x N / 삭제 ~55s + 2.7s x N (N = 공유 수)
failover IO 재개 노드 사망 53-57초(9샘플, 10-60개 전 구간) / standby 10-17초 / 관리 펜스 46-54초
쓰기 손실 전 시나리오 0 (NFSv4 hard mount)

스케일 측정

  • 1 -> 63개 램프: 60개 부근부터 생성 실패 시작. 원인은 드라이버의 pacemaker 리소스 시작 대기 예산(재시도 5회)에 있다. 공유 수가 늘수록 클러스터 전이 지연이 커지는데 예산은 그대로다. 백엔드 자체는 끝까지 버텼다. 실제 start 작업은 개수와 무관하게 1초 미만이지만, 공유 수가 늘면 CIB 변경 후 전이 완료까지 수십 초~2분이 걸리고 그 창에 드라이버가 포기한다(시작 1초 전에 포기한 사례 확인). 대기 예산을 conf opt으로 설정화하면 한계를 밀어 올릴 수 있다. 개선 과제로 이월
  • 60개부터 드라이버 periodic stats 보고도 60s 타임아웃 간헐 발생(치명적이지 않고 용량 보고만 늦어진다)
  • 노드 리소스는 62개까지 완만한 선형 성장만 관측, 이상 징후 없음. CIB 36KB(1개) -> 405KB(41개) -> 577KB(62개), CIB 쿼리 14ms -> 58ms, 메모리/Slab/load 평탄
  • 변형 비용은 CIB 쓰기가 유발하는 선거 -> join 지연 -> 전이 재계산 화해 사이클에서 나오며 62리소스 규모에서 ~2분이다. maintenance 홀드는 실측 0-20초로 공유 수와 무관해 비용에 거의 기여하지 않는다. 램프 전체에서 선거 515회 관측

생성/삭제 시간 분포 (전체 로그 기준, 생성 n=137 / 삭제 n=111)

공유 수 생성 med(범위) 삭제 med(범위)
1-9 86s (70-106) 66s (5-81)
11-19 116s (101-141) 101s (96-106)
21-29 141s (126-161) 106s (96-151)
31-39 177s (146-202) 136s (116-161)
41-49 217s (192-242) 166s (142-212)
51-63 272s (242-298) 206s (156-221)

운영 환산: 30개 기준 개별 생성 ~2.5분, 삭제 ~2분, 풀 프로비저닝 ~1시간. 40개 기준 개별 생성 ~3분, 삭제 ~2.5분, 풀 프로비저닝 1.5-2시간.

지속 부하 (soak, 전 개수 완성)

공유 시간 패스 총량 agg (MiB/s) dip
10 39.8분 65 170 GiB 66-70 0
20 39.7분 34 170 GiB 69.5 -> 71.5 0
30 38.5분 21 161 GiB 65.8 -> 66.0 0
40 43.7분 17 170 GiB 전반 61 -> 후반 66 단발 45/47, 즉시 회복
40 (재확인) ~40분 18 - 60-68 평탄 0
50 40.4분 13 162.5 GiB 67-70 평탄(전반 67.8 -> 후반 69.2) 0
60 40.3분 11 165 GiB 68-71 평탄(전반 69.0 -> 후반 70.4) 0
  • 전 구간 동일 천장이다. 병렬 스트림 수가 줄어도 총량은 그대로고 경합만 감소한다. 개수 무관성은 10-60 6점으로 완결됐다
  • 천장 63-73 MiB/s는 네트워크 경로가 만든다(장비 반납 직전 분리 측정). 로컬 저장 경로(XFS -> thin LV -> thin pool -> SAN)는 지속 dsync 덮어쓰기 40 GiB(8 GiB x 5패스, 랜덤 데이터)에서 501-557 MiB/s, 감쇠 0. 반면 드라이버 VM -> VIP 생 TCP는 74.2 MiB/s, 노드 간은 102-105 MiB/s. 상한을 쥐고 있는 쪽은 디스크도 어레이도 아닌 1 GbE 링크이며 캠페인 천장은 그 바로 아래에 있다. 10 GbE에서 천장이 어디로 가는지는 미검증
  • 50-60개는 장비 반납 당일 추가 완료. 60개는 manila-share 매니저 결함 (부록 A 결함 #11)을 우회해 백엔드 API로 직접 구성한 fleet으로 측정했다. data plane 자원(LV/export/클러스터 자원)은 정상 경로와 동일
  • 부하 중 load 8/16코어 이하, 메모리/Slab 평탄, thinpool 고정(덮어쓰기), 커널/pacemaker 오류 0

Failover (공유 10-60개 + IO 부하 중, 전 개수 완성)

시나리오 10개 20개 30개 40개 50개 60개 쓰기 손실
노드 사망(전원 차단) + stonith 대응 53.1초 54.2초 54.8초 54.1초 54.6초 56.5초 0
관리 펜스 (pcs stonith fence) 54.1초 46.3초 53.2초 52.8초 48.2초 46.6초 0
standby (계획 이동) ~9.6초 ~11초 ~10.5초 ~16초 ~17초 14.5초 0
  • 노드 사망의 IO 재개는 개수와 무관하다. 구조는 fence 감지+실행(34-43초, 개수 무관) + 자원 이동(수 초, 개수가 줄수록 단축) + 클라이언트 복구(~11초)이고 10-60개 전 구간 53-57초(9샘플)로 1분 요건을 충족한다. 잔여 개선축은 fence 감지 단축(현 34-43초로 전체의 3분의 2를 차지)과 클라이언트 NFS 재시도 간격이다. 차단 순간 발행 중이던 RPC 1개는 재시도 슬롯(timeo 60s)에서 +60초 뒤 완료되는 꼬리가 남는다(신규 흐름과 무관, 클라이언트 마운트 튜닝 영역)
  • standby는 전 개수 10-17초다. 10-30개 10-11초, 40-60개 14.5-17초로 자원 수에만 비례한다. 계획 이동의 표준 수단
  • 10/20/30/40/50/60 전 개수에서 재현 확인: 노드 사망 53.1/54.2/54.8/54.1/54.6/56.5초로 개수 무관, 3종 전부 1분 내, 쓰기 손실 전 개수 0. 50-60개는 권장 상한(30)을 넘는 검증 확장이다(생성 관리 비용은 별도 유의)
  • 관리 펜스에 무중단 구간은 없다. 전 측정에서 46-54초 IO 정지가 실측된다. 자원은 항상 fence 완료 후에 이동하므로(DC/비-DC 무관) 유지보수 격리와 이동은 standby로만 할 것
  • 이중 장애(활성 노드 사망 + 생존 노드 과부하)가 발생할 수 있지만 IO는 약 55초 만에 자동 복구되고 실패 쓰기는 0이다. 복구는 NFSv4 hard mount가 담당한다. "생존 노드 관리평면 정지"로 보였던 현상은 텔레메트리 분석 결과 D-state 누적에 따른 과부하 상태(신규 처리 불가 ~4분, 자동 회복)로 규명됐다. 노드 53사이클 실측 발병률 ~7.5%, 전부 자동 회복. 상세는 부록 A 결함 #5
  • 양노드 동시 강제 재부팅(완전 정지) @60개도 무개입 자동 복구 - 상호 펜스 없이 합류, 공유 자원 전체 자동 시작, 총 IO 아웃티지 ~3분 7초, 쓰기 손실 0. 클러스터 전체 재시작이 필요한 유지보수 시의 기대치
  • 운영 교훈
    • fence(reboot) 후 노드는 약 2분 내 자동 재부팅된다. 수동 전원 개입은 3분 대기 후에. 60-90초 만에 조기 확인하고 노드가 살아나지 않았다고 오판하지 말 것
    • 계획된 재부팅은 standby 후 clean shutdown으로만 해야 한다. unclean 종료는 fence가 먼저 발동한다. pacemaker/corosync 수동 정지는 펜싱 유발이므로 금지
    • 시나리오 연속 실행 시 standby는 반드시 해제하고 다음으로(잔류 상태의 노드 사망은 인수 불가 = 전체 정지)

운영 시사점

  • 30개 권장의 근거는 변형 비용이다: failover는 10-60개 전 구간에서 1분 요건을 충족하므로 개수 선택을 제약하지 않는다. 개수를 묶는 쪽은 관리 평면 비용이다. 개별 생성 ~2.5분, 삭제 ~2분, 30개 풀 프로비저닝 ~1시간이고 개수에 선형 증가한다. 여기에 운영 여유를 더한 값이 30개
  • 40개까지 램프(10/10 1차 성공)-soak(평탄)-데이터플레인이 검증돼 있고 50-60개는 soak-failover 축이 추가 완료됐다. 요건이 완화되면 재측정 없이 40개까지 올릴 수 있다
  • 확장 시 유의: 50개 부근부터 생성 재시도(~15%)가 늘고 manila 프로젝트 quota 기본값 50에 걸린다. 상향이 필요하다(PUT /v2/quota-sets/<project>)
  • 생성/삭제는 변형 순간의 전이 지연 변동성이 있으므로 저부하 시간대에 여유 있게 실행
  • 대량 생성 직후에는 마운트/실접속 검증을 별도 스텝으로 실행할 것. 공유 상태가 available이어도 access rule이 조용히 소실돼 마운트가 거부되는 결함이 있다(부록 A 결함 #9). 빈도가 개수에 비례해 급등한다
  • 운영 중 마주칠 수 있는 장애와 처치 절차는 부록 B에 모았다

결론

ASE4 2노드 HA에서 Manila 공유 30개 운영을 권장한다. 변형 비용과 운영 여유가 기준이며 데이터플레인은 측정 범위(62개) 내내 개수와 무관하게 안정적이었고 40개는 전 축이, 50-60개는 soak-failover 축이 검증돼 확장 여지는 충분하다. failover는 노드 사망 53-57초(9샘플, 10-60개 전 구간), standby 10-17초, 관리 펜스 46-54초로 세 시나리오 모두 1분 요건을 충족했고 쓰기 손실은 전 시나리오 0이었다. 한계는 관리 평면의 변형 비용(공유 수에 비례하는 생성/삭제 시간과 전이 지연)과 드라이버 대기 예산에서 온다. 대기 예산 설정화 + 변형 비용 축소가 이후 개선 과제다.


부록 A. 캠페인에서 발견한 결함 10건

측정 과정에서 드러난 백엔드(GMS)/드라이버 결함이다. 본문 수치는 전부 이들을 규명하고 조치한 뒤의 값이므로, 수치를 재현하려면 조치 상태를 함께 맞춰야 한다.

번호는 캠페인 기록과 동일하게 유지했다(#4는 결함이 아닌 초기 설정 실수여서 제외).

#8은 제품 결함이 아니라 네트워크 구성 문제다. 링크도 스위치도 정상으로 보여 찾기 어려운 함정이라 목록에 남겨뒀다.

# 결함 심각도 상태
1 GMS 웨지 - SIGCHLD 핸들러의 로깅 재진입 무한루프 치명 패치 적용, 소멸 실증
2 웨지 방치가 노드 펜싱(IPMI reboot)까지 확산 높음 기전 규명
3 삭제 EBUSY 잔해 (failover 이후) 중간 처치 절차 확립
5 생존 노드 D-state 누적 과부하 붕괴 높음 메커니즘 완결, 자동 회복(~4분) 확인
6 journald 로테이션으로 증거 소각 관측 재시험 계측 규칙 정립
7 자원 원장-CIB 불일치 -> resource API 전체 500 치명 핫패치로 자가치유 활성화
8 VIP가 rp_filter로 차단되는 본드에 구성 환경 구성 수정 완료 (source policy routing)
9 allow_access가 혼잡 중 조용히 소실 높음 원인 규명 + 회복 레시피 확립
10 GMS 프로세스 크래시 (crm_mon 타임아웃 탈출) 높음 원인 확정, 미수정
11 manila-share 매니저 웨지 (init 재수출 동결) 높음 우회 확립, 잔여 조사

공통 뿌리: #1, #7, #9, #10, #11은 전부 GMS가 단일 프로세스 + 단일 스레드 reactor라는 같은 구조에서 나온다. prefork/worker 격리 하나가 이 계급 전체를 소멸시킨다. 부록 C의 최상위 권고다.

A-1. GMS 웨지 (치명, 수정 병합 완료)

공유 30개 부근부터 관리 평면이 통째로 멈춘다. 1차 캠페인에서 5회 재현.

  • 증상: GMS 마스터가 fork한 자식을 reap하지 못해 좀비 누적(13분+), reactor는 살아 있으나 신규 TCP 접속 수용 중단(VIP는 물론 노드 로컬에서도 connect 불가). 단일 스레드가 CPU 100% 점유, strace -c 2초간 시스템콜 0회 = I/O 없는 userspace 무한 연산(최장 35분+ 무진행)
  • 노드와 데이터플레인은 무사: load 1.1, pacemaker CPU 1% 미만, 기존 29공유 NFS 쓰기 56 MB/s(기준선 수준)
  • 원인(gdb 백트레이스 4샘플로 확정): SIGCHLD 핸들러가 로그를 남긴다. 로그 핸들이 UTF-8 설정 때문에 PerlIO::encoding 레이어를 끼고 있어 print마다 encode-flush가 perl 코드를 재진입하고 그 재진입 루프의 opcode 경계에서 지연 디스패치가 SIGCHLD를 만나 핸들러가 다시 돌고 핸들러의 로깅이 같은 핸들로 또 print한다. 종료 조건 없는 재진입 로깅 사이클이다
  • 공유 수 의존성의 이유: DEBUG 로그가 exec 반환값 전체를 Data::Dumper로 덤프하는데 30개부터 한 덤프가 300KB+. 문자열이 커질수록 flush당 재진입 횟수와 디스패치 창이 늘어난다. 생성 시간 drift(65s -> 172s)도 같은 경로 비용
  • 범위: 다른 프로세스 인스턴스, 다른 작업 방향(생성 vs 삭제), 다른 자식 명령에서 프레임 단위로 동일하다. 특정 API에 국한되지 않는 모든 변형 API가 공유하는 기저 결함이다
  • 배제된 가설(전부 라이브 검증): 노드 자원 고갈(load 1-3, FD 26/1024), 방화벽 rate-limit(룰셋 비어 있음), conntrack 고갈(모듈 미적재), accept queue 포화(21/4096), IPC select 스핀(시콜 0회)
  • 패치: 핸들러를 no-op으로. 재검증에서 1 -> 62개 램프(이전 웨지 지점 30/33/37 전부 통과) + 62개 전량 삭제 동안 웨지 0회, GMS 재시작 0회 (패치 전: 36개 삭제 중 재시작 5회 + 노드 펜싱 2회)
  • 정식 수정 병합 완료: libgms !293 (main 병합, 별도 일감 #13740). https://gitlab.gluesys.com/dev3/libgms/-/merge_requests/293. IPC.pm의 SIGCHLD 핸들러를 no-op으로 바꾸되 핸들러 자체는 설치된 채로 둔다(AnyEvent 모델이 올라올 때 libev가 SIGCHLD를 가져가 waitpid(-1)로 reap하므로, 명시적 waitpid를 남기면 ECHILD를 받는다). 또 local로 유지해야 한다(libev가 %SIG를 우회하므로 전역 1회 설치는 조용히 덮인다). 잃은 로그 라인은 안전한 동기 지점에서 대신 기록
  • 주의: 노드에는 배포본을 직접 고친 핫패치가 올라가 있다. !293이 포함된 RPM으로 갱신하기 전까지는 업데이트 때마다 소실되므로 재적용 절차가 필요하다

A-2. 웨지 방치가 노드 펜싱까지 확산 (높음, 기전 규명)

  • 생성 플로우는 pcs node maintenance --all 진입 후 작업하고 나오는 구조라 hard wedge로 죽으면 양 노드 maintenance 잔류(모니터링 정지 상태로 방치)
  • 반쯤 삭제된 공유의 ShareCtl stop이 Not configured: share_path is mandatory로 실패하면 pacemaker가 이를 치명 오류로 분류해 해당 노드를 펜싱(IPMI reboot) 한다. 1차 캠페인 정리 중 펜스 2회 실측
  • 즉 관리 평면 결함이 HA 계층을 타고 노드 재부팅까지 번진다. 다만 양 노드는 재부팅 후 자동 복귀하고 VIP가 페일오버되어 데이터플레인은 무중단
  • 완화: 웨지 방치가 수 분 이상이어야 펜스로 간다. GMS 응답 불능 모니터링 + 빠른 재시작이 1차 방어선. ShareCtl stop 실패를 fatal로 두는 것과 maintenance 잔류가 함께 펜스 조건을 만든다

A-3. 삭제 EBUSY 잔해 (중간, 처치 절차 확립)

failover로 LV가 마운트된 채 넘어간 뒤 삭제하면 GMS delete가 "Device or resource busy"로 선행 실패하고 manila는 error_deleting으로 남는다. 처치 절차는 부록 B-2.

A-5. 생존 노드 D-state 누적 과부하 붕괴 (높음, 메커니즘 완결)

상대 노드의 사망/펜스/재부팅이 FC 패브릭을 흔들 때 생존 노드의 관리평면이 죽는 현상. 캠페인 내내 확률적으로 재발해 가장 오래 미해결로 남았다.

  • 프리즈처럼 보였지만 실제로는 D-state가 누적돼 과부하가 온다. 생존자의 자원 인수 (DM/LVM 작업 60여 건)가 FC 플랩 구간과 겹치면 D-state가 21 -> 45개로 누적돼 load 50까지 폭주하고, 이때 fork가 필요한 신규 작업만 정지한다. 기존 루프와 커널 NFS는 끝까지 생존
  • 완결된 인과 사슬(텔레메트리가 D 프로세스 이름까지 포획): 인수 시 GMS가 자원마다 vgs(VG 전체 스캔)를 실행하고 pacemaker가 네트워크 자원 (tc/ifconfig/ip)을 조작 -> FC 플랩에서 DM/LVM ioctl이 D로 블록 -> 후속 vgs/tc/ip와 유입 ssh 연결의 sshd-session까지 D로 적층 -> load 50, fork/exec 질식(PAM 응답 8ms -> 1.2초) -> FC 회복되면 D가 빠지며 자동 회복. ssh가 프리즈된 것처럼 보인 현상도 sshd-session이 D로 적층된 결과였다
  • 펜스 없이 ~4분 만에 자동 회복한다. 밤샘 53사이클 실측 발병률 4/53(~7.5%), 전부 자동 회복. IO 영향은 펜스가 도는 1사이클(~27초)뿐
  • 클린 재부팅만으로도 유발된다. 노드 사망이 필요 없고 유지보수 재부팅이면 충분하다. 특정 노드의 하드웨어와 무관하게 생존자 역할을 맡은 쪽에서 발생한다
  • 확률적이었던 이유: 인수 churn x FC 플랩 x 임계 초과의 3중 조건이 겹쳐야 발병한다. 의도적 재현은 6회 연속 실패했고 그 중 5회는 생존자가 FC 스톰 전체를 받고도 관리평면이 무사했다. 스톰은 필요조건이지만 발병은 저확률임이 실험으로 확정됐다
  • 완화 3안(GMS 측 구현 과제)
    • [최상위] 인수 중 vgs VG 전체 스캔의 캐싱/직렬화
    • FC 플랩 단축
    • sshd MaxStartups로 증상 완화

A-6. journald 로테이션으로 증거 소각 (관측)

  • GMS DEBUG 전체 덤프 + pacemaker 선거/모니터 폭풍으로 활성 노드 저널이 ~1GB/h 발생, SystemMaxUse=8G 도달 시 oldest-first vacuum이 돈다. 램프 1~50 구간의 저널이 소각돼 사후 복원이 불가능했다
  • GMS 파일 로그(/var/log/gms.log)는 journald와 무관하게 전 구간 보존돼 복원에 결정적이었다
  • 재시험 계측 규칙
    • 램프 전 양 노드 SystemMaxUse 상향(32G)
    • 체크포인트마다 pacemaker+gms 저널을 파일로 덤프
    • 프로브는 rc와 에러 라인을 반드시 남길 것. 에러 스트림을 버리면 즉사(EACCES)를 완료로 기록한다
    • 배치 성공 기준은 최종 사용자 경로(마운트+쓰기 1회)로. 성공 판정이 실패를 삼키면 결함 #9 같은 소실이 성공 로그로 남는다
    • 무중단 판정은 블록된 쓰기의 완료 라인이 나온 뒤에 내릴 것. in-flight 쓰기가 있으면 조회 시점 max-dur가 0.x초로 보여도 무중단이 아니다 (이 오판으로 관리 펜스 2회를 0초로 잘못 기록했다)

A-7. 자원 원장-CIB 불일치로 resource API 전체 500 (치명, 핫패치)

축소 캠페인에서 삭제 10개 전부 error_deleting으로 실패하며 발견. 드라이버의 prepare_delete가 호출하는 GET /pacemaker/resource가 500을 반환하는데, 생성 경로도 같은 list를 쓰므로 생성/삭제가 전부 차단되는 가용성 결함이다.

  • 메커니즘: GMS는 pacemaker 자원 기록을 LedisDB의 SET 멤버로 관리하는데 일부 delete 플로우가 멤버 쌍을 남긴다(CIB primitive는 사라졌는데 원장만 잔류)
  • 왜 자가치유가 안 되는가: Controller의 list()는 세 루프 모두 _delete_stale 자가치유 패턴을 갖추고 있으나, 세 모델이 전부 상속하는 around 'new'의 CIB 미발견 분기가 logconfess(=die)라서 find()가 undef를 반환하는 경로 자체가 존재하지 않는다. 치유 코드 세 벌이 전부 도달 불가능한 죽은 코드였다. 저자가 원장 쓰레기를 실제로 겪은 흔적 (하드코딩 스킵 + "왜 이런 값이 오는지 조사" TODO)은 남아 있으나 근본 계약 불일치는 미인지 상태였다
  • 양노드 원장 분기: 한쪽 노드 원장 106멤버 중 40개 stale, 다른 쪽은 62멤버 0 stale로 자동 화해가 관측되지 않는다
  • 핫패치: around-new의 미발견 분기를 logconfess -> warn + return으로 교체. 주입 시험에서 HTTP 200 복귀 + stale 레코드 17건이 한 번의 호출로 자동 삭제됨을 확인
  • 주의: 이것도 배포본 직접 수정이라 RPM 업데이트 시 소실
  • 즉시 처치 절차는 부록 B-3

A-8. VIP가 rp_filter로 차단되는 본드에 구성 (환경 구성, 수정 완료)

제품 결함이 아니라 네트워크 구성 문제다. 본문 failover 수치는 전부 이 구성을 바로잡은 뒤의 값이다. 같은 구성을 하면 매 failover마다 15-47초를 그냥 잃는다.

  • 구성: VIP의 IPaddr2가 본드(active-backup, 슬레이브 1개)에 붙어 있고, 노드에는 동일 대역의 connected 라우트가 2개(관리 NIC 우선 / 본드), rp_filter는 strict
  • 기전(패킷 + ARP + 노드 캡처 3중 입증): VIP start -> grat ARP가 본드 MAC을 광고 -> 클라이언트 이웃 엔트리가 본드 MAC이 됨 -> 클라이언트의 낙관적 송신과 유니캐스트 ARP 프로브가 본드 슬레이브에 도달은 하지만 rp_filter의 역방향 라우트 비정합(유입은 본드, 역방향은 관리 NIC)으로 사일런트 드롭, ARP 응답조차 나가지 않음 -> 클라이언트가 STALE->DELAY->PROBE->FAILED(~14초)를 거친 뒤 브로드캐스트로 재해석 -> 관리 NIC이 응답하며 정상 경로 확보 -> TCP/NFS 재전송 백오프 정렬(~0-30초)
  • 링크와 스위치는 정상이었다. 가설 이력은 본드 링크 불량 -> fence 디바이스 비대칭 -> (최종) 노드 내 rp_filter 순이었다
  • 매 takeover마다 결정적으로 발생한다(grat ARP가 항상 본드 MAC을 광고). 빠른/느린 차이는 그 뒤 재전송 타이밍뿐이라 개수/노드와 무관했다
  • 수정: source policy routing으로 본드를 대칭 VIP 경로화. VIP 발신을 전용 라우팅 테이블 -> 본드로 보내는 규칙 + src_valid_mark=1(strict 호환). 멱등 스크립트 + systemd 유닛(양 노드)으로 배포
  • 영속 함정 3건을 실증 후 보강했다
    • NM dispatcher는 본드 up에 발화하지 않는다
    • systemd는 실행 비트 없는 스크립트를 EXEC 거부한다 (scp 복사본이 644)
    • standby로 VIP가 내려가면 본드 재동기화가 정책 라우트를 지운다. 부팅 유닛이 성공한 뒤에도 소실 가능하다. 대응으로 30초 간격 재주입 타이머를 넣어 최대 30초 노출로 자가치유하게 했다
  • 효과: 본드 MAC 직결 복구(grat ARP 후 5초 내 REACHABLE), 노드 사망의 IO 정지부 54-96초 -> ~11초, 총 재개 t0 기준 53-57초
  • 잔여: 발행 중이던 RPC 1개는 클라이언트 NFS 재시도 슬롯(timeo 60s)에서 +60초 뒤 완료된다. 신규 흐름과 무관하며 클라이언트 마운트 튜닝 영역

A-9. allow_access가 혼잡 중 조용히 소실 (높음, 규명 + 회복 레시피)

공유는 available인데 마운트가 access denied로 막히면서 발견. 30개 재구성 중 3건(10%).

  • 타임라인: 드라이버가 update_access 처리 중 GMS를 호출했으나 GMS가 응답 없이 연결을 종료(GMS 로그에도 흔적 0) -> 드라이버 예외 -> manila rule 갱신 실패 -> 이후 11분간 재시도 0회(manila의 access rule 재시도가 이 케이스를 줍지 못한다)
  • 빈 export의 탄생: 이후 다른 공유의 allow가 유발한 bulk zone 재적용이 전체 zone을 재렌더하는데, 이 공유의 zone은 존재하되 rule이 없어 0바이트 export로 렌더된다
  • 메커니즘 확정(strace): 생성 러시 중 fork/reap 폭풍(SIGCHLD 62회/3초, 자식 IPC close 191회)이 단일 스레드 main을 점유해 연결들이 ~3.5초간 미서빙되다 보류 연결 전부가 80ms 버스트로 close된다. inactivity timeout 만료가 아니라 reactor 레벨의 일괄 폐기다. GMS는 요청을 항상 읽는다 (131/131). 읽고도 처리를 마치지 못한 채 연결이 죽는 셈이다
  • 실패가 성공으로 위장된 3겹
    • allow API는 202(비동기 수락)
    • 생성 스크립트 성공 기준이 share available뿐
    • 공유 상태 available + rule 상태 'exists'

진짜 실패는 최종 마운트 거부에서만 드러난다

  • 빈도가 개수에 급격히 비례: 30개 3/10 -> 40개 3/10 -> 50개 10/10 전멸
  • 생성 러시뿐 아니라 failover churn에서도 축적된다: 밤샘 53사이클 후 50공유 중 12개 export가 0바이트였고 그 중 6개는 exports.d 심볼릭 링크까지 유실돼 있었다
  • 회복: 러시가 끝난 뒤 느린 토글(deny 27-32초 + allow + 재마운트)로 9/10 복구된다. 러시 중에만 죽고 러시가 끝나면 토글이 먹힌다. 예외는 AccessGroup 원장 결손으로, 이 경우만 delete + 재생성. churn으로 생긴 0바이트 export는 토글(재렌더 재유발)을 피해 staging 직접 기록 + exportfs -r로 복구하는 편이 안전
  • 운영 교훈: 대량 생성 직후 마운트/실접속 검증을 별도 스텝으로 실행할 것 (available != 마운트 가능). 저부하 시간대 생성 권고가 이 결함에도 동일 적용

A-10. GMS 프로세스 크래시 (높음, 원인 확정 미수정)

  • 사슬: 토글 처리 -> 파일 동기 -> crm_mon 실행 -> IPC 타임아웃이 FATAL die -> 상위 try 블록을 뚫고 탈출 -> 전역 해체(global destruction) 진입 -> 해체 중 로거까지 die -> exit 255 -> systemd 재시작 (관리평면 공백 11초)
  • 2연속 크래시 실측: 재시작 2.5분 뒤 다시 사망했다. 부팅 직후 전체 재동기화(60+ 자원)가 같은 타임아웃을 다시 밟았다. 재시작 후에는 안정
  • 파급: 사망 순간 in-flight 드라이버 호출 전량 실패(RemoteDisconnected 48 + Connection reset 16 실측). 데이터플레인은 무영향(커널 NFS)
  • 과거 원인 불명으로 남았던 펜스 연쇄 사고도 동일 카운터로 소급 확인됨
  • 운영 규칙: 대량 변형 중 GMS 사망 + 자동 재시작(수 초~11초)은 있을 수 있고 재시작 직후 1회 재사망도 가능하다. 즉시 개입하지 말고 2회째 재시작 후 확인할 것

A-11. manila-share 매니저 웨지 (높음, 우회 확립)

60개 확장 시도 중 발견. 재시작 2회 모두 같은 지점에서 재현되는 결정적 웨지(확률 아님)다.

  • 증상: share 생성 요청이 API에서 creating으로 수록된 뒤 무진행. 스케줄러 로그에 create 스케줄 흔적 0, share 매니저 컨테이너는 "Re-exporting 50 shares"(init_host의 재수출) 직후 로그가 영구 동결된다. periodic과 RPC 서버가 init을 못 마쳐 아예 뜨지 않는다. 큐에 create cast만 쌓인다. 컨테이너 상태는 Up이라 헬스체크로 잡히지 않는다
  • 원인 스코프: 매니저의 GMS 호출이 읽힌 채 무기한 대기한다. 50공유 규모에서 GMS 단일 스레드 reactor가 큐에 쌓인 재수출 요청 처리(건당 CIB 파싱 + fork, DEBUG 덤프 수만 라인)에 장기 점유돼 후속 요청 응답을 굶기는 상태. 매니저 worker는 epoll idle, 연결은 ESTABLISHED
  • 미해명 잔여: client timeout=60s인데 ReadTimeout이 발화하지 않는다. 응답 drip 가설은 미확정이며 매니저 프로세스 스택 덤프가 필요했으나 장비 반납으로 미실시
  • 해제: VIP 보유 노드의 GMS 재시작. 다만 GMS가 재포화되면 재발(실측 1회)
  • 파급: 재수출 진행 중이던 44공유가 error로 전이(reset_status 일괄 복구로 해소), 재수출 중단마다 결함 #9의 빈 export가 13개 추가 발생
  • 우회(본 캠페인 채택): manila를 거치지 않고 드라이버가 보내는 GMS REST 호출열을 그대로 재현해 직접 구성했다. 차이는 대기 예산뿐 (START_RETRIES 5x2s 대신 60x5s)이며 멱등 설계라 중단 시 이어서 재실행 가능. 60개 확장 검증 자체가 이 우회로 수행됐다. manila의 관리 장부 없이도 data plane 구성이 완전 동작함이 부수적으로 실증됐다

부록 B. 장애 처치 레시피

B-1. GMS 웨지(응답 불능) 재발 시

  1. 구분: 노드는 살아 있는데 API만 무응답(+ 패치 전이라면 좀비 다수)이면 웨지다. 노드 자체 사망(ping/콘솔)과 구분할 것
  2. systemctl restart gms (stop 타임아웃 -> kill 후 재기동)
  3. pcs node maintenance --all 잔류 확인 -> pcs node unmaintenance --all
  4. 반쯤 만들어진 공유 잔해는 B-2 절차로 정리

수 분 내에 재시작하면 펜스로 가기 전에 회복 가능하다(부록 A 결함 #2). GMS 응답 불능 모니터링 + 빠른 재시작이 1차 방어선.

B-2. 삭제 EBUSY 잔해 / 반쯤 만들어진 공유

  1. pcs resource delete share_VG1_manila_<inst> + pcs resource delete rsc_VG1_manila_<inst>
    • cibadmin --delete --xpath 대신 반드시 pcs: constraints가 참조하는 primitive는 cibadmin이 schema validation으로 거부한다. pcs는 constraints까지 함께 정리한다
  2. 필요 시 umount -> lvchange -an VG1/manila_<inst> -> lvremove -f VG1/manila_<inst>
  3. manila reset_status로 available 전환 후 DELETE /v2/shares/{id} (POST action {"delete": {}}는 400 - DELETE verb를 쓸 것)

주의: 리소스/LV 이름은 manila share UUID가 아니라 share instance id (manila_<hex>) 기준이다. 접두사를 빠뜨려 lvchange/lvremove가 조용히 실패한 사례가 있다.

B-3. 삭제가 전량 error_deleting + "There are no resource" 500

생성까지 함께 막히는 상태다(부록 A 결함 #7). manila-share 로그에 GET /pacemaker/resource failed with HTTP 500: There are no resource가 있으면 원장-CIB 불일치다.

  1. VIP 보유 노드에서 원장 정리 스크립트를 dry-run - stale=N이면 원장 오염
  2. --apply로 stale 멤버 제거
  3. 공유 reset_status -> available 후 DELETE 재시도(통상 경로로 복귀)

핫패치가 적용된 상태라면 GET /pacemaker/resource 호출 자체가 stale을 자동 삭제하므로 1-2단계가 불필요할 수 있다.

B-4. 기타 운영 규칙

  • quota: manila 기본값 50이라 50개 부근에서 413으로 막힌다. PUT /v2/quota-sets/<project>로 상향(테스트는 100/2000 적용)
  • 패턴 킬은 PID로: pkill -f가 자기 커맨드라인에 자기 매칭되어 ssh 셸을 죽인 사고가 3회 있었다. [s]tall-probe 브래킷 트릭 또는 PID 지정
  • 원격 while-read 루프 안의 ssh는 -n으로 stdin 보호
  • ssh 세션 종료 = 원격 백그라운드 소멸: logind가 세션 cgroup 전체를 정리하므로 nohup으로도 살아남지 못한다. 장기 원격 실행은 ssh 연결을 유지한 채 구동할 것
  • 노드는 KST, 드라이버 VM은 UTC다. 로그 대조 시 9시간 보정을 빠뜨리지 말 것

부록 C. 개선 권고

C-1. GMS 측 (우선순위 순)

  1. [최상위] prefork/worker 격리. 단일 스레드 livelock이 서비스 전체 장애가 되는 구조를 제거한다. 결함 #1, #7, #9, #10, #11 계급 전체가 한 번에 소멸한다. 코드에 hypnotoad 주석이 이미 존재
  2. [최상위] 인수 중 vgs VG 전체 스캔의 캐싱/직렬화. 결함 #5의 근본 인자이자, 생성/삭제 시간이 공유 수에 선형 증가하는 O(n) 비용과 같은 뿌리
  3. 시그널 핸들러에서 로깅 금지. libgms !293으로 병합 완료. 남은 동일 뿌리 항목은 로그 핸들의 Encode 레이어 제거(재진입 표면 자체를 없앰)와 DEBUG 전체 덤프 축소(웨지 트리거 + 시간 drift + 저널 소각을 동시에 유발)
  4. 자원 원장 정리 보장 - delete 플로우의 kvstore 정리를 finally/END로 보장, 양노드 Ledis 분기 해소(복제 또는 단일 스토어). find()의 undef 반환 핫패치는 정식 커밋으로 이관 필요
  5. 혼잡 시 연결 종료 대신 정상 에러 응답(결함 #9). 클라이언트가 재시도할 수 있게 한다
  6. crm_mon 타임아웃 die의 탈출 차단 + global destruction 로거 결함 (결함 #10) + IPC exec에 timeout 전달(정의된 경로가 있으나 호출부 미사용)
  7. 공유당 fork 수 축소. cibadmin/pcs CLI 파싱 대신 CIB API, VG 전체 lvdisplay 열거 대신 개별 LV 쿼리. 변형 비용과 결함 #9 양쪽에 직접 효과
  8. zombie 회수 와치독, finally/END 기반 unmaintenance 보장

C-2. 드라이버 / manila 측

  1. _wait_started 대기 예산의 설정화(anystor_pacemaker_start_timeout 등, 예: 900s). 생성 도달 한계 ~60개를 직접 밀어 올리는 항목이다. START_RETRIES / STOP_RETRIES / POLL_INTERVAL을 conf opt으로
  2. manila의 update_access 실패 rule 재시도 보장(결함 #9의 manila 측 절반)
  3. anystor client의 timeout을 (connect, read) 쌍으로 명시화(결함 #11)
  4. manila-share init의 재수출 병렬화 / 타임아웃 상한(결함 #11)

C-3. 운영 측 즉시 조치

  • 노드에 직접 적용한 핫패치 2건(SIGCHLD, 원장 find())은 GMS RPM 업데이트 시 소실된다. 업데이트 후 재적용 절차를 둘 것. SIGCHLD 쪽은 libgms !293이 병합됐으므로 이 MR이 포함된 패키지로 올라가면 해소되고 원장 find()는 아직 핫패치만 있는 상태다
  • VIP source policy routing(결함 #8)은 systemd 유닛 + 재주입 타이머로 배포돼 재부팅에도 살아남는다. 노드 증설/재설치 시 동일 구성 적용 필요
  • GMS 응답 불능 모니터링(결함 #1, #2의 1차 방어선)과 대량 생성 후 마운트 검증 스텝(결함 #9)을 운영 절차에 포함할 것

C-4. 트리아지 방법론 (본 캠페인에서 검증)

관리 평면이 멈췄을 때 /proc/<tid>/syscall의 running 여부, 스레드별 utime delta, strace -c 시스템콜 0회 여부, voluntary_ctxt_switches 정지 여부

  • 이 4개로 스케줄러 livelock, 연산 폭주, I/O 대기를 즉시 구분할 수 있다. 그다음 gdb attach 1회(thread apply all bt)면 결판난다. 노드에 gdb/perf가 없으면 원격 규명이 불가능하므로 미리 설치해 둘 것.
실행 #20

오 지호님이 11일 전에 변경

  • 완료 기한 항목을 변경했습니다 (2026/09/16 => 2026/09/17)
  • 상태 항목을 변경했습니다 (진행 => 해결)
  • 진척도 항목을 변경했습니다 (0 => 100)
  • 점수 항목을 변경했습니다 (5.50 => 4.50)
실행 #22

오 지호님이 11일 전에 변경

  • 목표 버전 항목을 지정했습니다. (1.0)
실행 #23

오 지호님이 6일 전에 변경

  • 점수 항목을 변경했습니다 (4.50 => 3.50)

결함 #10(GMS 프로세스 크래시) 사인 정정 — 9/18 증거 재분석

9/17 결과 댓글 부록 A에서 이 결함의 사인으로 적은 "crm_mon 타임아웃 die가 try를 뚫고 탈출"은 틀렸음이 확인되었다. crm_mon die는 잡혔고, 프로세스를 죽인 것은 잡힌 예외의 unwinding이 남긴 망가진 상태에서 나온 2차 die다. 아래 세 곳을 갱신한다.

  • 부록 A 표 #10 행: GMS 프로세스 크래시 (죽은 Mojo tx 접근의 2차 die) | 높음 | 사인 재확정, 근본 수정 방법 확정
  • 부록 A-10 절: 아래 전문으로 갈음
  • 부록 C-1 항목 6: 아래 갱신문으로 갈음

A-10 전문 (갱신)

  • 사슬(2026-09-18 저널 재분석으로 정정): 종전 기록의 "crm_mon 타임아웃 die가 try를 뚫고 탈출"은 틀렸다. crm_mon die는 잡혔고, 프로세스를 죽인 것은 잡힌 예외의 unwinding이 남긴 망가진 상태에서 나온 2차 die다.
    • 1차 크래시(19:46:02, pid 16701): 19:45:51 crm_mon 타임아웃 FATAL die(요청 PHr2qBYnMQaz)는 [GMS.pm|680]의 예외 처리가 같은 요청 id로 로그 남기고 처리했다 -- try는 작동했다. 10초 뒤 NFS restart 요청(JkphvDJM_yom)이 성공 응답(SHARE_PROTO_CONTROL_OK)까지 마친 뒤 Transaction already destroyed(배포본 Pacemaker.pm:1659)가 처리기 없이 발생 -> 전역 해체 -> exit 255 -> systemd 재시작 (관리평면 공백 11초)
    • 2차 크래시(19:48:29, pid 870155): 재동기화 요청(u3rOcsPPUG3S)이 save_to_file after-modifier(배포본 is_active_node_for_share -> crm_parse)에서 같은 타임아웃 die. 이 경로는 around_dispatch hook 안이라 around_action의 try 범위 밖이다. Mojo의 최후 eval(Mojolicious::_exception, Mojolicious.pm:203)이 포착해 backtrace를 남겼지만 unwinding 중 Transaction already destroyed(Mouse/Meta/Class.pm:391)가 재발생해 사망
  • 왜 구할 수 없었나: Perl에서 unwinding/DESTROY 중 die는 어떤 eval도 못 잡는다. 1차 die를 잡는 것만으로 프로세스가 살지 않는다 -- 잡힌 예외가 스택을 풀며 공유 상태(transaction)를 망가뜨리고, 그 정리 경로가 다시 죽인다. 전역 해체 중 로거 FETCH die(Logger.pm:149)까지 겹쳐 마지막 흔적마저 소실
  • Transaction already destroyed의 정체(2026-09-18 배포 RPM 분석으로 확정): GMS 코드가 아니라 Mojolicious의 에러 메시지다(Mojo Changes: "Connection already closed"에서 개명). 발화점은 Mojolicious/Controller.pm:229 -- sub res { (shift->tx || croak 'Transaction already destroyed')->res }. controller의 tx 참조는 약참조(weakened)라서 커넥션이 닫히면 사라지고, 그 뒤의 $c->res 접근이 croak한다. Mojo FAQ("What does 'Transaction already destroyed' mean?")가 원인을 그대로 서술한다: non-blocking 작업 결과를 오래 기다린 사이 연결이 닫혀 tx가 사라지는 것. GMS에 대입하면 -- action($next->())이 포화 상태에서 IPC 때문에 오래 돌고, 드라이버가 60초 timeout으로 커넥션을 끊고, Mojo가 tx를 치우고, 돌아온 hook 꼬리가 죽은 tx를 건드린다. 배포본 Pacemaker.pm:1659는 around_dispatch 꼬리의 if ($c->res->code < 200 || ...) -- 응답 렌더 뒤 동기화 여부를 정하려고 읽는 줄이다. 배포본 확인은 배포 RPM 추출로 수행했다(1573/1751/1659 전부 backtrace와 줄 단위 일치)
  • 근본 수정 가능(2026-09-18 확정): Mojo FAQ가 권고하는 패턴을 hook 꼬리에 적용하면 사인 자체가 사라진다 -- hook 진입 시점(tx가 살아있음이 보장될 때) my $tx = $c->tx;로 강참조를 캡처하고, 꼬리에서는 $c->res->code 대신 eval { $tx->res->code }를 읽는다. 강참조가 hook 스코프에 tx를 붙잡아 두므로 커넥션 종료 뒤에도 응답 코드를 읽을 수 있고, eval 가드는 응답이 아예 렌더되지 않은 경우(1차 die로 action이 죽은 경우)까지 덮는다(undef면 non-success 취급). 수정 범위는 perl-gms-plugin-pacemaker의 around_dispatch 꼬리 ~3줄이며 배포본과 현행 main에 같은 패턴이 있다. 병행 권고: Role/Execute.pm:51의 logconfess(FATAL die)를 요청 단위 예외로 낮추고, 지연 문맥에서 $c->res/$c->req를 읽는 다른 곳이 플러그인에 더 있는지 훑을 것
  • 수정 방향 정정: 위 강참조 수정이 이 결함(#10)의 사인을 완전히 제거한다. 다만 "unwinding 중 die는 어떤 eval도 못 잡는다"는 Perl 규칙은 그대로라서 같은 계급의 사망이 다른 경로에서 새로 나올 수 있다 -- 그 구조적 안전망이 worker 격리(prefork) 권고다. uncatchable die도 워커 하나로 국한한다
  • 2연속 크래시 실측: 재시작 2.5분 뒤 다시 사망했다. 부팅 직후 전체 재동기화(60+ 자원)가 같은 타임아웃을 다시 밟았다. 재시작 후에는 안정
  • 파급: 사망 순간 in-flight 드라이버 호출 전량 실패(RemoteDisconnected 48 + Connection reset 16 실측). 데이터플레인은 무영향(커널 NFS)
  • 과거 원인 불명으로 남았던 펜스 연쇄 사고도 동일 카운터로 소급 확인됨
  • 운영 규칙: 대량 변형 중 GMS 사망 + 자동 재시작(수 초~11초)은 있을 수 있고 재시작 직후 1회 재사망도 가능하다. 즉시 개입하지 말고 2회째 재시작 후 확인할 것

C-1 항목 6 (갱신)

  1. perl-gms-plugin-pacemaker around_dispatch 꼬리에 tx 강참조 캡처 패턴 적용(결함 #10의 사인 제거, ~3줄) + Role/Execute.pm:51의 logconfess(FATAL die)를 요청 단위 예외로 강등 + IPC exec에 timeout 전달(정의된 경로가 있으나 호출부 미사용)
실행

내보내기 Atom PDF

클립보드 이미지 추가 (최대 크기: 50 MB)