새벽 3시, PagerDuty 알림이 울립니다. /var/lib/mysql 파티션 사용률이 93%입니다. df -h로 확인하니 20GB 파티션이 꽉 찼습니다. 전통 파티션 방식이라면 MySQL을 멈추고, 데이터를 임시 디스크에 옮기고, 파티션을 지웠다 새로 만들고, 다시 복원해야 합니다. 예상 다운타임 2~3시간, 새벽에 서비스 중단 공지를 써야 하는 상황.
같은 서버가 LVM으로 구성되어 있었다면 lvextend -L +20G -r /dev/vg_data/lv_mysql 한 줄, 3분 안에 끝납니다.
LVM & 볼륨 관리
— — —
1. 기존 파티션의 한계와 LVM의 등장
dnf install -y lvm2 || apt-get install -y lvm2
lsblk -f && pvs && vgs && lvs
dnf install -y mdadm || apt-get install -y mdadm
새벽 3시, 데이터베이스 서버에서 PagerDuty 알림이 울렸습니다. /var/lib/mysql 파티션 사용률이 93%였고, df -h로 확인해보니 20GB짜리 파티션 하나가 꽉 차 있었습니다. 파티션을 늘리려면 MySQL을 멈추고 데이터를 임시 디스크에 옮겨두고, 파티션을 지웠다 새로 만든 뒤 데이터를 다시 복원해야 했습니다. 예상 다운타임은 2~3시간이었고, 새벽에 서비스 중단 공지를 써야 하는 상황이었습니다. 같은 서버가 LVM으로 구성되어 있었다면 lvextend -L +20G 한 줄로 온라인 확장이 가능했을 겁니다.
전통적인 디스크 파티션 방식은 1980년대 설계된 개념으로, 현대 클라우드 인프라의 유연한 스토리지 요구를 충족하기 어렵습니다.
전통 파티션 방식의 한계:
- 크기 변경 불가: 파티션을 생성하면 크기가 고정됩니다. 확장하려면 서비스를 중단해야 합니다.
- 단일 디스크 종속: 하나의 파티션은 하나의 물리 디스크에만 존재합니다. 디스크 용량 한계가 곧 파티션 한계입니다.
- 낭비 발생:
/home에 공간이 남아도/var가 꽉 차면 이동이 불가합니다. - 사전 계획 필수: 초기 설치 시 정확한 용량 예측이 필요합니다. 예측이 틀리면 대공사가 필요합니다.
LVM이 제공하는 해결책:
- 운영 중인 서비스를 중단하지 않고 볼륨 크기를 늘릴 수 있습니다.
- 여러 물리 디스크를 하나의 스토리지 풀로 통합합니다.
- 스냅샷 기능으로 특정 시점의 상태를 순간 포착합니다.
- 새 디스크를 기존 볼륨 그룹에 추가해 동적으로 용량을 확장합니다.
LVM이 실제로 쓰이는 환경 — 현장에서 LVM이 필요한 실제 시나리오들입니다:
데이터베이스 서버 → 데이터 증가에 따라 주기적으로 스토리지 확장 필요 로그 서버 → 예측 불가능한 로그 증가량 개발/스테이징 환경 → 다양한 크기의 볼륨을 유연하게 할당 백업 서버 → LVM 스냅샷으로 일관성 있는 백업 생성
실무 포인트: 대부분의 Linux 배포판은 기본 설치 시 LVM을 사용합니다.
df -h로 마운트 포인트를 확인하면/dev/mapper/...형태의 경로가 보이는데, 이것이 LVM 논리 볼륨입니다.
— — —
2. LVM 3계층 구조 이해
LVM 서버에서 /var/log가 꽉 차 로그 수집이 멈췄습니다. vgs로 확인하니 VG에 여유 공간이 충분했는데, 막상 어떤 명령어를 써야 하는지 감이 안 왔습니다. 검색하면 pvcreate, vgextend, lvextend가 동시에 나오는데 무엇이 먼저인지, PV·VG·LV 중 어디에 명령을 내려야 하는지 헷갈렸습니다. 이 세 계층의 역할을 한 번 정확히 구분해두면, 이후 LVM 명령어 전체가 자연스럽게 읽힙니다.
LVM은 물리 스토리지를 3단계로 추상화합니다. 이 계층 구조를 이해하면 LVM의 모든 명령어가 자연스럽게 이해됩니다.
처음 보면 PV, VG, LV라는 세 가지 약자가 낯설게 느껴집니다. 레고 블록에 비유하면 직관적으로 이해됩니다.
- PV(Physical Volume)는 레고 블록 한 조각입니다. 실제 물리 디스크나 파티션을 LVM이 인식할 수 있도록 등록하는 단계입니다.
pvcreate /dev/sdb1을 실행하기 전까지는 LVM 입장에서 그 디스크는 "존재하지 않는 것"과 같습니다. - VG(Volume Group)는 블록들을 담는 바구니입니다. 여러 PV를 하나의 스토리지 풀로 묶어줍니다. 500GB 디스크와 1TB 디스크를 묶어 1.5TB짜리 풀을 만드는 것이 VG 생성입니다. 이후 모든 용량 계산은 이 풀 기준으로 이루어집니다.
- LV(Logical Volume)는 바구니에서 꺼내 실제로 쓰는 조각입니다. VG 풀에서 원하는 크기만큼 떼어내 파일시스템을 올리는 공간입니다. MySQL 데이터용으로 15GB, 로그용으로 10GB처럼 필요에 따라 동적으로 생성하고 나중에 늘릴 수 있습니다.
이 3계층 구조 덕분에 LVM은 물리 경계를 넘어 볼륨을 자유롭게 늘리고 줄일 수 있습니다.
아래는 동일한 구조를 텍스트로 표현한 것입니다:
물리 계층 (Physical) ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │ /dev/sdb │ │ /dev/sdc │ │ /dev/sdd │ │ (500GB HDD) │ │ (1TB SSD) │ │ (1TB SSD) │ └──────┬───────┘ └──────┬───────┘ └──────┬───────┘ │ │ │ ▼ ▼ ▼ PV 계층 (Physical Volume) ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │ PV: sdb │ │ PV: sdc │ │ PV: sdd │ │ pvcreate │ │ pvcreate │ │ pvcreate │ └──────┬───────┘ └──────┬───────┘ └──────┬───────┘ │ │ │ └─────────────────┼─────────────────┘ │ ▼ VG 계층 (Volume Group) — 스토리지 풀 ┌────────────────────────────────────────────────────┐ │ vg_data (2.5TB 통합 풀) │ │ vgcreate │ └──────────────────────┬─────────────────────────────┘ │ ┌────────────┼────────────┐ ▼ ▼ ▼ LV 계층 (Logical Volume) — 실제 사용 ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │ lv_mysql │ │ lv_logs │ │ lv_backup │ │ (500GB) │ │ (800GB) │ │ (1.2TB) │ │ lvcreate │ │ lvcreate │ │ lvcreate │ └──────┬───────┘ └──────┬───────┘ └──────┬───────┘ │ │ │ ▼ ▼ ▼ 파일시스템 & 마운트 포인트 /dev/vg_data/lv_mysql /dev/vg_data/lv_logs /dev/vg_data/lv_backup /var/lib/mysql /var/log /backup
각 계층의 역할 — PV → VG → LV 세 계층이 어떤 역할을 하는지 핵심 명령어와 함께 정리했습니다:
| 계층 | 이름 | 역할 | 핵심 명령어 |
|---|---|---|---|
| 1단계 | Physical Volume (PV) | 물리 디스크/파티션을 LVM에 등록 | pvcreate |
| 2단계 | Volume Group (VG) | PV들을 하나의 스토리지 풀로 통합 | vgcreate, vgextend |
| 3단계 | Logical Volume (LV) | VG에서 원하는 크기만큼 논리 볼륨 할당 | lvcreate, lvextend |
PE(Physical Extent)란?
LVM은 디스크를 **PE(Physical Extent)**라는 고정 크기 블록으로 나눕니다. 기본값은 4MB이며, VG 생성 시 변경할 수 있습니다. LV는 이 PE 단위로 할당됩니다. 예를 들어 PE 크기가 4MB이고 LV가 100GB라면, 해당 LV는 25,600개의 PE로 구성됩니다.
bash# PE 크기 확인 vgdisplay vg_data | grep "PE Size" # PE Size 4.00 MiB
— — —
3. LVM 전체 구성 실습
실습 전 루프백 이미지 파일을 생성하여 실제 디스크 없이 LVM을 실습할 수 있도록 환경을 준비합니다.
bash# 실습 디렉토리 준비 mkdir -p /tmp/linux/part3/exam_2 # 실습용 루프백 이미지 파일 생성 (실제 디스크 없이 LVM 실습) dd if=/dev/zero of=/tmp/linux/part3/exam_2/disk1.img bs=1M count=500 2>/dev/null dd if=/dev/zero of=/tmp/linux/part3/exam_2/disk2.img bs=1M count=500 2>/dev/null LOOP1=$(sudo losetup -f --show /tmp/linux/part3/exam_2/disk1.img) LOOP2=$(sudo losetup -f --show /tmp/linux/part3/exam_2/disk2.img) echo "루프백 장치: $LOOP1, $LOOP2"
이제 실습을 진행합니다.
실습에서는 /dev/sdb, /dev/sdc 두 개의 디스크를 사용합니다. 가상 머신이나 클라우드 인스턴스에 추가 디스크를 연결한 후 시작합니다.
현재 디스크 상태 확인 — LVM 설정 전에 현재 디스크와 파티션 구성을 파악합니다:
bash# 연결된 모든 블록 장치 확인 lsblk # 예시 출력: # NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINTS # sda 8:0 0 50G 0 disk # ├─sda1 8:1 0 1G 0 part /boot # └─sda2 8:2 0 49G 0 part / # sdb 8:16 0 20G 0 disk ← 새로 추가된 디스크 # sdc 8:32 0 20G 0 disk ← 새로 추가된 디스크 # 디스크 상세 정보 확인 fdisk -l /dev/sdb fdisk -l /dev/sdc
파티션 생성 (선택 사항):
물리 디스크 전체를 PV로 사용할 수도 있지만, 파티션을 먼저 생성하는 것이 일반적입니다. 파티션 타입을 Linux LVM (8e)으로 설정해야 합니다.
bash# /dev/sdb에 파티션 생성 fdisk /dev/sdb # fdisk 대화형 명령: # n → 새 파티션 생성 # p → Primary 파티션 # 1 → 파티션 번호 1 # Enter → 기본 시작 섹터 # Enter → 기본 끝 섹터 (전체 사용) # t → 파티션 타입 변경 # 8e → Linux LVM 타입 코드 # w → 변경사항 저장 # /dev/sdc도 동일하게 진행 fdisk /dev/sdc # 파티션 테이블 재로드 partprobe /dev/sdb partprobe /dev/sdc # 결과 확인 lsblk # sdb 8:16 0 20G 0 disk # └─sdb1 8:17 0 20G 0 part # sdc 8:32 0 20G 0 disk # └─sdc1 8:33 0 20G 0 part
참고: 파티션 없이 디스크 전체(
/dev/sdb)를 PV로 사용할 수도 있습니다. 이 경우 파티션 생성 단계를 건너뛰어도 됩니다.
pvcreate 명령으로 파티션(또는 디스크)을 LVM의 Physical Volume으로 초기화합니다.
bash# PV 생성 pvcreate /dev/sdb1 # Physical volume "/dev/sdb1" successfully created. pvcreate /dev/sdc1 # Physical volume "/dev/sdc1" successfully created. # PV 목록 확인 (간략) pvs # 예시 출력: # PV VG Fmt Attr PSize PFree # /dev/sdb1 lvm2 --- <20.00g <20.00g # /dev/sdc1 lvm2 --- <20.00g <20.00g # PV 상세 정보 확인 pvdisplay /dev/sdb1 # 예시 출력: # "/dev/sdb1" is a new physical volume of "<20.00 GiB" # --- NEW Physical volume --- # PV Name /dev/sdb1 # VG Name # PV Size <20.00 GiB # Allocatable NO # PE Size 0 # Total PE 0 # Free PE 0 # Allocated PE 0 # PV UUID abc123-...
PV 생성 시 유용한 옵션: — PE 크기 조정 등 PV 생성 시 자주 쓰는 옵션입니다:
bash# PE 크기를 8MB로 지정하여 PV 생성 pvcreate --physicalextentsize 8M /dev/sdb1 # 기존 데이터가 있는 디스크에 강제 생성 (주의: 데이터 손실 가능) pvcreate --force /dev/sdb1 # 여러 디스크를 한번에 PV로 생성 pvcreate /dev/sdb1 /dev/sdc1
vgcreate로 PV들을 묶어 Volume Group을 만들고, lvcreate로 실제 사용할 Logical Volume을 생성합니다.
Volume Group 생성 — 여러 PV를 하나의 VG로 묶어 큰 스토리지 풀을 만듭니다:
bash# vg_data라는 이름의 VG 생성 (두 PV를 하나의 풀로 통합) vgcreate vg_data /dev/sdb1 /dev/sdc1 # 출력: # Volume group "vg_data" successfully created # VG 상태 확인 (간략) vgs # 예시 출력: # VG #PV #LV #SN Attr VSize VFree # vg_data 2 0 0 wz--n- <39.99g <39.99g # VG 상세 정보 확인 vgdisplay vg_data # 예시 출력: # --- Volume group --- # VG Name vg_data # System ID # Format lvm2 # VG Access read/write # VG Status resizable # MAX LV 0 # Cur LV 0 # Open LV 0 # Max PV 0 # Cur PV 2 # Act PV 2 # VG Size <39.99 GiB # PE Size 4.00 MiB # Total PE 10238 # Alloc PE / Size 0 / 0 # Free PE / Size 10238 / <39.99 GiB # VG UUID xyz789-...
Logical Volume 생성 — VG에서 필요한 크기만큼 LV를 잘라냅니다:
bash# 15GB 크기의 lv_mysql 생성 lvcreate -L 15G -n lv_mysql vg_data # 출력: # Logical volume "lv_mysql" created. # 10GB 크기의 lv_logs 생성 lvcreate -L 10G -n lv_logs vg_data # VG의 남은 공간 전부를 lv_backup으로 사용 lvcreate -l 100%FREE -n lv_backup vg_data # LV 상태 확인 lvs # 예시 출력: # LV VG Attr LSize Pool Origin Data% Meta% Move Log Cpy%Sync Convert # lv_backup vg_data -wi-a----- <14.99g # lv_logs vg_data -wi-a----- 10.00g # lv_mysql vg_data -wi-a----- 15.00g # 상세 LV 정보 lvdisplay /dev/vg_data/lv_mysql
lvcreate 옵션 정리 — 용량, 이름, 스냅샷 등 lvcreate 주요 옵션과 예시입니다:
| 옵션 | 설명 | 예시 |
|---|---|---|
-L 크기 |
절대 크기 지정 | -L 15G |
-l PE수 |
PE 개수로 지정 | -l 3840 |
-l %VG |
VG 비율로 지정 | -l 50%VG |
-l %FREE |
남은 공간 비율 | -l 100%FREE |
-n 이름 |
LV 이름 지정 | -n lv_mysql |
LV는 파일시스템이 없는 빈 블록 장치입니다. 사용하려면 파일시스템을 생성하고 마운트 포인트에 연결해야 합니다.
파일시스템 생성 — LV에 ext4 또는 XFS 파일시스템을 생성합니다:
bash# ext4 파일시스템 생성 (범용적, 축소 가능) mkfs.ext4 /dev/vg_data/lv_mysql # 출력 예시: # mke2fs 1.46.5 (30-Dec-2021) # Creating filesystem with 3932160 4k blocks and 983040 inodes # Filesystem UUID: a1b2c3d4-... # Writing inode tables: done # Creating journal (16384 blocks): done # Writing superblocks and filesystem accounting information: done # xfs 파일시스템 생성 (고성능, 대용량 파일 최적화, 확장만 가능) mkfs.xfs /dev/vg_data/lv_logs # ext4로 lv_backup 생성 mkfs.ext4 /dev/vg_data/lv_backup
마운트 포인트 생성 및 마운트 — 디렉터리를 만들고 LV를 마운트합니다:
bash# 마운트 포인트 디렉토리 생성 mkdir -p /var/lib/mysql mkdir -p /var/log/app mkdir -p /backup # 임시 마운트 (재부팅 시 사라짐) mount /dev/vg_data/lv_mysql /var/lib/mysql mount /dev/vg_data/lv_logs /var/log/app mount /dev/vg_data/lv_backup /backup # 마운트 확인 df -hT | grep -E "vg_data|Filesystem" # 출력 예시: # Filesystem Type Size Used Avail Use% Mounted on # /dev/mapper/vg_data-lv_mysql ext4 15G 24K 14G 1% /var/lib/mysql # /dev/mapper/vg_data-lv_logs xfs 10G 105M 9.9G 2% /var/log/app # /dev/mapper/vg_data-lv_backup ext4 15G 24K 14G 1% /backup
UUID 확인 및 /etc/fstab 등록:
재부팅 후에도 자동으로 마운트되도록 /etc/fstab에 등록합니다. 장치 경로 대신 UUID를 사용하는 것이 안전합니다.
bash# UUID 확인 blkid /dev/vg_data/lv_mysql # /dev/mapper/vg_data-lv_mysql: UUID="a1b2c3d4-e5f6-7890-abcd-ef1234567890" BLOCK_SIZE="4096" TYPE="ext4" blkid /dev/vg_data/lv_logs # /dev/mapper/vg_data-lv_logs: UUID="b2c3d4e5-f6a7-8901-bcde-f12345678901" BLOCK_SIZE="512" TYPE="xfs" blkid /dev/vg_data/lv_backup # /dev/mapper/vg_data-lv_backup: UUID="c3d4e5f6-a7b8-9012-cdef-123456789012" BLOCK_SIZE="4096" TYPE="ext4" # /etc/fstab 편집 # 형식: UUID=<uuid> <마운트포인트> <파일시스템타입> <옵션> <dump> <pass> cat >> /etc/fstab << 'EOF' UUID=a1b2c3d4-e5f6-7890-abcd-ef1234567890 /var/lib/mysql ext4 defaults 0 2 UUID=b2c3d4e5-f6a7-8901-bcde-f12345678901 /var/log/app xfs defaults 0 2 UUID=c3d4e5f6-a7b8-9012-cdef-123456789012 /backup ext4 defaults 0 2 EOF # fstab 문법 검증 (마운트하지 않고 검사만) mount -a --fake # 오류 없으면 아무 출력 없이 종료됨 # 실제 마운트 적용 (언마운트 후 fstab 기준으로 다시 마운트) umount /var/lib/mysql mount -a df -h /var/lib/mysql
fstab 옵션 설명 — /etc/fstab의 각 필드 의미와 권장값입니다:
| 필드 | 설명 | 권장값 |
|---|---|---|
| dump | 백업 유틸리티 사용 여부 | 0 (비활성) |
| pass | fsck 검사 순서 | 1 (루트), 2 (나머지), 0 (비활성) |
— — —
4. LV 및 VG 확장
LVM의 가장 강력한 기능 중 하나는 서비스를 중단하지 않고 볼륨을 확장하는 것입니다. LV 확장 후 반드시 파일시스템도 확장해야 합니다.
VG 여유공간 확인 — 확장 전에 VG에 남은 PE 수량을 확인합니다:
bashvgs vg_data # VG #PV #LV #SN Attr VSize VFree # vg_data 2 3 0 wz--n- <39.99g 0 ← VFree가 0이면 먼저 VG에 디스크 추가 필요 # 여유공간이 있을 경우 진행 vgs vg_data # VG #PV #LV #SN Attr VSize VFree # vg_data 2 2 0 wz--n- <39.99g 14.99g
ext4 볼륨 확장 (온라인) — 서비스 중단 없이 LV와 파일시스템을 동시에 확장합니다:
bash# lv_mysql을 5GB 추가 확장 (현재 15G → 20G) lvextend -L +5G /dev/vg_data/lv_mysql # 출력: # Size of logical volume vg_data/lv_mysql changed from 15.00 GiB (3840 extents) # to 20.00 GiB (5120 extents). # Logical volume vg_data/lv_mysql successfully resized. # ext4 파일시스템 온라인 확장 (마운트된 상태에서 가능) resize2fs /dev/vg_data/lv_mysql # 출력: # resize2fs 1.46.5 (30-Dec-2021) # Filesystem at /dev/mapper/vg_data-lv_mysql is mounted on /var/lib/mysql; # on-line resizing required # old_desc_blocks = 2, new_desc_blocks = 3 # The filesystem on /dev/mapper/vg_data-lv_mysql is now 5242880 (4k) blocks long. # 확장 결과 확인 df -h /var/lib/mysql # Filesystem Size Used Avail Use% Mounted on # /dev/mapper/vg_data-lv_mysql 20G 24K 19G 1% /var/lib/mysql
xfs 볼륨 확장 (온라인):
xfs는 resize2fs가 아닌 xfs_growfs를 사용합니다. xfs는 온라인 확장만 가능하며 축소는 지원하지 않습니다.
bash# lv_logs를 3GB 추가 확장 lvextend -L +3G /dev/vg_data/lv_logs # xfs 파일시스템 확장 (마운트 포인트를 인자로 사용) xfs_growfs /var/log/app # 출력: # meta-data=/dev/mapper/vg_data-lv_logs isize=512 agcount=4, agsize=655360 blks # = sectsz=512 attr=2, projid32bit=1 # data = bsize=4096 blocks=2621440, imaxpct=25 # = sunit=0 swidth=0 blks # naming =version 2 bsize=4096 ascii-ci=0, ftype=1 # log =internal bsize=4096 blocks=2560, version=2 # realtime =none extsz=4096 blocks=0, rtextents=0 # data blocks changed from 2621440 to 3407872 # 확인 df -h /var/log/app # Filesystem Size Used Avail Use% Mounted on # /dev/mapper/vg_data-lv_logs 13G 105M 13G 1% /var/log/app
lvextend + 파일시스템 확장을 한 번에 — -r 옵션으로 LV 확장과 파일시스템 확장을 한 명령에 처리합니다:
bash# -r 옵션: lvextend 후 자동으로 resize2fs 또는 xfs_growfs 실행 lvextend -L +5G -r /dev/vg_data/lv_mysql
VG의 여유공간이 부족할 때 새 물리 디스크를 추가하여 스토리지 풀을 확장합니다. 이 과정도 서비스 중단 없이 진행됩니다.
새 디스크 인식 확인 — 추가한 디스크가 커널에 인식됐는지 확인합니다:
bash# 새로 추가된 디스크 확인 (예: /dev/sdd) lsblk | grep sdd # sdd 8:48 0 30G 0 disk # 새 디스크에 파티션 생성 (또는 디스크 전체 사용) fdisk /dev/sdd # n → p → 1 → Enter → Enter → t → 8e → w partprobe /dev/sdd
새 디스크를 PV로 등록 후 VG에 추가 — 새 PV를 기존 VG에 합쳐 스토리지 풀을 확장합니다:
bash# 새 파티션을 PV로 생성 pvcreate /dev/sdd1 # Physical volume "/dev/sdd1" successfully created. # 기존 vg_data에 새 PV 추가 vgextend vg_data /dev/sdd1 # Volume group "vg_data" successfully extended # VG 확장 결과 확인 vgs vg_data # VG #PV #LV #SN Attr VSize VFree # vg_data 3 3 0 wz--n- <69.98g <29.99g # ↑ PV 3개, VFree가 30GB로 늘어남 # 전체 상태 한 번에 확인 pvs # PV VG Fmt Attr PSize PFree # /dev/sdb1 vg_data lvm2 a-- <20.00g 0 # /dev/sdc1 vg_data lvm2 a-- <20.00g 0 # /dev/sdd1 vg_data lvm2 a-- <30.00g <29.99g vgs # VG #PV #LV #SN Attr VSize VFree # vg_data 3 3 0 wz--n- <69.98g <29.99g lvs # LV VG Attr LSize # lv_backup vg_data -wi-ao---- <14.99g # lv_logs vg_data -wi-ao---- 13.00g # lv_mysql vg_data -wi-ao---- 20.00g
이제 lvextend로 기존 LV를 추가 확장하거나 새 LV를 생성할 수 있습니다.
— — —
5. LVM 스냅샷
MySQL이 운영 중인 볼륨을 rsync로 백업하면 파일 절반은 백업 전 상태, 나머지 절반은 백업 중 변경된 상태가 섞인 깨진 백업본이 만들어집니다. 데이터베이스를 중단하지 않고 일관성 있는 백업을 만들려면, 특정 시점의 상태를 순간적으로 고정한 뒤 그 복사본을 백업해야 합니다. LVM 스냅샷이 바로 이 역할을 합니다. FLUSH TABLES WITH READ LOCK으로 MySQL을 잠깐 멈추는 순간 스냅샷을 찍고 즉시 잠금을 해제하면, 이후 백업 작업이 몇 시간 걸리더라도 서비스에는 영향이 없습니다.
LVM 스냅샷은 특정 시점의 LV 상태를 순간 포착합니다. 데이터베이스 백업 시 특히 유용합니다. 데이터베이스를 잠깐 flush하고 스냅샷을 생성한 뒤 백업하면, 일관성 있는 백업본을 얻을 수 있습니다.
스냅샷 동작 원리 (COW: Copy-on-Write):
스냅샷 생성 시점에는 실제 데이터를 복사하지 않습니다. 원본 데이터가 변경될 때만 변경 전 데이터를 스냅샷 공간에 복사합니다. 따라서 스냅샷 생성은 매우 빠르고, 스냅샷 크기는 원본 LV보다 훨씬 작아도 됩니다.
스냅샷 생성 직후: lv_mysql (원본) ──── 실제 데이터 ────→ 파일시스템 lv_snap (스냅샷) → (비어 있음, 원본 참조) 데이터 변경 후: lv_mysql (원본) ──── 새 데이터 ─────→ 파일시스템 lv_snap (스냅샷) → 변경 전 데이터 복사본 + 변경되지 않은 데이터는 원본 참조
스냅샷 생성 및 활용 — LVM 스냅샷으로 배포 전 백업 시점을 만들고 롤백에 활용합니다:
bash# MySQL 데이터 flush (백업 일관성을 위해) mysql -e "FLUSH TABLES WITH READ LOCK;" # lv_mysql의 5GB 스냅샷 생성 lvcreate -L 5G -s -n lv_mysql_snap /dev/vg_data/lv_mysql # 출력: # Logical volume "lv_mysql_snap" created. # MySQL 잠금 해제 mysql -e "UNLOCK TABLES;" # 스냅샷 확인 lvs # LV VG Attr LSize Origin Snap% Data% # lv_mysql vg_data owi-aos--- 20.00g ← 원본 # lv_mysql_snap vg_data swi-a-s--- 5.00g lv_mysql 0.00 ← 스냅샷 # 스냅샷 마운트하여 백업 mkdir -p /mnt/snap_backup mount -o ro /dev/vg_data/lv_mysql_snap /mnt/snap_backup # rsync로 백업 진행 (서비스 영향 없음) rsync -av /mnt/snap_backup/ /backup/mysql-$(date +%Y%m%d)/ # 백업 완료 후 스냅샷 제거 umount /mnt/snap_backup lvremove /dev/vg_data/lv_mysql_snap
스냅샷 사용 시 주의사항:
- 스냅샷 공간이 가득 차면 스냅샷이 무효화됩니다. 원본 데이터 변경량을 고려하여 충분한 크기를 할당하세요.
lvs의Snap%컬럼으로 스냅샷 사용률을 모니터링하세요.- 스냅샷은 임시 목적으로만 사용하고, 장기 보관은 rsync 또는 tar로 실제 백업을 생성하세요.
— — —
6. 소프트웨어 RAID: mdadm
새 서버에 디스크를 여러 개 달아야 할 때, "그냥 전부 하나로 묶으면 안 되나요?"라는 질문이 나옵니다. 묶는 방식에 따라 디스크 하나가 죽었을 때 서버가 살아남는지 여부가 달라지고, 같은 디스크 4개를 써도 실제로 쓸 수 있는 용량이 완전히 달라집니다. 데이터베이스 서버인지 로그 수집 서버인지, 성능이 중요한지 데이터 안전성이 중요한지에 따라 RAID 레벨 선택이 달라집니다. 하드웨어 RAID 컨트롤러 없이도 mdadm으로 소프트웨어 RAID를 구성할 수 있으며, 클라우드 환경에서도 같은 개념이 그대로 적용됩니다.
RAID(Redundant Array of Independent Disks)는 여러 디스크를 하나처럼 사용하여 성능 향상 또는 데이터 이중화를 제공합니다. 하드웨어 RAID 컨트롤러 없이도 Linux에서 소프트웨어 RAID를 구성할 수 있습니다.
RAID 레벨 비교표 — RAID 0/1/5/6/10 각 레벨의 특성과 최소 디스크 수를 비교합니다:
| RAID 레벨 | 최소 디스크 | 내고장성 | 읽기 성능 | 쓰기 성능 | 용량 효율 | 주요 용도 |
|---|---|---|---|---|---|---|
| RAID 0 (스트라이핑) | 2개 | 없음 | 매우 높음 | 매우 높음 | 100% | 임시 데이터, 캐시 |
| RAID 1 (미러링) | 2개 | 1개 디스크 | 높음 (읽기 분산) | 동일 | 50% | OS, 중요 데이터 |
| RAID 5 (패리티) | 3개 | 1개 디스크 | 높음 | 중간 | (N-1)/N | 파일서버, NAS |
| RAID 10 (1+0) | 4개 | 각 미러에서 1개 | 매우 높음 | 높음 | 50% | 데이터베이스 |
RAID 구조 시각화 — RAID 1과 RAID 5의 데이터 분산 구조를 시각적으로 표현합니다:
RAID 0 (스트라이핑) — 성능 ↑, 안전성 없음 디스크1: [A1][A3][A5][A7] 디스크2: [A2][A4][A6][A8] → 둘 중 하나 고장 시 전체 데이터 손실 RAID 1 (미러링) — 안전성 ↑, 용량 50% 디스크1: [A1][A2][A3][A4] 디스크2: [A1][A2][A3][A4] ← 완전 복사본 → 한 쪽 고장해도 데이터 보존 RAID 5 (패리티) — 균형잡힌 선택 디스크1: [A1][B1][C1][P4] 디스크2: [A2][B2][P3][C2] 디스크3: [A3][P2][B3][C3] → 1개 고장 허용, 용량 효율 66% (3디스크 기준) RAID 10 (미러+스트라이핑) — 최고 성능+안전성 디스크1: [A1][A3] ─┐ RAID 1 쌍 디스크2: [A1][A3] ─┘ 디스크3: [A2][A4] ─┐ RAID 1 쌍 디스크4: [A2][A4] ─┘ → 각 미러 쌍에서 1개씩 고장 허용
실무에서의 선택 기준:
- RAID 0: 재생성 가능한 임시 데이터 (빌드 캐시, 임시 파일 등)
- RAID 1: 부팅 디스크, 설정 파일, 소규모 중요 데이터
- RAID 5: 4TB 이상의 파일 서버 (단, 고장 시 rebuild 중 추가 고장 위험)
- RAID 10: 데이터베이스 서버 — 성능과 안전성 모두 중요
mdadm은 Linux 소프트웨어 RAID를 관리하는 표준 도구입니다. RAID 1(미러링) 구성을 실습합니다.
mdadm 설치 및 준비 — mdadm을 설치하고 실습용 루프백 디바이스를 준비합니다:
bash# RHEL/CentOS/Rocky Linux dnf install -y mdadm # Ubuntu/Debian apt-get install -y mdadm # 사용할 디스크 확인 (실습: /dev/sde, /dev/sdf) lsblk | grep -E "sde|sdf"
RAID 1 어레이 생성 — 두 디스크를 미러링하는 RAID 1 어레이를 생성합니다:
bash# /dev/md0 이름으로 RAID 1 어레이 생성 mdadm --create /dev/md0 \ --level=1 \ --raid-devices=2 \ /dev/sde /dev/sdf # 생성 시 확인 메시지: # mdadm: Note: this array has metadata at the start and # may not be suitable as a boot device. If you plan to # store '/boot' on this device please ensure that # your boot-loader understands md/v1.x metadata, or use # --metadata=0.90 # Continue creating array? y # mdadm: Increasingly creating RAID1 array /dev/md0 # RAID 동기화 진행 상황 확인 (초기 동기화 필요) cat /proc/mdstat # 출력 예시 (동기화 중): # Personalities : [raid1] # md0 : active raid1 sde[0] sdf[1] # 20955136 blocks super 1.2 [2/2] [UU] # [=========>...........] resync = 47.5% (9952640/20955136) finish=1.1min speed=168M/s # 동기화 완료 후: # md0 : active raid1 sde[0] sdf[1] # 20955136 blocks super 1.2 [2/2] [UU] # ← [UU]는 두 디스크 모두 정상 (Up) # 어레이 상세 정보 mdadm --detail /dev/md0 # 출력: # /dev/md0: # Version : 1.2 # Creation Time : Thu Mar 26 10:00:00 2026 # Raid Level : raid1 # Array Size : 20955136 (19.99 GiB 21.46 GB) # Used Dev Size : 20955136 (19.99 GiB 21.46 GB) # Raid Devices : 2 # Total Devices : 2 # Persistence : Superblock is persistent # Update Time : Thu Mar 26 10:05:00 2026 # State : clean # Active Devices : 2 # Working Devices : 2 # Failed Devices : 0 # Spare Devices : 0 # Layout : - # Chunk Size : - # Name : server:0 # UUID : a1b2c3d4:e5f6a7b8:c9d0e1f2:a3b4c5d6 # Events : 18 # Number Major Minor RaidDevice State # 0 8 64 0 active sync /dev/sde # 1 8 80 1 active sync /dev/sdf
RAID 어레이에 파일시스템 생성 및 마운트 — RAID 어레이에 파일시스템을 만들고 영구 마운트합니다:
bash# RAID 장치에 ext4 파일시스템 생성 mkfs.ext4 /dev/md0 # 마운트 포인트 생성 및 마운트 mkdir -p /data/raid1 mount /dev/md0 /data/raid1 # mdadm 설정 파일 저장 (재부팅 후 자동 인식) mdadm --detail --scan >> /etc/mdadm.conf # 또는 Rocky Linux/RHEL의 경우: mdadm --detail --scan >> /etc/mdadm/mdadm.conf # fstab에 등록 (UUID 사용 권장) blkid /dev/md0 # /dev/md0: UUID="d4e5f6a7-..." TYPE="ext4" echo 'UUID=d4e5f6a7-... /data/raid1 ext4 defaults 0 2' >> /etc/fstab
디스크 고장 시뮬레이션 및 교체 — 디스크 하나를 강제로 고장 내고 핫스왑 교체 과정을 실습합니다:
bash# 디스크 고장 시뮬레이션 (/dev/sdf를 고장 상태로 표시) mdadm /dev/md0 --fail /dev/sdf # 상태 확인 cat /proc/mdstat # md0 : active raid1 sde[0] sdf[1](F) # 20955136 blocks super 1.2 [2/1] [U_] # ← [U_]: sde는 정상(U), sdf는 고장(_) # 고장 디스크 제거 mdadm /dev/md0 --remove /dev/sdf # 새 디스크 준비 (실습에서는 /dev/sdg 사용) # 실제 환경에서는 물리적으로 디스크를 교체한 후 진행 # 새 디스크 추가 (자동 rebuild 시작) mdadm /dev/md0 --add /dev/sdg # rebuild 진행 확인 cat /proc/mdstat # md0 : active raid1 sdg[2] sde[0] # 20955136 blocks super 1.2 [2/1] [U_] # [========>............] recovery = 42.3% (8857856/20955136) finish=1.0min speed=195M/s # rebuild 완료 후 # md0 : active raid1 sdg[1] sde[0] # 20955136 blocks super 1.2 [2/2] [UU]
— — —
7. LVM 상태 모니터링 명령어 총정리
bash# ========== PV 관련 ========== # PV 목록 간략히 pvs # PV 상세 정보 pvdisplay # 특정 PV 상세 pvdisplay /dev/sdb1 # PV 삭제 (VG에서 제거 후 가능) pvremove /dev/sdb1 # ========== VG 관련 ========== # VG 목록 간략히 vgs # VG 상세 정보 vgdisplay # 특정 VG 상세 vgdisplay vg_data # VG에 PV 추가 vgextend vg_data /dev/sdd1 # VG에서 PV 제거 (데이터 이동 후) pvmove /dev/sdb1 # 기존 PV의 데이터를 다른 PV로 이동 vgreduce vg_data /dev/sdb1 # VG 이름 변경 vgrename vg_data vg_production # VG 삭제 vgremove vg_data # ========== LV 관련 ========== # LV 목록 간략히 lvs # LV 상세 정보 lvdisplay # 특정 LV 상세 lvdisplay /dev/vg_data/lv_mysql # LV 이름 변경 lvrename vg_data lv_mysql lv_database # LV 삭제 (언마운트 후) umount /var/lib/mysql lvremove /dev/vg_data/lv_mysql # ========== 전체 구조 한 눈에 ========== # pvs + vgs + lvs 통합 출력 lvm fullreport
새벽 2시, 모니터링 알림이 울립니다. /var/lib/mysql 파티션 사용률이 95%를 넘었습니다.
LVM이 없었다면 (전통 파티션):
- 서비스 중단 공지 발송
- MySQL 서비스 중지
- 데이터 임시 이동
- 파티션 재편성 또는 새 디스크로 데이터 마이그레이션
- 서비스 재시작
- 총 소요 시간: 2~4시간, 서비스 다운타임 발생
LVM이 있다면 — LVM 위에서 파티션 크기를 온라인으로 변경하는 방법입니다:
bash# 1. 상황 파악 (1분) df -h /var/lib/mysql vgs vg_data # VFree 확인 # 2a. VG에 여유공간이 있는 경우 (3분) lvextend -L +20G -r /dev/vg_data/lv_mysql # 완료. 서비스 중단 없음. # 2b. VG가 꽉 찬 경우 (10분) # 새 디스크 연결 → pvcreate → vgextend → lvextend -r # 완료. 서비스 중단 없음.
총 소요 시간: 3~15분, 서비스 다운타임 0
이것이 현대 리눅스 시스템 관리자가 LVM을 기본으로 사용하는 이유입니다. 클라우드 환경(AWS EBS, GCP Persistent Disk)에서도 온라인 볼륨 확장이 가능하지만, 인스턴스 내부의 파일시스템까지 확장하려면 결국 LVM 또는 resize2fs/xfs_growfs 명령이 필요합니다.
— — —
8. 트러블슈팅
상황 — LV 확장 명령은 성공했는데 실제 사용 가능한 공간이 늘지 않았습니다:
bashlvextend -L +10G /dev/vg_data/lv_mysql # Logical volume vg_data/lv_mysql successfully resized. df -h /var/lib/mysql # Filesystem Size Used Avail Use% Mounted on # /dev/mapper/vg_data-lv_mysql 15G 14G 1.0G 94% /var/lib/mysql # ← 여전히 15G로 표시됨!
원인 — lvextend는 LV(블록 장치) 크기만 늘립니다. 파일시스템은 그 위에 별도로 올라가 있어서, 파일시스템 드라이버에게 "공간이 늘었다"고 따로 알려야 합니다. -r 옵션을 생략하면 이 단계를 잊기 쉽습니다.
진단 — 파일시스템 타입을 먼저 확인합니다:
bashdf -T /var/lib/mysql # Filesystem Type Size Used Avail Use% Mounted on # /dev/mapper/vg_data-lv_mysql ext4 15G 14G 1.0G 94% /var/lib/mysql # ↑ 타입 확인 (ext4 또는 xfs)
해결 — 파일시스템 타입에 맞는 확장 명령을 실행합니다:
bash# ext4인 경우 resize2fs /dev/vg_data/lv_mysql # resize2fs 1.46.5 (30-Dec-2021) # Filesystem at /dev/mapper/vg_data-lv_mysql is mounted on /var/lib/mysql; # on-line resizing required # The filesystem on /dev/mapper/vg_data-lv_mysql is now 6553600 (4k) blocks long. # xfs인 경우 (반드시 마운트된 상태에서, 마운트 포인트 경로 사용) xfs_growfs /var/lib/mysql # 결과 확인 df -h /var/lib/mysql # Filesystem Size Used Avail Use% Mounted on # /dev/mapper/vg_data-lv_mysql 25G 14G 11G 56% /var/lib/mysql
예방: lvextend에 항상 -r 옵션을 붙이면 파일시스템 확장까지 자동으로 처리됩니다.
bashlvextend -L +10G -r /dev/vg_data/lv_mysql
상황 — LV를 확장하려는데 VG 여유 공간이 없어 명령이 실패합니다:
bashlvextend -L +10G /dev/vg_data/lv_mysql # Insufficient free space: 2560 extents needed, but only 0 available
원인 — VG 풀 자체가 꽉 찬 상태입니다. 물리 디스크를 새로 추가해 VG를 확장해야 합니다.
진단 — VFree가 0인지 확인합니다:
bashvgs vg_data # VG #PV #LV #SN Attr VSize VFree # vg_data 2 3 0 wz--n- <39.99g 0 # ↑ VFree 0: VG가 꽉 참
해결 — 새 디스크를 연결하고 PV → VG → LV 순으로 확장합니다:
bash# 1. 새 디스크 연결 확인 lsblk # sdd 8:48 0 50G 0 disk ← 새로 추가된 디스크 # 2. 파티션 생성 (또는 디스크 전체 사용) fdisk /dev/sdd # n → p → 1 → Enter → Enter → t → 8e → w partprobe /dev/sdd # 디스크 전체를 바로 PV로 사용할 경우 파티션 불필요: # pvcreate /dev/sdd # 3. PV 생성 pvcreate /dev/sdd1 # Physical volume "/dev/sdd1" successfully created. # 4. VG에 PV 추가 vgextend vg_data /dev/sdd1 # Volume group "vg_data" successfully extended # 5. VG 여유공간 확인 vgs vg_data # VG #PV #LV #SN Attr VSize VFree # vg_data 3 3 0 wz--n- <89.98g <49.99g # ↑ VFree 50GB 생김 # 6. LV 확장 진행 lvextend -L +10G -r /dev/vg_data/lv_mysql # Logical volume vg_data/lv_mysql successfully resized.
반복 작업이라면 스크립트로 자동화할 수 있습니다:
bash#!/bin/bash # 새 디스크를 VG에 추가하는 스크립트 NEW_DISK="/dev/sdd" VG_NAME="vg_data" # 파티션 생성 parted -s $NEW_DISK mklabel gpt mkpart primary 0% 100% set 1 lvm on partprobe $NEW_DISK # PV 생성 및 VG 확장 pvcreate ${NEW_DISK}1 vgextend $VG_NAME ${NEW_DISK}1 # 결과 확인 echo "=== 확장 완료 ===" vgs $VG_NAME
상황 — 재부팅 후 시스템이 부팅되지 않고 Emergency Mode로 떨어집니다:
output[FAILED] Failed to mount /var/lib/mysql. See 'systemctl status var-lib-mysql.mount' for details. [DEPEND] Dependency failed for Local File Systems. You are in emergency mode. After logging in, type "journalctl -xb" to view system logs, "systemctl reboot" to reboot, "systemctl default" or "exit" to boot into default mode. Give root password for maintenance (or press Control-D to continue):
원인 — /etc/fstab에 등록된 UUID가 실제 파일시스템 UUID와 다릅니다. LV를 삭제 후 재생성하거나 mkfs를 다시 실행하면 UUID가 바뀌는데, fstab이 이를 반영하지 못한 경우 발생합니다.
진단 — blkid로 실제 UUID를 확인하고 fstab과 비교합니다:
bash# Emergency Mode에서 root 암호 입력 후: blkid # /dev/mapper/vg_data-lv_mysql: UUID="NEW-UUID-HERE" TYPE="ext4" cat /etc/fstab | grep mysql # UUID=OLD-UUID-HERE /var/lib/mysql ext4 defaults 0 2 # ← UUID 불일치 확인
해결 — 루트를 읽기-쓰기로 리마운트 후 fstab을 수정합니다:
bash# 루트 파일시스템을 읽기-쓰기로 리마운트 mount -o remount,rw / # fstab 수정 (실제 UUID로 교체) sed -i 's/OLD-UUID-HERE/NEW-UUID-HERE/' /etc/fstab # 마운트 테스트 mount -a # 오류 없으면 성공 reboot
예방법:
bash# fstab 수정 후 항상 검증 실행 mount -a --fake # 실제 마운트 없이 문법 검사 mount -a # 실제 마운트로 오류 확인 # UUID를 fstab에 추가할 때는 blkid 출력을 직접 활용 blkid /dev/vg_data/lv_mysql | awk '{print $2}' | tr -d '"' # UUID=a1b2c3d4-e5f6-7890-abcd-ef1234567890
긴급 복구 팁:
Emergency Mode에서 vi를 사용하기 어렵다면, 해당 라인을 주석처리하고 재부팅 후 정상 환경에서 수정하세요.
bash# fstab에서 문제 라인을 주석처리 sed -i '/lv_mysql/s/^/#/' /etc/fstab reboot # 정상 부팅 후 fstab 수정 및 수동 마운트
— — —
9. 실전 운영 패턴
대용량 데이터를 다루는 프로덕션 환경에서는 LVM과 RAID를 함께 사용합니다. RAID로 하드웨어 고장에 대비하고, LVM으로 유연한 용량 관리를 합니다.
일반적인 프로덕션 스토리지 구성 — 운영 서버에서 자주 쓰는 LVM+RAID 조합 예시입니다:
물리 계층: /dev/sdb (1TB SSD) ─┐ /dev/sdc (1TB SSD) ─┘ → RAID 1 → /dev/md0 (1TB, 미러링) /dev/sdd (4TB HDD) ─┐ /dev/sde (4TB HDD) ─┤ /dev/sdf (4TB HDD) ─┘ → RAID 5 → /dev/md1 (8TB, 패리티) LVM 계층: pvcreate /dev/md0 /dev/md1 vgcreate vg_prod /dev/md0 /dev/md1 lvcreate -L 100G -n lv_system vg_prod → ext4 → / lvcreate -L 200G -n lv_mysql vg_prod → xfs → /var/lib/mysql lvcreate -L 500G -n lv_data vg_prod → xfs → /data lvcreate -L 2T -n lv_archive vg_prod → ext4 → /archive
모니터링 스크립트 예시 — VG 사용률을 주기적으로 점검하고 임계치 초과 시 알림을 보냅니다:
bash#!/bin/bash # /usr/local/bin/check-lvm-health.sh echo "=== LVM 상태 리포트 $(date) ===" echo -e "\n[PV 상태]" pvs echo -e "\n[VG 상태]" vgs echo -e "\n[LV 상태]" lvs echo -e "\n[LV 사용률 (80% 이상 경고)]" df -h | grep "/dev/mapper/" | while read line; do usage=$(echo $line | awk '{print $5}' | tr -d '%') mountpoint=$(echo $line | awk '{print $6}') if [ "$usage" -ge 80 ]; then echo "경고: $mountpoint → ${usage}% 사용 중" fi done echo -e "\n[RAID 상태]" cat /proc/mdstat echo -e "\n[스냅샷 사용률]" lvs -o lv_name,snap_percent --select 'lv_attr=~s'
Cron으로 정기 확인 등록 — 스토리지 모니터링 스크립트를 cron에 등록해 자동화합니다:
bash# 매 시간 상태 체크, 이상 시 이메일 발송 echo "0 * * * * root /usr/local/bin/check-lvm-health.sh | mail -s 'LVM Health Report' admin@company.com" \ >> /etc/cron.d/lvm-monitor
실무에서 자주 쓰는 명령어 조합 — LVM 운영에서 반복적으로 쓰는 명령어 패턴 모음입니다:
bash# LV 전체 사용률 한 눈에 보기 df -hT | grep mapper | sort -k5 -rh # LV별 I/O 모니터링 (iostat 사용) iostat -x /dev/mapper/vg_data-lv_mysql 1 # LVM 이벤트 로그 확인 lvmconfig --type current | head -50 journalctl -u lvm2-monitor.service # PV 간 데이터 균등 배분 pvmove /dev/sdb1 # sdb1의 데이터를 VG 내 다른 PV로 분산 # VG 스캔 (새 디스크 연결 후 인식이 안 될 때) vgscan pvscan
— — —
10. 주요 명령어 빠른 참조
bash# ========== LVM 구성 전체 흐름 ========== pvcreate /dev/sdb1 # 1. PV 생성 vgcreate vg_data /dev/sdb1 # 2. VG 생성 lvcreate -L 20G -n lv_data vg_data # 3. LV 생성 mkfs.ext4 /dev/vg_data/lv_data # 4. 파일시스템 생성 mount /dev/vg_data/lv_data /mnt/data # 5. 마운트 # ========== 용량 확장 ========== lvextend -L +10G -r /dev/vg_data/lv_data # LV 확장 + 파일시스템 확장 pvcreate /dev/sdc1 && vgextend vg_data /dev/sdc1 # 새 디스크 추가 # ========== 상태 확인 ========== pvs && vgs && lvs # 전체 요약 pvdisplay && vgdisplay && lvdisplay # 전체 상세 # ========== 스냅샷 ========== lvcreate -L 5G -s -n snap /dev/vg_data/lv_data # 스냅샷 생성 lvremove /dev/vg_data/snap # 스냅샷 삭제 # ========== RAID ========== mdadm --create /dev/md0 --level=1 --raid-devices=2 /dev/sde /dev/sdf mdadm --detail /dev/md0 # RAID 상태 cat /proc/mdstat # RAID 동기화 진행 상황 mdadm /dev/md0 --fail /dev/sdf # 디스크 고장 처리 mdadm /dev/md0 --remove /dev/sdf # 고장 디스크 제거 mdadm /dev/md0 --add /dev/sdg # 새 디스크 추가 (rebuild 시작)
개발 서버에서 각 팀마다 20GB씩 할당해달라는 요청이 10개 들어왔는데 실제 물리 디스크는 100GB뿐일 때, 실제로 다들 5GB씩만 쓰고 있다면 Thin Provisioning으로 모두를 만족시킬 수 있습니다. 하지만 이 방식에는 함정이 있습니다. 실제 사용량이 물리 용량을 초과하는 순간 thin pool이 가득 차고, 이때 데이터 손실이 발생합니다. 경고 없이 가득 찬 게 아니라 — 모니터링이 없으면 경고 없이 조용히 서비스가 멈춥니다. 그래서 Thin Provisioning을 쓰는 환경에서는 Data%, Metadata% 모니터링이 선택이 아닌 필수입니다.
Thin Provisioning은 실제 사용량보다 큰 논리 볼륨을 선언할 수 있습니다. 스토리지 효율이 높아지지만 thin pool이 가득 차면 데이터 손실이 발생합니다.
Thin Pool 생성 — 오버커밋을 허용하는 Thin Provisioning LV 풀을 만듭니다:
bash# 10GB thin pool 생성 (VG에서 메타데이터 + 데이터 풀 분리) sudo lvcreate -L 10G --thinpool thin_pool vg_data # Thin Pool에서 20GB 논리 볼륨 생성 (실제 10GB 풀에서 오버프로비저닝) sudo lvcreate -V 20G --thin -n lv_app vg_data/thin_pool # Thin Pool 상태 확인 sudo lvs -o name,pool_lv,data_percent,metadata_percent vg_data # LV Pool Data% Meta% # lv_app thin_pool 45.23 12.11 # thin_pool 45.23 12.11
용량/메타데이터 고갈 모니터링 — Thin Pool이 가득 차기 전에 경보를 받아야 서비스 장애를 막을 수 있습니다:
bash# 위험 기준: Data% > 80%, Meta% > 50% sudo lvs --noheadings -o data_percent,metadata_percent vg_data/thin_pool | \ awk '{ data=$1+0; meta=$2+0 if (data > 80) print "[경고] Thin Pool 데이터 " data "% 사용" if (meta > 50) print "[경고] Thin Pool 메타데이터 " meta "% 사용" }' # Thin Pool 자동 확장 설정 (lvm.conf) # thin_pool_autoextend_threshold = 80 # 80% 도달 시 # thin_pool_autoextend_percent = 20 # 현재 크기의 20% 추가 sudo lvmconfig --type diff | grep autoextend
메타데이터 고갈 복구 절차 — Thin Pool 메타데이터가 고갈됐을 때 복구하는 절차입니다:
bash# 메타데이터 영역 긴급 확장 (데이터는 보존) sudo lvextend --poolmetadatasize +1G vg_data/thin_pool # 데이터 영역 확장 sudo lvextend -L +5G vg_data/thin_pool
디스크 하나가 이상 징후를 보여서 pvmove로 데이터를 안전하게 옮기는 중에 서버가 재부팅됐습니다. 재부팅 후 VG가 활성화되지 않아 lvs를 쳐도 아무것도 안 보입니다. 이 상황에서 어떻게 해야 할지 모르면 패닉 상태에서 잘못된 명령을 치다가 데이터를 날릴 수 있습니다. 또는 신규 배포 이후 서비스에 문제가 생겨서 스냅샷으로 롤백해야 하는데, 롤백 절차를 한 번도 연습해보지 않았다면 장애 상황에서 제대로 작동하지 않습니다. 이 ConceptBlock은 LVM 운영 중 실제로 마주치는 세 가지 장애 상황과 복구 절차를 다룹니다.
pvmove 실패 시 복구 — pvmove 중단 후 미완료 상태로 남은 LV를 복구합니다:
bash# pvmove 중 시스템 재부팅된 경우 — 재개 명령 sudo pvmove --abort # 진행 중인 pvmove 중단 sudo vgchange --sysinit vg_data # VG 상태 초기화 sudo pvmove /dev/sdb1 # 재시작 # pvmove 진행 상황 확인 sudo lvs -a -o name,copy_percent | grep pvmove
vgchange 활성화 실패 복구 — 부팅 후 VG가 활성화되지 않을 때 복구하는 방법입니다:
bash# 디바이스를 찾지 못해 VG 활성화 실패 # "Couldn't find device with uuid XXX" sudo vgscan --mknodes # 디바이스 노드 재생성 sudo vgchange -ay vg_data # 강제 활성화 # 일부 PV 실패 시 partial 모드로 활성화 (데이터 손실 위험 — 긴급용) sudo vgchange -ay --partial vg_data
LVM 스냅샷 merge/rollback — 스냅샷을 원본에 병합하거나 원본을 스냅샷 시점으로 롤백합니다:
bash# 스냅샷 생성 (변경 전) sudo lvcreate -L 2G -s -n lv_app_snap /dev/vg_data/lv_app # 롤백: 스냅샷으로 되돌리기 sudo umount /mnt/app sudo lvconvert --merge vg_data/lv_app_snap sudo lvchange -an vg_data/lv_app sudo lvchange -ay vg_data/lv_app sudo mount /dev/vg_data/lv_app /mnt/app
ext4 vs XFS 확장/축소 제약 — 두 파일시스템의 온라인 확장·축소 지원 여부를 비교합니다:
| 파일시스템 | 확장 | 축소 | 주의사항 |
|---|---|---|---|
| ext4 | 온라인 가능 (resize2fs) |
오프라인만 가능 | 마운트 해제 후 e2fsck 필수 |
| XFS | 온라인 가능 (xfs_growfs) |
불가 | XFS는 축소 자체가 지원되지 않음 |
bash# ext4 확장 (온라인) sudo lvextend -L +5G vg_data/lv_app sudo resize2fs /dev/vg_data/lv_app # XFS 확장 (온라인) sudo lvextend -L +5G vg_data/lv_app sudo xfs_growfs /mnt/app # 마운트 포인트를 인수로 전달 # lvextend -r 옵션으로 자동 파일시스템 확장 (ext4/XFS 모두 지원) sudo lvextend -r -L +5G vg_data/lv_app
XFS 볼륨 축소가 필요한 경우: 데이터 백업 → 볼륨 삭제 → 더 작은 볼륨 생성 → 데이터 복원 순서로 진행해야 합니다.
다음 모듈에서는 디스크 장애 진단 — smartctl, badblocks, fsck로 디스크 하드웨어 오류와 파일시스템 손상을 탐지하고 복구하는 방법을 다룹니다.
'Linux' 카테고리의 다른 글
| [Linux] 네트워킹 기초 (Networking Basics) (0) | 2026.05.22 |
|---|---|
| [Linux] 디스크 트러블슈팅 (0) | 2026.05.22 |
| [Linux] 디스크와 스토리지 관리 (0) | 2026.05.22 |
| [Linux] Bash 스크립팅 기초 (0) | 2026.05.22 |
| [Linux] 텍스트 처리 (grep/awk/sed) (0) | 2026.05.22 |