删除 OpenStack Nova Volume 时遇到的 error_deleting 问题

前段时间 VPSee 在 OpenStack Nova 上删除一个 volume 的时候(vol-00000002)报错,检查了一下 volume 的状态是 error_deleting 然后就无法删除了,不管用 euca-delete-volume 还是 nova-manage volume delete 都无法删除。

# euca-delete-volume vol-00000002
ApiError: ApiError: Volume status must be available

# euca-describe-volumes 
VOLUME	vol-00000002  10  nova	error_deleting (mycloud, node01, None, None)	2011-08-30T13:15:24Z
VOLUME	vol-00000003  10  nova	available (mycloud, node01, None, None)	2011-08-30T13:20:04Z

查了一下要删除的 volume-00000002 的情况:

# lvdisplay
...
--- Logical volume ---
  LV Name                /dev/nova-volumes/volume-00000002
  VG Name                nova-volumes
  LV UUID                UgOwdr-W61E-MrdG-PrdY-IToa-LBi8-2XJjXF
  LV Write Access        read/write
  LV Status              available
  # open                 0
  LV Size                10.00 GiB
  Current LE             2560
  Segments               1
  Allocation             inherit
  Read ahead sectors     auto
  - currently set to     256
  Block device           251:2
...

既然用工具没法删除的话就干脆从数据库里直接删除,无非就是一条记录而已,先从 nova 数据库里找到这条 volume 记录并设置状态为 deleted,然后删除实际对应的 LVM. 从数据库里找到相应的表和记录后设置 status 为 deleted 状态:

# mysql -u root -p
Enter password:

mysql> use nova;

mysql> select status,id from volumes;
+----------------+----+
| status         | id |
+----------------+----+
| creating       |  1 |
| error_deleting |  2 |
| available      |  3 |
+----------------+----+
3 rows in set (0.00 sec)

mysql> update volumes set status='deleted' where id='2';
Query OK, 1 row affected (0.00 sec)
Rows matched: 1  Changed: 1  Warnings: 0

退出 mysql 数据库操作回到命令行就会看到 vol-00000002 的状态是 deleted 的,然后用 lvmremove 命令删除:

# euca-describe-volumes 
VOLUME	vol-00000002  10  nova	deleted (mycloud, node01, None, None)	2011-08-30T13:15:24Z

# ls /dev/nova-volumes/
volume-00000002 volume-00000003

# lvremove /dev/nova-volumes/volume-00000002 
Do you really want to remove active logical volume volume-00000002? [y/n]: y
  Logical volume "volume-00000002" successfully removed

最后用 nova-manage 命令从数据库里彻底删除 vol-00000002(当然也可以直接在 mysql 里用 SQL 语句操作删除对应的记录 DELETE FROM volumes WHERE id=’2′;):

# nova-manage volume delete vol-00000002

# euca-describe-volumes 
VOLUME	vol-00000003  10  nova	available (mycloud, node01, None, None)	2011-08-30T13:20:04Z

在 Debian 上安装 Xen 虚拟机集群管理工具 Ganeti

随着 Xen 服务器的增多,管理起来会越来越麻烦,如果有一些集成化的辅助工具来统一管理所有的服务器、虚拟机以及集中分配计算资源将会很方便。不过 VPSee 一般都避免使用一些太智能的工具,智能化自动工具有时候太智能,而且很难理解,特别是遇到问题的时候很难排错,因为自动和智能掩盖了工具背后的原理和过程,不知道是工具错了还是自己用错了。市面上有一些结合虚拟技术面向云计算的 Xen/KVM 管理工具,比如 Eucalyptus, OpenNebula, OpenQRM 等用来统一部署和管理虚拟环境,Ganeti 虽然没有前面几个强大,也勉强算一员。Ganeti 最初是由 Google 瑞士苏黎世 office 的一小撮人开发的 Xen/KVM 虚拟机集群管理工具,用来管理内部一些由廉价计算机组成的虚拟服务器集群,廉价计算机集群是 Google 的特色之一

详细一点说,Ganeti 是一个基于 Xen 或 KVM 虚拟技术的虚拟集群环境管理工具,用来管理一堆运行 Xen 服务器的统一管理界面,这样创建、删除、启动、关闭、重装、查看、移植虚拟机就用不着逐个去某台 Xen 服务器上操作,整合了整个虚拟操作环境,统一管理所有硬盘、操作系统、内存等资源,简化了操作,显然 Ganeti 需要运行在 Xen/KVM 平台上。把 Ganeti, Xen/KVM, LVM, DRBD, RAID10 等几个技术整合起来可以架设一个云计算基础环境,配上一个基于 Web 的控制面板和计费系统就可以当作 “云 VPS” 拿来忽悠。

安装和配置基本系统

首先在每个 node(node1, node2, …)上都安装上最基本的 Debian 系统(Ganeti 官方推荐1个集群里的 node 数目不要超过40个),VPSee 建议在所有 node 上一致采用最小化安装并使用最简单的分区方式:使用 sda1 做 /(10GB),使用 sda2 做 swap(2GB),剩余的空间将会在下面的步骤用到。安装完后开始对每个 node 都做如下类似配置,以 node1 为例,每个 node 上都需要配置 /etc/hosts,Ganeti 就是靠这里的 hostname 来识别 node 的,所以这一步很重要,设置好 hostname 后用 hostname -f 测试一下是否正确:

# vi /etc/hosts

127.0.0.1       localhost.localdomain   localhost
172.16.39.10    cluster1.vpsee.com      cluster1
172.16.39.11	node1.vpsee.com node1
172.16.39.12	node2.vpsee.com node2
172.16.39.101   inst1.vpsee.com         inst1

# hostname -f
node1.vpsee.com

如果 node1 上的 hostname 不正确可以修正:

# vi /etc/hostname
node1.vpsee.com
# /etc/init.d/hostname.sh start

接下来需要给每个 node 配置一个静态 IP,需要注意的是 Ganeti 在默认 bridge 模式下会是用 xen-br0 这个名字,所以我们需要在每个 node 上建立这个 bridge:

# vi /etc/network/interfaces 
auto lo
iface lo inet loopback

auto eth0
iface eth0 inet static
    address 172.16.39.11
    netmask 255.255.254.0
    network 172.16.38.0
    broadcast 172.16.39.255
    gateway 172.16.38.1
auto xen-br0
iface xen-br0 inet static
    address 172.16.39.11
    netmask 255.255.254.0
    network 172.16.38.0
    broadcast 172.16.39.255
    gateway 172.16.38.1
    bridge_ports eth0
    bridge_stp off
    bridge_fd 0

需要注意的是,要对每个想加入 Ganeti 的 node 做相应的配置和调整,比如 IP,hostname 等。在 node2, node3, …, nodeN 上重复上面的步骤。

安装和配置 LVM

这一步骤也需要在每个 node 上重复。先检查分区,在 sda3 上分区,分区格式是 LVM,每个 node 上的这个区将被拿来做 Ganeti 的存储池(storage pool)用来存储 Xen 虚拟机镜像(要注意的是记得在安装 debian 的是后就留出大部分空间来做 sda3):

$ sudo fdisk -l
...
   Device Boot      Start         End      Blocks   Id  System
/dev/sda1   *           1        1216     9764864   83  Linux
/dev/sda2            1216        1703     3906560   82  Linux swap / Solaris

$ sudo fdisk /dev/sda

n p 3 enter enter t 3 L 8e w

$ sudo fdisk -l
...
   Device Boot      Start         End      Blocks   Id  System
/dev/sda1   *           1        1216     9764864   83  Linux
/dev/sda2            1216        1703     3906560   82  Linux swap / Solaris
/dev/sda3            1703       60801   474711584+  8e  Linux LVM

安装 LVM 工具包,并用 LVM 工具创建物理卷和 LVM 卷组:

# apt-get install lvm2

$ sudo pvcreate /dev/sda3
  Physical volume "/dev/sda3" successfully created

$ sudo vgcreate xenvg /dev/sda3
  Volume group "xenvg" successfully created

重启系统以便刚创建的 LVM 生效:

# shutdown -r now

安装和配置 Xen 系统

按照 “在 Debian 上安装 Xen” 的步骤安装 Xen:

# apt-get install xen-hypervisor-3.2-1-amd64 \
xen-linux-system-2.6.26-2-xen-amd64 xen-utils-3.2-1 \
xenstore-utils xenwatch xen-shell xen-tools

安装和配置 Ganeti

可以从 Ganeti 官方网站下载源代码编译安装,为了少给自己添麻烦避免一些未知的编译、安装错误,建议新手直接从 debian 源安装:

# apt-get install ganeti

开始使用 Ganeti

在给集群(cluster)增加结点(node)之前需要先初始化这个集群,指定谁是老大(master node)。所以首先用 gnt-cluster 初始化一个 node (node1),这个 node1 将成为 master node,登陆到 node1 上,在 node1 上执行以下操作:

# gnt-cluster init node1

# gnt-node list
Node            DTotal  DFree MTotal MNode MFree Pinst Sinst
node1.vpsee.com 454044 454044   3893  3686   128     0     0

有了 master node (node1) 后,这个 master node 就是老大,用来管理其他的 slave nodes (node2, node3, …, nodeN) ,所有操作都要在 master node 上执行。现在我们可以在这个集群中加入 node2:

# gnt-node add node2.vpsee.com

# gnt-node list
Node            DTotal  DFree MTotal MNode MFree Pinst Sinst
node1.vpsee.com 454044 454044   3893  3686   128     0     0
node2.vpsee.com  37440  37440   2035  1024   969     0     0

可以看到这个集群已经统一管理 node1, node2 以及 node1 和 node2 上的资源,比如内存、硬盘等。现在我们终于可以开始创建虚拟机了,我们只需要在 master node (node1) 上执行安装虚拟机的命令就可以自动部署到集群中:

# gnt-instance add -t plain -n node2.vpsee.com -o debootstrap -s 5g \
--swap-size 256 -m 256 --kernel /boot/vmlinuz-`uname -r` \
--ip 172.16.39.101 inst1.vpsee.com
* creating instance disks...
adding instance inst1.vpsee.com to cluster config
 - INFO: Waiting for instance inst1.vpsee.com to sync disks.
 - INFO: Instance inst1.vpsee.com's disks are in sync.
creating os for instance inst1.vpsee.com on node node2.vpsee.com
* running the instance OS create scripts...
...

要重装虚拟机的话,执行 reinstall:

# gnt-instance reinstall inst1.vpsee.com
This will reinstall the instance inst1.vpsee.com and remove all data.
Continue?
y/[n]/?: y
Running the instance OS create scripts...

查看正在运行的虚拟机(instance):

# gnt-instance list
Instance          OS          Primary_node      Status  Memory
inst1.vpsee.com debootstrap node1.vpsee.com running    256

Ganeti 结合 DRBD 后还可以实现冗余、备份、在不同 node 之间自由迁移等功能,比如一个 node 挂了另一个 node 自动接替,某个 node 负载太高把上面的一个或几个 instance(虚拟机)暂时或永久迁移到另一个 node 降低单一 node 的负载等,非常酷。更多参考资料、帮助文档和命令行参数解释请看 Ganeti 的官方文档

挂载虚拟机镜像文件里的 LVM 逻辑分区

如果按照 “在 CentOS 上安装和配置 KVM” 这篇文章介绍的方法安装 guest 操作系统到一个 raw 文件里(virt-install … –disk path=/home/vpsee/centos.img …),那么在以后的维护过程中就可能会遇到麻烦。比如,前段时间 VPSee 碰到一位用户忘了 root 密码需要挂载这个虚拟机的文件系统并恢复 shadow,这时候就需要能够挂载虚拟机的文件系统。虚拟机的文件系统在一个 raw 文件上,这个镜像文件完全模拟了一个硬盘,包含硬盘分区表和 LVM 逻辑卷,所以不能通过 mout 简单挂载,需要一些额外的工作,那么如何才能挂载这个镜像文件里的 LVM 逻辑分区呢?

首先用 losetup 工具把 centos.img 文件和 loop 设备映射起来,-f 参数用来找出下一个可用的 loop 设备:

# losetup -f
/dev/loop0

# losetup /dev/loop0 /home/vpsee/centos.img

然后通过 fdisk /dev/loop0 来查看 centos.img 里的分区表,-u 参数指明使用 sector 为单位记录偏移量(offset)而不是 cylinder 为单位:

# fdisk -u -l /dev/loop0

Disk /dev/loop0: 10.7 GB, 10737418240 bytes
255 heads, 63 sectors/track, 1305 cylinders, total 20971520 sectors
Units = sectors of 1 * 512 = 512 bytes

      Device Boot      Start         End      Blocks   Id  System
/dev/loop0p1   *          63      208844      104391   83  Linux
/dev/loop0p2          208845    20964824    10377990   8e  Linux LVM

因为每个 sector 是 512 字节(bytes),所以 offset 等于 208845 X 512 = 106928640,这个 offset 很重要,在下面的操作中会用到。

先解除掉 /dev/loop0 和 centos.img 的映射,然后重新用 offset = 106928640 映射起来:

# losetup -d /dev/loop0
# losetup /dev/loop0 /home/vpsee/centos.img -o 106928640

现在可以看到系统上有2个 PV(物理卷组),一个是系统本身,一个是 centos.img 里面的 PV,这两个 PV 的名字都是一样的,是因为当初安装系统的时候都使用了默认的名字,这带来了麻烦,后面的操作会改变一个 PV 的名字以方便后续操作:

# lvm pvscan
  PV /dev/sda2    VG VolGroup00   lvm2 [465.66 GB / 0    free]
  PV /dev/loop0   VG VolGroup00   lvm2 [9.88 GB / 0    free]
  Total: 2 [475.53 GB] / in use: 2 [475.53 GB] / in no VG: 0 [0   ]

# lvm vgchange -ay
  2 logical volume(s) in volume group "VolGroup00" now active
  2 logical volume(s) in volume group "VolGroup00" now active

因为要给两个相同名字的 VolGroup00 改名,所以要先找到他们不同的 UUID:

# vgs -v
    Finding all volume groups
    Finding volume group "VolGroup00"
    Finding volume group "VolGroup00"
  VG         Attr   Ext    #PV #LV #SN VSize   VFree VG UUID                               
  VolGroup00 wz--n- 32.00M   1   2   0 465.66G    0  dqfs1x-QBY1-kNRr-l0X0-RnoW-GgIR-ZfjkCS
  VolGroup00 wz--n- 32.00M   1   2   0   9.88G    0  XrQNej-Aikn-qjxy-q7Lf-mBDk-gSfs-bLEgT6

上面第2个是 centos.img 里面的 VolGroup00(9.88GB 那个),改名为 vps01:

# vgrename XrQNej-Aikn-qjxy-q7Lf-mBDk-gSfs-bLEgT6 vps01
  Volume group "VolGroup00" successfully renamed to "vps01"

根据卷组名字和空间大小我们可以判断我们需要挂载 LogVol00 vps01(LogVol01 vps01 是 swap):

# lvm lvs
  LV       VG         Attr   LSize   Origin Snap%  Move Log Copy%  Convert
  LogVol00 VolGroup00 -wi-ao 460.03G                                      
  LogVol01 VolGroup00 -wi-ao   5.62G                                      
  LogVol00 vps01      -wi---   8.88G                                      
  LogVol01 vps01      -wi---   1.00G

现在终于可以挂载 centos.img 里的 LVM 逻辑分区了:

# mount /dev/mapper/vps01-LogVol00 /mnt

# ls /mnt
bin   dev  home  lib64       media  opt   root  selinux  sys  usr
boot  etc  lib   lost+found  mnt    proc  sbin  srv      tmp  var

使用完后,需要按顺序干净卸载:

# umount /mnt
# vgchange -an vps01
  0 logical volume(s) in volume group "vps01" now active
# losetup -d /dev/loop0

延伸阅读:如何挂载虚拟机镜像文件里的普通分区?