W4Networks:$5.95 512MB OpenVZ VPS

w4networks

W4 Networks 是一家澳大利亚服务商,提供非常便宜的 VPS,512MB VPS 只要 $5.95VPS,这可能是 VPSee.com 所有 512MB VPS 介绍里面价格最低的一家。w4networks.com 域名显示2009年2月才注册,网站上没有什么有用信息,联系方式除了 Email 连个电话都没有,很显然是一家新 “公司”。在 contactus.html 这个页面最下方有一条 “Copyright© 2009 BurstNetworks.com.au”,不知道 w4networks 和 BurstNetworks 有什么关系,BurstNetworks 是另外一家澳大利亚 VPS 公司。两款10美元以下的 VPS 配置如下:

服务器在 Kansas City MO 或 Scranton, PA
SolusVM 控制面板
10GB 硬盘/30GB
512MB Guaranteed RAM/1GB
400 GB 带宽
2个 IP
5.95美元/10美元

服务器硬件和网络信息:

All VPS servers are hosted on Quad core servers with 8Gb ram +. All servers on 100Mbps connections.

打造廉价的云存储服务器

Backblaze 是一家在线存储服务商,为用户提供在线数据备份服务。在一篇博客里他们分享了建造云存储服务器的过程和经验,他们正是用这些自己组装的设备来运行 Backblaze 云存储服务的的。本文数据、图片均来自:Petabytes on a budget: How to build cheap cloud storage.

价格对比

和那些专门的存储公司 Amazon S3/Dell/Sun Servers/NetApp Filers/EMC SAN 等相比,自己组装存储服务器要便宜得多:

cost of a petabyte chart

硬件

组装一个 67 TB 的 4U 存储服务器只要 $7867:
storage hardware

storage assembled

硬件 价格
Seagate ST31500341AS 1.5TB Barracuda 7200.11 SATA 3Gb/s 3.5″ 45 X $120.00
Intel E8600 Wolfdale 3.33 GHz LGA 775 65W Dual-Core Processor 1 X $280.00
Intel BOXDG43NB LGA 775 G43 ATX Motherboard 1 X $85.00
Kingston KVR800D2N6K2/4G 2×2GB 240-Pin SDRAM DDR2 800 (PC2 6400) 1 X $50.00
Western Digital Caviar WD800BB 80GB 7200 RPM IDE Ultra ATA100 3.5″ 1 X $38.00
Zippy PSM-5760 760 Watt Power Supply with Custom Wiring 2 X $270.00
Custom Designed 4U Red Backblaze Storage Pod Enclosure 1 X $748.00

软件

存储服务器上运行的是 64位的 Debian 4 Linux 操作系统和 JFS 文件系统,整个系统通过 HTTPS 访问。Backblaze 使用 Linux fdisk 工具在每个硬盘上创建一个分区,用 mdadm 工具把15个硬盘合成1个 RAID6。整个系统使用 Backblaze 自己在 Apache Tomcat 5.5 上定制的的应用程序,通过 HTTPS 访问。把所有系统占据的硬盘空间减去后,还剩下大约 87% 左右的硬盘空间可供使用。

Backblaze 介绍了一条重要的经验就是始终通过 HTTPS 存储、得到数据,而不是通过 iSCSI, NFS, SQL, Fibre Channel 什么的。这些技术 scale 起来都比不上廉价、稳定、简单的 HTTPS。

Backblaze 选择了 JFS 作为他们存储服务器的文件系统可能是看中了 JFS 快速重启、恢复文件系统的优点,系统崩溃后几秒或几分钟内就能修复文件系统到一致状态。JFS 是一个带日志功能的文件系统,为服务器的高吞吐量、可靠性和面向事务而设计,使用了类似数据库日志的技术,加上64位、B+ Tree、网络传输速度快、CPU 占用少、拷贝/删除大文件快,mount/unmount 快等特点,使得 JFS 很适合做存储、文件服务器、NAS 等的文件系统。但是有一些资料显示 JFS 在 Linux 上不太稳定,bug 较多,测试不充分等。

storage software

云存储

如果想组装一台自己的存储服务器,上面这些硬件组合起来的 67 TB 4U 服务器应该足够了;如果想建造云存储服务器群的话,除了需要大量的硬件、硬盘以外,还需要软件来管理这些庞大的设备,Backblaze 开发了自己的私有软件来满足云存储的要求,用来把数据打散成块、加密、传输数据到服务器备份以及从服务器得到数据、解密、复制、恢复数据传输给客户,还有监视和管理整个云存储系统等。

Mac 通过代理服务器 ssh

在学校、公司大多时候都需要通过代理服务器上网,想在 Mac 下通过代理服务器 ssh 的话可以使用 Corkscrew 小工具。Corkscrew 也支持 Linux。

下载 corkscrew-2.0.tar.gz 后解压编译,然后拷贝编译生成的 corkscrew 到用户的主目录下的 .ssh 中:

$ tar zxvf corkscrew-2.0.tar.gz
$ cd corkscrew-2.0
$ ./configure --host=apple
$ make
$ cp corkscrew $HOME/.ssh/

打开 .ssh/config,增加以下几行:

$ vi .ssh/config

Host vpsee.com 67.203.229.22
ProxyCommand /Users/vpsee/.ssh/corkscrew proxy.server.address 3128 %h %p
TCPKeepAlive yes
ServerAliveInterval 5

注意:把上面的 vpsee.com 67.203.229.22 换成要 ssh 的服务器地址(可以有多个,可以是域名也可以是 IP 地址);把 proxy.server.address 3128 换成相应的代理服务器地址和端口号;TCPKeepAlive 用来与 ssh 服务器保持连接。

ssh 服务器为了安全考虑发现客户端长时间闲置的话会主动断开 ssh 连接,如果不想 ssh 自动断开,ssh 客户端需要打开 TCPKeepAlive 选项定时给服务器发 TCP 包来欺骗服务器,让 ssh 服务器感觉客户端还在 “活动” 中。同时 ssh 服务器端也需要打开 TCPKeepAlive 选项(也就是说,ssh 客户端配置和 ssh 服务器端配置都要打开 TCPKeepAlive):

# vi /etc/ssh/sshd_config

TCPKeepAlive yes

Ravelry 的 Rails 架构

看多了超大规模高性能、超大数据库架构,有必要回到现实看看中等规模网站是如何架构的,毕竟不是每个网站、web app 都有机会做成 Google/Yahoo/Facebook/Amazon 那样。Ravelry 是一家用 Ruby on Rails 搭建的社区网站,学习一下 Ravelry 的 Rails 架构经验。

数据

数据和资料来源(2009):Ravelry

43万注册用户
每个月约20万用户上线,每天约7万用户登录
每天约360万 PV(只包括注册用户,不包括其他途径访问),实际 PV 大概1000万每天
每天约900新用户注册
论坛每天发布约5万个新帖
网站有230万个项目、190万个帖子、130万条消息、800万张图片(大部分保存在 Flickr)
包括创始人在内公司一共才3.5个人(其中一个是 part time)

技术架构

Ravelry 使用了以下一些 Open Source 项目和技术:

Gentoo Linux
MySQL
Ruby Enterprise Edition
Memcached
Nginx/HAProxy/Apache + mod_passenger
Tokyo Cabinet/Tyrant
Sphinx
Xen
Munin/Nagios/Syslog-ng
Amazon S3/Cloudfront

Ravelry 刚开始运行在一个 VPS 上,随着网站规模的变大逐渐转向自己的硬件,服务器是从 Silicon Mechanics 购买的,托管在 Hosted Solutions 和 Cogent。Ravelry 使用 Amazon S3 做存储,Amazon CloudFront 做 CDN。

Ravelry 目前总共有7台物理服务器,Xen 虚拟化后成13台,使用 Gentoo Linux 操作系统。其中:

  • 1台用来备份,1台用来测试;
  • 1台用来做 master 数据库服务器,1台做 slave 数据库服务器 + Sphinx 搜索引擎;
  • 另外3台用来做应用服务器,运行 Passenger,Ruby Enterprise Edition 和 Memcached。使用 Passenger 和 Ruby GC patches 节省了大量内存。

前端服务的架构大概像这样:nginx -> haproxy -> apache + mod_passenger,Ravelry 的创始人 Casey 提到他们主要的 scaling/tuning/performance 方面的工作还是集中在数据库部分。

Ravelry 使用 Capistrano 来快速部署新版本的网站程序,使用 Munin 来监测服务器资源、Nagios 来警告、Syslog-ng 来做日志,使用 Memcached 来缓存数据库对象,部分需要缓存 larger object 的地方用到了 Tokyo Cabinet/Tyrant。Ravelry 几乎把站内所有的用到 Markdown 标记语法的页面都缓存成了普通的 HTML。

Ravelry 在开始上线的头几个月几乎每天都会修正 bug、推出新版本,得益于 Ruby 的快速开和 prototype 的能力。Casey 说,Ruby is fun! Fun is important.

简单调试 Bash 脚本

用 Bash 写的脚本也可以进行调试,和 Python,Perl 等解释型语言一样。新建一个名为 servinfo 的脚本并增加可执行权限:

$ vi servinfo

#!/bin/bash

echo "Hostname: $(hostname)"
echo "Date: $(date)"
echo "Kernel: $(uname -mrs)"

$ chmod +x servinfo

用 bash -x 来调试上述脚本,Bash 先打印出每行脚本,再打印出每行脚本的执行结果:

$ bash -x servinfo
++ hostname
+ echo 'Hostname: vpsee'
Hostname: vpsee
++ date
+ echo 'Date: Thu Sep  3 19:33:48 SAST 2009'
Date: Thu Sep  3 19:33:48 SAST 2009
++ uname -mrs
+ echo 'Kernel: Linux 2.6.18-128.4.1.el5 i686'
Kernel: Linux 2.6.18-128.4.1.el5 i686

如果想同时打印行号的话,可以在脚本开头加上:

export PS4='+${BASH_SOURCE}:${LINENO}:${FUNCNAME[0]}: '

执行结果为:

$ bash -x servinfo
+ export 'PS4=+${BASH_SOURCE}:${LINENO}:${FUNCNAME[0]}: '
+ PS4='+${BASH_SOURCE}:${LINENO}:${FUNCNAME[0]}: '
++4:5:: hostname
+4:5:: echo 'Hostname: vpsee'
Hostname: vpsee
++4:6:: date
+4:6:: echo 'Date: Thu Sep  3 19:42:06 SAST 2009'
Date: Thu Sep  3 19:42:06 SAST 2009
++4:7:: uname -mrs
+4:7:: echo 'Kernel: Linux 2.6.18-128.4.1.el5 i686'
Kernel: Linux 2.6.18-128.4.1.el5 i686

如果只想调试其中几行脚本的话可以用 set -x 和 set +x 把要调试的部分包含进来:

#!/bin/bash

echo "Hostname: $(hostname)"
set -x
echo "Date: $(date)"
set +x
echo "Kernel: $(uname -mrs)"

这个时候可以直接运行脚本,不需要执行 bash -x 了:

$ ./servinfo
Hostname: vpsee
++ date
+ echo 'Date: Thu Sep  3 19:46:53 SAST 2009'
Date: Thu Sep  3 19:46:53 SAST 2009
+ set +x
Kernel: Linux 2.6.18-128.4.1.el5 i686

如果要调试一个非常复杂的 Bash 脚本的话,建议用专门的调试工具,比如:bashdb

Nerios:$9 128MB Xen VPS

nerios

Nerios Communications 2008年1月上线提供shell, BNC, 和 IRC hosting 服务,随后改变了业务,进入 managed VPS 市场。同年9月被另一个 hosting 公司 SystemInPlace 收购。至今 Nerios 仍是 SystemInPlace 的一个独立品牌,提供 managed VPS 服务。Nerios 没有提供 swap,不知道能不能自己建 swap 文件挂载 swap. VPS 配置如下:

服务器在 Chicago, IL 或 Fremont, CA
自制的控制面板
5GB 硬盘
128MB Guaranteed RAM
约660 GB 带宽
2个 IP
9美元

服务器硬件和网络信息:

Our typical server is based on AMD opteron processors. These machine’s are equipped with: 2 AMD Opteron 2216 HE dual-core processors, with a minimum of 8GB RAM. To ensure data integrity and redundancy, we utilize RAID-10 disk arrays with hotswap capabilities.

We provide bandwidth through your choice of Hurricane Electric and Steadfast Networks.

简单调试 Python 程序

在 Python 中也可以像 gcc/gdb 那样调试程序,只要在运行 Python 程序时引入 pdb 模块(假设要调试的程序名为 d.py):

$ vi d.py
#!/usr/bin/python

def main():
        i, sum = 1, 0
        for i in xrange(100):
                sum = sum + i
        print sum

if __name__ == '__main__':
        main()

$ python -m pdb d.py

运行上面的命令后进入以下界面,可以输入类似 gdb 的命令来改变程序的执行流程:

$ python -m pdb 1.py 
> d.py(3)()
-> def main():
(Pdb) 

list 显示程序的最近代码段:

(Pdb) list
  1  	#!/usr/bin/python
  2  	
  3  ->	def main():
  4  		i, sum = 1, 0
  5  		for i in xrange(100):
  6  			sum = sum + i
  7  		print sum
  8  	 
  9  	if __name__ == '__main__':
 10  		main()
[EOF]

next 或者 n 执行下一行代码:

(Pdb) next
> d.py(9)()
-> if __name__ == '__main__':

用 break 在第6行设置一个断点:

(Pdb) break d.py:6
Breakpoint 1 at d.py:6

(Pdb) list
  1  	#!/usr/bin/python
  2  	
  3  	def main():
  4  		i, sum = 1, 0
  5  ->		for i in xrange(100):
  6 B			sum = sum + i
  7  		print sum
  8  	 
  9  	if __name__ == '__main__':
 10  		main()
[EOF]

如果想在函数处设置断点:

(Pdb) break d.main
d.py:3

(Pdb) list
  1  	#!/usr/bin/python
  2  	
  3 B	def main():
  4  ->		i, sum = 1, 0
  5  		for i in xrange(100):
  6  			sum = sum + i
  7  		print sum
  8  	 
  9  	if __name__ == '__main__':
 10  		main()
[EOF]

还可以给断点加条件,比如设置条件只有当 sum > 50 的时候才 break:

(Pdb) break d.py:6, sum > 50
Breakpoint 1 at d.py:6

如果想查看某个变量的值,可以用 pp 命令打印出来:

(Pdb) step
> d.py(5)main()
-> for i in xrange(100):
(Pdb) pp sum
0

可以直接在程序里使用 pdb 模块,import pdb 后 pdb.set_trace():

#!/usr/bin/python
import pdb

def main():
        i, sum = 1, 0
        for i in xrange(100):
                sum = sum + i
        pdb.set_trace()
        print sum

if __name__ == '__main__':
        main()

这样只要运行程序 ./d.py 就可以直接运行到 print sum 处:

$ ./d.py 
> d.py(9)main()
-> print sum
(Pdb) 

总结

命令 用途
break 或 b 设置断点
continue 或 c 继续执行程序
list 或 l 查看当前行的代码段
step 或 s 进入函数
return 或 r 执行代码直到从当前函数返回
exit 或 q 中止并退出
next 或 n 执行下一行
pp 打印变量的值
help 帮助

斯威士兰,边境、食物

会场有 wifi,可惜 hotel 的 wifi 不是免费的,所以只能到会场发图片了。这几天会陆续发一些图片。

边境:虽然地图标得很清楚,路上还是迷路了几次,终于下午4点左右到了南非和斯威士兰的边境。出境南非和入境斯威士兰都很简单,没有什么刁难,过了一个检查站就到了斯威士兰领土,没有想像中的 “重兵把手”,只有一个边防人员伸头扫了扫车内,没有发现什么异物就放行了。

食物:说实话还不知道斯威士兰的传统食物是什么,不过传统服装已经秀过了,在注册 check in 的时候的领到一件传统服装,用来参加晚上的篝火宴会。食物很丰富,烤羊腿是我的最爱:)

swaziland food

swaziland food

swaziland border

swaziland border

斯威士兰,我来了

swaziland map

明天凌晨的飞机去非洲的一个小国家斯威士兰参加一个会议,确切的说是开车去斯威士兰,因为没有订到斯威士兰的机票,所以只能先飞到南非的约翰内斯堡再开车去斯威士兰,大概要开3-4个小时。这次参加会议的人很多,加上斯威士兰没什么飞机,提前两个月都没能订上机票,如果没订上 hotel 那可就惨了。今天闲下来查了一下斯威士兰的概况,主要是为收拾行李做准备。

VPSee 对斯威士兰兴趣不大,更关心的是能不能在会议上遇到有趣的人,听到有趣的演讲、有趣的讨论,看到有趣的演示。会议的内容是关于电信方面的学术和工业会议,华为是这次会议的赞助者之一,很高兴能在类似的国际会议里看到中国公司的身影。会议本身提供了一个很好的交流平台,对于那些有着海外战略的中国公司来说参与到国际化交流中非常重要,那些跨国巨头的公司文化不只是赚钱,他们参与到当地,帮助建设本地社区,利用雄厚的资金在各个大学建实验室,发奖学金,捐助设备,赞助各类学术/工业会议,参与工业标准的制定,无孔不入,极力维护一个以他们为主宰的食物链。扯远了~~

天气和温度

天气状况是最重要的了,因为决定如何收拾行李。斯威士兰全年气候大致可分为干湿两季,9月到4月是湿季,天气湿热,5月到8月是干季,干爽凉快。现在刚好在干湿季之间。

swaziland weather

提示

会议组织者给每个人发了邮件,提到了一些注意事项:
1、城市大型中心里面提供的自来水可以直接喝,不过为了安全最好饮用瓶装水;
2、禁止给皇室、警察、军用车、军用飞机拍照;给私有住宅拍照需要得到主人许可;赌场不允许拍照;
3、在一些地区疟疾是危险的,不要随便去游泳。

概况

去一个国家稍微了解一下国家总是好的,以下信息摘自 Wikipedia:

国名:斯威士兰王国 / Kingdom of Swaziland
首都:Lobamba、Mbabane
位置:位于非洲南端、被南非、莫桑比克包围的内陆国
面积:17,364 平方公里
人口:1,141,000 人
气候:大致可分为干湿两季,9月到4月是湿季,5月到8月是干季
语言:英语、SiSwati
宗教:基督教/天主教 83%
货币:Swazi Lilangeni (SZL) (L),与南非兰特等值,南非兰特可在斯威士兰使用
时区:UTC +2:00
插头:230V、50Hz,三个插脚排成三角形
电话国码:+268
节日:1/1新年、4/9国王恩史瓦帝三世诞辰纪念日、4/25国旗日、7/22国王索布札二世诞辰纪念日、8-9月芦苇节、9/6独立纪念日、12-1月丰年祭

用 Python 做单词拼写检查

这几天在翻旧代码时发现以前写的注释部分有很多单词拼写错误,这些单词错得不算离谱,应该可以用工具自动纠错绝大部分。用 Python 写个拼写检查脚本很容易,如果能很好利用 aspell/ispell 这些现成的小工具就更简单了。

要点

1、输入一个拼写错误的单词,调用 aspell -a 后得到一些候选正确单词,然后用距离编辑进一步嗮选出更精确的词。比如运行 aspell -a,输入 ‘hella’ 后得到如下结果:
hell, Helli, hello, heal, Heall, he’ll, hells, Heller, Ella, Hall, Hill, Hull, hall, heel, hill, hula, hull, Helga, Helsa, Bella, Della, Mella, Sella, fella, Halli, Hally, Hilly, Holli, Holly, hallo, hilly, holly, hullo, Hell’s, hell’s

2、什么是距离编辑(Edit-Distance,也叫 Levenshtein algorithm)呢?就是说给定一个单词,通过多次插入、删除、交换、替换单字符的操作后枚举出所有可能的正确拼写,比如输入 ‘hella’,经过多次插入、删除、交换、替换单字符的操作后变成:
‘helkla’, ‘hjlla’, ‘hylla’, ‘hellma’, ‘khella’, ‘iella’, ‘helhla’, ‘hellag’, ‘hela’, ‘vhella’, ‘hhella’, ‘hell’, ‘heglla’, ‘hvlla’, ‘hellaa’, ‘ghella’, ‘hellar’, ‘heslla’, ‘lhella’, ‘helpa’, ‘hello’, …

3、综合上面2个集合的结果,并且考虑到一些理论知识可以提高拼写检查的准确度,比如一般来说写错单词都是无意的或者误打,完全错的单词可能性很小,而且单词的第一个字母一般不会拼错。所以可以在上面集合里去掉第一个字母不符合的单词,比如:’Sella’, ‘Mella’, khella’, ‘iella’ 等,这里 VPSee 不删除单词,而把这些单词从队列里取出来放到队列最后(优先级降低),所以实在匹配不了以 h 开头的单词才去匹配那些以其他字母开头的单词。

4、程序中用到了外部工具 aspell,如何在 Python 里捕捉外部程序的输入和输出以便在 Python 程序里处理这些输入和输出呢?Python 2.4 以后引入了 subprocess 模块,可以用 subprocess.Popen 来处理。

5、Google 大牛 Peter Norvig 写了一篇 How to Write a Spelling Corrector 很值得一看,大牛就是大牛,21行 Python 就解决拼写问题,而且还不用外部工具,只需要事先读入一个词典文件。本文程序的 edits1 函数就是从牛人家那里 copy 的。

代码

 
#!/usr/bin/python
# A simple spell checker
# written by http://www.vpsee.com 

import os, sys, subprocess, signal

alphabet = 'abcdefghijklmnopqrstuvwxyz'

def found(word, args, cwd = None, shell = True):
    child = subprocess.Popen(args, 
        shell = shell,  
        stdin = subprocess.PIPE, 
        stdout = subprocess.PIPE, 
        cwd = cwd,  
        universal_newlines = True) 
    child.stdout.readline()
    (stdout, stderr) = child.communicate(word)
    if ": " in stdout:
        # remove \n\n
        stdout = stdout.rstrip("\n")
        # remove left part until :
        left, candidates = stdout.split(": ", 1) 
        candidates = candidates.split(", ")
        # making an error on the first letter of a word is less 
        # probable, so we remove those candidates and append them 
        # to the tail of queue, make them less priority
        for item in candidates:
            if item[0] != word[0]: 
                candidates.remove(item)
                candidates.append(item)
        return candidates
    else:
        return None

# copy from http://norvig.com/spell-correct.html
def edits1(word):
    n = len(word)
    return set([word[0:i]+word[i+1:] for i in range(n)] +                     
        [word[0:i]+word[i+1]+word[i]+word[i+2:] for i in range(n-1)] +
        [word[0:i]+c+word[i+1:] for i in range(n) for c in alphabet] +
        [word[0:i]+c+word[i:] for i in range(n+1) for c in alphabet])

def correct(word):
    candidates1 = found(word, 'aspell -a')
    if not candidates1:
        print "no suggestion"
        return  

    candidates2  = edits1(word)
    candidates  = []
    for word in candidates1:
        if word in candidates2:
            candidates.append(word)
    if not candidates:
        print "suggestion: %s" % candidates1[0]
    else:
        print "suggestion: %s" % max(candidates)

def signal_handler(signal, frame):
    sys.exit(0)

if __name__ == '__main__':
    signal.signal(signal.SIGINT, signal_handler)
    while True:
        input = raw_input()
        correct(input)

更简单的方法

当然直接在程序里调用相关模块最简单了,有个叫做 PyEnchant 的库支持拼写检查,安装 PyEnchant 和 Enchant 后就可以直接在 Python 程序里 import 了:

>>> import enchant
>>> d = enchant.Dict("en_US")
>>> d.check("Hello")
True
>>> d.check("Helo")
False
>>> d.suggest("Helo")
['He lo', 'He-lo', 'Hello', 'Helot', 'Help', 'Halo', 'Hell', 'Held', 'Helm', 'Hero', "He'll"]
>>>