理解 QEMU/KVM 和 Ceph(3):存储卷挂接和设备名称

本文发布时间: 2019-Mar-22
本系列文章会总结 QEMU/KVM 和 Ceph 之间的整合:(1)QEMU-KVM 和 Ceph RBD 的 缓存机制总结(2)QEMU 的 RBD 块驱动(block driver)(3)存储卷挂接和设备名称这篇文章分析一下一个 Ceph RBD 卷是如何被映射到一个 QEMU/KVM 客户机的,以及客户机中设备的命名问题。1. 遇到的设备命名问题1.1 通过 Nova 和 Cinder 做 Ceph RDB 卷挂接和卸载步骤挂接一个卷:#运行nova-attach 命令nova volume-attach INSTANCE_ID VOLUME_ID auto#在虚机中操作vm$ ls /dev/disk/by-id/virtio-15a9f901-ba9d-45e1-8vm$ mkfs.ext4 /dev/disk/by-id/virtio-15a9f901-ba9d-45e1-8vm$ mkdir -p /mnt/volumevm$ mount /dev/disk/by-id/virtio-15a9f901-ba9d-45e1-8 /mnt/volumevm$ echo 'Hello OpenStack' > /mnt/volume/test.txt卸载一个卷:#在虚机中操作vm$ umount /mnt/volume#运行 Nova 命令$ nova volume-detach <instanceid> <volumeid>1.2 两个问题Nova 的 volume-attach 命令为:root@hkg02kvm004ccz023:~# nova help volume-attachusage: nova volume-attach <server> <volume> [<device>]Attach a volume to a server.Positional arguments: <server> Name or ID of server. <volume> ID of the volume to attach. <device> Name of the device e.g. /dev/vdb. Use 'auto' for autoassign (if supported). Libvirt driver will use default device name.1.2.1 在 KVM 环境中,nova 的 volume-attach 命令中的 device 和 虚机中该设备的 device name 不一致命令:root@hkg02kvm004ccz023:~# nova volume-attach ap-uplthyfxulbx 8a309ad1-369c-483b-9a95-e4f330bb104e /dev/vdh+----------+--------------------------------------+| Property | Value |+----------+--------------------------------------+| device | /dev/vdh |虚机中:[root@ap-4oe4-4rz25pvadal7-cs7nmsu4izfy-server-uplthyfxulbx rules.d]# fdisk -lDisk /dev/vda: 42.9 GB, 42949672960 bytes...Disk /dev/vdb: 1073 MB, 1073741824 bytes...Disk /dev/vdc: 2147 MB, 2147483648 bytes...Disk /dev/vdd: 3221 MB, 3221225472 bytes...1.2.2 虚机中设备的 device name 在虚机重启后发生改变比如通过以下步骤来重现:挂接 volume1 到 /dev/vda,挂接 volume2 到 /dev/vdb 将 volume1 下载 重新启动虚机 发现 volume2 的设备名称变为了 /dev/vda2. 原因分析2.1 QEMU/KVM Linux 客户机中的 virtio-blk 设备整个过程涉及到以下一些模块:QEMU/KVM 的 virtio-blk 的几个模块,这在前两篇文章中都有提到 udv 和 udev rules,下面会提到 /dev 目录2.2 Linux udev 和udev rulesudev 是 Linux 内核的设备管理器(device manager),它主要是负责管理 /dev 目录中的设备节点(device nodes)和 /dev/disk 子目录中的与设备 ID 相关的的符号连接文件。当新的硬件设备(hardware devices)加入系统或者从系统删除时,Linux 内核通过 netlink socket 通知 udev,然后 udev 根据存在的 udev rules 来做相应的处理,默认地,它会在 /dev 目录中创建或者删除设备节点。2.2.1 一个 virtio-blk 设备的示例从下面的输出可以看出,内核分配的 device name 为 vdb,它是一个 block 设备,其父设备为 virtio,驱动为 virtio-blk,它属于 pci 设备类别。[root@ap-4oe4-4rz25pvadal7-cs7nmsu4izfy-server-uplthyfxulbx ibmcloud]# udevadm info -a -n /dev/vdb looking at device '/devices/pci0000:00/0000:00:07.0/virtio3/block/vdb': KERNEL=='vdb' SUBSYSTEM=='block' DRIVER=='' ... looking at parent device '/devices/pci0000:00/0000:00:07.0/virtio3': KERNELS=='virtio3' SUBSYSTEMS=='virtio' DRIVERS=='virtio_blk' ... looking at parent device '/devices/pci0000:00/0000:00:07.0': KERNELS=='0000:00:07.0' SUBSYSTEMS=='pci' DRIVERS=='virtio-pci' ... looking at parent device '/devices/pci0000:00': KERNELS=='pci0000:00' SUBSYSTEMS=='' DRIVERS==''2.2.2 udev rulesudev rules 由在/lib/udev/rules.d 目录中的文件来定义。通过这些文件,你可以做到:修改一个 devide node 的名称 (Rename a device node from the default name to something else) Provide an alternative/persistent name for a device node by creating a symbolic link to the default device node Name a device node based on the output of a program 修改权限和所有权(Change permissions and ownership of a device node) 启动一个脚本(Launch a script when a device node is created or deleted (typically when a device is attached or unplugged)) 修改网卡名称(Rename network interfaces)可见,udev 在收到 linux 内核发来的消息后,首先会查找 udev rules:如果存在,则执行其中定义的操作;如果不存在,则执行默认的操作,即使用 linux kernel 分配的默认名称来创建一个 device node。关于 udev rules 的详细信息,以及如何创建新的 rules,可以参考http://www.reactivated.net/writing_udev_rules.html。2.2.3 udev 从 linux 内核收到的消息步骤:在虚机中运行udevadm monitor 命令 在 OpenStack 中运行 nova attach-volume 和 detach-volume 命令 在虚机的console 中就可以看到下面的输出#删除设备时KERNEL[1458809817.791365] remove /devices/virtual/bdi/252:48 (bdi)UDEV [1458809817.791426] remove /devices/virtual/bdi/252:48 (bdi)KERNEL[1458809817.791447] remove /devices/pci0000:00/0000:00:0a.0/virtio5/block/vdd (block)UDEV [1458809817.791485] remove /devices/pci0000:00/0000:00:0a.0/virtio5/block/vdd (block)KERNEL[1458809817.795016] remove /devices/pci0000:00/0000:00:0a.0/virtio5 (virtio)UDEV [1458809817.796617] remove /devices/pci0000:00/0000:00:0a.0/virtio5 (virtio)KERNEL[1458809817.796695] remove /devices/pci0000:00/0000:00:0a.0 (pci)KERNEL[1458809817.796871] remove /devices/LNXSYSTM:00/LNXSYBUS:00/PNP0A03:00/device:24 (acpi)UDEV [1458809817.797283] remove /devices/pci0000:00/0000:00:0a.0 (pci)UDEV [1458809817.797301] remove /devices/LNXSYSTM:00/LNXSYBUS:00/PNP0A03:00/device:24 (acpi)#添加设备时KERNEL[1458809874.181538] remove /devices/LNXSYSTM:00/LNXSYBUS:00/PNP0A03:00/device:0c (acpi)KERNEL[1458809874.182088] add /devices/LNXSYSTM:00/LNXSYBUS:00/PNP0A03:00/device:25 (acpi)UDEV [1458809874.182104] remove /devices/LNXSYSTM:00/LNXSYBUS:00/PNP0A03:00/device:0c (acpi)UDEV [1458809874.186103] add /devices/LNXSYSTM:00/LNXSYBUS:00/PNP0A03:00/device:25 (acpi)KERNEL[1458809874.234695] add /devices/pci0000:00/0000:00:0b.0 (pci)UDEV [1458809874.238377] add /devices/pci0000:00/0000:00:0b.0 (pci)KERNEL[1458809874.241057] add /devices/pci0000:00/0000:00:0b.0/virtio5 (virtio)UDEV [1458809874.241358] add /devices/pci0000:00/0000:00:0b.0/virtio5 (virtio)KERNEL[1458809874.242006] add /devices/virtual/bdi/252:48 (bdi)UDEV [1458809874.242370] add /devices/virtual/bdi/252:48 (bdi)KERNEL[1458809874.243944] add /devices/pci0000:00/0000:00:0b.0/virtio5/block/vdd (block)UDEV [1458809874.258996] add /devices/pci0000:00/0000:00:0b.0/virtio5/block/vdd (block)也可以看出,Linux 内核向 udev 传入了该 device 所使用的 device name。2.3 virtio-blk 驱动模块那现在要看的是,客户机的 linux 内核传给 udev 的device name 是它自己产生的,还是由 QEMU/KVM 传入的。2.3.1 虚机的 libvirt xml 文件中确实指定了 taget device name <disk type='network' device='disk'> <driver name='qemu' type='raw' cache='writeback'/> ... <target dev='vdb' bus='virtio'/> <serial>6e5424e4-4e4c-4178-a4cc-e63698716e9b</serial> <alias name='virtio-disk1'/> <address type='pci' domain='0x0000' bus='0x00' slot='0x06' function='0x0'/> </disk>2.3.2 虚机的 qemu 进程中没有该 dev 参数-drive file=rbd:default/volume-6e5424e4-4e4c-4178-a4cc-e63698716e9b:id=cinder:key=AQBM+qVWdTNYKhAAMwULMEcH7TOIcVNyKjIaIg==:auth_supported=cephx;none:mon_host=10.110.156.54:6789;10.110.156.55:6789;10.110.156.56:6789,if=none,id=drive-virtio-disk1,format=raw,serial=6e5424e4-4e4c-4178-a4cc-e63698716e9b,cache=writeback -device virtio-blk-pci,scsi=off,bus=pci.0,addr=0x6,drive=drive-virtio-disk1,id=virtio-disk1 其中,选项 '-device' 指定了前端的设备类型,而 '-drive' 选项定义了后端存储,并且通过设备的'drive'属性把设备和存储关联起来。这里没有看到 dev ='vdb' 相关的设置,初步推断,KVM 目前不支持执行虚机中的 device name(是不是结论,还需要进一步研究)。2.4 客户机linux 内核分配 device name 的方法2.4.1 major number 和 minor numberLinux 中,每个磁盘由一个 major number 和 一个 minor number 共同组成其 device name 来唯一标识它。以下图为例,/dev/vd{a,b,c,d}表示四个磁盘,/dev/vda1 表示磁盘 /dev/vda 的第一个分区。[root@ap-4oe4-4rz25pvadal7-cs7nmsu4izfy-server-uplthyfxulbx rules.d]# fdisk -lDisk /dev/vda: 42.9 GB, 42949672960 bytes... Device Boot Start End Blocks Id System/dev/vda1 * 6 238312 41941888 83 LinuxDisk /dev/vdb: 1073 MB, 1073741824 bytes...Disk /dev/vdc: 2147 MB, 2147483648 bytes...Disk /dev/vdd: 3221 MB, 3221225472 bytes...vda 设备的属性如下,其中可以看到 major number 和 minor number,以及 device name:[ibmcloud@ap-4oe4-4rz25pvadal7-cs7nmsu4izfy-server-uplthyfxulbx rules.d]$ sudo udevadm info --query=all --name=/dev/vdaP: /devices/pci0000:00/0000:00:04.0/virtio1/block/vdaN: vdaW: 4S: block/252:0S: disk/by-path/pci-0000:00:04.0-virtio-pci-virtio1E: UDEV_LOG=3E: DEVPATH=/devices/pci0000:00/0000:00:04.0/virtio1/block/vdaE: MAJOR=252E: MINOR=0E: DEVNAME=/dev/vdaE: DEVTYPE=diskE: SUBSYSTEM=blockE: ID_PATH=pci-0000:00:04.0-virtio-pci-virtio1E: ID_PART_TABLE_TYPE=dosE: LVM_SBIN_PATH=/sbinE: DEVLINKS=/dev/block/252:0 /dev/disk/by-path/pci-0000:00:04.0-virtio-pci-virtio1而 Linux 内核是在检测到每一个磁盘的时候来分配这两个数字的,包括系统启动和新设备加入以后,DEVNAME 和这两个数字是直接相关的。因此,每个disk 的 device name 不是持久的 (persistent),而是可变的。3. 解决办法3.1 在 KVM 环境中,在 nova attach-volume 命令中使用 ‘auto’,在一般情况下,会解决device name 不一致的问题比如nova volume-attach 9ca18b9e-aeef-44ff-81ba-87a59a0c8eec 8a309ad1-369c-483b-9a95-e4f330bb104e auto当使用 ‘auto’时,nova 会调用下面的方法来分配一个最小的可用 device name。因为它的分配方法和 linux 内核的分配方式一致,因此,当一个虚机的所有 device 都由 nova 来管理时,cinder 看到的 device name 和 虚机中的 device name 将是一致的。当然了,如果通过别的方法给虚机在挂接volume,两者又会出现不一致。最后的函数,会从 nova 自己维护的 device mapping list 中通过从头开始遍历来分配一个未使用的 device name:def find_disk_dev_for_disk_bus(mapping, bus, last_device=False, assigned_devices=None): '''Identify a free disk dev name for a bus. Determines the possible disk dev names for the bus, and then checks them in order until it identifies one that is not yet used in the disk mapping. If 'last_device' is set, it will only consider the last available disk dev name. Returns the chosen disk_dev name, or raises an exception if none is available. ''' dev_prefix = get_dev_prefix_for_disk_bus(bus) if dev_prefix is None: return None if assigned_devices is None: assigned_devices = [] max_dev = get_dev_count_for_disk_bus(bus) if last_device: devs = [max_dev - 1] else: devs = range(max_dev) for idx in devs: disk_dev = dev_prefix + chr(ord('a') + idx) if not has_disk_dev(mapping, disk_dev): if disk_dev not in assigned_devices: return disk_dev可以看出,nova 还是做了不少事情来尽量保证 device name 的一致性,它已经做了它该做的了,只是由于 KVM 不支持,它无法做到完美。3.2 自定义 udev rules,来根据 device 的属性来命名一个 deviceudevadm info 命令输出的各个参数,都可以作为 udev rules 的过滤条件来定位到某一个 device,并给它命名为一个非默认名字。3.3 不使用 device name,而使用 device IDLinux 系统中,udev 负责根据 udev rules 维护如下几个目录中的系统连接符:[ibmcloud@ap-4oe4-4rz25pvadal7-cs7nmsu4izfy-server-uplthyfxulbx ~]$ ls -l /dev/disktotal 0drwxr-xr-x 2 root root 100 Mar 27 07:16 by-iddrwxr-xr-x 2 root root 140 Mar 27 07:16 by-pathdrwxr-xr-x 2 root root 80 Mar 27 07:16 by-uuid3.3.1 UUID[ibmcloud@ap-4oe4-4rz25pvadal7-cs7nmsu4izfy-server-uplthyfxulbx ~]$ ls -l /dev/disk/by-uuid/total 0lrwxrwxrwx 1 root root 10 Mar 27 07:16 002f642d-f277-49fd-b5ff-47a652b63fe3 -> ../../vda1lrwxrwxrwx 1 root root 9 Mar 27 07:16 1655a8ca-9627-4a81-acca-79fec66e1131 -> ../../vdbUUID 是给每一个文件系统分配一个唯一标识符的机制。这些标识符是被文件系统工具在分区被格式化的时候产生的,比如 mkfs.*。3.3.2 ID[ibmcloud@ap-4oe4-4rz25pvadal7-cs7nmsu4izfy-server-uplthyfxulbx ~]$ ls -l /dev/disk/by-id/total 0lrwxrwxrwx 1 root root 9 Mar 27 07:16 virtio-6e5424e4-4e4c-4178-a -> ../../vdblrwxrwxrwx 1 root root 9 Mar 27 07:16 virtio-8a309ad1-369c-483b-9 -> ../../vddlrwxrwxrwx 1 root root 9 Mar 27 07:16 virtio-e4585a93-9a90-4967-8 -> ../../vdcID 是依赖于硬件的序列号(hardware serial number)产生的。该 ID 是持久的、与系统无关的、SCSI 标准强制要求的、与设备而不是其中保存的数据比如文件系统相关的 ID。当该设备是由 cinder volume 挂接而来时,可以看出 device id 和 volume id 的映射关系:root@hkg02kvm004ccz023:~# cinder list | grep 6e5424e4-4e4c-4178-a| 6e5424e4-4e4c-4178-a4cc-e63698716e9b | in-use | sammyvol3 | 2 | None | false | 9ca18b9e-aeef-44ff-81ba-87a59a0c8eec |这也证明了 ID 只和 device (volume)相关。查看/lib/udev/rules.d/60-persistent-storage.rules 文件中的 virtio-blk 部分:# virtio-blkKERNEL=='vd*[!0-9]', ATTRS{serial}=='?*', ENV{ID_SERIAL}='$attr{serial}', SYMLINK+='disk/by-id/virtio-$env{ID_SERIAL}'KERNEL=='vd*[0-9]', ATTRS{serial}=='?*', ENV{ID_SERIAL}='$attr{serial}', SYMLINK+='disk/by-id/virtio-$env{ID_SERIAL}-part%n'比较奇怪的是,vda 怎么没出现在列表中。vda 是宿主机上一个镜像文件挂接而来的:<disk type='file' device='disk'> <driver name='qemu' type='qcow2' cache='none'/> <source file='/opt/stack/data/nova/instances/9ca18b9e-aeef-44ff-81ba-87a59a0c8eec/disk'/> <target dev='vda' bus='virtio'/> <alias name='virtio-disk0'/> <address type='pci' domain='0x0000' bus='0x00' slot='0x04' function='0x0'/> </disk>它的 udev 属性中 ID_SERIAL 的值为空 (ATTR{serial}=='') (sudo udevadm info --query=all --name=/dev/vda)。这是为什么呢?需要进一步研究。3.3.3 path[ibmcloud@ap-4oe4-4rz25pvadal7-cs7nmsu4izfy-server-uplthyfxulbx ~]$ ls -l /dev/disk/by-path/total 0lrwxrwxrwx 1 root root 9 Mar 27 07:16 pci-0000:00:04.0-virtio-pci-virtio1 -> ../../vdalrwxrwxrwx 1 root root 10 Mar 27 07:16 pci-0000:00:04.0-virtio-pci-virtio1-part1 -> ../../vda1lrwxrwxrwx 1 root root 9 Mar 27 07:16 pci-0000:00:06.0-virtio-pci-virtio3 -> ../../vdblrwxrwxrwx 1 root root 9 Mar 27 07:16 pci-0000:00:07.0-virtio-pci-virtio4 -> ../../vdclrwxrwxrwx 1 root root 9 Mar 27 07:16 pci-0000:00:08.0-virtio-pci-virtio5 -> ../../vdd该 path 和设备的最短物理访问路径(shortest physical path)有关,包括 SCSI host, channel, target, LUN numbers 以及可能得 partition number.等。需要注意的是,这里的硬件访问路径有可能变化,比如 pci slot 改变后。看看 path 相关的 udev rules:# by-path (parent device path)ENV{DEVTYPE}=='disk', ENV{ID_PATH}=='', IMPORT{program}='path_id %p'ENV{DEVTYPE}=='disk', ENV{ID_PATH}=='?*', SYMLINK+='disk/by-path/$env{ID_PATH}'ENV{DEVTYPE}=='partition', ENV{ID_PATH}=='?*', SYMLINK+='disk/by-path/$env{ID_PATH}-part%n'


(以上内容不代表本站观点。)
---------------------------------
本网站以及域名有仲裁协议。
本網站以及域名有仲裁協議。

2024-Mar-04 02:09pm
栏目列表