关键词:LVM、PV、VG、LV、磁盘扩容 标签:Linux 运维、存储管理、Shell 脚本

一、脚本定位

1.1 这份脚本解决什么问题

  • 创建物理卷、卷组和逻辑卷。
  • 对逻辑卷进行扩容与缩容。
  • 删除 LV、VG、PV。
  • 查看 LVM 当前状态。
  • 把旧盘数据迁移到新盘。

1.2 菜单入口示例

show_menu() {
    echo "1. 创建物理卷 (PV)"
    echo "2. 创建卷组 (VG)"
    echo "3. 创建逻辑卷 (LV)"
    echo "4. 扩展逻辑卷 (LV)"
    echo "5. 缩减逻辑卷 (LV)"
    echo "6. 删除逻辑卷 (LV)"
    echo "7. 删除卷组 (VG)"
    echo "8. 删除物理卷 (PV)"
    echo "9. 查看 LVM 信息"
    echo "10. 迁移数据到新盘"
}

二、核心能力整理

2.1 创建卷的流程

lvcreate -L "$lv_size" -n "$lv_name" "$vg_name"
mkdir -p "$mount_point"
mkfs.ext4 /dev/$vg_name/$lv_name
mount /dev/$vg_name/$lv_name "$mount_point"

2.2 扩容与缩容的边界条件

  • 扩容时通过 blkid 自动识别文件系统,再分别调用 resize2fsxfs_growfs
  • 缩容时先尝试卸载,再执行文件系统级处理,最后调用 lvreduce
  • 脚本明确写出了 XFS 不支持直接缩容,这一点非常适合单独整理成博客中的风险提示。
fs_type=$(blkid -o value -s TYPE /dev/$vg_name/$lv_name)
if [[ "$fs_type" == "ext4" || "$fs_type" == "ext3" || "$fs_type" == "ext2" ]]; then
    resize2fs /dev/$vg_name/$lv_name "$lv_size"
elif [[ "$fs_type" == "xfs" ]]; then
    echo "XFS 文件系统不支持直接缩减,需要手动处理。"
    return 1
fi

三、数据迁移部分的可发布价值

3.1 旧盘迁移到新盘的操作逻辑

pvcreate "$new_disk"
pvmove "$old_disk"
pvremove "$old_disk"

3.2 建议在正文里补充的风险说明

  • pvmove 前建议先做一次数据备份。
  • 缩容前必须确认文件系统类型和业务停机窗口。
  • 菜单脚本适合实验环境与运维值守场景,不建议在无人值守任务里直接交互执行。

四、完整脚本

以下为本文对应的完整脚本,便于直接复制复用。

4.1 lvm_management.sh

#!/bin/bash

# 颜色设置
COLOR_SUCCESS="\033[32m"  # 绿色
COLOR_FAIL="\033[31m"     # 红色
COLOR_MENU="\033[34m"     # 蓝色
COLOR_RESET="\033[0m"     # 重置颜色

# 显示主菜单
function show_menu {
    clear
    echo -e "$COLOR_MENU----------------------------"
    echo -e "$COLOR_MENU  LVM Management Menu"
    echo -e "$COLOR_MENU----------------------------"
    echo "1. 创建物理卷 (PV)"
    echo "2. 创建卷组 (VG)"
    echo "3. 创建逻辑卷 (LV)"
    echo "4. 扩展逻辑卷 (LV)"
    echo "5. 缩减逻辑卷 (LV)"
    echo "6. 删除逻辑卷 (LV)"
    echo "7. 删除卷组 (VG)"
    echo "8. 删除物理卷 (PV)"
    echo "9. 查看 LVM 信息"
    echo "10. 迁移数据到新盘"
    echo "11. 退出"
    echo -e "$COLOR_MENU----------------------------"
    read -p "请输入选项 [1-11]: " option
}

# 创建物理卷
function create_pv {
    read -p "请输入物理卷设备路径 (如 /dev/sdX): " pv_device
    pvcreate $pv_device
    print_result "创建物理卷 $pv_device"
}

# 创建卷组
function create_vg {
    read -p "请输入卷组名称: " vg_name
    read -p "请输入物理卷设备路径 (如 /dev/sdX): " pv_device
    vgcreate $vg_name $pv_device
    print_result "创建卷组 $vg_name"
}

# 创建逻辑卷
function create_lv {
    read -p "请输入卷组名称: " vg_name
    read -p "请输入逻辑卷名称: " lv_name
    read -p "请输入逻辑卷大小 (如 10G): " lv_size
    lvcreate -L $lv_size -n $lv_name $vg_name
    # 提示用户输入挂载目录
    read -p "请输入挂载目录: " mount_point
    if [ ! -d "$mount_point" ]; then
        mkdir -p $mount_point
    fi
    read -p "请选择文件系统类型 (ext4 或 xfs): " fs_type
    if [[ "$fs_type" == "ext4" ]]; then
        mkfs.ext4 /dev/$vg_name/$lv_name
    elif [[ "$fs_type" == "xfs" ]]; then
        mkfs.xfs /dev/$vg_name/$lv_name
    else
        echo -e "$COLOR_FAIL 不支持的文件系统类型,退出..."
        return 1
    fi
    mount /dev/$vg_name/$lv_name $mount_point
    echo -e "$COLOR_SUCCESS 逻辑卷 $lv_name 已挂载到 $mount_point"
}

# 扩展逻辑卷
function extend_lv {
    read -p "请输入卷组名称: " vg_name
    read -p "请输入逻辑卷名称: " lv_name
    read -p "请输入扩展的大小 (如 5G): " lv_size
    lvextend -L +$lv_size /dev/$vg_name/$lv_name
    fs_type=$(blkid -o value -s TYPE /dev/$vg_name/$lv_name)
    if [[ "$fs_type" == "ext4" || "$fs_type" == "ext3" || "$fs_type" == "ext2" ]]; then
        resize2fs /dev/$vg_name/$lv_name
    elif [[ "$fs_type" == "xfs" ]]; then
        xfs_growfs /dev/$vg_name/$lv_name
    else
        echo -e "$COLOR_FAIL 不支持的文件系统类型: $fs_type"
    fi
    print_result "扩展逻辑卷 $lv_name"
}

# 缩减逻辑卷
function reduce_lv {
    read -p "请输入卷组名称: " vg_name
    read -p "请输入逻辑卷名称: " lv_name
    read -p "请输入缩减后的大小 (如 5G): " lv_size
    mount_point=$(lsblk /dev/$vg_name/$lv_name -o MOUNTPOINT -J | jq -r '.blockdevices[0].mountpoint')
    if [ -n "$mount_point" ]; then
        umount $mount_point
    fi
    fs_type=$(blkid -o value -s TYPE /dev/$vg_name/$lv_name)
    if [[ "$fs_type" == "ext4" || "$fs_type" == "ext3" || "$fs_type" == "ext2" ]]; then
        resize2fs /dev/$vg_name/$lv_name $lv_size
    elif [[ "$fs_type" == "xfs" ]]; then
        echo "XFS 文件系统不支持直接缩减,需要手动处理。"
        return 1
    else
        echo "不支持的文件系统类型: $fs_type"
        return 1
    fi
    lvreduce -L $lv_size -r /dev/$vg_name/$lv_name
    if [ -n "$mount_point" ]; then
        mount /dev/$vg_name/$lv_name $mount_point
    fi
    print_result "缩减逻辑卷 $lv_name"
}

# 删除逻辑卷
function remove_lv {
    read -p "请输入卷组名称: " vg_name
    read -p "请输入逻辑卷名称: " lv_name
    mount_point=$(lsblk /dev/$vg_name/$lv_name -o MOUNTPOINT -J | jq -r '.blockdevices[0].mountpoint')
    if [ -n "$mount_point" ]; then
        umount $mount_point
    fi
    lvremove /dev/$vg_name/$lv_name
    print_result "删除逻辑卷 $lv_name"
}

# 删除卷组
function remove_vg {
    read -p "请输入卷组名称: " vg_name
    lv_list=$(lvs --noheadings -o lv_name /dev/$vg_name)
    if [ -n "$lv_list" ]; then
        echo "卷组 $vg_name 下有逻辑卷,请先删除逻辑卷"
        return
    fi
    vgremove $vg_name
    print_result "删除卷组 $vg_name"
}

# 删除物理卷
function remove_pv {
    read -p "请输入物理卷设备路径 (如 /dev/sdX): " pv_device
    vg_name=$(vgdisplay --noheadings -o vg_name $pv_device)
    if [ -n "$vg_name" ]; then
        vgreduce $vg_name $pv_device
    fi
    pvremove $pv_device
    print_result "删除物理卷 $pv_device"
}

# 查看LVM信息
function view_lvm_info {
    echo "请选择要查询的信息:"
    echo "1. 物理卷信息 (PV)"
    echo "2. 卷组信息 (VG)"
    echo "3. 逻辑卷信息 (LV)"
    echo "4. 所有信息"
    read -p "请输入选项 [1-4]: " option
    case $option in
        1) pvdisplay | egrep 'volume|PV Name|Total PE|Free PE' ;;
        2) vgdisplay | egrep 'Volume group|VG Name|VG Access|VG Status|MAX LV|Cur LV|Open LV|MAX PV|Cur PV|Act PV|VG Size|PE Size|Total PE|Free PE' ;;
        3) lvdisplay | egrep 'Logical volume|LV Path|LV Name|VG Name|LV Write Access|LV Status|LV Size|Current LE' ;;
        4)
            pvdisplay | egrep 'volume|PV Name|Total PE|Free PE'
            vgdisplay | egrep 'Volume group|VG Name|VG Access|VG Status|MAX LV|Cur LV|Open LV|MAX PV|Cur PV|Act PV|VG Size|PE Size|Total PE|Free PE'
            lvdisplay | egrep 'Logical volume|LV Path|LV Name|VG Name|LV Write Access|LV Status|LV Size|Current LE'
            ;;
        *)
            echo "无效的选项,请重新选择."
            ;;
    esac
}

# 迁移数据到新盘
function migrate_data {
    read -p "请输入新盘名称: " new_disk
    pvcreate $new_disk
    read -p "请输入要扩展的卷组名称: " vg_name
    vg_name_check=$(vgs --noheadings -o vg_name | grep -w $vg_name)
    if [ -z "$vg_name_check" ]; then
        echo "卷组不存在,请先创建卷组"
        return
    fi
    read -p "请输入要迁移的旧盘 (如 /dev/sdX): " old_disk
    pvmove $old_disk
    read -p "数据迁移完毕后,是否移除旧盘 $old_disk (y/n): " remove_choice
    if [[ "$remove_choice" == "y" || "$remove_choice" == "Y" ]]; then
        pvremove $old_disk
        echo -e "$COLOR_SUCCESS 已移除旧盘 $old_disk"
    fi
    print_result "迁移数据到新盘"
}

# 输出成功或失败的结果
function print_result {
    local message=$1
    echo -e "$COLOR_MENU----------------------------"
    if [ $? -eq 0 ]; then
        echo -e "$COLOR_SUCCESS $message 完成"
    else
        echo -e "$COLOR_FAIL $message 失败"
    fi
    echo -e "$COLOR_MENU----------------------------"
    read -p "按 Enter 键返回菜单..."
}

# 主菜单逻辑
while true; do
    show_menu
    case $option in
        1) create_pv ;;
        2) create_vg ;;
        3) create_lv ;;
        4) extend_lv ;;
        5) reduce_lv ;;
        6) remove_lv ;;
        7) remove_vg ;;
        8) remove_pv ;;
        9) view_lvm_info ;;
        10) migrate_data ;;
        11) exit 0 ;;
        *) echo "无效的选项,请重新选择." ;;
    esac
done