Sau khi dùng Raspberry Pi Imager ghi file ảnh của OS vào đĩa như bình thường, chúng ta sẽ chuyển OS trên partition rootfs vào LV.
Việc chuyển phần cài đặt thì đơn giản nhưng các driver của LVM ngay khi khởi động phải được nạp để có thể làm việc với rootfs trên LV. Khi khởi động Raspberry Pi nạp một ramdisk cho initrd, vì vậy chúng ta chỉ phải bổ sung initram chứa các driver cho LVM.
Chuẩn bị
- RPi 4
- 2 đĩa khởi động PiOS, trong đó 1 đĩa sẽ được chuyển PiOS sang logical volume
- Wifi
Ghi file ảnh vào đĩa
Sau khi chọn OS và đĩa (SD card), chúng ta chọn thêm phần Options bằng cách bấm vào nút hình bánh xe. Sau khi ghi file ảnh vào đĩa, Raspberry Pi Imager sẽ làm thêm vài cài đặt đã được chỉ định, bao gồm: đặt hostname, cho phép ssh, mật khẩu của pi, cài đặt wifi… Chú ý là Raspberry Pi 4 hỗ trợ wifi 5G nên nhanh hơn mạng LAN 100Mps.
Thay đổi phần khởi động của RPi
Khởi động từ đĩa mới tạo, ta gọi theo hostname là đĩa mail.example.net, cập nhật hệ thống, sau đó cài thêm LVM
sudo apt update && sudo apt -y upgrade
sudo apt install lvm2
initramfs-tools đã có sẵn trong hệ thống, tuy nhiên nếu chưa có thì cài thêm
sudo apt install initramfs-tools
Tiếp theo xem phiên bản của drivers
ls /lib/modules/
5.15.32+ 5.15.32-v7+ 5.15.32-v7l+ 5.15.32-v8+
Sửa file /boot/config.txt, ghi ở đầu file để yêu cầu nạp initramfs khi khởi động
[pi4]
initramfs initrd.img-5.15.32-v8+ followkernel
Script thực hiện các bước trên:
sudo apt update && sudo apt -y upgrade
sudo apt -y install lvm2
m=($(tr ' ' '\n' <<<"$(ls /lib/modules/)" | sort -n -r))
v=${m[0]}
sudo sed -i '/\[pi4\]/,$ d
1 i\
[pi4]\
initramfs initrd.img-'"$v"' followkernel\
' /boot/config.txt
Cuối cùng tạo initrd.img và ghi vào /boot. Tên file có thêm version để sau này dễ nhận biết và cập nhật.
sudo sed -i 's/^MODULES=most/MODULES=list/' /etc/initramfs-tools/initramfs.conf
sudo mkinitramfs -o /boot/initrd.img-5.15.32-v8+ 5.15.32-v8+
sudo apt clean
Có thể có vài thông báo lỗi do hệ thống fat trên partition boot không hỗ trợ, bỏ qua.
Trước khi khởi động lại có thể đặt IP tĩnh.
Cài đặt ip tĩnh, nếu cần
# Đặt static_ip và static_router. Thí dụ:
# static_ip=192.168.100.100
# static_router=192.168.100.1
sudo sed -i '/slaac private/,${/slaac private/!d}
/slaac private/a\
\
# static IP\
interface wlan0\
static ip_address='"$static_ip"'/24\
# static ip6_address=fd51:42f8:caae:d92e::ff/64\
static routers='"$static_router"'\
static domain_name_servers='"$static_router"' 8.8.8.8
' /etc/dhcpcd.conf
Test: Khởi động lại đĩa như bình thường nhưng bây giờ RPi dùng initram.
sudo journalctl -b | grep initr
Jun 10 22:21:29 mail.example.net kernel: Trying to unpack rootfs image as initramfs...
Jun 10 22:21:29 mail.example.net kernel: Freeing initrd memory: 13656K
Backup đĩa mail.example.net
Bây giờ chúng ta cần một đĩa thứ 2 cũng chạy Raspbian, và cũng cài lvm2 trên đây để tạo logical volume trên đĩa mail.example.net
Khởi động RPi bằng đĩa thứ 2, sau đó gắn đĩa mail.example.net vào hệ thống.
# sudo lsblk
NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINT
sda 8:0 1 59.5G 0 disk
├─sda1 8:1 1 256M 0 part
└─sda2 8:2 1 59.3G 0 part
mmcblk0 179:0 0 29.4G 0 disk
├─mmcblk0p1 179:1 0 256M 0 part /boot
└─mmcblk0p2 179:3 0 29.2G 0 part /
Đĩa mail.example.net gắn vào cổng USB phía trước, có tên trong hệ thống là sda
# Kiểm tra đĩa
sudo fsck.vfat /dev/sda1
sudo fsck.ext4 -f /dev/sda2
# Mount các partition của sda
sudo mkdir /mnt/rootfs
sudo mount /dev/sda2 /mnt/rootfs
# sudo mkdir /mnt/sda2/boot
sudo mount /dev/sda1 /mnt/rootfs/boot
# Backup đĩa sda để chuẩn bị xóa partition rootfs.
# Tên file backup là raspbian-bullseye.tar.gz
sudo tar -cvzf raspbian-bullseye.tar.gz -C /mnt/rootfs --exclude=tmp --exclude=var/log ./
sudo umount /mnt/rootfs/boot /mnt/rootfs
# Kiểm tra file backup, nếu cần
sudo tar -tvf raspbian-bullseye.tar.gz
Phân chia lại đĩa sda
Dùng parted xóa cấu trúc đĩa và tạo lại partition boot và partition lvm
# Hệ thống file kiểu DOS (MBR)
sudo parted /dev/sda mktable msdos
# Partition #1 FAT32 256M
sudo parted /dev/sda mkpart primary fat32 2048s 256MiB
# Partition #2 EXT4 phần còn lại của đĩa
sudo parted /dev/sda mkpart primary ext4 256MiB 100%
# Partition #2 kiểu LVM
sudo parted /dev/sda set 2 lvm on
Tạo logical volume
Chúng ta chỉ tạo một LV kích thước vừa đủ để chứa OS, phần còn lại trên partition 2 sẽ dùng sau.
# Tạo physical volume trước
sudo pvcreate /dev/sda2
# Tạo volume group trên physical volume
sudo vgcreate rpi.vg /dev/sda2
# Tạo logical volume
sudo lvcreate rpi.vg --name rootfs.lv --size 6G
Hệ thống đĩa của sda hiện nay như sau
# sudo vgs
VG #PV #LV #SN Attr VSize VFree
rpi.vg 1 1 0 wz--n- <59.27g <53.27g
# sudo lvs
LV VG Attr LSize
rootfs.lv rpi.vg -wi-a----- 6.00g
# sudo lsblk
NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINT
sda 8:0 1 59.5G 0 disk
├─sda1 8:1 1 255M 0 part
└─sda2 8:2 1 59.3G 0 part
└─rpi.vg-rootfs.lv 254:0 0 6G 0 lvm
Format và mount các volume
sudo mkfs.vfat -F 32 -n BOOT /dev/sda1
sudo mkfs.ext4 -L rootfs /dev/mapper/rpi.vg-rootfs.lv
sudo mkdir /mnt/rootfs
sudo mount /dev/mapper/rpi.vg-rootfs.lv /mnt/rootfs
sudo mkdir /mnt/rootfs/boot ## thư mục con trong rootfs.lv
sudo mount /dev/sda1 /mnt/rootfs/boot
Restore Raspbian từ file backup
# phục hồi dữ liệu từ file backup
sudo tar -xvf raspbian-bullseye.tar.gz -C /mnt/rootfs
Sửa đổi các điểm mount
Sửa đổi các điểm mount trong /mnt/rootfs/boot/cmdline.txt và /mnt/rootfs/etc/fstab để có thể khởi động vào logical volume
- PARTUUID=xxxxxxxx-02 => /dev/mapper/rpi.vg-rootfs.lv
- PARTUUID=xxxxxxxx-01 => partuuid mới của /dev/sda1
# rootfs giờ là /dev/mapper/rpi.vg-rootfs.lv
sudo sed -i 's/root=PARTUUID=[a-z0-9]*-02/root=\/dev\/mapper\/rpi.vg-rootfs.lv/' /mnt/rootfs/boot/cmdline.txt
# Lấy PARTUUID của /dev/sda1, thay đổi sau parted
pu=$(sudo blkid /dev/sda1 | grep -Po '(?<=PARTUUID=")(.*)(?=")')
# Thay đổi fstab
sudo sed -i "s/^PARTUUID=[a-z0-9]*-01/$pu/
s/^PARTUUID=[a-z0-9]*-02/\/dev\/mapper\/rpi.vg-rootfs.lv/" /mnt/rootfs/etc/fstab
Kiểm tra:
# cat /mnt/rootfs/boot/cmdline.txt
console=serial0,115200 console=tty1 root=/dev/mapper/rpi.vg-rootfs.lv rootfstype=ext4 ...
# cat /mnt/rootfs/etc/fstab
...
xxxxxxxx-01 /boot vfat defaults,flush 0 2
/dev/mapper/rpi.vg-rootfs.lv / ext4 defaults,noatime 0 1
...
Xong rồi, dọn dẹp
sudo umount /mnt/rootfs/boot /mnt/rootfs
sudo fsck.vfat /dev/sda1
sudo fsck.ext4 -f /dev/mapper/rpi.vg-rootfs.lv
rm -rf /mnt/rootfs
Khởi động lại
Cuối cùng khởi động lại bằng đĩa mail.example.net. rootfs bây giờ là logical volume.
# lsblk
NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINT
mmcblk0 179:0 0 29.4G 0 disk
├─mmcblk0p1 179:1 0 256M 0 part /boot
└─mmcblk0p2 179:3 0 29.2G 0 part
└─rpi.vg-rootfs.lv 254:0 0 10G 0 lvm /
Tạo thêm các LV trên phần đĩa trống của partition #2 nếu cần
lvcreate -L <size> -n <lv_name> rpi.vg
lvcreate -l <size%FREE> -n <lv_name> rpi.vg
Chú thích
- Điểm quan trọng là dòng lệnh nạp initram phải ở đầu file /boot/config.txt, nếu không các driver của lvm không được nạp lên sớm => không mount được vg, lv. Khi đó có thông báo lỗi rpi.vg not found => không boot được, ngay cả khi thêm option rootdelay ở cmdline.txt
- Dùng /dev/rpi.vg/rootfs.lv cũng hợp lệ như /dev/mapper/rpi.vg-rootfs.lv, nhưng không hoạt động trong config.txt
- Khi mount các LV trong fstab, các điểm mount phải tồn tại, nếu không sẽ không khởi động được
- Toàn bộ quá trình trên có thể ghép lại thành một script