用 iptables 屏蔽来自某个国家的 IP

星期六我们一位客户受到攻击,我们的网络监测显示有连续6小时的巨大异常流量,我们立即联系了客户,没有得到回应,我们修改和限制了客户的 VPS,使得个别 VPS 受攻击不会对整个服务器和其他 VPS 用户造成任何影响,我们一直保持这个 VPS 为开通状态(尽管一直受攻击),攻击又持续了24小时,星期天攻击仍在继续,我们忍无可忍,但是仍然无法联系到客户,我们向客户网站的另一负责人询问是否需要我们介入来帮助解决,这位负责人答应后我们立即投入到与 DDoS 的战斗中(我们动态扫描屏蔽坏 IP,现在客户网站已恢复。整个过程很有意思,以后有时间再写一篇博客来描述)。登录到客户 VPS 第一件事情就是查当前连接和 IP,来自中国的大量 IP 不断侵占80端口,典型的 DDoS. 所以第一件事是切断攻击源,既然攻击只攻80端口,那有很多办法可以切断,直接关闭网站服务器、直接用防火墙/iptables 切断80端口或者关闭所有连接、把 VPS 网络关掉、换一个 IP,⋯,等等。因为攻击源在国内,所以我们决定切断来自国内的所有访问,这样看上去网站好像是被墙了而不是被攻击了,有助于维护客户网站的光辉形象:D,那么如何屏蔽来自某个特定国家的 IP 呢?

方法很容易,先到 IPdeny 下载以国家代码编制好的 IP 地址列表,比如下载 cn.zone:

# wget http://www.ipdeny.com/ipblocks/data/countries/cn.zone

有了国家的所有 IP 地址,要想屏蔽这些 IP 就很容易了,直接写个脚本逐行读取 cn.zone 文件并加入到 iptables 中:

#!/bin/bash
# Block traffic from a specific country
# written by vpsee.com

COUNTRY="cn"
IPTABLES=/sbin/iptables
EGREP=/bin/egrep

if [ "$(id -u)" != "0" ]; then
   echo "you must be root" 1>&2
   exit 1
fi

resetrules() {
$IPTABLES -F
$IPTABLES -t nat -F
$IPTABLES -t mangle -F
$IPTABLES -X
}

resetrules

for c in $COUNTRY
do
        country_file=$c.zone

        IPS=$($EGREP -v "^#|^$" $country_file)
        for ip in $IPS
        do
           echo "blocking $ip"
           $IPTABLES -A INPUT -s $ip -j DROP
        done
done

exit 0

好 IP 和坏 IP 都被屏蔽掉了,这种办法当然不高明,屏蔽 IP 也没有解决被攻击的问题,但是是解决问题的第一步,屏蔽了攻击源以后我们才有带宽、时间和心情去检查 VPS 的安全问题。公布一份我们客户被攻击的网络流量图,在18点到0点所有带宽都被攻击流量占用,这时候客户无法登录 VPS,访问者也无法访问网站:

ddos

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

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

什么吃掉了我的硬盘?

经常收到我们的 VPS 用户询问,“我的 VPS 没装新东西,为什么硬盘越用越少了?”,这多半是因为系统上的各种日志长期积累下来的结果,比如 nginx, apache 等留下来的访问日志信息长时间不清理、而且访问量大的话会很容易就膨胀到 GB;有时候某种应用程序的日志藏在文件系统的深处,不易发觉;有时候自己拷贝了一些大文件放在某个地方时间长了就忘了。那么如何在 Linux 系统上找到这些占用硬盘空间的文件呢?

首先我们可以统计一下根目录下各个目录占用硬盘的情况,找出最占用硬盘的那个目录:

# du -sh /*
6.2M	/bin
17M	/boot
136K	/dev
97M	/etc
147G	/home
584M	/lib
16M	/lib64
16K	/lost+found
8.0K	/media
12K	/mnt
252K	/opt
0	/proc
1.7G	/root
28M	/sbin
8.0K	/selinux
3.1M	/srv
0	/sys
20K	/tmp
1.9G	/usr
748M	/var

从上面可看出 /home 占用了最多的硬盘,我们去看看 /home 下哪些用户占用了最多的空间:

# du -sh /home/*
4.0K	/home/bak
106M	/home/cos
28K	/home/guest
16K	/home/lost+found
105G	/home/vpsee
33G	/home/somebody
8.2G	/home/abc

可以看到 vpsee 这个用户和他的 home 目录 /home/vpsee 用掉了最多的硬盘(105GB),我们再看看 /home/vpsee 下究竟哪些文件占用了空间,用下面的一行命令找出某个目录下(这里是 /home/vpsee)大小超过 500MB 的文件(打印前40行并按照 MB 从小到大排列):

# find /home/vpsee -printf "%k %p\n" | sort -g -k 1,1 | \
awk '{if($1 > 500000) print $1/1024 "MB" " " $2 }' |tail -n 40

647.68MB  /home/vpsee/linux/debian-504-amd64-CD-1.iso
675.664MB /home/vpsee/linux/Fedora-13-i686-Live.iso
677.656MB /home/vpsee/unix/osol-0906-x86.iso
678.172MB /home/vpsee/linux/ubuntu-10.04-server-amd64.iso
700.133MB /home/vpsee/linux/ubuntu-10.04-desktop-i386.iso
1304.64MB /home/vpsee/mac/MacTeX.mpkg.zip

可以看到 /home/vpsee 是个 iso 收藏狂,收了一堆 Linxu ISO 安装文件,以前拨号上网时代大家都喜欢收集一些软件和工具存在硬盘里,现在网络这么发达,这些旧习惯已经没必要了,ISO 文件可有可无,需要的时候就去下一个最新的,用不着自己保留,网络就是我们的硬盘。

在 NetBSD 上安装和配置 OpenVPN

关于 OpenVPN 在 Linux 上的安装配置都写烂了,搜索一下到处都有,但是 NetBSD 上的资料并不多,VPSee 在这里简单记录一下在 NetBSD 上配置 OpenVPN 的过程,步骤和流程都是和 Linux 一样的,只不过具体操作的细节不同,相信我们的客户和熟悉 Linux 上 OpenVPN 的朋友能很快用我们的 64MB NetBSD VPS 架设一台经济实惠的个人用 OpenVPN 服务器出来。以下步骤在我们的 NetBSD VPS 上执行通过:

安装 OpenVPN Server

源码安装 OpenVPN 服务器(如果自己 /usr/ 下没有 pkgsrc 这个目录的话参考这篇文章:下载 pkgsrc 去下载解压一个):

# cd /usr/pkgsrc/net/openvpn
# make install clean clean-depends

拷贝 openvpn 启动配置文件到 NetBSD 的启动配置目录下,然后编辑 /etc/rc.conf 以便系统启动的时候自动运行 NetBSD:

# cp  /usr/pkg/share/examples/rc.d/openvpn /etc/rc.d

# vi /etc/rc.conf 
openvpn=YES

拷贝创建认证需要的一些脚本到 openvpn 的配置目录:

# cp /usr/pkg/share/openvpn/easy-rsa/* /usr/pkg/etc/openvpn/

给 vars 文件加写权限,然后做一些必要的编辑和定制:

# chmod +w /usr/pkg/etc/openvpn/vars
# vi /usr/pkg/etc/openvpn/vars

因为 vars 脚本需要 bash 才能运行,所以要先在 NetBSD 上安装 bash:

# cd /usr/pkgsrc/shells/bash2
# make install clean clean-depends

安装完 bash 后切入到 bash shell,然后运行:

# bash
bash-2.05b# source /usr/pkg/etc/openvpn/vars
NOTE: If you run ./clean-all, I will be doing a rm -rf on /usr/pkg/etc/openvpn/keys

现在开始创建 certificate authority (CA):

# cd /usr/pkg/etc/openvpn/
# ./build-ca

给服务器创建 certificates 和 private key:

# ./build-key-server server

给客户端创建 keys:

# ./build-key client

创建 Diffie Hellman 密钥,Diffie–Hellman 是一种安全协议,可让双方在没有任何对方预先信息的条件下通过不安全隧道创建一个密钥,并且这个密钥可用在后续的通讯中来加密通讯内容。OpenVPN 必须要 Diffie Hellman 密钥:

# ./build-dh

拷贝 OpenVPN Server 的样例配置文件到 openvpn 的配置文件目录(如果客户端不幸需要通过 http 代理访问 openvpn server 的话,可以在 server.conf 做一些设置,具体看:用代理连接 OpenVPN 的问题):

# cp /usr/pkg/share/examples/openvpn/config/server.conf /usr/pkg/etc/openvpn/

启动 OpenVPN 服务器,首次启动出错是因为没有找到 dh1024.pem 文件,把 dh1024.pem 等文件从 /usr/pkg/etc/openvpn/keys 里拷到上级目录后再次启动就成功了:

# /etc/rc.d/openvpn start
Starting openvpn.
Aug  4 20:20:19  openvpn[1266]: Cannot open dh1024.pem for DH parameters: error:02001002:system library:fopen:No such file or directory: error:2006D080:BIO routines:BIO_new_file:no such file

# mv /usr/pkg/etc/openvpn/keys/* /usr/pkg/etc/openvpn/

# /etc/rc.d/openvpn start
Starting openvpn.
add net 10.8.0.0: gateway 10.8.0.2

查看 OpenVPN 服务器正常启动后是否生成 tun 设备:

# ifconfig -a
tun0: flags=8051 mtu 1500
        inet 10.8.0.1 -> 10.8.0.2 netmask 0xffffffff
        inet6 fe80::216:3eff:fe55:4f16%tun0 ->  prefixlen 64 scopeid 0x3

在内核打开 IP 转发功能,并把 IP 转发写到启动文件:

# sysctl -w net.inet.ip.forwarding=1

# vi /etc/sysctl.conf
net.inet.ip.forwarding=1

安装 OpenVPN Client

OpenVPN 客户端软件有多种、有支持 Linux, MacOS X 和 Windows 的、有图形界面的、也有命令行的,根据自己系统和喜好挑一个客户端,在 Mac 上 VPSee 推荐开源的 Tunnelblick,不同客户端的配置都差不多,主要是把服务器上的 ca.crt, client.crt, .csr, .key 等文件拷到客户端,然后调整一下客户端的配置以连接上服务器。

连接测试

连接后就会在客户端的日志看到类似这样的成功连接信息:

tunnelblick

NetBSD 上安装和配置 Nginx+PHP+FastCGI+MySQL

NetBSD 支持 n 种硬件架构,是地球上支持最多体系的系统;OpenBSD 来自 NetBSD,自称是地球上最安全的系统;FreeBSD 和 NetBSD 一样来自原始的 4.4 BSD-lite,但是 FreeBSD 现在主要支持 i386,在 PC 体系上 FreeBSD 比 NetBSD/OpenBSD 好很多,性能也最好。这三种 BSD 各有特点,和不同的 Linux 发行版一样每个系统都有很多粉丝,当然使用 FreeBSD 的人最多。VPSee 的一台老掉牙的 IBM ThinkPad 就在跑 FreeBSD,嗯,现在这台机器还在工作呢。虽然 NetBSD 的性能在 i386 上没有 FreeBSD 那么好,但是它的稳定性是非常的赞,Benchmarking BSD and Linux 这篇测试的作者说:

Please note that NetBSD was the only BSD that never crashed or panicked on me, so it gets favourable treatment for that.

我们上周五发布了 NetBSD VPS,我们鼓励用户自己动手安装 Nginx+PHP+FastCGI+MySQL 和熟悉 NetBSD 环境,这篇文章在我们的 NetBSD VPS 上搭建:

下载 pkgsrc

The NetBSD Packages Collection (pkgsrc) 是 NetBSD 下的基于源码的软件包管理系统,不同于 Debian/Ubuntu 上的 apt-get,pkgsrc 是基于源代码的方式管理软件包的,有点像 Gentoo 里面的 emerge 系统。pkgsrc 有三个分支,HEAD、pkgsrc-yyyyQqq 和 pkgsrc-wip,前两个是 pkgsrc 项目组的正式分支。我们在这里使用 pkgsrc-yyyyQqq(季度分支),下载和解压 pkgsrc 并放到合适的目录:

# ftp ftp://ftp.NetBSD.org/pub/pkgsrc/pkgsrc-2010Q2/pkgsrc-2010Q2.tar.bz2
# tar jxvf pkgsrc-2010Q2.tar.bz2
# mv pkgsrc /usr/

安装 Nginx+PHP+FastCGI+MySQL

编译和安装 PHP,注意编译 PHP 的时候需要加上 fastcgi 说明,这样 PHP 编译才会把 FastCGI 部分编译进去:

# vi /etc/mk.conf
PKG_OPTIONS.php = fastcgi

# cd /usr/pkgsrc/lang/php5
# make install clean clean-depends

配置 PHP:

# /usr/pkg/etc/php.ini
cgi.fix_pathinfo=1

编译和安装 FastCGI:

# cd /usr/pkgsrc/www/spawn-fcgi/
# make install clean clean-depends

编译和安装 Nginx:

# cd /usr/pkgsrc/www/nginx
# make install clean clean-depends

配置 Nginx:

# cp /usr/pkg/share/examples/rc.d/nginx /etc/rc.d/
# vi /usr/pkg/etc/nginx/nginx.conf
location / {
            root   share/examples/nginx/html;
            index  index.html index.htm index.php;
        }

...

location ~ \.php$ {
            root           html;
            fastcgi_pass   127.0.0.1:9000;
            fastcgi_index  index.php;
            fastcgi_param  SCRIPT_FILENAME  /usr/pkg/share/examples/nginx/html$f
astcgi_script_name;
            include        /usr/pkg/etc/nginx/fastcgi_params;
        }

把 Nginx 加到启动文件以便启动系统时自动启动:

# vi /etc/rc.conf 
nginx=YES

编译和安装 MySQL:

# cd /usr/pkgsrc/databases/php-mysql
# make install clean clean-depends

# cd /usr/pkgsrc/databases/mysql5-server
# make install clean clean-depends

配置 PHP 以便加载 MySQL 动态连接库:

# vi /usr/pkg/etc/php.ini
extension=mysql.so

把 MySQL 加到启动文件以便启动系统时自动启动:

# cp /usr/pkg/share/examples/rc.d/mysqld /etc/rc.d/

# vi /etc/rc.conf 
mysqld=YES

启动 FastCGI:

# /usr/pkg/bin/spawn-fcgi -a 127.0.0.1 -p 9000 -f /usr/pkg/libexec/cgi-bin/php
spawn-fcgi: child spawned successfully: PID: 18554

启动 Nginx:

# /etc/rc.d/nginx start
Starting nginx.

启动 MySQL 和设置 root 密码:

# /etc/rc.d/mysqld start

# /usr/pkg/bin/mysqladmin -u root -p password 'new-password'

测试

写一个 phpinfo() 文件放在 nginx 目录下,然后打开浏览器测试是否能正确访问 php 文件:

# vi /usr/pkg/share/examples/nginx/html/index.php


http://www.vpsee.com/index.php

监测 Linux 进程的实时 IO 情况

作为系统管理员和 VPS 服务商,经常会碰到服务器或者 VPS 磁盘 IO 繁忙的时候,VPSee 通常都会用一些工具来检测,其中一个常用的工具就是自己写的 iotop 脚本,可以很方便看到哪个进程在频繁 IO. 上周五收到一位网友的邮件和留言,问到这篇文章:如何查看进程 IO 读写情况?里的 WRITE 为什么会出现是 0 的情况,这是个好问题,VPSee 在这里好好解释一下。首先看看我们怎么样才能实时监测不同进程的 IO 活动状况。

block_dump

Linux 内核里提供了一个 block_dump 参数用来把 block 读写(WRITE/READ)状况 dump 到日志里,这样可以通过 dmesg 命令来查看,具体操作步骤是:

# sysctl vm.block_dump=1
or
# echo 1 > /proc/sys/vm/block_dump

然后就可以通过 dmesg 就可以观察到各个进程 IO 活动的状况了:

# dmesg -c
kjournald(542): WRITE block 222528 on dm-0
kjournald(542): WRITE block 222552 on dm-0
bash(18498): dirtied inode 5892488 (ld-linux-x86-64.so.2) on dm-0
bash(18498): dirtied inode 5892482 (ld-2.5.so) on dm-0
dmesg(18498): dirtied inode 11262038 (ld.so.cache) on dm-0
dmesg(18498): dirtied inode 5892496 (libc.so.6) on dm-0
dmesg(18498): dirtied inode 5892489 (libc-2.5.so) on dm-0

问题

一位细心的网友提到这样一个问题:为什么会有 WRITE block 0 的情况出现呢?VPSee 跟踪了一段时间,发现确实有 WRITE 0 的情况出现,比如:

# dmesg -c
...
pdflush(23123): WRITE block 0 on sdb1
pdflush(23123): WRITE block 16 on sdb1
pdflush(23123): WRITE block 104 on sdb1
pdflush(23123): WRITE block 40884480 on sdb1
...

答案

原来我们把 WRITE block 0,WRITE block 16, WRITE block 104 这里面包含的数字理解错了,这些数字不是代表写了多少 blocks,是代表写到哪个 block,为了寻找真相,VPSee 追到 Linux 2.6.18 内核代码里,在 ll_rw_blk.c 里找到了答案:

$ vi linux-2.6.18/block/ll_rw_blk.c

void submit_bio(int rw, struct bio *bio)
{
        int count = bio_sectors(bio);

        BIO_BUG_ON(!bio->bi_size);
        BIO_BUG_ON(!bio->bi_io_vec);
        bio->bi_rw |= rw;
        if (rw & WRITE)
                count_vm_events(PGPGOUT, count);
        else
                count_vm_events(PGPGIN, count);

        if (unlikely(block_dump)) {
                char b[BDEVNAME_SIZE];
                printk(KERN_DEBUG "%s(%d): %s block %Lu on %s\n",
                        current->comm, current->pid,
                        (rw & WRITE) ? "WRITE" : "READ",
                        (unsigned long long)bio->bi_sector,
                        bdevname(bio->bi_bdev,b));
        }

        generic_make_request(bio);
}

很明显从上面代码可以看出 WRITE block 0 on sdb1,这里的 0 是 bio->bi_sector,是写到哪个 sector,不是 WRITE 了多少 blocks 的意思。还有,如果 block 设备被分成多个区的话,这个 bi_sector(sector number)是从这个分区开始计数,比如 block 0 on sdb1 就是 sdb1 分区上的第0个 sector 开始。

在 ArchLinux VPS 上编译内核

昨天我们有位 ArchLinux 用户遇到一个 OpenVPN 的问题,在他的 VPS 上安装和配置好 OpenVPN 后可以从自己电脑连上 VPN,但是不能用 VPN 出去(访问外部网站),VPSee 和用户一起排错发现配置 OpenVPN 时候忘了用 iptable 做 SNAT 或 MASQUERADE,进一步运行 iptables 报错,内核缺少 iptables 模块:

# iptables -t nat -A POSTROUTING -s 1.1.1.1 -j SNAT --to-source 2.2.2.2
can't initialize iptables table `nat': Table does not exist (do you need to insmod?)
Perhaps iptables or your kernel needs to be upgraded.

找到问题就好办了,解决的办法也很容易,下载内核代码、配置 iptable 内核选项、重新编译内核。(本文已经发给客户,我们的客户可以按照这里的步骤自己编译和替换内核,如果怕麻烦的话也可以找我们要已经编译好的内核。我们争取对我们的客户遇到的问题都能写出文章和流程来帮助理解和解决,这样不但我们自己客户受益,对其他 VPS 用户也有所帮助。)

安装必要工具

编译内核需要一些工具,比如 gcc 编译器什么的:

# pacman -S make gcc patch xmlto docbook-xsl

有一点要注意的就是不是每个内核都可以在 Xen 上运行的,因为是半虚拟化所以必须使用针对 Xen 的 Linux 内核版本,还好,ArchLinux 已经为我们准备好了官方的 Xen 版本内核:

# wget http://aur.archlinux.org/packages/kernel26-xen/kernel26-xen.tar.gz
# tar -xf kernel26-xen.tar.gz
# cd kernel26-xen

修改 kernel26-xen/PKGBUILD 文件,去掉下面这行的注释:

# vi PKGBUILD
pkgname=('kernel26-xen' 'kernel26-xen-headers') # Build kernel with a different name

编译和安装内核

如果打开 kernel26-xen/config 文件就会发现内核已经是为 Xen 配置好的,并且 iptable 相关选项已经是 m 状态(以内核模块的方式运行),如果不放心的话可以再次检查 CONFIG_IP_NF_IPTABLES=m 和 CONFIG_NF_NAT=m 等选项看看是否已经是 m 或 y 状态。在 ArchLinux 下编译和安装内核:

# makepkg --asroot
...
==> Creating package...
  -> Generating .PKGINFO file...
  -> Compressing package...
==> Finished making: kernel26-xen 2.6.34.1-1 (Mon Jul 19 15:15:05 EDT 2010)

# 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

配置 grub 以启动新内核:

# 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
...

测试

重启系统后检查和测试新装的 Linux 内核是否有了 iptables 内核模块并安装 iptables 用户端工具:

# uname -r
2.6.34-xen

# modprobe ip_tables

# lsmod
Module                  Size  Used by
ip_tables               9099  0 
x_tables               10364  1 ip_tables
evdev                   6810  0 
pcspkr                  1383  0 
rtc_core               11631  0 
rtc_lib                 1454  1 rtc_core
ext3                  108916  1 
jbd                    35426  1 ext3
mbcache                 4250  1 ext3

# pacman -Syy iptables

计算 SMP IRQ Affinity

前天我们讨论了如何绑定特定的硬件中断到特定的 CPU 上,分散和平衡各个中断到不同的 CPU 上以获取更大性能的处理能力。上篇限于篇幅的关系,没有来得及进一步说明 “echo 2 > /proc/irq/90/smp_affinity” 中的 ”2“ 是怎么来的,这其实是个二进制数字,代表 00000010,00000001 代表 CPU0 的话,00000010 就代表 CPU1, “echo 2 > /proc/irq/90/smp_affinity” 的意思就是说把 90 中断绑定到 00000010(CPU1)上。所以各个 CPU 用二进制和十六进制表示就是:

               Binary       Hex 
    CPU 0    00000001         1 
    CPU 1    00000010         2
    CPU 2    00000100         4
    CPU 3    00001000         8

如果我想把 IRQ 绑定到 CPU2 上就是 00000100=4:

# echo "4" > /proc/irq/90/smp_affinity

如果我想把 IRQ 同时平衡到 CPU0 和 CPU2 上就是 00000001+00000100=00000101=5

# echo "5" > /proc/irq/90/smp_affinity

需要注意的是,在手动绑定 IRQ 到 CPU 之前需要先停掉 irqbalance 这个服务,irqbalance 是个服务进程、是用来自动绑定和平衡 IRQ 的:

# /etc/init.d/irqbalance stop

还有一个限制就是,IO-APIC 有两种工作模式:logic 和 physical,在 logic 模式下 IO-APIC 可以同时分布同一种 IO 中断到8颗 CPU (core) 上(受到 bitmask 寄存器的限制,因为 bitmask 只有8位长。);在 physical 模式下不能同时分布同一中断到不同 CPU 上,比如,不能让 eth0 中断同时由 CPU0 和 CPU1 处理,这个时候只能定位 eth0 到 CPU0、eth1 到 CPU1,也就是说 eth0 中断不能像 logic 模式那样可以同时由多个 CPU 处理。

在 CentOS 上安装和配置 NFS 4.0

NFS 网络文件系统协议最早是于1984年由 SUN 公司开发的,NFSv4 是最新的修订版本,2003年4月发布(RFC 3530)。由于 NFS 是一个开放的协议和标准,所以各个系统上的实现不同,所表现出来就是稳定性和性能差异,VPSee 的经验是 NFS 在 Solaris 上最稳定、功能最丰富;在 FreeBSD 上的性能最好,但是缺少一些功能,比如 FreeBSD 8.0 内核才增加了 NFS 文件锁的支持;NFS 在 Linux 上表现最一般了,所以我们至今仍有部分服务器低调地运行着古老的 Solaris 2.5 和 NFSv3,相比 NFSv3 来说 NFSv4 做了一些重大改进,比如:性能的提升、安全性的增强和 ACL,更容易与防火墙集成等。下面地操作在 CentOS 5.5 上完成。

安装 NFS 服务器

安装 NFS 服务器所需的软件包:

# yum install nfs-utils nfs4-acl-tools portmap

配置 NFS 共享

和安装任何其他服务器软件一样,安装完 NFS 服务后就需要对 NFS 服务进行配置。输出服务器端的某个目录,以便 NFS 客户端能挂载和访问到这个目录:

# vi /etc/exports
/bak              172.16.20.11(rw,sync,fsid=0) 172.16.20.12(rw,sync,fsid=0)

172.16.20.11 和 172.16.20.12 上面的用户可以挂载 NFS 服务器(172.16.20.1)上的 /bak 目录到自己文件系统里;rw 在这里表示可读可写。

把 NFS 服务加入系统启动脚本并且手动启动 NFS 服务:

# chkconfig nfs on
# chkconfig portmap on

# /etc/init.d/portmap start
# /etc/init.d/nfs start

确认 NFSv4 服务器成功运行:

# rpcinfo -p
   program vers proto   port
    100000    2   tcp    111  portmapper
    100000    2   udp    111  portmapper
    100003    2   udp   2049  nfs
    100003    3   udp   2049  nfs
    100003    4   udp   2049  nfs
    ...
    100005    3   tcp    750  mountd

检查 NFS 服务器是否输出我们想共享的目录 /bak:

# exportfs
/bak          	172.16.20.11
/bak          	172.16.20.12

注意 NFS 使用 portmap,而且新版本的 portmap 使用 hosts.deny 和 hosts.allow 文件来控制访问源,修改这2个配置文件以便 NFS 客户端能正常连接到服务器:

# vi /etc/hosts.deny
portmap:ALL

# vi /etc/hosts.allow
portmap:172.16.38.0/255.255.254.0

使用 NFS 客户端

首先启动 portmap:

# /etc/init.d/portmap start

检查 NFS 服务器端是否有目录共享:

# showmount -e 172.16.20.1
Export list for 172.16.20.1:
/bak 172.16.20.11,172.16.20.12

使用 mount 挂载服务器端的目录 /bak 到客户端某个目录下:

# mkdir /home/vpsee/bak
# mount -t nfs4 172.16.20.1:/ /home/vpsee/bak/

# df -H
Filesystem             Size   Used  Avail Use% Mounted on
...
172.16.39.1:/        232G    23G   198G  11% /home/vpsee/bak

在 /etc/fstab 中挂载 nfs 文件系统:

# vi /etc/fstab
172.16.20.1:/    /bak  nfs4   soft,intr,rsize=8192,wsize=8192,nosuid

# chkconfig netfs on

在成功挂载服务器端的 /bak 后就可以在客户端里操作了,这时候写文件或者拷贝文件的时候会遇到一个权限问题报错 : Permission denied,这是因为上面我们在服务器端建立了 /bak 但是 /bak 的 owner 是 root:root,而客户端的正在访问 NFS 的用户是 vpsee,NFS 要求服务器端和客户端的 UID 和 GID 必须一致,所以要在 NFS 服务器端修改 /bak 的权限和所属:

# chown -R vpsee:vpsee /bak

注意使用 NFS 的时候,客户端的用户 UID 和 GID 必须和服务器端的 UID 和 GID 完全符合,否则会造成权限错误。在小规模用户的情况下,我们可以偷懒通过在客户和服务器两端同时建立相同的用户和组来解决这个问题,但是在大规模用户情况下最好的办法是利用 NIS 或者 OpenLDAP 来统一管理用户,做到一次登录,到处访问。

Troubleshooting

1、在上面的操作过程中,如果你不幸遇到下面这个问题的话,可以尝试更新 Linux kernel 或通过打开 IPv6 来解决这个问题,这是1个 bug

# mount -t nfs4 172.16.20.1:/ /home/vpsee/bak/
mount.nfs4: Cannot allocate memory

2、如果遇到如下问题,可能是因为你的 mount -t nfs 使用的是 nfsv3 协议,需要明确指出使用 nfsv4 协议挂载 mount -t nfs4:

# mount -t nfs 172.16.20.1:/ /home/vpsee/bak/
mount: mount to NFS server '172.16.20.1' failed: RPC Error: Program not registered.

# mount -t nfs4 172.16.20.1:/ /home/vpsee/bak/

如何在 Xen VPS 上升级 Linux

有客户询问能否重新 rebuild 他的 Ubuntu 10.04 VPS 并升级到我们刚推出的 Ubuntu 12.04 LTS Server 版本,其实用户可以自己完成升级而不用我们 rebuild,不损失任何数据。和其他一些 Xen VPS 服务商不同,我们采用 PyGrub 来启动和管理 domU 的内核而不是在 dom0 范围内统一管理内核,这意味着用户可以在自己 VPS 上启动和使用自己的内核,这样做的好处是用户完全可以自己升级 VPS 的内核以及整个 Linux 系统(版本),完全不用我们的干预。升级办法很简单,就和在自己电脑上升级 Linux 一样,不过 OpenVZ VPS 是不能这样包括内核在内完整升级到新版本的。VPSee 建议在做任何升级之前做好备份。

Linode 和 SliceHost 两大 Xen VPS 服务商都相继发布了 Ubuntu 10.04 LTS 版本的 VPS. 我们上个星期安装和测试了 Ubuntu 10.04 LTS VPS,使用 8GB SUN V20z 服务器跑32个 128/256MB VPS 的混合,每个 VPS 上跑不同的应用和压力,测试结果在意料之中,Ubuntu 10.04 LTS 已经被我们加入到 VPS 计划之中并应用到了生产环境。

升级 Ubuntu 12.04 到 14.04(2014年4月17日更新)

我们的用户可以在自己的 VPS 上升级 Ubuntu 12.04 LTS Server 到 Ubuntu 14.04 LTS Server:

# apt-get update
# apt-get upgrade
# do-release-upgrade -d
# reboot

升级 Ubuntu 10.04 到 12.04

同样的,升级 Ubuntu 10.04 LTS Server 到 Ubuntu 12.04 LTS Server:

# apt-get update
# apt-get upgrade
# do-release-upgrade -d
# reboot

升级 CentOS 5.4/5.5 到 5.8

正在使用 CentOS 5.4/5.5 VPS 的用户想要升级到刚发布的 CentOS 5.8 版本的话就更简单了,直接在命令行:

# yum update
# reboot

升级 CentOS 5.x 到 6.x

除了重装系统,没有办法从 5.x 直接升级到 6.x:

升级 Fedora 12, 13 到 14

使用 Fedora 12 VPS 的用户可以这样升级到 Fedora 14:

# rpm --import https://fedoraproject.org/static/97A1071F.txt

# yum update yum
# yum --releasever=14 distro-sync --skip-broken

升级 Debian 5 到 6

使用 Debian 5 VPS 的用户可以这样升级到 Debian 6:

# wget http://ftp-master.debian.org/keys/archive-key-6.0.asc
# apt-key add archive-key-6.0.asc
# aptitude update

# vi /etc/apt/sources.list
:%s/lenny/squeeze/g
:wq

# apt-get update
# apt-get install apt dpkg
# apt-get dist-upgrade

# reboot

升级 Debian 6 到 7

正在使用 Debian 6 的用户可以这样升级到 Debian 7:

# vi /etc/apt/sources.list
deb http://mirrors.kernel.org/debian/ wheezy main
deb-src http://mirrors.kernel.org/debian/ wheezy main

deb http://security.debian.org/ wheezy/updates main
deb-src http://security.debian.org/ wheezy/updates main

# apt-get update
# apt-get upgrade
# apt-get dist-upgrade

# reboot