科大开源软件镜像是怎样炼成的
Update (2014-09-29): 由于 mirrors 配置文件里有一些不适合公开的内容,现在配置文件不再公开,本文中的一些链接变成了死链,非常抱歉。
由于科大开源软件镜像(mirrors.ustc.edu.cn)发生磁盘故障,stephen、tux 和我 (boj) 都不在学校,加上7月 mirrors 出故障之后就一直没恢复彻底,是时候推倒重来了。这次 mirrors 重建要完全由在校的同学们完成,这也是锻炼技术的一个机会。这里,我来简要解释开源软件镜像包括哪些部分,应该如何搭建。由于 sourceforge 还在等着我们同步,希望能在三天内恢复基本服务,一周内重建好包括同步在内的整个系统。
WTF?
所谓开源软件镜像,就是从官方站点同步一些 GNU/Linux 发行版和知名开源软件的软件仓库。用户通过修改配置文件,就近使用软件仓库镜像,以加快下载速度,减轻官方站点的负载。
做一个开源软件镜像需要什么呢?
- 稳定的带宽,一般要 100Mbps 以上
- 较大的硬盘,一般要 several TB
- 稳定的服务器,在线时间 99% 以上
开源软件镜像主要是两个模块:提供下载服务、定期从官方源同步。事实上,科大开源软件镜像目前提供 HTTP、rsync、FTP 服务,可以通过 rsync、ftpsync、ssh-trigger、ftp-push、git 等方式从上游源(upstream)同步。一个实际运行的开源软件镜像还需要服务状态监控。
下文将分几个方面,介绍科大开源软件镜像(下称 mirrors)是如何搭建的:
- 网络配置
- 存储配置
- HTTP、rsync、FTP 服务
- 虚拟机
- 从上游源定期同步
- 服务器监控
网络配置
mirrors 目前有4个 ISP。它们是:
- 202.141.160.110 China Telecom(电信)
- 202.141.176.110 China Mobile(移动)
- 202.38.95.110 CERNET(教育网)
- 2001:da8:d800:95::110 CERNET2(教育网IPv6)
mirrors 有两块千兆网卡,eth0 是连接外网的,eth1 是网线直连磁盘阵列的。一根网线,怎么是4个ISP?奥妙在于网络中心交换机上的 VLAN。交换机上运行着 802.11Q 协议,主机需要在网卡上指定 VLAN ID,给数据包打上对应的 tag,才能访问对应的线路。China Telecom、China Mobile、CERNET/CERNET2 的 VLAN ID 分别是 10、400、95,也就是需要在 /etc/network/interfaces 里指定 vlan10、vlan400 和 vlan95 并分别绑定上述IP。
1 | iface vlan95 inet static |
下面还要配置路由规则(写在 /etc/rc.local 里)。全局的默认是教育网出口。来自教育网的请求要从教育网回复,来自电信的请求要从电信回复,来自移动的请求要从移动回复。关于这些配置的原理,参见多线接入主机的诡异 NAT。
1 | ip route add default via 202.38.95.126 |
我们在实际运行时发现,移动出口访问国外网络的网速比较快。有两种方案:
- 在同步脚本里 bind 某个特定的 IP 而非 0.0.0.0,rsync、lftp、wget、curl 等均有指定 bind-address 的参数。
- 使用 http://gitlab.lug.ustc.edu.cn/boj/smartproxy(需要注册登录)的ISP所在IP表,让教育网走教育网,电信走电信,默认走移动出口。
存储配置
mirrors 的存储资源包括:
- 10块 SATA 硬盘,采用硬 RAID1,构成 sda、sdb、sdc、sdd、sde,空间分别是1T或2T。现在损坏的是 sda。这些盘上建立了LVM卷组。其中两块1T的盘目前分别用于根文件系统和日志,三块2T的盘用于数据。
- 26TB 磁盘阵列,通过 iscsi 千兆网直连。目前分了一块 20TB 的 XFS 分区(其余空闲),已占用 13T,其中有 5T 的数据可以删除。需要扩容到至少 22TB 以容纳 预计占用 10T 的 sourceforge 镜像。
- 256GB SSD,计划用于缓存优化,尚未使用。
在重新配置硬盘的时候,要注意:
插拔硬盘时,不要改变硬盘的插槽,否则硬 RAID 会被破坏,可能导致大量数据丢失。
不要随意拆散 LVM 卷组。熟悉 vgdisplay、pvdisplay 等命令,查看清楚 LVM 信息后再操作。
配置完毕之后,一定要写个文档写明每个块设备的名称、UUID、LVM 卷组;每个分区或 LVM 卷的名称、挂载点、用途。(血泪教训啊)
iSCSI 的配置方法(来自 jameszhang):eth1 接口设置 192.168.10.2/24
sudo apt-get install open-iscsi
编辑 /etc/iscsi/iscsid.conf,改为 node.startup = automatic
sudo iscsiadm -m discovery -t st -p 192.168.10.1
sudo iscsiadm -m node –login 可以看到磁盘
配置好之后要使用 udev rules 把块设备名称(sda…)固定下来。不然重启之后硬盘名字变了,看起来很麻烦。fstab 和各种脚本里应尽量使用 UUID,以防万一。
HTTP、rsync、FTP 服务
请参考老 mirrors 的配置:(需要注册登录)
http://gitlab.lug.ustc.edu.cn/mirrors/mirrors-system-conf/tree/master
HTTP - Nginx
Mirrors 使用 Nginx 高性能 Web 服务器提供 HTTP 服务。Nginx 配置文件应当受到版本控制,建立一个独立的 git 仓库(目前是跟其他配置混杂在一起的)。
nginx.conf 是 nginx 配置文件的入口,它 include 其他配置文件,包括 conf.d 中的自定义 log format,还有 sites-enabled 中的“虚拟主机”。
sites-enabled 中的每个文件都是相对路径链接到 sites-available 中。其中 mirrors.ustc.edu.cn-{localhost,vlan10,vlan400,vlan95} 是 mirrors 主站4条线路的配置文件,它们 include 公共的主站配置文件 mirrors.ustc.edu.cn-common。对4条线路分开配置文件,可以方便对不同的线路定制不同的访问控制策略。
mirrors 的各个子站,包括 pypi、archlinuxarm、mirrors lab,都是单独的配置文件。newindex 和 testindex 是我们测试的未来首页,其中 newindex 中有根据 User Agent 判断是否浏览器的示例。
要注意,server 块内的 listen directive 应尽量列出所有监听的IP(三个 IPv4 和一个 IPv6),不要监听 0.0.0.0,因为 mirrors 以后可能添加 IP 专用于某个用途,我们的 “战略IP储备” 有 202.38.95.106 和 202.141.176.111。
Nginx 的一个问题是,当磁盘 I/O 成为瓶颈时,Nginx 响应速度会明显降低。因此应密切关注 mirrors 的 I/O 负载情况。
要注意配置 LogRotate,定时对日志文件进行压缩,并丢弃年代久远的日志文件。 http://gitlab.lug.ustc.edu.cn/mirrors/mirrors-system-conf/blob/master/logrotate.d/nginx
按照目前的 nginx 配置,添加源的时候,只要建立符号链接到 /var/www。
rsync - rsyncd
rsync 协议是大量文件同步的好工具,从上游源同步优先使用 rsync。为方便国内其他开源软件镜像,原则上所有镜像都提供 rsync 服务。
老 mirrors 的 rsyncd 配置文件:
http://gitlab.lug.ustc.edu.cn/mirrors/mirrors-system-conf/blob/master/rsyncd.conf
要写 /etc/rsyncd.motd 作为 “Message Of ToDay” 即 rsync 显示的头部一大堆文字。
rsyncd 会给每个连接 fork 一个进程,会在连接建立的时候读取最新的配置文件,因此修改配置文件不需要重启服务。
根据目前的 rsync 配置,添加源的时候,需要修改 rsyncd.conf 加入对应的配置。
FTP - vsftpd
mirrors 使用 vsftpd 作为 FTP 服务器:http://gitlab.lug.ustc.edu.cn/mirrors/mirrors-system-conf/blob/master/vsftpd.conf
由于每个 FTP 连接需要 vsftpd fork 一个新进程,对服务器的资源消耗较大,因此建议使用 HTTP 访问。基于性能上的考虑,我们之前只对一些必要的源开启了 FTP 访问。不过很多用户有批量下载文件的需要,又不会使用 lftp (http mirror)、rsync 等工具,因此还是有必要提供 FTP 访问的。可以先放开,再观察其访问量和对性能的影响。
根据目前的 FTP 配置,添加源的时候,需要修改 fstab 将数据目录 bind mount 到 /srv/ftp/project-name,并执行 mount -a 以使之生效。需要 bind mount 而不能符号链接,是由于 vsftpd worker 会 chroot,访问的目录不能超出根目录(/srv/ftp)。
虚拟机
我们使用 LXC Linux Container 实现服务的隔离。比如从上游源定期同步、生成 status 页面等操作,与 HTTP、rsync、FTP 等核心服务并没有直接关系,如果把它们放进虚拟机,就可以避免出了 bug 的脚本占用过多的资源影响核心服务,或者操作失误导致服务中断或数据损坏。
mirrors 中建议包括以下几个虚拟机(建议使用 Debian wheezy stable):
- ftp-push,给上游源开的可写 FTP 同步方式。
- rsync-push,给上游源开的可写 rsync 同步方式。
- lab,相对开放的实验虚拟机,初入 mirrors 的童鞋们可以在这里试手。
- web,生成 status 页面、维护 web 页面(将来 web 页面可能有除 status 页面外的更多定时生成内容)
- sync,rsync、ftpsync、git 同步脚本在这里运行。
- npm,Nodejs 源(需要 couchdb 数据库)和同步脚本。
- rubygems,rubygems 同步脚本。
- pypi,pypi 同步脚本。
- sf,即将为 SourceForge Mirror 提供的虚拟机。
LXC 操作方法和配置文件,可以参考以前的脚本:
http://gitlab.lug.ustc.edu.cn/mirrors/mirrors-main/tree/master
网络隔离
需要注意的是,上述脚本里虚拟机没有隔离网络命名空间。我们希望 LXC 使用隔离的网络命名空间,使用 SNAT 发起出站连接,使用 DNAT 做入站端口映射,不要让 LXC 里的网络访问与主站的核心服务有冲突的可能。
建议的各虚拟机网络配置:
- ftp-push:DNAT inbound only
- rsync-push:DNAT inbound only
- lab:DNAT inbound(如果不再用 Xen 虚拟化的话,也可以绑定 202.38.95.106),SNAT outbound
- web:不需要网络访问
- sync:SNAT outbound only
- npm:nginx 反向代理,SNAT outbound
- rubygems:SNAT outbound only
- pypi:SNAT outbound only
- sf:绑定固定 IP 202.141.176.111
从上游源定期同步
由于历史原因,mirrors 现有两套同步脚本:
- 成熟的 mirrors 主站同步脚本,与清华 tuna 的脚本比较类似,包括 rsync、ftp 同步方式:http://gitlab.lug.ustc.edu.cn/mirrors/mirrors-scripts/tree/master
- 不成熟的 mirrors-lab 同步脚本,包括 rsync、ftp、git 同步方式:http://gitlab.lug.ustc.edu.cn/mirrors/mirrors-lab-scripts/tree/master
建议在成熟的 mirrors 主站脚本基础上做修改,将原 mirrors-lab 的镜像加入进去,确保所有镜像都能正常同步。
要阅读这套脚本,crontab 是入口。从这里可以看到定时调用 ftpsync 和 ustcsync。(小作业:ftpsync 和 ustcsync 有什么区别?)可以看到,2小时、4小时、6小时、24小时为同步周期的都有。注意 crontab 中的分钟数,这些数字是随机的,以尽量分散镜像同步对磁盘和网络带来的压力。
建议在 sync 虚拟机里建立一个 mirror 用户,使用这个 crontab 来进行同步。
crontab 和 bin 目录里有些东西应该移到 web 虚拟机里:
- 生成首页的 genindex.py
- 生成 awstats 静态页面的脚本
- 生成同步状态(status 页面)的脚本
bin 目录里还有查看服务器状态的一些小工具。
服务器监控
一个稳定的服务需要维护者知晓其状态。这个状态分两方面,一是各种镜像的同步状态,二是服务器自身的运行状态。
status 页面
镜像同步状态,就是大家熟知的 status 页面了。mirrors 的 status 页面是每5分钟定时查询同步日志,以获取同步状态、镜像大小、最后同步时间等。目前的脚本是 stephen 所写:
http://gitlab.lug.ustc.edu.cn/mirrors/mirrors-scripts/blob/master/bin/get-mirror-status.py
正如大家注意到的,我们需要更多有关镜像的信息,例如
- 上游源类型及 URL
- 最近一次同步的变化量、用时和平均速度
- 预计下次同步时间
这些信息最好能以结构化格式(如 JSON)输出,前端页面再去 parse,这样方便其他程序处理。
我们还需要统计每个镜像的上游源质量,因此有必要在同步结束后把统计信息存入数据库,不然翻 log 太麻烦了,而且过期的 log 还会被删除。
collectd
collectd 是服务器状态监控软件,可以监控 CPU、网络、磁盘、内存等多项指标,存入 rrd 数据库,通过 collectd-web 在网页上浏览。它的结构请自己 Google。由于现在 mirrors 已经挂了,很抱歉不能跟大家分享 collectd 的截图。
下面是 mirrors 上 collectd 的配置文件:
http://gitlab.lug.ustc.edu.cn/mirrors/mirrors-system-conf/tree/master/collectd
一定记得恢复原有日志分区里的 collectd 数据,最好能跟新的拼接起来,这是研究 mirrors 一年来运行状态的宝贵资料。
外部监控
自己监控自己并不是十分可靠,因此 mirrors 还使用了 ganglia 和自编的短信报警。
ganglia 运行在 lug.ustc.edu.cn 服务器上,它监控着 USTC LUG 几乎所有服务器的运行状态。http://status.lug.ustc.edu.cn/
当一台机器挂机的时候,我首先看的就是 status.lug.ustc.edu.cn,看看 CPU、内存、磁盘、网络有什么异常,很容易定位到出事的时间点,猜测大致的原因,再去查 syslog、dmesg 或 nginx 的 access log、error log,就比较有针对性了。
针对 HTTP 服务的基本可用性,我们还有简单的监控脚本,每分钟检测一次,出现问题就发报警短信。
- 服务器状态列表
- 故障日志
当然,目前 mirrors 的报警机制还很缺乏,比如硬盘满了、OOM Kill、nf_conntrack 表满了,我们运维人员都不能在第一时间得知。希望尝试使用 nagios 等报警软件,让 mirrors 的运维更自动化一些。
结语
这篇文章希望让大家了解到,搭建开源软件镜像从概念上并不是一件很复杂的事,主要就是 HTTP、rsync、FTP 服务和自动从上游源同步两部分。但维护好一个有近百个源、数据量超过 10TB、每天近千万次 HTTP 访问、每天流量逾 4TB 的开源软件镜像,还是需要一些更复杂的架构和工具的。就目前而言,我们对 mirrors 的状态监控和日志分析还很不充分,整个系统似乎运行在黑盒子里;mirrors 近期的稳定性也很不令人满意。
现在 mirrors 硬盘坏了,再去机房看看吧。如果不能尽快找出 RAID 卡或者接线方面的问题,就当成那两块硬盘不存在吧,从现在的 1T 日志分区里抠出一部分用于 LXC 虚拟机的根分区(日志可以转移到磁盘阵列)。总之,要尽快恢复服务。
重建后的 mirrors 用什么架构我已经鞭长莫及,也没有时间去折腾了,希望这篇文章和 GitLab 上的代码能给大家一些帮助。