那些年我們一起犯過的錯
最后更新于2018/11/16
我們和一般技術公司不一樣的地方,在于對待錯誤的態(tài)度。接下來大家會對這一點深有體會。
我所能做的,就是和每個人談談我的失敗經(jīng)歷,我倒是敢保證,如果你們都重蹈我的覆轍,那么你們也一定會跌得很慘。
——唐納德·基奧
接下來昀哥我講五個故事:
引子:某行網(wǎng)上銀行上線那一天,我的悲慘遭遇
別人的錯誤1—前事不忘后事之師:臺灣飛機失速墜毀
別人的錯誤2—對抗人的錯誤:401航班失事
我的錯誤1—做好被攻陷的準備:剛接手工作幾個月,黑客入侵
別人的錯誤3—工具和風控:騎士資本集團的覆滅
引子:某行網(wǎng)上銀行上線那一天,我的悲慘遭遇
2001年,領導指派我對接某銀行網(wǎng)上銀行里的一個小項目。
項目內容比較簡單,我準備的也很充分,在公司測試服務器上拆了裝,裝了拆,演練了六七遍。
某行測試環(huán)境各方聯(lián)調的時候,我也是一次性通過。
萬萬沒想到,網(wǎng)銀首次上線部署的那一天,我這邊卻出事兒了。
那是一個周五的晚上,各家公司都集中到了該行軟件中心的新機房,開始通宵發(fā)布。
按部就班部署完之后,程序報錯,大概意思是父進程的身份標識無法傳遞給子進程,導致子進程訪問第三方網(wǎng)絡資源被拒絕。
我重裝了系統(tǒng)和服務,甚至格式化了磁盤,一遍又一遍地重試,結果都是一樣的。
一晚上很快就過去了。
某行的人看我面色不佳,勸我先去賓館房間休息一下,緩一緩。
走在路上,天色大亮,喉頭一甜,吐了一口血。
睡了一覺,又接著試,還是不行,這時候已經(jīng)到了周六下午三點。
我只好給領導打電話。領導和兩個技術總監(jiān)帶著筆記本電腦來了。
現(xiàn)場改了代碼,總算是過了。
在這個故事里,昀哥我哪些地方做錯了?
首先是我在最后時刻才尋求公司內部資源支持,所以我們遇到線上事故的時候,一定要拿起電話召集人馬,不要拖延。
其次,雖然上線前自己反復測試認為已經(jīng)沒有風險,但沒有關注自己代碼嚴重依賴的系統(tǒng)服務和第三方庫。
自此之后,我非常關注“失敗”,非常關注BUG、安全漏洞和投訴。
我通讀了所有的系統(tǒng)知識庫文章,經(jīng)常會跑去看一看各種開源系統(tǒng)的Feature List和Bugfix,看看它們發(fā)布了什么特性,修復了什么缺陷。
這件事改變了我的人生……
點題:
『我得到正確判斷的辦法,
通常是先收集各種錯誤判斷的例子,
然后仔細考慮怎樣避免得到這些下場?!?/span>
——《窮查理寶典2》查理·芒格
尾聲:
幾年后,我閱讀到一篇微軟KB,公布了一個系統(tǒng)組件的BUG,可能就是我那個。
別人的錯誤1—前事不忘后事之師:臺灣飛機失速墜毀
上個世紀九十年代,為了迎接開放大陸探親和兩岸三通,臺灣一窩蜂地成立了很多小航空公司,從零到一的過程中,大干快上,很容易埋下致命隱患。
在這樣的大時代背景下,2001年9月3日,臺灣凌天航空的一架貝爾206直升機在空中失速墜毀了,奇怪的是,調查小組竟然找不到原因。
第一,意外來得太快,毫無預警,駕駛員剛通報飛機失速,還不到一分鐘就墜毀了。
第二,機體摔得嚴重變形,但飛機卻沒有爆炸。
第三,一個負責監(jiān)視引擎燃燒狀況的警示燈,鎢絲嚴重燒壞變形,所以調查小組推測,它在出事時可能是點亮的,這是一個非常有價值的信息,調查小組決定沿著引擎燃燒狀況這一點排查下去。
但查了很久,也找不出可能導致引擎燃燒異常的原因。
有一天,化學鑒識人員打來電話。
你們提供的飛機燃料有沒有問題,那根本不是汽油,是水。
怎么可能?你們要不要再重驗看看?
早都驗過好幾次了。
調查小組只好對失事飛機燃料箱內的燃料重新采樣,再請他們檢驗,報告結果出來了,仍然是100%的純水。
檢查加油記錄,檢查貯油槽的汽油,檢查供應商,一切都合乎規(guī)定。
百思不得其解。
大家覺得會是什么原因呢?
這家航空公司擁有兩個自制的大貯油槽,原先失事飛機加油的那個貯油槽已經(jīng)用光汽油了,另外的這一大槽還剩下將近一半。
因為是小型民航公司,所以飛機航次不多,上一桶汽油大約用了整整四個多月。
他請人找來一條管子,伸到油槽底部,利用虹吸管原理把底層的汽油抽出來。
果然,管子流出來許多清澈無味的液體,不用化驗就知道那是水。
難怪飛機沒有爆炸。
再檢查給這架貝爾206加油的油罐車里,其所裝載的油料含水量為99.8%。
WHY?!
請注意臺灣是個島,濕度比較大,四個月以來,槽內空氣在白天溫度升高時凝成水滴,不斷地沿著內壁滑入汽油中。由于水的比重大于油,這些水全沉入了最底層。
調查小組找出國外貯油槽的設計圖,發(fā)現(xiàn)人家全都可以排除或避免底層液體被抽取使用。而小民航公司急匆匆制造出來的貯油槽,雖然外觀差不多,可是實際上的功能卻只是個容器而已。
所以,悲劇就這么發(fā)生了,那架倒霉的飛機有好幾個油箱,一個是原先剩下的汽油,其他的從油槽底層加了一肚子水,第一個油箱耗光后就突然失速掉了下來,一肚子水自然不會爆炸。
而大陸一線機場的燃油管道都敷設在地下,加油車只是一個移動的油泵。

【大陸一線機場的加油方式】
所以不必擔心抽到含水量998的燃油。

【油庫在地下】
點題:
『我們要把別人的歷史當作自己的未來,
這樣,
才能知道過去人家在做什么,
我們現(xiàn)在應該怎么做。』
——馮侖《行在寬處》
尾聲:
你以為這就是結局?
14年后。
2015年2月4日上午10:45,從臺北飛往金門的臺灣復興航空ATR-72-600型客機起飛不到五分鐘即墜毀。40人遇難。
墜毀客機機體嚴重損毀,但是河道上卻全無油污。

【沒有油污的河面】
別忘了這是一架剛起飛不久的飛機。

【干干凈凈的殘骸】
這是復興航空這架飛機失速的最后遺照。

【最后遺照】
整整齊齊擺放的行李箱,
提醒我們仍然是:
善于遺忘的愚蠢的人類。

【整整齊齊的行李箱】
所謂的出師其實就是把該犯的錯誤都犯一遍,
有人出師快其實是他試錯快。
如果有人說他沒有犯錯誤,
只能說他還沒學到家,
就算現(xiàn)在繞開了以后遲早還是要碰到的。
——韓冰
別人的錯誤2—對抗人的錯誤:401航班失事
1972年12月29日深夜,美國東方航空401號航班準備降落在邁阿密國際機場。

【美國東方航空相似機型】
機長開始做例行降落檢查,不久后卻以每小時365公里的速度撞向沼澤,飛機解體爆炸。

【殘骸】
雖然沼澤的黑泥吸收了飛機撞擊的能量,吸收了20噸航空燃油,有一些人僥幸活了下來,但也都渾身沾滿了強腐蝕性的航空燃油,在殘骸和斷肢中等待救援。

【大沼澤地國家公園】
到底發(fā)生了什么?
首先,這架飛機的自動駕駛有一個設計,你也可認為是缺陷,就是在自動駕駛接通的情況下,只要在方向舵上稍稍用點力,就會解除自動駕駛系統(tǒng)中負責高度的控制,設計者的本意可能是為了方便人工干預吧。

【Left Gear是左起落架指示燈,Right Gear是右起落架指示燈】
其次,用于指示前起落架放下鎖好的綠燈不亮。這可能是起落架真的有問題,但更有可能是燈泡壞了。
為了把前起落架指示燈拔出來檢查燈泡壞沒壞,機長讓一副把飛機切換成了自動駕駛,保持2000英尺的高度,在機場附近盤旋。
之后機長、一副、二副都忙著插拔燈泡。
燈泡確實是壞的。
機長怒火中燒,讓二副下到機腹直接觀察前起落架是否放下鎖好,但忙亂中,機長忘了開著陸燈,外面漆黑一片,所以二副無功而返。
機長把燈打開,二副又帶著人去觀察了。
在這個過程中,機長的身體碰了方向舵并前推,于是飛機開始緩慢地下降。
不幸的是,機組成員都在專心研究指示燈為什么不亮,而忽略了飛機在偏離機組設定高度上下250英尺時發(fā)出的警報聲。
其次,美國聯(lián)邦航空管理局(FAA)當時沒有要求空中管制員必須提醒機組高度不正確,再加上地面雷達本身經(jīng)常有錯誤顯示高度隨后恢復正常的缺陷,所以塔臺的空中管制員雖然注意到了401航班的飛行高度已經(jīng)降到了900英尺,但沒有干預。
401航班的最后一次生還機會喪失了。
當近地警報響起時,當機長終于發(fā)現(xiàn)有問題的時候,高度已經(jīng)不足200英尺了。
最后的對話是這樣的:
一副:飛行高度有變動。
機長:什么?
一副:高度還在2000英尺吧?
機長 :嘿,這是怎么了?
Stockstill: We did something to the altitude.
Loft: What?
Stockstill: We're still at 2,000 feet, right?
Loft: Hey—what's happening here?
這個時候飛機已經(jīng)拉不起來了。
99人當場死亡。機長、一副、二副都傷重不治。

【殘骸】
事后檢查,起落架其實是好的。
我們遇到的很多災難性事故,往往是一系列小問題、小故障、小 BUG 連起來造成的。401航班就是這樣。
插曲:
乘客,機組成員甚至于美國東方航空的高管,都聲稱在某些裝了401航班部件的飛機上,見到過奇怪的“東西”……
【401航班靈異事件】
尾聲:
6年后,1978年12月28日的美國聯(lián)合航空173號航班事故中,同樣是起落架指示燈沒有如期變綠,反而是出現(xiàn)一聲巨響,機體開始抖動,同樣是機長專注于排查問題,多次被提醒燃油即將耗盡,一再忽略。
最后,事故導致8名乘客和2名機組成員遇難。
173航班是航空安全史上的重要分水嶺:自此,分成173之前和173之后。
首先,173號航班的事故調查報告催生了航空界首套機組資源管理系統(tǒng),CRM。
美國國家運輸安全委員會(National Transportation Safety Board)要求各航空公司加強對機員的機組資源管理(CRM)訓練。CRM的前提是基于這樣一個假定:人的錯誤是普遍存在和不可避免的。如果人的錯誤是不可避免的,CRM就可以看成是一個對抗人的錯誤的工具。CRM包括六大工具:標準程序、標準喊話、交叉檢查、飛行簡令、檢查單和指令復誦與證實。
其次,美國聯(lián)邦航空管理局(FAA)要求空管員向飛行員提供飛行高度數(shù)據(jù)。
推薦書單:
每一位TeamLeader都應該讀一讀這本書:
航空業(yè)的“黑匣子思維”與一般人思維的根本區(qū)別在于:
一般人認為錯誤是不好的,出于本能會為錯誤找各種借口;
但這套方法,會把錯誤看成進步的契機。犯錯的人要改正,沒犯錯的人也要自省,從而杜絕重復犯錯,使整個組織,甚至整個行業(yè)都能從中獲益。
-救生衣必須出艙之后再充氣,是因為埃塞俄比亞航空961號;
-鋰電池必須隨身攜帶,是因為UPS航空6號班機;
-所有的飛機都安裝密碼駕駛艙門,是因為著名的911。
這本書的作者稱,航空業(yè)與醫(yī)療業(yè)對錯誤的態(tài)度是迥然不同的,航空業(yè)更愿意正視錯誤,飛行員們總體上說對自身的失誤都抱著公開和坦誠的態(tài)度,部分原因是錯誤會導致他們自己死亡。這個行業(yè)里有強勢并獨立的組織專門負責對空難進行調查。失敗不會被當成控訴某一位飛行員的理由,而會被視為能讓所有飛行員、航空公司和管理者們學習進步的一次寶貴機會。
階段性小結:錯誤是我們的財富
對于事故處理,我們遵從航天二十字訣:定位準確、機理清楚、可以復現(xiàn)、措施有效、舉一反三。
“豐田生產(chǎn)體系”與航空航天的這個原則是相通的,如果對待錯誤的態(tài)度是開誠布公的,那么整套系統(tǒng)就能從中學習,能取得進步。
我們堅持每錯必查、錯了又錯就整改、每錯必寫,用身體力行告訴每一個新員工直面錯誤、公開技術細節(jié)、分享給所有人,長此以往,每一次事故都會變?yōu)槲覀兊呢敻弧?/span>
我的錯誤1—做好被攻陷的準備:剛接手工作幾個月,黑客入侵
很多年前,沒想到我剛剛到任幾個月,數(shù)據(jù)庫就被黑客下載了。
過程是這樣的:
凌晨2:46,黑客通過某地IDC機房里的一臺肉雞,僅僅兩次嘗試輸入管理后臺登錄地址后,就準確地輸對了,說明此黑客清楚我們的后臺登錄拼寫規(guī)則,否則不可能在7秒內依次嘗試很難的拼寫。
登入8分鐘后,他利用 FCKeditor 版本2.4.2以下的PHP版上傳文件漏洞,上傳了多個 php 文件。
然后,他種了一個 rootkit,即在 include 文件夾下放了一個 lndex.php,瀏覽這個 php 就可以從網(wǎng)頁上修改操作系統(tǒng) root 帳號的密碼。
總之,黑客在凌晨3點到6點之間,在那臺宿主機上密密麻麻地放了很多 php 文件上來,并修改了很多系統(tǒng)文件。
幾天后,黑客又從那個肉雞來了,訪問他之前在服務器上種下的 rootkit。
然后,可能利用他從服務器上代碼配置文件中看到的數(shù)據(jù)庫用戶名和密碼,備份數(shù)據(jù)庫并下載。
尾聲:
內部IT系統(tǒng)的后端:
-在服務器端,日志文件里不要存儲用戶或商戶的敏感信息,如登錄密碼、銀行卡號、身份證號,曾經(jīng)有一個知名公司的工程師為了調試方便,將用戶的信用卡卡號和卡密記在日志文件里,但白帽子們發(fā)現(xiàn)能訪問到這個日志文件……
-我們認為數(shù)據(jù)庫有可能被盜走,所以要做到即使被拿走,也不要給客戶和用戶造成損失,所以數(shù)據(jù)庫存取這些商業(yè)敏感信息時需要做高強度的對稱加解密。
-工程配置文件只允許存儲加密后的數(shù)據(jù)庫登錄密碼,同時部署人員和開發(fā)人員都不允許掌握明文密碼。
內部IT系統(tǒng)的前端:
-登錄做兩步驗證;
-禁止多點登錄;
-從框架上做好防 XSS+SessionHijacking+CSRF+SQLi……
-我們認為第三方很有可能拿到我們的平臺登錄權限(通過 session hijacking,或通過內部人),所以即使在合法用戶登錄狀態(tài)下,敏感字段的展示要有遮擋,修改或查看敏感信息的時候要輸入短驗驗證身份;
-內部IT系統(tǒng)的 robots.txt 內容必須是:『User-agent: * Disallow: /』,禁止搜索引擎收錄。
點題:
我的態(tài)度是:放眼一年、三年、五年、十年,你的系統(tǒng)一定會被人攻陷,你的數(shù)據(jù)一定會被人拿走,往往是幾個初中級安全漏洞,再加上一次社會工程學,就能成功滲透,并不需要高危漏洞。所以要做好災難即將來臨的準備,即使被攻陷,被拿走,也不要給商戶和用戶帶來二次傷害。
別人的錯誤3—工具與風控:騎士資本集團的覆滅
2012年的時候,騎士資本是美國股票市場最大的經(jīng)紀商,分別占有紐交所和納斯達克 17% 的市場份額。
騎士資本的電子貿(mào)易部門管理的平均日交易量超過 33 億股,交易額高達210 億美元。
截止到 2012 年 7 月 31 日,騎士資本擁有高達 3 億6500 萬美元的現(xiàn)金及現(xiàn)金等價物。
在8月1日之前,騎士資本按照紐交所的項目計劃,更新了算法程序 SMARS,它從交易平臺接收大訂單,然后根據(jù)買家或賣家的股票交易數(shù)量把大訂單拆分成合適的小訂單。
這次更新去掉了一些過時的代碼,如 Power Peg,雖然它已經(jīng) 8年沒有用過了,但實際上 Power Peg 模塊一直處于待命狀態(tài),只要系統(tǒng)的某一個特殊的參數(shù)被設置為「YES」,該模塊就會被調用用來交易。
程序員開發(fā)了一個新的 RLP 模塊,取代之前的 Power Peg 模塊。取代后,之前那個特殊的參數(shù)被設置為「YES」,意思是使用 RLP 模塊。
聽起來是不是讓人擔心?
不用擔心,測試完全通過,雖然更新后的代碼沿用了以前用來激活 Power Peg 模塊的標識符,但代碼非??煽?。
7月27日到7月31日,騎士資本把 SMARS 軟件手動部署到公司為數(shù)不多的服務器上。
一共才 8 臺。
不幸的是,漏了一臺服務器。
因為沒有其他技術人員對部署過程做復查,所以沒有人發(fā)覺第 8 臺服務器上的 Power Peg 代碼并沒有被移除。
所以這臺服務器上并沒有 RLP 模塊,只有 Power Peg 模塊?!窹ower Peg」模塊在被停用后的第10年被啟動了。
災難正在一分一秒地迫近。
2012年8月1日早上9點30分開盤后,很多交易員感覺到異乎尋常的事情發(fā)生了,某些個股涌現(xiàn)出大量不符合常理的訂單,而且沒有停止的跡象。
這個系統(tǒng)竟然沒有斷開的開關。
于是乎,在 45 分鐘之內,騎士資本執(zhí)行了超過日均交易額 50% 的訂單,導致部分股票市值上升超過 10%,帶來的連鎖反應是其他股票價格暴跌。
由于沒辦法斷開系統(tǒng),也沒有相關情況的預案說明,魂飛魄散的程序員只能在每分鐘交易 800 萬股的生產(chǎn)環(huán)境里調試。
因為沒有能在線上發(fā)現(xiàn)問題,所以回滾了代碼。
情況反而惡化了。
原本只是第 8 臺上的 Power Peg 在瘋狂地工作。
現(xiàn)在另外 7 臺服務器上的 Power Peg 也加入了進來。
最后,騎士資本的技術人員和紐交所一起終于想辦法終止了交易系統(tǒng),然而已經(jīng)過去了 45 分鐘。
災難現(xiàn)場,一片狼藉。
在這 45 分鐘里,
對于內行人來說,騎士資本建立了 80 支個股 35 億美元的凈多頭倉位和 74 支個股 31 億 5000 萬美元的凈空頭倉位。
對外行人來說,騎士資本在 45 分鐘內虧損了 4 億 6000 萬美元,而上文提到,騎士資本僅有 3億6500萬美元的資產(chǎn),這意味著騎士資本破產(chǎn)了。
騎士資本集團在整個事件中犯下的錯誤有哪些呢?
1,Power Peg 模塊在停用時并沒有從系統(tǒng)中刪除,而是保留在系統(tǒng)里成為僵尸程序。
2,運維工程師手工部署,沒有交叉驗證,操作重大失誤。
3,他們的風險管理完全是事后管理,缺乏事前控制。雖然對公司的敞口設置了限額,但超過限額時交易系統(tǒng)無任何限制。
4,他們的風險管理工具PMON,是一個事后的風險管理工具,完全依賴于人工監(jiān)控。當交易量較大時,該系統(tǒng)還會有延遲,產(chǎn)生錯誤的報告。所以在災難發(fā)生的時候,業(yè)務人員沒有快速定位到敞口的來源,也沒有意識到問題的嚴重性。
點題:
1,工具:假定人的錯誤是不可避免的,上線部署就應該是自動化的,而且是可重復的過程,盡量排除人為因素的干擾。如果你常年靠手動發(fā)布,總有一天會大難臨頭。
2.風控:你的業(yè)務保障平臺,你的風控管理系統(tǒng),是你的最重要的伙伴,不要輕視它,在關鍵時刻,它會救你的命。
最后,我們再呼應一下主題:
第一,
『我得到正確判斷的辦法,
通常是先收集各種錯誤判斷的例子,
然后仔細考慮怎樣避免得到這些下場?!?/span>
——《窮查理寶典2》查理·芒格
第二,
錯誤是我們的財富。
我們堅持每錯必查、錯了又錯就整改、每錯必寫的RCA制度,
用身體力行告訴每一個新員工直面錯誤、公開技術細節(jié)、分享給所有人,
長此以往,每一次事故都會變?yōu)槲覀兊呢敻?,而不是包袱?/strong>
-EOF-
歡迎關注老兵筆記

????老兵筆記,拿起手機掃一下
浙公網(wǎng)安備 33010602011771號