✍️ 필사 모드: Linux 파일시스템 완전 가이드 2025: ext4, XFS, ZFS, Btrfs — Inode, Journaling, Copy-on-Write, Snapshot 심층 분석
한국어들어가며: 파일 뒤에 숨은 거대한 공학
간단한 질문
$ echo "hello world" > /tmp/test.txt
$ cat /tmp/test.txt
hello world
두 줄의 명령이다. 무슨 일이 일어났을까?
놀랍도록 많은 일이:
- 파일 생성:
/tmp디렉토리 항목에 새 이름 추가, 빈 inode 할당. - 데이터 블록 할당: 빈 4KB 블록을 찾아 예약.
- 데이터 쓰기: "hello world\n"을 블록에 기록.
- 메타데이터 업데이트: inode에 파일 크기, mtime, block pointer 기록.
- Journaling: 이 모든 변경을 로그에 먼저 기록 (커밋 전엔 내구성 없음).
- Page cache: OS가 데이터를 메모리에 캐싱.
- 읽기: 위 과정 역순. inode에서 block pointer → 블록 → 내용.
같은 작업을 ext4는 이렇게 하고, XFS는 조금 다르게, ZFS는 완전히 다르게 한다. 이 차이가 성능, 안정성, 기능의 차이를 만든다.
왜 이걸 알아야 하는가?
- 데이터베이스 성능: XFS가 PostgreSQL에 권장되는 이유.
- 백업과 복구: ZFS/Btrfs 스냅샷의 힘.
- 대용량 파일: ZFS/XFS의 강점.
- 컨테이너: OverlayFS의 기반.
- 장애 대응: fsck, journal recovery, scrub.
파일시스템은 "단순히 파일을 저장하는 것"이 아니다. 2000년대부터 진화를 거듭해 지금은 OS의 가장 정교한 서브시스템 중 하나다.
1. 파일시스템의 기본 구조
4가지 핵심 추상화
모든 Unix/Linux 파일시스템은 네 가지 핵심 개념으로 이루어진다:
- Superblock: 파일시스템 전체 메타데이터.
- Inode: 파일의 메타데이터 (permissions, owner, size, block pointers).
- Directory entry (dentry): 이름 → inode 매핑.
- Data blocks: 실제 파일 내용.
Superblock
파일시스템의 "헤더". 파일시스템 전체에 대한 정보:
Superblock:
- 파일시스템 타입 (ext4, xfs, ...)
- 블록 크기 (보통 4096 bytes)
- 전체 블록 수
- 남은 블록 수
- Inode 수
- 마운트 상태
- 마지막 fsck 시간
- UUID
...
손상되면 파일시스템을 마운트할 수 없다. 그래서 여러 백업 superblock을 유지한다.
Inode
Inode는 파일 하나의 모든 메타데이터:
Inode:
- file type (regular, directory, symlink, ...)
- permissions (rwx)
- owner uid, gid
- size in bytes
- atime, mtime, ctime
- link count (hard link 수)
- block pointers (데이터 블록 위치)
중요: inode에는 파일 이름이 없다. 이름은 디렉토리에 저장된다.
Directory Entry
디렉토리는 특수한 파일이다. 그 내용은 (이름, inode 번호) 쌍의 목록:
/home/alice/
├── (".", inode=1024)
├── ("..", inode=512)
├── ("report.txt", inode=2048)
└── ("photos", inode=3000)
이 구조의 시사점:
- Hard link: 같은 inode를 여러 이름으로 참조 가능.
- 이름 변경: 디렉토리 항목만 수정, inode는 그대로.
- inode 번호는 stat에서 보임:
ls -i로 확인.
Block Pointer의 진화
Inode가 파일의 데이터 블록을 어떻게 가리키는가?
ext2/3의 방식 (전통적):
Inode:
- Direct blocks: 12개 (48 KB)
- Single indirect: 1 블록 → 1024 블록 (4 MB)
- Double indirect: 1 블록 → 1024 * 1024 블록 (4 GB)
- Triple indirect: ... (4 TB)
작은 파일은 direct로 빠르게, 큰 파일은 indirect로 확장.
ext4/XFS의 방식 (Extent-based):
Inode:
- Extents: [(start_block, length), ...]
연속된 블록을 (시작, 길이) 로 표현. 100 MB 파일도 2~3개의 extent로 표현 가능.
효과: 큰 파일의 메타데이터 크기 급감. 성능 향상.
2. Journaling: 일관성의 수호자
문제: 갑작스런 전원 차단
파일 쓰기 중에 전원이 차단되면?
Step 1: 데이터 블록 할당 (블록 할당 비트맵 변경)
Step 2: 데이터 블록에 실제 내용 쓰기
Step 3: Inode 업데이트 (크기, 블록 포인터)
Step 4: 디렉토리 엔트리 추가
각 단계 사이에 전원이 끊기면?
- Step 2 직후: 할당은 했지만 데이터가 엉뚱함 → 쓰레기 파일.
- Step 3 직후: 데이터는 있지만 디렉토리에 이름 없음 → 고아 파일.
fsck: 옛 방식
ext2 시대엔 부팅 시 fsck 로 전체 파일시스템을 스캔했다. 수 TB 디스크면 수십 분~수 시간. 프로덕션 서버에선 상상도 할 수 없다.
Journal: 해결책
Journal(또는 log) 은 별도의 영역에 변경 사항을 먼저 기록한다:
1. Journal에 "A, B, C 변경 예정" 기록.
2. Journal 플러시 (fsync).
3. 실제 파일시스템에 A, B, C 적용.
4. Journal에서 해당 엔트리 삭제.
장애 복구 시:
- Journal을 읽어 완료되지 않은 트랜잭션 확인.
- 완료된 것은 재적용 (redo).
- 미완료는 취소 (undo) 또는 재적용.
- 수 초 만에 복구 완료.
ext4의 journaling 모드
data= 마운트 옵션:
journal mode (가장 안전):
- 메타데이터 + 데이터 모두 journal에 먼저.
- 가장 안전, 가장 느림.
- 각 쓰기가 두 번 일어남 (journal + actual).
ordered mode (기본):
- 메타데이터만 journal.
- 데이터는 먼저 쓰고 메타데이터 journal 커밋.
- 순서 보장으로 어느 정도 안전.
writeback mode (가장 빠름):
- 메타데이터만 journal.
- 데이터 쓰기 순서 보장 없음.
- 일관성만 있고 데이터 손실 가능성 높음.
ZFS/Btrfs: Journal 없음
ZFS와 Btrfs는 journal을 쓰지 않는다. 대신 Copy-on-Write (CoW) 트리 구조로 원자성을 보장한다. 다음 섹션에서 자세히.
3. ext4: Linux의 Workhorse
역사
- ext: 1992년, 최초.
- ext2: 1993년, inode 기반 구조 확립.
- ext3: 2001년, journaling 추가.
- ext4: 2008년, extent, 큰 파일, huge volume 지원.
ext4는 20년 넘게 Linux의 기본 파일시스템이었다. 지금도 Debian, Ubuntu 등의 기본.
주요 특징
- Extent 기반: 연속 블록을 효율적으로 표현.
- Huge volume: 1 EB (exabyte) 지원.
- Huge file: 16 TB 단일 파일.
- Journaling: 기본 ordered 모드.
- Multi-block allocator: 큰 쓰기를 한 번에 할당.
- Delayed allocation: 여러 쓰기를 모아 연속 블록 할당.
- Backward compatible: ext2/3 볼륨을 ext4로 마운트 가능.
Block Group
ext4는 디스크를 block group으로 나눈다:
Block Group 0:
- Superblock (복사본)
- Group Descriptor Table
- Block Bitmap
- Inode Bitmap
- Inode Table
- Data Blocks
Block Group 1:
- (같은 구조)
...
이점:
- inode와 데이터가 근처에 있어 seek 감소 (HDD 시대의 유산).
- 한 그룹 손상이 다른 그룹에 영향 없음.
- 병렬 할당 가능.
Extent Tree
파일이 커지면 extent 수도 증가. Inode에 인라인으로 못 담으면 extent tree를 사용:
Inode (4 extents 저장 가능)
↓ 초과 시
Extent block (B-tree)
├── Extent 1
├── Extent 2
├── ...
└── Extent N
Delayed Allocation
ext4의 영리한 트릭: 쓰기를 바로 블록 할당하지 않고 지연.
write(fd, data1, size1); // 메모리에만
write(fd, data2, size2); // 메모리에만
write(fd, data3, size3); // 메모리에만
fsync(fd); // 여기서 한 번에 할당!
효과:
- 연속 블록 할당 가능성 증가 → seek 감소.
- 작은 쓰기가 큰 쓰기로 병합 → 효율.
- 임시 파일이 짧게 살면 블록 할당 자체를 피함.
ext4의 한계
- 볼륨 크기: 1 EB지만 실전에선 100 TB 이상 권장 안 함.
- 파일 수: 수십억 파일 가능하지만 fsck 시간이 문제.
- 스냅샷 없음: LVM과 조합해야 함.
- 체크섬 옵션: metadata 체크섬만 (ext4 기본). 데이터 체크섬 없음.
- 온라인 shrink 제한적.
4. XFS: 대용량과 고동시성의 강자
역사
- 1993: Silicon Graphics가 IRIX를 위해 개발.
- 2001: Linux에 포팅.
- 2014: Red Hat Enterprise Linux 7의 기본 파일시스템.
- 현재: RHEL, CentOS, Rocky Linux의 기본.
설계 철학
XFS는 처음부터 극대 규모, 고동시성을 위해 설계되었다:
- B+ Tree 기반: 모든 구조가 B+ 트리.
- Allocation Group: 파일시스템을 병렬 처리 가능한 그룹으로 분할.
- Extent 기반: ext4보다 먼저 사용.
- Delayed allocation: ext4가 배웠다.
Allocation Group (AG)
XFS의 핵심 단위. 파일시스템을 여러 AG로 분할하고 각 AG는 독립적으로 동작:
XFS Filesystem
├── AG 0 (Superblock, AGF, AGI, ...)
├── AG 1
├── AG 2
...
└── AG N
각 AG는:
- AGF (Allocation Group Free space): 빈 공간 B+ tree.
- AGI (Allocation Group Inode): Inode B+ tree.
- 자체 free block bitmap 대신 B+ tree.
이점: 여러 AG를 동시 할당/해제 → 병렬 I/O 성능.
B+ Tree의 매력
ext4는 비트맵과 extent tree를 쓴다. XFS는 모든 것을 B+ tree로:
- Free space를 size와 offset 두 B+ tree로 인덱스.
- 최적의 "이만한 크기의 연속 공간" 검색 가능.
- 고정된 오버헤드 (O(log N)).
결과: 수천만 개 파일의 디렉토리에서도 빠름. ext4는 특정 규모 이상에서 둔해짐.
확장성 수치
XFS가 자랑하는 수치:
- 최대 파일 크기: 8 EB (exabyte).
- 최대 볼륨: 8 EB.
- 병렬 I/O: 수백 개의 CPU에서 선형 확장.
- Inode 수: 사실상 무제한 (B+ tree).
장점
- 대용량: 수 백 TB 파일시스템에 이상적.
- 큰 파일: 비디오, DB, 백업.
- 병렬 I/O: 멀티코어 서버.
- Online defragment: 운영 중 조각 모음.
- Growfs: 온라인 확장.
- Metadata 체크섬 (XFS v5).
- PostgreSQL, MySQL 권장.
단점
- 축소 불가: 한 번 만든 XFS는 줄일 수 없다. LVM과 함께 사용해도 제약.
- 작은 파일 많을 때: ext4보다 약간 느릴 수 있음.
- 메모리 사용: 메타데이터가 메모리를 더 씀.
실전 사용
Red Hat은 RHEL 7부터 XFS를 기본으로 한다. 왜?
- 엔터프라이즈 대용량 스토리지에 적합.
- 오랜 검증 (SGI IRIX 시절부터).
- 안정성 탁월.
- 데이터베이스 워크로드에 최적.
PostgreSQL 공식 문서도 XFS를 추천한다.
5. ZFS: 혁명적 아이디어의 집합
역사
- 2001: Sun Microsystems가 개발 시작.
- 2005: Solaris 10에 도입.
- 2008: OpenSolaris로 오픈소스화.
- 2013: OpenZFS 프로젝트 시작 (Linux, FreeBSD 포팅).
- 현재: TrueNAS, Proxmox, Ubuntu 22.04+ 기본 옵션.
설계 철학
ZFS는 "파일시스템과 볼륨 매니저의 통합" 이라는 급진적 접근. 기존엔 다음이 분리되어 있었다:
- 파일시스템 (ext4, XFS)
- 볼륨 매니저 (LVM)
- RAID (mdadm)
- 체크섬 (없음)
ZFS는 이 모두를 하나의 시스템으로. 각 레이어가 서로 알고 있기에 상호 최적화 가능.
Pool과 Dataset
ZFS는 zpool이라는 저장 풀을 만들고, 그 안에 dataset을 만든다:
tank (zpool)
├── tank/home (dataset: 파일시스템)
│ ├── alice
│ └── bob
├── tank/db (dataset: 파일시스템)
└── tank/swap (dataset: zvol, 블록 디바이스)
여러 dataset이 같은 pool의 공간을 유연하게 공유한다.
Copy-on-Write (CoW)
ZFS의 가장 큰 특징: 모든 쓰기가 copy-on-write.
- 기존 블록을 절대 덮어쓰지 않는다.
- 새 블록에 쓰고, 메타데이터도 새 블록에 쓰고, 마지막에 uberblock만 업데이트.
- 모든 변경이 원자적.
이점:
- Journaling 불필요: CoW 자체가 원자성 보장.
- 스냅샷이 공짜: 기존 블록 유지 + 메타데이터 참조만.
- 부분 손상 없음: 이전 상태 또는 새 상태만.
대가:
- 쓰기 오버헤드: 메타데이터 업데이트 연쇄.
- 쓰기 증폭: 작은 변경도 블록 전체 쓰기.
체크섬 Everywhere
ZFS는 모든 블록에 체크섬을 저장한다:
- 데이터 블록 체크섬.
- 메타데이터 체크섬.
- 메타데이터의 메타데이터 체크섬.
- ...
- Root는 uberblock의 체크섬.
읽을 때마다 검증. 불일치 감지 시:
- Mirror/RAID-Z: 다른 복사본에서 자동 복구.
- 단일 디스크: 에러 반환.
이는 "silent data corruption"(디스크가 조용히 데이터를 잘못 반환)을 감지하는 유일한 방법 중 하나.
스냅샷과 클론
# 스냅샷 생성 (즉시, 공간 거의 안 듬)
zfs snapshot tank/home@backup-2025-04-15
# 스냅샷 목록
zfs list -t snapshot
# 롤백
zfs rollback tank/home@backup-2025-04-15
# 클론 (스냅샷으로부터 writable 복사본)
zfs clone tank/home@backup-2025-04-15 tank/home-test
스냅샷은 즉시 만들어진다. 실제 데이터 복사 없음. 변경된 블록만 공간 소모.
Send/Receive
ZFS는 증분 백업을 블록 레벨로 제공:
# 전체 스냅샷 전송
zfs send tank/home@day1 | ssh backup zfs receive backup/home
# 증분 전송 (변경만)
zfs send -i tank/home@day1 tank/home@day2 | ssh backup zfs receive backup/home
놀랍도록 효율적. 1 TB 데이터셋의 증분 백업이 변경된 MB만 전송한다.
ARC (Adaptive Replacement Cache)
ZFS의 캐시 ARC는 일반 LRU보다 영리하다:
- 두 가지 목록: MRU (최근 사용), MFU (자주 사용).
- 패턴에 따라 적응적으로 조정.
- L2ARC: SSD를 2차 캐시로 사용 가능.
전통 Linux page cache와 별도로 동작. 그래서 ZFS 머신은 RAM을 많이 쓴다.
ZIL (ZFS Intent Log)
ZIL은 ZFS의 동기 쓰기 로그. 순차 쓰기에 최적. SLOG (Separate Log device)로 NVMe 같은 빠른 장치를 지정하면 sync write 성능 극대화.
RAID-Z
ZFS의 소프트웨어 RAID:
- RAID-Z1: 1개 디스크 패리티.
- RAID-Z2: 2개 디스크 패리티.
- RAID-Z3: 3개 디스크 패리티.
전통 RAID 5의 write hole 문제를 CoW로 해결. 또한 체크섬과 통합되어 silent corruption 탐지 + 복구.
단점
- 메모리 요구: ARC가 많은 RAM 필요. 1 TB당 1 GB RAM 권장.
- CPU 부담: 체크섬, 압축 때문.
- 쓰기 증폭: 작은 동기 쓰기가 많으면 불리.
- 라이선스: CDDL로 Linux 커널 기본 포함 불가 (OpenZFS는 별도 모듈).
- 파일시스템 축소 불가: 풀 확장은 쉽지만 축소는 복잡.
- Ext4/XFS보다 느린 경우: 단일 스레드 소형 랜덤 I/O.
언제 쓸까
- 백업 서버: 스냅샷, send/receive 강력.
- NAS: RAID-Z + 체크섬.
- 데이터 안정성 우선: Silent corruption 방지.
- 대용량 읽기: 압축 + ARC 덕분에 빠름.
언제 쓰지 말까
- 메모리 적은 시스템 (< 8 GB).
- 작은 VPS.
- 극도의 저지연 sync write 필요 (DB 전용 디스크).
6. Btrfs: Linux의 야심찬 답변
역사
- 2007: Oracle의 Chris Mason이 시작.
- 2009: Linux 2.6.29에 병합.
- 2014: "안정" 선언.
- 현재: SUSE의 기본 파일시스템. Fedora Workstation 기본 (2020+).
설계 목표
ZFS와 비슷한 기능을 GPL 라이선스로 Linux 커널 기본에 제공. 목표:
- CoW 파일시스템.
- 스냅샷, 서브볼륨.
- 체크섬.
- 내장 RAID.
- Online defragment, resize.
Subvolume과 Snapshot
# 서브볼륨 생성
sudo btrfs subvolume create /mnt/subvol1
# 스냅샷
sudo btrfs subvolume snapshot /mnt/subvol1 /mnt/subvol1-backup
# 목록
sudo btrfs subvolume list /mnt
ZFS와 비슷하지만 Btrfs는 서브볼륨 자체가 파일시스템 루트가 될 수 있다. 디렉토리와 비슷하게 보이지만 독립적 snapshot root.
체크섬
Btrfs도 CRC32 체크섬 (기본). 데이터와 메타데이터 모두.
Mirror(RAID1)가 있으면 자동 복구.
RAID
- RAID 0, 1, 10: 안정적.
- RAID 5, 6: 여전히 불안정 (2025년 현재). "write hole" 문제 완전 해결 못 함.
주의: Btrfs RAID 5/6는 프로덕션 비권장. 이것이 ZFS 대비 주요 약점.
컴프레션
# 마운트 옵션
mount -o compress=zstd /dev/sda1 /mnt
투명 압축. 읽기/쓰기 시 자동. zstd가 권장.
단점과 역사의 무게
Btrfs는 오랫동안 "다음 해에 안정화될 파일시스템" 이었다. 여러 문제가 있었다:
- RAID 5/6 불안정: 가장 큰 약점.
- Performance regression: 특정 워크로드에서 예상보다 느림.
- ENOSPC 버그 역사: "공간 있는데 쓰기 실패" 버그가 과거에 있었음.
- 복잡성: 많은 기능으로 버그 표면적 큼.
그러나 최근 몇 년간 많이 개선되었고, Fedora가 기본으로 채택한 이후 상당히 안정화됐다. 2025년 현재 RAID 0/1/10은 프로덕션 가능.
7. Page Cache: 모든 파일시스템의 친구
Linux Page Cache
모든 파일 읽기/쓰기는 기본적으로 page cache를 거친다:
read() → Page cache 확인 → 있으면 즉시 복사
→ 없으면 디스크 읽기 → 캐싱 → 복사
write() → Page cache에 쓰기 (dirty 표시)
→ 백그라운드에서 디스크로 flush (writeback)
영향
- 반복 읽기가 매우 빠름 (메모리 속도).
- 큰
free -h의cache칸: 정상. - fsync() 없이는 데이터가 영구적이지 않다.
- Dirty page가 너무 많으면 큰 pause (vm.dirty_ratio).
O_DIRECT
Page cache를 우회하는 옵션:
int fd = open("data.bin", O_RDONLY | O_DIRECT);
용도: 데이터베이스(자체 버퍼 풀 관리), 벤치마크. 엄격한 정렬 요구.
PostgreSQL은 전통적으로 page cache 활용, MySQL InnoDB는 O_DIRECT 선호. ScyllaDB는 완전 O_DIRECT.
fsync의 중요성
write() 성공만으로는 데이터가 디스크에 영구 저장된 게 아니다. fsync() 또는 fdatasync() 후에야 보장.
write(fd, data, size); // page cache까지만
fsync(fd); // 디스크까지 확인
데이터베이스는 commit 시 반드시 fsync. 성능 비용이 크다 (디스크 latency).
8. 파일시스템 벤치마크: 실전 비교
워크로드별 성능 특성
실전에서 각 파일시스템의 성향:
| 워크로드 | ext4 | XFS | ZFS | Btrfs |
|---|---|---|---|---|
| 작은 파일 많음 | 빠름 | 보통 | 느림 | 보통 |
| 큰 파일 순차 I/O | 빠름 | 매우 빠름 | 빠름 | 빠름 |
| 랜덤 I/O | 빠름 | 빠름 | 보통 | 보통 |
| 동시 쓰기 | 보통 | 매우 빠름 | 빠름 | 보통 |
| 메타데이터 heavy | 보통 | 빠름 | 느림 | 보통 |
| sync write | 빠름 | 빠름 | 보통 (ZIL 필요) | 보통 |
PostgreSQL 벤치마크
PostgreSQL 성능 (일반적인 결과):
- XFS: 기준 100% (PostgreSQL 공식 권장).
- ext4:
95100%. - ZFS (well-tuned):
8595% (ZIL/SLOG 필수). - Btrfs:
7085% (CoW가 DB에 불리).
Kernel compile 벤치마크
작은 파일 많은 경우:
- ext4: 기준 100%.
- XFS:
95100%. - Btrfs:
9095%. - ZFS:
8090%.
실측 주의
벤치마크는 하드웨어, 튜닝, 워크로드에 따라 크게 다르다. 자기 워크로드로 실제 테스트하는 것이 정답.
9. 선택 가이드
결정 트리
데이터 체크섬이 필수인가?
├── 예 → ZFS (검증됨) or Btrfs (이제 안정)
└── 아니오 →
볼륨이 100 TB 이상? 동시 접근 많음?
├── 예 → XFS
└── 아니오 → ext4 (안전한 기본값)
워크로드별 추천
PostgreSQL / MySQL:
- 추천: XFS. 공식 권장, 대용량과 고동시성 유리.
- 대안: ext4.
- 피하기: ZFS (sync write 오버헤드), Btrfs (CoW 부적합).
웹 서버 (정적 파일):
- 추천: ext4 또는 XFS. 차이 미미.
- ZFS도 OK (압축 + ARC 이점).
파일 서버 / NAS:
- 추천: ZFS (스냅샷, 체크섬, RAID-Z).
- 대안: Btrfs (RAID 1/10만).
백업 저장소:
- 추천: ZFS. Send/receive 강력.
컨테이너 호스트:
- 추천: XFS (/var/lib/docker) + OverlayFS.
- 또는 Btrfs의 subvolume.
개인 데스크톱:
- 추천: ext4. 단순, 안정, 빠름.
- 대안: Btrfs (스냅샷 원하면, Fedora 스타일).
VM 이미지 저장:
- 추천: XFS (대용량 파일 처리).
- ZFS는 zvol로 좋음.
고성능 저지연 (DB 전용):
- 추천: XFS with direct I/O.
- 또는 O_DIRECT 지원 ext4.
10. 실전 튜닝
ext4 튜닝
마운트 옵션:
# 안정 (기본)
defaults,data=ordered
# 성능 (덜 안전)
defaults,noatime,nodiratime,data=writeback,barrier=0
# SSD
defaults,noatime,nodiratime,discard
파일시스템 생성:
# 많은 작은 파일
mkfs.ext4 -I 512 -T small /dev/sda1
# 큰 파일 위주
mkfs.ext4 -T largefile /dev/sda1
-T는 inode 비율 프리셋.
XFS 튜닝
마운트 옵션:
defaults,noatime,nodiratime,inode64
생성:
mkfs.xfs -f -l size=512m /dev/sda1
# -l: log (journal) 크기. 큰 워크로드에 도움.
런타임 튜닝:
# stripe size (RAID와 조합)
mkfs.xfs -d sunit=1024,swidth=4096 ...
ZFS 튜닝
ARC 크기:
# /etc/modprobe.d/zfs.conf
options zfs zfs_arc_max=8589934592 # 8 GB
압축:
zfs set compression=lz4 tank/data # 빠름
zfs set compression=zstd tank/data # 더 압축
레코드 크기:
# DB에 최적화
zfs set recordsize=8K tank/postgres
# 큰 파일
zfs set recordsize=1M tank/media
동기 쓰기 정책:
# DB 데이터 파일
zfs set sync=standard tank/db
# ZFS 쓰기 캐시에 의존 (ZIL)
zfs set sync=disabled tank/temp # 위험
일반 튜닝: 커널 파라미터
# /etc/sysctl.conf
vm.dirty_background_ratio = 3 # 3%에서 백그라운드 flush 시작
vm.dirty_ratio = 10 # 10% 도달 시 동기 flush
vm.dirty_expire_centisecs = 3000 # 30초 넘은 dirty는 flush
vm.vfs_cache_pressure = 50 # 디렉토리/inode 캐시 유지 성향
dirty_ratio를 줄이면 "갑자기 멈춤" 현상 감소.
discard / fstrim (SSD)
SSD에서는 사용하지 않는 블록을 알려야 성능 유지:
# 주기적 TRIM
systemctl enable fstrim.timer
# 또는 마운트 옵션 (비권장, overhead)
mount -o discard /dev/sda1 /mnt
11. 흔한 문제와 해결
문제 1: "No space left on device" but 공간 있음
원인 1: Inode 고갈. 수백만 개의 작은 파일이 있을 때.
df -i # inode 사용량 확인
해결: 파일 정리 또는 mkfs에서 -N <count> (새로 만들 때).
원인 2: ZFS의 copy-on-write 오버헤드. 80% 이상 차면 성능 급락.
해결: ZFS pool은 80% 이하로 유지.
문제 2: fsck가 끝나지 않는다
원인: 거대한 ext4 파일시스템 (수십 TB).
해결:
- XFS로 이전 고려 (훨씬 빠른 복구).
e2fsck -f -D(정기 최적화).- Journaling 있으면 경미한 문제는 자동 복구.
문제 3: 백업 중 디스크 사용률 100%
원인: rsync, tar 등이 메모리+디스크 과부하.
해결:
ionice -c 3(idle 우선순위).nice -n 19.- ZFS/Btrfs 사용자는 스냅샷 + send/receive로 훨씬 효율적.
문제 4: Sync write가 매우 느림
원인: Sync write는 fsync + 물리 디스크 latency.
해결:
- ZIL/SLOG (ZFS): NVMe를 전용 log device로.
- Battery-backed RAID controller (하드웨어).
- PostgreSQL:
synchronous_commit = off(데이터 손실 감수).
문제 5: "silent corruption" 감지
증상: 파일이 손상되었는데 에러 없음.
원인: 디스크가 잘못된 데이터를 반환했는데 OS는 맞다고 받아들임.
해결:
- 체크섬 있는 파일시스템 (ZFS, Btrfs).
- ECC RAM.
- 정기적 scrub:
# ZFS
zpool scrub tank
# Btrfs
btrfs scrub start /mnt
12. 최신 동향
bcachefs
Kent Overstreet이 개발한 최신 파일시스템. 2023년 Linux 6.7에 병합:
- ZFS/Btrfs와 비슷한 기능.
- Tiered storage: SSD + HDD 자동 관리.
- 체크섬, 스냅샷, 압축.
- 아직 "실험적", 프로덕션 비권장.
Composefs / erofs
컨테이너용 읽기 전용 파일시스템. 이미지 배포에 최적.
- erofs: Enhanced Read-Only File System. 매우 작음.
- Composefs: 많은 컨테이너가 같은 파일을 공유할 때 효율.
Storage-class Memory 지원
Intel Optane 같은 byte-addressable 영구 메모리용. DAX 모드로 page cache 우회.
mount -o dax /dev/pmem0 /mnt/pmem
그러나 Optane은 2022년 단종, 이 방향은 불확실.
Filesystem-in-Userspace (FUSE)
커널 수정 없이 사용자 공간에서 파일시스템 구현:
- SSHFS: SSH로 원격 파일시스템.
- s3fs: S3를 파일시스템으로.
- rclone: 거의 모든 클라우드 스토리지.
단점: 오버헤드 (매 시스템콜이 context switch).
퀴즈로 복습하기
Q1. Journaling이 fsck를 어떻게 대체하는가?
A. ext2 시대: 전원 차단 등으로 inconsistent해진 파일시스템은 fsck로 전체 스캔해서 복구. TB 단위에선 수십 분~수 시간. 프로덕션에선 용납 불가.
Journaling: 변경을 두 단계로 진행한다:
- Journal에 변경 기록: "inode X를 수정, block Y 할당, 디렉토리 Z 수정" 같은 트랜잭션.
- Journal을 fsync로 디스크에 확정 (이 단계가 완료되면 안전).
- 실제 파일시스템에 변경 적용 (checkpoint).
- Journal entry 삭제.
장애 복구:
- 부팅 시 journal을 읽음.
- 완료 표시 있는 트랜잭션 → 아직 checkpoint 안 됐으면 재적용 (redo).
- 완료 안 된 것 → 버림.
- 수 초 내 완료.
Trade-off:
- 모든 쓰기가 journal + 실제, 두 번 쓰임 → 쓰기 성능 감소.
- ext4의 기본
ordered모드는 메타데이터만 journal에 써서 절충.
ZFS/Btrfs는 journal 없음: Copy-on-Write 자체가 원자성을 보장하므로 별도 로그 불필요. 더 우아한 접근이지만 쓰기 증폭 대가.
Journaling은 "빠른 복구 vs 약간 느린 쓰기" 의 선택이었고, 대부분의 현대 파일시스템이 이 선택을 했다.
Q2. XFS의 Allocation Group이 동시성에 어떤 이점을 주는가?
A. XFS는 전체 볼륨을 여러 Allocation Group (AG) 으로 분할한다 (기본 4개 이상). 각 AG는:
- 독립적인 free space 관리 (B+ tree).
- 독립적인 inode 할당 (B+ tree).
- 자체 통계.
동시성 이점:
-
병렬 할당: 여러 프로세스가 서로 다른 AG에서 동시에 파일 생성/확장 가능. 잠금 경합 최소.
-
병렬 I/O: 여러 워커가 각자 다른 AG의 메타데이터를 동시에 읽고 쓸 수 있음.
-
선형 확장: 코어 수가 늘어나도 각 코어가 다른 AG를 담당 → scalability.
ext4의 비교:
- ext4도 block group이 있지만, 전역 락 또는 적은 병렬성.
- AG처럼 독립적 B+ tree를 갖지 않음.
실전 효과:
- 다중 프로세스 쓰기 워크로드에서 XFS가 ext4 대비 2~3배 빠름.
- 수십 코어 서버에서 차이가 극명.
- 웹 서버, DB처럼 많은 파일에 동시 접근하는 경우.
이것이 XFS가 RHEL 기본 파일시스템이 된 이유다. 대용량 엔터프라이즈 환경, 다중 CPU, 대용량 스토리지에서 XFS의 AG 기반 설계가 빛을 발한다.
Trade-off:
- 볼륨을 AG로 나누는 오버헤드 (작은 볼륨에선 손해).
- 한 AG가 가득 차면 다른 AG로 이사 해야 함 (드물지만).
그래서 작은 볼륨(100 GB 이하)에선 ext4가 여전히 경쟁력 있고, 큰 볼륨(500 GB 이상)에서 XFS가 우세하다.
Q3. ZFS의 Copy-on-Write가 왜 journal을 불필요하게 만드는가?
A. Journal의 목적은 "partial write로 인한 inconsistency 방지"다. ZFS는 이를 완전히 다른 방법으로 해결한다.
ZFS의 CoW 원칙:
- 기존 블록을 절대 덮어쓰지 않는다. 모든 쓰기는 새 블록에.
- 변경된 블록의 부모 메타데이터 블록도 새로 복사하고 업데이트.
- 루트까지 이 연쇄가 올라감.
- 마지막에 uberblock (파일시스템 루트)만 원자적으로 업데이트.
결과:
- 쓰기가 끝나지 않은 상태에서 장애 → 기존 uberblock이 여전히 유효. 이전 상태가 그대로 보임.
- 쓰기가 끝남 → 새 uberblock 활성. 새 상태가 보임.
- 중간 상태는 절대 관찰되지 않는다.
이는 원자적 포인터 교체로 볼 수 있다. 트리 전체의 변경이 단일 포인터 업데이트로 commit된다.
Journal 없이도:
- 일관성 보장 (partial write 불가능).
- 빠른 장애 복구 (복잡한 redo/undo 불필요).
- 오래된 스냅샷도 원자성 유지.
대가:
- 쓰기 증폭: 작은 변경도 메타데이터 연쇄 업데이트 → 여러 블록 쓰기.
- 공간 사용: 구 데이터가 참조되는 동안 유지 (스냅샷 덕분).
- Fragmentation 증가: 같은 위치가 아닌 다른 위치에 쓰기.
Btrfs도 같은 원리. 이것이 ZFS와 Btrfs가 journaling 없이도 ext4/XFS보다 더 안전할 수 있는 이유다. 반면 성능 특성이 달라서 DB에는 부적합한 경우가 많다 (작은 sync write가 메타데이터 연쇄를 유발).
CoW는 원자성의 우아한 해결책이지만 모든 워크로드에 맞지는 않는다. 파일시스템 선택은 이 트레이드오프를 이해하는 데서 출발한다.
Q4. ZFS와 Btrfs가 체크섬으로 silent corruption을 어떻게 감지하고 복구하는가?
A. "Silent corruption"은 디스크가 잘못된 데이터를 에러 없이 반환하는 현상이다. 전통 파일시스템(ext4, XFS)은 이를 감지하지 못한다.
원인:
- 디스크의 우주선 비트 플립.
- 펌웨어 버그.
- 케이블 문제.
- RAM 비트 플립 (ECC 없는 경우).
ZFS/Btrfs의 접근:
- 쓰기 시: 각 블록의 체크섬(SHA-256 등)을 부모 메타데이터 블록에 저장.
- 읽기 시: 실제 데이터를 읽고 체크섬 계산 → 저장된 것과 비교.
- 일치: OK.
- 불일치: 에러 감지!
복구 (Mirror 또는 RAID):
읽기 시도: disk A → 체크섬 불일치 → 손상 감지
↓
자동 대체: disk B (mirror) → 체크섬 확인 → 정상
↓
자동 복구: disk A에 정상 데이터 다시 쓰기
↓
반환: 정상 데이터
애플리케이션에게는 투명하다. 그저 올바른 데이터를 받는다. ZFS는 백그라운드에서 복구까지 끝낸다.
Scrub: 주기적으로 모든 데이터를 읽고 체크섬 검증하는 작업. 아직 읽히지 않은 손상된 블록을 미리 찾아 복구한다.
# ZFS
zpool scrub tank
# Btrfs
btrfs scrub start /mnt
월 1회 정도 실행. 성능 영향 적음 (백그라운드).
실전 중요성:
- 페타바이트 규모에선 silent corruption이 통계적으로 반드시 발생.
- 중요한 데이터(백업, 법적 기록, 과학 데이터)는 체크섬 파일시스템 필수.
- 일반 HDD는 연간 수백 건의 unrecoverable read error 확률 있음.
체크섬 없는 파일시스템의 한계:
- 디스크 에러를 파일시스템 레벨에선 모름. 결과: 조용한 데이터 손실.
- 오래된 백업이 손상되어도 발견이 늦음.
ECC RAM과의 조합: 파일시스템 체크섬은 디스크 에러를 잡지만, RAM 에러는 못 잡는다. 중요한 데이터 서버는 ECC RAM + ZFS 조합이 골드 스탠다드.
이것이 Google, Amazon, Facebook 같은 대기업이 자체 스토리지 시스템에 체크섬을 기본으로 넣는 이유다. Silent corruption은 "있을 수 있는 문제"가 아니라 "반드시 일어나는 문제" 다.
Q5. PostgreSQL에 XFS가 권장되는 이유와 ZFS가 비권장되는 이유는?
A. PostgreSQL의 I/O 패턴을 이해하면 답이 나온다:
- Sync write 많음: WAL 커밋마다 fsync.
- 8KB 블록 단위 I/O: 작고 빈번.
- 동시 처리: 많은 클라이언트가 병렬로.
- 자체 버퍼 풀 관리:
shared_buffers가 page cache와 중복 경향.
XFS가 적합한 이유:
- 뛰어난 동시성: Allocation Group 기반 병렬 처리. DB의 다중 백엔드에 이상적.
- 낮은 metadata 오버헤드: 대부분 DB 파일은 append/update로 메타데이터 변경 적음.
- 큰 파일 효율: PG의 테이블 파일이 GB 단위로 커져도 extent로 효율적.
- Sync write 성능: fsync가 빠름. WAL 병목 최소화.
- Red Hat 지원: RHEL 공식 기본. 오랜 검증.
- PostgreSQL 공식 권장.
ZFS가 불리한 이유:
- 쓰기 증폭: CoW로 인해 작은 sync write가 여러 메타데이터 블록 쓰기로 증폭.
- Double caching: ZFS의 ARC + PostgreSQL의 shared_buffers → 메모리 중복 사용.
- ZIL 없으면 sync write 매우 느림: NVMe SLOG 필수.
- CoW fragmentation: 시간이 지날수록 랜덤 I/O 성능 저하.
- PostgreSQL 메모리 튜닝 복잡: ARC가 사용 가능 메모리 대부분 차지.
그러나 ZFS는 특정 이점이 있다:
- 스냅샷: DB 백업이 즉시.
- 압축: 저장 공간 절약 (30~50%).
- 체크섬: silent corruption 방어.
- Clone: 개발용 DB 복사본 즉시 생성.
실전 권장:
- 운영 DB 서버 (주요): XFS. 성능 최우선.
- 백업 / DR DB: ZFS. 스냅샷 + 압축 이점.
- 개발 DB: ZFS. 빠른 clone으로 여러 브랜치 테스트.
- 초고성능 DB: XFS + O_DIRECT + NVMe 직접.
튜닝 팁:
XFS with PostgreSQL:
mount -o noatime,nodiratime,inode64 /dev/sda1 /var/lib/pgsql
ZFS with PostgreSQL (만약 사용한다면):
zfs set recordsize=8K tank/postgres # PG block size에 맞춤
zfs set compression=lz4 tank/postgres
zfs set atime=off tank/postgres
zfs set primarycache=metadata tank/postgres # PG가 페이지 캐시
# + NVMe SLOG (ZIL) 필수
결론: XFS는 성능, ZFS는 안전성. PostgreSQL 같은 OLTP에는 성능이 우선이므로 XFS가 표준 선택이다. 단, 데이터 무결성이 극도로 중요하고 성능은 2순위인 경우엔 ZFS를 고려할 수 있다.
마치며: 파일 한 개 뒤의 거대한 엔지니어링
핵심 정리
- 4가지 기본: Superblock, inode, dentry, data block.
- Journaling: ext4, XFS의 내구성 보장.
- Copy-on-Write: ZFS, Btrfs의 원자성 + 스냅샷.
- 체크섬: ZFS, Btrfs의 데이터 무결성.
- ext4: 안전한 기본값.
- XFS: 대용량, 고동시성, DB.
- ZFS: 데이터 무결성, 스냅샷, 백업.
- Btrfs: GPL CoW, RAID 1/10만.
실전 선택
| 상황 | 추천 |
|---|---|
| 데스크톱 Linux | ext4 |
| 웹/앱 서버 | ext4 또는 XFS |
| 데이터베이스 | XFS |
| NAS / 파일 서버 | ZFS |
| 백업 저장소 | ZFS |
| 컨테이너 호스트 | XFS |
| 대용량 아카이브 | XFS 또는 ZFS |
| 개인 작업 VM | ext4 |
마지막 교훈
파일시스템은 눈에 보이지 않는 인프라다. 잘 작동할 때는 존재를 잊는다. 그러나 잘못된 선택이나 설정은 수년간 괴롭히는 문제를 만든다:
- 잘못된 fs: DB 성능 반토막.
- 체크섬 없는 fs: 수년 후 백업이 손상된 걸 발견.
- 잘못된 마운트 옵션: 일관성 없는 "이상한" 증상.
- Journal 모드 오해: 장애 시 데이터 손실.
이 글의 지식은 정기적으로 참고할 가치가 있다. 새 서버를 설정할 때, 기존 시스템을 튜닝할 때, 장애를 조사할 때. 파일시스템은 OS의 가장 복잡한 부분 중 하나지만, 기본 원리를 이해하면 대부분의 문제는 논리적으로 접근할 수 있다.
당신이 다음에 mkfs를 칠 때, "그냥 ext4"가 아니라 "왜 이 파일시스템을 고르는가" 라고 물어보자. 그 답이 있으면 이미 충분한 엔지니어다.
참고 자료
- ext4 Data Structures and Algorithms (Linux Kernel)
- XFS Algorithms and Data Structures
- OpenZFS Documentation
- Btrfs Wiki
- PostgreSQL File System Recommendations
- Linux Filesystems: Past, Present and Future
- Red Hat Storage Administration Guide: XFS
- Systems Performance (Brendan Gregg) - 성능 관점
- LWN: The state of the filesystems - 다양한 파일시스템 업데이트
- Jeff Bonwick: ZFS: The Last Word in Filesystems - 역사적 기고문
현재 단락 (1/620)
$ echo "hello world" > /tmp/test.txt