Mac 下远程访问 Ubuntu 桌面

自从给项目组每个人开了一个 Xen VPS 后麻烦就不断,一会儿要求加硬盘,一会儿要求加内存,还有人想在上面跑图形桌面,完全不顾 VPSee 的业余时间:(,昨晚简单测了一下多个 Ubuntu 9.04 桌面系统同时运行在一个服务器上(用 Xen 虚拟化)的情况,速度还可以,比想像的要好。主要时间都浪费在配置上,比如配置 FreeNX、VNC 等,网上教程都不全,用了不少时间排错。这里介绍 Mac 通过网络远程访问 Ubuntu 桌面的常用三种方法,最好的方法应该是用 FreeNX,安全而且速度快:

X11

这是最简单最方便的方法,不需要在 Ubuntu 端做任何配置,不过在 Mac 端必须已装有 X11,在 Terminal 上敲(把 ubuntu 换成对应的服务器 IP 地址或域名):

$ Xnest -geometry 1280x800 :1 & DISPLAY=:1 ssh -X ubuntu  gnome-session

mac x11 forwarding

FreeNX

上面用 X11 forwarding 的方法虽然很简单,在局域网内用用还可以,如果想在 Internet 上连一个远程桌面的话就不合适了,速度很慢。FreeNX 有安全、速度快、功能强等优点,尤其适应网络环境恶劣的情况。按照 Ubuntu 的一篇帮助文档:FreeNX执行如下步骤:

$ sudo vi /etc/apt/sources.list
deb http://ppa.launchpad.net/freenx-team/ppa/ubuntu jaunty main
deb-src http://ppa.launchpad.net/freenx-team/ppa/ubuntu jaunty main

$ sudo apt-key adv --recv-keys \
--keyserver keyserver.ubuntu.com 2a8e3034d018a4ce

$ sudo apt-get update

$ sudo aptitude install freenx

$ sudo /usr/lib/nx/nxsetup --install 

下载一个 Mac NX 客户端连接服务器报错,查看日志后发现 authenticate 没通过,怀疑是 SSH keys 问题:

The NX service is not available or the NX access was disabled on host 172.X.X.X.

在 NX 服务器端修改 sshd_config 文件:

$ sudo vi /etc/ssh/sshd_config
AuthorizedKeysFile      /var/lib/nxserver/home/.ssh/authorized_keys2

$ sudo /etc/init.d/ssh restart

在 NX 客户端把 /var/lib/nxserver/home/.ssh/client.id_dsa.key 文件拷贝到客户端后,用 NX 客户端工具导入。NX 客户端再次连接 FreeNX Server,依然报错,不过错误信息变了:

NX> 103 Welcome to: ubuntu user: root
NX> 105 listsession –user=”root” –status=”suspended,running” –geometry=”1440x900x32+render” –type=”unix-gnome”
mktemp: cannot create temp file /tmp/nxserver_tmp.rQAHzfLbK: Permission denied
/usr/bin/nxserver: line 273: $TMPFILE: ambiguous redirect

显然是 /tmp 权限问题,这个问题很容易解决:

$ sudo chmod 777 /tmp

再次连接,终于看到了 Ubuntu 桌面,如果你不需要太高的安全性,到这里就可以结束了,enjoy it.

上面的连接使用的是默认的 SSH Keys,会带来安全隐患,如果需要更高的安全性可以用自己定制的 SSH Keys,重新配置:

$ sudo dpkg-reconfigure freenx-server

上面命令会弹出对话框,选择 ”Create new custom keys” 后,会在 /var/lib/nxserver/home/custom_keys/ 下创建一个 client.id_dsa.key 文件,把这个文件拷贝到 NX 客户端,然后在客户端导入这个 Key 文件就可以正常连接 FreeNX 服务了。如果 key 通过了认证,可以在服务器端和客户端删除这个 key 文件。

VNC

还有一种古老的方法就是使用 VNC,和 FreeNX 相比VNC 速度较慢,因为 FreeNX 是将 X Window 的数据压缩后传输到远程客户端显示,而 VNC 则是直接截取屏幕后传输过去,在较差的网络中这种延迟和速度差异很明显。先在 Ubuntu 上装一个 VNC Server:

$ sudo apt-get install vnc4server
$ vncserver :1 -geometry 1024x768 -depth 16

然后在 Mac 上装一个 VNC Viewer(推荐 Chicken of the VNC)就可以连上 Ubuntu,不过这个时候看到的是 X Window 界面,不是 Ubuntu 桌面,在 X Window 中手动启动 Ubuntu 桌面:

$ gnome-session &

如果不想每次都手动的话,在当前用户目录根目录下修改 ~/.vnc/xstartup:

$ vi ~/.vnc/xstartup
# twm &
gnome-session &

$ sudo shutdown -r now

$ vncserver :1 -geometry 1024x768 -depth 16

重新启动 Ubuntu 并打开 VNC Server 服务,用 VNC 客户端连接就可以看到 Ubuntu 图形桌面了。

Linux 下给图片批量加水印

一个非盈利组织的项目负责人突发奇想想给他们网站上的每张照片加上水印,说实话那些照片都是平时活动、party 的生活照片用不着用水印那么夸张,第一次听说给生活照加水印的。没办法,谁让我们和他们有合作项目呢。还好他们服务器用的是 Linux,在 Linux 下给图片批量加水印简单多了,用 imagemagick + 一个小脚本搞定。

在 CentOS 下安装:

# yum install ImageMagick

在 Ubuntu 下安装:

$ sudo apt-get install imagemagick

先用画图工具制作好一个水印图片 watermark.jpg,然后执行 composite 命令把这个 watermark.jpg 水印加到图片 vpsee.jpg 上,-dissolve 15 是指 watermark.jpg 使用15%的透明附在原图上:

$ composite -gravity northeast -dissolve 15 watermark.jpg vpsee.jpg vpsee.jpg

要事先做个 watermark.jpg 好麻烦,有没有不用 watermark.jpg 直接加水印的方法?有,不过这种方法需要 Linux 系统上已经安装 True 字体(一般来说服务器都没有安装,不建议为了一个水印安装一个硕大的字体),以下命令把 vpsee.com 字符串加到 image.jpg 图片上:

$ mogrify -font /usr/share/fonts/truetype/thai/Purisa.ttf -pointsize 15 \
-verbose -draw "fill black text 5,23 'vpsee.com' \
fill orange text 6,24 'vpsee.com' " image.jpg

可以用下面的 shell script 对某个目录的所有图片加水印,需要注意的是处理带空白字符的文件名很麻烦,所以下面的脚本先处理空白字符,把包含空白字符的文件名用 “_” 字符替代,比如:image 1.jpg 替换成 image_1.jpg:

#!/bin/bash

echo "Image watermarking Script"
echo "http://www.vpsee.com"
echo ""

if [ $# -ne 2 ]
then
    echo "usage: ./watermark watermark.jpg picture_directory"
    echo ""
        exit
fi

MARK=$1
PICDIR=$2
for each in $PICDIR/*{.jpg,.jpeg,.png,.JPG,.JPEG,PNG}
do
    mv "$each" `echo $each | tr ' ' '_'`;
    composite -gravity northeast -dissolve 15.3 $MARK $each $each 2> /dev/null
    echo "$each: done!"
done
exit 0

imagemagick 的功能很强大,把上面脚本中的 composite 一行换成下面这行就成了批量给图片改大小了:

$ convert -resize 400 old_image.jpg new_image.jpg

如果想直接把原图改小,用新图片覆盖原图片的话:

$ convert -resize 400 image.jpg image.jpg

如何查看进程 IO 读写情况?

Linux Kernel 2.6.20 以上的内核支持进程 IO 统计,可以用类似 iotop 这样的工具来监测每个进程对 IO 操作的情况,就像用 top 来实时查看进程内存、CPU 等占用情况那样。但是对于 2.6.20 以下的 Linux 内核版本就没那么幸运了,根据 Stack Overflow 的这篇回帖 给出的方法,VPSee 写了一个简单的 Python 脚本用来在 linux kernel < 2.6.20 下打印进程 IO 状况。

Kernel < 2.6.20

这个脚本的想法很简单,把 dmesg 的结果重定向到一个文件后再解析出来,每隔1秒钟打印一次进程 IO 读写的统计信息,执行这个脚本需要 root:

#!/usr/bin/python
# Monitoring per-process disk I/O activity
# written by http://www.vpsee.com 

import sys, os, time, signal, re

class DiskIO:
    def __init__(self, pname=None, pid=None, reads=0, writes=0):
        self.pname = pname 
        self.pid = pid
        self.reads = 0
        self.writes = 0

def main():
    argc = len(sys.argv)
    if argc != 1:
        print "usage: ./iotop"
        sys.exit(0)

    if os.getuid() != 0:
        print "must be run as root"
        sys.exit(0)

    signal.signal(signal.SIGINT, signal_handler)
    os.system('echo 1 > /proc/sys/vm/block_dump')
    print "TASK              PID       READ      WRITE"
    while True:
        os.system('dmesg -c > /tmp/diskio.log')
        l = []  
        f = open('/tmp/diskio.log', 'r')
        line = f.readline()
        while line:
            m = re.match(\
                '^(\S+)\((\d+)\): (READ|WRITE) block (\d+) on (\S+)', line)
            if m != None:
                if not l:       
                    l.append(DiskIO(m.group(1), m.group(2)))
                    line = f.readline() 
                    continue            
                found = False   
                for item in l:  
                    if item.pid == m.group(2):
                        found = True            
                        if m.group(3) == "READ":
                            item.reads = item.reads + 1 
                        elif m.group(3) == "WRITE":
                            item.writes = item.writes + 1
                if not found:   
                    l.append(DiskIO(m.group(1), m.group(2)))
            line = f.readline()
        time.sleep(1)
        for item in l:
            print "%-10s %10s %10d %10d" % \
                (item.pname, item.pid, item.reads, item.writes)

def signal_handler(signal, frame):
    os.system('echo 0 > /proc/sys/vm/block_dump')
    sys.exit(0)

if __name__=="__main__":
    main()


继续阅读 »

用 Cacti 监视 Linux 服务器

给每年的 hacker 大会提供网络支持是件很有挑战的事情,弄不好就会有 hacker 当场黑掉网络这种尴尬事情出现。看看刚刚过去的 Defcon 大会技术人员是怎么架设网络的,使用了哪些有趣的硬件和软件,这里有 图片报道:2009 Defcon 的网络图片报道:2008年 Defcon 的网络。Defcon 技术人员用 OpenBSD 做 Firewall 跑在 Xeon 服务器上,用 Cacti 监测流量。VPSee 一直都用 Nagios 来监测自己的多台服务器,对 Cacti 很好奇,今天刚好有时间安装试试看。

安装 Cacti

安装必要的软件包:

# yum install net-snmp-utils php-snmp net-snmp-libs
# yum install perl-rrdtool rrdtool

Cacti 官方网站下载 cacti-0.8.7e.tar.gz:

# tar zxvf cacti-0.8.7e.tar.gz
# mv cacti-0.8.7e /var/www/cacti

给 cacti 打补丁:

# wget http://www.cacti.net/downloads/patches/0.8.7e/cli_add_graph.patch
# wget http://www.cacti.net/downloads/patches/0.8.7e/snmp_invalid_response.patch
# wget http://www.cacti.net/downloads/patches/0.8.7e/template_duplication.patch
# wget http://www.cacti.net/downloads/patches/0.8.7e/fix_icmp_on_windows_iis_servers.patch
# wget http://www.cacti.net/downloads/patches/0.8.7e/cross_site_fix.patch
# patch -p1 -N < cli_add_graph.patch
# patch -p1 -N < cross_site_fix.patch
# patch -p1 -N < fix_icmp_on_windows_iis_servers.patch
# patch -p1 -N < snmp_invalid_response.patch
# patch -p1 -N < template_duplication.patch

配置 Cacti

创建 Cacti 需要的数据库:

# mysql -u root -p
mysql> CREATE DATABASE cacti;
mysql> GRANT ALL ON cacti.* TO vpsee@localhost IDENTIFIED BY 'password';
mysql> FLUSH privileges;

初始化 Cacti 数据库表:

# mysql -u vpsee -p cacti < /var/www/cacti/cacti.sql

修改 Cacti 配置文件以连接到上面的数据库:

继续阅读 »

Kill 某个用户的所有进程

在一台100多人使用的 SUN 服务器上经常发现有人滥用资源,平时用用就算了,到了交作业的时候100多号人同时登录使用,服务器明显迟缓,特别是实验室用的是瘦客户机,没硬盘,SUN 客户端需要从服务器装载系统镜像,而且编译、运行程序都要在服务器上完成。如果发现某个用户运行很多进程,并且进程还有子进程,怎么能方便的找出全部进程并 kill 掉呢?

ps -ef | grep ^username | cut -c 10-15 | xargs kill -9

把全部进程打印出来根据用户名过滤后找出全部进程号,然后逐行 kill 掉。xargs 就是用来把 cut 后的输出逐个以空白符分割后输给 kill。注意上面的 grep ^username 不能缺 ^,^username 表示从一行开始匹配 username,比如就可以避免匹配到 sshd: username@pts/0。不过尽管加了 ^,上面的代码仍然有个小 bug,如果恰好有个进程名和用户名完全一样怎么办?可以用 ps -u 找出所有与 username 相关的进程,然后 grep -v 过滤掉 PID 只剩下进程信息,然后逐行 kill 掉,如下:

ps -u username | grep -v PID | cut -c 0-5 | xargs kill -9

不过上面的命令还有个小问题就是如果 cut 的时候不小心 cut 多了或者 cut 少了怎么办?可以用 awk 过滤一列信息出来:

ps -u username | grep -v PID | awk '{print$1}'| xargs kill -9

上面的命令也可以在 Linux 上运行,不过在 Linux 下可以用更简单的 killall,Solaris 上没有 killall:

killall -u username

一个小小的命令行反反复复改了多次,更别说上百万行的代码了,写代码太容易引入 bug 了,这就是为什么测试这么重要的原因,我觉得程序员应该用50%的时间写代码,50%的时间测代码;测试员也应该用50%的时间测代码,50%的时间写工具来自动测试代码。

Shell 的 IFS 变量

今天把一个 shell script 从 Linux 移植到 Solaris 时遇到一些小问题:

args=`tail -n 1 $file | head -1`

tail 的用法有点不一样。Solaris 下的 tail:tail -1 $file

IFS=”

javac $1
sort_program=`echo $1|sed ‘s/\.[^.]*$//’`
args=”2 1 3″
java $sort_program $args

上面的 script 编译一个 java 排序程序,然后用给定参数 2 1 3 运行,排序后输出 1 2 3。java 运行上面脚本时报错:

Exception in thread “main” java.lang.NumberFormatException: For input string: “2 1 3”

显然 java 把 “2 1 3” 字符串当作了参数,应该是 2 1 3,怎么会这样呢?echo $args 显示 args 的值的确是 2 1 3。调试了半天发现这个 shell script 开头有个 IFS,不知道什么时候加上去的,上面的那句 IFS 导致以新行切分文件时将 “2 1 3” 作为整体发给 java,而不是单独将 2 1 3 作为参数传给 java,所以去掉 IFS 语句就可以了。IFS 是个很有用的变量,默认下用来分割空格、制表、换行等,也可以用来分割指定字符,比如把 www:vpsee:com:8080 分割成 www vpsee com 8080 就可以用 IFS:

bash-3.00$ $line=www:vpsee:com:8080
bash-3.00$ $IFS=':'
bash-3.00$ $for i in $line; do  echo $i; done
www
vpsee
com
8080

上面 java 例子中的 `echo $1|sed ‘s/\.[^.]*$//’` 用来过滤掉后缀名,比如:编译 javac HelloWorld.java 需要 .java 后缀名,但是运行 java HelloWorld 就不需要带上 .class 后缀名。 下面的 shell script 得到一个文件名后打印出其不包含后缀名的文件名:

student=$1
student_title=`echo $1|sed 's/\.[^.]*$//'`
echo $student_title

用 Monit 监测 Linux 服务器

Monit 是一个用来监测系统状态的工具,不但可以用来监视进程、服务、文件、目录、文件系统,还可以在服务 down 掉的时候自动重启服务或者当某个进程占用过多的资源的时候自动停掉进程,并且支持 Email 报警功能、远程服务器监测、web 管理界面等,功能很多很强大。VPSee 用 monit 来监测多个 VPS 和独立服务器,如果某个 VPS down 了、某个服务器资源紧张,就能第一时间知道,而且某个服务停了的话可以自动重启服务。

安装 monit

在 CentOS 下安装和配置文件:

# wget http://packages.sw.be/rpmforge-release/rpmforge-release-0.3.6-1.el5.rf.i386.rpm
# rpm -Uvh rpmforge-release-0.3.6-1.el5.rf.i386.rpm

# yum install monit

# vi /etc/monit.conf

在 Debian 下安装和配置文件:

# apt-get install monit

# vi /etc/monit/monitrc

配置 monit


继续阅读 »

在 CentOS 上安装和配置 Xen

现在实验室 Masters 都配有1台 PC 和 1台笔记本,但是 Honours 只配有1台 PC,1台机器做项目很不方便,开发经常会用到多系统,比如有的人做的是手机 VoIP 的相关项目,手机客户端界面要在 Windows 平台上做,VoIP 服务器端要用到 Linux,所以要用虚拟机虚拟一个 Linux 出来,如果这些都跑在一台物理机器上会很慢。现在给 Honours 配置的 PC 只有 1G 的内存,如果运行 Windows + 手机模拟器 + Elipse IDE(需要 Java)+ VMware(VMware 上再跑个 Linux + Asterisk + MySQL),然后开个客户端收邮件、开几个浏览器看资料就会很困难。

所以 VPSee 打算把自己在用的1台 SUN 服务器捐出来做成 Xen 服务器,给每个 Honours 分一个虚拟系统,省下他们自己装虚拟机的时间和资源,VPSee 成了免费的 Xen VPS provider 了:)。SUN 服务器上同时运行16个 Xen 虚拟系统实例,每个配 256 MB 内存,4GB 硬盘,不开 GUI。下面的安装步骤和配置过程是基于 CentOS 5.3 版本,Ubuntu 版本可以参看:在 Ubuntu 上安装和配置 Xen,Debian 版本参看:在 Debian 上安装和配置 Xen,OpenSolaris 版本参看:在 OpenSolaris 上安装和配置 Xen,NetBSD 版本参看:在 NetBSD 上安装和配置 Xen. 对 OpenVZ 和 KVM 感兴趣的童鞋可以看:在 CentOS 上安装和配置 OpenVZ在 CentOS 上安装和配置 KVM.

安装 Xen

安装支持 Xen 的 Linux 内核 和 Xen:

# yum install kernel-xen xen

安装成功后,可以看到 xen.gz-2.6.18-128.2.1.el5 内核已经装好,修改 default 的值默认启动 Xen 内核。

# vi /etc/grub.conf 

default=0
timeout=2
splashimage=(hd0,0)/grub/splash.xpm.gz
hiddenmenu
title CentOS (2.6.18-128.2.1.el5xen)
	root (hd0,0)
	kernel /xen.gz-2.6.18-128.2.1.el5
	module /vmlinuz-2.6.18-128.2.1.el5xen ro root=/dev/VolGroup00/LogVol00
	module /initrd-2.6.18-128.2.1.el5xen.img
title CentOS (2.6.18-128.1.16.el5)
	root (hd0,0)
	kernel /vmlinuz-2.6.18-128.1.16.el5 ro root=/dev/VolGroup00/LogVol00
	initrd /initrd-2.6.18-128.1.16.el5.img


继续阅读 »