应用上云 2 小时烧掉近 50 万,创始人:差点破产,简直噩梦
英文:Sudeep Chauhan,轉自:InfoQ - 核子可樂
本文講述了我們在首款產品上市之前就差點破產、最后幸存下來并從中汲取教訓的故事。
2020 年 3 月,COVID-19 疫情全面爆發,我們的初創公司 Milkie Way 也遭受巨大打擊,差點破產。因為我們在對 Firebase 和 Cloud Run 進行內部測試的期間,一不小心在幾個小時里燒掉了 72000 美元(約 47 萬人民幣)。
2019 年 11 月,我們開始開發 https://announce.today 服務,希望借此打造 MVP 產品的可用功能 V1 版本。作為初步嘗試,我們的代碼以簡單的底層棧為依托,使用 JS、Python 代碼并將產品部署在 Google App 引擎之上。
因為團隊規模很小,所以我們的重點放在編寫代碼、設計 UI 以及準備產品身上。我們沒在云管理上花多少心思,當時的目標很簡單——能支撐起基本的開發流程(CI/CD)就行。
在這款 V1 Web 應用程序中,用戶體驗并不算流暢。但沒關系,我們的訴求只是開發出一些可供用戶體驗的產品,同時也構建了更好的 Announce 版本。隨著 COVID 疫情全面來襲,我們認為這正是做出改變的最佳時機,沒準 Announce 會成為各國政府在全球范圍內發布公告的理想選擇。
當時用戶還沒有創建任何內容,但我們認為能在平臺上提供一些既有數據可能更好。所以我們又建立了 Announce-AI 項目,旨在自動發布由 AI 創建的豐富內容。這里的豐富內容是指各類事件、地震等安全警告,以及本地用戶可能關心的相關新聞。
1 ?技術細節
為了開發 Announce-AI,我們決定使用 Cloud Functions。由于我們抓取數據的周期還很短,所以 Cloud Functions 幾乎是完美的選項。但在決定擴展規模之后,我們馬上遇到了麻煩——Cloud Functions 的超時時間長達 9 分鐘。
于是我們開始研究 Cloud Run,并發現它提供規模可觀的免費資源使用層!必須承認,那時候我們對 Cloud Run 并不夠了解,只是匆忙要求團隊在 Cloud Fun 上部署“測試”Announce-AI 功能。我們當時想得很簡單:盡快熟悉 Cloud Run,在探索中不斷學習。
為了簡單起見,我們在實驗中只引入一個很小的站點,就使用了 Firebase 作為數據庫(因為 Cloud Run 不提供任何存儲功能)。站點規模真的很小,完全用不上 SQL Server 或者任何其他成熟的商業數據庫。
我創建了名為 ANC-AI Dev 的新 GCP 項目,設置了 7 美元的云資源使用預算,并選擇使用 Firebase Project on the Free(Spark) 計劃。我們當時覺得,最糟糕的結果應該無非就是超出每日 Firestore 的免費限額,對吧?
在稍稍調整了代碼之后,我們開始部署流程,向其發出了幾條手動請求,而后就留下它保持運行。
2 ?噩夢由此開始
測試當天一切順利,我們又回到了 Announce 本體的開發當中。在第二天下班之后,我稍微睡了一會。醒來之后,我發現郵箱里有幾封來自 Google Cloud 的提醒郵件,而且郵件之間的間隔只有幾分鐘。
第一封郵件:Firebase Project 自動升級第二封郵件:預算超支幸運的是,我使用的信用卡設有 100 美元消費限額。于是收費停止,谷歌暫停了我們的所有賬戶。
第三封郵件:信用卡支付被拒我跳下床,登錄 Google Cloud Billing,并看到一張約 5000 美元的賬單。說實話,那一刻我慌得不行,根本沒辦法正常思考。我到處張望,想弄明白出了什么問題,包括到底該怎么付清這筆 5000 美元巨款。
但問題在于,賬單金額每分鐘都在上漲。
5 分鐘之后,賬單數額增長到了 15000 美元;20 分鐘后,數額增長至 25000 美元。我不確定這一切什么時候會停,或者說恐怕永遠不會停止?
2 個小時后,數額最終定格在 72000 美元。
這時我和我的團隊正忙著瘋狂確認情況。我徹底呆若木雞,根本不知道接下來該做點什么。我們停用了結算功能,同時關閉了所有服務。
由于我們在全部 GCP 項目中使用的都是同一張對公支付卡,所以谷歌已經全面關停了我們的賬戶及項目。
3 ?噩夢仍在繼續
事情發生在 3 月 27 日星期五晚上,即我們計劃發布 Announce V1 的三天之前。由于谷歌凍結了綁定同一張信用卡的全部項目,我們的產品開發工作陷入了僵局。士氣低落,這家年輕的公司前途未卜。
所有云項目都被關停,開發工作陷入僵局到這天午夜,我終于緩過神來,開始展開調查。我把調查工作中的每個步驟都詳細記錄在了文件當中……并將文件命名為“第 11 章”。
另外兩位團隊成員也加入了調查工作,與我一同不眠不休地探索真相。
第二天,也就是 3 月 28 日星期六,我打電話或發郵件給了十幾家律師事務所預約面談。大多數律所拒絕受理,只有一封郵件發來回復,讓我具體解釋解釋整個過程。必須承認,這件事即使對工程師來說也是細節過多、復雜難懂,我壓根不知道該怎么用簡單易懂的語言向律師做出說明。
作為一家自負盈虧的公司,我們拿不出 72000 美元。
到這時,我甚至認真研究過了《破產法》的第 7 章與第 11 章,并對接下來可能發生的一切做好了心理準備。
4 ?喘息之機:GCP 的漏洞
就在同一個周六,我開始查閱更多內容,特別是 GCP 說明文檔中的各種條目。好吧,我們確實犯了錯誤,但谷歌在一筆實際支付都沒完成的情況下就給我們計上了 72000 美元的賬,這正常嗎?!
GCP 與 Firebase1. 自動將 Firebase 賬戶升級為付費賬戶
在注冊 Firebase 時,我們從沒想過這還帶自動升級的,提示條款中也絕對沒有提及。我們的 GCP 項目確實接受了結算條款,因為只有這樣才能正常使用 Cloud Run;但 Firebase 不是,我們用的可是免費計劃。GCP 突然就進行了升級,并向我們收取巨額費用。
事實證明,他們就是這么設計的,理由是“Firebase 與 GCP 深度集成。”
2. 所謂的計費“限額”根本不存在,預算管理至少延遲了一天
GCP Billing 實際至少了延遲了一天。谷歌在大多數說明文檔中都建議用戶使用 Budgets 與自動關閉云功能。但你猜怎么著?在中斷功能被觸發或者通知到云用戶時,問題可能已經發生了。
結算過程大約需要一整天時間,所以我們第二天才收到計費提醒。
3. 谷歌應該向我們收取 100 美元,而不是 72000 美元!
由于我們的賬戶一直沒有實際支付,所以 GCP 應該先根據賬單信息收取 100 美元的費用,并在無法繼續付款后停止服務。但實際情況并非如此,后來我弄清了原因,問題仍然跟用戶這方無關。
我們賬戶的第一筆費用約為 5000 美元,下一筆就暴漲到了 72000 美元。
我們賬戶的計費上限為 100 美元4. 別相信 Firebase 儀表板!
不只是 Billing 功能,就連 Firebase 儀表板也要超過 24 個小時才能正常更新。
根據 Firebase 控制臺說明文檔,Firebase 控制臺的儀表板數字可能與 Billing 賬單報告“略有不同”。
以我們的情況為例,二者的差異高達 86585365.85%,也就是 86 萬倍。而且在向我們發出賬單之后,Firebase 控制臺的儀表板仍然顯示當月出現了 42000 次讀取 + 寫入操作(低于每日上限)。
5新的一天,新的挑戰
我之前曾在谷歌工作過大約六年半,也寫過幾十份項目說明文檔、取證報告。結合過往工作經驗,我整理出一份文件,向谷歌概述了這次事件,并總結了谷歌方面的錯誤和疏漏。照理來說,兩天后的周一,谷歌工作小組就會正常上班并接手處理。
增訂:部分讀者朋友建議我直接跟之前的谷歌同事聯系。事實上,我沒有動用任何原有的人脈,使用的就是普通開發商 / 公司采取的常規辦法。于是乎,我在聊天頻道、咨詢、冗余的電子郵件和一個個小錯誤身上浪費了無數時間。下面,咱們具體來看交涉過程中的種種細節。
離開谷歌的那一天與此同時,我們也在整理自己這邊犯下的錯誤,并制定新的產品開發策略。團隊里還有不少人根本不清楚發生了什么,只知道公司遇上了大麻煩。
作為前谷歌員工,我有豐富的“犯錯”經驗,還給谷歌造成了數百萬美元的損失。但谷歌的企業文化拯救了員工(當然,涉事工程師還是得寫一份長長的事件回顧報告)。這一次,我不再是谷歌人,我們手頭的資金有限、而之前投入巨大心力的成果正身陷風險。
先來看一個數字:1160 億,這是我們的測試代碼在一個小時之內讀取 Firestore 數據庫的次數。
這是我人生中第一次遭遇如此重挫,有可能徹底改變整個公司乃至我生活的未來方向。關于問題,我們可以說很多,但其中最重要的反而是個簡單的道理——保持堅強。
作為一家小公司的管理者,我手底下只有一支由 7 名工程師 / 實習生組成的團隊。谷歌那邊大概得 10 天左右才能與我們進一步接洽。在此期間,我們必須恢復開發,找到解決賬戶關停的方法。此外,產品及功能的設計工作也得盡快重啟。
6 ?我們到底做了什么?
因為團隊規模不大,我們希望盡可能使用無服務器架構。而 Cloud Functions 與 Cloud Run 等無服務器解決方案處理問題的基本思路,就是超時。
具體來講,實例會持續將 URL 抓取到網頁當中;但在 9 分鐘后,該實例就會超時。
可能是失敗激發了腦中的智慧,我在幾分鐘內就在白板上列出了一大堆設計問題。不知道為什么,在部署之前,我們能想到的就只有快速犯錯、快速嘗試。
Announce-AI 在 Cloud Run 上的“Hello World”版本為了克服超時限制,我建議使用 POST 請求(將 URL 作為數據)將作業發送至某一實例,且并發使用多個實例以替代串行使用單一實例。這樣 Cloud Run 中的每個實例只會抓取一個頁面,所以永遠不會超時。另外,由于 Cloud Run 的處理操作能夠精確到毫秒,所以全部頁面都將得到并發處理,整體性能得以高度優化。
部署在 Cloud Run 上的抓取器如果仔細觀察,就會發現流程中缺失了一些重要的部分:
不中斷的指數遞歸:由于沒有 break 語句,因此實例不知道該何時中斷。
POST 請求可以具有相同的 URL。如果存在指向上一頁的反射鏈接,則 Cloud Run 服務將陷入無限遞歸當中;而最糟糕的是,這個遞歸將呈指數增長(我們將最大實例數設置為 1000!)。
大家可以想象,這意味著多達 1000 個實例會不斷查詢,且每幾毫秒就向 Firebase 數據庫寫入一次。查看數據發布事件,我們發現 Firebase 在某一時間點上的每分鐘請求數量增長到了 10 億個!
GCP Billing Account 的月末交易摘要1160 億次讀取,3300 萬次寫入
總體來看,我們這套部署在 Cloud Run 的“Hello World”版本共執行了 1160 億次讀取與 3300 萬次寫入……我的媽呀!
Firebase 上的讀取操作成本:
(0.06 美元 / 100,000) * 116,000,000,000 = 69,600 美元
1600 個 Cloud Run 計算時
經過測試,我們假設該請求因日志記錄的停止而終止,但實際上它只是轉入后臺進程。由于我們沒有刪除服務(我們這是第一次用 Cloud Run,還不太了解具體細則),因此繼續有多個服務緩慢運行。
在 24 個小時之內,這些服務版本各自擴展到了 1000 個實例,共消耗掉 16022 個計算時。
7 ? 我們犯了什么錯誤?
在云上部署了存在缺陷的算法
根據之前的討論,我們確實發現了一種通過 POST 請求使用無服務器資源的新方法。這確實是種獨創性的方法,在互聯網上并沒有現成的參考,遺憾的是其中存在著我們當初沒有意識到的大問題。
使用默認選項部署 Cloud Run
在創建 Cloud Run 服務時,我們在服務中選擇使用默認值,即 max-instances 被設置為 1000,concurrency 設置為 80。一開始我們并不知道,這些預設值對我們的測試程序來說可以算是最不適用的組合。
如果我們把 max-instances 設定為 2,那我們的成本只會是現在的五百分之一,即由 72000 美元轉變為 144 美元。
如果我們將 concurrency 設定為 1,那么基本不會產生任何費用。
在不完全了解的情況下使用 Firebase
有些經驗必須從實踐當中獲取。Firebase 不像是能夠直接學習的編程語言,它是谷歌提供的一項容器化平臺服務,其中使用的是大量預定義規則,而且規則內容跟用戶的直覺或者傾向沒有任何關系。
另外,在 Node.js 中編寫代碼時,必須注意后臺進程。如果代碼進入后臺進程,則開發者很難意識到該服務仍在運行、而且在很長一段時間內持續運行。后來我們發現,這正是我們大多數云功能同樣出現超時的原因所在。
別在云上搞“快速失敗、快速學習”
云平臺像是一把雙刃劍。如果使用得當,它確實威力巨大;但如果使用不當,后果也將極為嚴重。
翻翻 GCP 說明文檔,大家就會發現它的頁數比幾本小說加起來還多。換言之,了解云定價及使用方式不僅非常耗時,而且要求相關人員充分了解云服務的工作原理。所以,別以為在傳統頭銜前面加個“云”是騙人的——這真是項技術活!
Firebase 與 Cloud Run 真的很強大
在峰值時期,Firebase 每分鐘能夠處理約 10 億次讀取,這真是太強大了。我們已經在 Firebase 上測試了 2 到 3 個月,目前仍在繼續學習,但完全沒有觸及到它的極限。
Cloud Run 也是如此!當 Concurrency == 60, max_containers == 1000 且每條 Request 用時 400 毫秒時,Cloud Run 每分鐘能夠處理 900 萬條請求!
60 * 1000 * 2.5 * 60 = 9,000,000 請求/分鐘
相比之下,谷歌搜索每分鐘也只能完成 380 萬次搜索。
使用 Cloud Monitoring
雖然 Google Cloud Monitoring 不會停止計費,但它能及時發送警報(延遲僅為 3 到 4 分鐘)。Google Cloud 的原型 / 命名結構有一定學習曲線,但在投入時間和精力之后,儀表板、警報與指標確實能讓我們更為輕松。
這些指標將保留 90 天。遺憾的是,我們在此次事件中的指標已經過期了,否則我很樂意在本文中向大家展示。
8 ?好消息:我們沒倒閉!
但很懸,太懸了在認真閱讀了關于此次事件的報告之后,經過一系列咨詢、討論與內部研究,谷歌直接免除了我們的賬單!
謝謝你,谷歌!
我們又恢復了活力,能夠繼續開發 Announce。而且這一次,我們擁有更好的視角、更強的架構與更安全的實現思路。
谷歌是我最欣賞的科技企業,這不只是因為它是一家值得為之工作的偉大公司,同時也因為它有著很強的同理心。谷歌提供的工具很合開發者的胃口,很重視說明文檔質量(大多數情況下),而且一直在不斷發展。(作者注:這只是我作為獨立軟件開發者的個人感受,絕非軟文或者刻意吹捧。)
9 ?接下來怎么辦?
經歷了這次事件,我們花了幾個月時間學習云架構和我們自己的業務體系。幾周之后,我們的知識提升到新的境界,于是開始使用經過改進的算法通過 Cloud Run 抓取“整個網絡”。
而在事后的整體分析中,我們決定放棄 V1 版本架構,轉而使用更具可擴展性的基礎設施為產品提供支持。
在 Announce V2 中,我們不再構建 MVP;相反,我們打造出一套平臺,借此快速迭代新產品,并在安全環境中進行全面測試。
這段經歷確實拖慢了我們的腳步……V2 在 11 月底才正式亮相,比原計劃的 V1 發布日晚了大約 7 個月。但 V2 可擴展性更強,能夠更充分地動用云資源,同時也擁有更好的優化水平。
我們還得以將其推向所有平臺,而不僅僅是 Web 平臺。
更重要的是,我們利用同一套平臺構建起第二款產品 Point Address。這兩種產品不僅可擴展性極佳、擁有出色的架構與高效性,還建立在同一套平臺之上。這使我們得以快速將業務靈感轉化為現實,并立即將其引入實際產品當中。
原文鏈接:
https://blog.tomilkieway.com/72k-1/
https://blog.tomilkieway.com/72k-2/
推薦閱讀 誤執行了rm -fr /*之后,除了跑路還能怎么辦?!程序員必備58個網站匯總大幅提高生產力:你需要了解的十大Jupyter Lab插件總結
以上是生活随笔為你收集整理的应用上云 2 小时烧掉近 50 万,创始人:差点破产,简直噩梦的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 答应我,调试Python代码,不要再用P
- 下一篇: 这个Python库助你发现网络图的社区结