在 CentOS 5.5 上升级 Xen 到 Xen 3.4.3

2008年 RedHat 收购 Qumranet 以后就一直在家搞他的 KVM,没有对 Xen 做任何升级,RHEL/CentOS 5.5 上默认的 Xen 依旧是很老很老的公元2007年发布的 Xen 3.1.2 版本。更糟糕的是 RedHat 在后续的 RedHat Enterprise Linux 6 里彻底放弃了 Xen. 如果以后想在新版本的 RHEL/CentOS 上用 Xen 的话就需要使用第三方源或者自己动手编译 Xen 源代码。自己编译源代码问题多多,看看前面那篇文章留下的150多个评论就知道会遇到多少麻烦了,所以没有特殊要求的话,VPSee 还是建议直接使用第三方源。

# cd /etc/yum.repos.d/
# wget http://www.gitco.de/repo/GITCO-XEN3.4.3_x86_64.repo
# yum update
# reboot

如果上面的 yum update 没问题的话重启系统就可以了进入 xen 3.4.3 了,如果有问题导致一些依赖需要解决的话可以彻底删除 xen 后重新升级安装:

# yum groupremove Virtualization
# yum groupinstall Virtualization
# reboot

在重启之前最好检查一下 grub 是否配置正确、默认启动是否是 xen.gz-3.4.3:

# vi /boot/grub/menu.lst
...
title CentOS (2.6.18-194.26.1.el5xen)
        root (hd0,0)
        kernel /xen.gz-3.4.3
        module /vmlinuz-2.6.18-194.26.1.el5xen ro root=LABEL=/
        module /initrd-2.6.18-194.26.1.el5xen.img
...

在 NetBSD 上安装和配置 Xen

到目前为止,VPSee 已经陆续介绍了:在 CentOS 上安装和配置 Xen在 Ubuntu 上安装和配置 Xen在 Debian 上安装和配置 Xen在 OpenSolaris 上安装和配置 Xen,加上这篇 “在 NetBSD 上安装和配置 Xen” 就完整了,这是我们目前所有应用在生产环境的 Xen 系统。有人可能会问为什么要用这么多种系统?管理起来不是更复杂吗?其实没想像的那么复杂,我们实验室应用的虚拟机生产环境和我们销售的 VPS 不同,因为可以不考虑成本,所以采用了昂贵的存储设备,虚拟机都是存储在专门的 iSCSI 存储系统上,Xen 服务器只不过是一个 node 用来连接和启动存储网络上的虚拟机镜像,所以使用什么系统做 node 不是很重要,事实上 NetBSD 是这几个系统里面最容易配置和安装的系统,没有 trouble shooting,没有 work around,安装即成功,非常的赞!以下操作在 NetBSD 5.0.2 上完成。

安装 Xen

在 NetBSD 上使用 pkg_add 来安装软件包,相当于 CentOS/Fedora 的 yum 和 Ubuntu/Debian 的 apt-get,如果在公司或者学校使用代理上网的话需要先配置代理服务器:

# vi .kshrc
...
PKG_PATH=http://ftp.us.netbsd.org/pub/pkgsrc/packages/NetBSD/ \
amd64/5.0.2_2010Q3/All

export HTTP_PROXY=http://username:[email protected]:3128/
export FTP_PROXY=http://username:[email protected]:3128/

安装 xen hypervisor 和 xen tools:

# pkg_add -v xenkernel33
# pkg_add -v python26
# pkg_add -v xentools33
# pkg_add -v e2fsprogs

在上面的步骤安装 xentools33 的是后屏幕会打印提示需要从 /usr/pkg/share/examples/rc.d/ 拷贝一些配置文件到 /etc/rc.d 以便开机启动后自动运行:

# cd /etc/rc.d
# cp /usr/pkg/share/examples/rc.d/xendomains .
# cp /usr/pkg/share/examples/rc.d/xend .
# cp /usr/pkg/share/examples/rc.d/xenbackendd .

# vi /etc/rc.conf
...
xend=yes
xenbackendd=yes
xendomains=yes

确定 xend 需要的 xen 设备存在(没有的话创建一个):

# cd /dev && sh MAKEDEV xen

把上面安装的 Xen Hypervisor (xen.gz) 拷贝到根目录下 /:

# cp /usr/pkg/xen3-kernel/xen.gz /

下载和安装 Xen dom0 kernel:

# cd /
# wget http://ftp.us.netbsd.org/pub/NetBSD/NetBSD-5.0.2/amd64/ \
binary/kernel/netbsd-XEN3_DOM0.gz
# gunzip netbsd-XEN3_DOM0.gz

配置 NetBSD 开机引导,比 Linux 的 Grub 和 LILO 都简单多了:

# vi /boot.cfg
...
menu=NetBSD/XEN:load /netbsd-XEN3_DOM0;multiboot /xen.gz dom0_mem=64M
installboot -v -o timeout=5 /dev/rwd0a /usr/mdec/bootxx_ffsv1

配置 bridge:

# vi /etc/ifconfig.bridge0
create
!brconfig $int add re0 up

重启系统,选择 NetBSD/Xen 内核登陆,登陆后用 xm 检查 xen 是否正常运行:

# shutdown -r now

# xm list
Name                                        ID   Mem VCPUs      State   Time(s)
Domain-0                                     0    64     1     r-----     18.5

安装虚拟机

先做个连接,方便以后使用:

# ln -sf /usr/pkg/etc/xen/ xen

在 NetBSD 上没有 xen-create-image, virt-install 等工具,而且没有 lvm,文件系统又不同,所以要创建 Linux 虚拟机(domU)比较麻烦,这里 VPSee 直接从另外一台 Linux Xen 服务器上拷贝一个虚拟机镜像(模版)过来运行,只需要编辑 debian.cfg 配置文件就可以了:

# vi /etc/xen/debian.cfg
bootloader = "/usr/pkg/bin/pygrub"
memory = 128
name = "debian"
vif = [ 'bridge=bridge0' ]
disk = ['file:/home/vpsee/debian.img,sda1,w']
root = "/dev/sda1"
extra = "fastboot"
extra = "xencons=tty1"

启动和查看 debian 虚拟机:

# xm create debian.cfg

# xm list
Name                                        ID   Mem VCPUs      State   Time(s)
Domain-0                                     0    64     1     r-----   4764.1
debian                                      10   128     1     ------      6.6

解决 INIT: Id “xvc0” respawning too fast 问题

有位也做 VPS 的同行网友前几天来信说他用 SolusVM 创建用 PyGrub 引导的 Debian VPS 时遇到如下问题,每隔几分钟这个消息就跳出来1次,只打印在控制台(console):

INIT: Id "xvc0" respawning too fast: disabled for 5 minutes

这条消息无害,不影响用户正常使用,因为用户一般是 ssh 登陆,甚至不会看到这条消息。tty 设备文件是用来让用户和系统打交道的(通过 ssh 或 console),通过 pygrub 使用自己内核启动的 VPS 如果没设置好 tty 就容易出现以上错误。解决办法很容易,进入 VPS(虚拟机)后在 /etc/inittab 注释掉 xvc0 一行、在 /etc/securetty 文件里加上 hvc0 一行(如果没有的话):

# vi /etc/inittab
hvc0:2345:respawn:/sbin/getty 38400 hvc0
#xvc0:2345:respawn:/sbin/getty 38400 xvc0

# vi /etc/securetty
# Standard hypervisor virtual console
hvc0

# Oldstyle Xen console
#xvc0

修改完毕后重载一下 inittab 就应该看不到那条烦人的错误提示了:

# telinit q

最近收到的邮件特别多,如果大家有技术问题欢迎直接发在博客上一起讨论,这样同样的问题只需要被解决和回复一次,而且也会帮助有类似问题的朋友通过搜索引擎找过来。我们一般会在有时间的时候回复,如果问题值得继续深入的话 VPSee 会抽时间写博客来详细解释。我们的 email 和即时聊天工具仅用做客户支持和商业合作,谢谢:)

在 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 的官方文档

Debian 上 Xen 和 X 之间的 bug

上周五 VPSee 在一台新 PC 上安装 Debian 的时候遇到一堆问题,先是安装程序没有找到网卡,没有网卡就需要自己编译驱动,但是编译又需要一些编译包、头文件和库,没有网络就没法 apt-get(因为只下载了 cd1),所以不得不从另外一台机器上下载网卡驱动 e1000e-1.2.10.tar.gz(内核树里居然也没有发现这款网卡,需要从 Intel 官网下载)以及编译驱动所必须的软件包和依赖包。最后 dpkg -i 软件包到 Debian、编译驱动、modprobe 驱动到内核,终于找到网卡。好不容易配好了网络,开始装 X,配置 X Window 的时候居然没有找到鼠标,一查原因原来是 gpm 鼠标服务由于某种原因没起来。最后把 X Window 和窗口管理器装上又发现字体和渲染都很难看,然后又下载其他的字体更改 .fonts.conf. 好不容易可以用桌面了,却发现在 Debian 上安装完 Xen 后进入 Xen(2.6.26-1xen-amd64)内核启动 X Window 就报错:

$ uname -r
2.6.26-1-xen-amd64

$ tail /var/log/Xorg.0.log 
(==) intel(0): RGB weight 888
(==) intel(0): Default visual is TrueColor
(II) intel(0): Integrated Graphics Chipset: Intel(R) G33
(--) intel(0): Chipset: "G33"
(--) intel(0): Linear framebuffer at 0xC0000000
(--) intel(0): IO registers at addr 0xD0300000

Fatal server error:
xf86MapVidMem: Could not mmap framebuffer (0xd0300000,0x80000) (Invalid argument)

VPSee 发现只要用 Xen 内核启动就无法进入 X,但是用原来的内核启动就可以。Debian 用户组有人2008年就报告了这个 bug,原因是 Debian 自带的这个 Xen 内核 2.6.26-1-xen-amd64 在启动 X 调用 mmap () 的时候,CPU 和显卡之间作内存映射(MMIO)出错,导致 X Window 不能启动。这个 bug 居然到现在都没有解决,可能大家都用 Debian 做 Xen 服务器,很少拿 Debian 桌面和 Xen 一起用吧。目前最简单的解决办法是限制 dom0 的内存(小于 2GB 就可以):

# vi /boot/grub/menu.lst
...
kernel          /boot/xen-3.2-1-amd64.gz dom0_mem=1024mb
...

这个 bug 只存在使用共享显卡的机器上,如果使用独立显卡就不会有这个问题。

Ubuntu 10.04 上编译安装 Xen 4.0.1 后的 xend 启动问题

因为 Ubuntu 10.04 不再官方维护 Xen 软件包了(RedHat 6 也不再支持 Xen 了),所以以后要想在这两大平台上用到 Xen 的话就需要自己亲自下载 Xen 源代码编译安装(当然也可以用第三方源)。今天 VPSee 在一台 Ubuntu 10.04 服务器上升级 Xen 到 4.0.1 的时候遇到一个问题,按照 Ubuntu 9.10 上源码安装 Xen 上的步骤编译和安装都正常,启动 Xen 内核也没问题,最后启动 xend 的时候报错如下:

$ sudo /etc/init.d/xend start
Traceback (most recent call last):
  File "/usr/sbin/xend", line 36, in 
    from xen.xend.server import SrvDaemon
ImportError: No module named xen.xend.server
Traceback (most recent call last):
  File "/usr/sbin/xend", line 36, in 
    from xen.xend.server import SrvDaemon
ImportError: No module named xen.xend.server
.Traceback (most recent call last):
  File "/usr/sbin/xend", line 36, in 
    from xen.xend.server import SrvDaemon
ImportError: No module named xen.xend.server
.Traceback (most recent call last):
  File "/usr/sbin/xend", line 36, in 
    from xen.xend.server import SrvDaemon

刚开始怀疑是 Python 版本问题,后来查了一下 /usr/sbin/xend 文件发现第36行:from xen.xend.server import SrvDaemon 在导入 SrvDaemon 就报错,xend 可能没有找到 xen tools,怀疑和安装路径有关。Ubuntu 上 Python 的默认安装在 /usr/lib/python2.6/ 下面,如果不设定 PYTHON_PREFIX_ARG 参数手动编译安装 xen tools 后也会默认安装到 /usr/lib/python2.6/,这样就出问题了,因为 xend 会默认从 /usr/local/lib/python2.6/dist-packages/ 找 script 和导入库,但是这时候 /usr/local/lib/python2.6/dist-packages/ 下面什么东西都没有。所以要改变 install-tools 的默认安装路径到 /usr/local/lib/python2.6/dist-packages/ 下,PYTHON_PREFIX_ARG 不带任何参数就可以了:

$ sudo make install-tools PYTHON_PREFIX_ARG=

最后检查一下是否能正常启动 xend:

$ sudo /etc/init.d/xend start
$ sudo xm list
Name                                        ID   Mem VCPUs      State   Time(s)
Domain-0                                     0  3713     4     r-----      6.4

挂载虚拟机镜像文件里的普通分区

用 virt-manager, virsh 或其他工具可以选择把 KVM 或 Xen HVM 的虚拟机安装在一个镜像文件上,虽然因为性能的关系这种方式不推荐在生产环境使用,但是因为用起来非常简便,所以 VPSee 平时做实验或测试的时候会经常用这种方式存储虚拟机。上星期提到了 “挂载虚拟机镜像文件里的 LVM 逻辑分区”,那么如何挂载虚拟机镜像文件里的普通分区呢?这比挂载 LVM 逻辑分区要简单多了,不用算那个地址偏移量 offset,也不用 LVM 打交道。

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

# losetup -f
/dev/loop0

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

一个镜像文件里可能包含多个分区,所以不能简单 mount -o loop centos.img,我们需要把 centos.img 里面包含的每个分区映射出来后在一个一个挂载,所以需要一个工具来读分区表,kpartx 就是这样一个在特定设备上读取分区表并为每个分区创建映射的工具,-a 参数表示加入分区映射;-v 参数表示完成后输出结果:

# kpartx -av /dev/loop0 
add map loop0p1 : 0 208782 linear /dev/loop0 63
add map loop0p2 : 0 1044225 linear /dev/loop0 208845
add map loop0p3 : 0 19711755 linear /dev/loop0 1253070

上面 loop0p1, loop0p2, loop0p3 分别映射到镜像文件里的3个分区里,可以通过 fdisk 来查看:

# fdisk -l /dev/loop0

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

      Device Boot      Start         End      Blocks   Id  System
/dev/loop0p1   *           1          13      104391   83  Linux
/dev/loop0p2              14          78      522112+  82  Linux swap / Solaris
/dev/loop0p3              79        1305     9855877+  83  Linux

有了上面的映射以后就可以挂载 centos.img 里的普通逻辑分区了,我们这里只挂载 root 分区,root 分区在 loop0p3,挂载成功后用 ls 查看一下是否正确:

# ls -l /dev/mapper/
total 0
crw------- 1 root root  10, 63 Sep 24 11:34 control
brw-r----- 1 root disk 253,  2 Oct 14 19:55 loop0p1
brw-r----- 1 root disk 253,  3 Oct 14 19:55 loop0p2
brw-r----- 1 root disk 253,  4 Oct 14 19:55 loop0p3

# mount /dev/mapper/loop0p3 /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

# kpartx -dv /dev/loop0
del devmap : loop0p1
del devmap : loop0p2
del devmap : loop0p3

# losetup -d /dev/loop0

挂载虚拟机镜像文件里的 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

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

用同一个配置文件启动任意 Xen DomU

当我们使用 xm create vps1 这行命令创建一个 Xen DomU (VPS) 的时候,xm 会自动在 /etc/xen/ 目录下匹配到 vps1 这个配置文件,通过正确解析 vps1 配置文件而成功启动一个 DomU,如果我们有很多个 DomU 需要启动的话,那就需要在 /etc/xen/ 下创建和编辑很多个对应的 DomU 配置文件。这些配置文件的内容都是差不多的,无非就是一些参数需要修改,那么如何只在 /etc/xen/ 下保留一份配置文件并让所有 DomU 能通过传参数的方式来启动不同的 DomU 呢?比如,如何通过下面一行命令来自定义配置文件参数并启动 DomU 呢?

# xm create vps.config vpsid=1 vpscpu=2 vpsmem=512

我们企图通过上面这行命令来启动一个 VPS ID 为1、CPU cores 数为2、内存大小为 512MB 的 VPS (DomU). 我们接下来需要编辑 vps.config 这个文件,让这个文件在 xm create 时能接受 vpsid, vpscpu, vpsmem 这些参数。

# A domU configuration file for all the domUs
# written by vpsee.com

def vpsid_check(var, val):
    val = int(val)
    if val <= 0:
         raise ValueError
    return val
def vpscpu_check(var, val):
    val = int(val)
    if val <= 0:
        return 1
    elif val >= 8:
        return 8
    return val
def vpsmem_check(var, val):
    val = int(val)
    if val <= 128:
        return 128
    return val

xm_vars.var('vpsid',
            use="VPS ID > 0",
            check=vpsid_check)
xm_vars.var('vpscpu',
            use="VPS CPU > 0 and <= 8",
            check=vpscpu_check)
xm_vars.var('vpsmem',
            use="VPS RAM >= 128",
            check=vpsmem_check)

xm_vars.check()

name="vps%d" % vpsid
bootloader = "/usr/bin/pygrub"
vcpus = "%d" % vpscpu
maxmem="%d" % vpsmem
memory="%d" % vpsmem
disk=[ "tap:aio:/home/vpsee/xen/vps%d/disk.img,sda1,w" % vpsid ]
vif = [ "bridge=xenbr0" ]
on_shutdown = "destroy"
on_poweroff = "destroy"
on_reboot = "restart"
on_crash = "restart"

上面的片段来自我们自己的脚本程序,VPSee 去除了一些和业务相关的代码,比如 xm create 的时候自动把客户名和客户的联系方式、VPS 配置、初始化流量等信息提交到数据库等。我们自己编写了很多类似的脚本帮助我们自动化管理和减少我们的管理、维护服务器和 VPS 的成本。

1GB 的 VPS 只有 727MB?

上周六我们一位客户报告了一个奇怪的问题,他在我们这里购买的 1024MB 内存的 VPS 只有 727MB,我们立即检查了 Xen 服务器上的配置和他的 VPS 运行情况,没有发现问题,他的 Xen VPS 配置文件以及 Xen 工具都是显示的是他有 1024MB 内存,客户发来了截图,并且给了我们 root 密码,我们登录进去看到客户的 VPS 确实只有 727MB,更奇怪的是我们可以通过动态调整来减少这个 VPS 的内存到 512MB,但是不能加到 1024MB,总是在 727MB 这个地方就加不上去了。我们服务器上有足够的内存,我们检查了各种 Xen 配置文件和 Xen 的日志,没有发现任何异常,Xen 技术已经相当成熟,如果是 bug 的话,应该早就有人遇到和解决了,所以我们估计和客户的系统有关。

这是一个很有意思的问题,VPSee 很想弄明白怎么回事,但是又不能不停的重启客户的 VPS 来测试,也没办法在自己机器上重现。这个时候客户给了我们一条重要的提示信息,他自己编译过 Linux 内核。和其他 VPS 服务商采用公共的内核不同,我们的 VPS 支持用户自己定义和编译内核。我们开始怀疑客户编译内核的时候某些选项弄错了,我们让客户发来他编译内核时用到的配置选项,他自己编译和使用的是64位的内核,而我们提供的 Linux VPS 是32位的,刚开始简单怀疑是32位程序使用64位内核的问题,可能部分32位程序调用64位内核提供的64位系统调用的时候出的问题。不过这个说法说不过去,因为64位是向下兼容的,运行大部分32位程序应该没问题(反过来32位内核运行64位程序就不一定了,因为64位程序指针会被32位内核截断成32位可能造成程序错误)。

周日下午连上服务器新建了一个 1024MB Arch Linux VPS,VPS 正常显示的是 1024MB,没有问题,按照客户的说明和这篇:在 ArchLinux VPS 上编译内核 提供的默认内核配置文件编译后,问题来了,启动系统后果然内存只有 727MB,打印内核信息只看到 727MB 内存:

# dmesg | more
...
last_pfn = 0x40000 max_arch_pfn = 0x1000000
Warning only 727MB will be used.
Use a HIGHMEM enabled kernel.
...

估计就是内核配置低端内存 LOWMEM 限制的问题,找到问题就好办了,检查默认的内核配置文件发现没有启用 HIGHMEM(CONFIG_NOHIGHMEM=y):

# vi kernel26-xen/config
...
CONFIG_NOHIGHMEM=y
# CONFIG_HIGHMEM4G is not set
# CONFIG_HIGHMEM64G is not set
CONFIG_PAGE_OFFSET=0xC0000000
CONFIG_X86_PAE=y
...

把上面的相关部分改成下面的配置就可以了:

# vi kernel26-xen/config
...
# CONFIG_NOHIGHMEM is not set
# CONFIG_HIGHMEM4G is not set
CONFIG_HIGHMEM64G=y
CONFIG_PAGE_OFFSET=0xC0000000
CONFIG_HIGHMEM=y
CONFIG_X86_PAE=y
...

重新编译安装内核并加入到 grub,最后重启系统登录后就会看到 1024MB 内存回来了:

# makepkg --asroot
# pacman -U kernel26-xen-2.6.34.1-1-i686.pkg.tar.xz
# pacman -U kernel26-xen-headers-2.6.34.1-1-i686.pkg.tar.xz

# vi /boot/grub/menu.lst
...
timeout 5
default 0

title Xen for ArchLinux (VPSee)
root (hd0,0)
kernel /boot/vmlinuz26-xen root=/dev/xvda1 ro console=/dev/xvc0
initrd /boot/kernel26-xen.img
...

# reboot

谢谢我们的客户给我们提供充分的信息和截图帮助我们找到和解决问题,也很感谢客户对我们的信任、遇到问题及时向我们反映而不是猜测和抱怨,不了解的人还以为我们在忽悠人呢。