6 minute read

硬碟分區+ LVM + storage pool 設定流程

Install

sudo apt update
sudo apt install libvirt-daemon-system libvirt-clients virt-manager gir1.2-spiceclientgtk-3.0 qemu-kvm
sudo apt install thin-provisioning-tools
sudo usermod -aG libvirt $(whoami)
newgrp libvirt

流程示意

【第一層:物理硬體 (Hardware)】
   └─ 物理硬碟 (/dev/sda, /dev/sdb)
      │
      └─ 【第二層:清除流程 (確保硬碟乾淨)】
           ├─ 使用指令清除分區表和檔案系統簽章,例如:
           │     ├─ wipefs -a /dev/sda
           │     ├─ dd if=/dev/zero of=/dev/sda bs=1M count=100
           │     └─ 確認硬碟已無分區:fdisk -l /dev/sda
           │
           └─ 注意事項:
                 ├─ 有資料的硬碟也能分區,但新分區會破壞原資料結構
                 └─ 建議正式使用前清除殘留資料,避免誤判與混亂

      │
      └─ 【第三層:分區 (Partitions)】
           └─ 利用分區工具(fdisk、parted)將物理硬碟分割成分區
                └─ 產生物理分區,如 /dev/sda1、/dev/sda2、/dev/sdb1 等

      │ └─ 【第四層:LVM 基礎設施 (在主機 OS 中操作)】
           └─ 實體卷 (PVs)
                └─ 使用 pvcreate 將分區初始化成實體卷 (PV)
                     └─ 例如:pvcreate /dev/sda1 /dev/sdb1
                │
                └─ 使用 vgcreate 組合實體卷成卷組 (VG)
                     └─ 例如:vgcreate vg_vms /dev/sda1 /dev/sdb1
                │
                └─ 卷組 (VG) 範例:
                     ┌──────────────────────────────┐
                     │  卷組 (VG): vg_vms          │  <== 統一的儲存資源池
                     └──────────────────────────────┘

                │
                ├───────────┐ (分支一:為 Directory Pool 準備)
                │           │
                │           ▼ (手動使用 lvcreate + mkfs + mount)
                │        ┌───────────────────────────────────┐
                │        │ 邏輯卷 (LV) -> 檔案系統 (ext4) -> 掛載點 │
                │        │ e.g., /dev/vg_vms/lv_for_isos -> /isos  │
                │        └───────────────────────────────────┘
                │
                └───────────┐ (分支二:為 LVM Pool 準備)
                            │
                            ▼ (保持原始狀態,不格式化)
                         ┌───────────────────────────────────┐
                         │ 剩餘未格式化的原始儲存空間          │
                         │ (由 vg_vms 直接提供)              │
                         └───────────────────────────────────┘

【第五層:Libvirt 儲存池 (虛擬化管理層操作)】
   │
   ├─ (對應分支一) ─> 【Directory Pool: 'iso-pool'】
   │                    │ (類型: dir)
   │                    │ (目標路徑: /isos)
   │                    │ (用途: 存放 .iso、.qcow2 等映像檔)
   │                    └──────────────────────────────────┘
   │
   └─ (對應分支二) ─> 【LVM Pool: 'vms-lvm-pool'】
                        │ (類型: lvm)
                        │ (來源: vg_vms)
                        │ (用途: 自動為 VM 生成 LV 作為虛擬磁碟)
                        └──────────────────────────────────┘
                           │
                           ▼

【第六層:虛擬機 (VMs) 的使用】
   │
   ├─ ISO 映像檔 (例如 ubuntu.iso)
   │    └─ 從 【Directory Pool: 'iso-pool'】 中選取並掛載到 VM 用於安裝。
   │
   └─ 虛擬機 (例如 anfu-vm-01)
        │
        └─ 建立 VM 時,從 【LVM Pool: 'vms-lvm-pool'】 中請求虛擬磁碟 (LV)
           │
           ▼
        ┌──────────────────────────────────────────────────┐
        │  虛擬磁碟 (由 Libvirt 自動建立新的 LV)              │
        │  例如: /dev/vg_vms/anfu-vm-01.img                 │
        └──────────────────────────────────────────────────┘

簡單理解就是 pvs 就是硬碟抽象成的物件, 然後可以選擇這些物件創建 vgs (pool) , 之後根據需要從vgs創建 適合大小的lv or 整個vgs轉成lvm

  • lvm 無法被vm直接使用, 需轉成storage pool, 之後就可以選擇pool 分割出一個volume, vm就可以選擇這個volume開機
  • lv 分割後可以直接格式化 然後mount作為 filesystem 目錄使用, 直接存qcow2 就可以放vm檔案, 但須自己管理檔案, 還有一個作法是把該dir可以定義成storage pool, 可以更方便管理映像檔

磁碟分區

分區目的是

  • 切割硬碟空間
  • 標記類型碼,跟作業系統說這塊磁區用哪種方式處理 e.g. lvm, linux file system , windows ntfs , EFI 系統分區(製作系統安裝碟)

以上資訊會寫在硬碟的開頭,可以理解成這硬碟的metadata 稱為分區表, 紀錄資訊例如 磁區0~x 是lvm, 磁區 x-y 是 ntfs …, 但這分區表有兩種格式

  • MBR: 舊格式
  • GPT: 目前主流

補充: GPT在硬碟結尾還會有一份 備份分區表

分區表格式比較

目前主流應該都是GPT , MBR是比較舊的類型(現在硬碟很容易就超過2T,比較不適用)

特性 MBR (Master Boot Record) GPT (GUID Partition Table)
硬碟容量限制 最大支援 2TB 理論上最大支援 9.4 ZB
分區數量限制 最多 4 個主要分區 預設最多 128 個分區
可靠性 分區表僅存於磁碟開頭,無備份 分區表在磁碟頭尾各有一份備份
啟動方式 配合傳統 BIOS (Legacy) 配合現代 UEFI
分區識別 使用 32-bit 磁碟簽章 為每個分區使用 GUID (全域唯一識別碼)
作業系統支援 幾乎所有系統 現代 64-bit 作業系統 (如 Windows 7+, macOS, Linux)

基本相關指令

lsblk
# 顯示目前物理硬碟狀態(無論有無掛載)

df
# 顯示掛載硬碟狀態

從當前硬碟重新初始化

  • unmount

假設要做一些操作, 若有被掛載, 可能會有一些issue, 需保證MOUNTPOINTS 需為空白

lsblk
#NAME   MAJ:MIN RM  SIZE RO TYPE MOUNTPOINTS
#sda      8:0    0  1.8T  0 disk
#├─sda1   8:1    0  800G  0 part /data1
#└─sda2   8:2    0    1T  0 part /data2/backups

umount /data1
umount /data2/backups

or

umount /dev/sda1
umount /dev/sda2

若被PID佔用

fuser -kv /data1
# 強制中止PID
  • 清除磁區資料
blkdiscard -v /dev/sda
## ssd 專用 對設備壽命佳

dd if=/dev/zero of=/dev/sda bs=4M status=progress
## hdd

建立分區

可先用需用lsblk 查找硬碟代號 e.g. nvme0n1 sda

sgdisk 是GPT專用的現代硬碟工具

# 變數化設備名稱
NEW_DISK="/dev/sdx"
sudo sgdisk --new=1:0:0 --typecode=1:8e00 "${NEW_DISK}"
# -n, --new=<partition:start:end>
# -t, --typecode=<partition:code>
# 8e00 是 Linux LVM 的 GPT GUID 代碼


sudo partprobe /dev/sdx
# 分區後, 讓系統重新解析分區
# or reboot 建議重開比較穩

可先用需用lsblk 查找硬碟代號 e.g. nvme0n1 sda

sgdisk 是GPT專用的現代硬碟工具

# 變數化設備名稱
NEW_DISK="/dev/sdx"
sudo sgdisk --new=1:0:0 --typecode=1:8e00 "${NEW_DISK}"
# -n, --new=<partition:start:end>
# -t, --typecode=<partition:code>
# 8e00 是 Linux LVM 的 GPT GUID 代碼


sudo partprobe /dev/sdx
# 分區後, 讓系統重新解析分區
# or reboot 建議重開比較穩

typecode對照表

typecode 代碼:分區類型碼 標記該磁區用途

每個硬碟代號 e.g. sda , nvme0n1 (不同兩顆硬碟), 分區代號都是分開的 前者有1,2,3 後者也可以1,2,3

8300為預設代碼 ext4 linux 標準檔案系統

代碼 (Code) GPT 名稱 用途
8300 Linux filesystem (預設值) 標準 Linux 檔案系統 (ext4, XFS 等)
8e00 Linux LVM 用於 LVM 的實體卷冊 (PV)
8200 Linux swap 用於 Linux 交換空間 (虛擬記憶體)
ef00 EFI System Partition 用於 UEFI 啟動的系統分區 (ESP)
0700 Microsoft basic data 用於 Windows NTFS, FAT32 等

–new 規則

第1個數字: 分區代號 , 0 請自動尋找最低的可用分區編號 , 若是1 則是指定分區代號1
第2個數字: 起始磁區 , 0 表示自動尋找適當的起始位置
第3個數字: 結束磁區 , 0 表示使用全部可用空間

其他範例

sgdisk --new=0:0:+500G /dev/sdx
# 建立一個 500GB 的分區

sgdisk --new=0:0:+100G --new=2:0:0 /dev/sdx
# 建立兩個分區,第一個 100GB,第二個使用剩下所有空間

sgdisk --new=0:0:0 --typecode=1:8e00 /dev/sda
# 把sda 全部建立成一個個lvm 格式的硬碟, 自動抓取分區代號, 與磁區起始位置, 然後全部建立
# Creating new GPT entries in memory.
# Warning: The kernel is still using the old partition table.
# The new table will be used at the next reboot or after you
# run partprobe(8) or kpartx(8)
# The operation has completed successfully.

LVM 初始化

流程 PVS, VGS , LV or LVM

PVS

Physical Volumes: LVM的的基本單位, 簡單說可以把 硬碟空間抽象成lvm的單位

  • 檢視
pvs
# 查看被分區成 pvs 的基本單位
#PV             VG        Fmt  Attr PSize    PFree
#/dev/nvme0n1p3 ubuntu-vg lvm2 a--  <950.82g <850.82g
#/dev/sda       local-lvm lvm2 a--    <1.82t  512.00m

#PV: 實體卷的路徑。可以是整顆硬碟 (/dev/sdc) 或一個分割區 (/dev/sda2)。
#VG: 這個實體卷目前屬於哪個「卷組」(Volume Group)。
#PSize: 這個實體卷的總大小。
#PFree: 這個實體卷上還有多少空間未被分配給卷組。
  • 創建

創建PVS, 先檢視格式化後的磁碟分區代號

lsblk
NAME                      MAJ:MIN RM   SIZE RO TYPE MOUNTPOINTS
sda                         8:0    0   1.8T  0 disk
└─sda1                      8:1    0   1.8T  0 part
pvcreate /dev/sda1
# Physical volume "/dev/sda1" successfully created.
pvs
#  PV             VG        Fmt  Attr PSize    PFree
#  /dev/nvme0n1p3 ubuntu-vg lvm2 a--  <950.82g <850.82g
#  /dev/sda1                lvm2 ---    <1.82t   <1.82t

VGS

Volume Groups: 可以理解成創建一個pool, 內含各硬碟被分區成LVM單位

一般來說一個硬碟只會有一個vgs, 或是把多個硬碟合併成一個vgs

  • 檢視vgs
vgs
#VG        #PV #LV #SN Attr   VSize    VFree
#ubuntu-vg   1   1   0 wz--n- <950.82g <850.82g

#VG: 卷組的名稱。
##PV: 這個卷組是由幾個實體卷 (PVs) 組成的。 (vg_data 是由 2 個 PV 組成)
##LV: 這個卷組上已經切出了幾個「邏輯卷」(lvg)。
#VSize: 這個卷組的總大小 (所有 PV 大小的總和)。
#VFree: 這個卷組中還有多少可用空間可以讓你切出新的邏輯卷 (lvg)。

pvs
#  PV             VG        Fmt  Attr PSize    PFree
#  /dev/nvme0n1p3 ubuntu-vg lvm2 a--  <950.82g <850.82g
#  /dev/sda1                lvm2 ---    <1.82t   <1.82t
# 可以看到若尚未設置vgs vg是空的

  • 創建vgs
vgcreate anfu_vg /dev/sda1
#Volume group "anfu_vg" successfully created

pvs
#  PV             VG        Fmt  Attr PSize    PFree
#  /dev/nvme0n1p3 ubuntu-vg lvm2 a--  <950.82g <850.82g
#  /dev/sda1      anfu_vg   lvm2 a--    <1.82t   <1.82t
# vg 會非空白
vgs
#  VG        #PV #LV #SN Attr   VSize    VFree
#  anfu_vg     1   0   0 wz--n-   <1.82t   <1.82t
#  ubuntu-vg   1   1   0 wz--n- <950.82g <850.82g
# vgs 會顯示在列表

  • 擴展vgs
vgextend anfu_vg /dev/sda2
# 假設有一個新的pvs
# 可以這樣擴展 vgs的大小

方案a. LV

Logical Volumes: 可以理解成選定一個pool(vgs) 拿出需要固定大小的 pvs, 輸出成 lv

這種是固定大小方案

lv = vgs 切一部分容量成 lv, 這邊可以直接格式化成filesystem mount(這邊需要自己管理檔案,若存vm可用qcow2), filesystem也可以抽象成storage pool, 但實際上適用於管理裡面的映像檔

  • 檢視lvg
lvg
#LV        VG        Attr       LSize   Pool Origin Data%  Meta%  Move Log Cpy%Sync Convert
#ubuntu-lv ubuntu-vg -wi-ao---- 100.00g
  • 創建lvg

假設我要從某vgs 切出一塊 lvg

lvcreate -n another_lv -L 200G anfu_vg

# -n or --name  lvg name
# -L or --size  lvg size

lvcreate --name will_tainan --size 200G anfu_vg
#  Logical volume "will_tainan" created.

  • 擴展lvg

擴展路徑規則 /dev/<vgs name>/<lvg name>

lvextend -L +8G /dev/anfu_vg/will_tainan
# 假設我要擃展 will_tainan lvg的大小

lvg 切出固定大小後 就是要格式化, 之後用mount掛載成目錄, 就可以當作一般硬碟用

sudo mkfs.ext4 /dev/ubuntu-vg/test_vm_storage

若正在掛載, 需更新掛載時設定的檔案系統大小

路徑規則 /dev/<vgs name>/<lvg name>

resize2fs /dev/anfu_vg/will_tainan
  • 刪除lvg

確認不能被掛載, MOUNTPOINTS 需為空

lsblk
#NAME                      MAJ:MIN RM   SIZE RO TYPE MOUNTPOINTS
#sda                         8:0    0   1.8T  0 disk
#└─sda1                      8:1    0   1.8T  0 part
#  └─anfu_vg-will_tainan   252:1    0   200G  0 lvm  /mnt/test
#nvme0n1                   259:0    0 953.9G  0 disk
#├─nvme0n1p1               259:1    0     1G  0 part /boot/efi
#├─nvme0n1p2               259:2    0     2G  0 part /boot
#└─nvme0n1p3               259:3    0 950.8G  0 part
#  └─ubuntu--vg-ubuntu--lv 252:0    0   100G  0 lvm  /

sudo umount /mnt/test
# 先確認不能被掛載狀態


lvremove /dev/anfu_vg/will_tainan
# 刪除lvg, 會回到vgs的容量


vgs
#  VG        #PV #LV #SN Attr   VSize    VFree
#  anfu_vg     1   1   0 wz--n-   <1.82t   <1.53t
#  ubuntu-vg   1   1   0 wz--n- <950.82g <850.82g
lvg
#  LV          VG        Attr       LSize   Pool Origin Data%  Meta%  Move Log Cpy%Sync Convert
#  will_tainan anfu_vg   -wi-a----- 300.00g
#  ubuntu-lv   ubuntu-vg -wi-ao---- 100.00g
lvremove /dev/anfu_vg/will_tainan
#Do you really want to remove and DISCARD active logical volume anfu_vg/will_tainan? [y/n]: Y
#  Logical volume "will_tainan" successfully removed.

方案b. lvm

Logic Volume Manage: 這種是把一個pvs 直接抽象成物件,不分容量,

若是整個vgs 要給vm使用, 就不用抽象成lv, 而是整包vgs當作一個lvm, 最後轉成storage pool, 之後vm要多少 直接跟切出固定大小, vm用這個volume開機

lvm = vgs 直接打包, 沒有所謂lvm的物件 lvm=整包vgs的lv 概念 , 這邊也可以抽象成 storage pool, 這邊切出volume實際上底層就是切出一個lv

storage pool manage

  • lvm 資源無法直接被vm使用 用需要轉成storage pool

  • lv 可轉可不轉, 一般會直接把lv格式化, 然後mount 當作file system使用, vm可直接存qcow2 在這個目錄, 但需自己管理檔案, 也支援把某目錄轉成storage pool, 主要是可以管理該目錄下的映像檔, 比較少用

  • 確認來源

sudo vgs
#  VG        #PV #LV #SN Attr   VSize    VFree
#  anfu_vg     1   1   0 wz--n-   <1.82t    1.62t
#  ubuntu-vg   1   1   0 wz--n- <950.82g <850.82g

  • 創建pool
sudo virsh pool-define-as lvm-pool lvm --source-name <vg_name> --target /dev/<vg_name>

lvm 設定 storage pool

sudo virsh pool-define lvm-pool.xml

這個若lvm pool綁定vgs, 則是沒有容量設定的,

vim lvm-pool.xml

這邊會需要path 基本上就是/dev/<vg_name>

lvm pool

<pool type='logical'>
  <name>anfu_lvm_pool</name>
  <source>
    <name>anfu_vg</name>
    <format type='lvm2'/>
  </source>
  <target>
    <path>/dev/anfu_vg</path>
  </target>
</pool>

lv格式化後 mount成一般目錄, 可將目錄轉成storage pool, 方便管理映像檔

<pool type='dir'>
  <name>default</name>
  <target>
    <path>/var/lib/libvirt/images</path>
  </target>
</pool>
sudo virsh pool-define lvm-pool.xml
#Pool anfu_lvm_pool defined from lvm-pool.xml

cat lvm-pool.xml
#<pool type='logical'>
#  <name>anfu_lvm_pool</name>
#  <source>
#    <name>anfu_vg</name>
#    <format type='lvm2'/>
#  </source>
#  <target>
#    <path>/dev/anfu_vg</path>
#  </target>
#</pool>

  • 驗證
sudo virsh pool-list --all
  • 啟動
sudo virsh pool-start anfu_lvm_pool
sudo virsh pool-autostart anfu_lvm_pool
  • 檢視設定檔
sudo virsh pool-dumpxml anfu_lvm_pool
sudo lvdisplay
# list 所有lv 路徑