最后更新于2018/11/16
我们和一般技术公司不一样的地方,在于对待错误的态度。接下来大家会对这一点深有体会。
我所能做的,就是和每个人谈谈我的失败经历,我倒是敢保证,如果你们都重蹈我的覆辙,那么你们也一定会跌得很惨。
——唐纳德·基奥
接下来昀哥我讲五个故事:
引子:某行网上银行上线那一天,我的悲惨遭遇
别人的错误1—前事不忘后事之师:台湾飞机失速坠毁
别人的错误2—对抗人的错误:401航班失事
我的错误1—做好被攻陷的准备:刚接手工作几个月,黑客入侵
别人的错误3—工具和风控:骑士资本集团的覆灭
引子:某行网上银行上线那一天,我的悲惨遭遇
2001年,领导指派我对接某银行网上银行里的一个小项目。
项目内容比较简单,我准备的也很充分,在公司测试服务器上拆了装,装了拆,演练了六七遍。
某行测试环境各方联调的时候,我也是一次性通过。
万万没想到,网银首次上线部署的那一天,我这边却出事儿了。
那是一个周五的晚上,各家公司都集中到了该行软件中心的新机房,开始通宵发布。
按部就班部署完之后,程序报错,大概意思是父进程的身份标识无法传递给子进程,导致子进程访问第三方网络资源被拒绝。
我重装了系统和服务,甚至格式化了磁盘,一遍又一遍地重试,结果都是一样的。
一晚上很快就过去了。
某行的人看我面色不佳,劝我先去宾馆房间休息一下,缓一缓。
走在路上,天色大亮,喉头一甜,吐了一口血。
睡了一觉,又接着试,还是不行,这时候已经到了周六下午三点。
我只好给领导打电话。领导和两个技术总监带着笔记本电脑来了。
现场改了代码,总算是过了。
在这个故事里,昀哥我哪些地方做错了?
首先是我在最后时刻才寻求公司内部资源支持,所以我们遇到线上事故的时候,一定要拿起电话召集人马,不要拖延。
其次,虽然上线前自己反复测试认为已经没有风险,但没有关注自己代码严重依赖的系统服务和第三方库。
自此之后,我非常关注“失败”,非常关注BUG、安全漏洞和投诉。
我通读了所有的系统知识库文章,经常会跑去看一看各种开源系统的Feature List和Bugfix,看看它们发布了什么特性,修复了什么缺陷。
这件事改变了我的人生……
点题:
『我得到正确判断的办法,
通常是先收集各种错误判断的例子,
然后仔细考虑怎样避免得到这些下场。』
——《穷查理宝典2》查理·芒格
尾声:
几年后,我阅读到一篇微软KB,公布了一个系统组件的BUG,可能就是我那个。
别人的错误1—前事不忘后事之师:台湾飞机失速坠毁
上个世纪九十年代,为了迎接开放大陆探亲和两岸三通,台湾一窝蜂地成立了很多小航空公司,从零到一的过程中,大干快上,很容易埋下致命隐患。
在这样的大时代背景下,2001年9月3日,台湾凌天航空的一架贝尔206直升机在空中失速坠毁了,奇怪的是,调查小组竟然找不到原因。
第一,意外来得太快,毫无预警,驾驶员刚通报飞机失速,还不到一分钟就坠毁了。
第二,机体摔得严重变形,但飞机却没有爆炸。
第三,一个负责监视引擎燃烧状况的警示灯,钨丝严重烧坏变形,所以调查小组推测,它在出事时可能是点亮的,这是一个非常有价值的信息,调查小组决定沿着引擎燃烧状况这一点排查下去。
但查了很久,也找不出可能导致引擎燃烧异常的原因。
有一天,化学鉴识人员打来电话。
你们提供的飞机燃料有没有问题,那根本不是汽油,是水。
怎么可能?你们要不要再重验看看?
早都验过好几次了。
调查小组只好对失事飞机燃料箱内的燃料重新采样,再请他们检验,报告结果出来了,仍然是100%的纯水。
检查加油记录,检查贮油槽的汽油,检查供应商,一切都合乎规定。
百思不得其解。
大家觉得会是什么原因呢?
这家航空公司拥有两个自制的大贮油槽,原先失事飞机加油的那个贮油槽已经用光汽油了,另外的这一大槽还剩下将近一半。
因为是小型民航公司,所以飞机航次不多,上一桶汽油大约用了整整四个多月。
他请人找来一条管子,伸到油槽底部,利用虹吸管原理把底层的汽油抽出来。
果然,管子流出来许多清澈无味的液体,不用化验就知道那是水。
难怪飞机没有爆炸。
再检查给这架贝尔206加油的油罐车里,其所装载的油料含水量为99.8%。
WHY?!
请注意台湾是个岛,湿度比较大,四个月以来,槽内空气在白天温度升高时凝成水滴,不断地沿着内壁滑入汽油中。由于水的比重大于油,这些水全沉入了最底层。
调查小组找出国外贮油槽的设计图,发现人家全都可以排除或避免底层液体被抽取使用。而小民航公司急匆匆制造出来的贮油槽,虽然外观差不多,可是实际上的功能却只是个容器而已。
所以,悲剧就这么发生了,那架倒霉的飞机有好几个油箱,一个是原先剩下的汽油,其他的从油槽底层加了一肚子水,第一个油箱耗光后就突然失速掉了下来,一肚子水自然不会爆炸。
而大陆一线机场的燃油管道都敷设在地下,加油车只是一个移动的油泵。
【大陆一线机场的加油方式】
所以不必担心抽到含水量998的燃油。
【油库在地下】
点题:
『我们要把别人的历史当作自己的未来,
这样,
才能知道过去人家在做什么,
我们现在应该怎么做。』
——冯仑《行在宽处》
尾声:
你以为这就是结局?
14年后。
2015年2月4日上午10:45,从台北飞往金门的台湾复兴航空ATR-72-600型客机起飞不到五分钟即坠毁。40人遇难。
坠毁客机机体严重损毁,但是河道上却全无油污。
【没有油污的河面】
别忘了这是一架刚起飞不久的飞机。
【干干净净的残骸】
这是复兴航空这架飞机失速的最后遗照。
【最后遗照】
整整齐齐摆放的行李箱,
提醒我们仍然是:
善于遗忘的愚蠢的人类。
【整整齐齐的行李箱】
所谓的出师其实就是把该犯的错误都犯一遍,
有人出师快其实是他试错快。
如果有人说他没有犯错误,
只能说他还没学到家,
就算现在绕开了以后迟早还是要碰到的。
——韩冰
别人的错误2—对抗人的错误:401航班失事
1972年12月29日深夜,美国东方航空401号航班准备降落在迈阿密国际机场。
【美国东方航空相似机型】
机长开始做例行降落检查,不久后却以每小时365公里的速度撞向沼泽,飞机解体爆炸。
【残骸】
虽然沼泽的黑泥吸收了飞机撞击的能量,吸收了20吨航空燃油,有一些人侥幸活了下来,但也都浑身沾满了强腐蚀性的航空燃油,在残骸和断肢中等待救援。
【大沼泽地国家公园】
到底发生了什么?
首先,这架飞机的自动驾驶有一个设计,你也可认为是缺陷,就是在自动驾驶接通的情况下,只要在方向舵上稍稍用点力,就会解除自动驾驶系统中负责高度的控制,设计者的本意可能是为了方便人工干预吧。
【Left Gear是左起落架指示灯,Right Gear是右起落架指示灯】
其次,用于指示前起落架放下锁好的绿灯不亮。这可能是起落架真的有问题,但更有可能是灯泡坏了。
为了把前起落架指示灯拔出来检查灯泡坏没坏,机长让一副把飞机切换成了自动驾驶,保持2000英尺的高度,在机场附近盘旋。
之后机长、一副、二副都忙着插拔灯泡。
灯泡确实是坏的。
机长怒火中烧,让二副下到机腹直接观察前起落架是否放下锁好,但忙乱中,机长忘了开着陆灯,外面漆黑一片,所以二副无功而返。
机长把灯打开,二副又带着人去观察了。
在这个过程中,机长的身体碰了方向舵并前推,于是飞机开始缓慢地下降。
不幸的是,机组成员都在专心研究指示灯为什么不亮,而忽略了飞机在偏离机组设定高度上下250英尺时发出的警报声。
其次,美国联邦航空管理局(FAA)当时没有要求空中管制员必须提醒机组高度不正确,再加上地面雷达本身经常有错误显示高度随后恢复正常的缺陷,所以塔台的空中管制员虽然注意到了401航班的飞行高度已经降到了900英尺,但没有干预。
401航班的最后一次生还机会丧失了。
当近地警报响起时,当机长终于发现有问题的时候,高度已经不足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?
这个时候飞机已经拉不起来了。
99人当场死亡。机长、一副、二副都伤重不治。
【残骸】
事后检查,起落架其实是好的。
我们遇到的很多灾难性事故,往往是一系列小问题、小故障、小 BUG 连起来造成的。401航班就是这样。
插曲:
乘客,机组成员甚至于美国东方航空的高管,都声称在某些装了401航班部件的飞机上,见到过奇怪的“东西”……
【401航班灵异事件】
尾声:
6年后,1978年12月28日的美国联合航空173号航班事故中,同样是起落架指示灯没有如期变绿,反而是出现一声巨响,机体开始抖动,同样是机长专注于排查问题,多次被提醒燃油即将耗尽,一再忽略。
最后,事故导致8名乘客和2名机组成员遇难。
173航班是航空安全史上的重要分水岭:自此,分成173之前和173之后。
首先,173号航班的事故调查报告催生了航空界首套机组资源管理系统,CRM。
美国国家运输安全委员会(National Transportation Safety Board)要求各航空公司加强对机员的机组资源管理(CRM)训练。CRM的前提是基于这样一个假定:人的错误是普遍存在和不可避免的。如果人的错误是不可避免的,CRM就可以看成是一个对抗人的错误的工具。CRM包括六大工具:标准程序、标准喊话、交叉检查、飞行简令、检查单和指令复诵与证实。
其次,美国联邦航空管理局(FAA)要求空管员向飞行员提供飞行高度数据。
推荐书单:
每一位TeamLeader都应该读一读这本书:
航空业的“黑匣子思维”与一般人思维的根本区别在于:
一般人认为错误是不好的,出于本能会为错误找各种借口;
但这套方法,会把错误看成进步的契机。犯错的人要改正,没犯错的人也要自省,从而杜绝重复犯错,使整个组织,甚至整个行业都能从中获益。
-救生衣必须出舱之后再充气,是因为埃塞俄比亚航空961号;
-锂电池必须随身携带,是因为UPS航空6号班机;
-所有的飞机都安装密码驾驶舱门,是因为著名的911。
这本书的作者称,航空业与医疗业对错误的态度是迥然不同的,航空业更愿意正视错误,飞行员们总体上说对自身的失误都抱着公开和坦诚的态度,部分原因是错误会导致他们自己死亡。这个行业里有强势并独立的组织专门负责对空难进行调查。失败不会被当成控诉某一位飞行员的理由,而会被视为能让所有飞行员、航空公司和管理者们学习进步的一次宝贵机会。
阶段性小结:错误是我们的财富
对于事故处理,我们遵从航天二十字诀:定位准确、机理清楚、可以复现、措施有效、举一反三。
“丰田生产体系”与航空航天的这个原则是相通的,如果对待错误的态度是开诚布公的,那么整套系统就能从中学习,能取得进步。
我们坚持每错必查、错了又错就整改、每错必写,用身体力行告诉每一个新员工直面错误、公开技术细节、分享给所有人,长此以往,每一次事故都会变为我们的财富。
我的错误1—做好被攻陷的准备:刚接手工作几个月,黑客入侵
很多年前,没想到我刚刚到任几个月,数据库就被黑客下载了。
过程是这样的:
凌晨2:46,黑客通过某地IDC机房里的一台肉鸡,仅仅两次尝试输入管理后台登录地址后,就准确地输对了,说明此黑客清楚我们的后台登录拼写规则,否则不可能在7秒内依次尝试很难的拼写。
登入8分钟后,他利用 FCKeditor 版本2.4.2以下的PHP版上传文件漏洞,上传了多个 php 文件。
然后,他种了一个 rootkit,即在 include 文件夹下放了一个 lndex.php,浏览这个 php 就可以从网页上修改操作系统 root 帐号的密码。
总之,黑客在凌晨3点到6点之间,在那台宿主机上密密麻麻地放了很多 php 文件上来,并修改了很多系统文件。
几天后,黑客又从那个肉鸡来了,访问他之前在服务器上种下的 rootkit。
然后,可能利用他从服务器上代码配置文件中看到的数据库用户名和密码,备份数据库并下载。
尾声:
内部IT系统的后端:
-在服务器端,日志文件里不要存储用户或商户的敏感信息,如登录密码、银行卡号、身份证号,曾经有一个知名公司的工程师为了调试方便,将用户的信用卡卡号和卡密记在日志文件里,但白帽子们发现能访问到这个日志文件……
-我们认为数据库有可能被盗走,所以要做到即使被拿走,也不要给客户和用户造成损失,所以数据库存取这些商业敏感信息时需要做高强度的对称加解密。
-工程配置文件只允许存储加密后的数据库登录密码,同时部署人员和开发人员都不允许掌握明文密码。
内部IT系统的前端:
-登录做两步验证;
-禁止多点登录;
-从框架上做好防 XSS+SessionHijacking+CSRF+SQLi……
-我们认为第三方很有可能拿到我们的平台登录权限(通过 session hijacking,或通过内部人),所以即使在合法用户登录状态下,敏感字段的展示要有遮挡,修改或查看敏感信息的时候要输入短验验证身份;
-内部IT系统的 robots.txt 内容必须是:『User-agent: * Disallow: /』,禁止搜索引擎收录。
点题:
我的态度是:放眼一年、三年、五年、十年,你的系统一定会被人攻陷,你的数据一定会被人拿走,往往是几个初中级安全漏洞,再加上一次社会工程学,就能成功渗透,并不需要高危漏洞。所以要做好灾难即将来临的准备,即使被攻陷,被拿走,也不要给商户和用户带来二次伤害。
别人的错误3—工具与风控:骑士资本集团的覆灭
2012年的时候,骑士资本是美国股票市场最大的经纪商,分别占有纽交所和纳斯达克 17% 的市场份额。
骑士资本的电子贸易部门管理的平均日交易量超过 33 亿股,交易额高达210 亿美元。
截止到 2012 年 7 月 31 日,骑士资本拥有高达 3 亿6500 万美元的现金及现金等价物。
在8月1日之前,骑士资本按照纽交所的项目计划,更新了算法程序 SMARS,它从交易平台接收大订单,然后根据买家或卖家的股票交易数量把大订单拆分成合适的小订单。
这次更新去掉了一些过时的代码,如 Power Peg,虽然它已经 8年没有用过了,但实际上 Power Peg 模块一直处于待命状态,只要系统的某一个特殊的参数被设置为「YES」,该模块就会被调用用来交易。
程序员开发了一个新的 RLP 模块,取代之前的 Power Peg 模块。取代后,之前那个特殊的参数被设置为「YES」,意思是使用 RLP 模块。
听起来是不是让人担心?
不用担心,测试完全通过,虽然更新后的代码沿用了以前用来激活 Power Peg 模块的标识符,但代码非常可靠。
7月27日到7月31日,骑士资本把 SMARS 软件手动部署到公司为数不多的服务器上。
一共才 8 台。
不幸的是,漏了一台服务器。
因为没有其他技术人员对部署过程做复查,所以没有人发觉第 8 台服务器上的 Power Peg 代码并没有被移除。
所以这台服务器上并没有 RLP 模块,只有 Power Peg 模块。「Power Peg」模块在被停用后的第10年被启动了。
灾难正在一分一秒地迫近。
2012年8月1日早上9点30分开盘后,很多交易员感觉到异乎寻常的事情发生了,某些个股涌现出大量不符合常理的订单,而且没有停止的迹象。
这个系统竟然没有断开的开关。
于是乎,在 45 分钟之内,骑士资本执行了超过日均交易额 50% 的订单,导致部分股票市值上升超过 10%,带来的连锁反应是其他股票价格暴跌。
由于没办法断开系统,也没有相关情况的预案说明,魂飞魄散的程序员只能在每分钟交易 800 万股的生产环境里调试。
因为没有能在线上发现问题,所以回滚了代码。
情况反而恶化了。
原本只是第 8 台上的 Power Peg 在疯狂地工作。
现在另外 7 台服务器上的 Power Peg 也加入了进来。
最后,骑士资本的技术人员和纽交所一起终于想办法终止了交易系统,然而已经过去了 45 分钟。
灾难现场,一片狼藉。
在这 45 分钟里,
对于内行人来说,骑士资本建立了 80 支个股 35 亿美元的净多头仓位和 74 支个股 31 亿 5000 万美元的净空头仓位。
对外行人来说,骑士资本在 45 分钟内亏损了 4 亿 6000 万美元,而上文提到,骑士资本仅有 3亿6500万美元的资产,这意味着骑士资本破产了。
骑士资本集团在整个事件中犯下的错误有哪些呢?
1,Power Peg 模块在停用时并没有从系统中删除,而是保留在系统里成为僵尸程序。
2,运维工程师手工部署,没有交叉验证,操作重大失误。
3,他们的风险管理完全是事后管理,缺乏事前控制。虽然对公司的敞口设置了限额,但超过限额时交易系统无任何限制。
4,他们的风险管理工具PMON,是一个事后的风险管理工具,完全依赖于人工监控。当交易量较大时,该系统还会有延迟,产生错误的报告。所以在灾难发生的时候,业务人员没有快速定位到敞口的来源,也没有意识到问题的严重性。
点题:
1,工具:假定人的错误是不可避免的,上线部署就应该是自动化的,而且是可重复的过程,尽量排除人为因素的干扰。如果你常年靠手动发布,总有一天会大难临头。
2.风控:你的业务保障平台,你的风控管理系统,是你的最重要的伙伴,不要轻视它,在关键时刻,它会救你的命。
最后,我们再呼应一下主题:
第一,
『我得到正确判断的办法,
通常是先收集各种错误判断的例子,
然后仔细考虑怎样避免得到这些下场。』
——《穷查理宝典2》查理·芒格
第二,
错误是我们的财富。
我们坚持每错必查、错了又错就整改、每错必写的RCA制度,
用身体力行告诉每一个新员工直面错误、公开技术细节、分享给所有人,
长此以往,每一次事故都会变为我们的财富,而不是包袱。
-EOF-
欢迎关注老兵笔记
老兵笔记,拿起手机扫一下