如何避免 Xen VPS 用户自己修改 IP 地址

作为 Xen VPS 服务商,我们分配独立的 IP 地址给 VPS,我们不希望 VPS 用户自己能随便修改 IP 地址,因为这样有可能和其他用户的 IP 地址造成冲突,而且造成管理上的不便,所以需要绑定 IP 给某个 VPS.

解决这个问题的办法有很多,从路由器、防火墙、操作系统、Xen 等层面都可以做限制。这里介绍的两个简单方法都是从 dom0 入手:一个是在 dom0 上利用 Xen 配置;一个是在 dom0 上利用 iptables.

利用 Xen 配置

Xen 上有个 antispoof 配置选项就是来解决这个问题的,不过默认配置没有打开这个 antispoof 选项,需要修改:

# vi /etc/xen/xend-config.sxp
...
(network-script 'network-bridge antispoof=yes')
...

修改 /etc/xen/scripts/vif-common.sh 里面的 frob_iptable() 函数部分,加上 iptables 一行:

# vi /etc/xen/scripts/vif-common.sh
function frob_iptable()
{
    ...
    iptables -t raw "$c" PREROUTING -m physdev --physdev-in "$vif" "$@" -j NOTRACK
}

修改完 Xen 配置后还需要修改 domU 的配置,给每个 domU 分配固定 IP 和 MAC 地址,还有 vif 名字:

# vi /etc/xen/vm01
...
vif = [ "vifname=vm01,mac=00:16:3e:7c:1f:6e,ip=172.16.39.105,bridge=xenbr0" ]
...

很多系统上 iptables 在默认情况下都不会理会网桥上的 FORWARD 链,所以需要修改内核参数确保 bridge-nf-call-iptables=1,把这个修改可以放到 antispoofing() 函数里,这样每次 Xen 配置网络的时候会自动配置内核参数:

# vi /etc/xen/scripts/network-bridge
antispoofing () {
    echo 1 > /proc/sys/net/bridge/bridge-nf-call-iptables
...
}

修改完毕后测试的话需要关闭 domU,重启 iptables 和 xend 服务,再启动 domU.

# xm shutdown vm01
# /etc/init.d/iptables restart
# /etc/init.d/xend restart
# xm create vm01

上面的方法在 Xen 3.x 上 测试有效,有人说在 Xen 4.x 上行不通,我们下面将要介绍的方法绕开了 Xen 配置,直接从 iptables 限制,在 Xen 3.x 和 Xen 4.x 上应该都可以用。

利用 iptables

首先在 dom0 上确定 iptables 已经开启,这里需要注意的是一定要在每个 domU 的配置文件中的 vif 部分加上 vifname, ip, mac,这样才能在 iptables 规则里面明确定义:

# /etc/init.d/iptables restart

# vi /etc/xen/vm01
...
vif = [ "vifname=vm01,mac=00:16:3e:7c:1f:6e,ip=172.16.39.105,bridge=xenbr0" ]
...

# vi /etc/iptables-rules
*filter
:INPUT ACCEPT [0:0]
:FORWARD ACCEPT [0:0]
:OUTPUT ACCEPT [0:0]
# The antispoofing rules for domUs
-A FORWARD -m state --state RELATED,ESTABLISHED -m physdev --physdev-out vm01 -j ACCEPT
-A FORWARD -p udp -m physdev --physdev-in vm01 -m udp --sport 68 --dport 67 -j ACCEPT
-A FORWARD -s 172.16.39.105/32 -m physdev --physdev-in vm01 -j ACCEPT
-A FORWARD -d 172.16.39.105/32 -m physdev --physdev-out vm01 -j ACCEPT
# If the IP address is not allowed on that vif, log and drop it.
-A FORWARD -m limit --limit 15/min -j LOG --log-prefix "Dropped by firewall: " --log-level 7
-A FORWARD -j DROP
# The access rules for dom0
-A INPUT -j ACCEPT
COMMIT

# iptables-restore < /etc/iptables.rules

当然,别忘了:

# echo 1 > /proc/sys/net/bridge/bridge-nf-call-iptables

Docker 的一个 Web 管理界面 Shipyard

Docker 的命令行就已经很好用了,如果非要加上基于 Web 的管理界面的话也有一些选择,如 DockerUI (Angular.js), Dockland (Ruby), Shipyard (Python/Django) 等,不过目前来看 Shipyard 项目要活跃一点,Shipyard 支持多 host,可以把多个 Docker host 上的 containers 统一管理;可以查看 images,甚至 build images;并提供 RESTful API 等等。

Shipyard 要管理和控制 Docker host 的话需要先修改 Docker host 上的默认配置使其支持远程管理。修改配置文件 docker.conf,把 /usr/bin/docker -d 这行加上 -H tcp://0.0.0.0:4243 -H unix:///var/run/docker.sock 参数:

$ sudo vi /etc/init/docker.conf
description     "Run docker"

start on filesystem or runlevel [2345]
stop on runlevel [!2345]

respawn

script
    /usr/bin/docker -d -H tcp://0.0.0.0:4243 -H unix:///var/run/docker.sock
end script

可以独立安装 Shipyard 也可以把 Shipyard 安装在一个 Docker 容器里。如果有人提供了 Docker 镜像甚至连安装的过程也省了,直接下载运行就可以了,Shipyard 的作者就提供了这么一个镜像(需要注意的是这里的默认登录用户名和密码是 admin/shipyard):

$ sudo docker pull shipyard/shipyard

$ sudo docker run -i -t -d -p 80:80 -p 8000:8000 ehazlett/shipyard

如果不想使用上面的 Docker 镜像,想直接安装在服务器上的话也不是很麻烦,可以自行设置登录用户名和密码:

$ git clone https://github.com/shipyard/shipyard.git
$ cd shipyard/

$ sudo pip install -r requirements.txt
$ sudo python manage.py syncdb --noinput
$ sudo python manage.py migrate
$ sudo python manage.py createsuperuser
Username (leave blank to use 'root'):admin
Email address: test@vpsee.com
Password:
Password (again):
Superuser created successfully.

$ sudo python manage.py runserver 0.0.0.0:8000
Validating models...

0 errors found
November 15, 2013 - 03:46:23
Django version 1.6, using settings 'shipyard.settings'
Starting development server at http://0.0.0.0:8000/
Quit the server with CONTROL-C.

开另一个终端或 ssh 会话输入以下命令:

$ sudo python manage.py celery worker -B --scheduler=djcelery.schedulers.DatabaseScheduler -E

安装成功后打开浏览器访问 http://192.168.2.45:8000/ 就可以看到登录界面,输入用户名和密码登录成功后到左边的 Hosts 里添加一个 Docker host,输入 Docker host 的 IP 地址就可以了:

a docker web ui

成功添加一个 Docker host 后就可以访问到这台 host 上正在运行的 containers 以及保存的 images 了:

a docker web ui

搭建自己的 Docker 私有仓库服务

关于 Docker 的介绍这里就省了,Docker 在其相关领域的火爆程度不亚于今年汽车行业里的特斯拉,docCloud 甚至把公司名都改成了 Docker, Inc. 好东西总是传播很快,我们现在已经有客户在 VPS 上用 Docker 来部署应用了。不了解 Docker 的小伙伴们可以看看 使用 Docker/LXC 迅速启动一个桌面系统 开头部分的介绍。

和初次接触 Xen/KVM 虚拟技术的体验不同,Docker 不用自己动手制作镜像,官方已经提供了很多版本的 Linux 镜像,直接从官方仓库(Public Repositories)下载就可以了。如果考虑到安全性和速度,我们可能会想在自己局域网里架设一个私有仓库(Private Repositories)来放我们自己的镜像,Docker-Registry 正是我们需要的工具。

用 git 下载源码后修改配置文件 config.yml,把 storage_path 部分改成 Docker 镜像仓库的存放地点:

$ git clone https://github.com/dotcloud/docker-registry
$ cd docker-registry

$ cp config_sample.yml config.yml
$ vi config.yml
...
# This is the default configuration when no flavor is specified
dev:
    storage: local
    storage_path: /home/vpsee/registry
    loglevel: debug
...

$ mkdir /home/vpsee/registry

安装一些必要软件包和一些 Docker-Registry 需要用到的 Python 工具和库:

$ sudo apt-get install build-essential python-dev libevent-dev python-pip libssl-dev

$ sudo pip install -r requirements.txt

Docker-Registry 实际上是个基于 Flask 的 web app,安装成功后就可以这样运行了:

$ sudo gunicorn --access-logfile - --debug -k gevent -b 0.0.0.0:80 -w 1 wsgi:application

打开浏览器,访问 IP 地址就可以看到 docker-registry 私有仓库在运行了:

Docker-Registry

查看一下现有系统上已经有了哪些镜像:

$ sudo docker images
REPOSITORY      TAG        ID              CREATED         SIZE
vpsee/ubuntu    latest     936a54e8a345    2 weeks ago     12.29 kB (virtual 327.8 MB)
ubuntu          latest     8dbd9e392a96    6 months ago    131.5 MB (virtual 131.5 MB)
ubuntu          precise    8dbd9e392a96    6 months ago    131.5 MB (virtual 131.5 MB)
ubuntu          quantal    b750fe79269d    7 months ago    24.65 kB (virtual 180.1 MB)

我们打算把 vpsee/ubuntu 这个镜像(ID 是 936a54e8a345)上传(push)到我们刚创建的私有仓库里(这个私有仓库的 IP 地址是 192.168.2.45),会看到提示 Username/Password,初次 push 的话,可以自己设置用户名和密码:

$ sudo docker tag 936a54e8a345 192.168.2.45/vpsee
$ sudo docker push 192.168.2.45/vpsee
Username: vpsee
Password:
Email: docker@vpsee.com
Account created. Please use the confirmation link we sent to your e-mail to activate it.
The push refers to a repository [192.168.2.45/vpsee] (len: 1)
Processing checksums
Sending image list
Pushing repository 192.168.2.45/vpsee (1 tags)
Pushing 8dbd9e392a964056420e5d58ca5cc376ef18e2de93b5cc90e868a1bbc8318c1c
Buffering to disk 58266504/? (n/a)
Pushing 58.27 MB/58.27 MB (100%)

完成 push 后,我们的私有仓库就有了第一个镜像了:

$ sudo docker images
REPOSITORY          TAG      ID             CREATED        SIZE
vpsee/ubuntu        latest   936a54e8a345   2 weeks ago    12.29 kB (virtual 327.8 MB)
ubuntu              latest   8dbd9e392a96   6 months ago   131.5 MB (virtual 131.5 MB)
ubuntu              precise  8dbd9e392a96   6 months ago   131.5 MB (virtual 131.5 MB)
ubuntu              quantal  b750fe79269d   7 months ago   24.65 kB (virtual 180.1 MB)
192.168.2.45/vpsee  latest   936a54e8a345   2 weeks ago    12.29 kB (virtual 327.8 MB)

以后只要 docker pull 192.168.2.45/vpsee 就可以从我们自己的私有仓库下载和运行镜像了,本地网络速度当然会快很多。

开源虚拟桌面系统 Ulteo OVD

我们几年前用 Solaris + SunRay Software 实现的类 VDI 系统(不熟悉 SunRay 的小伙伴可看看 SunRay/Sun VDI 和 JavaOne由 SunRay 耗电量想到的),后来改成 CentOS + SunRay Software,SUN 被 Oracle 收购后事情变得复杂起来,Solaris 不再免费,SunRay Software 和 Hardware 貌似都被 Oracle 叫停。我们不得不开始寻找下一个替代品,开源 VDI 选择不多,Ulteo 可能是唯一的一个。

Mandrake Linux 的创始人 Gaël Duval 离开 Mandriva 公司后,开发了一套开源虚拟桌面(Open Virtual Desktop, OVD)系统 Ulteo,允许用户在任何设备上通过浏览器运行(远程服务器上的)Linux 或 Windows 应用程序(不过浏览器需要有 Java 的支持)。虽然 Ulteo 叫它 OVD,实际上就是我们通常所说的桌面虚拟化 VDI.

Ulteo 这个开源桌面虚拟化产品包括了一些新特性,比如:新的 Web 门户风格模块(Web Portal OVD module),可以让远程应用程序独占一个本地浏览器窗口,看上去像这个程序运行在本地一样;Ulteo 支持多个认证服务,比如可以很好的和我们内部的 LDAP 服务器连接起来,不用另外创建用户登陆;用户之间可以共享文件、文件夹;可以定制应用程序组和用户组,允许不同的用户使用不同的程序等等。

Ulteo OVD 安装很容易,可以作为独立的软件安装在现有的系统上(支持 Ubuntu/RHEL/CentOS/SUSE Linux Enterprise Server/Windows Server);可以下载已经定制好的 Ulteo OVD DEMO DVD 刻盘后安装,或者更懒一点,直接下载一个虚拟机镜像 Ulteo OVD DEMO VIRTUAL APPLIANCE(支持 VMWare Player, Citrix XenServer, VirtualBox 和 KVM)运行。

安装完 Ulteo 后看屏幕提示,默认用户名和密码都是 ulteo,如果要改变网络设置的话,可以运行 net-config.sh:

### Ulteo OVD 3.0 ###
With a web browser, you can connect to:
 - http://172.20.1.227/ovd            to launch a user session
 - http://172.20.1.227/ovd/admin      to administrate OVD

This virtual machine is configured to obtain the network configuration
using DHCP. If you don't have a DHCP server available, log as
"ulteo" (password "ulteo") and run:

sudo net-config.sh

To configure the basic network parameters. Reboot after configuration.

ulteo-ovd-3 login:

从客户端的浏览器上输入安装好 Ulteo 的服务器地址(这里的服务器 IP 地址是 172.20.1.227)就会出现 Ulteo 登陆界面,登陆前最好先到 http://172.20.1.227/ovd/admin/ 创建一个新用户。登陆后是标准的 Linux 桌面,运行很流畅:

ulteo ovd

对于那些使用内部 DNS 服务的环境来说 Ulteo 有个 DNS 配置小问题需要解决, 用 ssh 登陆 Ulteo 服务器后发现网络和 DNS 配置(/etc/resolv.conf)都没问题,但是从客户端浏览器登陆 Ulteo 后无法上网。查了一下日志发现 Ulteo 桌面实际上运行在一个 chroot 环境,需要 chroot 进去后修改桌面环境的 /etc/resolv.conf,而不是修改 Ulteo 服务器上的 /etc/resolv.conf):

$ sudo uchroot
[sudo] password for ulteo:
(OVD)root@ulteo-ovd-3:/#

(OVD)root@ulteo-ovd-3:/# vi /etc/resolv.conf
nameserver 192.168.1.80
nameserver 192.168.1.81

修改完成后,在客户端浏览器退出 Ulteo 界面重新登陆就可以了。

使用 Docker/LXC 迅速启动一个桌面系统

Docker 是 dotCloud 最近几个月刚宣布的开源引擎,旨在提供一种应用程序的自动化部署解决方案,简单的说就是,在 Linux 系统上迅速创建一个容器(类似虚拟机)并在容器上部署和运行应用程序,并通过配置文件可以轻松实现应用程序的自动化安装、部署和升级,非常方便。因为使用了容器,所以可以很方便的把生产环境和开发环境分开,互不影响,这是 docker 最普遍的一个玩法。更多的玩法还有大规模 web 应用、数据库部署、持续部署、集群、测试环境、面向服务的云计算、虚拟桌面 VDI 等等。

Docker 使用 Go 语言编写,用 cgroup 实现资源隔离,容器技术采用 LXC. LXC 已经足够成熟,被多个主流 PaaS 服务商采用(比如 dotCloud),国内的一些互联网公司也在用(比如腾讯)。虽然都是企图解决自动化部署方面的问题,Docker 的解决方式有别于我们常提到的 Puppet/Chef,他们虽然走的是不同的路,但也可以拿来一起用。

上面说了 Docker 有很多玩法,下面介绍的玩法是:在服务器上用 docker 创建桌面系统,然后在客户端上通过 ssh 远程连接桌面,可以看作是平民化的 VDI 解决方案。

安装 Docker/LXC

如果 Linux kernel 是 3.8 以前的内核,或者内核缺少 aufs 模块需要安装额外的扩展模块:

$ sudo apt-get install linux-image-extra-`uname -r`

安装 lxc-docker:

$ sudo apt-get install software-properties-common
$ sudo add-apt-repository ppa:dotcloud/lxc-docker
$ sudo apt-get update
$ sudo apt-get install lxc-docker

运行一个简单系统

运行 docker 就会自动下载一个 ubuntu 镜像(第一次运行才需要下载),并在一个 container(容器)里运行一个 ubuntu 系统(类似虚拟机)和 shell:

$ docker run -i -t ubuntu /bin/bash

在 host 上 ps 一下发现 docker 使用 lxc-start 工具和 /var/lib/docker/containers/…/config.lxc 里的参数启动1个 lxc 容器并运行 /bin/bash 程序:

$ ps aux | grep docker
root     28103  0.0  0.0  21164  1116 ?        S    10:52   0:00 lxc-start -n a581df505cb9ea07e93de28d76fc9b4e1ee48b981ce994740efdaa65d0d307a3 -f /var/lib/docker/containers/a581df505cb9ea07e93de28d76fc9b4e1ee48b981ce994740efdaa65d0d307a3/config.lxc -- /sbin/init -g 172.16.42.1 -e HOME=/ -e PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin -e DEBIAN_FRONTEND=noninteractive -- /bin/bash /src/startup.sh

运行一个 Hello world

启动一个 ubuntu 系统然后打印 hello world,囧,一个 ubuntu 系统的存在就是为了打印一个 hello world,世界变了,反了,操作系统和应用程序的地位颠倒了~

$ docker run -i -t ubuntu echo hello world
hello world

运行一个 “桌面” 系统

来看看如何启动一个 “桌面” 系统,按照 docker-desktop 项目给的方法这里的 Dockerfile开始 build 一个简单的 “桌面系统”:

$ docker build -t vpsee/docker-desktop git://github.com/rogaha/docker-desktop.git
Step 1 : FROM ubuntu:12.10
 ---> b750fe79269d
...
Successfully built 7774f89504e5

build 完后就可以用 docker images 命令看到:

$ docker images
REPOSITORY             TAG                 ID                  CREATED             SIZE
ubuntu                 12.04               8dbd9e392a96        3 months ago        131.5 MB (virtual 131.5 MB)
ubuntu                 12.10               b750fe79269d        3 months ago        24.65 kB (virtual 180.1 MB)
ubuntu                 latest              8dbd9e392a96        3 months ago        131.5 MB (virtual 131.5 MB)
ubuntu                 precise             8dbd9e392a96        3 months ago        131.5 MB (virtual 131.5 MB)
ubuntu                 quantal             b750fe79269d        3 months ago        24.65 kB (virtual 180.1 MB)
base                   latest              b750fe79269d        3 months ago        24.65 kB (virtual 180.1 MB)
base                   ubuntu-12.10        b750fe79269d        3 months ago        24.65 kB (virtual 180.1 MB)
base                   ubuntu-quantal      b750fe79269d        3 months ago        24.65 kB (virtual 180.1 MB)
base                   ubuntu-quantl       b750fe79269d        3 months ago        24.65 kB (virtual 180.1 MB)
vpsee/docker-desktop   latest              7774f89504e5        18 minutes ago      12.29 kB (virtual 1.576 GB)

启动这个刚 build 好的 “桌面系统” 吧,注意 a581df505cb9 是这个容器的 ID:

$ docker run -d vpsee/docker-desktop
a581df505cb9

这个系统有自己的内部 IP 地址,外界不能直接访问,所以要想从外界 ssh 登陆的话,需要把它的 ssh 服务端口(22)和 host 上某个端口通过 docker port 命令做个映射,这样访问 host 上的 49153 端口就是访问某个 container 里面的 22 端口了:

$ docker port a581df505cb9 22
49153

查看一下当前运行的容器:

$ docker ps
ID                  IMAGE                         COMMAND                CREATED             STATUS              PORTS
a581df505cb9        vpsee/docker-desktop:latest   /bin/bash /src/start   24 minutes ago      Up 24 minutes       49153->22

然后我们可以在 Mac/Linux 客户端上通过 ssh 访问这个 “桌面” 了:

$ ssh -YC -c blowfish docker@192.168.2.45 -p 49153 ./docker-desktop
docker@192.168.2.45's password:

注意上面的 password 是随机生成的(每次创建新系统密码都会变),那密码是什么呢?通过 docker logs 可以找到:

$ docker logs a581df505cb9
User: docker Password: ieFi2iu1Phie
Adding user `docker' to group `sudo' ...
Adding user docker to group sudo
Done.
.bashrc
.config/
.config/rox.sourceforge.net/
.config/rox.sourceforge.net/ROX-Filer/
.config/rox.sourceforge.net/ROX-Filer/pb_Default
.config/rox.sourceforge.net/ROX-Filer/globicons
.config/rox.sourceforge.net/ROX-Filer/panels
.config/rox.sourceforge.net/ROX-Filer/menus2
.config/rox.sourceforge.net/ROX-Filer/Options
spring-desktop-wallpaper-1920x1200-0911085.jpg
docker-desktop

从 Mac 上访问远端桌面的话需要有 X 环境(下载 XQuartz 安装即可);从 Linux 上可以直接访问。登陆后界面如下:

docker desktop

使用 noVNC 开发 Web 虚拟机控制台

OpenNebula 的控制面板 Sunstone 对 OpenNebula 私有云的管理员来说很方便实用,不用敲命令,但是对云计算、虚拟机不熟悉的用户来说有点复杂。所以,我们打算开发一个内部使用的 OpenNebula 控制面板,并和我们的其他内部服务集成起来。从不同 VPS 服务商那里用过 VPS 的用户都知道 VPS 控制面板的几个基本功能,创建、删除、重装、控制台访问。其中控制台访问功能的实现就是我们今天要讨论的主题。

我们知道不管是 VMware, Xen 还是 KVM,都可以配置 VNC 访问,然后通过 VNC 客户端访问这些虚拟机的控制台,这些 VNC 客户端往往需要下载安装,如果要开发虚拟机的 web 控制面板的话当然最好能配一个 web 的 VNC 客户端。

noVNC 正是我们需要的 HTML5 VNC 客户端,采用 HTML 5 WebSockets, Canvas 和 JavaScript 实现,noVNC 被普遍用在各大云计算、虚拟机控制面板中,比如 OpenStack Dashboard 和 OpenNebula Sunstone 都用的是 noVNC. 前面说了 noVNC 采用 WebSockets 实现,但是目前大多数 VNC 服务器都不支持 WebSockets,所以 noVNC 是不能直接连接 VNC 服务器的,怎么办呢?需要一个代理来做 WebSockets 和 TCP sockets 之间的转换,理解这一点很重要。这个代理也已经有了,在 noVNC 的目录里,叫做 websockify.

基本

下载 noVNC 代码后然后运行 noVNC/utils/websockify.py,把本机 VNC 服务(localhost)的端口(5900)和 noVNC 的代理端口(8000)连接起来,这样通过 noVNC/vnc.html 访问 8000 端口就自动转换到 5900 端口上(事实上 vnc.html 你可以放在任何机器上用浏览器直接打开用):

$ git clone https://github.com/kanaka/noVNC

$ cd utils
$ ./websockify.py 8000 localhost:5900
WARNING: no 'numpy' module, HyBi protocol is slower or disabled
WebSocket server settings:
  - Listen on :8000
  - Flash security policy server
  - No SSL/TLS support (no cert file)
  - proxying from :8000 to localhost:5900

流程大概是这样:

vnc.html -> 192.168.2.20:8000 -> websockify.py -> localhost:5900

当然代理和 VNC 服务可以不在同一机器上,比如代理在 192.168.2.20, VNC 服务在 192.168.2.21:

$ ./websockify.py 8000 192.168.2.21:5900

流程大概是这样:

vnc.html -> 192.168.2.20:8000 -> websockify.py -> 192.168.2.21:5900

理解了上面的过程,应该有想法如何集成 noVNC 到自己的程序了,很简单,先用 websockify 架一个代理,然后用 vnc.html 访问这个代理,vnc.html 和 vnc_auto.html 可以当作我们的范例程序参考。集成 noVNC 到自己的程序也没啥难度,按照官方文档修改相应参数就可以了,特别注意 INCLUDE_URI 这个变量。

如果想深入了解 OpenNebula 和 noVNC 请继续 ……

深入

如果给每个运行的 VNC 服务器开一个代理(和端口)是不是很繁琐?如果有成百上千个虚拟机呢?这么多虚拟机这么多端口怎么编程实现一一对应呢,是不是很麻烦?难道这么点东西还要个数据库来记录这些对应关系吗?我们来看一下 OpenNebula 是怎么实现 noVNC 集成的。

首先 OpenNebula 使用 websocketproxy.py 带 –target-config 参数的办法启动了代理,所有代理都在一个端口(29876)下,并会在指定的目录下(/var/lib/one/sunstone_vnc_tokens)生成一个对应的文件(one-96),打开这个文件就会看到前面的一串代码和后面的 VNC 服务(主机名:端口)对应起来。这样一个(启动了 VNC 服务的)虚拟机就对应在 sunstone_vnc_tokens 这个目录生成一个文件,文件内容能识别这个代理对应到哪个主机上的 VNC 服务。

# ps aux | grep websockify
oneadmin 13661  0.0  0.2 197576  8388 ?        S    Jul04   0:07 python /usr/share/one/websockify/websocketproxy.py --target-config=/var/lib/one/sunstone_vnc_tokens 29876

# cat /var/lib/one/sunstone_vnc_tokens/one-96
cirjhccnthfinxpdlig: cloud32:5900

修改 noVNC/vnc_auto.html 文件的 host, port, path 部分,注意 path 是带 token 的,token 的参数要和上面的那串奇怪的代码一致:

...
      host = "192.168.2.20";
      port = "29876";
...
      path = "websockify/?token=cirjhccnthfinxpdlig";
...

我们编程时只要改变上面的 token 参数就可以切换连接到不同的虚拟机 VNC,这样编程就方便多了。这个是我们利用 vnc_auto.html 修改过后的 VNC 连接界面(为了配合博客美观,截图有意缩小了):
novnc

如果对我们的控制面板感兴趣的话,可以看看截图,只完成了最最基本的几个功能,算作工作时间外的 side project,还在不紧张的开发中,开发工具是 Python/Flask/Bootstrap/MongoDB.
vpsee cloud

使用 ionice 限制 Xen 虚拟机磁盘 IO

作为 VPS 服务商我们需要保证每个 VPS 公平的使用 host(服务器)的资源,避免某个 VPS 因为程序死循环、挂起、滥用等因素 “拖累” 其他 VPS,如果出现这个情况如何临时限制这个 VPS 的磁盘 IO 呢?有个办法是通过通过修改每个虚拟机 CPU 权重的办法间接、不精确的限制 IO. 在 Linux 上限制资源(CPU、内存、IO 等)的通常办法是用 cgroups,不过今天介绍的 ionice 要更容易一些。

首先找到哪个虚拟机(VPS)正在大量 IO(假设是 vps0001),找到这个虚拟机后用 xm list 查出这个虚拟机使用的 ID 号,然后用 ID 配上 blkback(blkback.24)找出这个虚拟机(通过 Xen 的 blkback 驱动)关联哪些硬盘(blkback.24.xvda 和 blkback.24.xvdb),以及所使用的进程号(25089 和 25090):

# xm list vps0001
Name                                      ID Mem(MiB) VCPUs State   Time(s)
vps0001                                   24     1024     2 -b----  70030.7

# ps aux | grep blkback.24
root      7434  0.0  0.1  61172   768 pts/16   D+   02:48   0:00 grep blkback.24
root     25089  0.0  0.0      0     0 ?        S<    2012   0:00 [blkback.24.xvda]
root     25090  0.0  0.0      0     0 ?        S<    2012   0:00 [blkback.24.xvdb]

找到进程号后我们就可以 ionice 了:

# ionice -p 25089 -c 2 -n 7

使用 ionice 之前查一下帮助文件,-c 是指定调度类型,这里选择的是 2,best-effort;-n 指定调度优先级,0 最高,7最低;-p 是指定进程号:

OPTIONS
-c The scheduling class. 1 for real time, 2 for best-effort, 3 for
idle.

-n The scheduling class data. This defines the class data, if the
class accepts an argument. For real time and best-effort, 0-7 is
valid data.

-p Pass in a process pid to change an already running process. If
this argument is not given, ionice will run the listed program
with the given parameters.

ionice 把磁盘 IO 调度分成三类:

  • real time 实时调度,设置后立即访问磁盘,不管系统中其他进程是否有 IO,可能会使得其他进程处于等待状态,不能用在这里;
  • best effort 默认调度,可以指定调度优先级(从0到7,数值越小、优先级越高);同一优先级的进程采用 round-robin 算法调度;
  • idle 空闲调度,只有当前系统没有其他进程磁盘 IO 时,才能进行磁盘 IO.

额,如果太过分,我们就把这个进程的调度改成 idle,这样会极大降低这个虚拟机的 IO,虚拟机只能保持基本可用状态,不推荐~

# ionice -p 25089 -c 3

升级 OpenNebula 3.x 到 OpenNebula 4.0

正在测试 OpenNebula 的升级工作,安装和测试新版 OpenNebula 4.0 是一件事,还有一件事是测试升级过程中可能遇到的问题和解决办法。升级是危险操作,每一个阶段都可能会有问题,所以升级前的各种备份和测试工作都需要准备妥当,配置文件、虚拟机、数据库等等都是必须备份的。好的升级除了应该保证数据完整、各部分运行正常外还应该尽量让用户察觉不到。

OpenNebula 官方文档说如果要升级的话需要参考相关版本的文档然后逐个版本依次升级:

If you are upgrading from a version prior to 3.8, read the 3.4 upgrade guide, 3.6 upgrade guide and 3.8 upgrade guide for specific notes.

逐个版本升级的过程太麻烦,我们看看能不能一次升级到位。

先停掉 OpenNebula 3.x 的相关服务:

$ sunstone-server stop
$ econe-server stop
$ occi-server stop
$ oneacctd stop
$ one stop

导出数据库(会在 /var/lib/one 下生成一个名为 one.db.bck 的备份文件):

$ onedb backup -v -f --sqlite /var/lib/one/one.db

关闭所有虚拟机后,然后在控制节点和计算节点正常安装 OpenNebula 4.0.

安装完 OpenNebula 4.0 后用新版的 onedb 升级数据库(也就是升级 one.db.bck 里面的表结构、字段等),这个升级过程将会自动从 3.2.1 到 3.3.0 到 3.3.80 到 … 逐个版本升级到 3.9.80,整个过程完成后会纪录在当前目录的 one.upgrade.log 日志文件里:

$ onedb upgrade -v -f --sqlite one.db.bck

注意升级前(onedb upgrade)一定要关闭所有虚拟机,否则升级数据库会报错:

> Running migrator /usr/lib/one/ruby/onedb/3.3.0_to_3.3.80.rb
You can't have active VMs. Please shutdown or delete the following VMs:
...

如果不想(或者不允许)关闭所有虚拟机的话,可以通过直接修改数据库的办法 “骗过” 升级脚本(onedb),只需要到数据库里手动把所有 VMs 的状态(state)改成 shutdown 就可以了:

$ sqlite3 one.db.bck
sqlite> SELECT oid,name,state,lcm_state FROM vm_pool WHERE (state <> 1 AND state <> 6);
sqlite> UPDATE vm_pool set state=1 WHERE (state <>1 and state <>6);

数据库升级完后就要开始升级配置文件,OpenNebula 4.0 一些配置文件、参数以及目录结构有改动,比如 OpenNebula 配置文件里的一些驱动名改了,im_kvm 改成了 kvm、vmm_kvm 改成了 kvm,为了兼容,所以需要增加 im_kvm 和 vmm_kvm. 修改 oned.conf 配置文件:

$ vi /etc/one/oned.conf
...
IM_MAD = [
      name       = "kvm",
      executable = "one_im_ssh",
      arguments  = "-r 0 -t 15 kvm" ]
IM_MAD = [
      name       = "im_kvm",
      executable = "one_im_ssh",
      arguments  = "-r 0 -t 15 kvm" ]
...
VM_MAD = [
    name       = "kvm",
    executable = "one_vmm_exec",
    arguments  = "-t 15 -r 0 kvm",
    default    = "vmm_exec/vmm_exec_kvm.conf",
    type       = "kvm" ]
VM_MAD = [
    name       = "vmm_kvm",
    executable = "one_vmm_exec",
    arguments  = "-t 15 -r 0 kvm",
    default    = "vmm_exec/vmm_exec_kvm.conf",
    type       = "kvm" ]
...

注意 authn 这一行需要有 server_cipher:

$ vi /etc/one/oned.conf
...
AUTH_MAD = [
    executable = "one_auth_mad",
    authn = "ssh,x509,ldap,server_cipher,server_x509"
]
...

/var/lib/one 目录结构中增加了一些新目录,比如 datastore, vms 等,OpenNebula 3.x 老版本的虚拟机镜像在 /var/lib/one 需要移到新的 /var/lib/one/vms 下,所以需要登陆到每个 OpenNebula 节点移动虚拟机镜像目录:

$ mkdir /var/lib/one/vms
$ mv /var/lib/one/[0-9]* /var/lib/one/vms

然后启动所有服务和虚拟机、观察日志文件,看看新版 OpenNebula 有什么问题:

$ one start
$ occi-server start
$ sunstone-server start

在 CentOS 6.4 上安装和配置 OpenNebula 4.0

我们实验室的 OpenNebula 3.2 已经很稳定的运行了两年,除了开头一个月不熟悉这套云计算软件有点乱、容易犯错外接下来的时间里都很稳定,期间还包括一次防火演习(突然拉闸似断电)和安全检查(计划中的断电),服务器、虚拟机、存储设备、系统和服务都能自动起来并能正常工作。现在正在考虑升级的事情,OpenNebula 4.0 有很多新特性值得一试,现在的问题是升级很麻烦,官方文档貌似说要逐个版本升级,这个是小问题,更大的问题是如何升级整个运行中的生产环境,这里面有太多的因素需要考虑到,想了都头大~先安装一个 OpenNebula 4.0 试玩一下,看看数据库表结构、一些工具、配置文件,看看能不能琢磨出一个可行的升级方案出来,具体的升级操作将会在圣诞节期间进行(人最少的时候),不过现在应该开始计划和测试了。

下面的安装过程采用最小化的 CentOS 6.4 安装版本(CentOS-6.4-x86_64-minimal.iso)。不熟悉 OpenNebula 和云计算的朋友可以先看看 “在 CentOS 上安装和配置 OpenNebula” 的开头部分预热一下。

开始之前先装上 EPEL 源,然后升级系统:

# yum install wget
# wget http://dl.fedoraproject.org/pub/epel/6/x86_64/epel-release-6-8.noarch.rpm
# rpm -ivh epel-release-6-8.noarch.rpm
# yum update

到官网 http://downloads.opennebula.org/ 下载 OpenNebula 4.0 for CentOS 6.4 的安装包、解压后安装,因为有包依赖问题,所以最好用 yum localinstall 安装,不要用 rpm -ivh 命令直接安装。在控制节点(或叫做头节点)上安装 opennebula 和 opennebula-sunstone 等:

# cd /usr/src
# tar zxvf CentOS-6.4-opennebula-4.0.0-1.tar.gz
# cd opennebula-4.0.0-1

# yum localinstall opennebula-common-4.0.0-1.x86_64.rpm
# yum localinstall opennebula-ruby-4.0.0-1.x86_64.rpm
# yum localinstall opennebula-4.0.0-1.x86_64.rpm
# yum localinstall opennebula-sunstone-4.0.0-1.x86_64.rpm
# yum localinstall opennebula-server-4.0.0-1.x86_64.rpm

在计算节点上安装 kvm 和 opennebula-node-kvm:

# yum install qemu-kvm qemu-kvm-tools libvirt
# yum localinstall opennebula-node-kvm-4.0.0-1.x86_64.rpm

# /etc/init.d/libvirtd start

安装搞定,OpenNebula 4.0 的安装就是这么简单,开始在控制节点上启动服务吧:

# service opennebula start
Starting OpenNebula daemon:                                [  OK  ]
# service opennebula-sunstone start
Starting Sunstone Server daemon: VNC proxy started
sunstone-server started
                                                           [  OK  ]

我们通常都会通过 IP 访问(或者内部域名)访问 sunstone 界面,所以需要改变 sunstone 的默认接听地址,把 host: 127.0.0.1 换成机器所在 IP 地址,需要重启服务让设置生效。注意这里使用 9869 端口,CentOS 6.x 默认是开启防火墙的,所以可以把这个端口加到防火墙规则里也可以直接把防火墙关掉:

# vi /etc/one/sunstone-server.conf
...
# Server Configuration
#
:host: 192.168.2.150
:port: 9869

# service opennebula-sunstone restart
Stopping Sunstone Server daemon: VNC server is not running
sunstone-server stopped
                                                           [  OK  ]
Starting Sunstone Server daemon: VNC proxy started
sunstone-server started
                                                           [  OK  ]

# /etc/init.d/iptables stop
iptables: Flushing firewall rules:                         [  OK  ]
iptables: Setting chains to policy ACCEPT: filter          [  OK  ]
iptables: Unloading modules:                               [  OK  ]

打开浏览器,访问 http://192.168.2.150:9869 登陆界面,那登陆的用户名和密码是啥呢?用户名和密码在安装的时候已经随机生成,oneadmin 是用户名,后面的一串是密码:

# cat /var/lib/one/.one/one_auth
oneadmin:0dee417dfb22f2372866d686c7b12889

登陆成功后界面比以前的版本有很大改进:

opennebula 4.0 sunstone

OneStack:分步部署 All-in-One 的 OneStack 实验环境

囧,上次 OneStack 项目作者 Kayven 发过来的 OneStack 介绍和配置的文章只发了一半就忘了(本来打算分两部分发的~),今天在草稿箱里把这部分整理出来了,希望文章内容还没有过时(OpenStack 和 Ubuntu 版本都有了很大变化 …)。

前部分介绍请看:OneStack:Ubuntu 12.04 上一键自动部署 OpenStack。下面介绍的是用 OneStack 分步部署 OpenStack 的方法:

1、切换 root,或者用 sudo 执行脚本:

$ sudo -i

2、(可选,如果不需要跳过本步骤):
系统语言设置,可以参考 oneStack.sh locale 部分,不在此介绍。比如设置 apt 源:

# vi /etc/apt/sources.list

3、设置网络(可以参考 oneStack.sh locale 部分):

# vi /etc/network/interfaces

4、配置参数,除了网络 ip,其它可以不变:

## 数据库
MYSQL_PASSWD=${MYSQL_PASSWD:-"cloud1234"}
## 自行检查下面network/interfaces的两个网卡设置与此处一致
OUT_IP="192.168.139.50"
## 选择虚拟技术,裸机使用kvm,虚拟机里面使用qemu
VIRT_TYPE="qemu"
## token, 登录dashboard密码
ADMIN_TOKEN="admin"

5、部署基本系统

./setup_base.sh

6、添加镜像(Ubuntu12.04),添加一个实例,注意:里面的 ip 需要自己配置,镜像地址可以自己改动:

./setup_test.sh

7、分别部署控制节点和计算节点:
控制节点同上,可以去掉 nova-compute;
计算节点,只需要配置网络、数据库、虚拟机技术即可。

配置参数,除了计算节点 ip,其余与控制节点相同(包括 nova.conf 里的 ip)自行检查下面 network/interfaces 的两个网卡设置:
ServerControlIP=”192.168.139.50″
computeControlIP=”192.168.139.150″ # 本计算节点的外网IP

## token, 登录 dashboard 密码
ADMIN_TOKEN="admin"

## 网络配置
NETWORK_CONF=${NETWORK_CONF:-"/etc/network/interfaces"}
cat <$NETWORK_CONF
# The primary network interface
auto eth0
iface eth0 inet static
pre-up ifconfig eth0 hw ether b8:ac:6f:9a:ee:e5 #不需要绑定网卡的不需要这一行
        address 192.168.139.51 #外网ip
auto eth1
iface eth1 inet static
pre-up ifconfig eth1 hw ether b8:ac:6f:9a:ee:e5
        address 10.0.0.2 #内网IP

INTERFACES
sed -i -e "s/192.168.139.51/$computeControlIP/g" $NETWORK_CONF
/etc/init.d/networking restart

注意:以下与控制节点相同

## 配置 /etc/nova/nova.conf,这里与控制节点的配置相同!比如ip是控制节点的ip
MYSQL_PASSWD=${MYSQL_PASSWD:-"cloud1234"}
NOVA_DB_USERNAME=${NOVA_DB_USERNAME:-"novadbadmin"}
NOVA_DB_PASSWD=${NOVA_DB_PASSWD:-"cloud1234"}
OUT_IP="192.168.139.50" 
……
## 选择虚拟技术,裸机使用 kvm,虚拟机里面使用 qemu
VIRT_TYPE="qemu"

7、执行 ./addComputeNode.sh

./addComputeNode.sh

如果对这篇有任何技术问题请直接咨询 OneStack 项目的原作者 Kayven (Hily.Hoo@gmail.com),git/github 粉也可以从这里获得源码: https://github.com/Kayven/OneStack