Instagram团队之前发表过一篇文章: What Powers Instagram:Hundreds of Instances,Dozens of Technologies。这篇文章中提到的技术架构堪称经典,很适合初创项目的快速启动。
这个小团队使用了很多不同的技术和策略,保证他们能轻松的应付快速增长带来的压力。他们混合使用SQL、NoSQL,一堆开源项目,和云服务;云服务他们选择了Amazon,他们认为Amazon要比他们自己部署IDC更有优势;用异步队列来串联组件;系统架构在众多的对外API和内部Services之上;数据存储在内存和云端;大部分代码是动态语言;等等等等。非常时髦的一个架构,基于这个架构之上他们得以快速前进,并且保持精简。
那篇文章非常值得一读,有兴趣的同学可以直接看原文。这里我列出一些要点:
架构原则:
- 保持简单;
- 不重复造轮子;
- 尽量使用成熟稳定的技术
操作系统/主机
INS在Amazon EC2上跑Ubuntu Linux 11.04 (“Natty Narwhal”),这个版本经过验证在 EC2 上够稳定。之前的版本在EC2上高流量的时候都会出现各种不可预测的问题( freezing episodes)。因为只有3名工程师,需求依然在不断的变化中,因此自托管主机不是一个好的选择。
负载均衡
每一个对Instagram 服务器的访问都会通过负载均衡服务器;使用2台nginx机器做DNS轮询。这种方案的缺点是当其中一台退役时,需要花时间更新DNS。最近,INS转而使用Amazon的ELB(Elastic Load Balancer)负载均衡器,使用3个NGINX 实例可以实现调入调出(and are automatically taken out of rotation if they fail a health check);同时在 ELB 层停掉了 SSL , 以缓解nginx的 CPU 压力。使用Amazon的Route53服务作为DNS服务,AWS控制台上的GUI工具很好用。
应用服务器
接下来是应用服务器用来处理请求。INS在Amazon的High-CPU Extra-Large机器上运行了Django ,随着用户的增长,INS已经在上面跑了25个Django实例了(幸运地,因为是无状态的,所以非常便于水平扩展)。但是,个别工作负载是属于计算密集型而非IO密集型,因此High-CPU Extra-Large类型的实例刚好提供了合适的比重(CPU和内存)。
使用 Gunicorn 作为 WSGI 服务器。过去曾用过 Apache 下的 mod_wsgi 模块,不过发现 Gunicorn 更容易配置并且节省 CPU 资源。使用 Fabric 加速部署。Fabric最近增加了并行模式,因此部署只需要花费几秒钟。
数据存储
INS大部分数据(用户信息,照片的元数据、标签等)存储在PostgreSQL中;INS基于不同的Postgres 实例进行了切分,其中主要分片集群包含12个四倍超大内存云主机(且12个副本在不同的区域);
亚马逊的网络磁盘系统(EBS)每秒的寻道能力不够,因此,将所有工作放到内存中就变得尤为重要。为了获得合理的性能,创建了软 RAID 以提升 IO 能力,使用的 Mdadm 工具进行 RAID 管理;
INS 使用 vmtouch用来管理内存数据,尤其是在故障转移时,从一台机器到另一台机器,甚至没有活动的内存概要文件的情况。这里是脚本,用来解析运行于一台机器上的vmtouch 输出并打印出相应vmtouch命令,在另一台机器上执行,用于匹配他当前的内存状态;
所有的PostgreSQL实例都是运行于主-备模式(Master-Replica),基于流复制,并且我们使用EBS快照经常备份我们的系统。为了保证快照的一致性(原始灵感来源于ec2-consistent-snapshot),INS使用XFS作为文件系统,通过XFS,当进行快照时,可以冻结&解冻RAID阵列。为了进行流复制,这其中使用的工具是repmgr。
对于从应用服务器连接到数据,INS很早就使用了Pgbouncer做连接池,此举对性能有巨大的影响。其中,Christophe Pettus的博客 有大量的关于Django、PostgreSQL 和Pgbouncer 秘诀的资源。
照片直接存储在亚马逊的S3,那时已经存储了几T的照片数据。INS使用亚马逊的CloudFront作为CDN,这加快了全世界用户的照片加载时间。
INS也广泛的使用了Redis; 其中 main feed、activity feed、sessions系统(这里是INS的Django session backend),和其他 相关系统 都使用了Redis。因为所有的Redis数据都需要放在内存中,因此INS最后使用了几个四倍超大内存云主机用于跑Redis。Redis也是运行于主-备模式(Master-Replica),并且经常将DB保存到磁盘,最后使用EBS快照备份这些数据(在主机上进行导出太费劲了)。由于Redis 允许写入备份,使得在线故障转移非常方便,转移到一台新的Redis 机器,而不需要停机。
为了geo-search API,INS一直使用PostgreSQL了很多个月,不过后来迁移到了Apache Solr.他有一套简单的JSON接口,这样应用程序相关的信息,只是另一套API而已。
最后,和任何现代Web服务一样,INS使用了Memcached 做缓存,那时已经使用了6个Memcached 实例。INS使用pylibmc & libmemcached进行连接。Amzon最近启用了一个灵活的缓存服务(Elastic Cache service),但是它并不便宜;
任务队列&推送通知
当一个用户决定分享一张Instagram 的照片到Twitter 或Facebook,或者是当我们需要通知一个 实时订阅者有一张新的照片贴出,我们将这个任务推到 Gearman,一个任务队列系统能够写于Danga。这样做的任务队列异步通过意味着媒体上传可以尽快完成,而“重担”可以在后台运行。INS大概有200个工作实例(都用Python写的)在给定的时间内对队列中的任务进行消费,并分发给不同的服务。INS的feed feed fan-out也使用了Gearman,这样posting就会响应新用户,因为他有很多followers。
对于消息推送,INS找到的最划算的方案是https://github.com/samuraisam/pyapns,一个开源的Twisted 服务,已经为INS处理了超过10亿条通知,并且绝对可靠。
监控
对于100多个实例,监控变得非常重要。INS使用Munin进行图形化度量。基于Python-Munin,INS也写了很多Munin 插件。用于图形化度量非系统级的东西(例如,每秒的签入人数,每条照片发布数等)INS使用Pingdom作为外部监控服务,PagerDuty 用于事件通知。
Python错误报告,INS使用Sentry,一个Disqus的工程师写的非常好的开源Django app。在任何时间,INS工程师都可以实时查看系统发生了什么错误。
信息概括
- 大量使用Amazon服务,工程师不用耗费时间自己维护服务器
- 100+个EC2实例用于各种用途
- Ubuntu Linux 11.04(“Natty Narwhal”),他们认为这个版本更加稳定
- Amazon的ELB(Elastic Load Balancer)路由请求,ELB后面起了3个Nginx实例
- ELB上关闭了SSL,因为它降低了CPU的使用率
- DNS使用Amazon的Route53
- 25+个Django应用服务运行在High-CPU Extra-Large类型的机器上
- CPU使用比内存使用更加容易达到边界值,所以使用High-CPU Extra-Large的实例来平衡内存和CPU
- Gunicorn是他们的WSGI服务器。Apache更难配置,更耗CPU
- Fabric被用来在所有机器上并行执行命令。部署花费更少的时间。
- PostgreSQL(存储用户,照片元数据,标签等)运行在12个Quadruple Extra-Large Memory实例上
- 12个PostgreSQL副本运行在不同的节点上
- PostgreSQL的Master-Replica使用 流复制模式,利用EBS的快照来频繁备份
- EBS部署在软件RAID上,使用 mdadm以获取适当的IO
- 将所有工作中的数据集存储在内存中,因为EBS每秒磁盘寻道次数有限
- Vmtouch(轻量级的文件系统缓存诊断工具)被用来管理内存数据,尤其是当 故障转移时目标机器没有足够空余的内存
- 用XFS做文件系统,保证数据快照时RAID阵列上数据的一致性
- Pgbouncer用作 PostgreSQL的连接池
- 有几个TB的照片存在Amazon S3上
- 用Amazon CloudFront做CDN
- Redis用来支撑feed,活动消息,session系统和 其它服务
- Redis运行在几台Quadruple Extra-Large Memory实例上,偶尔也会做下切分
- Redis也是Master-Replica,副本持久化到磁盘上,并由EBS通过快照备份(这么搞是因为他们发现直接在Master上做dump相当吃力)
- Geo-Search使用Solr,Solr提供的JSON接口也很简单易用
- 6个memcached实例做缓存,因为Amazon Elastic Cache服务并不便宜,mmc客户端使用pylibmc和libmemcached
- Gearman用做:向Twitter,Facebook等平台异步分享照片;新照片发布的通知;feed的反送
- 200个Python进程处理Gearman的任务队列
- Pyapns处理超过10亿条Apple的push通知,异常稳定
- Munin用做监控和系统度量工具,用 Python-Munin写了很多图表插件,如每分钟注册人数,每秒钟图片发表数等等
- Pingdom做内部服务的监控
- PagerDuty用来处理通知和事件
- Sentry用来做Python的错误报告