[Linux] LVM & 볼륨 관리

2026. 5. 22. 22:25·Linux
SMALL
시나리오

새벽 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이 온라인 볼륨 확장으로 해결하는 문제를 설명할 수 있다
2PV·VG·LV 3계층 구조를 이해하고 각 계층의 명령어를 구분할 수 있다
3pvcreate → vgcreate → lvcreate → mount → fstab 전체 흐름을 실습할 수 있다
4운영 중단 없이 lvextend와 resize2fs/xfs_growfs로 볼륨을 확장할 수 있다
5LVM 스냅샷으로 일관성 있는 백업 포인트를 생성하고 롤백할 수 있다

— — —

1. 기존 파티션의 한계와 LVM의 등장

실습 환경 준비
LVM 도구 설치
dnf install -y lvm2 || apt-get install -y lvm2
연결된 블록 디바이스 확인
lsblk -f && pvs && vgs && lvs
mdadm 설치 (RAID 실습용)
dnf install -y mdadm || apt-get install -y mdadm
개념
왜 LVM이 필요한가?
왜 LVM이 필요한가 — 전통 파티션의 한계 vs LVM의 유연성

새벽 3시, 데이터베이스 서버에서 PagerDuty 알림이 울렸습니다. /var/lib/mysql 파티션 사용률이 93%였고, df -h로 확인해보니 20GB짜리 파티션 하나가 꽉 차 있었습니다. 파티션을 늘리려면 MySQL을 멈추고 데이터를 임시 디스크에 옮겨두고, 파티션을 지웠다 새로 만든 뒤 데이터를 다시 복원해야 했습니다. 예상 다운타임은 2~3시간이었고, 새벽에 서비스 중단 공지를 써야 하는 상황이었습니다. 같은 서버가 LVM으로 구성되어 있었다면 lvextend -L +20G 한 줄로 온라인 확장이 가능했을 겁니다.

전통적인 디스크 파티션 방식은 1980년대 설계된 개념으로, 현대 클라우드 인프라의 유연한 스토리지 요구를 충족하기 어렵습니다.

전통 파티션 방식의 한계:

  1. 크기 변경 불가: 파티션을 생성하면 크기가 고정됩니다. 확장하려면 서비스를 중단해야 합니다.
  2. 단일 디스크 종속: 하나의 파티션은 하나의 물리 디스크에만 존재합니다. 디스크 용량 한계가 곧 파티션 한계입니다.
  3. 낭비 발생: /home에 공간이 남아도 /var가 꽉 차면 이동이 불가합니다.
  4. 사전 계획 필수: 초기 설치 시 정확한 용량 예측이 필요합니다. 예측이 틀리면 대공사가 필요합니다.

LVM이 제공하는 해결책:

  • 운영 중인 서비스를 중단하지 않고 볼륨 크기를 늘릴 수 있습니다.
  • 여러 물리 디스크를 하나의 스토리지 풀로 통합합니다.
  • 스냅샷 기능으로 특정 시점의 상태를 순간 포착합니다.
  • 새 디스크를 기존 볼륨 그룹에 추가해 동적으로 용량을 확장합니다.

LVM이 실제로 쓰이는 환경 — 현장에서 LVM이 필요한 실제 시나리오들입니다:

데이터베이스 서버      → 데이터 증가에 따라 주기적으로 스토리지 확장 필요
로그 서버              → 예측 불가능한 로그 증가량
개발/스테이징 환경    → 다양한 크기의 볼륨을 유연하게 할당
백업 서버              → LVM 스냅샷으로 일관성 있는 백업 생성

실무 포인트: 대부분의 Linux 배포판은 기본 설치 시 LVM을 사용합니다. df -h로 마운트 포인트를 확인하면 /dev/mapper/... 형태의 경로가 보이는데, 이것이 LVM 논리 볼륨입니다.

— — —

2. LVM 3계층 구조 이해

개념
PV → VG → LV: LVM의 3단계 추상화
LVM 3계층 추상화 — PV·VG·LV 구조와 논리 볼륨 확장

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은 물리 경계를 넘어 볼륨을 자유롭게 늘리고 줄일 수 있습니다.

LVM 3계층 구조 — PV·VG·LV 추상화와 온라인 볼륨 확장 흐름

아래는 동일한 구조를 텍스트로 표현한 것입니다:

물리 계층 (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로 사용할 수도 있습니다. 이 경우 파티션 생성 단계를 건너뛰어도 됩니다.

PV 생성: 물리 디스크를 LVM에 등록

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
VG 생성 및 LV 생성: 스토리지 풀 구성

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
파일시스템 생성, 마운트, fstab 등록

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 확장

LV 확장: lvextend + 파일시스템 확장

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에 새 디스크 추가: pvcreate + vgextend

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 스냅샷

개념
LVM 스냅샷: 백업 전 일관성 있는 순간 포착
LVM 스냅샷 CoW 메커니즘 — 쓰기 시점 복사 원리

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

개념
RAID 레벨 비교: 속도 vs 안정성 트레이드오프
RAID 레벨 비교 — RAID 0·1·5·10 속도와 안정성 트레이드오프

새 서버에 디스크를 여러 개 달아야 할 때, "그냥 전부 하나로 묶으면 안 되나요?"라는 질문이 나옵니다. 묶는 방식에 따라 디스크 하나가 죽었을 때 서버가 살아남는지 여부가 달라지고, 같은 디스크 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으로 RAID 1 구성 및 디스크 교체

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
실무 맥락
새벽 디스크 공간 위기 — LVM 온라인 확장으로 서비스 중단 없이 3~15분 내 해결

새벽 2시, 모니터링 알림이 울립니다. /var/lib/mysql 파티션 사용률이 95%를 넘었습니다.

LVM이 없었다면 (전통 파티션):

  1. 서비스 중단 공지 발송
  2. MySQL 서비스 중지
  3. 데이터 임시 이동
  4. 파티션 재편성 또는 새 디스크로 데이터 마이그레이션
  5. 서비스 재시작
  6. 총 소요 시간: 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. 트러블슈팅

트러블슈팅
lvextend 성공 메시지가 떴는데 df -h에 여전히 기존 용량이 표시됨 (파일시스템 미확장)

상황 — 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
트러블슈팅
Insufficient free space: 2560 extents needed, but only 0 available

상황 — 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
트러블슈팅
[FAILED] Failed to mount /var/lib/mysql — 재부팅 후 Emergency Mode 진입

상황 — 재부팅 후 시스템이 부팅되지 않고 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를 함께 사용합니다. 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 시작)
개념
LVM Thin Provisioning — 용량 초과 리스크와 모니터링
LVM Thin Provisioning — 초과 할당 리스크와 모니터링

개발 서버에서 각 팀마다 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
개념
LVM 장애 대응 — pvmove·vgchange 실패·스냅샷 rollback
LVM 장애 대응 의사결정 트리 — pvmove·vgreduce·스냅샷 롤백

디스크 하나가 이상 징후를 보여서 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로 디스크 하드웨어 오류와 파일시스템 손상을 탐지하고 복구하는 방법을 다룹니다.

반응형
LIST

'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
'Linux' 카테고리의 다른 글
  • [Linux] 네트워킹 기초 (Networking Basics)
  • [Linux] 디스크 트러블슈팅
  • [Linux] 디스크와 스토리지 관리
  • [Linux] Bash 스크립팅 기초
cumo
cumo
  • cumo
    이것저것
    cumo
    • 분류 전체보기 (147) N
      • 이것저것 (1)
      • 보안뉴스 (15)
      • Project (12)
      • wargame (1)
      • Cloud (25)
      • DevOps (21)
      • Linux (43)
      • 네트워크 (23)
      • AWS Developer BootCamp (1)
      • WEB&WAS (3)
  • 블로그 메뉴

    • 홈
    • 태그
    • 방명록
  • 링크

    • 도구모음 사이트
    • 참고 기술 블로그
  • 공지사항

  • 인기 글

  • 태그

    nano
    터미널멀티플렉서
    ubuntu
    Volume Group
    부팅서비스
    vi
    포트진단
    스토리지확장
    서버편집
    텍스트편집기
    인프라
    리눅스
    서버관리
    Linux
    bash입문
    데몬
    디렉토리구조
    vim
    눅스네트워킹
    논리볼륨
  • 최근 댓글

  • 최근 글

  • 반응형
  • hELLO· Designed By정상우.v4.10.3
cumo
[Linux] LVM & 볼륨 관리
상단으로

티스토리툴바