Instagram是一款免费照片分享移动应用,目前支持IOS和Android,并且还有一个简单的网页版。在1年多的时间里,仅靠3个工程师,Instagram发展到140万个用户,1.5亿张图片(几个TB),以10亿美元的天价被Facebook收购。不得不说,Instagram是业界的一个神话。今天我们就来看看到底是什么样的技术架构支撑着这个10亿美元的公司。

Instagram团队之前发表过一篇文章: What Powers Instagram:Hundreds of Instances,Dozens of Technologies。这篇文章中提到的技术架构堪称经典,很适合初创项目的快速启动。

这个小团队使用了很多不同的技术和策略,保证他们能轻松的应付快速增长带来的压力。他们混合使用SQL、NoSQL,一堆开源项目,和云服务;云服务他们选择了Amazon,他们认为Amazon要比他们自己部署IDC更有优势;用异步队列来串联组件;系统架构在众多的对外API和内部Services之上;数据存储在内存和云端;大部分代码是动态语言;等等等等。非常时髦的一个架构,基于这个架构之上他们得以快速前进,并且保持精简。

那篇文章非常值得一读,有兴趣的同学可以直接看原文。这里我列出一些要点:

架构原则:
  1. 保持简单;
  2. 不重复造轮子;
  3. 尽量使用成熟稳定的技术

操作系统/主机

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的错误报告