上線第一年,Instagram 的技術棧都有哪些東西?

fzr發表於2015-11-16

// 伯樂線上編注:英文原文寫於 2012 年,正好 Instagram 上線一年。

每次在活動中和其他工程師聊天時,被問的最多問題就是“你們的技術棧中都有什麼?”。我們認為,從一個高層次來把驅動 Instagram 的東西講清楚,會很有意思;也許在將來會有這其中某些系統的詳細描述。

本文會講述我們系統上線一年多點時間中的演變,其中部分系統我們已經重做了。大家也可從本文一窺一個僅有小型工程團隊的創業公司,在一年多點時間內是如何擴充套件到 1400 萬使用者。我們選擇某項系統的核心準則是:

  • 越簡單越好
  • 不要重複造輪子
  • 儘量用成熟和穩定的技術

我們將自頂向下的進行敘述:

作業系統/託管

我們在 Amazon EC2 上執行 Ubuntu Linux 11.04(“Natty Narwhal”)。我們發現,在高流量下 EC2 安裝 Ubuntu 之前的版本,會有各種不可預知的凍結髮作,但 Natty 卻很穩定。我們只有 3 名工程師,我們的需求仍在不斷髮展,所以自託管不是一個選擇,我們已經深入探索過了。考慮到無與倫比的增長,不過我們可以在未來重新考慮自託管。

負載均衡

每一個對 Instagram 伺服器的請求都會通過負載均衡機器。過去我們是執行 2 臺 Nginx 機器,然後在機器之間進行輪循DNS。這個方法的缺點是,當其中一個機器需要被替換的時候,需要花時間進行DNS更新。最近,我們改用亞馬遜的彈性負載平衡器,3個Nginx例項在後臺可供換入換出(當健康檢查結果不佳時,會自動跳出機器環)。我們也會在 ELB 級終止SSL,從而減少Nginx上的CPU負載。我們使用亞馬遜的 route53 來做 DNS,他們最近在 AWS 控制檯增加了一個很好的GUI工具。

應用伺服器

接下來是處理請求的應用程式伺服器。我們在亞馬遜高速CPU超大型機器上執行Django,隨著使用者的增長,我們使用的機器數目已經從只有幾臺到超過25臺了(幸運的是,這一部分很容易橫向擴充套件,因為它們都是無狀態的)。我們已經發現,我們實際的工作負載是受CPU限制,而不是受記憶體限制的,所以超大例項型別的高速處理器提供了正確的記憶體和處理器的平衡方式。​

我們使用 http://gunicorn.org / 作為我們的WSGI應用伺服器;以前使用的是Apache和mod_wsgi,但是發現Gunicorn更易於配置,並且不那麼消耗CPU。為了能立刻在許多機器上立刻執行命令(比如部署程式碼),我們使用Fabric,它最近增加了一個很有用的並行模式以至於部署任務只需要幾秒鐘。​

資料儲存

我們的大部分資料(使用者,照片後設資料,標籤,等等)都儲存在PostgreSQL;以前寫過關於我們如何在不同的Postgres例項間分片。我們的主要分片叢集包括12個四核超大記憶體例項(以及放在不同區域的十二個副本。)

我們發現,亞馬遜的網路硬碟系統(EBS)每秒磁碟尋道數遠不夠我們使用,所以把所有工作集放在記憶體中是非常必要的。為了得到合理的IO效能,我們使用mdadm在軟體RAID中建立了EBS驅動器。

還有一個快速提示,我們發現 vmtouch 對管理記憶體資料是一個非常好的工具,尤其是當不能從一臺機器到另一個沒有活躍備份的機器的時候。這裡有一份指令碼,我們用它來解析一臺機器上執行的一個vmtouch輸出,並列印出相應的vmtouch命令,該命令​用於執行在另一個匹配當前記憶體狀態的系統。​

所有PostgreSQL例項都執行在一個基於流式副本的啟動程式中,我們使用EBS快照備份系統。我們使用XFS檔案系統,可以在快照時凍結和解凍RAID陣列,以保證快照一致(最初的靈感來自於EC2快照一致)。為了啟動副本流,我們的員工最喜歡的工具是repmgr

要從應用程式伺服器連線到資料庫,我們之前將PostgreSQL連線放入 Pgbouncer 池對效能有巨大影響。我們發現Christophe Pettus的部落格有很多Django資源,以及PostgreSQL和Pgbouncer的小建議。

照片直接儲存在亞馬遜S3,目前儲存了幾TB的照片資料。我們使用Amazon CloudFront 作為 CDN,這有助於減少來自世界各地使用者的影象載入時間(比如在日本,日本是第二個最歡迎我們的國家)。

我們還廣泛使用了Redis,它支撐了主要的資料流、活動流、會話系統(這是我們的Django會話後端),以及其他相關係統。所有Redis的資料需要儲存在記憶體中,所以我們也為Redis執行幾個超大記憶體例項,偶爾對於給定的子系統在幾個Redis之間分片。我們在一個主副本程式中執行Redis,並讓副本不斷儲存資料庫到磁碟上,並使用EBS快照備份這些資料庫轉儲(我們發現在主副本上轉儲DB太繁重)。因為Redis允許寫入副本,它使一個新的Redis機器的線上切換變得很容易,而不需要任何的停機時間。

對於地理搜尋API,我們已經使用PostgreSQL幾個月了,但一旦我們的媒體條目被分片,我們轉而使用Apache Solr。它有一個簡單的JSON介面,就我們的應用程式而言,這只是另一個能使用的API。

最後,與其他任何現代Web服務一樣,我們使用Memcached快取,目前有6個Memcached例項,使用 pylibmc 和 libmemcached 連線。亞馬遜最近推出一個彈性快取服務,但它並不比執行我們自己的例項更便宜,所以我們還沒有急著去切換到新服務。

任務佇列 & 推送通知

當使用者決定將 Instagram 上的照片分享到 Twitter 或 Facebook,或者當我們需要通知實時訂閱者一個新照片的到來時,我們把任務寫入Gearman,Gearman是一個佇列系統,起初是由Danga開發的。在任務佇列上非同步完成意味著媒體上傳很快就能完成,而’繁重負載’可以在後臺執行。在任意時間,都有大約200臺機器(都是用Python寫的)消費任務佇列的訊息,在分享服務之間切換。我們還在Gearman中送出響應,所以釋出照片對於一個擁有大量粉絲的使用者和一個新使用者來講,都能快速響應。​

為了做推送通知,我們發現最符合成本效益的解決方案:https://github.com/samuraisam/pyapns,一個開源的Twisted服務,它已經為我們處理了超過十億的推送通知,並且堅如磐石。​

監控

對於100個以上的例項,始終清楚模組之間執行情況非常重要。我們用 Munin 來將各個系統連線成圖,也提醒我們是否有異常發生。我們基於 Python-Munin,寫了很多常用Munin外掛,來連線不是系統級的圖(例如,每分鐘使用者註冊數,每秒的上傳照片數,等等)。我們使用 Pingdom 作為服務的外部監督,使用 PagerDuty 處理通知和突發事件。​

對於Python的錯誤報告,我們使用 Sentry,,一個非常讚的開源Django程式,由Disqus的小夥伴開發。在任何時間,我們可以登入並實時看到我們的系統發生了什麼錯誤。​

相關文章