为 OpenStack Nova 制作 CentOS 镜像

做 CentOS 镜像的过程和去年写的那篇为 OpenStack Nova 制作 Ubuntu 镜像的步骤差不多,不过这半年 OpenStack 发展神速,比以前要稳定多了,有些步骤可以省了,而且有些命令工具、参数、功能都有改动,比如以前的 uec-publish-image 改成了现在的 cloud-publish-image,功能也有变化。下面的制作镜像步骤在 Ubuntu 11.10 + OpenStack Diablo Release 上完成。

安装 CentOS 镜像

下载要安装的 CentOS 版本,这里选用最新的 CentOS 6.2:

$ wget http://mirrors.kernel.org/centos/6.2/isos/x86_64/CentOS-6.2-x86_64-minimal.iso

创建一个 10GB 大小的 “硬盘”(raw 格式),最好用一个较小的 “硬盘”,体积太大提交到云里会花很长时间,而且每次运行实例也会花很长时间:

$ kvm-img create -f raw centos.img 10G
Formatting 'centos.img', fmt=raw size=10737418240

使用刚才下载的 CentOS “安装盘” 和刚创建的 “硬盘” 引导启动系统,用 -vnc 参数打开 vnc 访问,这样可以从其他机器登录到这个界面安装系统:

$ sudo kvm -m 512 -cdrom CentOS-6.2-x86_64-minimal.iso \
-drive file=centos.img -boot d -net nic -net tap -nographic -vnc :0

用 vncviewer 登录后按照屏幕提示完成 CentOS 安装。需要注意的是在分区阶段把 10GB 硬盘全部划分成一个 ext4 root 分区,不要创建多个分区也不要创建 swap 区:

$ vncviewer 172.16.39.111:5900

安装完后会自动重启,如果没有重启的话按照下面的命令启动刚刚安装好的虚拟机镜像 centos.img,如果出现 failed to find romfile “pxe-rtf8139.bin” 的错误提示可以通过安装 kvm-pxe 解决:

$ sudo kvm -m 512 -drive file=centos.img -boot c -net nic -net tap \
-nographic -vnc :0
kvm: pci_add_option_rom: failed to find romfile "pxe-rtl8139.bin"

$ sudo apt-get install kvm-pxe

再次用 vnc 登录虚拟机镜像,安装一些必要工具(因为这个镜像将会是模板,所以最好保持最简最小化):

$ vncviewer 172.16.39.111:5900

# yum update
# yum upgrade
# yum install openssh-server
# chkconfig sshd on

修改分区加载表(/etc/fstab),注释或删除以前的,加上 UUID=cec-rootfs 一行:

# vi /etc/fstab
#UUID=47a90bea-2d88-4c82-a335-09c1533b1538 / ext4 defaults 1 1
LABEL=cec-rootfs                           / ext4 defaults 0 0

在网络接口配置里面注释或删除这行 #HWADDR= 一行,启用 DHCP:

# vi /etc/sysconfig/network-scripts/ifcfg-eth0
DEVICE="eth0"
#HWADDR="00:11:22:12:34:56"
#NM_CONTROLLED="yes"
BOOTPROTO=dhcp
ONBOOT="yes"

注射 ssh key 以便外界可以用 ssh -i mykey.priv root@host 的形式无密码登录到虚拟机实例,在 /etc/rc.local 文件中加入下面这些:

# vi /etc/rc.local
...
mkdir -p /root/.ssh
echo >> /root/.ssh/authorized_keys
curl -m 10 -s http://169.254.169.254/latest/meta-data/public-keys/0/openssh-key
| grep 'ssh-rsa' >> /root/.ssh/authorized_keys
chmod 700 ~/.ssh
chmod 600 ~/.ssh/authorized_keys
echo "AUTHORIZED_KEYS:"
echo "--------------------"
cat /root/.ssh/authorized_keys
echo "--------------------"

别忘了还需要修改 sshd 的配置实现无密码登录:

# vi /etc/ssh/sshd_config
...
RSAAuthentication yes
PubkeyAuthentication yes
PermitRootLogin without-password
ChallengeResponseAuthentication no
PasswordAuthentication no
UsePAM no

需要关闭 SELINUX,否则即使上面 ssh 设定允许 root 无密码登录也无效:

# vi /etc/selinux/config
SELINUX=disabled
SELINUXTYPE=targeted

70-persistent-net.rules 会自动添加其他的网络接口,需要删除这个文件避免自动添加除了 eth0 以外的接口,关闭虚拟机准备发布镜像:

# rm -rf /etc/udev/rules.d/70-persistent-net.rules

# shutdown -h now

发布 CentOS 镜像

CentOS 镜像已经做好了,现在可以发布到云里了:

$ cloud-publish-image amd64 centos.img mybucket
ami-00000007    mybucket/centos.img.manifest.xml

等待一段时间后出现 ami-00000008 mybucket/centos.img.manifest.xml 表示我们刚制作的 CentOS 镜像已经正式发布到云里,以后就可以以这个镜像为模板来快速生成虚拟机实例(instance)。可以通过 euca-describe-images 来查看:

$ euca-describe-images
IMAGE	ami-00000008	images/centos.img.manifest.xml		available	private		x86_64	machine	 	 	instance-store

第一个 CentOS 虚拟机实例

有了 CentOS 镜像(模板)以后我们就可以以这个 “镜像” 为模板来为云计算用户创建 n 个 CentOS 虚拟机(操作系统)实例,不过在运行实例之前需要 key:

$ euca-add-keypair mykey > mykey.priv
$ chmod 600 mykey.priv 

$ euca-describe-keypairs
KEYPAIR	mykey	76:7d:93:88:a0:e5:3e:5d:4b:62:cd:85:c5:23:7a:05

$ euca-run-instances -k mykey -t m1.small ami-00000008
RESERVATION	r-hzwwif81	vpseecloud	default
INSTANCE	i-0000002a	ami-00000008	pending	vpsee (vpseecloud, None)	0		m1.small	2012-02-01T14:26:51Z	unknown zone	aki-00000001	ami-00000000

上面使用 euca-run-instances 创建一个实例后可以用 nova-manage 命令看到:

$ euca-describe-instances
RESERVATION	r-z973l7mm	vpseecloud	default
INSTANCE	i-0000002a	ami-00000008	172.16.39.200	172.16.39.200	running	vpsee (vpseecloud, cloud00)	0		m1.small	2012-02-01T13:56:02Z	nova	ami-00000000	ami-00000000

还记得在 Ubuntu 上安装和配置 OpenStack Nova 的创建网络部分吗?看看现在云里面 IP 的分配情况:

$ sudo nova-manage network list
id   	IPv4              	IPv6           	start address  	DNS1           	DNS2           	VlanID         	project        	uuid
1    	172.16.38.0/23    	None           	172.16.38.2    	8.8.4.4        	None           	None           	None           	None

刚才用 euca-run-instances 启动的一个 ubuntu 虚拟机实例的 IP 就是从 172.16.38.2 开始的,目前分配的 IP 是 172.16.39.200(从 euca-describe-instances 可以看出来),所以 ssh 登录这个 IP 就登上了我们的 CentOS 云虚拟机:

$ ssh -i mykey.priv root@172.16.39.200
[root@server-25 ~]#

想制作 Windows 虚拟机镜像的话可以看这篇:为 OpenStack Nova 制作 Windows 镜像

如何更换 OpenStack 默认的 Hypervisor

我们的云计算头节点这周到货了,采用的是 Dell PowerEdge R710,Intel Xeon CPU E5645 @2.40GHz (12 Cores), 4x300GB SAS (15K), 96GB 内存。这样的配置对于我们的头节点来说过于强大,我们可能考虑用 VMware ESXi 虚拟后用其中的一台虚拟机做 OpenStack/OpenNebula 头节点,用 VMware ESXi 有个问题,免费版本的 VMware ESXi 5.0 虚拟出来的单台虚拟机最多只能支持8核心,而这台服务器逻辑上有24核心,有点浪费。图片最上面的是头节点(Dell PowerEdge R710),中间的是计算节点(Dell PowerEdge M710HD),最下面的是存储。

dell poweredge r710

OpenStack 几乎支持现在所有主流的虚拟技术和 Hypervisor,如 KVM, Hyper-V, LXC, QEMU, UML, VMWare ESX/ESXi, Xen/XenServer 等,未来还会支持 OpenVZ 和 VirtualBox. 不过 OpenStack 首选的 Hypervisor 是 KVM,OpenStack 安装后默认使用的是 KVM (–libvirt_type=kvm),不需要特别配置。如果由于某种原因,比如服务器 CPU 不支持 Intel VT-x/AMD-V 不能使用 KVM 或者想简单尝试一下另外一种 Hypervisor 怎么办呢?方法很容易,1、更改 OpenStack Nova 的配置文件(–libvirt_type);2、给所有的 Compute 结点装上相应的 Hypervisor 就可以了。以下以 LXC 为例来说明如何更换 OpenStack Nova 的 Hypervisor,LXC 使用的是与 Xen 和 KVM 完全不同的虚拟技术,和 OpenVZ 的容器技术有点类似。

首先在所有 openstack nova compute 结点上替换现有的 nova-compute-kvm 到 nova-compute-lxc:

$ sudo apt-get install nova-compute-lxc

LXC 使用 cgroup 文件系统来限制资源和进程,libvirt 需要 cgroup 文件系统来运行 LXC,我们只要在 nova compute 上创建一个 ctroups 目录并且在 /etc/fstab 最后加上 none /cgroups cgroup cpuacct,memory,devices,cpu,freezer,blkio 0 0 这行就可以了,别忘了重启系统:

$ sudo mkdir /cgroups

$ vi /etc/fstab
none /cgroups cgroup cpuacct,memory,devices,cpu,freezer,blkio 0 0

$ sudo reboot

重启后可以看到 /cgroups 下面多了很多东西:

$ ls /cgroup/
blkio.io_merged                   cpu.shares
blkio.io_queued                   devices.allow
blkio.io_service_bytes            devices.deny
blkio.io_serviced                 devices.list
blkio.io_service_time             libvirt
blkio.io_wait_time                memory.failcnt
blkio.reset_stats                 memory.force_empty
blkio.sectors                     memory.limit_in_bytes
blkio.throttle.io_service_bytes   memory.max_usage_in_bytes
blkio.throttle.io_serviced        memory.memsw.failcnt
blkio.throttle.read_bps_device    memory.memsw.limit_in_bytes
blkio.throttle.read_iops_device   memory.memsw.max_usage_in_bytes
blkio.throttle.write_bps_device   memory.memsw.usage_in_bytes
blkio.throttle.write_iops_device  memory.move_charge_at_immigrate
blkio.time                        memory.numa_stat
blkio.weight                      memory.oom_control
blkio.weight_device               memory.soft_limit_in_bytes
cgroup.clone_children             memory.stat
cgroup.event_control              memory.swappiness
cgroup.procs                      memory.usage_in_bytes
cpuacct.stat                      memory.use_hierarchy
cpuacct.usage                     notify_on_release
cpuacct.usage_percpu              release_agent
cpu.rt_period_us                  tasks
cpu.rt_runtime_us

修改 OpenStack Nova 配置,将 nova-compute.conf 里面的 –libvirt_type=kvm 改成 lxc:

$ sudo vi /etc/nova/nova-compute.conf
--libvirt_type=lxc

$ sudo restart nova-compute

重启所有 nova compute 结点上的 nova-compute 服务,有必要的话重启所有 nova compute 结点。

那镜像怎么办呢?以前为 KVM 上传的镜像也可以用在 LXC 上吗?嗯,可以。下载 oneiric-server-cloudimg-amd64.tar.gz 解压并镜像到 OpenStack:

$ wget http://uec-images.ubuntu.com/oneiric/current/oneiric-server-cloudimg-amd64.tar.gz
$ tar zxvf oneiric-server-cloudimg-amd64.tar.gz

$ euca-bundle-image -i oneiric-server-cloudimg-amd64.img
$ euca-upload-bundle -b oneiric -m /tmp/oneiric-server-cloudimg-amd64.img.manifest.xml
$ euca-register oneiric/oneiric-server-cloudimg-amd64.img.manifest.xml

$ euca-describe-images
IMAGE	ami-00000001	oneiric/oneiric-server-cloudimg-amd64.img.manifest.xml		available	private		x86_64	machine	 	 	instance-store

$ euca-run-instances -k vpsee -t m1.tiny ami-00000001

$ euca-describe-instances
RESERVATION	r-4bbu7bd7	sanbi	default
INSTANCE	i-00000001	ami-00000001	172.16.39.6	172.16.39.6	running	vpsee (vpseecloud, node00)	0		m1.tiny	2012-01-20T08:04:05Z	nova	ami-00000000	ami-00000000

需要注意的是,OpenStack 目前不支持混合 Hypervisor,也就是说所有 nova compute 结点上必须使用同一种 Hypervisor,不过支持混合 Hypervisor 的 OpenStack 正在计划开发中。

Hello World, OpenNebula Cloud API 编程

先报告一下我们云计算项目的进度。去年休假前订购的服务器和部件已经陆续到货了,计算节点采用的是 Dell PowerEdge M710HD 刀片服务器,特别为数据中心级虚拟应用设计,海量内存、密集 IO 吞吐等优势,特别适合云计算、虚拟机等应用。现在正在等 Dell 的售后技术人员过来安装服务器和存储阵列,有些电源和机柜问题需要解决,顺利的话下周服务器可以上线。

dell poweredge m710hd

OpenNebula 提供了 XML-RPC 的方式访问 OpenNebula Cloud Api (OCA),这样就允许不同操作系统、不同语言编写的客户端程序可以通过 XML-RPC 远程调用的方式来访问 OpenNebula 服务。下面通过两个不同语言编写的最简单例子抛砖引玉一下,来看看如何是如何与 OCA 打交道的。

OpenNebula 绝大部分是由 Ruby 编写的,其提供的 Ruby OCA API 实现当然是最丰富和完整的。先安装 Ruby OCA Bindings:

$ sudo gem install oca

用 Ruby 编写一小段代码试验一下,以下代码用来打印当前云里每个计算结点的 hostname:

#!/usr/bin/ruby

require 'rubygems'
require 'oca'

include OpenNebula

# OpenNebula credentials
CREDENTIALS = "oneadmin:vpsee"

# XML_RPC endpoint where OpenNebula is listening
ENDPOINT    = "http://localhost:2633/RPC2"

client = Client.new(CREDENTIALS, ENDPOINT)
host_pool = HostPool.new(client)
rc = host_pool.info

# Print all the hostname from the host pool
host_pool.each do |host|
     puts host.name
end

再来看看用 Python 如何编写上面类似功能的代码。安装 Python OCA Bindings:

$ sudo easy_install oca

用 Python 编写一小段代码看一下:

#!/usr/bin/python

import oca

# OpenNebula credentials
CREDENTIALS = "oneadmin:vpsee"

# XML_RPC endpoint where OpenNebula is listening
ENDPOINT    = "http://localhost:2633/RPC2"

client = oca.Client(CREDENTIALS, ENDPOINT)
host_pool = oca.HostPool(client)
host_pool.info()

# Print all the hostname from the host pool
for host in host_pool:
    print host.name

应该没人会想在这种情况下用 Java 或 C++ 吧,Programming Examples 里面提供的 Java OCA 和 C++ 例子比 Ruby, Python 复杂得多。

为 OpenNebula VM 添加 swap 分区

给 OpenNebula 制作 Ubuntu 模板的时候推荐只分一个单独区,不要交换区(swap),交换区可以以后在虚拟机外部由 OpenNebula 加上,这样把 root 区和 swap 区分开更灵活一些,方便以后创建不同大小 swap 的虚拟机。以下是在 OpenNebula 上为 Ubuntu 虚拟机增加 swap 的步骤:

修改 Ubuntu 模板文件,注意 DISK 一栏的 target 是 sda 不是 sda1,否则会出现 Boot Failed: 的错误。

$ vi ubuntu.one
NAME   = ubuntu
CPU    = 1
MEMORY = 512

OS = [ ARCH = x86_64,
        BOOT = hd,
        ROOT = sda1
]

DISK = [ source   = /var/lib/one/images/ubuntu.img,
         clone    = no,
         target   = sda,
         readonly = no ]

DISK = [ type     = swap,
         size     = 512,
         target   = sdb ]

GRAPHICS = [ type ="vnc",
             listen ="0.0.0.0",
             port = "5900" ]

启动 Ubuntu 虚拟机后,OpenNebula 就为这个虚拟机准备了一个大小为 512MB 的分区 /dev/sdb,现在登录虚拟机激活这个 swap,这样 Linux 就能识别 /dev/sdb 是个交换分区了(这个过程其实和在自己电脑上增加交换分区或用文件充当交换分区的过程差不多):

$ vncviewer 172.16.39.111:5900

$ sudo swapon /dev/sdb

$ free
             total       used       free     shared    buffers     cached
Mem:        504112      90896     413216          0      11440      33160
-/+ buffers/cache:      46296     457816
Swap:       524284          0     524284

最后可以在 /etc/fstab 里增加一条记录以便重启虚拟机以后系统能自动加上 swap:

$ sudo vi /etc/fstab
/dev/sdb    none    swap    sw    0    0

重置 OpenStack Nova 数据库

如果使用 OpenStack 的时候遇到莫名其妙的问题,或者直接操作 OpenStack 数据表的时候把里面的关系或记录搞砸了,或者使用一些从网上抄来连自己都不理解的命令把数据库记录弄乱了,或者自己想推倒重来等等,碰到这些问题的时候大家想到的第一个办法就是“重装系统,重装 OpenStack”,其实大部分时候都不必要重装,只需要重置一下数据库就可以了,避免重复劳动把操作命令整理到下面的简单脚本里:

#!/bin/bash
# Reset nova database and restart nova services
# written by vpsee.com

# Delete the database and create a new one
mysql -u root -ppassword -e "DROP DATABASE nova;"
mysql -u root -ppassword -e "CREATE DATABASE nova;"

# Sync up the database scheme
nova-manage db sync

# Create a network
nova-manage network create private 192.168.0.1/24 1 32

# Create a admin user and a project and assign the admin user to it
nova-manage user admin vpsee
nova-manage project create vpseecloud vpsee

# Restart all the services
restart libvirt-bin
restart nova-api
restart nova-scheduler
restart nova-network
restart nova-volume
restart nova-compute

解决 Warning: failed to parse error message from AWS: :1:0: syntax error 问题

Warning: failed to parse error message from AWS: :1:0: syntax error 是个常见问题,很多朋友都在 Ubuntu 上安装和配置 OpenStack Nova 的时候遇到,100楼,120和120楼都遇到这个问题。引起这个问题的原因很多。如果遇到 nova-manage service list 服务都正常而 euca-describe-availability-zones verbose 报错的情况通常都是因为 novarc 这个文件捣鬼,nova 不能正确读取 novarc 里面的权限设置使得认证失败,认证失败当然也就不能正确执行命令了。

# nova-manage service list
Binary           Host      Zone        Status     State Updated_At
nova-scheduler   vpsee     nova        enabled    :-)   2011-12-05 13:26:51
nova-volume      vpsee     nova        enabled    :-)   2011-12-05 13:26:49
nova-network     vpsee     nova        enabled    :-)   2011-12-05 13:26:51
nova-compute     vpsee     nova        enabled    :-)   2011-12-05 13:26:52

# euca-describe-availability-zones verbose
Warning: failed to parse error message from AWS: :1:0: syntax error
None: None

修改 nova.conf 文件,在末尾明确加上 –use_deprecated_auth=true 这行(这个设置本来是默认的,以前版本的 OpenStack Nova 是不需要的),然后重启 nova 服务并重新生成 novacreds.zip,记得一定要 source novarc 哟:

# vi /etc/nova/nova.conf
...
--use_deprecated_auth=true

# restart libvirt-bin; restart nova-network; restart nova-compute;
restart nova-api; restart nova-objectstore; restart nova-scheduler;
restart nova-volume; restart glance-api; restart glance-registry

# nova-manage project zipfile mycloud vpsee /home/vpsee/creds/novacreds.zip
# cd /home/vpsee/creds
# unzip novacreds.zip
# chown -R vpsee:vpsee /home/vpsee/creds/

# source /home/vpsee/creds/novarc

# euca-describe-availability-zones verbose
AVAILABILITYZONE	nova	available
AVAILABILITYZONE	|- vpsee
AVAILABILITYZONE	| |- nova-scheduler	enabled :-) 2011-12-05 13:32:44
AVAILABILITYZONE	| |- nova-volume	enabled :-) 2011-12-05 13:32:43
AVAILABILITYZONE	| |- nova-network	enabled :-) 2011-12-05 13:32:44
AVAILABILITYZONE	| |- nova-compute	enabled :-) 2011-12-05 13:32:47

再附带一句,OpenStack 正在飞速发展中,这样或那样的问题也许在新版本里就不存在了,以前没有的问题也可能第一次出现在新版本中。

为 OpenNebula 制作 Ubuntu 镜像

上次我们介绍了给 OpenNebula 制作 Windows 镜像,当然了少不了 Ubuntu 了,为 OpenNebula 制作 Ubuntu 镜像的步骤和制作 Windows 镜像差不多,以下是具体步骤:

首先下载需要安装的 ubuntu 版本:

$ wget http://releases.ubuntu.com/11.10/ubuntu-11.10-server-amd64.iso

创建一个 10GB 大小的 “硬盘”(raw 格式):

$ kvm-img create -f raw ubuntu.img 10G
Formatting 'ubuntu.img', fmt=raw size=10737418240

然后使用刚才下载的 ubuntu “安装盘” 和刚创建的 “硬盘” 引导启动系统,使用 -vnc 参数打开 vnc 访问,以便可以用其他机器远程登录进行安装操作:

$ sudo kvm -m 512 -cdrom ubuntu-11.10-server-amd64.iso \
-drive file=ubuntu.img -boot d -nographic -vnc :0

在其他机器上用 vnc 客户端登录后就可以看到 Ubuntu 安装界面,按照屏幕的提示完成 ubuntu 的安装工作,需要注意的是分区的时候只分一个区给 /,不要分 swap 区,以后 VPSee 将会提到如何给虚拟机加上交换分区:

$ vncviewer 172.16.39.111:5900

安装完后会自动重启,shutdown -h now 虚拟机后再按照下面命令启动刚刚安装好的虚拟机镜像 ubuntu.img,如果出现 failed to find romfile “pxe-rtf8139.bin” 的错误提示可以通过安装 kvm-pxe 解决:

$ sudo kvm -m 512 -drive file=ubuntu.img -boot c -nographic -vnc :0
kvm: pci_add_option_rom: failed to find romfile "pxe-rtl8139.bin"

$ sudo apt-get install kvm-pxe

再次用 vnc 登录虚拟机镜像,升级和更新系统,可以安装一些必要工具,比如 OpenSSH 之类的:

$ vncviewer 172.16.39.111:5900

$ sudo update
$ sudo upgrade
$ sudo apt-get install openssh-server

创建和编辑虚拟网络配置文件,然后创建一个 OpenNebula 虚拟网络(参考:在 CentOS 上安装和配置 OpenNebula):

$ vi small_network.net
NAME = "Small network"
TYPE = FIXED

BRIDGE = br0
LEASES = [ IP="172.16.39.111"]
LEASES = [ IP="172.16.39.112"]
LEASES = [ IP="172.16.39.113"]

$ onevnet create small_network.net 

$ onevnet list
  ID USER     NAME              TYPE BRIDGE P #LEASES
   0 oneadmin Small network    Fixed    br0 N       0

创建和编辑 Ubuntu 虚拟机的启动配置文件。注意别忘了加上 ARCH = x86_64(否则无法正常启动 Ubuntu),我们刚才安装的是 Ubuntu 64位 Server 版(ubuntu-11.10-server-amd64.iso):

NAME   = ubuntu
CPU    = 1
MEMORY = 512

OS = [ ARCH = x86_64,
        BOOT = hd,
        ROOT = sda1
]

DISK = [ source   = /var/lib/one/images/ubuntu.img,
         clone    = no,
         target   = sda,
         readonly = no ]

GRAPHICS = [ type ="vnc",
             listen ="0.0.0.0",
             port = "5900" ]

NIC    = [ NETWORK = "Small network" ]

依照上面的配置在 OpenNebula 上创建一个 Ubuntu 虚拟机,等待一下 OpenNebula 会自动根据当前资源情况调度,期间不断用 onevm list 命令查看当前虚拟机的创建情况,状态会从 pend -> prol -> boot -> runn,runn 状态就表示虚拟机已经成功创建并正常运行。最后检查一下 OpenNebula 是否成功创建一个名叫 ubuntu 的虚拟机:

$ onevm create ubuntu.one

$ onevm list
   ID     USER     NAME STAT CPU     MEM        HOSTNAME        TIME
   42 oneadmin   ubuntu runn   1    512M          node00 00 01:16:39

为 OpenNebula 制作 Windows 镜像

说是给 OpenNebula 做 Windows 镜像其实就是做个 KVM 上的 Windows 虚拟机而已,操作步骤无非就是:创建一个硬盘、在硬盘上安装 Windows 系统、把安装好的 Windows 镜像当作模板来复制和创建 Windows 虚拟机。

创建一个 10GB 大小的 “硬盘”(raw 格式):

$ kvm-img create -f raw windowsxp.img 10G
Formatting 'windowsxp.img', fmt=raw size=10737418240

然后使用 ISO 文件的 windowsxp.iso 安装盘来安装 Windows,注意运行 kvm 需要 root 权限,否则会出现 open /dev/kvm: Permission denied 错误:

$ sudo kvm -m 1024 -cdrom windowsxp.iso -drive file=windowsxp.img -boot d -nographic -vnc :0

在另外一台机器上启动 vnc 客户端登录完成 windows 安装:

$ vncview 172.16.39.111:5900
或者
$ vncview 172.16.39.111:0

安装完 windows 后可以进行一些必要的定制,比如打开 RDP 访问、设置防火墙不要屏蔽 RDP 和 ICMP 等。

创建和编辑虚拟网络配置文件,然后创建一个 OpenNebula 虚拟网络(参考:在 CentOS 上安装和配置 OpenNebula):

$ vi small_network.net
NAME = "Small network"
TYPE = FIXED

BRIDGE = br0
LEASES = [ IP="172.16.39.111"]
LEASES = [ IP="172.16.39.112"]
LEASES = [ IP="172.16.39.113"]

$ onevnet create small_network.net 

$ onevnet list
  ID USER     NAME              TYPE BRIDGE P #LEASES
   0 oneadmin Small network    Fixed    br0 N       0

创建和编辑 Windows XP 虚拟机的启动配置文件:

$ vi windowsxp.one

NAME  = winxp
CPU   = 1
MEMORY = 1024

OS = [ boot = hd ]

DISK = [ source  = /var/lib/one/images/windowsxp.img,
         clone    = no,
         target   = hda,
         readonly = no ]

GRAPHICS = [ type ="vnc",
             listen ="0.0.0.0",
             port = "5900" ]

FEATURES = [ acpi="yes" ]

NIC    = [ NETWORK = "Small network" ]

依照上面的配置在 OpenNebula 上创建一个 Windows 虚拟机,等待片刻后不断用 onevm list 命令查看当前虚拟机的创建情况,状态一般会从 pend -> prol -> boot -> runn,runn 状态就表示虚拟机已经成功创建并正常运行。最后检查一下 OpenNebula 是否成功创建一个名叫 winxp 的 Windows 虚拟机:

$ onevm create windowsxp.one

$ onevm list
   ID     USER     NAME STAT CPU     MEM        HOSTNAME        TIME
   32 oneadmin     winxp runn   6   1024M          node00 00 00:20:26

上面的 Windows 虚拟机无法正确得到 OpenNebula 预分配的 IP 地址,需要在 Windows 里配置网络连接手动绑定静态 IP,有办法可以从 OpenNebula 那里自动获得 IP,这个问题留到以后再说。

如何删除 OpenStack Nova 僵尸实例

前天强制重启一台 OpenStack Nova 控制结点以后发现虚拟机消失,但是 euca-describe-instances 命令显示 instances 仍然是 running 的状态,使用 euca-terminate-instances 终止命令仍然无效,暂时把这样的 instance 称作“僵尸实例(zombie instance)”:

# virsh list
 Id Name                 State
----------------------------------

# euca-describe-instances
RESERVATION	r-bkl83j20	bangcloud	default
INSTANCE	i-0000001d	ami-00000002	172.16.39.121	172.16.39.121	running	vpsee (vpseecloud, node00)	0			2011-11-10T12:45:12Z	nova	aki-00000001	ami-00000000
RESERVATION	r-j335q6ny	bangcloud	default
INSTANCE	i-0000001e	ami-00000002	172.16.39.122	172.16.39.122	running	vpsee (vpseecloud, node00)	0			2011-11-10T12:54:27Z	nova	aki-00000001	ami-00000000

# euca-terminate-instances i-0000001d
# euca-terminate-instances i-0000001e

删除 OpenStack Nova Volume 时遇到的 error_deleting 问题 这篇文章提到的解决办法一样,直接操作数据库来删除这2条僵尸实例的记录。登录 mysql,使用 nova 数据库,找出要删除 instance 的 id,然后删除:

# mysql -u root -p
Enter password:

mysql> use nova;

mysql> select * from instances;

mysql> delete from instances where id = '29';
ERROR 1451 (23000): Cannot delete or update a parent row: a foreign key constraint fails (`nova`.`virtual_interfaces`, CONSTRAINT `virtual_interfaces_ibfk_1` FOREIGN KEY (`instance_id`) REFERENCES `instances` (`id`))

MySQL 删除 id 为 29 的 instance 时触发外键限制错误,简单的办法是暂时关闭外键检查,等删除后再打开:

mysql> SET FOREIGN_KEY_CHECKS=0;
Query OK, 0 rows affected (0.00 sec)

mysql> delete from instances where id = '29';
Query OK, 1 row affected (0.04 sec)

mysql> delete from instances where id = '30';
Query OK, 1 row affected (0.04 sec)

mysql> SET FOREIGN_KEY_CHECKS=1;
Query OK, 0 rows affected (0.00 sec)

删除 instance 29 和 30后再用 euca-describe-instances 命令验证一下:

# euca-describe-instances

解决 DataSourceEc2.py[WARNING]: ‘http://169.254.169.254′ failed: url error 问题

上周在一台 Ubuntu 11.10 服务器上安装和配置 OpenStack Nova 后,上传一个从 Ubuntu 官方下载的 oneiric-server-cloudimg-amd64.tar.gz 模板,然后启动一个 Ubuntu 11.10 实例(instance)的时候过了很长时间才能从 vnc 看到 Ubuntu login: 界面,打印出终端输出结果如下,貌似系统多次尝试从 http://169.254.169.254 得到 metadata 失败:

# euca-run-instances -k vpsee -t m1.tiny ami-00000002

# euca-get-console-output i-00000128
...
[    0.980222] init: lxcguest pre-start process (57) terminated with status 1
cloud-init start-local running: Mon, 24 Oct 2011 13:19:49 +0000. up 2.61 seconds
no instance data found in start-local
ci-info: lo    : 1 127.0.0.1       255.0.0.0
ci-info: eth0  : 1 172.16.38.2     255.255.254.0   02:16:3e:4f:61:4e
ci-info: route-0: 0.0.0.0         172.16.38.1     0.0.0.0         eth0   UG
ci-info: route-1: 172.16.38.0     0.0.0.0         255.255.254.0   eth0   U
cloud-init start running: Mon, 24 Oct 2011 13:19:52 +0000. up 5.23 seconds
2011-10-24 13:19:55,312 - DataSourceEc2.py[WARNING]: 'http://169.254.169.254' failed: url error [timed out]
2011-10-24 13:19:59,321 - DataSourceEc2.py[WARNING]: 'http://169.254.169.254' failed: url error [timed out]
2011-10-24 13:20:31,944 - DataSourceEc2.py[CRITICAL]: giving up on md after 208 seconds
...

找了一下资料发现网上有人用绑定 169.254.169.254 到 eth0 的办法,不过 VPSee 试了行不通。

$ sudo ip addr add 169.254.169.254/32 scope link dev eth0

metadata 的转发需要网关来完成,但是从下面的代码(nova/network/linux_net.py)来看,nova 只在 FlatDHCPManager 和 VlanManager 网络模式下调用 metadata_forward() 函数,nova 在 FlatManager 网络模式下不做任何设置,所以需要手动配置 iptable 转发 169.254.169.254 的 80 端口到 nova api 服务器上(网关)。

def metadata_forward():
    """Create forwarding rule for metadata"""
    _confirm_rule("PREROUTING", "-t nat -s 0.0.0.0/0 "
             "-d 169.254.169.254/32 -p tcp -m tcp --dport 80 -j DNAT "
             "--to-destination %s:%s" % (FLAGS.ec2_dmz_host, FLAGS.ec2_port))

所以解决办法有两个,要么在网关(nova api 所运行的服务器)上手动运行 iptable 定向端口:

$ sudo iptables -t nat -A PREROUTING -d 169.254.169.254/32 -p tcp -m tcp --dport 80 -j DNAT --to-destination 172.16.39.110:8773

要么改变 nova.conf 配置使用 FlatDHCPManager 模式:

$ sudo vi /etc/nova/nova.conf
--daemonize=1
--dhcpbridge_flagfile=/etc/nova/nova.conf
--dhcpbridge=/usr/bin/nova-dhcpbridge
--logdir=/var/log/nova
--state_path=/var/lib/nova
--lock_path=/var/lock/nova
--verbose
--ec2_host=http://172.16.39.110
--osapi_host=http://172.16.39.110
--rabbit_host=172.16.39.110
--glance_api_server=172.16.39.110:9292
--routing_source_ip=172.16.39.110
--sql_connection=mysql://vpsee:vpsee@172.16.39.110/nova
--image_service=nova.image.glance.GlanceImageService
--network_manager=nova.network.manager.FlatDHCPManager
--fixed_range=172.16.38.0/23
--flat_interface=dummy0
--public_interface=eth0

重启 nova 各个服务以后再重新启动一个新 instance 并输出 console:

# euca-run-instances -k vpsee -t m1.tiny ami-00000002

# euca-describe-instances
RESERVATION	r-0xzn6el3	cloud	default
INSTANCE	i-00000215	ami-00000002	172.16.39.121	172.16.39.121	running	vpsee (cloud, cloud00)	0		m1.tiny	2011-10-28T12:46:52Z	nova	aki-00000001	ami-00000000

# euca-get-console-output i-00000215
...
cloud-init start-local running: Fri, 28 Oct 2011 12:47:05 +0000. up 2.94 seconds
no instance data found in start-local
ci-info: lo    : 1 127.0.0.1       255.0.0.0
ci-info: eth0  : 1 172.16.39.121   255.255.254.0   02:16:3e:6d:9b:b6
ci-info: route-0: 0.0.0.0         172.16.38.1     0.0.0.0         eth0   UG
ci-info: route-1: 172.16.38.0     0.0.0.0         255.255.254.0   eth0   U
cloud-init start running: Fri, 28 Oct 2011 12:47:08 +0000. up 5.49 seconds
found data source: DataSourceEc2
Generating public/private rsa key pair.
...

最后用 ssh 登录刚启动的 instance 测试一下,很多朋友在这篇文章的评论里问到“我的 instance 可以 ping 通,为啥不能 ssh?”的问题,可能是 ssh 的时候忘了带上 key。需要注意的是 cloud-init 这个脚本在启动 instance 后自动把 ssh 密钥注射到了 instance,ssh 的时候需要带上 vpsee.pem(还记得启动的时候用了 # euca-run-instances -k vpsee …吗?),还需要注意的是 Ubuntu 官方下载的 oneiric-server-cloudimg-amd64.tar.gz 模板的默认用户名是 ubuntu(不是 root),不需要密码登录(用 ssh key 登录):

# ssh -i vpsee.pem ubuntu@172.16.39.121

Welcome to Ubuntu 11.10 (GNU/Linux 3.0.0-12-virtual x86_64)

 * Documentation:  https://help.ubuntu.com/

  System information as of Fri Oct 28 10:29:14 UTC 2011

  System load:  0.0               Processes:           54
  Usage of /:   46.2% of 1.35GB   Users logged in:     0
  Memory usage: 8%                IP address for eth0: 172.16.39.121
  Swap usage:   0%

  Graph this data and manage this system at https://landscape.canonical.com/
Get cloud support with Ubuntu Advantage Cloud Guest

http://www.ubuntu.com/business/services/cloud

To run a command as administrator (user "root"), use "sudo ".
See "man sudo_root" for details.

ubuntu@server-24:~$