原文:http://instagram-engineering.tumblr.com/post/13649370142/what-powers-instagram-hundreds-of-instances-dozens-of
當我們與其他工程師偶遇和交流的時候,有一個問題經常被問及,“你們的技術架構(technology stack)是怎么樣的”?我們覺得從較高的層次來描述Instagram的所有構成系統是一件有趣的事情;未來你可能期待更深入的描述這些系統。這就是我們的系統,僅僅1年時間,並且我們活了下來,其中有一部分我們一直在修改。一個小型團隊的初創公司,可以在一年多一點時間發展到1400多萬用戶規模。我們選擇一種技術的核心原則是:
盡量保持簡單
不重復發明輪子
盡量用被驗證的可靠的技術
我們將自頂向下進行介紹:
操作系統 / 主機
我們在亞馬遜的 EC2上跑Ubuntu Linux 11.04 (“Natty Narwhal”)。我們發現之前的版本在EC2上高流量的時候都會出現各種不可預測的問題( freezing episodes)。我們只有3名工程師,我們的需求依然在不斷的變化中,因此自托管主機不是我們的選擇,也許未來當用戶量空前增長的時候,我們會考慮。
負載均衡
每一個對Instagram 服務器的訪問都會通過負載均衡服務器;我們使用2台nginx機器做DNS輪詢。這種方案的缺點是當其中一台退役時,需要花時間更新DNS。最近,我們轉而使用亞馬遜的Elastic 負載均衡器,使用3個NGINX 實例可以實現調入調出(and are automatically taken out of rotation if they fail a health check);我們同時在 ELB 層停掉了 SSL , 以緩解nginx的 CPU 壓力。我們使用亞馬遜的Route53服務 作為DNS服務,他們最近在AWS控制台上增加了一套很好的GUI工具。
應用服務器
接下來是應用服務器用來處理我們的請求。我們在亞馬遜的High-CPU Extra-Large機器上運行了Django ,隨着用戶的增長,我們已經在上面跑了25個Django實例了(幸運地,因為是無狀態的,所以非常便於水平擴展)。我們發現我們的個別工作負載是屬於計算密集型而非IO密集型,因此High-CPU Extra-Large類型的實例剛好提供了合適的比重(CPU和內存)。
我們曾經使用 http://gunicorn.org/ 作為我們的WSGI服務器;我們使用mod_wsgi 和Apache,但是發現Gunicorn 更容易配置,且對CPU的要求更低。為了 一次在多個實例上執行命令(像部署代碼),我們使用Fabric,Fabric最近增加了並行模式,因此部署只需要花費幾秒鍾。
數據存儲
我們大部分數據(用戶信息,照片的元數據、標簽等)存儲在PostgreSQL中;我們 之前已經說了關於如何基於不同的Postgres 實例進行切分的。我們的主要分片集群包含12個四倍超大內存雲主機(且12個副本在不同的區域);
我們發現亞馬遜的網絡磁盤系統(EBS)每秒的尋道能力不夠,因此,將我們所有工作放到內存中就變得尤為重要。為了獲得合理的性能,創建了軟 RAID 以提升 IO 能力,使用的 Mdadm 工具進行 RAID 管理;
順便提一下,我們發現vmtouch用來管理內存數據是個極好的工具,尤其是在故障轉移時,從一台機器到另一台機器,甚至沒有活動的內存概要文件的情況。這里是腳本,用來解析運行於一台機器上的vmtouch 輸出並打印出相應vmtouch命令,在另一台機器上執行,用於匹配他當前的內存狀態;
我們所有的PostgreSQL實例都是運行於主-備模式,基於流復制,並且我們使用EBS快照經常備份我們的系統。為了保證我們的快照的一致性(原始靈感來源於ec2-consistent-snapshot)我們使用XFS作為我們的文件系統,通過XFS,當進行快照時,我們可以凍結&解凍RAID陣列。為了進行流復制,我們最愛的工具是repmgr 。
對於從我們的應用服務器連接到數據,我們很早就使用了Pgbouncer做 連接池,此舉對性能有巨大的影響。我們發現Christophe Pettus的博客 有大量的關於Django、PostgreSQL 和Pgbouncer 秘訣的資源。
照片直接存儲在亞馬遜的S3,當前已經存儲了幾T的照片數據。我們使用亞馬遜的CloudFront 作為我們的CDN,這加快了全世界用戶的照片加載時間(像日本,我們第二最受歡迎的國家)
我們也廣泛的使用了Redis ; 我們的main feed、activity feed、sessions系統(這里是我們的Django session backend),和其他 相關系統 都使用了Redis。因為所有的Redis數據都需要放在內存中,因此我們最后使用了幾個四倍超大內存雲主機用於跑Redis。我們的Redis也是運行於主-備模式,並且經常將DB保存到磁盤,最后使用EBS快照備份這些數據(我們發現在主機上進行導出太費勁了)。由於Redis 允許寫入備份,使得在線故障轉移非常方便,轉移到一台新的Redis 機器,而不需要停機。
為了我們的geo-search API,我們一直使用PostgreSQL了很多個月,不過后來遷移到了Apache Solr.他有一套簡單的JSON接口,這樣我們的應用程序相關的,只是另一套API而已。
最后,和任何現代Web服務一樣,我們使用了Memcached 做緩存,並且當前已經使用了6個Memcached 實例,我們使用pylibmc & libmemcached進行連接。亞馬遜最近啟用了一個靈活的緩存服務,但是它並不比運行我們自己的實例便宜,因此我們並沒有切換上去;
任務隊列&推送通知
當一個用戶決定分享一張Instagram 的照片到Twitter 或Facebook,或者是當我們需要通知一個 實時訂閱者有一張新的照片貼出,我們將這個任務推到 Gearman,一個任務隊列系統能夠寫於Danga。這樣做的任務隊列異步通過意味着媒體上傳可以盡快完成,而“重擔”可以在后台運行。我們大概有200個工作實例(都用Python寫的)在給定的時間內對隊列中的任務進行消費,並分發給不同的服務。我們的feed feed fan-out也使用了Gearman,這樣posting就會響應新用戶,因為他有很多followers。
對於消息推送,我們找到的最划算的方案是https://github.com/samuraisam/pyapns,一個開源的Twisted 服務,已經為我們處理了超過10億條通知,並且絕對可靠。
監視
對於100多個實例,監控變得非常重要。我們使用Munin進行圖形化度量。我們基於 Python-Munin,也寫了很多Munin 插件。用於圖形化度量非系統級的東西(例如,每秒的簽入人數,每條照片發布數等)我們使用Pingdom作為外部監控服務,PagerDuty 用於事件通知。
Python錯誤報告,我們使用Sentry,一個Disqus的工程師寫的令人敬畏的開源的Django app。在任何時間,我們可以開始工作,並查看我們的系統發生了什么錯誤,實時的。
是你嗎?
如果你對我們系統的這篇描述感興趣,或者你正躍躍欲試的想告訴我們關於系統你有什么不同的見解,我們都靜候佳音。We’re looking for a DevOps person to join us and help us tame our EC2 instance herd.