RHEL 중급 #2 LVM — PV/VG/LV, 스냅샷, 확장

11 분 소요

기초 #6의 자동 파티셔닝에서 만난 /dev/mapper/rhel-root가 LVM이었습니다. 이번 글에서는 그 LVM을 직접 다뤄봅니다. PV / VG / LV의 구조를 먼저 잡고, 새 디스크를 붙여 LV를 확장하는 흐름, 스냅샷으로 백업 직전 상태를 잡는 방법, 그리고 thin provisioning과 RAID 옵션까지 순서대로 정리합니다.

RHEL 중급 시리즈에서 이번 글의 위치:

  • #1 SELinux 입문 — Enforcing/Permissive, 라벨, 트러블슈팅
  • #2 LVM — PV/VG/LV, 스냅샷, 확장 ← 이번 글
  • #3 스토리지 심화 — Stratis, NFS, Samba
  • #4 네트워킹 — NetworkManager (nmcli), bonding, teaming
  • #5 로그 관리 — journald, rsyslog, log rotation
  • #6 작업 스케줄링 — cron, systemd timer, at
  • #7 컨테이너 입문 — Podman/Buildah/Skopeo (Docker와의 차이)

LVM이 푸는 문제 #

전통적인 파티션은 한 번 잡으면 크기가 고정됩니다. /home이 가득 찼는데 /var은 한가하다면? 디스크를 통째로 백업하고, 파티션 테이블을 새로 잡고, 복원하는 큰 작업이 필요합니다. 운영 머신은 이 동안 멈춰야 합니다.

LVM(Logical Volume Manager)은 물리 디스크와 파일시스템 사이에 한 층을 더 끼워서이 한계를 풉니다. 디스크가 가득 차면 새 디스크 한 장을 추가하고, 명령 두세 줄로 LV를 늘리고, 파일시스템을 그대로 확장합니다. 다운타임 없이.

LVM의 세 층
   /dev/sdb1   /dev/sdc1   /dev/sdd1     ← 물리 파티션
       │           │           │
       ▼           ▼           ▼
     [PV]        [PV]        [PV]        ← Physical Volume
       └───────────┼───────────┘
            ┌─────────────┐
            │     VG      │              ← Volume Group (풀)
            │  data_vg    │
            └──┬───┬───┬──┘
               │   │   │
               ▼   ▼   ▼
            [LV] [LV] [LV]               ← Logical Volume
             │    │    │
             ▼    ▼    ▼
            xfs  ext4 swap                ← 파일시스템 / swap
             │    │
             ▼    ▼
           /data /home

세 층의 역할:

약자의미
Physical VolumePV디스크 / 파티션 한 장을 LVM 용으로 표시한 것
Volume GroupVGPV 여러 장을 묶은 풀. “사용 가능한 저장소 풀”
Logical VolumeLVVG에서 잘라낸 논리 볼륨. 여기에 파일시스템을 올림

핵심은 VG가 추상화 층 이라는 것. VG의 총 용량은 PV들의 합이고, LV는 그 안에서 자유롭게 잘라 쓰며, 디스크를 추가하면 VG가 커지고, LV를 늘리면 거기서 가져옵니다.

직접 만들어보기 — PV → VG → LV #

기초 #6에서 추가한 빈 디스크 /dev/vdb를 LVM으로 다시 잡아보겠습니다.

확인
$ lsblk
NAME            MAJ:MIN RM  SIZE RO TYPE MOUNTPOINTS
vda             ...          40G  0 disk
├─vda1                       ...
└─vda2                       ...
  ├─rhel-root  ...
  └─rhel-swap  ...
vdb             252:16   0   20G  0 disk          ← 빈 디스크 한 장

1) PV 생성 — pvcreate #

물리 볼륨 만들기
$ sudo pvcreate /dev/vdb
  Physical volume "/dev/vdb" successfully created.

$ sudo pvs
  PV         VG   Fmt  Attr PSize   PFree
  /dev/vda2  rhel lvm2 a--  39.00g     0
  /dev/vdb        lvm2 ---  20.00g 20.00g

pvs는 PV 한 줄 요약, 더 자세히 보려면 pvdisplay /dev/vdb. 디스크 통째로 PV로 잡았는데, 파티션 단위로도 가능 (pvcreate /dev/vdb1).

2) VG 생성 — vgcreate #

볼륨 그룹 만들기
$ sudo vgcreate data_vg /dev/vdb
  Volume group "data_vg" successfully created

$ sudo vgs
  VG      #PV #LV #SN Attr   VSize   VFree
  data_vg   1   0   0 wz--n- <20.00g <20.00g
  rhel      1   2   0 wz--n- <39.00g      0

data_vg라는 이름의 새 VG가 만들어졌고, PV 한 장(/dev/vdb)을 품고 있습니다. 이름 규칙은 자유롭지만 용도가 드러나는 이름(data_vg, app_vg, db_vg)이 운영에 좋습니다.

3) LV 생성 — lvcreate #

VG에서 LV를 잘라냅니다. 일부만, 또는 전체를.

논리 볼륨 만들기
# 10GB짜리 LV 하나
$ sudo lvcreate -L 10G -n data_lv data_vg
  Logical volume "data_lv" created.

$ sudo lvs
  LV       VG      Attr       LSize   ...
  data_lv  data_vg -wi-a-----  10.00g
  home     rhel    -wi-ao---- <10.00g
  root     rhel    -wi-ao----  25.00g
  swap     rhel    -wi-ao----   4.00g

옵션 풀이:

  • -L 10G — 절대 크기. -l 100%FREE로 VG의 남은 공간 전부, -l 50%VG로 VG 전체의 50% 같은 비율 지정도 가능.
  • -n data_lv — LV 이름.
  • 마지막 data_vg — 어느 VG에서 잘라낼지.

4) 파일시스템 만들고 마운트 #

LV는 일반 블록 장치처럼 /dev/<VG>/<LV> 또는 /dev/mapper/<VG>-<LV>로 접근합니다.

포맷 / 마운트
$ sudo mkfs.xfs /dev/data_vg/data_lv
$ sudo mkdir -p /data
$ sudo mount /dev/data_vg/data_lv /data
$ df -hT /data
Filesystem                   Type Size Used Avail Use% Mounted on
/dev/mapper/data_vg-data_lv  xfs  10G  175M 10G   1%   /data

기초 #6의 흐름과 동일하게 /etc/fstab에 UUID로 영구 등록:

UUID 확인 후 /etc/fstab
$ sudo blkid /dev/data_vg/data_lv
/dev/mapper/data_vg-data_lv: UUID="..." TYPE="xfs"
/etc/fstab
UUID=...  /data  xfs  defaults,nofail  0  0

여기까지가 새 디스크를 LVM으로 운영하는 표준 흐름 입니다. 한 번 익혀두면 같은 패턴이 반복됩니다.

LV 확장 — 운영의 일상 #

LVM의 진짜 가치는 여기서 드러납니다. /data가 80% 찼다고 가정해봅시다.

1) 새 PV 추가 #

새 디스크 /dev/vdc(10GB)를 머신에 붙였다고 합시다.

PV 추가
$ sudo pvcreate /dev/vdc
  Physical volume "/dev/vdc" successfully created.

2) VG에 PV 합치기 — vgextend #

VG 확장
$ sudo vgextend data_vg /dev/vdc
  Volume group "data_vg" successfully extended.

$ sudo vgs data_vg
  VG      #PV #LV #SN Attr   VSize  VFree
  data_vg   2   1   0 wz--n- 29.99g 19.99g    ← 20G + 10G

VG의 총 용량이 늘었습니다. 아직 LV는 그대로(10GB)입니다.

3) LV 확장 — lvextend #

LV 확장
$ sudo lvextend -L +10G /dev/data_vg/data_lv
  Size of logical volume data_vg/data_lv changed from 10.00 GiB to 20.00 GiB.
  Logical volume data_vg/data_lv successfully resized.

-L +10G는 “현재보다 10GB 더”, -L 20G는 “최종 20GB로” 입니다. + 차이를 헷갈리지 마세요. 전체로 가려면 -l +100%FREE.

4) 파일시스템 확장 — xfs_growfs #

LV는 늘었지만, 그 위 파일시스템은 아직 옛 크기입니다. XFS 라면:

XFS 확장
$ sudo xfs_growfs /data
$ df -hT /data
Filesystem                   Type Size Used Avail Use% Mounted on
/dev/mapper/data_vg-data_lv  xfs  20G  175M 20G   1%   /data    ← 20G로 늘어남

ext4라면 sudo resize2fs /dev/data_vg/data_lv. 마운트된 상태에서 확장 가능한 것이 핵심입니다. 다운타임 없이 디스크가 늘어납니다.

lvextend의 편의 옵션-r (또는 --resizefs)를 붙이면 LV 확장 + 파일시스템 확장을 한 번에 합니다.

$ sudo lvextend -r -L +10G /dev/data_vg/data_lv

운영에서는 거의 항상 이 쪽을 씁니다. 단, fs가 인식 가능한 종류여야 합니다(XFS, ext2/3/4, reiserfs 등).

축소 — XFS는 안 되고, ext4는 됨 #

기초 #6에서 짚은 함정. XFS는 축소 불가. ext4는 축소 가능합니다.

ext4 LV를 줄이는 흐름
# 1. 마운트 해제 필수 (XFS와 다른 점 — ext4도 축소는 unmount 필요)
$ sudo umount /data

# 2. 파일시스템 검사
$ sudo e2fsck -f /dev/data_vg/data_lv

# 3. 파일시스템 축소
$ sudo resize2fs /dev/data_vg/data_lv 8G

# 4. LV 축소 (반드시 fs보다 같거나 큰 크기로)
$ sudo lvreduce -L 8G /dev/data_vg/data_lv

# 5. 다시 마운트
$ sudo mount /dev/data_vg/data_lv /data

순서가 핵심 — fs 먼저 줄이고 LV 줄이기. 거꾸로 하면 데이터가 잘려나갑니다. 위험한 작업이라 운영에서는 백업 후에만 진행하고, 가능하면 새 LV 만들고 데이터 옮기는 쪽이 안전합니다.

스냅샷 — 한 시점을 통째로 잠그기 #

LVM의 가장 강력한 기능. 어떤 LV의 한 시점을 거의 즉시 떠두고, 그 이후의 변경은 별도로 관리해서 원본을 그대로 유지합니다.

스냅샷의 모양
   원본 LV (data_lv) ─── 시간 →
        ├── 스냅샷 시점 (T0) — lvcreate -s
        T0 │ T0이후 변경된 블록은 스냅샷에 별도 보관
           │ 원본을 읽으면 현재, 스냅샷을 읽으면 T0 시점

만들기 #

스냅샷 생성
# data_lv의 T0 시점 스냅샷, 1GB의 변경량을 수용
$ sudo lvcreate -L 1G -s -n data_snap /dev/data_vg/data_lv
  Logical volume "data_snap" created.

$ sudo lvs
  LV         VG      Attr       LSize  Origin   Data%
  data_lv    data_vg owi-aos--- 20.00g
  data_snap  data_vg swi-a-s---  1.00g data_lv   0.01

-s가 스냅샷 플래그. -L 1G변경량 (COW) 영역의 크기 — 원본 전체 크기가 아니라, T0이후 원본에서 변경되는 데이터를 수용할 공간입니다. 변경이 많을 것으로 예상되면 크게 잡습니다.

백업 패턴 #

스냅샷이 가장 자주 쓰이는 경우입니다.

스냅샷 → 백업 → 삭제
# 1. 스냅샷 (즉시, 원본 멈추지 않음)
$ sudo lvcreate -L 5G -s -n data_snap /dev/data_vg/data_lv

# 2. 스냅샷을 마운트해 백업
$ sudo mkdir -p /mnt/snap
$ sudo mount -o ro,nouuid /dev/data_vg/data_snap /mnt/snap
$ sudo tar czf /backup/data-$(date +%F).tar.gz -C /mnt/snap .

# 3. 마운트 해제하고 스냅샷 제거
$ sudo umount /mnt/snap
$ sudo lvremove /dev/data_vg/data_snap

원본은 백업 동안 한 번도 멈추지 않았고, 백업은 T0 시점의 일관된 데이터입니다. 원본을 stop 하지 않고 일관된 백업 — 운영의 표준 패턴입니다.

-o nouuid — XFS는 같은 UUID의 fs를 두 번 마운트하지 못하게 막아둡니다. 스냅샷은 원본과 같은 UUID를 갖기 때문에 이 옵션이 필요합니다. ext4는 필요 없습니다.

복구 (롤백) #

T0 시점으로 원본을 되돌리고 싶다면:

스냅샷으로 롤백
# 1. 원본 LV가 마운트돼 있으면 해제
$ sudo umount /data

# 2. 스냅샷을 원본에 병합
$ sudo lvconvert --merge /dev/data_vg/data_snap
  Merging of volume data_vg/data_snap started.
  Merge of snapshot into logical volume data_vg/data_lv has finished.

# 3. 다시 마운트하면 T0 상태
$ sudo mount /dev/data_vg/data_lv /data

--merge가 스냅샷을 원본에 흡수시켜 T0으로 되돌립니다. 머지 후 스냅샷은 자동으로 사라져요.

활성 LV의 머지 — 원본이 쓰이는 중이라 unmount가 안 되면, 머지는 다음 활성화 시 (다음 부팅)에 일어납니다. 즉 재부팅하면 자동 롤백. 운영에선 이걸 의식하고 작업해야 합니다.

스냅샷의 한계 #

  • 변경량 영역이 차면 스냅샷이 무효화 됩니다. -L을 너무 작게 잡으면 백업 중에 망가질 수 있습니다. 보통 원본의 10~20% 가 안전선.
  • 장기 보관용이 아님. 스냅샷은 백업 / 임시 테스트 / 롤백에 쓰고, 영구 보관에는 별도 백업 (tar, rsync, AWS S3 등)으로 옮겨야 합니다.

한 단계 더 — Thin Provisioning #

전통적 LV는 만들 때 정한 크기를 VG에서 즉시 잘라갑니다. 100GB LV 두 개를 만들면 VG에서 200GB가 즉시 차감됩니다. 실제 사용 데이터가 적어도요.

Thin LV는 “필요할 때만 가져가는” 방식입니다. VG에 100GB만 있어도 thin pool 위에서 200GB 짜리 thin LV 두 개를 만들 수 있습니다. (실제 사용량이 풀 용량을 넘지만 않으면.)

thin pool + thin LV
# 100GB의 thin pool
$ sudo lvcreate -L 100G -T data_vg/thin_pool

# pool 위에 200GB thin LV 두 개 (overprovisioning)
$ sudo lvcreate -V 200G -T data_vg/thin_pool -n thin_a
$ sudo lvcreate -V 200G -T data_vg/thin_pool -n thin_b

$ sudo lvs
  LV          VG      Attr       LSize   Pool      Data%
  thin_pool   data_vg twi-aotz-- 100.00g
  thin_a      data_vg Vwi-a-tz-- 200.00g thin_pool   0.01
  thin_b      data_vg Vwi-a-tz-- 200.00g thin_pool   0.01

장점: 디스크 할당 유연성 + 즉시 만들기 + 효율적 스냅샷 (변경량 따로 잡지 않아도 됨). 단점: pool이 가득 차면 모든 thin LV가 멈춥니다. 모니터링 (lvs, 알람)이 필수.

운영에서는 컨테이너 워크로드 / 가상 머신 디스크처럼 동적으로 늘어나는 경우에서 자주 씁니다. 자세한 디자인은 고급 시리즈의 몫입니다.

다른 옵션 — Striping과 RAID #

Striping (RAID 0) #

여러 PV에 데이터를 나눠 써서 IO를 분산. 속도는 빠르지만 한 PV가 죽으면 전체 데이터 손실.

2-way striping
$ sudo lvcreate -L 10G -i 2 -I 64 -n stripe_lv data_vg
  # -i 2: 2 개 PV에 stripe
  # -I 64: 64KB 청크

Mirroring (RAID 1) #

두 PV에 같은 데이터를 동기 복제. 한 PV가 죽어도 데이터 안전.

2-way mirror
$ sudo lvcreate -L 10G -m 1 -n mirror_lv data_vg
  # -m 1: 1 개의 추가 복사본

RAID5 / RAID6 #

RAID5 — 패리티 분산
$ sudo lvcreate -L 10G --type raid5 -i 3 -n raid5_lv data_vg
  # -i 3: 데이터 PV 3 개 + 자동으로 패리티 1 개 = 4 개 PV 필요

운영에서는 하드웨어 RAID 또는 mdadm을 더 자주 쓰지만, LVM RAID는 디스크 두 장만으로 간편한 미러를 구성할 때 유용합니다.

모니터링 / 점검 #

용량과 상태 #

요약
$ sudo pvs           # PV 목록
$ sudo vgs           # VG 목록
$ sudo lvs           # LV 목록
$ sudo lvs -a        # 숨겨진 내부 LV 까지 (스냅샷, thin metadata 등)
상세
$ sudo pvdisplay /dev/vdb
$ sudo vgdisplay data_vg
$ sudo lvdisplay /dev/data_vg/data_lv

자동 활성화 #

VG/LV 활성화
$ sudo vgchange -a y data_vg     # 활성화
$ sudo vgchange -a n data_vg     # 비활성화 (마운트 해제 후)

부팅 시 자동 활성화는 /etc/lvm/lvm.confauto_activation_volume_list에서 제어합니다. 일반 운영은 기본값으로 충분합니다.

자주 만나는 함정 #

“lvextend 했는데 df가 그대로입니다” #

LV만 늘리고 파일시스템 확장(xfs_growfs / resize2fs)을 안 한 경우입니다. -r 옵션으로 한 번에 가는 게 안전합니다.

“스냅샷이 갑자기 사라졌습니다” #

스냅샷의 변경량 영역이 가득 차면 자동으로 invalidate됩니다. lvsData%가 100%에 가까워지면 즉시 더 큰 스냅샷으로 옮기거나, 백업을 끝내고 제거해야 합니다.

“VG에 PV가 안 들어갑니다 — ‘already in volume group’” #

이미 다른 VG에 속해 있는 PV. pvs로 어디 속해 있는지 확인. 진짜 안 쓰는 거면 그 VG에서 vgreduce로 빼고 가져옵니다.

“디스크 통째로 PV로 만들었는데 부팅 후 인식 안 됩니다” #

/etc/fstab에 디바이스 이름(/dev/vdb)으로 등록한 경우입니다. 디바이스 이름은 부팅마다 바뀔 수 있습니다. VG/LV 이름(/dev/data_vg/data_lv) 또는 UUID로 등록하면 문제없습니다.

“thin pool이 가득 찼습니다 — 모든 thin LV가 read-only” #

thin provisioning의 가장 큰 위험입니다. pool 80% 알람을 반드시 모니터링하고, 가득 차기 전에 lvextend로 pool 자체를 늘려야 합니다.

AlmaLinux / Rocky 차이 #

이번 글의 모든 명령이 그대로 동작합니다. LVM은 커널과 lvm2 패키지 위에서 동작하고, 그 패키지는 RHEL의 것을 그대로 가져옵니다.

자주 쓰는 명령 한 표 #

명령하는 일
pvcreate <device>PV 생성
pvs / pvdisplay <pv>PV 목록 / 상세
vgcreate <vg> <pv>...VG 생성
vgs / vgdisplay <vg>VG 목록 / 상세
vgextend <vg> <pv>VG에 PV 추가
vgreduce <vg> <pv>VG에서 PV 제거
lvcreate -L <size> -n <lv> <vg>LV 생성 (절대 크기)
lvcreate -l 100%FREE -n <lv> <vg>VG의 남은 공간 전부로 LV 생성
lvcreate -L <size> -s -n <snap> <orig>스냅샷 생성
lvcreate -L <size> -T <vg>/<pool>thin pool 생성
lvcreate -V <size> -T <vg>/<pool> -n <lv>thin LV 생성
lvextend [-r] -L +<size> <lv>LV 확장 (+ fs도 함께)
lvreduce -L <size> <lv>LV 축소 (위험)
lvconvert --merge <snap>스냅샷을 원본에 머지 (롤백)
lvremove <lv>LV 삭제
vgchange -a y/n <vg>VG 활성화 / 비활성화
xfs_growfs <mount>XFS 확장
resize2fs <device>ext4 확장 / 축소
lvs / lvs -aLV 목록 (+ 내부 LV까지)

정리 #

이번 글에서 정리한 흐름:

  • LVM은 **PV (물리) → VG (풀) → LV (논리)**의 세 층으로 디스크 운영을 추상화합니다.
  • 새 디스크 추가의 표준: pvcreatevgcreate / vgextendlvcreatemkfs.xfs → mount.
  • LV 확장은 lvextend -r -L +<size> 한 줄로 (LV + fs 동시), 마운트된 상태로 안전.
  • XFS 축소 불가, ext4는 축소 가능하지만 위험. 가능하면 새 LV로 옮기기.
  • 스냅샷lvcreate -s로 즉시 생성, 백업 / 롤백의 표준 패턴. 변경량 영역 크기에 주의.
  • Thin provisioning으로 over-provisioning이 가능하지만, pool 가득 참 모니터링이 필수입니다.
  • Striping / Mirroring / RAID는 LVM 자체가 지원하지만 운영에선 hw RAID / mdadm이 더 일반적.

다음 — 스토리지 심화 #

LVM은 여전히 RHEL의 표준이지만, RHEL 8부터는 그다음 세대로 Stratis도 들어왔습니다. 단일 머신을 넘어 네트워크로 디스크를 공유하는 NFS / Samba 역시 운영에서 자주 만나는 영역입니다.

#3 스토리지 심화 — Stratis, NFS, Samba에서는 Stratis가 LVM + XFS 위에서 어떤 관리 모델을 제공하는지, NFS 서버 / 클라이언트의 설정 흐름, 그리고 Windows 호환 Samba까지 살펴봅니다. 로컬 디스크를 넘어 네트워크 스토리지를 다루는 방법을 정리하는 글입니다.

X