開始時,Picnik使用了一個開源項目,Mogilefs,用于文件存儲。我們的大部分服務器都有幾個空閑的驅動器插槽,我們在這些插槽上接入大容量的SATA驅動器,用于 Mogilefs文件存儲。大部分后臺服務都是CPU密集型的,所以與這些I/O密集型的存儲配合得相當好。這個策略工作得很好,但存儲需求超過CPU需求之后,就不行了,這時候,Amazon的S3服務看起來好像是我們擴展存儲的最容易也最便宜的方法。
在測試S3之前,我們實際上并沒有對費用用做多少評估,一方面是當時并沒有太多的云計算可供選擇,另外就是一些令人尊敬的工程師也極力推薦S3,最后,我們從來就沒指望會大量使用。
因為開發者的機器沒有使用Mogile,所以已經有一個框架用于接口不同的文件存儲系統,這樣一來,增加S3的支持就相對容易些。事實上,僅用了大約一天就實現了S3支持,又測試了一兩天,然后就將其打包到我們的每周例行發布中了。這種實現的簡易性也是我們選擇S3的另一個關鍵因素。
最初,我們計劃只將最早的文件遷移到S3,這些文件是從2007年12月份開始的,因為這些文件訪問頻率比較低,不用怎么擔心性能和可用性方面的問題。這個模式非常棒,S3看起來性能也很好。
唯一的不足,我們從Mogilefs中遷移文件的速度不夠快,沒有跟上文件增長的速度。而且 Mogilefs也開始出現一些性能問題了。我們的解決方案跟其他幾家大型網站一樣:將文件直接存儲到S3。開始時,只將一小部分新文件直接存儲到S3,然后逐漸增加,一直到絕大部分新文件都流向Amazon。這樣,事情又搞定了,我們就轉去解決其他問題了。
雖然S3已經相當可靠了,仍然出現了一些值得注意的問題。我們遇到的第一個問題是最終一致性(eventual consistency)問題,基本上,這意味著不能保證立即讀取剛寫入的文件,在寫入到西雅圖的S3集群后,再試圖從EC2中讀,這個問問題會更嚴重。通過將所有的文件訪問都從我們在西雅圖的數據中心代理而使這個問題有所緩和,但這樣一來,我們的帶寬就增加了。
我們遇到的第二個問題是Amazon?返回HTTP500錯誤,我們的代碼能夠對不成功的請求進行重試,這在大多數情況下工作良好。每一兩周,我們都會遇到錯誤突然爆發,以至于重試邏輯都不起作用了。這種爆發會持續一小時左右。一天,我正在查看發生錯誤的關鍵字(keys),注意到這些關鍵字都有相同的前綴!結果證明,S3是基于關鍵字的范圍對數據進行分區的,這意味著維護(如增減某個分區容量)會導致某個范圍內的關鍵字大量出錯。Amazon這樣做是為了保持S3的高性能。對我們而言,這種突發性錯誤更大程度上是種煩惱,因為我們還有Mogilefs在起作用,如果寫到S3失敗,將其寫到Mogile:就是了。隨著增長率趨于穩定,這個問題現在已經很少出現了,但Mogile仍然在發揮作用。
其實我們遇到的這些問題是構建大規模系統必然會發生的,所以 Amazon也用不著掩飾什么。人們很容易忘記,這其實是一個有著很多用戶的規模巨大的分布式系統。
隨著流量的增長,我們越來越依賴于S3。要是S3宕掉了,一天的大部分時間里,我們的Mogl都無法處理龐大的請求。幸運的是,S3大部分問題都不是發生在我我們網站的高峰時間,所以 Mogilev還能夠應付。我也應該提到的是,Mogile在兩種情況下會宕機幾個小時,種情況是修改 MYSQL的表結構,還有就是調試Mogilev的Perl代碼。這種時候,1009%的流量都會壓到S3上,而我們的用戶則從來不知道發生了什么。
“無限”存儲的一個危險是很容易造成浪費。對我們來說,我并沒怎么注意刪除無用文件的后臺作業業,對于創建的文件,最終會刪除掉近75%,而無用文件增長起來是很快的。
即使我們曾經注意到了這個問題,我們事實上還是決定忽略它。Picnik的每一個人都很忙,而且這看起來也不是什么大不了的問題,再說了,還有更棒的新功能或其他的伸縮性問題需要我們去關注。有趣的是,S3讓我們選擇或者雇用和訓練更多的人,或者更簡單,寫張支票就行了。在我們的信用卡月度額度快用光的時候,一切都變了。
經過幾個月的調整、分析和重寫代碼,我們最后拿出了一份清理無用文件的可伸縮方案。首先是數據庫對無用文件記錄進行清理,然后在數據庫的文件記錄和S3上的關鍵字列表之間做一個大型的連接操作(a large merge-join),以執行實際的刪除。
在實現更好的清理系統的過程中間,我們開始意識到,S3對我們的工作負荷(workload)來說,實際上是非常昂貴的。先前的成本分析完全沒有考慮PUT操作的成本。很多S3的負荷中,存儲成本占了大頭,因為文件上載以后,在隨后的一個很長時段內,只是偶爾訪問下。正如前面所提到的,我們的負荷是創建大量文件,然后在隨后的幾天里就刪掉了這意味著PUT操作的成本上升了。
意識到這點以后,我們開始努力優化Mogilefsl的性能,并且研究高性能的NAS產品。最后,我們實現了一個基于Linux的NFS概念系統作為前端存儲,這意味著只需要在S3上存儲超過1周的大約25%的文件,這些留下來的文件也有了一個對S3來說更加友好的存取模式。
有很長一段時間,我們都不清楚S3是不是仍然合適。盡管更為傳統的NAS硬件看起來貴了點,但如果你對長期存儲需求有信心的話,可以在一年或兩年內分期付款。而另一方面,許多創業公司的CFO(包括我們自己的)都會告訴你,為了保持靈活性和一定程度的自由,多花點兒錢也值得一一這種靈活與自由就是S3提供的。當然,這種靈活性比將此花費算做運維費用還是資本費用更為重要。至于我們所關心的,就只是運維費用了,因為這直接與流量和功能有關。
混合計算
Picnik主要的服務端組件之一是我們的渲染場(render farm)。用戶在Picnik上保存圖片時,經常需要在服務端重建這個圖片。這時,客戶端會向服務器發送一大段XML文本描述用戶的編輯操作。Web服務器收到后,會將所需要的圖片連同XML文本一起打包,并將其加入到渲染作業隊列中。渲染服務器獲取該作業,重建圖片,然后將結果圖片返回給Web服務器。此時,客戶端處于阻塞狀態,等待服務器的響應。大多數時間,客戶只需要等待幾秒鐘。
雖然這是可伸縮系統的典型架構,我們在設計時仍然考慮到了未來對云計算的需求。這時的渲染服務器不需要訪問任何內部服務,如數據庫或存儲服務器。簡言之,它們非常適合于運行在EC2上,另外,我們已有了一個自己開發的配置管理和代碼部署系統,稱為Server manager。
像S3一樣,實際實現起來既簡單又快速。內部的渲染場已經考慮到了運行在Xen之上的WM了,所以我需要做的就是一些簡單修改,使渲染服務器的VM映像適合于EC2的Xen找,然后將其打包為AMI。在映像啟啟動時,首先連接Server manager?獲取需要安裝和運行的組件列表,其中之一是Renderserver,Renderserver用于連接渲染隊列以獲取渲染作業。我要做的第一件事就是激活兩個實例運行一下看看怎么樣一一棒極了!
第二階段就是去實現云操作的終極目標(loly Grail)了:自動伸縮(auto-scaling)。我們的自動伸縮實現起來還是很容易的,因為所有處理都是通過隊列實現的。由于用戶在等待渲染結果,所以自動伸縮代碼的目標是維護一個空隊列5。每分鐘都會喚醒Server manager的一個線程,輪詢隊列的統計信息(上一分鐘已做過平衡),然后進行計算,看為了維持閑者和忙者的比例需要做些什么。當然,由于流量和網絡延遲會有小幅波動,為避免不必要的振蕩而對閑忙比例的修正會出現遲滯現象,如EC2實例有的時候需要幾分鐘才能啟動,我們的代碼也考慮到了這些問題。所有這些經驗性的調整經歷了一兩周的時間,系統運行起來以后,就非常簡單啦。
自動伸縮并不僅僅是典型的容量需求問題,我們也遇到了諸如到EC2的網絡延遲加大了,或發布了一個代碼修正而使得渲染速度變慢了。遇到這些情況時,我們先停止自動伸縮,直到找出背后真正的原因并加以改正為止。我們還修正了一個錯誤,這個錯誤使一小部分用戶的保存操作失敗,這一修正使渲染負載増加了20%正好在圣誕節之前。
這種設置也很適合批處理作業。一段時間以前,我們要重建一批縮略圖,我就寫了一些代碼,將作業提交給渲染隊列,然后用新的縮略圖文件更新數據庫記錄。我不需要做任何特別的事情來分配空間或將作業設置在晚上負載較輕的時候處理,Server manager只是增加新的實例以適應新的負載需求。
從財務方面來說,使用EC2比使用S3更清楚。我們試圖擴建內部渲染以滿足平均的容量需求,同時,將做渲染的CPU轉換到做Web服務的CPU也容易。這意味著將云作為渲染服務器給Web服務器帶來了一些動態特性,這讓我們易于適應負載模式的變化,而且,通過逐漸購買硬件的方式,這也讓我們更有效地使用現有的硬件。例如,可以在數據中心訂購個新的機柜,然后把服務器上架,而不用擔心浪費大部分的機柜電力。
一般與EC2有關的問題主要集中在連接性上。雖然互聯網作為一個整體是可靠的,但任何兩點之間的連接卻并非如此。通常,如果問題出現在網絡和數據中心之間,只有一小部分用戶受影響,但是,假如網絡恰好是云計算提供者,則所有用戶都會受影響。這種類型的宕機可能非常嚴重,因為問題可能出在這樣的區域,就是不論是你還是云計算提供者都沒有為之付費,即雙不管的區域。
在發生嚴重問題時(而且是在繁忙時段),唯一的選擇就是甩掉負載。過去,我們只有種辦法控制讓多少用戶進來,現在我們按優先級將用戶分類(游客、免費用戶、合作伙伴、金牌用戶)。可情的是,大多數情況下,你不得不等待宕機恢復。不論哪種情況,我們做的第一件事情就是更新 Twitter信息(fecd),這些消息也顯示在我們的It's raining onour Picnik”頁面上。我們并不指責任何人一個用戶才不關心這些呢。
我們并不像對內部服務器那樣監控EC2實例。Nagiosi通過 Servermanager自動獲取EC2實例的信息,Nagiost也監控隊列深度,因為這是很多問題的預警器(early indicator)。
Caci以圖示方式顯示運行實例數(通過EC2API)及集群層層面上的性能數據。我們不需要在 Cacti上增加單個實例的信息,因為它并不實際處理集群,何況實例還是動態變化的。
事實上,我們并不關心單個實例的性能,已經知道這些實例比本地機器上的要慢一點。沒關系的,因為自動伸縮系統總能在現有資源的條件下找到平衡。
由于實例是從隊列中獲取作業的,EC2實例稍微慢一點,只是少干點活兒而已,不會躺倒不干。這使我能夠集中精力關注高層的性能數據,如一天中使用EC2實例的比例是多少。在一天結束時,只需要針對web服務器做容量規劃,從而決定硬件購買決策,而渲染服務器只是從未使用的容量中獲益。
要想有效使用網站建設云計算資源,需要對應用架構和配置管理/自動化有一個合理的“增長”態度。我們將渲染服務器設計為可分解的,以及我們手邊已經具備配置管理系統等,這些事實使得自動伸縮實現起來既容易又可靠。
本文地址:http://murenxiang.com.cn//article/3306.html