Linux를 정리하자 - 5일차 Disk and Filesystem2

0

linux

목록 보기
5/6

Filesystem

The Logical Volume Manager(LVM)

이전 포스트에서는 디스크를 직접 관리하고 사용하는 방법을 살펴보았다. 즉, 파티션을 통해 저장 장치의 특정 위치를 지정하여 데이터를 저장하는 방식이다. 예를 들어, /dev/sda1과 같은 블록 장치에 접근하면, 이는 /dev/sda의 파티션 테이블에 따라 특정 장치의 특정 위치를 가리키게 된다. 다만, 정확한 위치는 하드웨어에 의해 결정될 수 있다.

이 방식은 일반적으로 잘 동작하지만, 설치 이후 디스크를 변경할 때 몇 가지 단점이 있다. 예를 들어, 디스크를 업그레이드하려면 새 디스크를 설치하고, 파티션을 나누고, 파일 시스템을 생성한 후, 부트 로더 설정을 변경하는 등의 작업을 수행해야 하며, 마지막으로 새로운 디스크로 전환해야 한다. 이 과정은 오류가 발생하기 쉽고, 여러 번의 재부팅이 필요할 수도 있다.

더 심각한 문제는 추가적인 저장 공간이 필요할 때 발생한다. 새로운 디스크를 설치하려면 해당 디스크에 대한 마운트 지점을 선택해야 하며, 기존 디스크와 새로운 디스크 간에 데이터를 수동으로 적절히 분배해야 한다.

이러한 문제를 해결하기 위해 LVM(Logical Volume Manager)은 물리적 블록 장치와 파일 시스템 사이에 또 다른 계층을 추가한다. LVM의 개념은 여러 개의 물리적 볼륨(PV, Physical Volume)을 선택하여 하나의 볼륨 그룹(VG, Volume Group)으로 묶는 것이다. 볼륨 그룹은 일종의 데이터 풀처럼 작동하며, 여기에서 논리적 볼륨(LV, Logical Volume)을 할당한다.

위 그림은 하나의 볼륨 그룹 내에서 물리적 볼륨과 논리적 볼륨이 어떻게 구성되는지를 개략적으로 보여준다. 이 그림에는 여러 개의 물리적 및 논리적 볼륨이 있지만, 대부분의 LVM 기반 시스템에서는 하나의 물리적 볼륨(PV)과 두 개의 논리적 볼륨(루트 및 스왑)만 사용하는 경우가 많다.

논리적 볼륨(Logical Volume, LV)은 결국 블록 장치이며, 일반적으로 파일 시스템이나 스왑 서명이 포함된다. 따라서 볼륨 그룹(VG, Volume Group)과 논리적 볼륨 간의 관계는 디스크와 파티션의 관계와 유사하다고 볼 수 있다. 하지만 중요한 차이점은 논리적 볼륨이 볼륨 그룹 내에서 어떻게 배치될지를 사용자가 직접 정의하지 않는다는 것이다. LVM이 이러한 과정을 자동으로 처리한다.

LVM을 사용하면 강력하고 유용한 작업을 수행할 수 있다. 예를 들면 다음과 같다.

  • 볼륨 그룹에 새로운 물리적 볼륨(PV, Physical Volume, 예: 추가 디스크)을 추가하여 크기를 확장할 수 있다.
  • 기존 논리적 볼륨을 수용할 수 있는 충분한 공간이 남아 있다면, 볼륨 그룹에서 PV를 제거할 수도 있다.
  • 논리적 볼륨의 크기를 조정할 수 있으며, 이와 함께 fsadm 유틸리티를 사용하여 파일 시스템 크기도 조정할 수 있다.

이 모든 작업을 시스템을 재부팅하지 않고 수행할 수 있으며, 대부분의 경우 파일 시스템을 마운트 해제할 필요도 없다. 물론 새로운 물리적 디스크 하드웨어를 추가하려면 시스템을 종료해야 할 수도 있지만, 클라우드 환경에서는 새로운 블록 스토리지 장치를 실시간으로 추가할 수 있는 경우가 많다. 따라서 LVM은 이러한 유연성이 필요한 시스템에서 매우 유용한 선택이 된다.

우리는 LVM에 대해 적절한 수준으로 자세히 살펴볼 것이다. 먼저 논리적 볼륨과 그 구성 요소를 다루는 방법을 익힌 후, LVM이 실제로 어떻게 동작하는지와 이를 기반으로 하는 커널 드라이버에 대해 좀 더 깊이 알아보도록 하자.

Working with LVM

LVM은 볼륨 및 볼륨 그룹을 관리하기 위한 여러 개의 사용자 공간 도구를 제공한다. 대부분의 명령은 lvm 명령을 기반으로 하며, 이는 인터랙티브한 범용 도구이다. 또한, 특정 작업을 수행하는 개별 명령도 존재하는데, 이들은 실제로 lvm에 대한 심볼릭 링크일 뿐이다. 예를 들어, vgs 명령은 lvm> 프롬프트에서 vgs를 입력하는 것과 동일한 효과를 가지며, /sbin/vgs는 실제로 lvm에 대한 심볼릭 링크로 존재한다.

앞서 언급한 vgs 명령은 현재 시스템에서 설정된 볼륨 그룹을 보여준다. 출력은 비교적 간결하며, 다음과 같은 예제 LVM 설치에서 얻을 수 있는 결과를 살펴보자.

vgs
  VG        #PV #LV #SN Attr   VSize   VFree 
  ubuntu-vg   1   2   0 wz--n- <10.00g 36.00m

첫 번째 줄은 헤더이며, 각 후속 줄은 하나의 볼륨 그룹을 나타낸다. 각 열은 다음을 의미한다.

  1. VG: 볼륨 그룹의 이름. ubuntu-vg는 Ubuntu 설치 시 LVM을 구성하면 기본적으로 할당되는 이름이다.
  2. #PV: 해당 볼륨 그룹을 구성하는 물리적 볼륨(PV)의 개수.
  3. #LV: 해당 볼륨 그룹 내의 논리적 볼륨(LV) 개수.
  4. #SN: 논리적 볼륨 스냅샷 개수(여기서는 자세히 다루지 않는다).
  5. Attr: 볼륨 그룹의 상태 속성을 나타낸다. 예제에서는 w(쓰기 가능), z(크기 조정 가능), n(일반 할당 정책)이 활성화되어 있다.
  6. VSize: 볼륨 그룹의 총 크기.
  7. VFree: 볼륨 그룹에서 아직 할당되지 않은 공간의 크기.

이러한 출력은 대부분의 경우 볼륨 그룹을 개략적으로 이해하는 데 충분하다. 그러나 더 자세한 정보를 얻으려면 vgdisplay 명령을 사용할 수 있다. 다음은 같은 볼륨 그룹을 vgdisplay로 조회한 결과이다.

# vgdisplay
  --- Volume group ---
  VG Name               ubuntu-vg
  System ID             
  Format                lvm2
  Metadata Areas        1
  Metadata Sequence No  3
  VG Access             read/write
  VG Status             resizable
  MAX LV                0
  Cur LV                2
  Open LV               2
  Max PV                0
  Cur PV                1
  Act PV                1
  VG Size               <10.00 GiB
  PE Size               4.00 MiB
  Total PE              2559
  Alloc PE / Size       2550 / 9.96 GiB
  Free  PE / Size       9 / 36.00 MiB
  VG UUID               0zs0TV-wnT5-laOy-vJ0h-rUae-YPdv-pPwaAs

앞에서 본 정보와 중복되는 부분도 있지만, 몇 가지 새로운 항목을 확인할 수 있다.

  • Open LV: 현재 사용 중인 논리적 볼륨의 개수.
  • Cur PV: 해당 볼륨 그룹을 구성하는 물리적 볼륨의 개수.
  • Act PV: 현재 활성화된 물리적 볼륨의 개수.
  • VG UUID: 볼륨 그룹의 고유 식별자(UUID). 하나의 시스템에서 동일한 이름을 가진 여러 볼륨 그룹이 존재할 수 있으므로, UUID를 사용하면 특정 볼륨 그룹을 정확히 식별할 수 있다. 대부분의 LVM 명령(예: vgrename)은 UUID를 볼륨 그룹 이름 대신 사용할 수 있도록 지원한다.

출력에서 PE Size(Physical Extent Size)라는 항목이 있는데, 이는 Physical Extent(PE)의 크기를 나타낸다. PE는 물리적 볼륨의 조각과 같은 개념이며, 블록보다 훨씬 큰 단위이다. 위의 예제에서는 PE 크기가 4MB로 설정되어 있다.

Total PEAlloc PE / Size 항목을 보면 대부분의 PE가 사용 중인 것을 확인할 수 있다. 하지만 이것이 파일 시스템 내에서 실제로 사용된 공간을 의미하는 것은 아니다. 이는 단순히 논리적 볼륨(파일 시스템 및 스왑 공간 포함)에 할당된 공간을 나타낼 뿐이다.

LVM을 사용하면 논리적 볼륨을 유연하게 확장하거나 축소할 수 있으며, 시스템을 재부팅하지 않고도 이러한 작업을 수행할 수 있다는 장점이 있다. 아래는 논리적 볼륨과 관련된 다양한 조작 방법을 살펴보도록 하자.

볼륨 그룹을 나열하는 것과 마찬가지로, 논리적 볼륨을 나열하는 명령어로는 lvslvdisplay가 있다. lvs는 간단한 요약 정보를 제공하고, lvdisplay는 보다 자세한 정보를 출력한다.

다음은 lvs 명령을 실행한 예제이다.

lvs
  LV     VG        Attr       LSize   Pool Origin Data%  Meta%  Move Log Cpy%Sync Convert
  root   ubuntu-vg -wi-ao----  <9.01g
  swap_1 ubuntu-vg -wi-ao---- 976.00m

기본적인 LVM 구성에서는 위 출력의 처음 네 개 열만 이해하면 충분하며, 나머지 열은 대부분 비어 있다. 주요 열의 의미는 다음과 같다.

  • LV: 논리적 볼륨의 이름.
  • VG: 해당 논리적 볼륨이 속한 볼륨 그룹의 이름.
  • Attr: 논리적 볼륨의 속성.
  • w(쓰기 가능)
    • i(할당 정책 상속)
    • a(활성화됨)
    • o(열림 상태)
  • LSize: 논리적 볼륨의 크기.

더 자세한 정보를 확인하려면 lvdisplay 명령을 사용하면 된다. 예를 들어, root 논리적 볼륨의 정보를 조회하면 다음과 같이 출력될 수 있다.

lvdisplay /dev/ubuntu-vg/root
  --- Logical volume ---
  LV Path                /dev/ubuntu-vg/root
  LV Name                root
  VG Name                ubuntu-vg
  LV UUID                CELZaz-PWr3-tr3z-dA3P-syC7-KWsT-4YiUW2
  LV Write Access        read/write
  LV Creation host, time ubuntu, 2018-11-13 15:48:20 -0500
  LV Status              available
  # open                 1
  LV Size                <9.01 GiB
  Current LE             2306
  Segments               1
  Allocation             inherit
  Read ahead sectors     auto
  - currently set to     256
  Block device           253:0

이 출력에는 몇 가지 흥미로운 정보가 포함되어 있다. 몇 가지 주요 항목을 살펴보면 다음과 같다.

  • LV Path: 논리적 볼륨의 실제 장치 경로(/dev/ubuntu-vg/root).
  • LV Name: 논리적 볼륨 이름(root).
  • VG Name: 논리적 볼륨이 속한 볼륨 그룹(ubuntu-vg).
  • LV UUID: 논리적 볼륨의 고유 식별자(UUID). (이 값은 볼륨 그룹의 UUID와 다름).
  • LV Write Access: 읽기/쓰기 가능 여부(read/write).
  • LV Status: 논리적 볼륨의 현재 상태(available).
  • # open: 현재 사용 중인 개수(1).
  • LV Size: 논리적 볼륨의 크기(<9.01 GiB).
  • Block device: 커널에서 인식하는 주요(Major) 및 부차(Minor) 장치 번호(253:0).

논리적 볼륨의 LV Path/dev/ubuntu-vg/root로 표시되지만, 이는 실제로 심볼릭 링크일 가능성이 크다. 이를 확인하려면 ls -l 명령을 사용하면 된다.

ls -l /dev/ubuntu-vg/root
lrwxrwxrwx 1 root root 7 Nov 14 06:58 /dev/ubuntu-vg/root -> ../dm-0

위에서 볼 수 있듯이, /dev/ubuntu-vg/root/dev/dm-0을 가리키는 심볼릭 링크이다.

아래는 이 /dev/dm-0 장치가 어떻게 생성되는지에 대해 살펴보겠다.

Using Logical Volume Devices

LVM이 시스템에서 설정을 완료하면 논리적 볼륨의 블록 장치가 /dev/dm-0, /dev/dm-1 등의 이름으로 생성된다. 하지만 이러한 장치 이름은 예측할 수 없기 때문에, LVM은 보다 안정적인 이름을 제공하는 심볼릭 링크를 생성한다. 앞서 /dev/ubuntu-vg/root가 이에 해당하는 예시였다.

또한, 대부분의 시스템에서는 /dev/mapper 디렉터리에도 심볼릭 링크가 생성된다. 이 링크의 이름은 볼륨 그룹과 논리적 볼륨의 조합을 사용하지만 계층 구조 없이 단일 파일 이름 형식으로 구성된다. 예를 들어, ubuntu-vg 볼륨 그룹의 root 논리적 볼륨은 /dev/mapper/ubuntu--vg-root로 표시된다. 여기서 udev는 볼륨 그룹 이름의 단일 대시(-)를 이중 대시(--)로 변환하고, 볼륨 그룹과 논리적 볼륨을 단일 대시(-)로 구분한다.

많은 시스템에서 /dev/mapper 내의 링크는 다음과 같은 주요 설정 파일에서 사용된다.

  • /etc/fstab (파일 시스템 마운트 설정)
  • systemd (서비스 및 부팅 관리)
  • 부트로더 설정 (예: GRUB)

이러한 링크는 기본적으로 논리적 볼륨을 가리키는 블록 장치이며, 일반적인 블록 장치처럼 사용할 수 있다. 즉, 파일 시스템을 생성하거나, 스왑 공간을 만들거나, 마운트할 수 있다.

LVM에서 마지막으로 살펴볼 주요 구성 요소는 물리적 볼륨(PV)이다. 볼륨 그룹(VG)은 하나 이상의 물리적 볼륨으로 구성된다. PV는 단순한 블록 장치처럼 보일 수 있지만, 내부적으로 다양한 정보를 포함하고 있다.

LVM에서는 PV를 조회하는 명령어로 pvs(간략한 출력)와 pvdisplay(상세 출력)를 제공한다.

  • pvs 명령어를 사용한 물리적 볼륨 조회
pvs
  PV         VG        Fmt  Attr PSize   PFree 
  /dev/sda1  ubuntu-vg lvm2 a--  <10.00g 36.00m
  • PV: 물리적 볼륨의 장치 경로 (/dev/sda1).
  • VG: 해당 물리적 볼륨이 속한 볼륨 그룹 (ubuntu-vg).
  • Fmt: LVM 메타데이터 형식 (lvm2).
  • Attr: 속성 (예: a--는 할당 가능함을 의미).
  • PSize: 물리적 볼륨의 전체 크기 (<10.00g).
  • PFree: 사용되지 않은 공간 (36.00m).
  • pvdisplay 명령어를 사용한 상세 정보 조회
pvdisplay
  --- Physical volume ---
  PV Name               /dev/sda1
  VG Name               ubuntu-vg
  PV Size               <10.00 GiB / not usable 2.00 MiB
  Allocatable           yes 
  PE Size               4.00 MiB
  Total PE              2559
  Free PE               9
  Allocated PE          2550
  PV UUID               v2Qb1A-XC2e-2G4l-NdgJ-lnan-rjm5-47eMe5
  1. PV Name: 물리적 볼륨의 장치 경로 (/dev/sda1). 참고로 PV는 개별적으로 이름을 가지지 않는다.
  2. VG Name: 속한 볼륨 그룹 (ubuntu-vg).
  3. PV Size: 전체 크기 (<10.00 GiB), 일부 공간은 PE 단위에 맞지 않아 사용 불가 (not usable 2.00 MiB).
  4. Allocatable: 논리적 볼륨을 위해 공간을 할당할 수 있는지 여부 (yes).
  5. PE Size: 물리적 익스텐트(PE) 크기 (4.00 MiB).
  6. Total PE: 해당 물리적 볼륨이 포함하는 총 PE 개수 (2559).
  7. Free PE: 할당되지 않은 PE 개수 (9).
  8. Allocated PE: 이미 할당된 PE 개수 (2550).
  9. PV UUID: 물리적 볼륨의 고유 식별자(UUID).

PV는 개별적으로 이름을 가지지 않으므로, UUID를 통해 특정 PV를 식별할 수 있다.

PE는 LVM에서 물리적 볼륨을 작은 조각으로 나눈 기본 단위이다. 모든 PE 크기는 동일하며 (PE Size = 4.00 MiB), 논리적 볼륨을 할당할 때 사용된다.

예제에서는 9개의 PE가 비어 있어 (Free PE = 9), 추가로 36MB만 할당 가능하다.
새로운 논리적 볼륨을 만들거나 기존 볼륨을 확장하려면 더 많은 물리적 볼륨을 추가해야 한다.

이제 PV, VG, LV의 개념을 모두 익혔다. 다음으로는 PV 메타데이터와 직접적인 LVM 작업(볼륨 추가, 확장, 축소 등)을 알아보도록 하자.

Constructing a Logical Volume System

새로운 볼륨 그룹을 생성하고 두 개의 디스크 장치를 사용하여 몇 개의 논리적 볼륨을 만드는 방법을 살펴보자. 여기서는 5GB와 15GB 크기의 두 개의 디스크 장치를 결합하여 하나의 볼륨 그룹을 만든 후, 이 공간을 각각 10GB 크기의 두 개의 논리적 볼륨으로 나눌 것이다. LVM이 없다면 거의 불가능한 작업이다.

이 예제에서는 VirtualBox 디스크를 사용한다. 현대적인 시스템에서는 이러한 용량이 매우 작지만, 개념을 설명하는 데에는 충분하다.

아래는 볼륨 개념도를 보여준다. 새 디스크 장치는 /dev/sdb/dev/sdc에 위치하며, 새로운 볼륨 그룹의 이름은 myvg이고, 두 개의 새로운 논리적 볼륨의 이름은 mylv1mylv2이다.

제일 먼저 disk에 partition을 나누어 LVM 용도로 레이블을 지정하도록 하자.

parted /dev/sdb print
Model: ATA VBOX HARDDISK (scsi)
Disk /dev/sdb: 5616MB
Sector size (logical/physical): 512B/512B
Partition Table: msdos
Disk Flags: 

Number  Start   End     Size    Type     File system  Flags
 1      1049kB  5616MB  5615MB  primary               lvm

parted /dev/sdc print
Model: ATA VBOX HARDDISK (scsi)
Disk /dev/sdc: 16.0GB
Sector size (logical/physical): 512B/512B
Partition Table: msdos
Disk Flags: 

Number  Start   End     Size    Type     File system  Flags
 1      1049kB  16.0GB  16.0GB  primary               lvm

반드시 디스크를 파티션할 필요는 없다. LVM에서 물리적 볼륨(PV) 은 어떤 블록 디바이스도 될 수 있으며, /dev/sdb와 같은 전체 디스크도 사용할 수 있다.

그러나 디스크를 파티션하면 부팅이 가능해지며, 또한 해당 블록 디바이스가 LVM 물리적 볼륨으로 사용됨을 명확하게 식별하는 역할을 한다.

이제 /dev/sdb1/dev/sdc1 파티션이 준비되었으므로, 첫 번째 단계는 하나의 파티션을 물리적 볼륨(PV)으로 지정하고 이를 새로운 볼륨 그룹(VG)에 할당하는 것이다.

이 작업은 vgcreate 명령어 하나로 수행할 수 있다. 다음 명령어를 실행하면 /dev/sdb1을 초기 PV로 하는 myvg 볼륨 그룹이 생성된다:

vgcreate myvg /dev/sdb1
  Physical volume "/dev/sdb1" successfully created.
  Volume group "myvg" successfully created

대부분의 시스템에서는 새로운 볼륨 그룹을 자동으로 감지한다. 이를 확인하려면 vgs 명령어를 실행한다.

vgs
  VG   #PV #LV #SN Attr   VSize  VFree 
  myvg   1   0   0 wz--n- <5.23g <5.23g

이제 /dev/sdc1을 기존 볼륨 그룹 myvg에 추가하려면, vgextend 명령어를 사용한다.

vgextend myvg /dev/sdc1
  Physical volume "/dev/sdc1" successfully created.
  Volume group "myvg" successfully extended

이제 다시 vgs 명령어를 실행하면, 두 개의 PV가 추가되었으며 총 크기가 두 파티션을 합한 크기가 된 것을 확인할 수 있다.

vgs
  VG    #PV #LV #SN Attr   VSize   VFree  
  myvg    2   0   0 wz--n- <20.16g <20.16g

이제 논리 볼륨(LV)을 생성하자. 우리는 각각 10GB 크기의 두 논리 볼륨을 생성할 예정이다.

lvcreate 명령어는 볼륨 그룹에서 새 논리 볼륨을 할당하는 역할을 한다. 여러 개의 논리 볼륨을 생성할 때 가장 중요한 것은 크기를 정하는 것과 논리 볼륨의 유형을 지정하는 것이다. PV는 extents로 나뉘어 있으므로 원하는 크기와 정확히 일치하지 않을 수 있다. 그러나 대부분은 충분히 근사치이므로 PE(물리적 확장)에 대해서 깊게 신경 쓸 필요는 없다.

lvcreate--size 옵션을 사용하여 논리적 볼륨의 사이즈를 결정하고 --extents 옵션을 사용하여 PE의 갯수를 지정할 수 있다.

lvcreate --size 10g --type linear -n mylv1 myvg
  Logical volume "mylv1" created.
lvcreate --size 10g --type linear -n mylv2 myvg
  Logical volume "mylv2" created.

lvs 명령어를 사용하여 논리 볼륨이 제댈 생생되었는 지 확인하고, vgdisplay를 통해 volume group의 상태를 자세히 살펴볼 수 있다.

vgdisplay myvg
  --- Volume group ---
  VG Name               myvg
  System ID             
  Format                lvm2
  Metadata Areas        2
  Metadata Sequence No  4
  VG Access             read/write
  VG Status             resizable
  MAX LV                0
  Cur LV                2
  Open LV               0
  Max PV                0
  Cur PV                2
  Act PV                2
  VG Size               20.16 GiB
  PE Size               4.00 MiB
  Total PE              5162
  Alloc PE / Size       5120 / 20.00 GiB
  Free  PE / Size       42 / 168.00 MiB
  VG UUID               1pHrOe-e5zy-TUtK-5gnN-SpDY-shM8-Cbokf3

free PE가 42개 있는 것을 볼 수 있다. 이는 우리가 선택한 논리 볼륨의 크기가 볼륨 그룹의 모든 PE 갯수를 다 채우지 못했기 때문이다.

Manipulating Logical Volumes: Creating Partitions

새로 생성된 논리 볼륨을 사용할 준비가 되었다. 이제 이를 일반적인 디스크 파티션처럼 파일 시스템을 만들고 마운트하여 사용할 수 있다. 앞서 언급했듯이, /dev/mapper에 디바이스에 대한 심볼릭 링크가 생성되며, 이 경우에는 볼륨 그룹에 대해 /dev/myvg 디렉토리가 생성된다. 예를 들어, 아래 세 가지 명령어를 실행하여 파일 시스템을 생성하고 임시로 마운트한 후, 논리 볼륨의 실제 공간을 확인할 수 있다.

  1. 파일 시스템 생성: 먼저 mkfs 명령어를 사용하여 논리 볼륨에 ext4 파일 시스템을 생성한다. 예를 들어, /dev/mapper/myvg-mylv1에 파일 시스템을 생성하려면 다음 명령을 실행한다.
mkfs -t ext4 /dev/mapper/myvg-mylv1
mke2fs 1.44.1 (24-Mar-2018)
Creating filesystem with 2621440 4k blocks and 655360 inodes
Filesystem UUID: 83cc4119-625c-49d1-88c4-e2359a15a887
Superblock backups stored on blocks: 
        32768, 98304, 163840, 229376, 294912, 819200, 884736, 1605632
Allocating group tables: done
Writing inode tables: done
Creating journal (16384 blocks): done
Writing superblocks and filesystem accounting information: done
  1. mount: 파일 시스템을 생성한 후, mount 명령어를 사용하여 해당 논리 볼륨을 /mnt에 마운트한다.
mount /dev/mapper/myvg-mylv1 /mnt
  1. 디스크 용량 확인: df 명령어를 사용하여 마운트된 파일 시스템의 사용 가능한 공간을 확인할 수 있다.
df /mnt
Filesystem             1K-blocks  Used Available Use% Mounted on
/dev/mapper/myvg-mylv1  10255636 36888   9678076   1% /mnt

이 명령어들이 실행되면, mylv1 논리 볼륨에 ext4 파일 시스템이 생성되고, 이를 /mnt에 마운트하여 사용 가능한 공간을 확인할 수 있다.

Removing Logical Volumes

아래의 그림과 같이 두번째 논리 볼륨을 삭제하여 첫번째 논리 볼륨의 크기를 조정하도록 하자.

먼저, 삭제할 논리 볼륨에 중요한 데이터가 없는지 확인하고, 이 볼륨이 현재 시스템에서 사용 중이지 않도록 (즉, unmount되었음을 확인한 후) lvremove 명령어로 삭제한다. 논리 볼륨을 삭제할 때는 볼륨 그룹과 논리 볼륨 이름을 슬래시(/)로 구분하는 다른 문법을 사용해야 한다 (예: myvg/mylv2):

lvremove myvg/mylv2
Do you really want to remove and DISCARD active logical volume myvg/mylv2? [y/n]: y
  Logical volume "mylv2" successfully removed

y를 누르면 삭제가 진행된다.

Resizing Logical Volumes and Filesystems

이제 첫 번째 논리 볼륨인 mylv1을 크기 변경할 수 있다. 이 작업은 볼륨이 사용 중이고 파일 시스템이 마운트된 상태에서도 할 수 있다. 그러나 중요한 점은 두 가지 단계가 있다는 것이다. 더 큰 논리 볼륨을 사용하려면, 논리 볼륨과 그 안의 파일 시스템을 모두 크기 변경해야 한다 (마운트된 상태에서도 가능). 하지만 이 작업은 매우 일반적인 작업이므로, lvresize 명령어에는 파일 시스템 크기 변경을 자동으로 수행하는 -r 옵션이 있다.

이 작동 방식을 보여주기 위해, 두 개의 별도 명령가 있는데, 하나는 free PE를 늘려주는 방식이고, 하나는 lvresize를 사용하는 방법이다. lvresize가 훨씬 간단하므로 lvresize만 알아보도록 하자.

lvresize -r -l +100%FREE myvg/mylv1

이처럼 ext2,ext3,ext4 파일 시스템을 마운트한 상태에서 확장하는 것은 매우 편리하다. 그러나 반대 방향으로는 작동하지 않는다. 즉, 마운트된 파일 시스템을 축소할 수 없다. 파일 시스템을 축소하려면 먼저 파일 시스템을 마운트 해제하고, 논리 볼륨 축소 과정은 반대로 해야 한다. 다시 말해, 수동으로 크기 변경할 때는 논리 볼륨을 축소한 후 파일 시스템을 축소해야 하며, 새로운 논리 볼륨이 파일 시스템을 수용할 수 있도록 해야 한다. 하지만 lvresize-r 옵션을 사용하면 파일 시스템과 논리 볼륨 크기를 자동으로 조정할 수 있어 훨씬 더 편리하다.

Filesystem internal

전통적인 유닉스 파일 시스템에는 두 가지 주요 구성 요소가 있다.

  1. a pool of data blocks: 데이터를 저장할 수 있는 데이터 블록 풀
  2. database system: 데이터 풀을 관리하는 데이터베이스 시스템

데이터베이스는 inode 데이터 구조를 중심으로 구성된다. inode는 특정 파일을 설명하는 데이터 집합으로 파일의 종류, 권한, 그리고 가장 중요한 것은 파일 데이터가 데이터 풀 내에서 어디에 위치하는지에 대한 정보를 포함한다. Inodeinode table에 나열된 번호로 식별된다.

파일 이름과 디렉터리도 inode로 구현된다. 디렉터리 inode는 파일 이름 list와 다른 inode에 해당하는 링크를 포함한다.

실제 예를 들기 위해 새 파일 시스템을 생성하고 마운트한 후 마운트 지점으로 디렉터리를 변경하고, 다음 명령어로 몇 개의 파일과 디렉터리를 추가했다.

mkdir dir_1
mkdir dir_2
echo a > dir_1/file_1
echo b > dir_1/file_2
echo c > dir_1/file_3
echo d > dir_2/file_4
ln dir_1/file_3 dir_2/file_5

dir_2/file_5dir_1/file_3에 대한 하드 링크로 생성했음을 주목하자. 즉, 이 두 파일은 같은 데이터에 대한 inode를 지칭하고 있으므로, 같은 파일이다.

이 파일 시스템에서 디렉터리를 탐색하면 그 내용은 그림 4-9와 같이 나타날 것이다.

filesystem의 실제 layout을 살펴보면 다음과 같을 것이다.

ext2/3/4 파일시스템에서는 inode 번호 2에서 시작한다. 이 번호는 루트 inode이다. 위 그림을 보면 inode table에서 이 번호가 디렉토리 inode(dir)임을 알 수 있다. 따라서 이 inode를 따라가면 데이터 풀에서 루트 디렉토리의 내용을 볼 수 있다. 루트 디렉토리에는 dir_1dir_2라는 두 개의 항목이 있으며, 이 항목들은 각각 inode 12와 7633에 해당한다. 이 항목들을 탐색하려면 inode 테이블로 돌아가서 그 중 하나를 살펴보면 된다.

이 파일시스템에서 dir_1/file_2를 확인하려면, 커널은 다음과 같은 절차를 따른다.

  1. 경로의 구성 요소를 결정한다: dir_1이라는 디렉토리, 그 뒤에 file_2라는 항목이 있다.
    루트 inode를 따라가서 해당 디렉토리 데이터를 찾는다.
  2. inode 2의 디렉토리 데이터에서 dir_1이라는 이름을 찾고, 이는 inode 12를 가리킨다.
  3. inode 12를 inode 테이블에서 찾아서 그것이 디렉토리 inode임을 확인한다.
  4. inode 12의 데이터 링크를 따라가면 디렉토리 정보(데이터 풀에서 두 번째 박스)를 찾을 수 있다.
  5. inode 12의 디렉토리 데이터에서 경로의 두 번째 구성 요소(file_2)를 찾는다. 이 항목은 inode 14를 가리킨다.
  6. inode 14를 inode 테이블에서 찾아본다. 이는 파일 inode이다.
  7. 이 시점에서 커널은 파일의 속성을 알게 되고, inode 14의 데이터 링크를 따라가서 파일을 연다.

이와 같은 시스템은 inode가 디렉토리 데이터 구조를 가리키고, 디렉토리 데이터 구조가 다시 inode를 가리키는 방식으로, 우리가 익숙한 파일시스템 계층 구조를 만든다. 또한 디렉토리 inode에는 .(현재 디렉토리)과 ..(부모 디렉토리, 루트 디렉토리를 제외한)는 항목이 포함되어 있어 참조점을 얻고 디렉토리 구조를 따라가기 쉽다.

디렉토리의 inode 번호를 보려면 ls -i 명령어를 사용하면 된다. 다음은 이 예제의 루트에서 얻을 수 있는 결과이다. (더 자세한 inode 정보를 원하면 stat 명령어를 사용)

ls -i
  12 dir_1  7633 dir_2

여기서 inode 테이블에서의 링크 카운트에 대해 궁금할 것이다. 이미 ls -l 명령어의 출력에서 링크 카운트를 본 적이 있지만, 그것을 무시했을 것이다. 링크 카운트 필드는 해당 inode를 가리키는 디렉토리 항목의 총 개수를 나타낸다. 대부분의 파일은 디렉토리 항목에 한 번만 나타나기 때문에 링크 카운트가 1이다. 이는 예상되는 것이다. 파일을 만들 때, 일반적으로 새로운 디렉토리 항목과 새로운 inode가 함께 만들어진다. 그러나 inode 15는 두 번 나타난다. 첫 번째는 dir_1/file_3으로 만들어지고, 두 번째는 dir_2/file_5로 링크된다. 하드 링크는 이미 존재하는 inode에 대해 디렉토리에서 새 항목을 수동으로 만든 것이다. ln 명령어(옵션 -s 없이)를 사용하면 하드 링크를 수동으로 만들 수 있다. 참고로 소프트링크인 ln -s로 링킹을 걸면 inode에 대한 링크를 거는 것이 아니라 특정 파일에 대한 링크를 걸기 때문에 inode 카운트가 늘지 않는다.

파일을 삭제하는 것도 때때로 언링크(unlink)라고 불린다. 예를 들어 rm dir_1/file_2를 실행하면, 커널은 inode 12의 디렉토리 항목에서 file_2라는 항목을 찾고, 이 항목이 inode 14를 가리킨다고 확인한다. 그런 다음 커널은 디렉토리 항목을 제거하고 inode 14의 링크 카운트를 1 감소시킨다. 그 결과 inode 14의 링크 카운트는 0이 되고, 커널은 더 이상 inode를 가리키는 이름이 없다는 것을 알게 된다. 따라서 inode와 관련된 데이터도 삭제할 수 있다.

하지만 rm dir_1/file_3을 실행하면, inode 15의 링크 카운트는 2에서 1로 변경된다(왜냐하면 dir_2/file_5가 여전히 그것을 가리키고 있기 때문이다). 이 경우 커널은 inode를 삭제하지 않도록 한다.

디렉토리에 대해서도 링크 카운트는 비슷하게 작동한다. 예를 들어 inode 12의 링크 카운트는 2인데, 이는 두 개의 inode 링크가 있기 때문이다. 하나는 inode 2의 디렉토리 항목에서 dir_1을 가리키고, 두 번째는 inode 12의 디렉토리 항목에서 .(현재 디렉토리)을 가리킨다. 만약 새로운 디렉토리 dir_1/dir_3을 만든다면, inode 12의 링크 카운트는 3으로 증가한다. 왜냐하면 새 디렉토리가 부모 디렉토리를 가리키는 .. 항목을 포함하기 때문이다. 이는 inode 12가 inode 2를 가리키는 방식과 유사하다.

작은 예외가 하나 있다. 루트 inode 2는 링크 카운트가 4이다. 그러나 위 그림에서는 세 개의 디렉토리 항목 링크만 보인다. "네 번째" 링크는 파일시스템의 슈퍼블록에 있다. 슈퍼블록은 루트 inode를 찾을 위치를 알려주기 때문이다.

Block Allocation

새 파일에 대한 data pool block을 할당할 때, 파일시스템은 어떤 블록이 사용 중이고 어떤 블록이 사용 가능한지 어떻게 알까? 가장 기본적인 방법 중 하나는 block bitmap이라는 추가적인 관리 데이터 구조를 사용하는 것이다. 이 방식에서는 파일시스템이 일련의 바이트를 예약하며, 각 비트는 데이터 풀의 하나의 블록에 해당한다. 값이 0이면 블록이 비어 있고, 1이면 블록이 사용 중이라는 뜻이다. 따라서 블록을 할당하고 해제하는 것은 비트 값을 바꾸는 작업에 해당한다.

파일시스템에서 문제가 발생하는 이유는 inode 테이블 데이터와 블록 할당 데이터가 일치하지 않거나 링크 카운트가 잘못될 때이다. 예를 들어 시스템을 깔끔하게 종료하지 않으면 이런 문제가 발생할 수 있다. 따라서 파일시스템을 점검할 때, fsck 프로그램은 inode 테이블과 디렉토리 구조를 따라가면서 새로운 링크 카운트와 새로운 블록 할당 맵(예: 블록 비트맵)을 생성하고, 새로 생성된 데이터를 디스크의 파일시스템과 비교한다. 불일치가 있을 경우, fsck는 링크 카운트를 수정하고 디렉토리 구조를 따라갔을 때 나타나지 않았던 inode나 데이터를 어떻게 처리할지 결정해야 한다. 대부분의 fsck 프로그램은 이런 "고아" 파일들을 파일시스템의 lost+found 디렉토리에 새 파일로 만든다.

Working with Filesystems in User Space

사용자 공간에서 파일과 디렉토리를 작업할 때, 그 밑에서 일어나고 있는 구현에 대해 많이 신경 쓸 필요는 없다. 프로세스는 마운트된 파일시스템의 파일과 디렉토리 내용을 커널 시스템 호출을 통해 접근할 것으로 기대된다. 그런데도 이상하게도 특정 파일시스템 정보를 사용자 공간에서 액세스할 수 있다. 특히, stat() 시스템 호출은 inode 번호링크 카운트를 반환한다.

파일시스템을 유지 관리하지 않는다면, inode 번호, 링크 카운트 및 기타 구현 세부 사항에 대해 걱정할 필요는 없다. 일반적으로 이 정보는 호환성을 위해 사용자 모드 프로그램에서 접근할 수 있다. 또한, Linux에서 사용할 수 있는 모든 파일시스템이 이러한 파일시스템 내부 구조를 가지고 있는 것은 아니다. VFS 인터페이스 계층은 시스템 호출이 항상 inode 번호와 링크 카운트를 반환하도록 보장하지만, 이 번호들이 반드시 의미가 있는 것은 아니다.

비전통적인 파일시스템에서는 전통적인 Unix 파일시스템 작업을 수행할 수 없을 수도 있다. 예를 들어, 마운트된 VFAT 파일시스템에서는 디렉토리 항목 구조가 Unix/Linux가 아닌 Windows용으로 설계되었기 때문에 ln 명령어를 사용하여 하드 링크를 만들 수 없다.

다행히도 Linux 시스템에서 사용자 공간에 제공되는 시스템 호출은 파일 접근을 위해 충분히 추상화된 인터페이스를 제공하여, 밑단 구현에 대해 알 필요 없이 파일을 쉽게 접근할 수 있다. 또한 파일명은 형식에 유연성이 있어 대소문자 혼합 이름도 지원하며, 다른 계층적 스타일의 파일시스템을 지원하는 데 문제가 없다.

특정 파일시스템 지원이 반드시 커널에 있어야 하는 것은 아니다. 예를 들어, 사용자 공간 파일시스템에서는 커널이 시스템 호출의 통로 역할만 하면 된다.

0개의 댓글