本系列文章会总结 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-
vm$ mkfs.ext4 /dev/disk/by-id/virtio-15a9f901-ba9d-45e1-
vm$ mkdir -p /mnt/volume
vm$ mount /dev/disk/by-id/virtio-15a9f901-ba9d-45e1- /mnt/volume
vm$ 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-attach
usage: 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 -l

Disk /dev/vda: 42.9 GB,  bytes
...
Disk /dev/vdb: MB, bytes
...
Disk /dev/vdc: MB, bytes
...
Disk /dev/vdd: 3221 MB, 3221225472 bytes
...

1.2.2 虚机中设备的 device name 在虚机重启后发生改变

比如通过以下步骤来重现:

  1. 挂接 volume1 到 /dev/vda,挂接 volume2 到 /dev/vdb
  2. 将 volume1 下载
  3. 重新启动虚机
  4. 发现 volume2 的设备名称变为了 /dev/vda

2. 原因分析

2.1 QEMU/KVM Linux 客户机中的 virtio-blk 设备

整个过程涉及到以下一些模块:

  • QEMU/KVM 的 virtio-blk 的几个模块,这在前两篇文章中都有提到
  • udv 和 udev rules,下面会提到
  • /dev 目录

2.2 Linux udev 和 udev rules

udev 是 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 rules

udev 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 内核收到的消息

步骤:

  1. 在虚机中运行 udevadm monitor 命令
  2. 在 OpenStack 中运行 nova attach-volume 和 detach-volume 命令
  3. 在虚机的console 中就可以看到下面的输出
#删除设备时
KERNEL[1458809817.791365] remove /devices/virtual/bdi/: (bdi)
UDEV [1458809817.791426] remove /devices/virtual/bdi/: (bdi)
KERNEL[1458809817.791447] remove /devices/pci0000:/::0a./virtio5/block/vdd (block)
UDEV [1458809817.791485] remove /devices/pci0000:/::0a./virtio5/block/vdd (block)
KERNEL[1458809817.795016] remove /devices/pci0000:/::0a./virtio5 (virtio)
UDEV [1458809817.796617] remove /devices/pci0000:/::0a./virtio5 (virtio)
KERNEL[1458809817.796695] remove /devices/pci0000:/::0a. (pci)
KERNEL[1458809817.796871] remove /devices/LNXSYSTM:/LNXSYBUS:/PNP0A03:/device: (acpi)
UDEV [1458809817.797283] remove /devices/pci0000:/::0a. (pci)
UDEV [1458809817.797301] remove /devices/LNXSYSTM:/LNXSYBUS:/PNP0A03:/device: (acpi) #添加设备时
KERNEL[1458809874.181538] remove /devices/LNXSYSTM:/LNXSYBUS:/PNP0A03:/device:0c (acpi)
KERNEL[1458809874.182088] add /devices/LNXSYSTM:/LNXSYBUS:/PNP0A03:/device: (acpi)
UDEV [1458809874.182104] remove /devices/LNXSYSTM:/LNXSYBUS:/PNP0A03:/device:0c (acpi)
UDEV [1458809874.186103] add /devices/LNXSYSTM:/LNXSYBUS:/PNP0A03:/device: (acpi)
KERNEL[1458809874.234695] add /devices/pci0000:/::0b. (pci)
UDEV [1458809874.238377] add /devices/pci0000:/::0b. (pci)
KERNEL[1458809874.241057] add /devices/pci0000:/::0b./virtio5 (virtio)
UDEV [1458809874.241358] add /devices/pci0000:/::0b./virtio5 (virtio)
KERNEL[1458809874.242006] add /devices/virtual/bdi/: (bdi)
UDEV [1458809874.242370] add /devices/virtual/bdi/: (bdi)
KERNEL[1458809874.243944] add /devices/pci0000:/::0b./virtio5/block/vdd (block)
UDEV [1458809874.258996] add /devices/pci0000:/::0b./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--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--a4cc-e63698716e9b:id=cinder:key=AQBM+qVWdTNYKhAAMwULMEcH7TOIcVNyKjIaIg==:
auth_supported=cephx\;none:mon_host=10.110.156.54\:\;10.110.156.55\:\;10.110.156.56\:,if=none,id=drive-virtio-disk1,
format=raw,serial=6e5424e4-4e4c--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 number

Linux 中,每个磁盘由一个 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 -l

Disk /dev/vda: 42.9 GB,  bytes
...
Device Boot Start End Blocks Id System
/dev/vda1 * Linux Disk /dev/vdb: MB, bytes
... Disk /dev/vdc: MB, bytes
... Disk /dev/vdd: MB, bytes
...

vda 设备的属性如下,其中可以看到 major number 和 minor number,以及 device name:

[ibmcloud@ap-4oe4-4rz25pvadal7-cs7nmsu4izfy-server-uplthyfxulbx rules.d]$ sudo udevadm info --query=all --name=/dev/vda
P: /devices/pci0000:/::04.0/virtio1/block/vda
N: vda
W:
S: block/252:0
S: disk/by-path/pci-::04.0-virtio-pci-virtio1
E: UDEV_LOG=
E: DEVPATH=/devices/pci0000:/::04.0/virtio1/block/vda
E: MAJOR=252
E: MINOR=0
E: DEVNAME=/dev/vda
E: DEVTYPE=disk
E: SUBSYSTEM=block
E: ID_PATH=pci-::04.0-virtio-pci-virtio1
E: ID_PART_TABLE_TYPE=dos
E: LVM_SBIN_PATH=/sbin
E: DEVLINKS=/dev/block/: /dev/disk/by-path/pci-::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 - ]
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 的属性来命名一个 device

udevadm info 命令输出的各个参数,都可以作为 udev rules 的过滤条件来定位到某一个 device,并给它命名为一个非默认名字。

3.3 不使用 device name,而使用 device ID

Linux 系统中,udev 负责根据 udev rules 维护如下几个目录中的系统连接符:

[ibmcloud@ap-4oe4-4rz25pvadal7-cs7nmsu4izfy-server-uplthyfxulbx ~]$ ls -l /dev/disk
total
drwxr-xr-x root root Mar : by-id
drwxr-xr-x root root Mar : by-path
drwxr-xr-x root root Mar : by-uuid

3.3.1 UUID

[ibmcloud@ap-4oe4-4rz25pvadal7-cs7nmsu4izfy-server-uplthyfxulbx ~]$ ls -l /dev/disk/by-uuid/
total
lrwxrwxrwx root root Mar : 002f642d-f277-49fd-b5ff-47a652b63fe3 -> ../../vda1
lrwxrwxrwx root root Mar : 1655a8ca--4a81-acca-79fec66e1131 -> ../../vdb

UUID 是给每一个文件系统分配一个唯一标识符的机制。这些标识符是被文件系统工具在分区被格式化的时候产生的,比如 mkfs.*。

3.3.2 ID

[ibmcloud@ap-4oe4-4rz25pvadal7-cs7nmsu4izfy-server-uplthyfxulbx ~]$ ls -l /dev/disk/by-id/
total 0
lrwxrwxrwx 1 root root 9 Mar 27 07:16 virtio-6e5424e4-4e4c-4178-a -> ../../vdb
lrwxrwxrwx 1 root root 9 Mar 27 07:16 virtio-8a309ad1-369c-483b-9 -> ../../vdd
lrwxrwxrwx 1 root root 9 Mar 27 07:16 virtio-e4585a93-9a90-4967-8 -> ../../vdc

ID 是依赖于硬件的序列号(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 | | None | false | 9ca18b9e-aeef-44ff-81ba-87a59a0c8eec |

这也证明了 ID 只和 device (volume)相关。

查看 /lib/udev/rules.d/60-persistent-storage.rules 文件中的 virtio-blk 部分:

# virtio-blk
KERNEL=="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
lrwxrwxrwx root root Mar : pci-::04.0-virtio-pci-virtio1 -> ../../vda
lrwxrwxrwx root root Mar : pci-::04.0-virtio-pci-virtio1-part1 -> ../../vda1
lrwxrwxrwx root root Mar : pci-::06.0-virtio-pci-virtio3 -> ../../vdb
lrwxrwxrwx root root Mar : pci-::07.0-virtio-pci-virtio4 -> ../../vdc
lrwxrwxrwx root root Mar : pci-::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"
												

理解 QEMU/KVM 和 Ceph(3):存储卷挂接和设备名称的更多相关文章

  1. 理解 QEMU/KVM 和 Ceph(2):QEMU 的 RBD 块驱动(block driver)

    本系列文章会总结 QEMU/KVM 和 Ceph 之间的整合: (1)QEMU-KVM 和 Ceph RBD 的 缓存机制总结 (2)QEMU 的 RBD 块驱动(block driver) (3)存 ...

  2. 理解 QEMU/KVM 和 Ceph(1):QEMU-KVM 和 Ceph RBD 的 缓存机制总结

    本系列文章会总结 QEMU/KVM 和 Ceph 之间的整合: (1)QEMU-KVM 和 Ceph RBD 的 缓存机制总结 (2)QEMU 的 RBD 块驱动(block driver) (3)存 ...

  3. k8s之存储卷及pvc

    1.存储卷概述 因为pod是有生命周期的,pod一重启,里面的数据就没了,所以我们需要数据持久化存储,在k8s中,存储卷不属于容器,而是属于pod,也就是说同一个pod中的容器可以共享一个存储卷,存储 ...

  4. 部署ceph存储集群及块设备测试

    集群环境 配置基础环境 添加ceph.repo wget -O /etc/yum.repos.d/ceph.repo https://raw.githubusercontent.com/aishang ...

  5. k8s-数据持久化存储卷,nfs,pv/pvc

    目录 数据持久化-储存卷 官方文档 存储卷类型 1.emptyDir 2.hostpath 3.pv/pvc(推荐使用) nfs官方文档 安装测试nfs pv/pvc管理nfs 官方文档 pv/pvc ...

  6. KVM初探之一--存储池与存储卷,使用virt-install创建虚拟机

    在机器上装了一个Centos6.2用来测试KVM,对比Windows下的VMware worstation,KVM功能强大,定制性高,只要入了门,绝对是功能强大.用来作云计算不错.不过就是操作性差了点 ...

  7. SUSE CaaS Platform 4 - Ceph RBD 作为 Pod 存储卷

    RBD存储卷 目前 CaaSP4 支持多种 Volume 类型,这里选择 Ceph RBD(Rados Block Device),主要有如下好处: Ceph 经过多年开发,已经非常熟,社区也很活跃: ...

  8. 理解 Linux 网络栈(3):QEMU/KVM + VxLAN 环境下的 Segmentation Offloading 技术(发送端)

    本系列文章总结 Linux 网络栈,包括: (1)Linux 网络协议栈总结 (2)非虚拟化Linux环境中的网络分段卸载技术 GSO/TSO/UFO/LRO/GRO (3)QEMU/KVM + Vx ...

  9. KVM 介绍(8):使用 libvirt 迁移 QEMU/KVM 虚机和 Nova 虚机 [Nova Libvirt QEMU/KVM Live Migration]

    学习 KVM 的系列文章: (1)介绍和安装 (2)CPU 和 内存虚拟化 (3)I/O QEMU 全虚拟化和准虚拟化(Para-virtulizaiton) (4)I/O PCI/PCIe设备直接分 ...

随机推荐

  1. .Net中的并行编程-6.常用优化策略

                本文是.Net中的并行编程第六篇,今天就介绍一些我在实际项目中的一些常用优化策略.      一.避免线程之间共享数据 避免线程之间共享数据主要是因为锁的问题,无论什么粒度的锁 ...

  2. web桌面程序之图标拖动排序的分析

    在web桌面程序里,图标拖动并重新排序是个比较常见的功能.这个功能我之前反复修改了好几遍,现在终于整理出了比较理想的解决思路,决定拿出来分享下. 这一功能主要有哪些难点呢?我总结了一下一共有2处难点: ...

  3. IClient for js开发之地图的加载

    进行web开发之前首先需要安装IServer以及iClient for JavaScript的开发包.在这两中都具备的前提下进行第一步,如何调用IServer中发布的服务 调用iServer 中发布的 ...

  4. VisualCaptcha – 灵活的可视化验证码解决方案

    visualCaptcha 是一个可配置的验证码解决方案,专注于可访问性和简单性,同时保持安全性.它也支持移动,视网膜设备,并有一个创新的可访问性的解决方案. visualCaptcha 现在可以跨多 ...

  5. windows10 网络热点

    (1)快捷键 :win + x (2)判断自己的电脑是否支持无线接口,输入以下命令,会出现提示是否支持无线接口,如果不支持,请不要往下看了.. netsh wlan show drivers (3)若 ...

  6. HTML中tr标签设置边框不显示的解决办法

    今天在操作表格的时候发现设置表格中行的边框没有显示,然后自己新建了一个表格发现确实不显示 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Tr ...

  7. 学习笔记——关于HTML(含HTML5)的块级元素和行级(内联)元素总结

    1.首先我们要知道什么是块级元素和行级(内联)元素? 块级(block)元素的特点: ①总是在新行上开始: ②高度,行高以及外边距和内边距都可控制: ③宽度缺省是它的容器的100%,除非设定一个宽度: ...

  8. firefox中flash经常崩溃

    建议: 1.安装flashblck插件 2.添加配置文件 在C:\Windows\SysWOW64\Macromed\Flash添加mmc.cfg. mmc.cfg的内容: SlientAutoUpd ...

  9. Autodesk 最新开发技术研讨会-北京-上海-武汉-成都-西安-PPT下载

    经过半月的奔波,转遍祖国大好河山.结论,还是喜欢成都,安逸~,好希望能在成都生活,竹林里品茶,不亦乐乎~ 如果你没能参加我们的研讨会,下面的PPT供参考: 面向世界.面向未来 – Autodesk 云 ...

  10. 一文让你彻底了解iOS字体相关知识

    写本文的契机主要是把自己整理的关于iOS字体方面的知识不断更新写在这篇博文中,用来自己以后查阅. 一.iOS原生字体展示 在label中选择字体的font,并把font由system改成custom后 ...