Instagram五位傳奇工程師背后的技術(shù)揭秘

2013-04-03 09:44:08來源:CSDN作者:

Instagram是一家基于iOS和Android的社交圖片照片分享應(yīng)用開發(fā)商。憑借著獨特的運營理念,自2010年3月成立以來,短短一年的時間就吸引了1400萬用戶。而后隨著手機相機改動、圖像處理升級、與Facebook等社交靈活交互、

Instagram是一家基于iOS和Android的社交圖片照片分享應(yīng)用開發(fā)商。憑借著獨特的運營理念,自2010年3月成立以來,短短一年的時間就吸引了1400萬用戶。而后隨著手機相機改動、圖像處理升級、與Facebook等社交靈活交互、支持Android等服務(wù)不斷升級,用戶量迅速沖擊3000萬,于2012年9月被Facebook以7.15億美元收購。而截止到今年2月底,其活躍用戶成功突破1億。

\
Instagram兩位創(chuàng)始人 

與高速增長相背離的是,從成立之初僅有凱文·希斯特羅姆(Kevin Systrom)和邁克·克里格(Mike Krieger)兩位創(chuàng)始人,到2011年獲得A輪風投700萬美元的4位員工,再到被收購時的13人團隊,Instagram人員組織一直極為精簡。

如此小規(guī)模的團隊居然可以如此自如地應(yīng)對飛速增長的用戶數(shù)并提供創(chuàng)新服務(wù),這不能不說是硅谷的又一個財富傳奇。以至于Instagram技術(shù)團隊撰寫的《Instagram:數(shù)百的實例 大量的技術(shù)》一經(jīng)發(fā)布,就獲得了創(chuàng)業(yè)企業(yè)CTO們的熱烈回應(yīng)。彼時,Instagram的團隊還在尋找一個“可以馴服EC2 實例群的DevOps”。

沒有想到,收購如此來勢洶洶。2012年4月10日,F(xiàn)acebook宣布收購Instagram。兩天之后,Instagram的聯(lián)合創(chuàng)始人Mike Krieger公開發(fā)表《如何成為十億美元公司》演講,第一次向外界全面地展現(xiàn)了Instagram創(chuàng)業(yè)歷程以及其中不得不說的技術(shù)“秘密.”。本文為演講PPT全文翻譯,有助于創(chuàng)新技術(shù)團隊更好認識和了解Instagram13人團隊創(chuàng)造奇跡所依賴的技術(shù):

Instagram技術(shù)團隊:

  • 2010年: 2位工程師
  • 2011年: 3位工程師
  • 2012年: 5位工程師

Instagram核心原則:

  • 1 simplicity(簡潔主義)
  • 2 optimize for minimal operational burden(為盡量減少運維負擔而優(yōu)化)
  • 3 instrument everything(監(jiān)控一切)

一、初創(chuàng)階段:

兩名沒有任何后端的實戰(zhàn)經(jīng)驗的創(chuàng)始人;

通過托管在洛杉磯某處的一臺機器(甚至性能都沒有MacBook Pro強);

存儲采用CouchDB(Apache CouchDB 是一個面向文檔的數(shù)據(jù)庫管理系統(tǒng));

產(chǎn)品上線第一天有25000注冊用戶。

二、上線階段:

因為忘記favicon.ico圖標文件,在Django上引起大量404錯誤。

這是第一個經(jīng)驗教訓。后面還有:

ulimit -n ,設(shè)置Linux內(nèi)核可以同時打開的文件描述符的最大值,例如size為4092。

memcached -t 4,設(shè)置用于處理請求的線程數(shù)。

prefork/postfork 線程的預加載還是后加載。

顯然,絕大多數(shù)系統(tǒng)擴展問題繁瑣而困難。而在不斷出現(xiàn)問題并解決問題的過程中,Instagram決定遷往AWS的EC2。

三、遷移階段:

“let’s move to EC2”就像是“對100碼速度行駛的汽車更換所有部件”。

\

具體分析:

1.  數(shù)據(jù)庫擴展

早期:django ORM+postgresql(PostGIS)

因為PostGIS而選擇了postgresql,PostGIS在對象關(guān)系型數(shù)據(jù)庫PostgreSQL上增加了存儲管理空間數(shù)據(jù)的能力,相當于Oracle的spatial部分,數(shù)據(jù)庫可部署在獨立服務(wù)器上。

隨著照片數(shù)量的爆發(fā)式增長,最大內(nèi)存為68G的EC2顯然無法支持。

改變:進行vertical partitioning(垂直分區(qū)),并通過django db routers使垂直分區(qū)更加容易。

如:照片則映射到photodb

def db_for_read(self, model):<br>&nbsp; &nbsp; if app_label == 'photos':<br>&nbsp; &nbsp; &nbsp; &nbsp; return 'photodb'

幾個月以后,photodb>60G的時候,采用horizontal partitioning(水平分區(qū),用“分片”sharding實現(xiàn))

但sharding也帶來諸多問題:

1). 數(shù)據(jù)檢索(多數(shù)情況下,很難知道用戶的主訪問模式)

2). 當有分片變得太大的時候怎么辦?

\

可以采用,基于范圍的分片策略(如MongoDB一樣)

\

3). 性能下降,特別是由EC2實例磁盤IO導致,解決方法是:預先切分(pre-split),即預先切分上千個邏輯切片,將它們映射到較少的物理分區(qū)節(jié)點中去。

\
 

\

2.  選擇合適工具

進行緩存/反規(guī)范化數(shù)據(jù)設(shè)計

用戶上傳圖片時:

1). 用戶上傳帶有標題信息和地理位置信息(可選)的照片;

2). 同步寫到這個用戶對應(yīng)的數(shù)據(jù)庫(分片)中;

3). 進行隊列化處理

a 如果帶有地理位置信息,通過異步的POST請求,將這個圖片的信息送到Solr(Instagram 用于geo-search API的全文檢索服務(wù)器)。

b 跟隨者的信息分發(fā)(follower delivery),即告訴我的follower ,我發(fā)布了新的照片。如何來實現(xiàn)的呢?每個用戶都有一個follower 列表,新照片上傳時會把照片ID發(fā)送給列表中的每一個用戶,用Redis 來處理這一業(yè)務(wù)簡直太棒了,快速插入,快速子集化。

c 當需要生成feed的時候,我們通過ID+#的格式,直接在memcached中查找信息

Redis適合什么樣的場景?

1).數(shù)據(jù)結(jié)構(gòu)相對有限;

2).對頻繁GET的地方,對復雜對象進行緩存;

不要將自己綁定在非得以內(nèi)存數(shù)據(jù)庫為主要存儲策略的方案上。

關(guān)于Follow圖譜

第一版:簡單的數(shù)據(jù)庫表格(source_ id, target_id, status) 

需要來回答如下查詢:我關(guān)注誰?誰關(guān)注我?我是否關(guān)注某人?某人是否關(guān)注我? 

當數(shù)據(jù)庫的壓力變大時,Instagram開始在Redis中并發(fā)存儲關(guān)注圖譜,但這也帶來了內(nèi)容一致性(consistency)的問題。而不一致性一度會帶來緩存失效問題。

PostGIS結(jié)合輕量的memcached緩存,可以支撐上萬的請求量。

需要注意點:

1). 核心數(shù)據(jù)存儲部分有一個萬能的組件支撐,就像:Redis;

2).千萬不要試想用兩種工具去做同一個工作;

3.  保持敏捷

1). 廣泛的單元測試和功能測試

2). 堅持DRY(Don’t Repeat Yourself)原則

3). 使用通知/信號機制實現(xiàn)解耦

4). 我們大部分工作使用Python來完成,只有逼不得已的時候,才會用C

5). 頻繁的代碼復查,盡量保持“智慧共享”。

6). 廣泛的系統(tǒng)監(jiān)控

4.  往Android平臺擴展

12小時增加100萬新用戶的關(guān)鍵:

1). 偉大的工具可以使讀取更具擴展性,例如:redis: slaveof <host> <port>(SLAVEOF 命令用于在 Redis 運行時動態(tài)地修改復制(replication)功能的行為);

2). 更短的迭代周期;

3). 不要重復發(fā)明輪子,例如想開發(fā)一個系統(tǒng)監(jiān)控的守護進程,完全沒有必要,HAProxy完全能勝任這一工作;

4). 找強大的技術(shù)顧問;

5). 技術(shù)團隊保持開放的氛圍并積極回饋開源世界;

6). 關(guān)注優(yōu)化,想辦法讓系統(tǒng)速度快上一倍。

7). 保持敏捷;

8).使用最少部件,最干凈的解決方案;

9). 不要過度的優(yōu)化,除非你提前知道自己的系統(tǒng)將如何擴展

PPT很好的保持了Instagram“simplicity”的哲學,即使提到技術(shù),也精簡到了極致。為了讓更多朋友明了,特別從其工程師博客上選擇更多細節(jié)來補足,而這里,是這5位工程師實踐經(jīng)驗的總結(jié),其中不乏那些極為實用的開源工具。

四、其他細節(jié)技術(shù)

1. 操作系統(tǒng)/主機

在Amazon EC2上跑Ubuntu Linux 11.04 (“Natty Narwhal”),這個版本經(jīng)過驗證在 EC2 上夠穩(wěn)定。但之前的版本在EC2上高流量的時候都會出現(xiàn)各種不可預測的問題。

2. 負載均衡

每一個對Instagram 服務(wù)器的訪問都會通過負載均衡服務(wù)器;我們使用2臺Nginx機器做DNS輪詢。這種方案的缺點是當其中一臺退役時,需要花時間更新DNS。最近,轉(zhuǎn)而使用Amazon的ELB(Elastic Load Balancer)負載均衡器,使用3個Nginx 實例可以實現(xiàn)調(diào)入調(diào)出(而當某個Nginx實例通不過故障檢測,系統(tǒng)會自動將其從循環(huán)中抽離);同時在 ELB 層停掉了 SSL , 以緩解nginx的 CPU 壓力。使用Amazon的Route53服務(wù)作為DNS服務(wù),這是AWS控制臺上增加的一套很好的GUI工具。

3. 應(yīng)用服務(wù)器

在Amazon的High-CPU Extra-Large機器上運行了Django ,隨著用戶的增長,已經(jīng)在上面跑了25個Django實例了(幸運地,因為是無狀態(tài)的,所以非常便于橫向擴展)。但發(fā)現(xiàn)個別工作負載是屬于計算密集型而非IO密集型,因此High-CPU Extra-Large類型的實例剛好提供了合適的比重(CPU和內(nèi)存)。

為此,使用 Gunicorn 作為 WSGI 服務(wù)器。過去曾用過 Apache 下的 mod_wsgi 模塊,不過發(fā)現(xiàn) Gunicorn 更容易配置并且節(jié)省 CPU 資源。使用 Fabric 加速部署。Fabric最近增加了并行模式,因此部署只需要花費幾秒鐘。

4. 數(shù)據(jù)存儲

大部分數(shù)據(jù)(用戶信息,照片的元數(shù)據(jù)、標簽等)存儲在PostgreSQL中;并基于不同的Postgres 實例進行切分的。主要分片集群包含12個四倍超大內(nèi)存云主機(且12個副本在不同的區(qū)域);

而亞馬遜的網(wǎng)絡(luò)磁盤系統(tǒng)(EBS)每秒的尋道能力不夠,因此,將所有工作放到內(nèi)存中就變得尤為重要。為了獲得合理的性能,創(chuàng)建了軟 RAID 以提升 IO 能力,使用的 Mdadm 工具進行 RAID 管理;

這里,vmtouch用來管理內(nèi)存數(shù)據(jù)是個極好的工具,尤其是在故障轉(zhuǎn)移時,從一臺機器到另一臺機器,甚至沒有活動的內(nèi)存概要文件的情況。這里是腳本,用來解析運行于一臺機器上的vmtouch 輸出并打印出相應(yīng)vmtouch命令,在另一臺機器上執(zhí)行,用于匹配他當前的內(nèi)存狀態(tài);

所有的PostgreSQL實例都是運行于主-備模式(Master-Replica),基于流復制,并且使用EBS快照經(jīng)常備份我們的系統(tǒng)。為了保證快照的一致性(原始靈感來源于ec2-consistent-snapshot)使用XFS作為我們的文件系統(tǒng),通過XFS,當進行快照時,可以凍結(jié)&解凍RAID陣列。為了進行流復制,我們最愛的工具是repmgr 。

對于從應(yīng)用服務(wù)器連接到數(shù)據(jù),我們很早就使用了Pgbouncer做連接池,此舉對性能有巨大的影響。我們發(fā)現(xiàn)Christophe Pettus的博客 有大量的關(guān)于Django、PostgreSQL 和Pgbouncer 秘訣的資源。

照片直接存儲在亞馬遜的S3,當前已經(jīng)存儲了幾T的照片數(shù)據(jù)。使用亞馬遜的CloudFront作為我們的CDN,這加快了全世界用戶的照片加載時間。

為了geo-search API,我們一直使用PostgreSQL了很多個月,不過后來遷移到了Apache Solr.他有一套簡單的JSON接口,這樣我們的應(yīng)用程序相關(guān)的,只是另一套API而已。

最后,和任何現(xiàn)代Web服務(wù)一樣,使用了Memcached 做緩存,并且當前已經(jīng)使用了6個Memcached 實例,我們使用pylibmc & libmemcached進行連接。Amzon最近啟用了一個靈活的緩存服務(wù)(Elastic Cache service),但是它并不比運行我們自己的實例便宜,因此我們并沒有切換上去;

5. 任務(wù)隊列&推送通知

當一個用戶決定分享一張Instagram 的照片到Twitter 或Facebook,或者是當我們需要通知一個 實時訂閱者有一張新的照片貼出,我們將這個任務(wù)推到 Gearman,一個任務(wù)隊列系統(tǒng)能夠?qū)懹贒anga。這樣做的任務(wù)隊列異步通過意味著媒體上傳可以盡快完成,而“重擔”可以在后臺運行。我們大概有200個工作實例(都用Python寫的)在給定的時間內(nèi)對隊列中的任務(wù)進行消費,并分發(fā)給不同的服務(wù)。我們的feed feed fan-out也使用了Gearman,這樣posting就會響應(yīng)新用戶,因為他有很多followers。

對于消息推送,找到的最劃算的方案是,一個開源的Twisted 服務(wù),已經(jīng)為我們處理了超過10億條通知,并且絕對可靠。

6. 監(jiān)控

基于 Python-Munin,寫了很多Munin 插件,使用Munin進行圖形化度量。用于圖形化度量非系統(tǒng)級的東西(例如,每秒的簽入人數(shù),每條照片發(fā)布數(shù)等),使用Pingdom作為外部監(jiān)控服務(wù),PagerDuty 用于事件通知。

Python錯誤報告,是使用的Sentry,一個Disqus的工程師寫的令人敬畏的開源的Django app。在任何時間,都可以實時的查看系統(tǒng)發(fā)生了什么錯誤。

文章到這里,并沒有結(jié)束。對今天已經(jīng)跨越“億”線的Instagram而言,“為了最小的運營負擔而優(yōu)化程序,利用一切能用到的(開源)工具與云平臺,極簡的技術(shù)主張”未變,不信,看看最近的新聞,《Instagram的負載均衡武器:Eureka填補了Amazon Web Services的大缺口》還在續(xù)寫屬于他自己的技術(shù)傳奇。(@紅心李的分析對本文也有貢獻,審校/仲浩)

鏈接:Mike Krieger《如何成為十億美元公司》演講PPT

關(guān)鍵詞:Instagram

贊助商鏈接: