HP Gen8 MicroServer家庭存储服务器使用一周年记

博主是一个爱拍照的码农。从2003年开始拥有数码相机以来,平均每年都会积攒60GB~100GB的数码照片。如何保存这些照片又方便,又安全?

最开始的时候,照片存在台式机的硬盘里,有一次赶上硬盘损坏,一下子几乎丢了一年的照片,别提多伤心了。后来用两个移动硬盘做备份。每次备份照片都需要手工把照片同时拷贝到两个移动硬盘里,费时费事,还容易出错,很不方便。再后来有了百度云盘,一直用免费账号,有3TB多的存储空间,很爽很开心的用了两年。

后来终究觉得照片这么重要的资产,交给别人去管不放心(后来百度云不是限速下载了么),考虑了很久,去年10月终于下决心自建一套照片和视频存储。整理了硬件需求如下

 

  1. 硬件成熟、稳定,支持四个机械硬盘
  2. 千兆以太网
  3. 兼顾成本
  4. 低功耗

 

软件需求如下:

  1. 支持Linux
  2. 支持Samba
  3. 支持PT下载

 

比较了各种专用的NAS,DIY存储服务器,当然还有HP Gen8 MicroServer(以下简称Gen8)方案,选来选去,发现还是德淘HP Gen8 MicroServer方案最灵活,而且成本较低。

看一下HP的官方Spec:

Intel Celeron® G1610T 2x 2.30 GHz

2 GB RAM (max. 16 GB)

4x 3.5″ SATA3

RAID 0 / 1 / 10

5x USB 3.0, 5x USB 2.0, 1x GB-LAN

 

Gen8的主要优点

  1. HP是老牌商用服务器厂商了,这么多年的供应链管理和质量管理经验,做出来的产品应该不会不靠谱。
  2. 支持自己安装、配置操作系统。能装Linux
  3. 海淘裸机的全部费用才1500左右,跟其他专业家用NAS相比,基本算最便宜的啦。

然而,Gen8的缺点也比较明显:

  1. CPU和内存配置都比较低,不适合做复杂运算类工作;
  2. 机箱过小,硬件扩展能力较差;
  3. 这是一台服务器,配置比较复杂;

 

不过对于博主这样仅仅用Gen8 做存储和下载的码农来说,上面这些缺点都不是很要紧啦。存储和下载,本身不会占用太多CPU和内存资源,也不需要扩展插卡。Gen8 应付博主的所有需求,妥妥的,足够啦。

张大妈上有不少关于德淘Gen8的攻略,感兴趣的可以自行挖贴。

以下是Gen8的外观照片(声明一下,不是开箱,是使用一年以后的状态):

正面照:

1

背面照:

2

正面开舱门照:

dsc05590

背面IO口照:

dsc05593

拆机内部右侧照:

dsc05589

拆机内部顶侧照:

dsc05588

拆机内部左侧照:

dsc05587

拆机内部风扇照:

dsc05591

 

 

博主这里主要想给大家介绍的是如何配置和使用这台服务器:

磁盘硬件配置

启动盘:1 x SanDisk CZ48 32GB USB 3.0 U盘。

博主用了一个32GB容量的优盘作为操作系统盘。Gen8主板上自带一个USB插口,可以使用USB设备作为引导盘引导系统,可以像这样把U盘藏在机箱里:

SanDisk CZ48 32GB USB 3.0 U盘

3

Gen8提供四个盘位,博主用了三个,两个做存储盘,一个做下载盘,具体配置如下:

存储盘:2 x SEAGATE 4TB SATA3台式机硬盘(ST4000DM000)

下载盘:1 x SEAGATE 1TB SATA3台式机硬盘(ST1000DM003)

后面软件配置把两个4TB存储盘做成镜像备份,保证数据安全。下面是照片:

4

 

磁盘BIOS配置

Gen8 主板集成了一个B120i磁盘控制器。B120i支持两种工作模式:AHCI和RAID模式。两种模式各有优缺点:RAID模式配置镜像备份相对简单,但是硬盘无法休眠(Spin-Down);AHCI模式支持硬盘休眠,但是AHCI模式下系统无法感知到硬盘温度,导致系统把提高风扇转速。

综合考虑下来,由于我希望服务器24小时在线,随时访问随时用,但使用频率又不是很高,所以还是选了AHCI模式,以便不使用Gen8的时候,让硬盘休眠,延长硬盘使用寿命。至于风扇转速问题,我感觉噪音不是很大,也就没去管。事实上,网上可以找到一些破解固件,在AHCI模式下强制降低转速的,感兴趣的可以自行搜索。不过考虑到稳定性风险,博主还是没敢用。

 

操作系统和文件共享

Gen8 可选的主流操作系统方案主要有以下几种:Windows Server,Linux,VMWare ESXi虚拟化方案。博主不会玩Windows Server,也不想因为高虚拟化把系统的层次结构弄得太复杂,再加上博主自己是一个Debian粉,选系统的时候,想当然的选了Debian。当然,其实选更流行的Ubuntu也差不多。

文件共享用了Samba方案,也就是Windows文件共享。除了Gen8之外,博主家里有一台Windows客厅电脑,一台Macbook Pro,一台Windows笔记本,还有一台LInux/Windows双系统的PC。之所以选Samba,主要是因为Linux/Windows/OSX对Samba都有原生系统级支持,用起来很方便,而且手机上也有ES文件浏览器这样的软件,也能访问共享存储。

Linux硬盘自动Spin-down配置

安装hdparm:

sudo apt-get install hdparm

hdparm配置添加到启动脚本:

/sbin/hdparm -B127 -S180 /dev/sda

/sbin/hdparm -B127 -S180 /dev/sdb

/sbin/hdparm -B127 -S180 /dev/sdc

#/dev/sda, /dev/sdb, /dev/sdc分别是三块硬盘的设备名

双盘自动备份

博主用Linux标准的定时任务实现双硬盘定时自动备份。其实也就一条cron配置:

0 0 * * * /usr/bin/rsync -rlptDog –delete /home/super/sdc/backup/ /home/super/sdd/backup/ 1>/dev/null 2>/dev/null

#每天0点,自动将/home/super/sdc/backup/目录内的文件镜像同步至/home/super/sdd/backup/

PT下载

博主用Linux下的BT软件Transmission做PT下载。Transmission提供Web操作界面,对于没有显示器的Gen8来说,用起来非常方便。Transmission安装和配置都很简单,感兴趣的自行搜索吧。

 

整体网络架构

5

有线网络性能测试(客厅电脑 <–> TP-link WDR7500 <–>Gen8):

6

无线网络性能测试(客厅电脑 <–> TP-link WDR7500 <–>Gen8):

7

 

HP MicroServer防尘

Gen8采用前面板进风,后面板出风的方式进行散热。博主在Gen8前面板进风口处添加了双层防尘棉,并用强力磁铁固定到Gen8前面板(Gen8前面板是铁质)。如下图:

8

 

黑色防尘棉上的圆形金属就是强力磁铁。强力磁铁和防尘海绵都是在马云家买的,大家自行搜索吧。

前面板安装防尘棉后,系统温度平均提升1摄氏度,不过相比它带来的好处,这点损失不算啥。

下面是刚换下来的,已经使用了半年的防尘海绵:

9

 

 

北京市夫妇一方为独生子女的家庭办理再生育子女《生育服务证》的程序和所需材料须知

夫妇一方为独生子女的家庭办理再生育子女
《生育服务证》的程序和所需材料须知
【一方为本市户籍居民,一方为外省市户籍居民】
办事依据:
依据《北京市人口与计划生育条例》(以下简称《条例》)及《北京市生育服务证管理办法》等相关文件要求,一方为本市户籍、一方为外省市户籍的夫妻,符合条件的,到本市一方户籍所在地乡(镇)人民政府或街道办事处申请并经区(县)人口计生委审批,办理再生育一个子女《生育服务证》。
办理条件:夫妇一方为独生子女,并且只有一个子女的
办事程序:
申请办理再生育一个子女《生育服务证》的夫妻,应当按照以下程序办理:
1、在北京市及各区(县)人口和计划生育委员会网站下载或到本市一方户籍所在地乡(镇)人民政府或街道办事处领取《再生育一个子女申请审批表》(以下简称《申请审批表》),并如实填写;
2、本市一方存档单位(无存档单位的由村居委会)在《申请审批表》“单位情况证明”栏注明婚育情况并盖章;
3、到本市一方户籍所在地乡(镇)人民政府或街道办事处提交《申请审批表》及相关材料;
4、本市一方户籍所在地乡(镇)人民政府或街道办事处、区(县)人口计生委审核、盖章,符合条件的,办理再生育一个子女《生育服务证》;在外省市户籍一方“单位意见”栏注明“外省市已出具婚育情况证明”并盖章。
所需材料:
申请办理再生育一个子女《生育服务证》的夫妻,应当提供以下材料:
(一)基础材料
1、夫妻双方个人申请;
2、《再生育一个子女申请审批表》原件(一式两份);
3、夫妻双方户口本(首页、本人页及变更页)、身份证原件及复印件(一份);
4、《结婚证》原件及复印件(一份);
5、外省市一方户籍地区(县)人口计生部门出具的婚育情况证明原件。
(二)根据《条例》第十七条申请所需提交的材料
符合《条例》第十七条相关款项申请再生育子女的,在提交以上基础材料同时,还需分别提供相应的证明材料。
根据《条例》第十七条第二款第(二)项“夫妻一方为独生子女,并且只有一个子女的”申请的,本人是独生子女的还需提交:
(1)其父母的户口本(首页、本人页及变更页)原件及复印件(一份);
(2)其父母存档单位出具(无存档单位的人员由户籍地村居委会出具)的生育子女情况证明原件((父)母死亡的由其原存档单位或户籍地村居委会出具);父(母)为外省市户籍的,由父(母)户籍所在地区(县)人口计生部门出具生育子女情况证明原件。
(3)其父母原生育两个子女的,需提供相关部门出具的其他子女死亡的有效证明;其他子女达到法定婚龄后死亡的,村居委会应出具其未生育或收养子女的证明。
(三)特殊情况需提交的材料
除上述材料外,申请人有下列情形之一的,需提交相应证明材料:
1、一方或双方再婚的,需提交历次《离婚证》原件及《离婚协议书》复印件(一份)或法院判决(调解)书原件及复印件(一份)。(《离婚协议书》复印件应加盖婚姻登记部门查档章,在档案馆存档的复印件应加盖档案馆印章)
2、办理了《独生子女父母光荣证》(以下简称《光荣证》)的,退回《光荣证》原件,没有办理《光荣证》或《光荣证》丢失的,提交本人从未办理《光荣证》或《光荣证》丢失的情况声明,已享受的独生子女父母奖励优惠待遇不予收回。
3、一方或双方丧偶的需提供相关部门出具的原配偶死亡销户证明或《死亡医学证明书》或其他有效死亡证明原件及复印件(一份)。
4、在外国办理结婚登记或离婚的,需由我国驻该国使领馆对结婚或离婚证明材料进行认证,结婚或离婚证件经有资质的翻译公司翻译,并在办理时提交上述材料原件及复印件(一份)。
5、子女已出生申请补办第二个子女《生育服务证》的,需提供包括子女《出生医学证明》原件及复印件(一份)在内的相关材料。
6、子女在国外或境外出生的,应提供国外或境外医疗机构颁发的《出生医学证明》,并经有资质的翻译公司翻译。在办理时提交上述证件的原件及复印件(一份)、子女的护照或旅行证原件及复印件(一份)。
7、证件、证明材料系外文的,需提供有资质的翻译机构出具并盖章的中文翻译件原件。
8、其他特殊情况需要提交的材料。
备注:
1、允许生育第二个子女的,生育间隔不少于四年或者女方年龄不低于二十八周岁;
2、以上证明材料复印件请申请人签字和日期。
办理时限:材料齐全且真实有效的法定时限内优先办理
办理部门:北京市昌平区人口和计划生育委员会
联系电话:010-69742879
地 址:北京市昌平区城北街道煤市口胡同2号

2013,year of change

1. 老婆和孩子

老大出生以后,家里闹腾了好久。终于在过去的一年重新稳定下来。这是一好。

习公上任之初,我便隐约中看到两位蒋公的影子,报以厚望。过不然,18大放开单独二胎,心中对未来充满了期盼。这是二好。

2. 工作

又换了一份工作。这次换之前做过不少尝试。想过去参加Andy的Andy Fiber项目,见过豌豆荚的王俊煜,还见过不少其他人,最终还是无缘。

在智联招聘上贴了一份简历,却发现对自己感兴趣最多的还是百度。

第一次邀请,拒绝了;第二次,于是从了。于是今天回了百度。还得感谢鸣雷哥,不计前嫌的力荐。工作收入还算满足预期,不算多,至少能养家了。这便是三好。

3. 父母

真是有儿方知父母心。父母都健康,这是最好。

4. 关于2014

多做规划。

更多的努力,相信付出一定会有回报。

希望一切更好。

 

Docker 分析(2)

Docker镜像管理

1. 架构

1)Index

存储镜像元数据,包括用户账户信息、镜像的校验码、镜像名称等。

镜像包括四大功能模块:

Web UI

Meta-data store (comments, stars, list public repositories)

Authentication service

Tokenization

Index是dotCloud提供的服务,未公开源码

2)Registry

Registry功能是存储容器镜像,可以依赖于Index运行,也可以独立运行。

2. 镜像管理流程

1)下载镜像

Contact the Index to know where I should download “samalba/busybox”

  1. Index replies: a. “samalba/busybox” is on Registry A b. here are the checksums for “samalba/busybox” (for all layers) c. token
  2. Contact Registry A to receive the layers for “samalba/busybox” (all of them to the base image). Registry A is authoritative for “samalba/busybox” but keeps a copy of all inherited layers and serve them all from the same location.
  3. registry contacts index to verify if token/user is allowed to download images
  4. Index returns true/false lettings registry know if it should proceed or error out
  5. Get the payload for all layers

2)上传镜像

upload

  1. Contact the index to allocate the repository name “samalba/busybox” (authentication required with user credentials)
  2. If authentication works and namespace available, “samalba/busybox” is allocated and a temporary token is returned (namespace is marked as initialized in index)
  3. Push the image on the registry (along with the token)
  4. Registry A contacts the Index to verify the token (token must corresponds to the repository name)
  5. Index validates the token. Registry A starts reading the stream pushed by docker and store the repository (with its images)
  6. docker contacts the index to give checksums for upload images

3)镜像存储

本地直接存储为目录。

3. 镜像管理接口参考文档

http://docs.docker.io/en/latest/api/registry_index_spec/

 

Docker配置管理接口

HTTP方法 HTTP Location 对应命令行 接口说明
GET /auth    
  /version version  
  /info info  
  /images/viz images  
  /images/json images  
  /images/search search  
  /images/{name:.*}/history history  
  /images/{name:.*}/json inspect  
  /containers/ps    
  /containers/json ps  
  /containers/{name:.*}/export export  
  /containers/{name:.*}/changes diff  
  /containers/{name:.*}/json inspect/attach  
POST /auth login  
  /commit commit  
  /build build  
  /images/create import/pull/run  
  /images/{name:.*}/insert insert  
  /images/{name:.*}/push push  
  /images/{name:.*}/tag tag  
  /images/getCache    
  /containers/create run  
  /containers/{name:.*}/kill kill  
  /containers/{name:.*}/restart restart  
  /containers/{name:.*}/start start/run  
  /containers/{name:.*}/stop stop stop 和 kill的区别是stop先尝试发送SIGTERM,在发送SIGKILL;
  /containers/{name:.*}/wait wait  
  /containers/{name:.*}/resize attach  
  /containers/{name:.*}/attach logs/attach/run  
DELETE /containers/{name:.*} rm  
  /images/{name:.*} rmi  
OPTIONS      

 

 

Docker 分析(1)

Docker是我看过的第一个Go写的项目。

1. Docker 守护进程初始化流程

1.1 初始化桥接网卡

/sbin/ip route/sbin/ip link add docker0 type bridge

/sbin/ip addr add 172.16.42.1/24 dev docker0

/sbin/ip link set docker0 up

1.2 配置初始iptables

/sbin/iptables -t nat -A POSTROUTING -s 172.16.42.1/24 ! -d 172.16.42.1/24 -j MASQUERADE/sbin/iptables -t nat -D PREROUTING -m addrtype –dst-type LOCAL -j DOCKER/sbin/iptables -t nat -D OUTPUT -m addrtype –dst-type LOCAL ! –dst 127.0.0.0/8 -j DOCKER/sbin/iptables -t nat -D OUTPUT -m addrtype –dst-type LOCAL -j DOCKER/sbin/iptables -t nat -D PREROUTING -j DOCKER/sbin/iptables -t nat -D OUTPUT -j DOCKER

/sbin/iptables -t nat -F DOCKER

/sbin/iptables -t nat -X DOCKER

/sbin/iptables -t nat -N DOCKER

/sbin/iptables -t nat -A PREROUTING -m addrtype –dst-type LOCAL -j DOCKER

/sbin/iptables -t nat -A OUTPUT -m addrtype –dst-type LOCAL ! –dst 127.0.0.0/8 -j DOCKER

该步骤创建自定义的netfilter chain,并将所有到本地的流量都导入到名为DOCKER的chain进行处理。

2. 创建容器实例流程

2.1 客户端:

2.1.1 执行命令

docker run -i -t -p 8888 base  /bin/bash

2.1.2 调用守护进程rest api

2.2 守护进程:

2.2.1. 准备Rootfs

ro = [/var/lib/docker/graph/b750fe79269d2ec9a3c593ef05b4332b1d1a02a62b4accb2c21d589ff2f5f2dc/layer /var/lib/docker/graph/27cf784147099545/layer], rw = /var/lib/docker/containers/d20c06250006494e17d858428d6db3242850e75e4adf9c924abcf7f643f83a8b/rw, target = /var/lib/docker/containers/d20c06250006494e17d858428d6db3242850e75e4adf9c924abcf7f643f83a8b/rootfs

Docker容器的文件系统基于Aufs实现CopyOnWrite,rootfs目录里存镜像,rw存容器本地修改过的内容。

2.2.2 配置容器网络

1)分配容器IP地址2)配置容器桥接网卡

3)配置端口映射

/sbin/iptables -t nat -A DOCKER -p tcp –dport 49153 -j DNAT –to-destination 172.16.42.5:8888

2.3. 启动容器

2.3.1 基于LXC配置模板,动态生成LXC配置文件

2.3.2 根据docker客户端传入参数,生成lxc-start命令行参数

2.3.3 调用lxc-start启动容器,并将进程的标准输出、标准错误输出重定向到虚拟终端或者文件

/usr/bin/lxc-start, CmdArgs = lxc-start -n d20c06250006494e17d858428d6db3242850e75e4adf9c924abcf7f643f83a8b -f /var/lib/docker/containers/d20c06250006494e17d858428d6db3242850e75e4adf9c924abcf7f643f83a8b/config.lxc — /sbin/init -g 172.16.42.1 -e TERM=xterm -e HOME=/ -e PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin — /bin/bash

2.3.4 容器启动后,配置容器的默认路由(在容器内执行配置路由命令)

/sbin/ip route add default via 172.16.42.1

注:

1)同一台机器上的不同容器的虚拟网卡桥接到同一个虚拟的桥接网卡,即docker0接口。

2)由于Docker0接口没有桥接实际外网网口,并且docker没有修改系统默认的ip_forward内核参数(默认为0,不转发),所以默认情况下Docker容器只能访问本机和本机上的其他容器

3)本机以外的机器只能通过DNAT映射的端口号访问容器提供的服务

3. 停止容器实例

3.1客户端

3.1.1 执行命令

docker kill 7ca7b68f0a6b

3.1.2. 调用守护进程rest api

3.2 守护进程:

1)停止容器lxc-kill -n 7ca7b68f0a6bfda2e6b21385735b05966dae4b8035e28a0d3a57bd629db6a58f 92)删除DNAT映射/sbin/iptables -t nat -D DOCKER -p tcp –dport 49153 -j DNAT –to-destination 172.16.42.5:8888

这次到此为止,下篇文章讲Docker的镜像管理和API接口。

Google Fiber CDN技术分析

1. 视频资源和带宽

20Mbps码率的视频为例,每秒钟视频占用存储空间为2.5MB,每分钟为150MB。以长度为120分钟长度,码率为20Mbps的高清电影来说,整个视频文件的容量为18GB,如果按照3分钟长度将整个视频划分,将得到40450MB大小的视频文件;如果按照1分钟长度切分视频,将得到120150MB大小的视频文件。由此可见,与传统CDN不同,Google Fiber CDN主要面向大型文件提供存储和缓存功能。

2. 资源定位

Google Fiber的CDN更像是一个CDN运营商服务,不仅给Youtube提供高清视频缓存,更需要给NetFlix,hulu等外部视频内容网站提供高清视频缓存。

通常,CDN运营商借助于智能DNS技术为内容商提供内容缓存和加速服务。内容提供商将资源文件所属域名的CNAME配置为DNS服务商的域名。用户在访问内容资源时,实际的访问请求被定向到CDN服务商的域名内。CDN服务商通过智能DNS将用户请求定向到CDN节点。

虽然我本人身在大宋,从未有肉身翻墙的机会。但是可以料想,Google Fiber给外部视频内容提供商提供的CDN服务不会不借助DNS实现请求重定向。当用户请求被调度到Google Fiber CDN之后,最大的问题成为,如何处理这么大流量的请求,如何有效的构建CDN节点内部和对外的网络流量。

3. Google Fiber CDN节点基本架构猜想

看好了,这里只是猜想,猜错了我不负责哦!

如第一节所述,高清视频属于流量密集型应用。如果CDN节点采用负载均衡+缓存的传统结构,CDN节点内部将产生大量无效的内部流量。用户请求一个视频资源文件,CDN节点需要将资源先从缓存节点发送到负载均衡节点,然后再由负载均衡节点将内容返回给用户。而缓存节点和负载均衡节点之间的流量占用了CDN节点内部大量的带宽资源。

CDN内部流量

如上图所示,如果采用传统LB+Cache的CDN结构,Cache和LB之间将产生大量无效流量,从而降低了CDN节点整体吞吐能力。

大流量CDN节点,最佳的做法应该是让用户请求直接定位到资源文件。但外部内容提供商和CDN服务商松耦合的业务和技术架构决定了内容商不可能直接把CDN节点资源服务器IP地址告诉用户,这该肿么办?

猜想:Google Fiber CDN采用HTTP Redirect实现对内容资源的定位和负载均衡。基本架构如下图所示:

Google Fiber负载均衡

4. 其他问题

1)CDN节点建在哪里?

答:最靠近小区的地方。

2)还有什么东西没说?

答:Google Fiber CDN whole picture;CDN资源和流量的调度;Brad大神新写的Groupcache在Google Fiber里是如何应用的。以上几个问题等到有一天真要在我大宋建立Google Fiber的时候再回答吧。

此系列文章到此结束。

视频CDN技术分析(3)

传统CDN在应用于视频缓存时存在的问题是:

1)负载均衡器成为系统吞吐量瓶颈

视频文件流量远高于图片和其他网页资源文件。如果采用层级化的集群结构,由于负载均衡设备的吞吐能力将远小于缓存集群单机最大吞吐能力之和,负载均衡设备将成为系统整体性能的瓶颈。

2)静态的一致性哈希算法难以应对热点视频缓存需求

过一致性哈希,负载均衡器同一个视频文件的访问请求定向到同一台缓存服务器。对于大量用户、缓存文件数量巨大的,单一热点造成负载不均衡的可能性较小。但在小区环境,用户量较少,对热播电视剧、奥运会开闭幕式这样的热门节目,有可能有大量用户在同一时间端收看相同的直播或者点播节目。对于这样的情景,基于一致性哈希算法做负载均衡将难以满足业务需求。

从上一篇技术分析中,我们了解到优酷对视频资源定位时,直接使用IP地址,而非域名。我猜想优酷的视频CDN的架构应该如下面这张图这样:

Andy Fiber

1)CDN存储节点的主机,直接暴露外网地址。服务器直接对外服务,以此减少负载均衡架构带来的网络带宽收敛;

2)在中央节点设立资源管理和调度器(DataManager),接收用户请求,并根据用户归属位置,以及资源热度,选择一个合适的资源地址,返回给用户。由于管理资源量大,用户并发规模较高,在设计DataManager这样的模块时,通常会采用分域设计。可以是基于数据的分域,也可以是基于用户的分域,或者分级管理调度。——有没有发现DataManager要解决的问题其实和PaaS平台资源调度和管理非常像:-)

3)中央节点DataManager仅需要存储视频文件元数据,无须专门设立全量视频存储。CDN节点的主机,一方面可以充当视频文件的缓存;另一方面也可以承担全量存储的功能——对于任一视频资源文件,DataManager仅需要保证其副本个数不小于最小副本个数,并且DataManager可以全局调度用户请求。这样,CDN可以将流量在最靠近用户、成本最低的的节点传送给用户。

好了,今天就到这里吧,明天继续写对Google Fiber CDN的分析。

视频CDN技术分析(2)

传统CDN架构

1. 传统CDN整体架构

传统CDN-整体架构

源站:传统CDN架构中,源站一般由源站存储和源站缓存两部分构成。源站存储一般采用分布式存储,如TFS,HDFS,MobileFS,Swift等等,源站存储提供大容量、高可用的存储功能;源站缓存主要面向CDN节点未命中的流量。

Edge Cache(CDN节点):Edge Cache是用户侧的缓存节点。如果将用户类比成CPU,则源站缓存可以被称为L2 Cache,Edge Cache,即CDN节点则可以被称为L1 Cache。

2. 源站存储架构

传统CDN-源站存储

以TFS、MogileFS两种比较有代表性的分布式文件系统为例,核心的存储系统主要包括Directory Server和Data Server两部分。

Directory Server:主要任务是维护存储文件的元数据,包括文件名,文件的物理存储位置,副本数量等,以及维护数据的可靠性(位置数据文件的副本数);

Data Server:主要负责存储数据,检查数据完整性,回收清理无用数据等。根据是否支持chunk存储,存储存在不同的设计选择:多个文件存储到一个chunk(例如TFS小文件),不分chunk(例如MogileFS),将一个大文件分成多个小的chunk(例如sheepdog虚拟机块设备存储)。

将小文件合并成大文件的主要目的是减少对系统inode cache的依赖,减少磁盘I/O因系统缓存抖动造成性能抖动。将大文件拆分为多个小的chunk(block),主要目的是提升基于网络实现的虚拟机块存储的随机访问性能。是否分chunk决定于应用存储的场景。

3. Edge Cache(CDN节点)架构

传统CDN-EdgeCache

如上图所示,传统架构包括基于LVS和Haproxy构建负载均衡集群,以及基于Apache TrafficServer或者Squid构建的缓存集群两部分。负载均衡策略通常使用Haproxy的一致性哈希算法,基于缓存对象的全部或部分URL对流量进行静态负载均衡。整个集群的最大吞吐量为LVS负载均衡器的上行容量,缓存容量为缓存服务器缓存容量之和。

4. 传统CDN面临的技术难题

1)如何应对高并发的用户请求。

2)如何从分利用不同类型的存储介质,充分发挥存储资源的性能。

对高并发的诉求,推动用户选择有更好并发性能的缓存服务器,比如ATS;而对存储性能的需求,推动缓存服务器脱离操作系统文件系统,转向直接管理内存、SSD以及机械硬盘等存储介质。

debian wheezy 使用微软雅黑中文字体配置笔记

1. 卸载文泉译中文字体
apt-get purge ttf-wqy-* xfonts-wqy

2. 将微软雅黑字库文件拷贝到指定字体目录
mkdir -p /usr/share/fonts/truetype/msfonts
cp /mnt/sda2/Windows/Fonts/msyh* /usr/share/fonts/truetype/msfonts/

3。运行字体配置工具
cd /usr/share/fonts/truetype/msfonts/
mkfontscale
mkfontdir
fc-cache -fv

4. 重启电脑

视频CDN技术分析(1)

1. 在线视频应用的类型

在线视频分直播和非直播两种类型。直播和非直播两种类型的视频分别对缓存和存储系统的需求很不相同:

对于非直播视频,比如订阅电影、电视剧等,存储系统需要提供视频文件的全量、高可靠存储。而缓存提供热点内容的加速。

对于直播视频,比如新闻,比赛现场直播等,存储和缓存系统只需要保存最近时间片视频内容,而缓存系统系统也只针对最近时间片的内容进行加速。视频时间片过期后,缓存系统应当迅速丢弃过期内容。

2. 视频协议类型

常见的在线视频协议分两大类:非HTTP协议,主要代表是RTMP系列协议。该协议族由Adobe开发,存在协议专利,并且软件License成本较高,视频网站应用不多。另一类是HTTP文件下载类协议,例如FLV格式的视频,以及HTML5中的video标签指定下载文件,以及HLS(HTTP Live Streaming)协议指定下载文件。这些协议的共同特点是都支持HTTP协议下载视频文件。

实际中,国内安卓和PC平台在线视频应用多通过下载FLV文件实现视频播放,而iOS平台则较多使用HLS协议下载,而国外Netflix则的视频则是基于Silverlight播放器和WMV/WMA文件。随着各个主流浏览器对HTML5支持的成熟,通过HTML5播放在线视频的应用也呈逐渐增多的趋势。

3. 常见的在线视频文件特征

通常单个视频会被拆分为多个小文件下载,以优酷网为例,以下链接:http://v.youku.com/v_show/id_XMzU3ODU3NzEy.html,在某次播放时会被拆解成如下多个下载链接:

http://f.youku.com/player/getFlvPath/sid/00_00/st/flv/fileid/030002110050AB37FCDC0C055EEB3E54E37BA8-CB03-ABBA-1762-4817E50184AF?K=12da7b24db7aef9d28289a20

http://f.youku.com/player/getFlvPath/sid/00_00/st/flv/fileid/030002110150AB37FCDC0C055EEB3E54E37BA8-CB03-ABBA-1762-4817E50184AF?K=696b19354b4b439d261d0060

http://f.youku.com/player/getFlvPath/sid/00_00/st/flv/fileid/030002110250AB37FCDC0C055EEB3E54E37BA8-CB03-ABBA-1762-4817E50184AF?K=bcc1c874037016e8261d0060

http://f.youku.com/player/getFlvPath/sid/00_00/st/flv/fileid/030002110350AB37FCDC0C055EEB3E54E37BA8-CB03-ABBA-1762-4817E50184AF?K=1731bc5f48b5f11e28289a20

http://f.youku.com/player/getFlvPath/sid/00_00/st/flv/fileid/030002110450AB37FCDC0C055EEB3E54E37BA8-CB03-ABBA-1762-4817E50184AF?K=4386454c885c3bbe241166a0

http://f.youku.com/player/getFlvPath/sid/00_00/st/flv/fileid/030002110550AB37FCDC0C055EEB3E54E37BA8-CB03-ABBA-1762-4817E50184AF?K=6351e505b031d56c261d0060

http://f.youku.com/player/getFlvPath/sid/00_00/st/flv/fileid/030002110650AB37FCDC0C055EEB3E54E37BA8-CB03-ABBA-1762-4817E50184AF?K=f7af66261b44096e28289a20

http://f.youku.com/player/getFlvPath/sid/00_00/st/flv/fileid/030002110750AB37FCDC0C055EEB3E54E37BA8-CB03-ABBA-1762-4817E50184AF?K=c0d1d358cc3dc10b28289a20

http://f.youku.com/player/getFlvPath/sid/00_00/st/flv/fileid/030002110850AB37FCDC0C055EEB3E54E37BA8-CB03-ABBA-1762-4817E50184AF?K=935bce87f4e327e0261d0060

http://f.youku.com/player/getFlvPath/sid/00_00/st/flv/fileid/030002110950AB37FCDC0C055EEB3E54E37BA8-CB03-ABBA-1762-4817E50184AF?K=c1d3c196c74663aa261d0060

http://f.youku.com/player/getFlvPath/sid/00_00/st/flv/fileid/030002110A50AB37FCDC0C055EEB3E54E37BA8-CB03-ABBA-1762-4817E50184AF?K=c0234e12ee97cb3d241166a0

http://f.youku.com/player/getFlvPath/sid/00_00/st/flv/fileid/030002110B50AB37FCDC0C055EEB3E54E37BA8-CB03-ABBA-1762-4817E50184AF?K=f13ec4bc8ee8c7a2241166a0

http://f.youku.com/player/getFlvPath/sid/00_00/st/flv/fileid/030002110C50AB37FCDC0C055EEB3E54E37BA8-CB03-ABBA-1762-4817E50184AF?K=7e6d5bb19849c787241166a0

http://f.youku.com/player/getFlvPath/sid/00_00/st/flv/fileid/030002110D50AB37FCDC0C055EEB3E54E37BA8-CB03-ABBA-1762-4817E50184AF?K=6917f37f70d6b16a261d0060

http://f.youku.com/player/getFlvPath/sid/00_00/st/flv/fileid/030002110E50AB37FCDC0C055EEB3E54E37BA8-CB03-ABBA-1762-4817E50184AF?K=34ef0b2b23744560241166a0

http://f.youku.com/player/getFlvPath/sid/00_00/st/flv/fileid/030002110F50AB37FCDC0C055EEB3E54E37BA8-CB03-ABBA-1762-4817E50184AF?K=2a89ca1bf5420e5d241166a0

http://f.youku.com/player/getFlvPath/sid/00_00/st/flv/fileid/030002111050AB37FCDC0C055EEB3E54E37BA8-CB03-ABBA-1762-4817E50184AF?K=78d52287001417f1241166a0

浏览器依次访问这些链接时,视频网站会返回视频文件的真正的FLV视频文件下载地址,例如:

“http://123.234.0.34/youku/65731A8885B3F83A13A68121DB/030002111050AB37FCDC0C055EEB3E54E37BA8-CB03-ABBA-1762-4817E50184AF.flv”

以上的例子中,优酷网视频的真正地址是使用IP地址直接访问,每段视频(Video Chunk)长度约为6分钟,每段视频文件大小约为13MB左右。很明显,以上的例子中,优酷网使用了自建的CDN进行了分发。

另外,同一视频相同时间片对应的视频链接地址或者FLV下载地址可能不同。土豆网吴岷曾在分享土豆网自建CDN的架构(http://vdisk.weibo.com/s/a-CrB)时提到,土豆网的视频存储和缓存系统会实时调度系统的流量,并根据用户的请求返回最合适的视频URL地址。也就是说针对同一个视频片段,视频网站可能为不同用户返回不同的视频文件URL。

4. 流量特征

传统的CDN,主要面向静态网页文件、小图片(in tens of KBs)、低码率视频片段(in MBs)的文件,特点是并发请求数量较大,单请求处理的吞吐量较少,即RPS/Bandwidth较大。

视频CDN的特点在于视频片段文件较大(in tens of MB)。如果是20Mbps的高清视频,则单个视频片段的大小则可能超过100MB。用户访问视频CDN资源时,通常对资源的请求数小于传统的网络,但占用带宽更大,及RPS/Bandwith较小。

我眼中的Google Fiber

几个月前跟Andy聊到google fiber,于是对Google fiber进行了简单的分析。我想这也不算什么秘密,于是决定贴到博客,给我的博客除除草。

Google Fiber向小区住户提供速率高达1Gbps的高速网络接入服务,为小区住户在家办公,远程教育,以及高清视频娱乐提供管道支持。Google Fiber包括三要素:管道,家庭终端,以及视频存储和缓存。

Google Fiber的核心是“网”,高清视频是这张网“杀手应用”,而Google Fiber CDN是在在这张网上构筑高清视频的基石。

1. 管道
对于Google Fiber来说,由于承诺为用户提供1Gbps接入速率。较高的速率对OLT设备的需求,以及网络结构的需求跟传统运营商使用的结构就不太一样了,在网络接入侧最大的区别莫过于带宽需求比传统网络大了两个数量级。

主流运营商光纤入户FTTH常用的技术是EPON/GPON。以EPON为例,EPON的物理链路速率为双向1.25Gbps,运营商通常会在采用TDM OLT技术,在局端OLT和用户端ONU之间放置多个分光设备,在单个OLT端口承载多路用户分享带宽。

在Google Fiber的应用场景,每个TDM OLT端口只能满足一个住户的带宽需求。对于中等规模的小区(2000个住户),将有上千个OLT接入端口的需求。这一方面对OLT设备提出了更高端口密度的需求;另一方面,如果OLT设备放在运营商局端,运营商将不得不在局端和小区间铺设大量的光纤。所以更大的可能是,Google Fiber的OLT放在小区侧。当然,另一种可能是,采用WDM OLT技术。
关于网络,我找的的两篇参考文献如下:

High Performance, Low Cost, Colorless ONU for WDM-PON

FTTH Look Ahead – Technologies & Architectures

2. 家庭终端
家庭终端是Google Fiber的最后一米。Google Fiber向用户提供三种类型的设备,分别是:Network Box,TV Box,以及Storage Box,分别提供网络、电视机顶盒以及本地存储功能。

3. 高清视频存储和缓存
存储的作用是存储内容源的全量信息。对Google Fiber的应用场景来说,存储首要保证内容存储的可靠性,然后是存取性能;业界有数不清分布式存储的开源实现,主要的区别有几个方面:元数据的存储方式(集中存储 or 哈希),数据的保存方式(是否分块存储?源文件是否分片存储?是否支持多副本等),以及数据访问接口(支持WebDAV 、HTTP 还是Posix标准接口等)。存储功能应该在城域网汇聚节点,或者更高级别的网络核心实现。

缓存则是将热数据就近保存近用户侧网络,减少汇聚层网络流量压力。用户访问首先落在缓存服务器上。缓存服务器检查本地资源,如果没有被请求资源,则加载并将其返回给用户。

Google Fiber一定会对高清视频内容进行缓存。Google Fiber提供的超大带宽,决定了Google Fiber的CDN在网络拓扑中相对位置及架构与传统的视频CDN存在一些不同。针对Google Fiber CDN的分析,我会在后面的文章里详细介绍。

马尾行之船政文化景区图片记-2012年9月22日

到福建也两个月了,周末不是宅着,就是加班着,就是没出过远门。马上国庆了,决定远行一趟,去马尾看船。
坐了一个小时的公共汽车,首先到了罗星塔公园。
公园大门:

塔身全貌:

“朝朝朝朝朝朝朝,长长长长长长长”,怎么年,什么意思?呵呵

来张全景:

罗星塔公园旁边的一号船坞。开始看了有些失望——怎么就一艘破船?后来在近代海军博物馆看了原理介绍,又觉得还是有些意思的。那么,现代的船坞,又是如何工作的呢?

游完了一号船坞,又先后去了昭忠祠、马限山公园、马江海战纪念馆、近代海军博物馆。
昭忠祠:

船政精英馆:

梅园监狱:

英国领事分馆:

领事分馆宽敞的走廊:

进口的大炮:

最后一站,是近代海军博物馆。
我喜欢看这三搜船:




于是感叹,打仗和摄影差不多,器材真的很重要!

两年了,一晃而过

本想写点回忆,写点总结,记录两年来走过的路。

回忆太琐碎,如果要挟得客观,就得把痛苦和快乐的事情一一道来。

两年来,快乐不少,痛苦也许更多。

想来想去,还是不要分享痛苦的好,于是就此止住。

两年前,我以为自己是something;

两年后,我知道自己神马都不是。

伤过我的人,我记着,因为这也是人生的一部分;

我伤过的人,我知道无法弥补,对不起。

printf(“儿子,你好!”)

一个apache log分析工具

帮朋友分析VPS性能时写的小东西。没有“产品化”,不过我觉得基本可用了,可以释放出来。

有需求的话,可以按需稍加修改,获取想要的信息,:-)


#!/usr/bin/env python

import re
import sys


g_ip_record={}
g_time_record={}

def analyze_by_ip():
	print 'number of IPs : %d' % len(g_ip_record)
	


def analyze_by_time():
	keys = g_time_record.keys()
	keys.sort()
	for hour_min in keys:
		print '%s : %d' % (hour_min , g_time_record[hour_min])

def analyze():
	analyze_by_time()
	analyze_by_ip()
	

def get_hour_min(time):
	#pattern = re.compile('.+/May/2011:(\d+:\d+:)\d+.+')
	#pattern = re.compile('.+/Jun/2011:(\d+:\d+:)\d+.+')
	#pattern = re.compile('.+/.+/.+:(\d+:\d+:)\d+.+')
	pattern = re.compile('24/Jun/2011:(\d+:\d+:)\d+.+')
	match = pattern.match(time)
	if match:
		return match.group(1)
	else:
		#print 'unmatched time string: %s' % time
		return ''


class access_record:
	def __init__(self, time, browser):
		self.time = time
		self.browser = browser

def process_access_record(ip, time, browser):
	#print 'ip=%s, time=%s, browser=%s' % (ip, time, browser)
	access_rec = access_record(time, browser)
	try:
		g_ip_record[ip].append(access_rec)
	except KeyError:
		g_ip_record[ip] = [access_rec]

	hour_min = get_hour_min(time)
	
	#print 'hour=%d' % hour
	if hour_min:
		try :
			g_time_record[hour_min] += 1
		except KeyError:
			g_time_record[hour_min] = 1


def set_parse_pattern():
	parse_pattern = '/dragonsight_service/compass_lite_ad.xml'
	#pattern = re.compile('(^\d+\.\d+\.\d+\.\d+).+\[(.+)\].+compass_lite_update.xml.+\"(.+)\"$')
	pattern = re.compile('(^\d+\.\d+\.\d+\.\d+).+\[(.+)\].+\"(.+)\"$')
	return pattern


def parse_log_line(line_data, pattern):
	ip = None
	time = None
	browser = None
	match = pattern.match(line_data)
	if match:
		ip = match.group(1)
		time = match.group(2)
		browser = match.group(3)

	return (ip, time, browser)

def main():
	if len(sys.argv) != 2:
		print 'Usage: parse_apache_log.py <log_file>'
		exit (-1)

	log_pattern = set_parse_pattern()
	file_name = sys.argv[1]
	log_f = open (file_name, "r")

	while True:
		line_data = log_f.readline()
		if not line_data :
			break;
		ip, time, browser = parse_log_line(line_data, log_pattern)
		if ip and time and browser:
			process_access_record(ip, time, browser)

	log_f.close()

	analyze()

if __name__ == '__main__':
	main ()



关于pysectunnel.py

发表上一遍博客《每个人都应该有属于自己的VPN协议》和相关代码之后,相继收到很多同学的评论和邮件——真没想到我的博客还有人来读啊……

关于pysectunnel.py的使用方法,现在这个小程序仅仅是刚好能用的状态,很丑,很低效。所以我决定还是闭关修炼一段时间,等小玩意儿完善一些了在正式发布吧。

敬请耐心等待。

strongpapa

每个人都应该有属于自己的VPN协议

既然有些技术是基于协议识别的和DPI的,为什么不自己创造一种VPN协议呢?

一个及其简单的原型,需要配合squid和国外VPS使用,欢迎来信索取使用方式。


#!/usr/bin/env python

import sys
import socket
import threading
import time


help_msg ="""
Info:
	pysectunnel forwards encrypted data from local_port to sink_port on sink_host.
Usage:
	pysectunnel local_port sink_host sink_port
"""

LOGGING=0
LOG_LEVEL=1

def log(log_level, log_str):
	if LOGGING:
		if log_level <= LOG_LEVEL:
			print '%s:%s' % (time.ctime(), log_str)
			sys.stdout.flush()

def encrypt_data(data):
        encrypted_data=[]
        for i in range(len(data)):
                encrypted_data.append( chr(ord(data[i]) ^ 0x17) )

        return ''.join(encrypted_data)


class TunnelThread(threading.Thread):
	def __init__(self, local_sc, sink_sc, encrypt_enable):
		threading.Thread.__init__(self)
		self.local_sc = local_sc
		self.sink_sc = sink_sc
		log(1, 'Creating new tunnel thread %s ( %s -> %s )' % ( self, self.local_sc.getpeername(), self.sink_sc.getpeername() ) )
	def run(self):
		while True:
			try:
				data = self.local_sc.recv(1024)
				if not data:
					break

				encrypted_data = encrypt_data(data)
				self.sink_sc.sendall(encrypted_data)

			except:
				break
		log(1, 'Terminating tunnel thread %s' % self)

		
class TunnelManager:
	def __init__(self, local_port, sink_host, sink_port):
		self.local_port = int(local_port)
		self.sink_host = sink_host
		self.sink_port = int(sink_port)
		print 'Initializing PySecTunnel...'	

	def work(self):
		print 'PySecTunnel working...'
		manager_sc = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
		manager_sc.bind(('0.0.0.0', self.local_port))
		manager_sc.listen(5)
		while True:
			local_sc, address = manager_sc.accept()
			print 'Accepted connection from %s:%s' % address
			sink_sc = socket.socket( socket.AF_INET, socket.SOCK_STREAM )
			try:
				sink_sc.connect( ( self.sink_host, self.sink_port ) )
				log (1, 'Creating new tunnel thread %s ( %s -> %s )' % (self, local_sc.getpeername(), sink_sc.getpeername() ) )
			except:
				log(0, 'Error connecting')

			TunnelThread( local_sc, sink_sc, 0 ).start()
			TunnelThread( sink_sc, local_sc, 1 ).start()
				
def main():
	if len(sys.argv) != 4:
		print help_msg
		return 1
	local_port = sys.argv[1]
	sink_host = sys.argv[2]
	sink_port = sys.argv[3]	
	tunnel_manager = TunnelManager(local_port, sink_host, sink_port)
	tunnel_manager.work()

	return 0

if __name__=='__main__':
	sys.exit(main())