최근 고성능 데이터베이스 서버의 NVMe SSD에서 간헐적으로 I/O 성능 저하와 지연 시간(latency) 급증 현상이 보고되었다. 특히 대량의 작은 블록 쓰기(small block write) 작업이 집중될 때 이러한 문제가 두드러지게 나타났으며, 시스템 모니터링 상으로는 CPU 사용률이나 메모리 부족 현상이 관찰되지 않았다. 초기에는 애플리케이션 계층의 문제로 판단했으나, 파일 시스템 캐시를 우회하는 O_DIRECT I/O 테스트에서도 동일한 증상이 재현되어 하드웨어 또는 OS 커널 레벨의 문제일 가능성이 높아졌다.
이러한 현상은 특히 데이터베이스의 WAL(Write-Ahead Log)이나 NoSQL 데이터베이스의 컴팩션(compaction) 작업처럼 예측 불가능한 패턴의 I/O가 빈번하게 발생하는 환경에서 치명적인 영향을 미칠 수 있다. 단순히 디스크 교체를 고려하기 전에, NVMe 컨트롤러의 동작 방식, 커널 I/O 스케줄러, 그리고 펌웨어의 GC(Garbage Collection) 및 웨어 레벨링(Wear Leveling) 메커니즘을 종합적으로 분석하여 근본적인 원인을 파악하고 해결하는 것이 중요하다.
NVMe SSD I/O 지연 시간 급증 재현 및 초기 분석
문제 재현을 위해 fio 툴을 사용하여 실제 워크로드와 유사한 패턴의 I/O를 발생시켰다. 특히 4KB 랜덤 쓰기(random write) 패턴에서 평균 지연 시간이 평소보다 10배 이상 증가하는 현상이 관찰되었다. 이 과정에서 iostat과 nvme-cli 툴을 활용하여 NVMe 장치의 상태를 면밀히 모니터링했다.
# iostat -x 1 10 /dev/nvme0n1
# iostat 명령어로 NVMe 장치의 초당 I/O 통계 및 대기열 길이 모니터링
# r/s: 초당 읽기 요청 수, w/s: 초당 쓰기 요청 수
# rkB/s: 초당 읽은 KB 수, wkB/s: 초당 쓴 KB 수
# await: I/O 요청 평균 대기 시간 (ms), %util: 장치 사용률
# avgqu-sz: 평균 I/O 큐 길이
# 문제가 발생할 때 await 값이 급증하고 avgqu-sz가 비정상적으로 높아지는지 확인
# nvme smart-log /dev/nvme0n1
# NVMe SMART 로그를 통해 온도, 남은 수명, 미디어 오류 등 하드웨어 상태 확인
# Critical Warning: 주요 경고 상태 (예: 온도 초과, 안정성 저하)
# Data Units Read/Written: 읽고 쓴 데이터 양
# Host Read/Write Commands: 호스트가 발행한 읽기/쓰기 명령 수
# Controller Busy Time: 컨트롤러가 명령 처리로 바쁜 시간 (분)
# Power Cycles/On Hours: 전원 켜짐 횟수/시간
# Unsafe Shutdowns: 안전하지 않은 종료 횟수
# Media and Data Integrity Errors: 미디어 및 데이터 무결성 오류 횟수
# Number of Error Information Log Entries: 오류 로그 엔트리 수
초기 분석 결과, iostat 상에서 await 값이 급증하고 avgqu-sz가 비정상적으로 높아지는 현상이 확인되었다. 이는 NVMe 컨트롤러가 I/O 요청을 처리하는 데 병목 현상이 발생하고 있음을 시사한다. nvme smart-log에서는 특이한 하드웨어 오류나 온도 문제는 발견되지 않았다. 따라서 문제는 NVMe 장치 자체의 물리적 결함보다는, 특정 I/O 패턴에 대한 NVMe 컨트롤러의 처리 방식 또는 OS 커널의 I/O 스케줄링 정책에 있을 가능성이 높다고 판단했다.
NVMe 컨트롤러 내부 동작과 Garbage Collection 부하
NVMe SSD의 성능은 낸드 플래시의 특성상 쓰기 증폭(Write Amplification)과 가비지 컬렉션(Garbage Collection, GC)에 크게 영향을 받는다. 낸드 플래시는 페이지 단위로 쓰기가 가능하지만, 삭제는 블록 단위로만 가능하다. 따라서 데이터가 삭제되면 해당 블록은 '무효화된 페이지'를 포함하게 되며, 새로운 데이터를 쓰기 위해서는 이 무효화된 페이지들을 정리하여 유효한 페이지들만 다른 블록으로 옮기고 해당 블록 전체를 지워야 한다. 이 과정이 GC이다.
작은 블록의 랜덤 쓰기가 빈번하게 발생하면, 낸드 플래시의 유효 데이터와 무효 데이터가 뒤섞이게 되어 GC 작업이 더욱 빈번하고 복잡하게 수행된다. GC는 백그라운드에서 동작하지만, 이 작업이 너무 많아지면 컨트롤러의 자원을 소모하여 실제 호스트의 I/O 요청 처리 속도를 저하시키고 지연 시간을 증가시킨다. 특히 SSD의 여유 공간이 부족해질수록 GC 부하는 가중된다.
# nvme id-ctrl /dev/nvme0n1
# NVMe 컨트롤러의 상세 정보 확인 (ID, 펌웨어 버전, 지원 기능 등)
# fw_rev: 펌웨어 버전 확인. 최신 펌웨어인지 확인하여 업데이트 필요성 검토
# sqes/cqes: Submission Queue Entry Size / Completion Queue Entry Size
# nn: Number of Namespaces
# oacs: Optional Admin Command Support (예: 펌웨어 활성화, 안전한 삭제 등)
# aocs: Asynchronous Event Request Support
# frl: Firmware Revision Log (펌웨어 변경 이력)
nvme id-ctrl 명령어를 통해 펌웨어 버전을 확인하고, 제조사 웹사이트에서 해당 펌웨어의 릴리즈 노트를 검토하여 알려진 성능 문제나 버그 픽스가 있는지 확인하는 것이 중요하다. 오래된 펌웨어는 특정 워크로드에서 최적화되지 않아 성능 저하를 유발할 수 있다. 펌웨어 업데이트는 잠재적인 성능 개선을 가져올 수 있지만, 신중한 테스트와 백업 후에 진행해야 한다.
Linux 커널 I/O 스케줄러와 NVMe 최적화
Linux 커널은 다양한 I/O 스케줄러를 제공하며, 각 스케줄러는 특정 워크로드에 최적화되어 있다. 전통적인 HDD 환경에서는 CFQ나 Deadline 스케줄러가 주로 사용되었지만, NVMe와 같은 초고속 SSD에서는 이러한 스케줄러들이 오히려 오버헤드를 유발하여 성능을 저하시킬 수 있다. NVMe 장치는 자체적으로 큐 관리 및 명령 재정렬 기능을 내장하고 있기 때문에, 커널 스케줄러가 개입하는 것은 불필요하거나 비효율적일 수 있다.
최신 Linux 커널은 NVMe 장치에 최적화된 none 또는 mq-deadline 스케줄러를 기본으로 사용하거나 권장한다. none 스케줄러는 커널 레벨의 스케줄링을 거의 수행하지 않고, I/O 요청을 즉시 하드웨어로 전달하여 NVMe 컨트롤러가 자체적으로 최적화하도록 맡긴다. mq-deadline은 다중 큐(multi-queue)를 지원하며 deadline 스케줄러의 장점을 유지하면서 NVMe의 병렬성을 활용한다.
# 현재 NVMe 장치의 I/O 스케줄러 확인
cat /sys/block/nvme0n1/queue/scheduler
# 출력 예시: [none] mq-deadline kyber bfq
# 대괄호로 묶인 것이 현재 활성화된 스케줄러
# I/O 스케줄러를 'none'으로 변경 (재부팅 시 유지되지 않음)
echo 'none' > /sys/block/nvme0n1/queue/scheduler
# 또는 'mq-deadline'으로 변경
# echo 'mq-deadline' > /sys/block/nvme0n1/queue/scheduler
# 재부팅 후에도 설정을 유지하려면 GRUB 설정 수정
# /etc/default/grub 파일 열기
# GRUB_CMDLINE_LINUX_DEFAULT="... scsi_mod.use_blk_mq=1" 추가
# scsi_mod.use_blk_mq=1은 모든 SCSI/SATA/NVMe 장치에 대해 멀티큐 I/O 스택을 강제 활성화
# NVMe 장치에 대해서는 기본적으로 멀티큐 스택이 사용되지만, 명시적으로 설정하여 일관성 확보
# grub-mkconfig -o /boot/grub/grub.cfg (Debian/Ubuntu) 또는 grub2-mkconfig -o /boot/grub2/grub.cfg (CentOS/RHEL) 실행
# 시스템 재부팅
I/O 스케줄러를 변경한 후에는 반드시 fio 등의 툴로 성능 테스트를 다시 수행하여 개선 여부를 확인해야 한다. 특히 none 스케줄러는 대부분의 NVMe 환경에서 최적의 성능을 제공하는 것으로 알려져 있다.
TRIM/Discard 명령어 활용 및 파일 시스템 마운트 옵션
TRIM 명령어(또는 Discard)는 운영체제가 SSD에 더 이상 사용되지 않는 데이터 블록을 알려주는 메커니즘이다. 이를 통해 SSD 컨트롤러는 해당 블록을 가비지 컬렉션 대상으로 미리 표시하여, 실제 쓰기 작업이 발생하기 전에 불필요한 GC 작업을 줄이고 성능 저하를 방지할 수 있다.
TRIM은 두 가지 방식으로 동작할 수 있다:
- 온라인 TRIM (Continuous TRIM): 파일 시스템에서 파일이 삭제될 때마다 즉시 TRIM 명령을 발행한다.
mount옵션에discard를 추가하여 활성화한다. - 오프라인 TRIM (Periodic TRIM): 특정 주기(예: 매주)로
fstrim명령을 수동 또는 스케줄러(cron)를 통해 실행하여 한 번에 대량의 TRIM 작업을 수행한다.
온라인 TRIM은 즉각적인 성능 이점을 제공할 수 있지만, 파일 삭제 시마다 TRIM 오버헤드가 발생하여 오히려 지연 시간을 유발할 수도 있다. 특히 빈번한 파일 삭제가 발생하는 워크로드에서는 주기적인 오프라인 TRIM이 더 효율적일 수 있다.
# 현재 파일 시스템의 마운트 옵션 확인
mount | grep /dev/nvme0n1p1
# 출력 예시: /dev/nvme0n1p1 on /data type ext4 (rw,noatime)
# discard 옵션이 있는지 확인
# /etc/fstab 파일 수정 (persistent mount options)
# UUID=xxxx-xxxx-xxxx-xxxx /data ext4 defaults,noatime,discard 0 0
# 또는 주기적인 TRIM을 선호한다면 discard 옵션 제거 후 아래 스케줄링 설정
# fstrim 서비스 활성화 (주기적인 TRIM)
# systemctl enable fstrim.timer
# systemctl start fstrim.timer
# fstrim.timer는 기본적으로 일주일에 한 번 fstrim을 실행하도록 설정되어 있음
# 수동으로 fstrim 실행하여 즉시 TRIM 수행
# sudo fstrim -v /data
# -v: verbose 모드로 처리된 바이트 수 출력
어떤 TRIM 전략이 최적인지는 워크로드 패턴에 따라 다르다. 데이터베이스와 같이 지속적으로 데이터가 추가되고 삭제되는 환경에서는 discard 옵션으로 온라인 TRIM을 활성화하는 것이 유리할 수 있다. 반면, 대규모 파일이 한 번에 삭제되는 백업/아카이빙 시스템에서는 주기적인 fstrim이 더 나을 수 있다. 실제 환경에서는 discard 옵션을 활성화한 후 성능 모니터링을 통해 지연 시간 증가 여부를 확인하고, 문제가 발생하면 noatime만 유지하고 discard는 제거하여 주기적인 fstrim으로 전환하는 것을 권장한다.
사이드 이펙트 방지 트러블슈팅 FAQ
Q. NVMe 펌웨어 업데이트는 안전한가요?
A. NVMe 펌웨어 업데이트는 일반적으로 안전하지만, 항상 위험이 따릅니다. 업데이트 전에 반드시 중요 데이터를 백업하고, 제조사에서 제공하는 공식 업데이트 도구와 절차를 따르세요. 잘못된 펌웨어 업데이트는 장치 손상이나 데이터 손실로 이어질 수 있습니다. 펌웨어 업데이트 후에는 충분한 테스트를 통해 안정성과 성능을 검증해야 합니다.
Q. none I/O 스케줄러가 항상 최적인가요?
A. 대부분의 NVMe SSD 환경에서 none 스케줄러는 최적의 성능을 제공합니다. NVMe 컨트롤러 자체의 효율적인 큐 관리 및 명령 재정렬 기능을 신뢰하는 방식이기 때문입니다. 그러나 특정 구형 NVMe 장치나 아주 특수한 워크로드에서는 mq-deadline이 더 나은 성능을 보일 수도 있습니다. 항상 변경 후 실제 워크로드로 성능 테스트를 수행하여 최적의 스케줄러를 찾아야 합니다.
Q. discard 마운트 옵션 사용 시 주의할 점은 무엇인가요?
A. discard 옵션은 파일 삭제 시마다 TRIM 명령을 발행하여 즉각적인 공간 회수 및 GC 부하 감소에 도움을 줍니다. 하지만, 아주 작은 파일이 빈번하게 생성되고 삭제되는 워크로드에서는 매번 TRIM 명령을 발행하는 오버헤드가 누적되어 오히려 I/O 지연 시간을 증가시킬 수 있습니다. 이러한 경우에는 discard 옵션을 제거하고 fstrim.timer 서비스를 활성화하여 주기적으로 TRIM을 수행하는 것이 더 효율적일 수 있습니다.
Q. NVMe 장치의 온도가 너무 높으면 성능에 영향을 미치나요?
A. 네, NVMe SSD는 고성능을 내는 만큼 발열도 상당합니다. 온도가 일정 수준 이상으로 올라가면 컨트롤러는 과열 방지를 위해 성능을 의도적으로 낮추는 스로틀링(throttling)을 적용합니다. 이는 I/O 지연 시간 증가와 성능 저하의 직접적인 원인이 됩니다. nvme smart-log 명령어로 온도를 주기적으로 확인하고, 필요하다면 추가적인 방열 솔루션(예: 히트싱크)을 고려해야 합니다. 서버 랙의 공기 흐름 관리도 중요합니다.
이번 트러블슈팅을 통해 NVMe SSD의 특정 I/O 워크로드에서 발생하는 성능 저하 및 지연 시간 급증 문제가 단순히 하드웨어의 물리적 결함이 아닌, NVMe 컨트롤러의 GC 메커니즘, Linux 커널의 I/O 스케줄러, 그리고 파일 시스템의 TRIM 전략 등 복합적인 요인에 의해 발생할 수 있음을 확인했다. 문제 해결을 위해 펌웨어 업데이트 검토, none I/O 스케줄러 적용, 그리고 discard 마운트 옵션의 신중한 사용 또는 주기적 fstrim 전환 등의 조치를 취할 수 있다.
이러한 접근 방식은 단순히 문제를 해결하는 것을 넘어, 고성능 스토리지 시스템의 내부 동작 원리와 OS와의 상호작용을 깊이 이해하는 데 도움을 준다. 실제 프로덕션 환경에서는 이러한 변경 사항을 적용하기 전에 항상 충분한 테스트와 검증 과정을 거쳐야 하며, 워크로드 특성에 맞는 최적의 설정을 찾아 나가는 것이 중요하다.
