- 출처 / 참고: Red Hat Enterprise Linux 8/9 DM Multipath Configuration and Administration, Linux man-pages (multipath.conf(5), multipathd(8))
명령어:
multipath,multipathd,iostat,dmsetup,cat,systemctl
키워드: DM-Multipath, I/O Hang, queue_if_no_path, no_path_retry, dev_loss_tmo, fast_io_fail_tmo, path_checker, D-state
사용처: SAN/iSCSI 스토리지의 모든 경로가 일시 단절되었을 때 서버 전체가 멈추거나, 경로 복구 후에도 I/O 큐가 풀리지 않는 장애 해결
실행예제
스토리지 점검 또는 네트워크 일시 장애로 인해 다중 경로 중 일부(또는 전체)가 끊겼을 때, dm-multipath 레이어가 I/O를 무한정 큐에 쌓아두면서 애플리케이션 및 시스템 콜이 D(Uninterruptible Sleep) 상태로 멈추는 현상을 진단합니다.
# 1. 멀티패스 디바이스 상태 및 큐잉(Queueing) 정체 여부 확인
$ sudo multipath -ll
# mpathb (36001405a12b450123000000000000001) dm-1 OPEN-E,SAN
# size=2.0T features='1 queue_if_no_path' hwhandler='0' wp=rw
# `-+- policy='service-time 0' prio=0 status=active
# |- 3:0:0:1 sdc 8:32 failed faulty running
# `- 4:0:0:1 sdd 8:48 failed faulty running
# -> 모든 물리 경로(sdc, sdd)가 failed 상태인데 'queue_if_no_path' 기능으로 인해 I/O 에러를 리턴하지 않고 영구 대기열에 가둠
# 2. multipathd 데몬 상태 및 큐 모드 실시간 확인
$ sudo multipathd show maps
# name sysfs uuid
# mpathb dm-1 36001405a12b450123000000000000001
$ sudo multipathd show map mpathb status
# map mpathb: 1 queue_if_no_path (queueing enabled)
# -> queueing enabled 상태로 인해 상위 파일시스템/DB로 에러가 전달되지 않고 프로세스가 정지됨
# 3. I/O 대기(await) 및 D 상태 프로세스 점검
$ iostat -xz 1 2
# Device r/s w/s rkB/s wkB/s r_await w_await aqu-sz %util
# dm-1 0.00 0.00 0.00 0.00 0.00 0.00 845.00 100.00
# -> 입출력 처리량은 0인데 대기 큐 크기(aqu-sz)가 수백 개 이상 적체된 상태 확인
# 4. multipathd 대화형 콘솔(CLI)을 통한 즉각적인 큐 플러시 테스트
$ sudo multipathd disablequeueing map mpathb
# ok
# -> 대기 중이던 I/O를 즉시 에러로 반환하여 프로세스 락을 강제 해제
스크립트
시스템 내 모든 멀티패스 디바이스(dm-*)를 점검하여, 가용 경로가 전혀 없음에도 무한 큐잉(queue_if_no_path) 상태로 멈춰 있는 고아 디바이스를 감지하고 강제 큐 비우기(disablequeueing)를 수행하여 서버 행을 해소하는 복구 쉘 스크립트입니다.
#!/bin/bash
# fix_multipath_io_hang.sh - Detect and resolve DM-Multipath I/O Hang and infinite queueing
echo "=========================================="
echo " [Linux DM-Multipath I/O Hang 진단 및 복구]"
echo "=========================================="
if [ "$EUID" -ne 0 ]; then
echo " [오류] 멀티패스 관리를 위해 루트(root) 권한이 필요합니다."
exit 1
fi
if ! systemctl is-active --quiet multipathd; then
echo " [치명적] multipathd 서비스가 정지되어 있습니다!"
echo " -> 서비스 시작 시도: systemctl start multipathd"
systemctl start multipathd
fi
# 1. multipath 전체 맵 이름 추출
MAP_NAMES=$(multipath -ll | grep -E "^[a-zA-Z0-9_]+ \(" | awk '{print $1}')
if [ -z "$MAP_NAMES" ]; then
echo " [안내] 구성된 멀티패스 디바이스가 없습니다."
exit 0
fi
echo -e "\n[1] 멀티패스 디바이스 경로 헬스체크:"
HANG_DETECTED=0
for MAP in $MAP_NAMES; do
MAP_INFO=$(multipath -ll "$MAP")
ACTIVE_PATHS=$(echo "$MAP_INFO" | grep -c "status=active")
READY_PATHS=$(echo "$MAP_INFO" | grep -c "ready running")
IS_QUEUEING=$(echo "$MAP_INFO" | grep -c "queue_if_no_path")
printf " - 디바이스: %-15s | 활성경로: %s개 | 큐잉기능: %s\n" "$MAP" "$READY_PATHS" "$([ $IS_QUEUEING -gt 0 ] && echo 'ON' || echo 'OFF')"
# 가용 경로가 0개인데 큐잉이 켜져 있는 경우 -> 영구 I/O Hang 상태
if [ "$READY_PATHS" -eq 0 ] && [ "$IS_QUEUEING" -gt 0 ]; then
echo " [!] 치명적 감지: [${MAP}] 모든 물리 경로 단절 및 무한 큐잉 적체 중!"
HANG_DETECTED=1
echo " -> 긴급 조치: multipathd disablequeueing map ${MAP} 실행 중..."
multipathd disablequeueing map "$MAP" >/dev/null 2>&1
if [ $? -eq 0 ]; then
echo " -> [완료] 대기열 강제 반환 성공. 상위 프로세스 행(Hang)이 해제되었습니다."
fi
fi
done
# 2. multipath.conf 최적화 권장안 검사
echo -e "\n[2] /etc/multipath.conf 핵심 타임아웃 파라미터 점검:"
CONF_FILE="/etc/multipath.conf"
if [ -f "$CONF_FILE" ]; then
grep -vE "^#|^$" "$CONF_FILE" | grep -E "no_path_retry|fast_io_fail_tmo|dev_loss_tmo" | sed 's/^/ /'
if grep -q "queue_if_no_path" "$CONF_FILE"; then
echo " [주의] /etc/multipath.conf에 레거시 'queue_if_no_path'가 하드코딩되어 있습니다."
echo " -> 'no_path_retry fail' 또는 특정 횟수(예: no_path_retry 12)로 교체를 권장합니다."
fi
else
echo " [안내] 사용자 설정 파일(/etc/multipath.conf)이 존재하지 않아 커널 기본값이 적용 중입니다."
fi
echo -e "\n=========================================="
echo " [진단 완료]"
echo "=========================================="
해설
Linux Device Mapper Multipath(dm-multipath)는 서버와 스토리지 사이의 다중 경로 장애에 대비한 고가용성 드라이버입니다. 그러나 설정 파라미터 중 경로 유실 시의 동작 모드가 잘못 설계되어 있으면, 장애 발생 시 유연하게 페일오버되거나 빠른 에러를 반환하는 대신 시스템 전체 I/O를 영구적으로 멈추게(Hang) 만드는 치명적 상태에 빠집니다.
1. queue_if_no_path의 함정과 무한 I/O 블로킹
- 동작 원리:
- 스토리지와의 연결 경로가 일시적으로 끊겼을 때 상위 파일시스템이나 애플리케이션으로 I/O 에러(
EIO)를 리턴하면 DB나 파일시스템이 읽기 전용(Read-Only)으로 마운트되거나 크래시됩니다. - 이를 막기 위해 도입된 기능이
queue_if_no_path입니다. 연결이 복구될 때까지 들어오는 모든 I/O 요청을 메모리 큐에 대기(Queueing)시킵니다.
- 스토리지와의 연결 경로가 일시적으로 끊겼을 때 상위 파일시스템이나 애플리케이션으로 I/O 에러(
- 발생 문제:
- 만약 스토리지 스위치 전원 차단, 광케이블 절단 등 물리적 장애로 인해 경로가 장시간 복구되지 않으면, 커널은 들어오는 I/O를 무한정 큐에 쌓아둡니다.
- 결국 메모리가 고갈되거나 I/O를 요청한 모든 스레드(Java, C, Oracle 프로세스)가 커널의 D 상태에 갇혀 명령 입력조차 먹통이 되는 시스템 동결(System Freeze)로 번집니다.
2. 현대 멀티패스의 표준: no_path_retry 아키텍처
현대 리눅스 커널은 무한정 큐잉을 지양하고, 일정 횟수/시간 동안만 큐잉을 유지한 뒤 경로가 복원되지 않으면 I/O 실패(fail)를 리턴하는 방식을 표준으로 채택합니다.
no_path_retry <number>:- 경로 점검 주기(
polling_interval, 보통 5초)마다 재시도 횟수를 차감합니다. - 예:
no_path_retry 12$\rightarrow$ $12 \times 5\text{초} = 60\text{초}$ 동안만 큐잉을 유지하고, 60초 후에도 경로가 없으면 에러를 리턴하여 프로세스 행을 차단합니다.
- 경로 점검 주기(
no_path_retry fail:- 경로가 0개가 되는 즉시 에러를 리턴합니다 (HA 클러스터 환경 권장).
3. 안정적인 /etc/multipath.conf 권장 구성
# /etc/multipath.conf
defaults {
user_friendly_names yes
find_multipaths yes
# 무한 큐잉 방지: 60초(12회 x 5초) 후 실패 반환
no_path_retry 12
# 장애 경로 빠른 감지 (초 단위)
fast_io_fail_tmo 5
# 원격 포트 최종 정리 타임아웃
dev_loss_tmo 30
# 검사 도구
path_checker tur
path_grouping_policy group_by_prio
}
- 변경 후 데몬 리로드:
sudo systemctl reload multipathd
주의사항
- 클러스터 환경(Pacemaker / Oracle RAC)의 펜싱(Fencing) 충돌:
- 클러스터 환경에서
no_path_retry값을 너무 길게(수 분 이상) 주면 스토리지 하트비트가 멈췄을 때 클러스터 소프트웨어가 노드를 비정상으로 오인하여 노드를 강제 재부팅(Reboot fencing)시키는 연쇄 장애가 발생합니다. 클러스터 환경은no_path_retry fail또는 짧은 초(5~10초)를 주어야 합니다.
- 클러스터 환경에서
multipath -F(Flush All) 실행 시 서비스 중단 주의:- 멀티패스 맵을 재구성하겠다고 마운트가 물려있는 상태에서
multipath -F를 내리면 마운트된 디바이스 매퍼 테이블이 손상되어 시스템이 패닉에 빠질 수 있습니다. 반드시 상위 마운트 및 LVM 활성화를 해제한 후 플러시해야 합니다.
- 멀티패스 맵을 재구성하겠다고 마운트가 물려있는 상태에서
- SCSI 드라이버 타임아웃과의 연계:
- SCSI 계층의
fast_io_fail_tmo값이 FC HBA의 링크 다운 타임아웃보다 길게 설정되면 HBA 수준에서 I/O를 먼저 버려 멀티패스가 페일오버 결정을 제때 내리지 못하므로 값을 스토리지 권장 가이드와 일치시켜야 합니다.
- SCSI 계층의