拉巴力的纸皮箱


  • 首页

  • 标签

  • 归档

  • 关于

  • 搜索

Java垃圾回收总结

发表于 2021-08-17

  • Java中成熟的垃圾回收器有串行垃圾回收器、并行垃圾回收器、并发标记回收器(Concurrent Mark Sweep, CMS)、垃圾优先回收器(Garbage First,也称为G1)。在JDK 11中引入了一款新的垃圾回收器ZGC,在JDK 12中又引入了另一款新的垃圾回收器Shenandoah。虽然新的垃圾回收器不断地涌现,但是垃圾回收的基本算法变化并不大。简单来说,回收算法主要有复制、标记清除、标记压缩。JVM中不同的垃圾回收器都是基于这些基本算法实现的,不同的垃圾回收器区别在于:选择的算法不同,实现时后台线程采用的并行/并发方式不同。

  • 垃圾回收针对的是堆空间

  • 目前垃圾回收算法主要有两类:

    • 引用计数法:在堆内存中分配对象时,会为对象分配一段额外的空间,这个空间用于维护一个计数器,如果对象增加了一个新的引用,则将增加计数器的值;如果一个引用关系失效,则减少计数器的值。当一个对象的计数器的值变为0,则说明该对象已经被废弃,处于不活跃状态,可以被回收。引用计数法需要解决循环依赖的问题
    • 可达性分析法(也称为根引用分析法),基本思路就是通过根集合(root set)作为起始点,从这些节点出发,根据引用关系开始搜索,所经过的路径称为引用链,当一个对象没有被任何引用链访问到时,则证明此对象是不活跃的,可以被回收。在JVM中常见的根(root)有线程栈帧(thread frame,用于跟踪线程中活跃对象)、符号表(symbol dictionary)、字符串表(string table)、对象监视器(object synchronizer)、元数据对象(universe)等,这些根共同构成了根集合。
  • JVM的垃圾回收采用了可达性分析法。垃圾回收算法也在不断地演化,按照不同的标准有不同的分类:

    • 从垃圾回收算法实现主要分为复制(copy)、标记清除(mark-sweep)和标记压缩(mark-compact)。
    • 从回收方式上可以分为串行回收、并行回收、并发回收。
    • 从内存管理上可以分为代管理和非代管理。
  • JVM垃圾回收器基于分代管理和回收算法,结合回收的方式,实现了串行回收器、并行回收器、CMS、G1、ZGC和Shenandoah。

  • 从程序执行方式的角度可以分为以下3类:

    • 串行执行:应用程序和垃圾回收器交替执行,垃圾回收器执行的时候应用程序暂停执行。串行执行指的是垃圾回收器有且仅有一个后台线程执行垃圾对象的识别和回收。
    • 并行执行:应用程序和垃圾回收器交替执行,垃圾回收器执行的时候应用程序暂停执行。并行执行指的是垃圾回收器有多个后台线程执行垃圾对象的识别和回收,多个线程并行执行。
    • 并发执行:应用程序和垃圾回收器同时运行,除了在某些必要的情况下垃圾回收器需要暂停应用程序的执行,其余的时候在应用程序运行的同时,垃圾回收器的后台线程也运行,如标识垃圾对象并回收垃圾对象所占的空间。
  • JKD7 默认垃圾收集器Parallel Scavenge(新生代)+Parallel Old(老年代)

  • JKD8 默认垃圾收集器Parallel Scavenge(新生代)+Parallel Old(老年代)

  • JKD9的默认垃圾回收器是G1

  • JDK15 正式删除CMS。JDK11就已经把CMS标记为过期。JDK9开始使用G1作为「默认」的垃圾回收器(JDK11中ZGC开始崭露头角)

  • Minor GC:又称新生代GC

  • Full GC:又称为Major GC或老年代GC

垃圾收集器 种类

  1. Serial 垃圾收集器(单线程、复制算法)[‘sɪriəl]
  2. ParNew 垃圾收集器(Serial+多线程)【Serial收集器的多线程版本】
  3. Parallel Scavenge 收集器(多线程复制算法、高效)[‘perə.lel] [‘skævəndʒ] 【“吞吐量优先”收集器】
    自适应调节策略也是 ParallelScavenge 收集器与 ParNew 收集器的一个
    重要区别。
  4. Serial Old 收集器(单线程标记整理算法 )【Serial收集器的老年代版本】
  5. Parallel Old 收集器(多线程标记整理算法)【Parallel Scavenge收集器的老年代版本】
  6. CMS 收集器(多线程标记清除算法) (Coucurrent Mark Sweep)【并发收集、低停顿】
    最主要目标是获取最短垃圾回收停顿时间
  7. G1 收集器 (Garbage-First)【面向服务端应用的垃圾收集器】
  • 相比与 CMS 收集器,G1 收集器两个最突出的改进是:

    • 基于标记-整理算法,不产生内存碎片。
    • 可以非常精确控制停顿时间,在不牺牲吞吐量前提下,实现低停顿垃圾回收。
  • CMS收集器的关注点尽可能地缩短垃圾收集时用户线程的停顿时间,而Parallel Scavenge收集器更关注系统的吞吐量(Throughput)。

  • CMS

    • 多线程,使用标记清除算法。并发收集,垃圾回收停顿时间短。但对CPU资源较敏感,CPU核心较少时会导致并发标记、并发清除时吞吐量骤减;无法处理浮动垃圾,老年代空间不能完全使用,需要预留一部分空间;由于基于标记清除算法,会产生大量空间碎片,将会给大对象分配带来很大麻烦,往往会出现老年代还有很大空间剩余,但是无法找到足够大的连续空间来分配当前对象,不得不提前触发一次Full GC。
  • G1

    • 多线程,从局部(两个Region之间)来看,是基于复制算法,从整体来看是基于标记整理算法,不会产生空间碎片,相比于CMS,可预测停顿时间。适合大堆,追求低停顿。

JVM GC参数

  • -XX:+UseSerialGC 开启此参数使用Serial & Serial Old搜集器(client模式默认值)
  • -XX:+UseParNewGC 开启此参数使用ParNew & Serial Old收集器
  • -XX:+UseParallelGC 开启此参数使用parallel scavenge & Serial old收集器(server模式默认值)
  • -XX:+UseParallelOldGC 使用Parallel scavenge & Parallel old收集器
  • -XX:+UseConcMarkSweepGC 使用ParNew & CMS收集器 (使用ParNew + CMS + Serial Old的收集器组合进行垃圾回收,Serial Old作为CMS出现Concurrent Mode Failure失败后的后备收集器)
  • -XX:+UseG1GC

总结和适用场景

CMS

  • 以获取最短回收停顿时间为目标的收集器。
  • CMS收集器工作时,GC工作线程与用户线程可以并发执行,以此来达到降低收集停顿时间的目的。
  • 优点:并发收集、低停顿
  • CMS收集器之所以能够做到并发,根本原因在于采用基于“标记-清除”的算法并对算法过程进行了细粒度的分解。
  • CMS是老年代的垃圾回收器,在老年代分配不下的时候,触发CMS。
  • CMS的最大问题:CMS会使内存碎片化,老年代产生了很多的碎片,然后从年轻代过来的对象无法找到空间,造成了promotion failed。这时候,CMS既没有机会进行垃圾回收,又放不下新来的对象,在这种情况下,CMS会调用SerialOld来进行垃圾回收。这是一件很恐怖的事情。

G1

  • 致力于在多CPU和大内存服务器上对垃圾回收提供软实时目标(soft real-time goal)和高吞吐量(high throughput)
  • G1适合8/16G以上的内存使用
  • 整体采用标记-整理算法,局部是通过是通过复制算法,不会产生内存碎片
  • G1垃圾收集器相对比其他收集器而言,最大的区别在于它取消了年轻代、老年代的物理划分,取而代之的是将堆划分为若干个区域(Region),这些区域中包含了有逻辑上的年轻代、老年代区域。这样做的好处就是,我们再也不用单独的空间对每个代进行设置了,不用担心每个代内存是否足够。
  • G1重新定义了堆空间,打破了原有的分代模型,将堆划分为一个个区域。这么做的目的是在进行收集时不必在全堆范围内进行,这是它最显著的特点。区域划分的好处就是带来了停顿时间可预测的收集模型:用户可以指定收集操作在多长时间内完成。即G1提供了接近实时的收集特性。
  • 可预测的停顿
  • G1在逻辑上分代,在物理上不分代。G1引入了分而治之的思想,把内存分为一个一个的小块(region)。每个region逻辑上属于下面四种分代中的一种。
  • 四种分代:
    a. Old区:老对象
    b .Survivor区:存活对象
    c. Eden区:新生对象
    d. Humongous区:大对象,如果这个对象特别大,可能会跨两个region。

ZGC

  • 和G1类似,但ZGC的region的大小更加灵活和动态。zgc的region不会像G1那样在一开始就被划分为固定大小的region。
  • zgc的region核心亮点就是:动态。
  • 停顿时间控制在10ms之内停顿时间不会因为堆变大而变长堆大小支持TB级。
  • ZGC在对象回收的吞吐量方面略逊于G1回收器(差距小于15%)
  • Tencent Kona JDK11无暂停内存管理ZGC生产实践
    • 超大堆应用。超大堆(百 G 以上)下,CMS 或者 G1 如果发生 Full GC,停顿会在分钟级别,可能会造成业务的终端,强烈推荐使用 ZGC。
    • 高 SLA 需求的应用。如对响应时间有 P999 时限要求的实时和软实时应用,此类应用无论堆大小,均推荐采用低停顿的 ZGC。

Shenandoah

  • ZGC是Oracle JDK的。而Shenandoah只存在于OpenJDK中,因此使用时需注意你的JDK版本

新生代关系和组合关系

  • 当 CMS回收失败时, 备选 Serial Old GC

Reference

  • 新一代垃圾回收器ZGC设计与实现
  • 弄明白CMS和G1,就靠这一篇了
  • 十种常见的垃圾回收器简介
  • Java 虚拟机系列三:垃圾收集器一网打尽,船新的 ZGC 和 Shenandoah 听说过吗
  • [JVM垃圾回收-垃圾回收的各种分类& 垃圾收集器的组合关系(https://blog.csdn.net/qcl108/article/details/108875189)

《小狗钱钱》-笔记

发表于 2021-08-13

前言

  • 财务自由是今天每个人都可能实现的目标,当然这需要我们拥有追求自己真正想要的生活的勇气。正如一句名言所说:“并非困难使我们放弃,而是因为我们放弃,才显得如此困难。

  • 什么都无法阻挡你将金钱的规律铭记在心,你就会发现自己的财务状况在日益改善。没有任何东西能够阻挡一个顺应时势的想法。这一点也适用于每一个人的生活。什么都无法阻挡你享受自己与生俱来的权利——富裕。富有尊严、财务上游刃有余的生活符合我们的自然法则。只要你不放弃,那就没有任何东西可以阻碍你达到这个目标。就是现在,马上行动起来吧。我们的生活是一次旅行。如果我们掌握了金钱的规律,那么这次旅行就可以为我们开创机遇,并将我们引向那个自己从未想过的方向。

第一章 白色的拉布拉多犬

  • 中国的智者老子说过:‘天下难事,必作于易;天下大事,必作于细。

第二章 梦想储蓄罐和梦想相册

  • 学习就是认识新观念和新想法的过程。假如人们始终以同一种思维方式来考虑问题的话,那么始终只会得到同样的结果。因为我对你讲述的许多内容是你以前从未接触过的,所以我建议你,在你还没有做之前,不要轻易下结论。没有想象力的人是很难成就大事的。我们对一件事投入的精力越多,成功的可能性也越大。可是大多数人把精力放在自己并不喜欢的事情上,而不去想象自己希望得到的东西。

  • 看看你的爸爸妈妈,他们拥有的钱比你零花钱的10倍还要多得多,也许是你的100倍。尽管如此,他们的情况也并不好。钱的数目并不是决定性因素,更重要的是我们怎么来使用它。我们首先必须学会量入为出,只有这样,我们才有能力获得更多的钱。

第三章 达瑞,一个很会挣钱的男孩

  • 第一,为别人解决一个难题,那么你就能赚到许多钱;第二,把精力集中在你知道的、能做的和拥有的东西上。

第四章 堂兄的挣钱之道

  • 你最好想清楚你喜欢做什么,然后再考虑你怎么用它来挣钱。我就是这样想出派送面包这项服务的。

  • “但是,我想提醒你两件重要的事情。”我听见马塞尔说,“第一,无论在什么时候都不能把希望只寄托在一份工作上,它持续的时间不会像你设想的那么长,所以你要立即寻找另一份替代的工作。”

  • 这些困难是你现在还难以预料的。到那时候就能看出来,你到底是一个洋娃娃似的胆小鬼呢,还是一个像我一样能挣很多钱的人。情况顺利的时候,人人都能挣到钱。只有在逆境中,一切才能见分晓。”

第五章 钱钱以前的主人

  • 72小时规定!”“72小时规定?”我紧接着问钱钱。“很简单。当你决定做一件事情的时候,你必须在72小时之内完成,否则你很可能永远不会再做了。

第六章 爸爸妈妈犯下的错误

  • “大多数人都认为工作肯定是一件艰苦而令人不愉快的事情,”他向我解释道,“其实只有做自己喜欢的事情的人,才能真正获得成功。”

  • 钱钱接着说:“第二个忠告是,应当尽可能少地偿还贷款——也就是大人们说的分期付款。

  • 如果每年偿还1%,虽然需要支付的利息逐年减少,但最后总共支付的利息数额仍会达到贷款数额的3倍左右。为了能快一点儿还清1万马克贷款,人们当然选择每年付较高的分期付款。许多人和银行约定的分期付款数额刚好在他们承受能力的上限,因此他们手里的钱一直很紧张。

  • 第三个忠告是针对消费贷款的。消费贷款是与住房无关的贷款。假如人们为了购置新的汽车、家具、电视机或其他用于生活的商品而贷款,就是消费贷款。这时候贷款的人应当遵守的一个原则,就是将不用于生活的那部分钱的一半存起来,另一半用于偿还贷款。”“可是我奶奶常说,债务应当尽快还清,”我想起奶奶的话,“所以应该把所有不用于生活的钱都用来还债。”“当你还清了债务的时候,又达到了什么目标呢?”钱钱问我。“爸爸妈妈总是说,那时候他们肩上的一个重担就可以卸下来了。”我试着给钱钱解释。“他们是这么以为的。”钱钱赞同地点点头,“可是事实上,当他们还清了债务的时候,他们拥有的财产为零,也就是一无所有。一无所有可不是目标呀。”我吃了一惊,问道:“那么目标该是什么呢?”“去美国旅行,买笔记本电脑——这些才是目标,”钱钱耐心地解释给我听,“或者把不花的钱都积攒起来。”

  • 所有的消费贷款都是不明智的。聪明的做法是只把以前积攒起来的财富用于支出。

  • 将扣除生活费后剩下的钱的一半存起来,剩下的一半用于支付消费贷款。最好根本不申请消费贷款。

第七章 在金先生家

  • 你当然也应该得到一点儿什么……让我想想。你照顾钱钱很长一段时间了,确切地说,是一年多。如果我一天付你10马克,你觉得怎么样?”我一点儿都不高兴。我气呼呼地说:“我愿意照顾钱钱是因为我一见到它就喜欢上了它,而不是为了挣什么钱。”金先生笑了,但我并不觉得他是在嘲笑我——这两者之间还是有一点儿细微差别的。他向我解释道:“吉娅,大多数人都是这么想的,我曾经也这么想。可是请你告诉我,你为什么不能因为做了一件自己喜欢的事情而挣到钱呢?”类似的话我已经听过许多次了。的确如此,马塞尔对我说过,汉内坎普先生也说过。尽管如此,我还是觉得心里有些不安。“我要告诉你一件事,”金先生接着说,“恰恰是因为你喜欢我的钱钱,我才要每天付你10马克,因为由此我知道,它在你身边过得很舒服,你也会继续好好地照顾它。正是你的真情实意才让你的劳动显得那样珍贵。”

  • “鹅代表你的钱。如果你存钱,你就会得到利息。利息就相当于金蛋。”我不敢肯定自己是不是真的懂了。金先生接着说:“大多数人生来并没有‘鹅’。这就是说,他们的钱不足以让他们依靠利息来生活。”“可是要靠利息生活的话,这个人肯定得有很多很多的钱才行,是这样吗?”我不解地打断了金先生的话。“你需要的钱其实比你想象的要少得多。”金先生答道,“如果你有2.5万马克,能得到12%的利息的话,那每年就有3000马克。”

  • 那么2.5万马克就是你的‘鹅’,而你是不会‘杀’它的。

  • “你必须作出选择!”金先生点点头,“你可以马上拿出你的钱,用在任何一个地方——比如一旦你有了3000马克,你可以马上飞往加利福尼亚——可是那样的话,你也就‘杀死’了你的‘鹅’;你也可以选择将一部分钱存起来,那样过了一段时间之后,仅靠每年的利息,你就可以飞往加利福尼亚了。”

  • 我已经想好该怎么分配我的钱了,我也要把50%的收入变成我的‘鹅’,40%放入我的梦想储蓄罐,剩下的10%用来花。”金先生看着我,眼光中充满了赞许。

  • 越是把注意力放在疼痛上,我就越会觉得疼。谈论疼痛就像给植物施肥一样。所以我很多年以前就改掉了抱怨的习惯。

第十一章 爸爸妈妈不明白

  • 幸运其实只是充分准备加上努力工作的结果。

  • 你干的活最多只值报酬的一半,另一半报酬源于你的想法和实施这个想法的勇气。

第十二章 陶穆太太归来

  • 要想过更幸福、更满意的生活,人就得改变自身。这和钱无关,金钱本身既不会使人幸福,也不会带来不幸。金钱是中性的,既不好,也不坏。只有当钱属于某一个人的时候,它才会对这个人产生好的影响或者坏的影响。钱可以被用于好的用途,也可以被用于坏的用途。一个幸福的人有了钱会更幸福;而一个悲观忧虑的人,钱越多,烦恼就越多。

  • “我妈妈总说,金钱会使人的本性变坏。”我反驳道。“金钱会暴露一个人的本性,”陶穆太太解释说,“金钱就像一个放大镜,它帮你更充分地展现出你本来的样子。好人可以用钱做很多好事。而如果你是盗贼,那你很可能会把钱挥霍在一些蠢事上。”

第十三章 巨大的危机

  • “我想,你刚刚又找到了一个做有钱人的很好的理由。”钱钱提示我。我疑惑地望着它。“你可以做一个有能力帮助别人的人,而别人也会相信你,愿意接受你的帮助。”钱钱解释说。

第十四章 投资俱乐部

1.确定自己希望获得财务上的成功。
2.自信,有想法,做自己喜欢做的事。
3.把钱分成日常开销、梦想目标和金鹅账户三部分。
4.进行明智的投资。
5.享受生活。

第十五章 演讲

  • 如果你没有做今天这件事情,你就永远不会知道,给自己一些压力之后,你能够做到些什么。一个人觉得最引以为自豪的事情,往往是那些做起来最艰难的事情。这一点你千万不要忘记。

第十六章 俱乐部的投资行动

  • 基金就像一口大锅,许多没有时间、没有相关的知识或者没有兴趣亲自去买股票的投资人都会把钱投进这口锅里,这些钱由金融界的专家——也就是所谓的基金经理人——去投资买股票。国家对此进行严格的监督,基金经理人必须遵守一定的规定。比如,他们至少必须购买20种不同的股票。

  • 如果我们打算投资买基金,就要准备把自己的钱在里面放上5~10年。对于那些能等这么长时间的人来说,基金几乎是一种零风险的投资。”

  • 挑选基金时的注意事项:1.基金应该至少有10年历史。假如它在这么长时间内一直有丰厚的利润,那我们可以认为,未来它也会运作良好。2.应该选择大型的跨国股票基金。这种基金在世界各地购买股票,以此分散风险,所以十分安全。3.对基金的走势图进行比较。我们应该观察在过去10年间哪些基金的年终利润最好。

  • “有一个相当简单的公式,如果你们运用这个公式的话,可以不用去看那些又复杂又麻烦的表格。它的名字叫72公式。”老太太讲解说,“你们直接用72除以你们投资的年利润百分比,得出的数字就是这笔钱翻一倍所要的年数。”“啊?”莫尼卡嘟哝了一声。“72除以12是多少?”陶穆太太问。“6。”马塞尔飞快地算了出来。“对!这就是说,如果你们每年能得到12%的利润,6年以后你们的钱就会翻一倍。”马塞尔一边思考,一边说:“要想知道15%的时候会怎么样,那我就必须用72除以15,等于4.8年。”

  • “简单地说,如果你们投资的收益率是15%,差不多5年以后,钱就会翻一倍。

  • ”陶穆太太说道,“也就是说,如果将来我们也能得到15%,那5年以后我们的2万马克就会涨到4万马克,10年以后就会是8万,15年后是16万,而20年后是32万。”

第十七章 爷爷奶奶害怕风险

  • 只有当我们把它卖出的时候,才会有亏损。可是我们并没有这么做。

  • 现在我们可以用比实际价值低的价钱购买股票和基金。不久以后,又有人会愿意付出相当于它们实际价值的钱,把它们买进。这样我们就会大赚一笔。

  • 银行存折肯定不是保存钱的最合适的地方,金先生总是把银行存折叫作“吞钱机器”。

  • 你要理解你的爷爷奶奶,他们是为你好。他们只是想让你免受损失,想尽他们所知来帮助你。

  • 到了他们这个年纪,很可能有过几次吃亏的经历。现在他们想保护自己,也保护你。这是可以理解的。不过说真的,你应该多谢你的爷爷奶奶,因为他们可能帮你避免了一个错误。”“避免了什么错误?”“我觉得,你们现在用2万马克再次买进基金不是一个好主意。我认为最多1万马克就够了。”

  • 可是如果行情继续下跌怎么办呢?所以你最好不要投入太多的钱。而且如果行情真的继续下跌,那时要是你手头还有钱用来再一次买进的话,不是更好吗?”“但我们并不知道,行情是不是真的还会继续下跌。”“没错,我们是不知道。没有人能知道,所有试图预测未来走势的专家总是计算失误,意想不到的情况很多。正因为如此,你应该始终储备一些现金。决不能把你全部的钱都投资在股票或者基金上面。”

  • 我怎么知道通货膨胀率有多高,会吃掉我多少钱呢?”“目前是3%左右。如果你现在想计算具体的数目,我可以告诉你一个相当简单的公式,就是72公式。这个公式很实用,我们可以通过它计算出自己的钱翻一倍需要多少年,也可以用来帮助我们计算通货膨胀。它可以告诉我们,在一定通货膨胀率下,我们的钱在多长时间后会贬值一半。按72除以3%的通货膨胀率计算,得到24,就是说24年以后,你的钱只值现在的一半。”

  • 没错!所以我把存折叫作‘吞钱机器’。因为你从这里得到的利息连通货膨胀带来的损失都抵消不了

  • 但是我们几乎没有别的选择。你总不能把你所有的钱都投资买股票。就算你还很年轻,也该留一些现金做储备。只有这样才能达到分散风险的最佳效果。”

  • 当然也有一些储蓄种类的利率比较高,可是你必须把钱放在银行里存很长一段时间。这种方式的坏处是,碰上再次买进的合适时机,你不能马上采取行动。”“那么,我该拿百分之几的钱投资日拆呢?”“这要根据你的具体情况而定。你还小,20%就够了。”

  • 我决定向金钱魔法师们建议,每人只拿出2500马克用来再次买进基金,剩下840马克,可以投资在日拆上。

第十八章 大冒险的结局

  • 而自从他买了一辆新车之后,甚至比往常提早一个小时就起床了。当一个人不需要再为钱的问题烦心之后,竟然会发生如此巨大的变化,真令人难以置信。

  • 我们买的第一只基金的行情虽然持续下跌了7个月,但我们并没有卖出,所以没有亏损。这以后行情开始爬升,如果我们卖出的话,可以获取不少利润。不过我们没有理由这样做,我们想要的是让我们的鹅不断地长大。马塞尔曾经想过卖出手里的基金,他说这叫提取利润。陶穆太太却问他准备怎样处置这笔钱,怎样让它继续增长。我们得出的结论是:再次选择同样的基金进行投资。于是马塞尔立即意识到现在卖出基金毫无意义。

  • 我想起了金先生对我说过的一句话:不要为失去的东西而忧伤,而要对拥有它的时光心存感激。对我来说,这句话的意思是:从现在开始,我再也得不到钱钱的建议了,但我还是必须应对各种情况。

自力更生——写给成年人的后记

  • 钱钱拆除了偏见的围墙,让人眼前豁然开朗,它告诉我们:经营活动并不是童工劳动,而是一种能激发人的热情的游戏;赚钱并不枯燥,相反会带来激动人心的时刻,释放人的发明创造的才能。

  • 我们推崇一种聪明的、简朴的生活方式。也就是说,宁愿购买一件一流产品,也不要不停地买许多的二流产品。而且,不要仅仅因为一件产品的外观不再时髦而新产品正在流行,就不断追逐新鲜的东西。生活质量不是由越来越多的高科技产品堆砌而成的,而体现在一些别的方面,比如悠闲地享受一下生活,增进邻里关系,

  • 表达感情或者从事艺术性和创造性的活动。

附录

  • 股票是一家公司的股权证明——你可以购买这家公司的一份或若干份股权。每一份股权就是一张股票。如果公司赢利(赚钱),你就可以根据自己所拥有的股份数量,从公司的利润中分得一部分。你的股份越少,分到的利润就越少;股份越多,分到的利润就越多。如果公司亏损,那么你所持有的股票就会贬值。在这种情况下,你不应该急于卖出股票,而应该等待公司重新赢利。在第十六章中,陶穆太太对此有详细说明。

总结

  • 并非困难使我们放弃,而是因为我们放弃,才显得如此困难。
  • 无论在什么时候都不能把希望只寄托在一份工作上,它持续的时间不会像你设想的那么长,所以你要立即寻找另一份替代的工作。
  • 72小时规定
  • 尽可能少地偿还贷款
  • 消费贷款是不明智的
  • 基金

  • 评论:转:聪明理财五大定律
    4321定律:家庭资产合理配置比例是家庭收入的40%用于供房及其他方面投资,30%用于家庭生活开支,20%用于银行存款以备应急之需,10%用于保险。
    72定律:不拿回利息利滚利存款,本金增值一倍所需要的时间等于72除以年收益率。比如,如果在银行存10万元,年利率是2%,每年利滚利,多少年能变20万元?答案是36年。
    80定律:股票占总资产的合理比重等于80减去年龄的得数添上一个百分号(%)。比如,30岁时股票可占总资产50%,50岁时则占30%为宜。
    家庭保险双十定律:家庭保险设定的恰当额度应为家庭年收入的10倍,保费支出的恰当比重应为家庭年收入的10%。
  • 评论:转
    吞钱机器:储蓄利息永远跑不赢通货膨胀。把钱存在银行,你的存款金额看起来是在增加,然而它的实际购买力是在不断降低的。所以,真正富有的人,钱都不会存在银行,而是通过理财投资,跑赢通货膨胀。
  • 评论:投短线叫投机,投长线才叫投资。价格终会回归价值,长期投资价值,才有相对稳定的收益。
  • 评论:转:
    • 关于基金的三原则:
      1. 基金就像一口大锅,许多没有时间、没有相关的知识或者没有兴趣亲自去买股票的投资人都会把钱投进这口锅里,这些钱由金融界的专家——也就是所谓的基金经理人——去投资买股票。国家对此进行严格的监督,基金经理人必须遵守一定的规定。比如,他们至少必须购买20种不同的股票。
      2. 基金符合投资的一切要求。由于它的这些特点,它也非常适合儿童和青少年。如果能够在5~10年内不动用这些钱,基金投资是很保险的,它会带来丰厚的利润……
      3. 基金也符合第三条原则,它很容易操作,几乎就像在银行开一个普通的账户一样简单。
    • 挑选基金时的注意事项:
      1.基金应该至少有10年历史。假如它在这么长时间内一直有丰厚的利润,那我们可以认为,未来它也会运作良好。
      2.应该选择大型的跨国股票基金。这种基金在世界各地购买股票,以此分散风险,所以十分安全。
      3.对基金的走势图进行比较。我们应该观察在过去10年间哪些基金的年终利润最好。

  • 评论:比特币不跌50%以上不入手,入手拿5年!等第5年比特币牛市,比特币不赚10倍不出货!

《深度工作:如何有效使用每一点脑力》-笔记

发表于 2021-08-13

前言

  • 深度工作(Deep Work):在无干扰的状态下专注进行职业活动,使个人的认知能力达到极限。这种努力能够创造新价值,提升技能,而且难以复制。

  • 浮浅工作(Shallow Work):对认知要求不高的事务性任务,往往在受到干扰的情况下开展。此类工作通常不会为世界创造太多新价值,且容易复制。

  • 深度工作假设(The Deep Work Hypothesis):深度工作的能力日益稀少,而几乎同时,其在社会经济中的价值也日益提升。因此,能够培养这项技能,并将其内化为工作生活之核心的人,将会取得成功。

如何在新经济形势下成为赢家

  • 我发现有两类人注定会成功,而且我认为可以推广借鉴:一种是能够利用智能机器进行创造性工作的,一种是自己所在领域的个中翘楚。在数字鸿沟不断扩大的当下,有什么窍门能够为进入此类有利领域提供助力?我认为如下两种核心能力是关键。

    • ·迅速掌握复杂工具的能力
    • ·在工作质量和速度方面都达到精英层次的能力
  • 智能机器的复杂性是难以掌握的。因此,要想较好地运用这些机器,你就要培养出掌握复杂事物的能力。而且由于这些科技变化很快,掌握复杂事物的过程便永远不会结束:你必须能够快速完成,一次又一次。当然,这种迅速掌握复杂事物的能力并不仅仅是能熟练运用智能机器所必需的;基本上也是想要成为任何领域的超级明星的关键因素,即便是与科技关联性很小的领域。

  • 如果你无法学习,就无法成功。

  • 由此我们总结出想要加入当前经济形势下赢家群体的另一项要点:如果你不产出,就不会成功,不管你的技艺多么纯熟,天资多么聪颖。

  • 上文阐述的两种核心能力依赖于你进行深度工作的能力。如果你没有掌握这项基本能力,想要学习艰涩的知识或达到精英水平就会很挣扎。这些能力对于深度工作的依赖性并非即时显现的,这要求我们更深入地探究与学习、专注和生产力相关的科学

深度工作帮助你迅速掌握困难的事物

  • (1)你的注意力全情投入到某个你希望提升的技能或想要掌握的理念上;

  • (2)你能得到反馈意见,这样你就可以调整自己的方法,保持注意力的投入有最佳产出。

  • 刻意练习不能在有干扰的情况下进行,要求在无干扰状态下保持专注。

深度工作有助于精英级产出的实现

  • 高质量工作产出=时间×专注度

  • 工作时专注度达到最高,单位时间里的工作产出也将实现最大化。

  • 当你从某项任务A转移到任务B时,你的注意力并没有即时转移,你的注意力残留仍然在思考原始任务。如果在转移工作之前,你对任务A缺乏控制且关注度较低,残留会尤其浓厚,但即使你在转移工作之前已经完成了任务A,你的注意力还是会有一段分散的时间。

  • 注意力残留的概念有助于解释专注度公式的真实性,因此也有助于解释格兰特的高效产出。格兰特长时间不转移注意力,完成单一困难任务,使注意力残留负面影响降到最低,从而使他在当前任务上的表现成果最优化。换言之,当格兰特与世隔绝数日完成一篇论文时,其效率水平远高于奉行多任务策略的一般教授,这些教授的工作反复受到残留量极大的干扰。

  • 要达到个人巅峰的产出效率,你需要长时间、无干扰地高度专注于单一任务。换一种说法,使你的表现最优化的做法是深度工作。如果你无法做到长时间深度工作,就很难使你的表现达到质量和数量的巅峰,而这种巅峰状态对于你的职业成功越来越重要。除非你的才能和技能全面压制对手,否则对手中的深度工作者定将超越你的表现。

杰克·多西是怎么回事?

  • 杰克·多西没有深度工作而取得成功,在其所处的精英管理层中是很常见的。

  • 明确了这一事实之后,我们必须退后一步提醒自己,这种现象并不会破坏深度工作的普遍价值。为什么?因为这些高管工作中分心的必然性是在其特定工作中特有的现象。从根本上讲,一名优秀的首席执行官就是一部难以自动化的决策引擎,与《危险边缘》游戏中IBM的“沃森”机器人没有太大区别。他们努力积累起丰富的经验库,打磨并证明了自己在市场中的灵敏嗅觉。而后他们全天都必须处理和解决电子邮件、会议、现场考察等纷至沓来的工作。要求一名首席执行官花上4个小时的时间深度思考单一问题浪费了他们的价值所在。最好是聘用三个聪明的副手,深度思考这些问题,然后将解决方案呈递给高管做决策。这种特殊案例很重要,因为这种状况告诉我们,如果你是一家大型公司的高管,或许就不需要听取下述章节中的意见。另一方面,它也告诉我们不能将这些高管的工作方法外推至其他工作中。多西鼓励外界打扰,克里·特雷纳不断查阅电子邮件,虽然有这些案例,但并不意味着你学着他们的做法也能成功:他们的行为是公司领导者这个特定角色所特有的。

  • 我们必须时刻记住,在社会经济的某些角落,深度工作并没有价值。除了高管之外,还有部分类型的销售人员和说客,对他们而言持续联系是其最大价值所在。甚至还有一些人身处深度工作有所助益的领域,却在备受干扰中经过艰苦努力取得成功。

  • 深度工作并非是我们的经济中唯一有价值的技能,不培养这种能力也有可能做得很好,但是不需要深度工作的职业会越来越少。

第2章 深度工作是少见的

  • 发现即使很短暂的干扰也会显著延长完成一项任务所需要的时间。

度量的黑洞

  • 大致说来,由于知识工作者的工作复杂性比体力劳动者高,所以也更难衡量个体努力所带来的价值。

  • 我们不应期望破坏深度工作的行为对底线的影响很容易被察觉。恰如汤姆·考克兰的发现,此类度量属于不透明区,难以轻松衡量。我将这个区域称作度量黑洞。当然,难以衡量深度工作相关的度量,并不意味着我们在商业中就应忽略其作用。

最小阻力原则

  • 谈及工作场所中普通存在的干扰行为,我们必须为占主导地位的联结文化留个位置,这种文化期望人们能够迅速阅读和回复电邮(及相关交流)。

  • 最小阻力原则(The Principle of Least Resistance):在工作环境下,若各种行为对于底线的影响没有得到明确的反馈意见,我们倾向于采用当下最简单易行的行为。

  • 最小阻力原则受到度量黑洞的保护,少有人对其加以审视,在这种原则支配下的工作文化,免去了我们短期内对保持专注和做计划的忧虑,却牺牲了长期的满足感和真实价值的产出。这样一来,最小阻力原则就驱使我们在深度工作愈发受到青睐的经济形势下流于浮浅工作。

忙碌代表生产能力

  • 经理栖居于一片迷茫的精神领地,受冥冥中难以捉摸但必须应答的命令驱使而焦躁不安。

  • 我认为知识工作者越来越多地表现为可视的忙碌,是因为他们没有更好的方法证明自身价值。我们来给这种倾向性起一个名字。

  • 忙碌代表生产能力(Busyness as Proxy for Productivity):在工作中,对于生产能力和价值没有明确的指标时,很多知识工作者都会采用工业时代关于生产能力的指标,以可视的方式完成很多事情。

  • 这种思维方式为很多有损深度的行为之盛行提供了又一种解释。如果你随时都在收发电子邮件,如果你不断安排、参加会议,如果有人在Hall之类的即时通讯系统中发布一个新问题,让你在几秒钟内就参与其中,又或者你在开放式办公室中漫步,随时向遇到的人道出自己的想法——所有这些行为都可以使你在公众眼里看似很忙碌。如果你将忙碌看作生产能力,那么想要自己和他人信服你的工作做得很好,这些行为就至关重要。

对互联网的顶礼膜拜

  • 已故传播学理论学者、纽约大学教授尼尔·波兹曼(Neil Postman)提出的一个警告中找到基础。20世纪90年代初期,个人电脑革命首次进入快车道,波兹曼在文章中辩称我们的社会与科技的关系愈发令人不安。他写道,我们不再权衡新科技的利弊,不再平衡新增效益和新引入的问题之间的关系。我们开始自以为是地认定,只要是高科技就是好的,而不用再做探讨。他将这种文化称作技术垄断(Technopoly),在提出警醒时也没有拐弯抹角:“技术垄断阻断了其他选择,

  • 在技术垄断的时代,深度工作有很大的劣势,因为它所创建的品质、匠心和通达等价值都是传统的,与技术垄断无关的。更糟糕的是支持深度工作往往要抵制新的高科技。

对生意来讲是坏事,对个人来讲是好事

  • 包括深度工作很难,浮浅工作更简单;当工作中没有明确目标时,围绕浮浅工作的表面忙碌会成为一种本能;还有在我们的文化中已经形成了一种信念,认为与“网络”相关的行为都是好的,不论其对我们创造有价值事物之能力有何影响。由于深度工作价值以及忽略深度工作所造成的损失很难直接衡量,这些潮流才会大行其道。

  • 本书的终极目标:系统地培养个人进行深度工作的能力,并由此获得丰富的成果

从神经学角度论证深度

  • 你的为人、你的思考、你的感受和所做之事,以及你的喜好,恰是你所关注事物的概括。”

  • 从神经学角度来看,靠浮浅事务度过的一天很可能会是枯燥、令人沮丧的一天,即使抓住你注意力的浮浅事务看似无害甚至有趣。

从心理学角度论证深度

  • 一个人的身体或头脑在自觉努力完成某项艰难且有价值的工作过程中达到极限时,往往是最优体验发生的时候。

  • 具有讽刺意味的是,工作其实比休闲时光更容易带来享受,因为工作类似于心流活动,有其内在目标、反馈规则和挑战,所有这些都鼓励个人积极参与到工作中,专注其中,全身心投入到工作里。休闲时光则组织松散,需要很大的努力才能创造出值得享受的事情。

深度智人

  • “我将活出专注的人生,因为这是最好的选择。”

第二部分 准则

  • 你的意志力是有限的,它在使用的过程中会被不断消耗。换言之,你的意志力并非性格的展现,可以无限制地使用;相反,它恰如肌肉一般,会疲劳。

  • 培养深度工作的习惯,关键在于越过良好的意图,在工作生活中加入一些特别设计的惯例和固定程序,使得进入并保持高度专注状态消耗的意志力最小化。

选定你的深度哲学

  1. 禁欲主义哲学的深度工作
  2. 双峰哲学的深度工作
  3. 节奏哲学(更符合人类的真实天性、适合常规办公室工作)
  4. 记者哲学
  • 克努特采用的是我所谓的禁欲主义哲学的深度工作日程安排。这种哲学通过摒弃或最小化浮浅职责,从而实现深度工作的最大化。禁欲主义哲学的实践者往往有明确且价值极高的职业目标追求,而且他们在职业上取得的大部分成就都是由于工作表现特别突出。

  • 适用禁欲主义哲学的个人是有限的。如果你不属于这个群体,也大可不必太过嫉妒。如果你属于这个群体——对世界的贡献是实在的、清晰的、可以个体化的[插图],那么你就应该认真考虑一下这种哲学,因为这种哲学或许会成为决定性因素,决定你完成的是一个庸庸碌碌还是能为后人所铭记的职业生涯。

  • 我将荣格这种方式称作双峰哲学的深度工作。这种哲学要求你将个人时间分成两块,将某一段明确的时间用于深度追求,余下的时间做其他所有事情。在深度时间里,双峰工作者会像禁欲主义者一般工作——追求高强度、无干扰的专注。在浮浅时间里,专注并非首要目标。这种划分深度和开放时间的做法可以在多个时间层级上实现。

  • 节奏哲学。这种哲学认为轻松启动深度工作的最好方法就是将其转化成一种简单的常规习惯。换言之,其目标是创造一种工作节奏,让你不需要你投入精力便可以决定是否需要以及何时需要进入深度状态。链条法是节奏哲学深度工作日程安排的典型例子,因为这种方法结合了一种简单的启发式调度(每天都要做这项工作)和一种提醒你做这项工作的简单方法:日历上的大红X。

  • 实施节奏哲学的另外一种常见方式是拿掉链条法中的视觉辅助工具,转而设定一个启动时间,每天在这个时间开始深度工作。

  • 节奏哲学与双峰哲学形成了一种有趣的对比。节奏哲学下或许难以达到双峰哲学追随者喜好的最高强度深度思考。然而,这种方法的好处在于更符合人类的真实天性。节奏日程安排者通过雷打不动的惯例支持深度工作,确保能够定期完成一定的工作,在一年的时间里往往能够累积更多的深度工作时长。

  • 对于很多人来说,并非因为自控原因才倾向于选择节奏哲学,而是由于现实中某些工作的确不允许你在需要深入的时候一连消失几天。

  • 常规办公室工作的深度工作者最常选择节奏哲学的原因吧。

  • 我将这种在日程安排中随时可插入深度工作的方法称作记者哲学

  • 我个人也偏爱记者哲学的深度工作,因为这也是我将各项工作安排到日程中所采用的主要方法。换言之,我在深度工作中不是禁欲主义(尽管偶尔我也会嫉妒同行计算机科学家唐纳德·克努特完全与世隔绝但却不用心怀歉意),我也不会像双峰主义者一样接连安排多天的深度工作时间,此外,尽管我很有兴趣采用节奏哲学,但是我的日程安排已经很满,没办法压缩出时间施行这种习惯。我更多的时候如艾萨克森一样,面对每周的工作,竭尽可能压缩出更多的深度工作时间。比如,写作本书的过程中,我会充分利用任何一小段空闲时间。如果我的孩子睡着了,我就会拿出笔记本,把自己锁到书房里。如果妻子在周末要去附近的安纳波利斯拜访她的父母,我就会抓住有额外的人照看孩子的机会,躲到他们房子的一个安静角落去写作。如果工作中有一次会议取消了,或是下午没有安排,我就会来到学校里最喜欢的一间图书馆里,写上几百个词。诸如此类。

习惯化

  • 伟大的创造性头脑如艺术家般思考,却如会计般工作。”

不要独自工作

  • 深度工作与协作之间的关系非常微妙。然而,这种关系值得我们花时间去解开,因为恰当展开协作可以提升你在职业生活中深度工作的质量。

  • “我们鼓励员工到开阔区域工作,因为我们相信意外发现——员工互相协作会产生新想法。”

  • 在提倡专注还是意外发现之间做出选择,暗示了深度工作(个体努力)无法与创造性洞见(协作努力)相容。然而这个结论是有缺陷的。我认为这个结论的基础是对偶然创造力理论的片面理解。

  • 偶然创造理论似乎非常符合这些历史记录。我们可以颇有信心地争辩道,晶体管的发明或许需要贝尔实验室的支持,将固态物理学家、量子论理论学家和世界一流的实验物理学者汇聚一堂,互相学习各自的专长,得到偶然的意外发现。这项发明不太可能由某位容身卡尔·荣格那石塔一样的学术处所深度思考的科学家完成。

  • 这种隔音办公室与宽阔公共空间的组合,形成了中心辐射型的创新建筑结构,在这里偶遇的意外发现和与世隔绝的深度思考都能实现。这种设置囊括了两个极端,一方面我们能找到独立的思考者,没有外界激发灵感,但也少了外来的干扰,另一方面我们能看到在开放式办公室里互相协作的思考者,灵感不断,却也缺乏将其付诸实践的深度思考。

  • 如果将注意力转回到20号楼和贝尔实验室上,我们就能发现,二者的建筑设计也有同样的特点。二者都不同于现代的开放式办公室布局,而是利用标准的私人办公室结合共享走廊的方式。

  • 他们的创造性魔力更多的是由于这些办公室共享少量的长联通空间——迫使研究人员在不同地点来往时互相交流。换言之,这些宽大走廊提供了高效中枢。

  • 因此,我们可以抛弃摧毁深度工作的开放式办公室概念,但是可以保留激发偶然创造力的创新产出理论。关键在于保持一种中心辐射型的布局:时常来到中枢区域与他人交流想法,同时也保留独立的辐射区域,可以在其中完成深度工作,处理偶遇的想法。

  • 总结一些关于深度工作中协作之作用的实用结论。20号楼和贝尔实验室的成功证明与世隔绝并非有效深度工作所必需的条件。事实上,这两个例子表明,对于很多类型的工作而言——特别是追寻创新的——协作深度工作可以产出更好的效果。因此,这种策略要求你思考这种选择,考虑如何更好地将深度融入你的职业生活中。

  • 在深度工作时,恰当的时机可以采用协作的方式,因为这样可以推动你的成果提升到一个新档次。与此同时,也不要过分追求交流和积极的偶遇,以免破坏了专注的状态,因为我们最终还是靠专注从包围在我们周围的各种想法的漩涡中提取有用之物。

像经商一样执行

  • 据克里斯坦森回忆,格鲁夫在一次会间休息时问他:“我该如何去做这件事?”克里斯坦森就与他探讨了商业策略,向格鲁夫解释说他可以成立一个新的公司业务单元之类的。格鲁夫生硬地打断了他:“你真是个天真的学院派啊。我问你如何做,你却告诉我应该做什么。我知道自己该做什么。我只是不知道如何做而已。”

  • 克里斯坦森后来解释说,这种什么与如何的区别非常重要,但在职业世界中往往容易被忽略。找出实现某个目标的战略往往很简单,但是真正引领公司上行的反而是确定了战略之后该如何实施战略。

  • 4DX框架下的4种原则,针对每一种原则我都会介绍自己是如何将其加以改进,以应对深度工作习惯培养中的特定问题。

    • 原则1:关注点放到极端重要的事情上正如《高效能人士的执行4原则》的作者所说的:“你想做的事情越多,完成的事情反而越少。”这句话阐述的意思是,执行需要专注于少量“极端重要的目标”。这样简化选择,有助于组织和集中足够的精力来达成实在的成果。
    • 原则2:抓住引领性指标确定了极端重要的目标之后,你需要衡量自己的成功程度。在4DX框架下,有两种衡量指标:滞后性指标和引领性指标。滞后性指标用于描述你最终尝试改善的方面
      • 引领性指标则“衡量了实现滞后性指标的新行为”
      • 引领性指标引导你将注意力转移到提升你在短期内可以直接控制的行为上,并会对你的长期目标带来积极的影响。
      • 对于专注于深度工作的个人而言,确定相应的引领性指标非常容易:专注于极度重要目标上的深度工作状态时间
    • 原则3:准备一个醒目的计分板“计分的时候,人们的表现很不同。”4DX的作者解释道。当驱使你的团队专注于所在组织的极度重要目标时,在一个公开的地方记录、跟踪他们的引领性指标非常重要。这个计分板可以制造一种竞争氛围,驱使他们专注于这些指标,即使其他诉求吸引他们注意力的时候也不例外。此外计分板还可以强化动机。一旦团队注意到他们在引领性指标上的成功,他们就会很投入地保持这种状态。
    • 在前述的一项原则中,我提出要想一个个体专注于深度工作,用于深度工作的时间应该作为引领性指标。因此这个人的计分板应该是工作场所的人工制品,显示这个人当前的深度工作时间。在我早期的4DX实验中,我设定了一种简单但有效的计分方式。[插图]我拿出一张卡片纸,将其剪成条,每一条记录当前学期的一个周。而后我在每一条卡片上记下了每周的日期,将其贴在电脑显示器旁边的墙上(在这里我无法忽略它的存在)。每一周我都在当周的卡片上简单地做个标记,记录当周的深度工作时间。为了使这个计分板带来的动力最大化,每当我的学术论文取得重要进展时(比如解决了一项关键论证),我就会在实现这个成果的那个小时标记上画一个圈。这么做有两个目的。首先,这样可以使我从本能层面将累积的深度工作时长与实在的结果相联系。第二,这样有助于我校准个人对于完成每项成果所需深度工作时间的期望。这种做法(比我最初想象的要更有效)激励我每周都挤出更多的深度工作时间。
    • 原则4:定期问责4DX的作者详细阐释了保持专注于引领性指标的最后一步是要“保持节律性地与同享极度重要目标的团队会面”
    • 对于专注于深度工作的个人而言,很可能没有任何团队去会面,但是这并不意味着你就可以免去定期问责的过程。贯穿本书,我都在探讨、推荐每周回顾的习惯,在每周回顾的过程中你可以指定下一周的工作计划(参见准则4)。在实验4DX的过程中,我每周回顾自己的计分板,庆贺表现好的一周,理清是什么导致了表现糟糕的一周,而且最重要的是找出能够确保未来几天得到好分数的方法。此举使我不断调整日程计划,以满足引领性指标的要求,大幅增加深度工作时间。
  • 在整个4DX的实验过程中,目标的明晰性,辅以引领性指标计分板提供的简单但却难以回避的反馈,促使我达到了此前从未实现的深度状态。现在回想起来,这并不是因为我的深度工作强度提升了,而是因为它变得更规律了。以前我常常将深度思考的过程放到论文截稿期前,而4DX习惯能帮助我的头脑全年都保持专注。我必须承认,那一年令我筋疲力尽(特别是我还在同时写作本书)。但是那一年的经历也同样是对4DX的有力认可:到2014年夏天,我有9篇论文被接收,比此前任何一年的成果都要多上一倍

图安逸

  • 大多数情况下,你在浮浅工作上投入越多的时间,就能完成越多的工作。但是作为一名作家和艺术家,克莱德尔关注的是深度工作——能够创造出对世界有价值的苦功夫。他坚信完成这样的工作需要定期放松大脑。

  • 一种更实际但也同样有很好启发性的方法:工作日结束的时候,在第二天早晨到来之前,屏蔽掉对工作问题的担忧——晚饭后不要查电子邮件,不要回顾白天的对话,也不要筹划如何处理即将到来的挑战;彻底屏蔽与工作相关的思考。如果需要更多的时间完成工作,就加一下班,但是一旦屏蔽工作之后,大脑就必须放松,如克莱德尔享受金凤花、椿象和星星一样

  • 安逸时光之价值背后的科学道理。仔细研究文献,发现下面三种原因或许可以解释这种价值。

    • 原因1:安逸时光有助于提升洞察力
      • 积极刻意地去思考这些决定结果反而会更糟糕,不如在了解相关信息之后转移到其他事情上,让潜意识去考虑这些事。
      • 无意识思维理论(Unconscious Thought Theory, UTT)——理解有意识和无意识思考在决策中所起作用的一种尝试。从更高层的角度上,这个理论提出在需要严格规则应用的决策中,必须要采用有意识思维。比如,如果你需要做数学计算,只有有意识思维才能严格按照数学运算法则得出正确结果。另一方面,对于涉及大量信息和多项模糊不清之处,甚至存在矛盾和约束条件的决策,无意识思维或许更适合。
      • UTT推测这种现象是由于大脑的这些区域有更多的神经频宽,可以处理更多的信息,相比有意识思考中心能够筛取更多的潜在解决方法。在这种理论下,你的有意识思维就好似家庭电脑一样,可以通过某种事先编写好的程序,得出有限数量问题的正确答案;而你的无意识思维就好像谷歌庞大的数据中心,通过数据算法在兆兆字节无结构可循的信息中筛选,为困难问题找出出乎人们意料的解决方案。这项研究的结果显示,给有意识的头脑休息的时间可以激活无意识头脑,从而理清最复杂的职业挑战。因此,屏蔽工作的习惯并不一定会降低你高效工作的时间,反而会使你开展的工作类型多样化。
    • 原因2:安逸时光有利于补充深度工作所需的能量
      • 对于本书的目的真正重要的是认识到ART不仅限于从自然中获得的益处。这个理论的核心机理在于,自主性引导注意力的能力可以得到复原,只要你能停下相应的活动一段时间。在自然中行走可以得到这样的精神放松,因此任何放松行为都可以有同样的效果,只要能够提供类似的“天然引人入胜的刺激”,能够暂时放下自主性专注状态。与朋友轻松地交谈,做晚饭的同时听听音乐,和孩子玩玩游戏,跑跑步——在你屏蔽工作的晚上可以填满时间的各种活动——与在自然中行走都有同样的注意力复原作用。另一方面,如果你整个晚上不停地查看、回复电子邮件,或是晚饭后又安排几个小时赶上即将到期的进度,你就剥夺了自主性注意力复原所必需的无干扰休息。即使中间做的这些工作只用去了很少的时间,也会使你无法达到注意力复原所需的深度放松。只有彻底放下,直到第二天开始之前不再工作,才能说服你的大脑充分放松,开始补充能量,为接下来的一天做好准备。换一种说法,晚间挤出一点时间工作可能会降低你第二天的工作效率,以致最后完成的工作比屏蔽工作还要少。
      • 你每天处于深度工作状态的时间是有限的。如果你的日程安排足够合理(比如,利用准则4中介绍的一些策略),工作时间应该就已经达到了每日深度工作的极限。因此,进一步说来,到夜里你已经没有足够的精力做到有效的深度工作了。任何可以在夜里做的工作都不会是高价值产出的活动,不会对你的事业精进带来真正的益处;你此时的努力应该局限到低价值的浮浅任务上(以一种缓慢、低能耗的节奏进行)。换言之,推后了夜间的工作,你不会有什么重要的损失。
      • 首先必须接受这种承诺,一旦工作日终了,就不能让任何职业相关的事情侵扰你的注意力,再小的也不可以。这其中尤其包括查看电子邮箱,以及浏览与工作相关的网站。上述两种情况下,即使短暂的工作侵扰也会形成一种自我强化的干扰流,持续长时间阻碍前文描述的屏蔽优势
  • 定期休息大脑可以提升深度工作的质量。工作时,努力工作。完成时,就放松下来。

  • 不要不断分心,而要不断专注

  • 使用令人分心的网络工具这件事本身,并不能减损你大脑专注的能力。实际上减损这种能力的行为是,稍有无聊或遭遇一点点认知上的挑战,就从低刺激、高价值的活动转向高刺激、低价值的活动,这使得你的大脑不能容忍没有新奇性的东西。

像罗斯福一样工作

  • 有一点需要提醒的是,一定要给自己设定一个几乎不可能的时间期限。你应该总是可以赶在最后期限前完成任务(至少是接近),但是这期间需要你用上吃奶的力气。

  • 这个策略的主要意图很明显。深度工作需要专注的强度远远超出了大部分知识工作者的舒适区。

  • 从某种意义上说,罗斯福冲锋配以截止期限,为大脑控制注意力的部分提供了反复的训练,可以系统性地提升你平时的成就水平。另一个益处就是这些冲锋不与分心兼容(在分心的情况下,你是不可能赶在截止期限前完成任务的)。因此,每一次的冲锋都是一个抵抗新奇刺激的过程:你心底里感到无聊,并且真的想寻求更多的新奇刺激,但是你得抵抗。正如我们在前边所讨论的,你抵抗这种冲动的实践越多,你的抵抗力就越强。

  • 在应用这个策略几个月之后,随着前所未有的高强度体验,你对于专注的理解也会改变。如果你像年轻的罗斯福一样,就可以把由此节省出来的时间投入到生活中的赏心乐事中,比如说试着打动那些眼光老辣的纳塔尔鸟类学俱乐部成员。

有成果的冥想

  • 有成果的冥想的目标是:在身体劳作而心智空闲的时候(比如走路、慢跑、开车、淋浴),将注意力集中到一件定义明确的专业难题上。因个人专业不同,这个难题可能是为一篇文章列提纲,写一篇讲话稿,推演一个证明,或者是打磨一个商业策略。如同佛教的打坐,你的注意力可能会涣散或停滞,但你必须不断的把它重新集中到当前的问题上。

  • 住在波士顿时,在每天上下班的过河旅途中,我至少做一次有成果的冥想。随着这方面能力的提高,我的成绩也有了提升。比如说,在步行的路上,我想好了上一本书的大部分章节的提纲,也在攻克学术研究方面的棘手问题上取得了进展。我建议你在生活中采用有成果的冥想。你并不需要每天进行严格的练习,一周进行至少两到三次即可。幸运的是,为这个策略找时间是简单的,因为这只需要你利用本可能被浪费掉的时间(比如说遛狗和通勤)。如果一切顺利,这种做法可以提升你在专业上的产出,却不需要占用你的工作时间。实际上,为了用有成果的冥想来解决你当前最紧急的问题,你甚至可以考虑在工作时间安排一次散步。

  • 然而,我在这推荐有成果的冥想并不仅仅是因为它能提高生产效率(这当然已经很好了),而是因为它可以迅速提高你深度思考的能力。按照我的经验,有成果的冥想可以帮助你实现在本准则引言中介绍的两个关键理念。它通过迫使你抵抗分心,不断地把自己的注意力集中到一个定义明确的问题上,来增强你抵抗分心的心智。通过迫使你在一个问题上不断深入研究,助力专注能力的提升。为了更好地利用有成果的冥想,有一点必须要注意,那就是和其他的冥想一样,这种能力需要实践来磨炼。

准则3 远离社交媒体

  • 我们越来越深刻地意识到这些工具把我们的时间碎片化,削弱了我们集中注意力的能力。这一点现在几成定论。

  • 在训练自己的同时沉迷于手机应用和网页浏览中,那么你的努力就可能会事倍功半。一个人的意志力是有限的,你的工具对你越有吸引力,你就越难在重要的事情上集中注意力。因此,要掌握深度工作的艺术,你必须摆脱各种各样的诱惑,重新掌控自己的时间和注意力。

  • 当前知识分子讨论网络工具和注意力的问题时表现出来的无能。明明知道这些工具在压榨自己的时间,瑟斯顿却不知所措,他感觉自己唯一的选择就是(暂时的)完全戒掉网络。这种认为应对社交媒体和娱乐信息节目分散注意力问题的唯一方法就是选择激进的“网络假期”[插图]的想法,正逐渐占据我们文化讨论的主流。这种二元论的处理问题方法存在的缺点就是这两种选择都太残忍,因此不可能有用。很显然,认为可以戒掉网络的观点属于冠冕堂皇地偷换概念,对大多数人来说是不可行的(除非你是一名正在尝试写一篇关于分散注意力事物的记者)。没人会真的效仿巴拉唐德·瑟斯顿的做法——这个事实也证明了另一个选择的正确性:认识到我们目前注意力被分散的状态是不可避免的并接受现实。

  • 认识到这些工具并不完全是邪恶的,有些甚至对你的成功和幸福是十分重要的;然而与此同时,也意识到应该对那些能够经常占用你时间和注意力(更不要说个人信息)的网站设立一个严格的限制标准,大部分人应该更少地使用此类工具。换言之,我不会要求你像2013年的巴拉唐德·瑟斯顿那样完全戒掉网络25天,但是我会要求你避免那种促使他开展激进实验的状态——注意力分散并且超高度依存于网络。网络的使用存在着一个中庸状态。如果你对深度工作的习惯感兴趣,你必须努力争取达到这个中庸状态。

  • 选择网络工具的“任何益处法”:一旦发现使用一款网络工具有任何可能的益处,或者是不使用就可能错过某些事,你就觉得有足够理由使用这款网络工具。 很显然,该方法的缺点就是忽视了伴随这款网络工具而来的各种弊端。这些网络工具具有致瘾性——从那些对实现职业和个人目标有更直接帮助的活动中(比如说深度工作)抢走时间和注意力。如果过度使用这些工具,你将陷入精疲力竭、注意力散乱的网络依赖状态,就是这种状态使巴拉唐德·瑟斯顿和像他一样数以百万计的人们饱受煎熬。这就是任何益处思维定式带来的不容易察觉的危害。使用网络工具也是可以带来害处的。如果你不努力权衡利弊,一看到可能的益处就决定不加限制地使用某种工具,那么你就可能在不知不觉中失掉了在知识工作世界里取得成功的能力。客观来讲,这个结论并不令人惊奇。在网络工具的背景下,我们已经习惯了任何益处的思维定式,但是如果我们放宽视界,在熟练劳动[插图]的维度下思考这种思维定式,我们就会发现这是一种诡异的、目光短浅的工具选择方法论。

  • 工具选择的手艺人方法:明确在你的职业和个人生活中决定成功与幸福的核心因素。只有一种工具对这些因素的实际益处大于实际害处时才选择这种工具。

  • 请注意,此手艺人方法与任何益处方法恰好相对立。任何益处思维定式认为任何潜在的益处都可以成为使用此工具的借口,手艺人方法要求这些益处能够影响到核心因素,并且益处大于害处。

在你的网络使用习惯中采用关键少数法则

  • 问题不是推特是否带来了益处,而是它带来的益处是否足够抵消它所消耗的你的时间和注意力

  • 关键少数法则在许多情境中,80%的已知效果源自20%的可能原因。

  • 有一个正式的数学原理能够说明这一现象(一个80/20的分布大概符合幂律分布,这种分布在现实世界进行测量时会经常遇到),并可以来创造性地提醒大家,在许多情况中,一个结果成因中的诸因素并不是地位平等的。

  • 再进一步,假设这一法则对我们生命中的重要目标也是成立的。

  • 如果公司80%的利润来自20%的顾客,那么公司可以通过从带来低利润的顾客身上节省更多的精力,用于更好地服务少数带来高利润的顾客——花在后者身上每一小时的产出都高于花在前者身上的。这个道理也适用于你的职业和私人生活。把花在低影响力活动上的时间(比如在脸谱网上找老朋友)转投到高影响力的活动上(比如和一位好朋友共进午餐),这样你就能取得目标的更大成功。因此,放弃使用一款网络工具的逻辑是放弃它所能带来的小益处,转而致力于你已经知道的可能带来更大益处的活动。

戒掉社交媒体

  • 我认为社交媒体大行其道的一个原因,就是它打破了努力创作有实际价值的作品和吸引到人们注意力之间的正相关关系。相反的,它用浅薄的集体主义式交换取代了永恒的资本主义交换:如果你注意我说了什么,我就会注意你说了什么,不管这话语有无价值。

不要用网络来消遣

  • 一旦你阅读了其中某一网站的一篇文章,页面旁边或底部的链接会吸引你接着点击,持续点击。人类心理学中任何一个可使用的把戏都用于其中,从把标题列为“流行”或“趋势”到使用醒目的图片,目的就是吸引住你。比如,就在此时此刻,BuzzFeed上最受欢迎的文章包括:“17个倒过来拼写就会意思完全不同的单词”和“33只赢得一切的狗”。一周的工作结束时,如果你有些空闲时间,这些文章就成为你主要的娱乐,在这种情况下,这些网站尤其有害。当你在排队,或者等待电视节目中的情节有所进展,或者是等待吃饭的时候,这些文章可以成为你打发时间的工具。

  • 然而,如我在准则2中所说,这些行为是有害的,因为它们损害你抵抗分心事物的能力,使你在试图深度工作的时候更难集中注意力。更可怕的是,这些网络工具不需要你登录,因此在生活中更难戒掉(这使得之前的两个策略失效)。它们总是触手可得,只需要随手点几下。幸运的是,阿诺德·本内特在一百多年前就发现了解决之道:在你的娱乐时间做更多的思考。换言之,这个策略就是指在个人娱乐的时候,不要被任一随意的事物吸引,相反应该主动思考我如何度过“一天中的一天”。我们之前提到的这些致瘾性网站在真空中才能活跃:如果你没有在某一个特定时段给自己安排任务,这些网站总是一种有诱惑力的选择。如果你在自由时间有高质量的事情去做,这些网站对你的注意力的控制就会减弱。

  • 因此,在晚上或周末到来之前就确定要做的事情是十分重要的。一些安排好的爱好为这些时间提供了充足的养料。为了特定的目标完成特定的活动,这将填满你的时间。根据本内特所言,每个晚上都有序地阅读自己挑选好的一系列书,也是一个很好的选择。同样的活动还有锻炼,与益友(面对面)交往。

  • 消遣做得这么有条理会有损消遣的目的,因为许多人相信消遣就是要没有任何计划,没有责任。安排得一板一眼的晚上是否会让你在第二天工作的时候感到困乏,无法焕然一新?感谢本内特,他已经预料到了这样的担忧。他解释称,此等担忧源自对真正令人类恢复精力的事物的误解: 什么?你认为在那16个小时投入全部的精力会削弱工作8小时的价值?不是的。恰恰相反,它必定会增加工作8小时的价值。人们都要懂得一个重要的道理,人的智力系统可以进行长时间的高强度活动:它不像人的手脚一样会疲倦。除睡觉以外,它只需要变化,而不是停止。

  • 按照我的经验,这个分析完全正确。如果在你全部的清醒时间,都能给自己的大脑找到有意义的事情去做,而不是放任自己在迷糊的状态下漫无目的地浏览几个小时网页,那么在一天结束时,你会觉得更加充实,第二天开始时更加轻松。总结一下,如果你想抵御娱乐网站对你时间和精力的诱惑,那么就给大脑找一些高质量的替代活动。这样不仅可以使我们避免分心,保持专注的能力,同时还有可能实现本内特的宏伟目标:体验到何为生活,而不仅仅是生存。

准则4 摒弃浮浅

  • 2007年夏天,软件公司37signals(现在叫basecamp)做了一个实验:将每周5天工作制缩短成4天。尽管工作日少了一天,但是员工似乎还可以完成相同的工作量,于是他们的这项改革变成了永久政策:每年5~10月,37signals的员工只需从周一工作到周四(售后服务除外,他们一周7天都要工作)。公司发起人之一贾森·弗里德(Jason Fried)在博文里风趣地说道:“在夏季,人们应该享受美好的天气。”

  • 4天完成40小时的工作对于员工来说是十分有压力的。但是他解释道,这不是他所建议的工作方式。“一周4天工作制的意义在于做更少的工作。”他写道,“不是一天10小时的工作……是正常的一天8小时。

  • 很少有人能做到一天工作8个小时。在充斥着各种会议、干扰、网页浏览、办公室政治和私人事务的一个普通工作日里,能够专心工作几个小时就已经很幸运了。更少的正式工作时间有助于挤压出更高的效率。如果每个人都只有更少的时间完成任务,他们就会更加尊重时间。人会变得珍惜时间,而这是一件很好的事情。他们不会把时间花在无关紧要的事情上。如果拥有的时间变少,你就会更聪明地利用时间。

  • 换句话说,37signals工作时间的减少主要是集中在浮浅工作而不是深度工作。因为深度工作几乎没有减少,所以重要的事情仍能完成。结果证明那些原来看似十分紧急的浮浅工作其实是无关紧要的。

  • 虽然深度工作的价值远远超过了浮浅工作,但这并不意味着你必须像堂吉诃德一样试图把你所有的时间都投入到深度工作中。其中一个方面是,一定量的浮浅工作在大多数知识工作中是必要的。你应该可以避免每10分钟检查一次自己的电子邮件,但是不可能永远不回复重要的信息。从这个意义讲,我们应该明白该准则的目标是减少浮浅工作在我们日程中的分量,而不是将其消除。

  • 在给定的一天时间内,一个人可以保持多长时间的深度工作。

  • 一天一小时是一个合理的上限。对于熟悉此类活动严酷性的人来说,上限可以达到4个小时,但是很少能持续更久。

  • 这意味着一旦你在一天中达到了深度工作的上限后,继续试图增加深度工作效果就会下降。因此,浮浅工作并不可怕,只有其比例增加太多影响到你当天的深度工作上限时才需要注意。乍一看,这个警示或许没有那么可怕。通常一个工作日8个小时。这8小时内,没有一个熟练的深度工作者可以保持超过4个小时的深度状态。这样的结果就是一天中你可以有半天做浮浅工作,而且不会有害处。可是你可能没有意识到这些时间其实很容易被占用,尤其要考虑那些会议、预约、电话和其他计划内事务。在很多工作中,这些时间终结者将导致你每天只有很少的时间可以单独工作。

  • 总而言之,希望你能用怀疑的眼光对待浮浅工作,因为其害处经常被低估,而作用却经常被高估。这种工作不可避免,但是你必须对其加以限制,使其不影响你充分深度工作的能力,因为深度工作决定着你的最终工作成效。接下来的策略将助力你的实践活动。

一天的每一分钟都要做好计划

  • 我们一天中的大多数时间是在浑浑噩噩中度过的,对于应该如何安排时间并没有考虑太多。这是一个问题。如果不果敢地调整深度工作与浮浅工作的关系,不在行动前暂停一下问问自己“现在去做什么是最有意义的”,你将很难避免被烦琐杂事占满日程。接下来的几个段落将会介绍一些促使你这样做的策略。这些观点乍一看显得极端,但是很快你就会发现要充分享用深度工作的好处,就需要一天中的每一分钟都要做好计划。

  • 我建议:为本策略专门准备一个笔记本,在每个工作日开始的时候翻开新的一页。在页面的左侧,每隔一行写下这一天的每一个小时,包括普通工作日的全部时间。接下来是最重要的一个环节:将工作日的每一天划成方格,把活动放在这些空格中。

  • 完成一天的工作计划之后,每一分钟都应该在某个方格里有所体现。实际上,你已经为这一天的每一分钟安排了时间。此时,请你使用这个工作计划来引导一天的工作。可以预见的是,大部分人从这一步开始就遇到了麻烦。随着一天工作的展开,关于该工作计划,有两个方面容易(并且也极为可能)出岔子。第一,你的计划可能不符合实际。例如你为写新闻通讯稿预留了两小时,但是实际上你花掉了两个半小时。第二,你可能会被打断,新的工作任务会出现到你的日程里。这些事情也会打乱你的日程。这些都没有问题。如果你的日程被打断,在紧接下来的空闲时间,就应该花几分钟修改一天余下时间的计划。你可以翻到新的一页,也可以擦掉原有的重新编写,还可以像我一样:划掉原有的方格,在其右方为今天剩余的时间画出新的方格(我画的格子很瘦,所以有足够的空间修改几次)。有些日子里,你可能一天要修改十多次计划。如果出现了这种情况,不要气馁。你的目标并不是竭尽全力维持既定的计划,而是在时间的推进中掌握工作的主动权,即使是在一天中,我们的决定也会一变再变。如果你觉得计划改变的频率高得难以接受,这里有一些可以增加稳定性的诀窍。首先,你应该承认,你对大多数事情所需要的时间是预备不足的。刚开始尝试这个习惯时,很可能会一厢情愿地制定计划——完美一天的样板。经过一段时间的练习,你应该尝试准确地(甚至有些保守地)预估完成任务所需要的时间。

  • 第二个诀窍就是使用备用方格。如果你不确定一个既定任务需要多长时间,先初步预估需要的时间,然后在下面再多安排一个方格,这个方格可以有多重作用。如果这个既定任务需要更多时间才能完成,那么就占用下边这个备用方格。但是如果你在预定的时间内完成了任务,那么就把备用的方格安排做其他的活动(比如说一些非紧急事务)。这样就可以保证在不改变日程的情况下,容许不可预知的事情发生。

  • 在我的日常安排原则里,除了经常会为探索性思考和探讨预留大块的时间格之外,还时刻保持着一项准则,即一旦有灵光乍现,有了突破性的洞见,一天的其他日程安排就都可以暂时抛到一边(当然,实在抛不到一边的也没有办法)。之后我会坚持探究这种新的洞见,直到理清其中的头绪。这之后我会抽身出来,重新安排一天余下的时间。换言之,我不仅允许日程中有突发性改变,甚至会主动寻求这种改变。约瑟夫的批评是源于他对日程表的误解,他认为日程表就是强迫一个人严格按计划行事。然而我所说的日程计划的核心目的不是限制,而是强调谋划周到。在一天里时常拿出一点时间来询问自己“在今天剩下的时间里,我做什么最有道理”,是一个很简单的习惯。这是一个让你思考如何产出最大化的习惯,而不是让你对自己的答案保持不折不挠的忠贞。我甚至要说,一个同时具有综合计划能力和修正自己计划的意愿的人,相比那些采用传统“自发性”方法、一天没有任何计划的人,将享有更多的创造性洞见。没有计划,你的时间很可能被浮浅事务占用——电子邮件、社交媒体、浏览网页。这些浮浅事务虽然在短时间内会令人愉悦,但是并不有助于培养创造力。借助计划,你可以确保经常性的安排时间来处理新主意,或者在有挑战性的领域进行深度工作,或者在一个固定时间内进行头脑风暴——这些活动更容易带来创新(例如回想一下在准则1中,很多著名的创意思考者所遵循的一些习惯)。因为当创新性想法出现时,你也愿意放弃原本的日程安排,所以在灵感涌现的时候,你也可以像那些纷扰中搞创新的人一样跟进。

  • 总而言之,该策略的目的在于帮助你认识到,深度工作要求你尊重自己的时间。要做到真正尊重时间,下面这一条建议是个很不错的开端:提前决定你一天的每一分钟要做什么工作。因为一个人的日程都是由内在驱动和外在要求这两股力量决定的,所以开始的时候你对这个主意有所抵触也是自然的。但是如果你想发掘自己的潜力,成为一个有所成就的人,那就必须打破这种疑虑。

定量分析每一项活动的深度

  • 浮浅工作:对认知要求不高的事务性任务,通常在受到干扰的情况下开展。此类工作通常不会为世界创造太多新价值,且容易复制。

  • 为你提供一个明确而且稳定的计量表,以衡量一项给定任务的深浅度。如使用这个策略,你需要先问自己一个简单(但是很有启发性的)问题来评估这些任务: 要让一个刚毕业还没有在该领域接受特别训练的大学生完成这项工作需要多久(几个月)?

向老板申请浮浅工作预算

  • 有一个很重要的问题很少被问及:你的时间应该有几成被投入到浮浅工作中?这个策略建议你问一下这个问题。也就是说,如果你有老板的话,应该和他谈谈这个问题(或许你应该先向他们介绍“深度工作”和“浮浅工作”的定义)。如果你是自己当老板,问自己这个问题。两种情况下,都力争得到一个答案。然后——这是重要的一环,试着控制在这个预算范围内。

  • 你可以自信地对老板说:“这正是我上周在浮浅工作上的时间占比,”然后迫使他或她明确同意你给出的比例。在数据及其阐释的经济现实面前(比如说,让一位受过高等训练的专业人士回复电子邮件、一周参加30个小时的会议,是不可思议的浪费),老板自然会得出这样的结论:你需要拒绝一些事情,简化一些事情,即使这会使得你老板或同事的生活舒适度降低。因为一项生意的最终目的还是产出价值,而不是确保员工的生活尽可能简单。如果你自己做老板,这项练习将使你正视现实:你“繁忙”的日程中只有很少一部分时间是真正产出价值的。这些残酷的数据将激发你的信心,减少那些偷走时间的浮浅活动。没有这些数据,一位企业家将很难拒绝任何可能产生某种积极回报的机会。

  • “我要上推特!”“我要保持在脸谱网上的活跃度!”“我得优化博客上的小工具栏!”因为独处的时候,拒绝这些活动可能会显得你很懒。通过学习并坚持这种深浅度的分配比例,你可以摆脱因羞耻感而带来的无条件接受,转而坚持更健康的习惯,把本来留给浮浅工作的时间节省下来以发挥其最大效用(这样你仍然面临很多机遇),把浮浅工作占用的时间和精力限制得足够小,从保证你的深度工作推动事业进步。

  • 一份不需要深度工作的工作不可能使你在当前的知识经济时代取得成功。在这种情况下,你应该感谢老板的反馈,然后迅速谋划如何转型到一个重视深度的新岗位。

5点半之前结束工作

  • 5点半之前结束工作在我写这段话前的连续7天,我进行了65个不同的电子邮件对话。在这65组对话中,我在5点半后只发送了5封电子邮件。上例中的这些数据直接说明了一个事实:除极个别例外,我不在5点半后发电子邮件。总体来说,电子邮件和我们的工作是紧密相连的,所以这个事例告诉我们一个更令人吃惊的现实:我不在5点半后工作。

  • 我把这种坚持叫作固定日程生产力。因为我确定了一个坚定的目标,在某个固定时间后不再工作,然后在工作中寻找提高产出的策略以达成目标。

  • 减少浮浅工作为实现深度工作节省了更多的精力,使我们的产出比采用密集日程安排时更高。其二,我们的时间有限,因而会更谨慎地思考个人的组织习惯,这也使得我们产出的价值能够高于采用长时间但混乱的日程安排的人。

变得不容易联系到

  • 森特诺认为用私人一对一的对话来一遍又一遍地回答相同的问题是十分不经济的。如果你过了这一步,他会让你点击几个复选框来做出以下三种承诺:√我不是在问安东尼奥一个使用谷歌搜索10分钟就可以得到答案的问题。√我不是复制粘贴了常见请求给安东尼奥发垃圾邮件,以推销我个人的不相关生意的。√如果安东尼奥在23个小时内答复我,我将为某位陌生人做一件善事。当你点击同意了所有三个承诺之后,联系页面才会出现一个信息框供你输入信息。

结论

  • 深度的生活并不是适合所有人。你需要为此付出艰苦的努力,从根本上改变你的习惯。对于很多人来说,快速地收发电子邮件和在社交媒体上发消息所带来的繁忙假象会给他们带来慰藉,深度的生活却是要你摆脱这些东西。在你尽个人全力去创造一件美好的事物时,会有一种不安牵扰着你,因为这迫使你面对自己最好的成果(暂且)还没有那么好的可能。与涉足政坛,期望做出一番事业相比,夸夸其谈地品论我们的文化会显得更安全。

总结

  • 如果你无法学习,就无法成功
  • 无干扰状态下保持专注
  • 高质量工作产出=时间×专注度
  • 转移工作、注意力残留 -> 专注、深度工作
  • 深度工作并非是我们的经济中唯一有价值的技能,不培养这种能力也有可能做得很好,但是不需要深度工作的职业会越来越少。
  • 最小阻力原则
  • 忙碌代表生产能力(Busyness as Proxy for Productivity):在工作中,对于生产能力和价值没有明确的指标时,很多知识工作者都会采用工业时代关于生产能力的指标,以可视的方式完成很多事情。
  • 支持深度工作往往要抵制新的高科技
  • 当工作中没有明确目标时,围绕浮浅工作的表面忙碌会成为一种本能
  • 工作其实比休闲时光更容易带来享受?
  • 意志力是有限的
  • 培养深度工作的习惯,关键在于越过良好的意图,在工作生活中加入一些特别设计的惯例和固定程序,使得进入并保持高度专注状态消耗的意志力最小化。!!!!!
  • 选定你的深度哲学(节奏哲学)、并习惯化
  • 恰当展开协作可以提升你在职业生活中深度工作的质量
  • 这种隔音办公室与宽阔公共空间的组合,形成了中心辐射型的创新建筑结构,在这里偶遇的意外发现和与世隔绝的深度思考都能实现。
  • 专注于少量“极端重要的目标”
  • 引领性指标
  • 专注于极度重要目标上的深度工作状态时间
  • 4DX框架下的4种原则 !!!
  • 在整个4DX的实验过程中,目标的明晰性,辅以引领性指标计分板提供的简单但却难以回避的反馈,促使我达到了此前从未实现的深度状态。
  • 定期放松大脑!!工作日结束的时候,在第二天早晨到来之前,屏蔽掉对工作问题的担忧。
  • 定期休息大脑可以提升深度工作的质量。工作时,努力工作。完成时,就放松下来。
  • 不要不断分心,而要不断专注
  • 设定一个几乎不可能的时间期限;深度工作需要专注的强度远远超出了大部分知识工作者的舒适区。
  • 有成果的冥想
  • 远离社交媒体
  • 在晚上或周末到来之前就确定要做的事情是十分重要的。(避免陷入其他无效的诱惑)
  • 人的智力系统可以进行长时间的高强度活动:它不像人的手脚一样会疲倦。除睡觉以外,它只需要变化,而不是停止。!!!
  • 如果你想抵御娱乐网站对你时间和精力的诱惑,那么就给大脑找一些高质量的替代活动。这样不仅可以使我们避免分心,保持专注的能力,同时还有可能实现本内特的宏伟目标:体验到何为生活,而不仅仅是生存。
  • 减少浮浅工作在我们日程中的分量,而不是将其消除
  • 一天的每一分钟都要做好计划(笔记本)
  • 深度工作要求你尊重自己的时间。要做到真正尊重时间,下面这一条建议是个很不错的开端:提前决定你一天的每一分钟要做什么工作。
  • 5点半之前结束工作。固定日程生产力。
  • 变得不容易联系到;文档化

个人管理经验总结

发表于 2021-08-13
  • 情商、基于别人的角度思考

  1. 以身作则
  2. 技术功底,才能令人信服
  3. 保证成员 免干扰、休息时间如无必要不打扰
  4. 情商,基于别人角度考虑
  5. 真诚、谦虚、透明

  1. 避免用“以为”作为借口,基于“对方角度”、“将心比心”的方式,思考问题;
  2. “将心比心”的方式不奏效?一般大公司都追求一定的人才密集性,基本不会出现这个问题;
  3. 注重沟通的有效性(阐述事情的上下文),避免浪费双方的时间;
  4. 真正靠谱的人,当离职的时候,基本不需要什么交接,现有的文档修修补补即可,因为平时善于同步团队信息和分享。做到了可替代性很强,同时“不可替代性”很强。

《技术领导力:程序员如何才能带团队 》- 周明耀 - 总结

  1. 技术管理,技术在前,管理在后。一个技术从业者,核心竞争力是技术能力。技术管理最大的特性是表率,是感染力。
  2. 管理看起来套路很多,其实最终都是人性和策略,理解人性,善用策略,就能做好管理。对于聪明人来说,有实践机会,管理可以在短时间内达到一个不错的水准,但技术永远需要长时间积累。
  3. 很多技术领导带团队取得了一点成绩,就开始沾沾自喜,以为这事离了自己不行,其实是团队作战的功劳。大部分情况下,不是团队离不开领导,而是你离不开你的团队!
  4. 作为技术管理者,需要持续保持自己的技术能力,需要不断强化自己的管理能力和方方面面的综合性能力
  5. 只有做好当前的事情,你才有资格谈技术理想
  6. 在软件行业,没有在专业上的持续学习和成长,是不会成功的。技术变革如此迅速,如果不坚持学习,可能大学毕业几年后就被淘汰了。在不断更新的软件开发行业中,这并不意味着你不需要使用大学里学习的知识,而是除此外还需要不断地增加对新的技术、工具、方法、框架的深度认识和理解。
  7. 做好自己。- And now, as I lie on my deathbed, I suddenly realize:If I had only changed myself first, then by example I would have changed my family.From their inspiration and encouragement, I would then have been able to better my country, and who knows, I may have even changed the world.如果一开始我仅仅去改变我自己,然后作为一个榜样,我可能改变我的家庭;在家人的帮助和鼓励下,我可能为国家做一些事情。然后谁知道呢?我甚至可能改变这个世界。
  8. 在做这些决定之前,有没有做足充分的技术调研、产品调研工作,能不能尽量详细地说清楚已有框架、产品的实现原理、优缺点、是否适用于你的产品场景等,这些都是前期技术调研的工作,也是体现我们脚踏实地做事的一个环节。
  9. 一个人的逻辑能力属于软实力,它是决定你是否可以承担团队领导职责的关键因素。
  10. 举事以为人者,众助之;举事以自为者,众去之。
  11. “平时的艰苦训练是战时胜利的保证,这才是对士兵的最大仁慈。我是一个很坏的家伙。我要让他们尝试每一分钟的地狱生活,然后我又为他们痛哭!”
  12. 为了公司、自身的形象,我们需要无比庄敬自强,公平待人,不可欺负弱势的人,也不可以做损及他人或自己的事。同时,我们需要一个谦卑的团队负责人,谦卑的人不会固执己见,而是会虚怀若谷地聆听他人的言论。
  13. 有了真诚,才会有虚心,有了虚心,才肯丢开自己去了解别人,也才能放下虚伪的自尊心去了解自己。建筑在了解自己了解别人上面的爱,才不是盲目的爱。
  14. 我在生活中也是一个很仔细的人,也喜欢仔细的人。仔细的人一般做事都比较负责,愿意承担责任,也懂得抓细节。抓细节是作为一名技术团队管理者必须做到的。所谓的管理浮于表面,一般都是说管理者不关注细节,例如不参与设计、不参与代码审核,而只是高喊要注意开发设计、注意开发质量,这样的领导对于技术团队来说,尤其是一线技术团队经理来说,是不合格的,也是不能服众的。我觉得一个仔细的人,他一定也是一个善于观察的人,而善于观察、思考其实也是一个团队领导者所必须具备的品质,否则他的团队一定会在某一阶段或者一直处于混乱状态。
  15. 一个人一生如果想要获得过人的成就,就注定与读书和终生学习形影不离。功利性的学习是非常狭隘的,收获也是非常有限的,但是终生学习的回报却是不可估量的。
  16. “钱能买到一切有价的东西,独独换不回健康,也唯有生命才是无价的。”我们经常会听说有人得癌症了,有人又猝死了,这些例子数不胜数。虽然我能理解健康的重要性,但经常无可奈何。一个现场问题出现了,我们要一直忙到凌晨,可能偶尔还需要通宵应对。但是空下来以后,一定记得好好睡一觉,把睡眠补回来。因为只有保持身体健康,才能更好地带领团队。
  17. 回到技术管理工作。一直有同仁问我,什么时候可以转为技术管理?如果在军队里面,一个士兵说我杀敌本领不行,是不是可以升为将军?同样的道理,最好是技术能力比较强之后再转管理,水到渠成,技术不行的人即使转了管理,也难使人信服。
  18. 从技术转管理本身就是一个很大的转变,一旦跳进去,就很难爬出来了。我认为最大的差异就是,技术面对的是系统性问题,需要的是逻辑思维能力,是智商,而管理需要面对的很多是非逻辑思维能力,是情商。系统问题需要智商,而人是活的,你要理解人,就需要情商,情商是什么?我个人理解,情商就是如何站在别人的角度看问题。
  19. 作为技术团队管理者,你也需要有一定的技术功底,能够让“英雄”觉得可以从你这里学到一些知识,这样才能合作愉快。最后,需要对“英雄”的薪资、福利有所倾斜,并且多和他们沟通,让他们自己选择技术方向,为他们指明职业发展方向,并把他们放入关键项目里。
  20. 大多数公司内部都是垂直组织结构,所以管理团队、管理自己的团队领导岗位,大致上可以分为向下管理、向上管理、对外管理、自我管理这四个方面,管理的最终目标是:“不要让你的下属陷入困境,不要让你的同事陷入困境,尤其是在任何情况下,都不要让你的上级陷入困境”。
  21. 成功地管理程序员最重要、最关键的因素,是在技术层面得到他们的尊重。
  22. 你所选择的这位空降管理者,你需要充分考虑他是否有良好、可以被证明的履历,这样才能让他获得团队的尊重。所以说,一般情况下技术团队是不会空降高管的。(真实!)
  23. 我们要学会保护团队成员,让他们免受组织中的各种问题、争议和“机会”的干扰。
  24. 公司内部非重要部门组织的会议,尽量不要放在周五的晚上和休息日进行。利用这种休息时间,看起来很有效率,其实是在过度使用研发资源,过度消费员工对公司的满意度。周五晚上和休息日可以打扰研发人员的事情是:(1)现场问题,必须立即处理(不处理会损害公司未来利益);(2)重要客户提出需求,要求立即做出回复(不处理会损害公司当前利益);(3)特别重大的突发事件(对你和公司都很重要)。
  25. “信任但要核实”,这是里根总统经常引用的一句谚语,也是列宁的口头语。
  26. 记住,没有记录的会议,相当于没有开过。每一个重要会议,都需要有完整的会议记录,包括参加人员、讨论议题(逐一写下来)、讨论过程大体描述、每一个议题的最终结论(包含流程图)、遗留问题、下一次会议时间及议题等。
  27. 你老板的级别越高,你的报告就越要精简,越要注重大局,细节更少、局面更大、文字更少、项目符号更多。
  28. 另外,你还要避免只把问题带给领导的情况,最好是拿着几套潜在的解决方案和问题一起交给他。即使你没有特别好的解决方案,或者没有找到最好的解决方案,也会让领导觉得你已经做好了自己的功课,过来找他是为了寻求他的建议和忠告,而不是直接把问题抛给他。
  29. 公平、公正,是做事、做人的基本原则。
  30. 如果程序员觉得没有前途,不思进取,而资质较好的程序员很快又被提拔为管理者,那我们的软件开发将很难有技术和人才的积累。
  31. 作为一名团队管理者,有一点是需要做到:“不要作恶!”
  32. 程序员都是些无拘无束的人,常见的激励方法往往没什么用。除了进行必要的技术监督并把开发实践和过程落实到位之外,善于利用程序员的自我意识和改变世界的欲望也很关键。这就需要一类既能理解程序员的工作方式,又能理解工作本身的技术管理者,他们不仅能有效地激励程序员超常发挥,而且能按时交付结果。
  33. 杰出的程序员偏爱能够满足他们更高理想或要求的公司和项目,他们非常在意自己所做的事情,常常为了想要的结果而超负荷工作,而不会在某种压力下自愿做低技术含量的重复劳动。
  34. 大公司可能会采用“矩阵管理”方式组建团队,这意味着团队成员都有各自隶属的职能领域,但为了完成某个指定的项目,被“临时”分配到了一个矩阵型的团队中。这样能够在项目人事配备方面获得最大的灵活性,但是也带来了考核的难题。如果团队成员很优秀,这不会是一个问题。但是当团队成员表现不佳时,则可能成为“临时”团队负责人的烦心事。这个问题通常被称为有责任但没有权力。(真实!)
  35. 不要小看了这两个词的力量,正是这两个词决定了OKR和KPI的本质差异:OKR关注的是目标,KPI关注的是指标。当我们关注“目标”的时候,我们会思考接下来我要做的事情是什么;而我们关注“指标”的时候,我们会思考自己的工作如何评价。
  36. 彼得·德鲁克在《管理的实践》中说:“并不是有了工作才有目标,而是相反,有了目标才能确定每个人的工作。所以企业的使命和任务,必须转化为目标。”
  37. OKR让我们做正确的事情,KPI让我们正确地做事情!
  38. 软件的主要目的就是把人类生活的非核心生命周期软件化、虚拟化,以提供更低的成本和更高效率的新生活,让核心生命周期的运行能够更加容易,让非核心生命周期的处理更少地占用人类的时间,变相地延长人类生命。
  39. 一个合格的开发经理必须同时做到“按预期交付成果”“让客户满意”“让员工满意”
  40. 从这些工作任务的性质来看,开发经理是项目的推动者、技术的输出者,也是关系的协调者。总的来说,开发经理往往是决定一个项目成败的关键人物,要求其职业素养高、技术能力强、综合能力强、职责范围广。也正是因为要求太高,所以很多公司将开发经理、项目经理区分开,即项目经理可以不用管技术,专心做协调、进度把控工作。
  41. 软件开始开发前需要确定投入与产出的比值,也就是ROI(Return On Investment,投资回报率),一旦确定需要创建,就需要安排一系列的资源来支撑这个软件的生存,这是需求的最原始描述。
  42. 在需求评审阶段,邀请产品、开发、测试相关人员进行需求评审,产品需求评审主要针对以下几个方面进行:❑ WHY:为什么做这个需求?❑ WHAT:需求的价值是什么?❑ WHEN:需求期望什么时候上线?截止日期?❑ HOW:需求是否完整?正常场景是什么?异常场景是什么?技术上分别怎么应对?
  43. 对于还未上线运营的新系统,一般会让应用的产品经理或负责人给出一个预估的比例,但是这个预估需要我们进行评估,不是随意的。对于已上线运营的应用,一般会分析实际的交易数据来确定交易比例,这样会更加精准。
  44. 当你去问一个老人很多问题的时候,虽然可能很快得到解答,但当有一天,有很多新人来问你问题的时候,你就能体会到什么问题该问,什么问题不该问。
  45. 总结项目经验教训的目的在于总结问题、分析原因,避免以后犯同样的错误,而不是追究谁的责任。再重申一句,复盘会不要太严肃,它不是“批斗会”,而是为了总结经验,不断优化,不再犯同样的错误。
  46. 在项目中发生需求变更是难免的,我们不能抵制变更,但是一定要用切实有效的办法控制变更。需求发生变更后一定要认真仔细修改相关的文件,如需求文档、项目计划、设计文档等,并通过书面方式通知团队成员,不能在会议上一笔带过,并且要确保团队成员都准确地知道了变更的内容以及下一步的计划。注意,再小的变更都会给项目带来影响,多次微小的变更可能引发连锁变化,所以不能因为变更影响小而疏忽怠慢。
  47. 向进度落后的项目中增加人手,只会使进度更加落后”,这句话摘自《人月神话》。(实际还是看具体情况)
  48. 根据时间管理的经验,安排工作的原则顺序为:重要/紧急工作>重要/不紧急>不重要/紧急>不重要/不紧急。
  49. 缺少强有力项目管理能力的技术领导,很容易让团队的部分成员陷入迷茫。提出的命题很大,但是落实到每位技术人员身上,一部分人会迷茫,不知道每天应该干点什么,所以最好的方式是对工作进行拆分,每天的工作要能说清楚它的具体目标、要求标准等,这样才能让大家有存在感和参与感。
  50. 为了更好地设计系统、理解技术,你一定要组织调研项目和预研项目,这是因为你或者团队不可能什么技术、框架都懂,更何况技术发展非常快,只有针对技术进行调研、预研,才能够真正跟上技术发展的潮流,否则终有一天你会被技术岗位淘汰。
  51. 业务、架构和技术之间是共生的关系,而不是互斥的关系。先有业务问题,才会有技术来解决业务问题。而业务的长大要求,提高了对技术的要求,导致了对业务生命周期的拆分,以并行的方式提升效率,形成了架构,也形成了新的技术。所以在这三者的关系里:业务是核心,技术是解决业务问题的工作,而架构是让业务长大的组织方法。架构需要用技术来实现拆分,技术需要架构来合理组织,以提升效率。软件和业务最终是要合体的。

  • 正如很多管理环节,领导交代工作,喜欢你够聪明,自己去领悟。奉行的是好话不说第二遍,这样好像显得彼此的效率都很高,实际上这是很模糊的,下属极易做错做偏,不仅成功率低,往往还需要大量的成本去弥补。
  • 在日本的管理中有一个问五次原则,1告诉你去做什么。2让你复述一次。3做这件事的目的是什么。4你觉得行动过程中会有哪些问题,哪些你能自己搞定,哪些需要我来协助。5如果全权交给你来做,你会有什么想法和创意。
  • 看似好像很啰嗦繁杂,却实在地提高了沟通的效率和质量,有效避免了理解偏差和权责不清导致的错误和矛盾。

  • 有些人身上的光环是自己有本事,有些人身上的光环完全是公司的,一定要分清楚。
  • Leadership并不是当领导和经理,而是一种特征,这种特征有如下两个简单的表象:
    • 帮人解问题。团队或身边中大多数人都在问:“这问题怎么办?”,而总是你能站出来告诉大家这事该怎么办?
    • 被人所依赖。团队或身边中大多数人在做比较关键的决定时,都会来找你咨询你的意见和想法。

摘自陈皓

技术领导力是:

尊重技术,追求核心基础技术。
追逐自动化的高效率的工具和技术,同时避免无效率的组织架构和管理。
解放生产力,追逐人效的提高。
开发抽象和高质量的可以重用的技术组件。
坚持高于社会主流的技术标准和要求。

那么作为一个软件工程师怎样才算是拥有“技术领导力”呢?我个人认为,是有下面的这些特质。

能够发现问题。能够发现现有方案的问题。
能够提供解决问题的思路和方案,并能比较这些方案的优缺点。
能够做出正确的技术决定。用什么样的技术、什么解决方案、怎样实现来完成一个项目。
能够用更优雅,更简单,更容易的方式来解决问题。
能够提高代码或软件的扩展性、重用性和可维护性。
能够用正确的方式管理团队。所谓正确的方式,一方面是,让正确的人做正确的事,并发挥每个人的潜力;另一方面是,可以提高团队的生产力和人效,找到最有价值的需求,用最少的成本实现之。并且,可以不断地提高自身和团队的标准。
创新能力。能够使用新的方法新的方式解决问题,追逐新的工具和技术。

Leader 和 Boss 的不同
再或者用通俗的话说,Leader 是大家跟我一起上,而 Boss 则是大家给我上,一个在团队的前面,一个在团队的后面。

如何成为众人愿意追随的 Leader
说白了,要成为一个大家愿意追随的人,那么你需要有以下这些“征兆”。

帮人解决问题。团队或身边大多数人都在问:“这个问题怎么办?”,而你总是能站出来告诉大家该怎么办。

被人依赖。团队或身边大多数人在做比较关键的决定时,都会来找你咨询意见和想法。

《清单革命》


《不拘一格:网飞的自由与责任工作法》-笔记

发表于 2021-08-11

推荐序一 企业如何最大化地驱动创新

  • 网飞文化的内核是人才,它的理论基础是我们大家必须关注的“人才效益现象”,即一个富有才华的人所产出的创新效果将数倍于一个能力中等的人,并且随着技术和创新发展,这种倍数还在不断增加。这里需要强调的是,这种人才效益现象针对的是创新能力起决定性作用的工作岗位,比如技术研究和产品开发等。针对这一现象,里德提出将人才密度作为企业创新能力的内核基础。因为优秀人才能激励其他优秀人才,而精英创造的出色成果能感染更多的出色人才。一个企业只具备少量的人才是不够的,它需要累积足够的人才密度才能具备高水平的创新能力。

  • 在管理上放权,赋予员工更多的自由,同时也让员工承担其相应的企业责任。它们的核心宗旨是让员工最大化地施展他们的才华和对企业的责任心,使企业能最大化地发挥人才潜能,驱动创新。

  • 网飞再下一层更为核心的企业改革是从“控制管理模式”转型到“情景管理模式”。这对很多企业管理者和企业创新探索者来讲,是一个长期追求但非常难实现的企业管理境界。在企业决策过程当中,理想的情况是每一个重要决定都由最了解情况、专业能力最强的相关责任人来做决策,但是绝大部分企业在管理上都采用金字塔决策模式,重要的决定都是由领导层来做,而领导层往往并不是最适合做这些决策的。网飞所采用的是树形的决策模式,企业领导层只负责树根部分,确保整个企业有高度一致的战略方向,同时赋能每一根树枝,让它们能基于相应的业务情景来做决策。

推荐序二 打造面向未来的新型组织文化

  • 网飞文化的核心是“人才重于流程,创新高于效率,自由多于管控”。

  • “自由与责任的关系并不是像我先前所想的那样背道而驰,相反,自由是通往责任的一条途径。

  • “当老板放弃‘决策审批者’这一身份时,公司业务发展会更加迅速,员工创新能力也会增强。”

自序一 没有规则的规则

  • 如果我们自己都做不好,他们又能起什么作用呢?

  • 规定和管理流程成了我们工作的基础,那些擅长在条条框框里循规蹈矩的人得到了提拔,而许多有创造力且特立独行的员工却感到窒息,于是他们便离职去了别处。看到他们离开,我很难过,但那时我相信这就是公司成长的过程。

  • 经过多年的反复实践、试错,以及不断的改进,我们最后终于找到了行之有效的办法。如果你给员工更多的自由,而不是制定规则来阻止他们发挥自己的判断,他们会做出更好的决定,也更有责任感。这样,员工工作起来会更愉快,更有动力,公司经营也会更加灵活。但是,要实现这种自由,你必须有一个基础,即让公司先从以下两个方面得到提升:第一,提高人才密度。在大多数公司,规则和控制流程针对的都是那些表现马虎、做事不专业或不负责任的员工。但如果你规避或者剔除掉这样一些人,你就完全不需要那些规则。如果你能组建一支几乎完全由高绩效员工组成的团队,那么大多数规则都是可以去掉的。人才密度越高,你能提供的自由度就越大。第二,提高坦诚度。有才华的人身上有很多东西值得学习。但在一般情况下,讲究客套的人际交往会妨碍员工做出必要的反馈,从而影响绩效水平的提升。如果优秀的员工都养成坦诚反馈的习惯,那么他们就能更好地完成工作,同时对彼此负责,这就进一步减少了对传统管控的依赖。在此基础上,须做好一道减法——减少管控。首先,将员工手册由厚变薄,差旅、经费支出、休假等相关规定统统可以不要。然后,随着人才密度越来越大,反馈越来越频繁和坦诚,你就可以取消整个组织的审批流程,教会你的经理们“进行情景管理,而非控制管理”,同时让员工把握这样一个原则:工作不是要费心地取悦老板。

  • 创建自己特有的、富于自由与责任的企业文化。

自序二 把员工当成真正的成年人

  • 且不说炒掉那些努力工作但业绩并不出众的员工是否有悖道德,这些幻灯片让我觉得这是一种极其糟糕的企业管理模式。这一模式违反了哈佛商学院教授埃米·埃德蒙森(Amy Edmondson)所说的“心理安全”原则。

  • 埃德蒙森教授在2018年出版的《无畏的组织》(The FearlessOrganization)一书中指出:如果你想激励创新,你就需要创造一个让员工可以安心地放飞梦想、大胆发言和勇于冒险的环境。工作环境让员工越有安全感,就越能激发他们的创新意识。很显然,网飞公司没有人读过这本书。他们雇用了最优秀的员工,然后向他们灌输一种忧患意识,告诉他们如果不追求卓越,就自个儿拿一笔遣散费走人。这听起来无疑是扼杀了创新的希望。

  • 如果没有限期休假的制度,员工真正休假的时间可能就更少了。这样的认识是有理论依据的,即心理学家所说的“损失规避”。我们人类都不想失去自己已经拥有的东西,这种感觉甚至超过我们对新事物的渴望。面对可能失去的东西,我们会竭尽全力地抓住它。所以,如果有休假的安排,我们还是会尽可能地去休假。

  • 如果没有假期,你当然也没必要担心会失去假期,所以就更不可能去休假了。而许多传统政策中“过期作废”的规则听起来像是一种限制,但实际上是在鼓励人们抓住机会休假。

  • 没有人会赞同将工作环境建立在秘密和谎言的基础上,但有时采取一定的策略,显然比直言不讳更好。例如,当团队成员陷入困境,需要鼓舞士气或者增强自信时,我们就需要委婉地处理。而“总是坦诚”的一揽子规则听起来却是在破坏关系,磨灭激情,还会造成一个不太愉快的工作环境!

  • 总的来说,网飞的《自由与责任》给我的印象就是极度男性化,充斥了过多的对抗性和彻头彻尾的攻击性。你可能会觉得,这种公司的创造者,就是一个在一定程度上用机械论和理性主义观点来看待人性的工程师。

  • 网飞认为你具有惊人的判断力……判断力几乎可以解决所有模棱两可的问题,而流程做不到。

  • 我越来越感到好奇:这样一个组织,在现实生活中是如何成功运作的呢?我刚开始的感觉是,缺乏规则必然会造成混乱;如果员工不能发挥出超高的工作水平,就得立马走人,这又难免会引起员工的恐慌。

  • 史蒂夫·乔布斯在斯坦福大学的毕业典礼上做过一场著名的演讲。他在演讲中说道:“面向未来,你无法将所有节点串联在一起;只有回望过去,你才能看清这些节点是如何串在一起的。你要相信,这些节点会在未来以某种方式联系在一起。所以你要有一种信念,这种信念可能是你的直觉、你认定的命运,抑或你向往的生活、你所相信的因果报应,或者其他某种想法。这种方法从来没有让我失望过,这让我的生活变得与众不同。”

1 优秀同事造就优质工作环境

  • 具有非凡的创造力、工作出色,且与他人合作良好的员工是留下来的最佳人选

  • 在一个真正人才济济的公司,每个人都会努力工作。工作效率高的人,在整体人才密度高的环境中,也能得到更好的发展。我们的员工都在相互学习,团队也在高效运作。这既增加了个人的积极性和满意度,也使整个公司的工作效率更高。我们发现,周围全是优秀人才的环境,能够让你的工作上到一个新的台阶。最重要的是,与才华横溢的同事一起工作很令人振奋,容易受到鼓舞,同时能感受到很多的乐趣。

  • 今天,公司拥有7 000名员工,但和当时只有80名员工一样,我依然有这样的感受。事后看来,一个团队只要有一两个表现欠佳的人,就会拉低整个团队的绩效。如果你有五名优秀员工和两名表现欠佳的员工,这两名表现欠佳的员工会造成如下后果:

    • ·消耗管理者的精力,使他们没有时间把精力放在优秀员工身上。
    • ·团队讨论的质量得不到保证,拉低团队整体智商。
    • ·强迫他人围绕着他们开展工作,致使工作效率低下。
    • ·排挤其他追求卓越的员工。
    • ·向团队表明你接受平庸,从而使问题更加严重。
  • 对于优秀员工而言,好的工作环境并不意味着一间豪华的办公室,一个好的健身房,或者一顿免费的寿司午餐,而在于周围全是才华横溢的人,具有合作精神的人,让你不断进步的人。如果每一名员工都很优秀,他们就会相互学习、相互激励,工作表现也会迅速得到提升。

  • 许多人的工作在细节方面做得并不好,这实际上向他人表明,表现平平也是可以的。这样便导致了公司整体水平的下降。

  • 构建一个高效且具有创造力的工作环境,离不开出色的员工。他们背景不同,看问题的角度各异,但他们有着共同的特点,那就是:具有超凡的创新能力,能够完成繁重的任务,并能很好地相互协作。你必须首先确保这一关键点落实到位,否则其他原则都没有意义。

  • 作为领导者,你的首要目标是营造一个完全由优秀员工组成的工作环境。

    • ·优秀的员工能完成大量重要的工作,而且极富创造力和工作热情。
      团队中如果有成员过于狂傲,做事懒散,平庸,或者悲观,整个团队的表现都会受到影响。
  • 迈向自由与责任的企业文化当你淘汰了表现欠佳的员工,提高了人才密度之后,就可以着手引入坦诚的企业文化。

2 以积极的态度说出你真实的想法

  • 我也努力把坦诚这一信条带到公司。我开始鼓励每个人说出自己的真实想法,但意图必须是积极的,不要攻击或伤害他人,从而将各种想法、意见和反馈摆到台面上来加以解决。

  • 我们当时没有雇任何新人,也没有提高任何人的薪水,但日益增加的坦诚度却让公司的人才密度得到了提高。

  • 评价一个人,要人前人后一个样。

  • 在给予反馈和接受反馈成为一种常态之后,人们会学得更快,工作效率也会更高。

  • 高绩效+无私的坦诚=极高绩效

  • 你认为你的观点得不到支持。

    • ·你不想被视作一个“麻烦”。
    • ·你不想陷入不愉快的争论。
    • ·你不想惹恼或激怒你的同事。
    • ·你担心会被认为缺乏团队精神。
  • 在网飞,如果你与同事有不同意见,或者是有好的建议却不说出来,就会被视为对公司不忠,因为你本可以为企业提供帮助,但你却没有这样做。

  • 当我第一次听说网飞的坦诚文化时,我对此表示怀疑。网飞不仅提倡坦诚反馈,而且还提倡持续反馈。当时在我看来,这只会让员工听到更多伤人的话。大多数人都不愿接受刺耳的言论,觉得这样的话可能会让思想变得消极。鼓励人们坦诚地发表反馈,这一想法听起来不仅令人觉得难以适应,而且风险很大。但是,当我开始与网飞员工合作的时候,我看到了这样做的好处。

  • 我们不喜欢但需要坦诚很少有人喜欢受到批评。工作中收到负面的反馈,会让你对自己产生怀疑,让你感到沮丧,感到自己很脆弱。你的大脑会对负面反馈做出反应,就像面对身体威胁时的战逃反应一样,都会将激素释放到血液中,从而加快反应速度并产生一定的情绪。

  • 要说有什么比面对面的批评更令人不安的,那就是当着众人的面收到负面反馈。在我的演讲过程中,那位当着同事的面提出反馈的女士给了我很大的帮助。她告诉我她的意见对我有用,需要及时地反馈给我。不过,在众人面前收到反馈,会向大脑发出危险警报。我们的大脑对遭受群体排斥这类信号特别关注,因为大脑具有求生的机制,而我们最成熟的生存技能之一就是尽可能寻求安全。在原始社会,遭受排斥就意味着孤立和死亡。如果有人在你的部落宗族面前指出你犯的错误,你的大脑中一直对危险保持警惕的杏仁核——这也是大脑中最原始的一个结构——将会发出警报:“你将受到群体的排斥。”面对这种情况,我们的本能反应就是逃跑。同时,也有大量的研究表明,收到积极的反馈会刺激大脑释放催产素。这种令人愉悦的激素也能使母亲在哺乳时感到快乐。这便能解释为什么很多人喜欢说恭维的话,而不愿给出诚实的、建设性的意见。

  • 然而,研究也表明,我们大多数人出于本能,还是能够理解真相的价值。

  • 尽管赞美可以带来愉悦,但多数人还是认为,同积极反馈相比,纠正性反馈更能帮助我们提高水平和能力。持这一观点的人数几乎是持相反观点人数的三倍。多数人都说,他们觉得积极反馈对于他们的成功没有太大的帮助。

  • 反馈环是提高绩效最有效的办法之一。如果在我们合作共事的过程中,能不断地提出并接收到反馈,便能学得更快,完成得更多。反馈有助于我们避免误解,营造共担责任的氛围,同时减少对权力和规则的需求。

  • 在考虑是否给予反馈时,人们经常会纠结于这样一个问题:他们既不想伤害接收者的感受,又希望能给对方提供帮助。而网飞的目标则是:帮助彼此取得成功,不要担心偶尔伤害了对方的感受。更重要的是,我们发现,在恰当的氛围中采用正确的方法,我们完全可以大胆地提供反馈而不会对他人造成伤害。

  • 如果你想在自己的机构或团队中培养坦诚的文化氛围,可以采取几个步骤。要做到第一步并不容易。你可能会认为,培养坦诚的第一步是从最简单的步骤开始:领导者向员工提供大量反馈。但我建议,首先将重点放在更困难的事情上面:让员工向领导者坦诚地反馈。当然,领导者与员工间的反馈也可能是同时进行的,但只有员工向领导者提供了真实的反馈,坦诚反馈的最大好处才会真正体现出来。

  • 你在组织中的地位越高,收到的反馈就越少,你就越有可能是“赤裸着身体在工作”,也越容易犯下除你之外所有人都看得见的错误。这不仅会导致整个机构的运作出问题,而且还很危险。

  • 另一项至关重要的,是你在获取反馈时的行为反应。你必须向员工表明,如果你能心怀感激地面对他人的批评,能够给予足够的“认同提示”,那么你也可以放心地提供反馈意见。

  • 我在洗手间碰到特德。他问我第一天感觉怎么样。我对他说:“哇,特德,我简直不敢相信,那个人在会议上居然敢用那种态度对你说话。”特德一副迷惑不解的样子。他说:“布赖恩,如果哪一天你因为害怕不受待见而不敢提出反馈意见,那你可能就得离开网飞了。我们聘请你来,就是需要听你的意见。会议室里的每一个人,都有责任把他的想法坦率地告诉我。”

  • 一个领导者要想获得员工的反馈,有些事情是必须要做的。他不仅要向员工征求反馈,而且要告诉员工,自己期待着他们的反馈(就像他对布赖恩所说的那样)。当你收到反馈时,需要通过认同提示进行回应。

  • 罗谢尔认真遵循了网飞的原则,即在这种情况下,保持沉默就是对公司的不忠。晚上,她给里德写了一封电子邮件,发送之前自己先“读了100遍。因为即使是在网飞,还是感觉有一定的风险”。最后,她把邮件发了出去。在邮件里,她是这么说的:嗨,里德:昨天我也是参会者中的一员。听了你对帕蒂所说的话,我感觉似乎有些轻率,而且对帕蒂也不够尊重。我之所以提出这一点,是因为在去年的务虚会上,你谈到了创建一个良好的对话环境的重要意义。在这种环境下,人们应该有勇气把心里的话讲出来,无论是赞同还是反对。昨天在会议室里,有董事和副总裁,还有一些不太了解你的人。听到你对帕蒂说话的语气,如果我也不了解你,那我今后无论如何也不敢当着众人向你表达我的观点。因为担心你会否定我的想法。我刚才对你讲的,希望你不要介意。罗谢尔在听完罗谢尔的故事之后,我想到了过去我曾经做过的工作,从斯里兰卡咖喱餐厅的服务员到一家大型跨国公司的培训经理,再到一家波士顿小型公司的董事和一所商学院的教授。我也努力回忆了一下,看自己在担任这些不同角色的过程中,是否曾听到过有人礼貌而坦诚地告诉领导,说他在会议上说话的语气不太妥当。回忆的结果是一个大大的“不”。我给里德发了一封邮件,问他是否记得5年前罗谢尔的这封邮件,他几分钟之内就回复了。艾琳:我还记得我们开会的房间,以及我和帕蒂所坐的位置。我还记得当时有些沮丧,情绪没控制好。里德他还把他回复罗谢尔的邮件也转发给了我。罗谢尔:非常感谢收到你的反馈。如果你发现我仍有不当之处,请继续与我联系。里德

  • 罗谢尔的反馈是坦诚的,同时也是经过深思熟虑的,其真正目的是帮助里德做得更好。但营造坦诚氛围的最大风险,就是可能会造成人们有意或无意的滥用。这就需要迈出培养坦诚文化的第二步。学会正确地给予和接受反馈

  • 坦诚把握不好便会尽显丑陋。

  • 尽管网飞大力提倡反馈,但仅靠坦诚是没有用的,坦诚的氛围并不意味着一切。网飞的员工最初向我提供反馈时,我感到非常吃惊,以为反馈的原则就是“说出你的想法,不惜一切代价”。

  • 事实上,网飞的管理者们花费了大量的时间,帮助他们的员工懂得了何为正确的反馈、何为错误的反馈。

  • 他们的经验可以总结为4项准则,我把它们统称为4A反馈准则,你也可以尝试一下。4A反馈准则提供反馈

    1. 目的在于帮助(Aim to assist):反馈的目的必须是积极的。反馈不是为了发泄,不是为了中伤他人,也不是为自己捞取资本。反馈者应清晰阐述这样做对他人和公司有什么样的好处,而不是对自己有什么好处。“你在与外部合作伙伴会面时在剔牙,这样做很让人生气。”这是错误的反馈方式。正确的反馈应该是这样:“如果在与外部合作伙伴见面时你不再剔牙,那么合作伙伴可能会觉得你很敬业,我们就更有可能建立牢固的关系。”
    2. 反馈应具有可行性(Actionable):你的反馈必须说明接收人可以做一些什么样的改变。我在古巴的那次演讲中,如果收到的是这样一个反馈:“你在演讲过程中的做法与你自己的观点不符。”那这样的反馈就是有问题的。而正确的反馈可以是这样的:“你选取听众发言的方式导致了最后的参与者只有美国人。”或者这样说更好:“如果你还有别的方法,让其他国籍的参会者也发一下言,那你的演讲将更有说服力。”接收反馈
    3. 感激与赞赏(Appreciate):我们在受到批评时都会为自己辩护或寻找借口,这是人类的本能;我们都会条件反射式地进行自我保护,维护自身的名誉。当你收到反馈时,你需要有意识地反抗这种本能,并且问一问自己:“我该如何去认真地聆听,以开放的心态去认真地对待反馈?既不辩护,也不生气,还应该满怀欣赏和感激。”
    4. 接受或拒绝(Accept or discard):在网飞,你会收到很多人的反馈。你需要认真地听,同时也认真地思考。不是每条反馈都要求你照办,但有必要向反馈者真诚地致谢。你和反馈者都必须清楚:对反馈意见的处理完全取决于反馈的接收者。
  • 在本章开头的案例中,道格向乔丹提出了反馈,让乔丹看到在印度工作时,应该如何调整自己的行为,这是遵循4A反馈准则的一个典范。道格意识到,乔丹与客户会面的方式会影响他自己的计划。而道格的目标,就是帮助乔丹改进行为方式,并帮助团队取得成功(4A准则之“目的在于帮助”)。道格提供的反馈意见让乔丹很受用。乔丹说,他现在与印度方面合作,采取的就是更好的方法(4A准则之“反馈应具有可行性”)。乔丹表达了对道格的感谢(4A准则之“感激与赞赏”)。当然,他可以选择不接受反馈,但是这一次他接受了。他说:“现在出去之前,我也不会对别人说教了。取而代之的是,我会对同事说:‘嘿,这可是我的弱点!如果下次印度的尼廷为我们安排城市观光,我又盯着手表看的话,就把我狠狠地教训一顿!’”(4A准则之“接受或拒绝”。)

  • 大多数人都和道格一样,会觉得及时的反馈尤其困难。他们会首先把自己调整好,等条件和时机成熟之后再说出想法。这样一来,反馈的效果就没有那么好了。于是,我们就要迈出坦诚文化培养的第三步。当场反馈,实时反馈

  • 剩下的最后一个问题就是:我们应该在什么时间、什么地点提供反馈呢?答案就是:随时随地。这可能意味着反馈意见还是在私底下说最合适。艾琳在网飞做主题演讲时,当着三四个人的面收到了第一个反馈,这样也挺好的。如果反馈真的对别人有极大的帮助,我们当着40个人的面说出来都没有问题。

  • 每一只高高举起的手都像是一个挑战。似乎所有的人都在喊:“你知道自己在做什么吗?!”面对一个又一个挑战,我不禁加快了语速,心里也感到不安。听众对我的质疑越多,我就越担心自己没法把内容讲完,于是讲得越来越快。

  • 后来,罗丝的亲密同事比安卡在房间的后面挥动着手臂,这无疑为罗丝提供了一件救生衣。这是一种典型的网飞风格。“罗丝!你这样不行!你快要失去控制了!你听起来像是在为自己辩解!你讲得太快了。你没有好好地听别人的问题。你在重复自己的东西而没有回应别人的关切。深吸一口气,你需要大家的参与。”她大声喊道。

  • 那一刻,我仿佛看到了观众眼中的自己——说得多,听得少,一副气喘吁吁的样子。我深吸了一口气。“谢谢你,比安卡。你是对的。我是怕时间不够。我需要每个人都了解这个项目。我来这里的目的就是想听到并解答大家的问题。我们回到正题上面来吧。刚才还有哪位提问我没有叫到的?”我有意识地改变了自己着力的方向,这让整个会议的气氛也发生了变化。大家的声调缓和了,脸上开始露出微笑,先前那种咄咄逼人的气势也烟消云散了,我也说服了大家参与这个项目。比安卡的坦诚拯救了我。

  • 比安卡的目的就是要帮助罗丝取得成功(4A准则之“目的在于帮助”);她告诉罗丝可以采取哪些具体措施以调整自己的表现(4A准则之“反馈应具有可行性”);罗丝对比安卡报以感谢(4A准则之“感激与赞赏”);最后,她听从了比安卡的建议,这对所有人都是有好处的(4A准则之“接受或拒绝”)。

  • 如果你遵循这一准则,随时随地都可以提出反馈,最大限度地让接收者受益。

  • 厘清什么是无私的坦诚,什么是有才华的浑蛋

  • 和我们共事的,不乏聪明绝顶的人。这类人你是知道的,他们具有惊人的洞察力,口齿清楚,解决问题时总能直击要害。你的机构中人才越密集,聪明人也就越多。但是,如果周围全是聪明人,你可能就有危险了。有时候,有才华的人听到的赞美之词太多,就会觉得自己真的比其他人更优秀。如果有他们认为不明智的想法,他们可能会报以嘲笑;如果有人发言不够清晰,他们可能会翻白眼;他们还会侮辱那些他们认为天赋不如自己的人。换句话说,这些人就是浑蛋。如果你在团队中倡导坦诚的文化氛围,就必须把这样的人剔除出去。许多人可能会认为“这个人确实很聪明,没有他不行”,但是,不管这样的人有多么出色,如果让他留在团队里,你营造坦诚氛围所付出的努力就不会有太好的效果。浑蛋对整个团队的效率有很大的影响,他们可能会将你的组织从内部撕裂。因为他们老是喜欢中伤同事,然后丢下一句:“我这是坦诚。”

  • 坦诚的文化并不意味着不加考虑地说出自己的想法。相反,每个人都需要仔细地审视一下4A准则。在你提出反馈之前,可能需要反思,有时还需要做一些准备,必要时可以让专门的人员进行监督和指导。

  • 在网飞任职初期,我所在团队的一名工程师在我的专业领域犯了一个大错。之后他还发来一封电子邮件推卸责任,而且也没有提出任何解决办法。我非常生气,于是打电话给他,希望他能纠正自己的错误。在电话中,我直言不讳地批评了他。我并不喜欢这样做,但是我觉得自己是在为公司着想。让我没想到的是,一周后,他的经理站在了我的办公桌前。他告诉我,他知道我与那名工程师联系过,并且认为从技术层面讲,我确实没有错。但他问我是否知道,自从我批评了那名工程师之后,那名工程师就一直情绪不振;还问我是否故意把他的员工搞得那么缺乏动力,工作效率低下。不,当然不是。那名经理继续说:“你应该把要求给我的工程师讲清楚,让他积极地去解决问题。你是这样做的吗?”“当然,那是肯定的。”“那好。今后请一直都这样做。”于是,我就一直是这样做的。这场对话持续了不到两分钟,但效果立竿见影。请注意,他并没有指责我品行有问题。相反,他问我:“你打算伤害公司的利益吗?”“你采取的方式合理吗?”这些问题实际上只有一个答案。如果他只是对我说:“你这人有点浑蛋。”我可能会回答:“我哪里浑蛋了?”但他通过几个问题,让我在回答的同时也进行了反思。

  • 贾斯廷部分遵循了4A准则。他的目的是帮助工程师走上正确的道路。他强调了要牢记公司的利益。或许他的意见也是可行的,但是他依然被认为有些过火,因为他通过反馈宣泄心中的不满,这就违反了第一条准则。

  • 此外,还有其他一些一般性的反馈准则,例如“还在气头上切勿发表批评意见”“在给予纠正性反馈时要注意语气平和”,等等。这些对于反馈都是有帮助的。

本章要点

  • ·一旦有了坦诚的氛围,高效率的员工将成为杰出的员工。坦诚的反馈将成倍地提高团队的工作效率。

  • ·在日常的会谈中引入反馈机制,从而为坦诚搭建舞台。

  • ·按照4A准则,指导员工有效地提供和接收反馈。

  • ·作为领导者,要不断征求反馈意见,在收到反馈时用认同提示予以回应。

  • ·要营造坦诚的氛围,先清除掉团队中的浑蛋。

  • 有了人才密度和坦诚的氛围,你就可以着手取消管控,营造更加自由的工作环境。

  • 迈向自由与责任的企业文化

3上 取消限期休假制度

  • 创造性工作的价值不应当通过工作时长来衡量。靠时间来衡量价值的想法源于工业时代,那时的工作都是靠人工完成,但现在主要由机器完成。

  • 在步入信息化时代的今天,人们关注的是你的成果,而不是你大量的付出。

  • 我担心的是,若是公司不给员工安排假期,员工就会放弃休假。我们“无期限的”休假制度会不会变成“无休假”制度?事实证明,我们很多杰出的创意都是员工在放松状态下的灵光乍现。

  • 休假能够让员工的身心得到放松,使他们能够进行创造性的思考,并且以崭新的姿态面对自己的工作。如果一直不停地工作,那么他只会在原地转圈,而无法从全新的角度去看待问题。

  • 公司借此向员工传递了这样一个信息:公司是信任员工的。从而进一步增强了他们的责任心。尽管如此,如果另外两步没有走好,我的噩梦就真的有可能成为你的现实。第一步——休长假,领导要带头

  • 在没有相关制度的情况下,员工休假的长短在很大程度上取决于他的领导和周围的同事。所以,如果你真的要实行无期限的休假制度,必须从鼓励领导休长假开始,让他们做出表率。

  • 由于没有制度的规定,大多数员工都会观察部门其他员工的情况,以了解“软性的限度”。

  • 随着公司规模的扩大,越来越多的部门并没有以里德为榜样,实行帕蒂所倡导的无期限的休假。在这样一些部门中,这项制度还真有点像是“无休假”的制度。不过,还是有很多部门领导学着像里德那样休假,并且让下面的人都看得到。一旦他们这样做了,员工们也会以他们为榜样,进而为公司带来很多意想不到的好处。

  • “作为领导,光靠说是不够的,员工们也在看我们是如何做的。如果我只是说‘我希望你们能够在工作与生活之间找到一个可持续的、健康的平衡点’,但自己每天却工作12个小时,那么员工也只会看我的行动,而不会听我说的话。”

  • 重要的并不是假期的长短,而是可以完全按照自己喜欢的方式来安排生活。只要你工作出色,没有人会在意你有没有休假。

  • 要实施无期限休假,领导做表率是第一步。另一个值得关注的问题就是取消休假期限以后,有的员工可能会觉得非常自由,于是会选择在不恰当的时候休假,而且一休就是几个月,以致对团队工作和公司业务造成影响。

  • 原有的休假制度取消后,员工会一时陷入迷茫之中:有些人会不知所措,直到老板明确告诉他们可以怎么休假;如果不告诉他们,他们是不会主动休假的。而另一些人会觉得他们完全自由了,因而做出一些极不恰当的决定,例如在不适宜的时候休假,从而给其他同事带来很多的麻烦。这不仅会降低团队的工作效率,而且可能导致被解雇的命运,这对谁都没有好处。

  • 由于没有书面的制约,每个部门经理都应当花时间与团队成员进行沟通,告知员工怎样做才合适。会计部门主管应当坐下来与团队成员交流一下,告诉大家1月份不适合休假,还可以分析一下哪些月份休假更合适。那位在厨房里泪盈盈的经理也应该与团队商量,共同设定一系列休假参数。例如“一次只能一名团队成员休假”,以及“在休假备案之前,应确保不会让团队其他成员感到非常不妥”。经理设定的情景越清晰越好。比如像那位会计部门的主管就可以这样对员工说:“若需要一个月的假期,至少得提前三个月提出来;若只是五天的假期,通常提前一个月就可以了。”

  • 网飞公司的宗旨是:一名优秀员工胜过两名普通员工。

  • 事实上,要想实行无期限休假并不难,你需要做的仅仅是创建一个相互信任的环境。我们公司就是基于以下三条准则:(1)始终为公司的最大利益行事;(2)绝不做任何妨碍他人实现其目标的事;(3)努力实现自己的目标。只要满足上述几条,员工完全可以按照自己的意愿休假。

  • 给予自由,再落实责任

  • 我曾以为如果我们不追踪记录员工的假期,公司会被搞得天翻地覆。但事实上,公司的秩序并没有发生大的变化,而员工的满意度更高了。一些喜欢特立独行的员工,比如像前面提到的工程师萨拉(连续工作三周,每周工作80个小时,然后跑去探访亚马孙森林亚诺玛米部落)就特别支持这一做法。我们的这一举措,使高绩效的员工可以更好地掌控自己的生活,同时又使每个人感到更加自由。由于我们的人才密度很高,我们的员工都非常认真负责;由于我们有坦诚的文化氛围,如果有员工滥用制度或自由,其他员工就会当面阻止,直接告诉他那样做的不良后果。

  • 差不多与此同时,公司里又出现了一些新的迹象。我和帕蒂都注意到,员工们在办公室更具有责任感了。这从一些小事情就可以看出来,比如会有人主动把冰箱里变质的牛奶拿出去扔掉。

  • 给员工更多自由,可以使他们更具归属感和责任感

  • 于是我和帕蒂便提出“自由与责任”的理念。我们之所以这样说,不仅仅是因为我们需要两者兼有,而是员工获得更多自由之后,自然就会产生归属感和责任感。我也逐渐明白了,自由与责任的关系并不是像我先前所想的那样背道而驰,相反,自由是通往责任的一条途径。

3下 取消差旅和经费审批

  • 如今在网飞,我不希望公司任何人在这种没有意义的讨论上浪费时间;我更不希望有才华的员工在发挥聪明才智的时候,却被一些愚蠢的规章制度困扰,这无疑会破坏富有奇思妙想和充满创造力的工作氛围。

  • 我们确定了关于公司开销的第一条准则:怎么花自己的钱,就怎么花公司的钱

  • 据一项研究显示:被调查者一旦确信自己的行为不会为人所知,远超半数的人会利用漏洞,为自己谋取更多利益。

  • 没有透明度,就没有财务报销的自由。

  • 正是公司给予员工的自由,让他在关键时刻做出了有利于公司的决断。当然,自由不是取消报销制度的唯一好处,还有一个好处就是减少流程,提高效率。

  • 随着一个快速灵活的初创企业发展为成熟的企业,公司往往会建立一套完整的体系对员工的支出进行监管。这让管理有了一种控制感,但往往也会拖慢公司业务的进程。

  • 企业拥有一支高绩效的团队,员工才会认真负责地工作;企业拥有坦诚的文化氛围,员工才会互相监督,共同维护公司利益。在此前提下,企业可以放松对员工的管控,给予他们更多的自由。

本章要点

  • (上)·企业取消限期休假之后,员工休假无须事先获得批准,员工本人及上级领导无须记录休假时长。·员工自行决定是否休假及休假时长,几个小时、一天、一周或一个月都可以。·取消限期休假会造成制度空缺。应为员工提供请假情景以填补制度空缺。但这一切需要基于充分的讨论,以确定员工在何种情景下适合休假。·老板的表率作用很关键。取消了限期休假制度,但没有带头休假的领导,整个企业或者部门等于没有假期。
  • (下)·企业取消差旅及报销制度后,应鼓励管理人员就员工如何进行事前支付及事后审核设定相关情景。如果有超支的情况,需要设定更加详细的情景。·企业取消费用管控之后,财务部门每年需要对收据进行抽检。·如果员工滥用权利,无论其表现多么优秀,都应予以开除并向全体员工进行通报。这一点非常必要,以此告诫其他员工这类行为的严重后果。·员工的自由消费可能会增加企业成本。但相较于超支所增加的成本,员工自由所带来的收益会更高。·由于员工开支自由,他们在该花钱的时候便能够及时地做出决定,从而有助于业务的开展。·由于不存在采购订单及采购流程的管理成本和时间等待,企业能够节约更多的资源。·相对于一个靠各种规则构建的体制,员工在自由环境中的花费可能更小。你告诉员工你相信他们,那么员工也会向你表明,他们值得你信任。
  1. 继续探索提高人才密度的新方法。为吸引和留住优秀人才,我们必须确保薪酬具有足够大的吸引力。
  2. 继续探索提高公司坦诚度的新方法。企业要想在管控方面放手,就必须确保在没有监管的情况下,员工能够对信息有充分的了解,从而做出正确的决定。这就要求提高组织的透明度,做到信息公开化。如果我们希望员工能够自己做出明智的决定,就需要他们像高层一样,了解公司的业务状况。

4 支付行业最高薪资

  • 一名最好的程序员为你增加的价值何止10倍啊,简直有上百倍!我曾在微软与比尔·盖茨共事,据说他对这个问题有更深刻的理解。他经常引用这样一句名言:“一名优秀车工的工资是一名普通车工的好几倍;而一名优秀程序员写出来的代码比一名普通程序员写出来的要贵上一万倍。”在软件行业,这种说法虽有争议,但也算是一条尽人皆知的原则。

  • 我和帕蒂就想,网飞的哪些部门也可以遵循精英原则呢?对此,我们把工作分成了操作型和创造型两类。

  • 但是在网飞,像这样的岗位并不多。我们大多数工作都需要依靠员工的创新和创造力。

  • 对于创造性的工作,最优秀员工的工作效率可以轻轻松松地高出普通员工10倍以上

  • 对公司所有操作型的工作,根据明晰的标准,按市场中间价开工资。但是对于创造型的工作,我们会给某一名能力超强的员工开出市场上的最高工资,而不是花同样的钱去雇十几名或更多表现平平的普通员工。这样,我们的员工团队就得到了精简。我们靠的是一个高效率的员工来代替很多普通的员工,同时,我们给他的工资也是相当可观的。

  • 我们最不希望的是,我们的员工在12月份才实现自己同年1月份设定的目标,结果仍然获得了奖励。这种做法的风险是,员工会专注于目标本身,而不考虑现阶段怎么做才对公司发展最有利。

  • 除此之外,我也不能接受这样的想法,即如果你拿出更多金钱摆在优秀员工的面前,他们就会更加卖力地工作。绩效高的人会自觉地追求成功,会竭尽全力做好自己的工作,无论是否有奖金摆在他们面前。

  • 我很喜欢前德意志银行首席执行官约翰·克莱恩说的这样一句话:“我不知道为什么要跟你签订一份含有奖金的合同。我不会因为有人给我的奖金多就更加努力,也不会因为给我的奖金少就松懈下来。”任何能力与薪水相称的管理者都会这么说。

  • 研究也证明了里德的感觉是对的。依照绩效制定的薪酬对日常工作有一定激励作用,但实际上也影响了创造力的发挥。

  • 创造性工作要求在一定程度上解放你的大脑。如果你总想着要怎么做才能表现好,才能得到高额的奖金,那么你就缺少开放的认知空间,产生最好的想法和最好创意的可能性也微乎其微。结果,你反倒做得更差。

  • 我在网飞的发现也确实如此。在我们用足够高的工资帮助员工减轻家庭负担之后,他们最具创造力。但是如果他们并不确定自己能否得到额外的报酬,创造力就会下降。由此可见,有利于激发创造力的,是足够高的工资,而非绩效奖金。

  • 当我们决定开出丰厚的工资就不再支付额外奖金之后,一个大大的惊喜是我们吸引了更多的优秀人才。

  • 网飞是愿意花钱来吸引和留住人才的,因此他们与员工的谈话主要是想弄清楚两点:(1)估计自己未来的员工在其他公司能挣多少钱;(2)网飞支付的薪水会略高于其他公司。

  • 起初,新员工会因为拿着市场最高工资而动力满满。但不久之后,随着他自身能力的提高,竞争者会开出更高的工资诱使他跳槽。如果他的实力跟高薪匹配,那么他的市场价值就会继续上升,跳槽的概率就会越来越大。因此,自相矛盾的是,每一家公司在薪资方面的做法就像是在鼓励员工跳槽,从而降低了公司的人才密度。公关总监若昂讲述了他在前雇主那里遇到的类似问题。

  • 工资审查的时候,大多数公司是用“加薪池”和“工资等级”来决定工资的涨幅,而非员工的市场价值。

  • 如果一个员工放弃原来的工作,跳槽到一家新的公司,那么他工资的平均涨幅将达到10%~20%。一直待在同一家公司,是没什么“钱途”的

  • 如果存在长期雇佣关系,而且员工的市场价值不太可能在几个月内飙升,那么加薪池和工资级别对大多数公司的员工都是有效的。但是如果考虑到员工频繁地换工作,而经济情况又在不断地变化,这一做法显然就不再适用了。

  • 但是,像网飞这种给员工开市场上最高工资的模式确实很少见,也很难理解。

  • 向猎头了解自己的市场价值

  • 在世界上几乎所有的公司,员工去参加其他公司的面试都会让现任老板感到生气、失望,甚至会疏远员工。员工对老板来说越有价值,老板就会越生气,其原因显而易见。一名优秀的新员工哪怕仅仅是决定去其他公司面试,老板都会有投资受损的风险;如果他参加了面试,发现新工作比自己当前的工作好,那么老板就会失去这名员工,至少是失去他工作的热情。这就是为什么大多数公司的老板会让他们的员工产生这样一种感觉:跟其他公司的招聘人员交谈就像是做了叛徒似的。

  • 拉里的上司特德·萨兰多斯在跟员工分享当月最新市场信息时说:“市场对人才的需求在持续升温,你们将会不断接到招聘人员的电话。这些电话可能来自亚马逊、苹果和脸书。如果你们不确定自己目前拿到的是不是市场最高工资,你们可以接听这些电话,弄清楚自己在那些公司可以拿到多少钱。如果你们发现同样的工作,它们给的工资比我们高,那么请告诉我们。”拉里听罢十分吃惊:“网飞大概是唯一一家公开鼓励员工去跟竞争对手交谈,甚至去面试的公司。”

  • 如果我发现员工在别处能挣到更多的钱,我会立马给他们涨工资。”为了留住最优秀的员工,最好在他们得到其他工作机会之前,主动把工资涨上去。当然,拉里自己也是这一方案的受益者,他得到了更高的工资,而特德也留住了他这样一个人才。但是,特德的这种做法听起来未免风险太大。有多少人接到招聘人员的电话之后,就会喜欢上新的工作,最终离开自己的团队呢?对于这个问题,特德是这样解释的:如果市场对人才的需求持续升温,招聘人员就会不断给优秀人才打电话,我们的员工自然会对新工作产生好奇。这时候,我说什么都没有用,一些员工还是会去跟他们交谈,然后去参加面试。要是我不明确允许他们这么做的话,他们就会背地里偷偷摸摸地去参加面试,然后跳槽,那我连挽留他们的机会都没有。就在我公开发布这项规定的一个月前,我们损失了一位非常优秀的高管,她的才华是无可取代的。她来找我的时候,已经接受了其他公司的工作,所以事情已经没有挽回的余地了。她告诉我,她喜欢网飞的工作,但是其他公司给她的工资要比网飞高40%。听罢,我的心都沉了。要是我早知道她的市场价值已经有了变化,那我一定会对她的工资进行相应的调整!这就是我允许员工尽可能多地去跟其他公司交流的原因,但前提是他们得光明正大地去做,并且回来后将获取的信息告诉我。

  • 网飞的规则就是,当招聘人员打电话给你的时候,你在说“不用了,谢谢”之前,先问一句:“多少钱?”

  • 第四个关键点为了提高员工队伍的人才密度,在所有创造型的部门,我们宁愿聘用一名优秀的员工,也不要聘用10名或者更多普通的员工。优秀人才的市场价格无论有多高,都要以市场最高价聘用他们。为防止竞争对手给他们开出更高的工资,每年至少给他们调一次工资。如果你当前的预算没法给这些优秀员工开出市场最高价,那就算解雇一些没那么优秀的员工,也一定要把他们的工资提上去。这样,公司的人才密度才会更高。

本章要点

  • ·如果要打造一支有创造力且人才密度高的团队,目前大多数公司的薪资方式都不够理想。
  • ·把你的员工分为创造型和操作型两类。给创造型的员工市场最高的工资,这就意味着招到一个能力超群的人,而不是10个或更多水平一般的人。要努力打造一支完全由高水平人才组成的团队,这对于一些关键技术和重大问题的解决尤为重要。
  • ·不要搞绩效奖金,也不要股权激励,要把这些全部包含在工资里面。
  • ·引导员工发展自己的人际网络,及时了解自己以及所在团队不断变化的市场价值。
  • 这可能意味着他们会接听招聘人员的电话,甚至去参加其他公司的面试。然后,要及时对他们的薪资进行相应的调整。迈向自由与责任的企业文化人才密度的不断增加,为提高员工决策的自由度做好了准备。不过,你得首先把坦诚放在第一位。在大多数公司里面,大部分员工即使很有才华,在决策上的自由度也相当小,因为公司最高层所掌握的一些信息对他们而言都是秘密。如果你的公司全都是责任心很强的员工,他们自励、自觉且自律,那么公司的很多信息都可以和他们分享。而在大多数公司里,这些信息可能都是不公开的。

5 开卷管理

  • 要迅速建立信任,最好的办法莫过于直接说出一个潜在的秘密。

  • 此处,我们暂时使用“潜在秘密”这样一个表达。秘密这个词的诡异之处就在于,一旦你把它告诉了某一个人,它就不再是秘密了。

  • 不论大事小事、好事坏事,如果你的第一反应是把信息公之于众,那其他人也会这样做。在网飞,我们称之为“阳光行动”,对此我们付出了很多努力。

  • 我不想让我的员工觉得自己是在为网飞工作,而是让他们感觉自己是网飞的一分子。”从那时起,我就认定,如果你在网飞工作,没有人会为你在头顶撑一把伞。你要做好淋雨的准备。

  • 我的目标就是让员工感到自己是公司的主人,从而增强他们的责任感。不仅如此,向员工公开信息还有另外一个好处:它使我们的员工变得更加聪明了。你把那些通常只有高管才知道的信息直接分享给底层员工,他们就可以自己做判断,完成更多的工作。由于不需要浪费时间去寻求信息和获得批准,他们的工作效率会更高。没有上级的指示,他们自己就可以做出更好的决策。

  • 对于我们的员工来说,透明度代表我们相信员工能够认真负责地对待工作。我们对他们的信任又会增强他们的归属感、使命感和责任感。

  • 但是,尽管会有个别员工辜负你的信任,但处理完这一个案之后,请继续对其他员工保持透明。不要因为一个人的失职而迁怒于大多数的人。

  • 网飞是真正地把员工当成可以独立处理复杂信息的成年人来看待,我很欣赏这一点。这种做法会给员工带来一种巨大的认同感与责任感。

  • 在网飞,你的住房状况以及你各方面的生活都跟工作无关。公司是把你当成一个正常的成年人看待,同时把所有的信息都跟你分享,以便你能做出明智的决定。

  • 总之,透明是我们的准则,但任何事情都不是绝对的。

  • 我确实有一份只对我的6名直接下属开放的文档。在这份文档里,我们可以发表任何东西,包括对“爱尔兰共和军问题”的担忧,这对公司其他成员是不开放的。但这样的情况很少。一般来说,每当我们举棋不定的时候,我都会尽早公开整个实施流程,以此获得员工的认同感。这也让员工明白,尽管情况总在不断地变化,但他们至少可以随时获知事情的动向。

  • 我也知道,要真正做到这一点是有难度的。任何想要信息透明化的领导都会认识到:将信息公之于众肯定会跟个人隐私存在冲突。毋庸置疑,二者同等重要。但问题是,当公司有人离职的时候,每个人都很想知道原因,就算你极力掩饰,终有一天也会真相大白。相反,如果你坦诚地把原因告诉大家,那么流言就会戛然而止,员工对你的信任也会只增不减。

  • 如果事情与工作相关,那就应该告知每一个人;而如果只是牵涉员工的私人问题,那么愿不愿意分享就由员工自己决定吧。

  • 如果一个人公开承认自己的错误,人们会觉得他更加值得信任,这是人的本性。从那以后,每当觉得自己犯了错误,我都毫无保留地说出来。我很快就发现,领导把自己的错误公之于众,一个最大的好处就是可以鼓励员工把犯错当作一件很正常的事情,继而鼓励他们在不确定一件事情是否能够成功之前,敢于去冒险尝试。这样,整个公司的创新能力就能得到大大的提升。由此我们得出一个结论:自我揭露建立信任,主动求助促进学习,敢于认错赢得谅解,而公开你的失败则可以鼓励更多员工大胆地放手一搏。这就是在场景四的测试中,我毫不犹豫地选择B的原因。谦逊是一位领导、一个模范人物的重要品质。当你取得成功的时候,要轻描淡写地带过,或者让别人来说。当你犯了错误的时候,一定要清楚而响亮地说出来。这样,其他人就可以从你的错误中学习,从你的错误中获益。换言之,就是——成功了小声说,犯错了大声说。

  • 坦诚地对待错误,对人际关系、健康状况和工作表现都是有利而无害的。

  • 这种倾向被称为“出丑效应”,指一个人犯了错误之后的吸引力是增加还是减少,取决于他总体表现出来的能力。

  • 一名领导有卓越的才能,又深受团队的爱戴,那么当他把自己的错误拿出来“见阳光”时,就更容易建立起信任并起到激励的作用,他的公司也会因此受益。而对于一名刚刚崭露头角或者没有取得信任的领导人来说,这项建议可能并不适用。在大声说出自己的错误之前,你得先让员工相信你的工作能力。

  • 如果你拥有了最优秀的员工,并且营造了坦诚反馈的文化氛围,那么,公开企业的秘密会增强员工的主人翁意识和责任感。你要相信自己的员工能够正确把握并处理重要信息,而你的员工也会向你表明:他们是值得信任的。

本章要点

  • ·要建立透明的企业文化,就要考虑一下你平时传递给员工怎样的信号。不要大门紧锁的办公室,不要充当警卫的助理,其实所有的地方都不用上锁。
  • ·对员工开诚布公。教会他们怎么去阅读财务报表。跟公司里的每个人分享敏感的财务和战略信息。
  • ·如果公司有重组或裁员之类的打算,在事情确定下来之前,提前跟员工说明情况。这可能会引起一些焦虑和不安,但是你建立起来的信任比负面影响更重要。
  • ·当公司透明度与员工的个人隐私相冲突时,请遵循以下原则:如果是工作中出现的状况,那么请果断选择透明,坦诚地告知所发生的事情;如果与员工私人生活相关,那么请告诉你的员工,以你的立场不便透露,如果他们关心的话,可以直接去问当事人。
  • ·只要你的能力已被大家认可,你就可以公开地告诉大家你所犯过的错误,并且鼓励各部门的负责人也这么做。这可以在整个机构内增加信任度,传达良好的意愿,同时激发员工的创新能力。

6 无须决策审批

  • 从那时起,我开始意识到这种决策金字塔可能造成的弊端。我作为公司老板,肯定会有自己的观点,也很乐意和大家分享。但对于日常事务的决策,比方说买多少张光碟,我并不是最佳人选。我告诉他:“特德,你的工作不是要让我高兴,也不能因为我赞成,就做出这样的决策。你的决策应当有利于公司的发展,不能因为我的错误决定而让公司的业务受到影响!”

  • 在大多数公司,老板都会对员工的决策进行审批。然而,这种审批方式会限制员工的创新,并阻碍公司的发展。在网飞,我们鼓励员工不要一味认同上司的决策。我们不希望员工因为上司的否定而放弃任何一个好主意。这就是我们一直强调的:工作的目的不在于取悦老板,而在于对公司有利。

  • 对于大多数企业,无论是否实行了微观管理,员工都倾向于做出最容易获得上司青睐的决策。对此最合理的解释是:上司地位高,所以知道得多。如果你不想丢掉工作,也不想因违背上级命令而遭到指责,那你就乖乖地听话吧。

  • 但是,这类自上而下的决策模式并不值得我们学习,因为我们相信:公司的员工有了自主决策权,效率才会更高,才会更具创新性。我们一直在努力培养员工独立的决策能力,公司高层也很少参与具体事务的决定,我们对此感到骄傲。

  • 实行分散决策模式的前提是高人才密度和高透明度。如果尚未满足这两个条件,实行这样的决策模式恐怕只会适得其反。而一旦满足了条件,你就可以考虑逐步取消管控。你不仅可以取消假期追踪这一类管控制度,还可以取消工作环节中的种种管控,从而大幅提高员工的创新能力。

  • 如果你的员工足够优秀,你可以把决策权下放给他们,让他们去实施那些他们相信能够带来效益的好点子。这样,创新也会随之产生。当然,对于某些产业而言,必须保证零失误。但网飞的业务并不涉及与安全相关的产业,如医疗、核能等。我们的市场就是需要创新。从长远来看,我们面临的最大危机不是犯错误,而是缺乏创新,缺乏让客户满意的娱乐创意,这将最终导致我们被市场淘汰。

  • 如果你希望团队更富有创新性,那么,你需要教会员工自己寻求途径推动业务发展,而不是一味地讨好老板。同时,你也需要鼓励员工敢于挑战自己的上司

  • 网飞创新过程如果你有一个令自己心动的主意,你需要:1.收集异议或者交流想法。2.对重大决策进行彻底检验。3.知情指挥要大胆下注。4.庆祝成功,正视失败。

  • 决策不是个人成功或失败的问题,而是一个学习过程;员工通过不断地学习,就能推动业务向前发展。同时,这样的交流还可以帮助新员工,让他们敢于公开承认自己的失败,就像网飞的其他员工一样。

  • 如果你一直抓着员工的失误不放,这无疑是断送了未来的冒险之路。这样一来,尽管员工知道你倡导分散决策,但不会再按照你所倡导的那样去做。

  • 如果你能坦然面对失败,所有人都能受益。你之所以会成功,是因为周围的人相信你告诉了他们实情,知道你会对自己的行为负责。团队之所以会成功,是因为每位成员能够从失败中吸取教训。公司之所以成功,是因为每位员工都能清楚地认识到,失败是创新的必经之路。我们不应该惧怕失败,而应该更加坦然地去面对。

  • 在网飞,我们与其将下注失败看作一件隐秘的事情,还不如把它看成是一个错误。当克里斯谈论曾经的失败时,无论是“探索者”还是“纪念品”,他都不会觉得尴尬。这也正是网飞所倡导的大胆思考,敢于决策。在这种情况下,你会觉得让你站在讲台上,或通过文字把自己的失败讲出来也不是什么困难的事。你会大胆地说:“看吧,我下了这样一个赌注,可结果并不如意。”

  • 但有些错误确实会令人感到很尴尬,尤其是因为你的判断出现重大失误,或者因为疏忽大意造成的严重错误。

  • 如果出现这种令你感到尴尬的严重失误,你难免会想到逃避责任,这样的想法在网飞也是不可接受的。面对这样的错误,你就必须更加地坦诚。只要不是经常犯同样的错误,你把它说出来,是能够获得谅解的。但是,如果你始终只字不提,然后继续犯同样的错误(你越是否认,就越容易犯同样的错误),那最终将导致更加严重的后果。

  • 公司不会因为这种事情解雇我们的。公司解雇的是那些不敢冒险、不敢大胆采取行动的人,还有那些不愿在公开场合谈论失败的员工。”当然,再有这种媒体推广活动,我再也不敢不事先沟通了。否则,我就真的会被解雇了。我利用假期剩余的时间,向所有人讲述了我犯的错误,以及我从中吸取的教训。我写了很多备忘录,打了很多电话。我的整个假期都在曝光,但不是在希腊海滩的阳光下。

  • 如果你的团队具有足够高的人才密度和组织透明度,那么决策过程就能够更加迅速,且更具创新性。员工可以充分发挥想象,调查论证,最后实施计划,即使遭到上级的反对也不会影响计划的进行。

本章要点

  • ·在追求高效和创新的公司里,重大问题的决策权应当分散在各个不同层次,而不是按等级进行分配。
  • ·要让这种决策模式正常运作,领导应该让员工明白这样一个原则:“工作不是为了取悦你的上司。”
  • ·新员工加入公司时,告诉他们每个人都有一把下注的筹码。有些下注会成功,有些则会失败。公司看重的是员工下注的总体结果,而不是单独某一次下注。
  • ·为帮助员工做出正确的决策,应鼓励他们收集异议,交流意见,对重大的决策还要进行充分的调查论证。
  • ·鼓励员工即使遭遇失败,也要敢于把失败讲出来。

7 员工留任测试

  • 我们公司实行分散决策的机制,也就是说,决策者寻找最佳人选,而被选择的人又继续寻找他们认可的最佳人选,依此类推,企业就得以良好地运转。特德将这种模式称作“层级选择”,这就是建立在高人才密度基础之上的生产力。

  • 经过讨论之后,帕蒂提议我们应该把网飞想成是一支专业运动队。

  • 把高人才密度的工作环境比作专业运动队,我觉得十分贴切,因为职业运动员都具有以下特质:·追求卓越。负责人保证每个职位在任何时候都是最佳人选。·训练就是为了胜利。教练和队员都必须不断给予和接受坦诚的反馈。·明白光有努力是不够的。记住:如果你付出了一等努力却只收获了二等成绩,你可以赢得我们的尊重与感谢,但也不得不下场休息。

  • 在一个高绩效的团队里,精诚合作与彼此信任缺一不可,所有队员既要个人能力突出,又要灵活配合。一名优秀队员,不能仅仅个人表现卓越,还需要有无私的精神,将团队利益置于个人得失之上。他要把握传球时机,懂得如何帮助队友,明白胜利的唯一途径就是让整个团队取得胜利。这恰恰是网飞想要培养的企业文化。从这时开始,我们就在公司宣传这样一个口号:我们是一个团队,不是一个家庭。

  • 我们认为员工留任测试适用于公司的每个人,也包括我们自己。设想一下,要是别人坐了我的位子,公司是不是会更好?我们这样做的目的是为了让离职的人不会感到羞愧。想想曲棍球这样的奥运会项目,被替换下场的球员都会感到沮丧,但他也会因其曾拥有的高超球技和过人胆识帮助球队排名第一而受到人们的尊敬。网飞的员工离职时,我们也是一样的想法。我们永远都是朋友,离开网飞并不是一件丢人的事情。

  • 事实证明,留任测试是行之有效的,公司各级管理者都一直在坚持这项举措。我也向我的上级——董事会提议,我自己也不应该有特权,他们不必等到我犯了错误才找人取代我。只要能提高公司的绩效,他们完全可以聘用一位更有能力的首席执行官。每个季度,我都保持着积极向上的心态,为了保住自己的职位而不断学习,努力工作。

  • 在网飞,或许你一直在兢兢业业地工作,你全身心地投入公司的发展,也确实做出了不错的成绩,然而有一天,当你走进办公室,却突然被告知自己被解雇了……这无异于一个晴天霹雳!被解雇的原因不是来势汹汹的金融危机,也不是大规模的计划外裁员,而只是你的成绩没能达到领导的期望,因为你只是做到了称职。

  • 如果你加入一个由10个人组成的团队,你工作的第一天就会有人告诉你,无论你干得多么好,整个团队只有两个人会得到“优秀”,7个人将获得“合格”评价,而剩下的那个人就会获得“不合格”评价。这样,员工的心思都花在内部争斗上,反倒忽略了与其他公司的竞争。

  • 据说,有一名微软的工程师也说过:人们总是大张旗鼓地阻挠他人进行努力。在微软,我学会表面上彬彬有礼,同时向同事隐瞒必要的信息,以确保他们的排名不会超过我,这是我在微软学到的最有价值的事情。

  • 我们鼓励公司的经理们采用员工留任测试,但我们十分谨慎,并不会采取堆栈排序这样的评估手段。无论是末位淘汰制还是“后百分之几的人必须被开除”,这些都是网飞最为排斥的规定。

  • 更为重要的是,那些手段虽然让经理开除了表现平庸的员工,但同时也扼杀了团队。

  • 我们要的是高绩效的员工同网飞的竞争者到市场上去拼杀,而不是自相残杀。末位淘汰制提高了人才密度,却阻碍了团队的高效协作。

  • 幸运的是,我们不需要在高人才密度和通力合作之间做出艰难抉择,员工留任测试可以实现两者兼得。其中关键的原因在于,我们并不是一个真正的职业运动队。在网飞的团队中,每个位置并没有固定员工数量,我们不是在严格的规则下开展运动项目,我们也无须限制参与的人数,没有人会因为同事的优秀而失去自己的工作。恰恰相反,我们团队中优秀的人才越多,我们就越能创造非凡的成就;成就越丰,队伍的成长就越快;队伍越大,我们能提供的职位就越多;职位增多,我们就能为高绩效人才开辟出更广阔的施展空间。

  • 评论:[^]所以说奈飞的留任测试和通用电气、微软自己现如今在中国公司很流行的末位淘汰制的区别在于,末尾淘汰制是在团队内部进行排名,你的竞争对手是你的同事。为了不被同事甩在后面,我们就口蜜腹剑,当面一套背后一套,从不推心置腹。这样的结果就是恶性竞争,各自为战。而留任测试中,员工的竞争对手是他自己都看不到的,市场上其他公司的任何优秀员工。他们需要在整个市场上保持竞争力,才不至于被替换掉。

本章要点

  • ·为鼓励经理们重视绩效,要教会他们运用员工留任测试思考这样的问题:“在我的团队中,如果谁告诉我他要跳槽去别的公司从事类似的工作,我是否会尽最大努力挽留他?”·避免堆栈排序制度,因为这会导致内部竞争,破坏团队协作。
  • ·一种高绩效的企业文化,应该把公司看作一支职业运动队,而不是一个家庭。要让经理在团队中培养员工的责任感,让团队富有凝聚力,让员工之间充满浓浓的情谊;同时也要果断地调整人员配置,确保每个位置的员工都是最佳人选。
  • ·当你意识到不得不开除某个员工的时候,不要再为他制订绩效改进计划,那样只会让当事人感到难堪,同时消耗了企业的人力物力,可以考虑把那笔钱作为遣散费直接发给他。
  • ·精简机构营造了高绩效的文化,同时可能让员工感到些许恐惧。公司可以鼓励员工进行“员工留任提示”,让他直接问上司:“如果我想要辞职,你会在多大程度上挽留我?”
  • ·当一名员工被解雇之后,坦诚地向其他员工公布解雇的原因,并真诚地解答他们的困惑,这会消除他们心里的恐惧,同时也能增加他们对上司、对公司的信任。

8 反馈循环

  • 网飞有这样一条规则:“不在背后议论别人。”我们在别人背后的评头论足越少,导致低效和负面情绪的闲言碎语就越少,我们就越能摆脱“办公室政治”的不愉快。

本章要点

  • ·做到坦诚就像去看牙医。就算你倡议人人都要每天刷牙,也还是有些人不会这样做;有些人刷牙的时候也会漏掉一些不顺手的地方。每6~12个月进行一次彻底的检查,保证牙齿干净,保证反馈清晰。
  • ·在一个坦诚的工作环境中,绩效考核并不是最好的机制,因为绩效考核获得的反馈通常都是自上而下的,而且往往都只来自一个人(老板)。
  • ·360度书面反馈是一个很好的年度反馈机制,但是要避免匿名和量化评分,不要把结果和升职加薪联系起来,并且鼓励员工自愿给出公开的意见和建议。
  • ·“360度面对面”晚餐同样也是行之有效的反馈手段。留出几个小时的时间,组织者给出明确的指示,遵循4A准则,使用“开始、停止、继续”三类意见和建议,给出大约25% 的肯定意见和75%的发展性意见——所有建议应该是切实可行的,不要说空话。

9 情景管理而非控制管理

  • 情景管理而非控制管理。如果在别的公司,一旦涉及大笔经费的支出,公司高层一定会牢牢抓住话语权,反复讨论之后再做定夺,而网飞却并非如此。这就像亚当解释的那样:“特德不会替我做决定,但他会设定情景,帮助我考量决策是不是符合公司的战略需要,而他预设的情景正是我决策的参考依据。”

  • 控制型管理还是情景管理?最广为人知的决策方式就是领导拍板。领导需要审批决策,指导过程,选拔人员。有时,他可能会直接告诉员工该做什么,并且经常进行检查,纠正那些与他的意图不符的做法;有时,他也会试着给员工更多的权力,用流程控制代替直接监督。

  • 只有在条件成熟的情况下,情景管理才能发挥作用,其中首要的条件就是公司要拥有高人才密度。

  • 要选择控制型管理还是情景管理,你需要回答的第一个问题就是“公司员工属于哪个层次的人才”。如果你的员工工作还很吃力,你就需要加强监督,不断检查他们的工作情况,确保他们做出正确的决定;假如你拥有一支高绩效的工作团队,情景管理就能让团队获得更多的自由,同时迸发出更强的创造力。

  • 但是,到底采用控制型还是情景管理型也不完全是由人才密度决定的,你同样需要考虑行业特点和预期目标。

  • 实现情景管理的第三个必要条件,除了高人才密度(首要条件)、创新性目标(而非错误防范性目标)之外,你还需要“松散耦合”的体制。

  • 与之相对的是松散耦合系统,系统中各个模块没有那么紧密的联系,可以只更改特定的模块,不必重新构建基础。这就是软件工程师更偏爱松散耦合系统的原因。在松散耦合系统中,对特定模块的修改不会影响系统的其他部分,整个系统非常灵活。

  • 在一个组织机构中,不同团队就像是电脑系统的不同组成模块。在紧密耦合型公司,大老板做出决策并自上而下层层传递,往往导致众多部门相互牵扯。一旦某个部门出现问题,反馈必须逐级上传至大老板。而在松散耦合型公司,只要确定出现的问题不会波及其他部门,经理甚至员工本人都有权自行做出决定或解决问题。如果老板的指令需要公司自上而下逐级传递,那么这个公司采用的多半是控制型管理模式,相应的就是紧密耦合型体制。在一个紧密耦合型企业中,如果想要尝试对一个部门或一个团队进行情景管理,你会发现这种体制让你寸步难行,因为所有重要的决策都是由最高层做出的。你也许想把决策权下放给你的员工,但你做不到,所有重要的事情不仅要得到你的批准,还要得到你的上司和你上司的上司的批准。

  • 即使公司已经实现高人才密度,并且将创新作为发展目标,但若是解决不好耦合机制的问题,情景管理可能还是无法实现。

  • 网飞就是一家采取“知情指挥”模式的松散耦合型公司。公司的决策制定权高度分散,集中控制的流程、规则或者政策也很少,这就给了员工极大的自由度,提高了部门的灵活性,加快了整个公司的

  • 如果想要公司在松散耦合的体制中高效运转,让员工个人也能做出重大决策,那么老板和员工必须就他们的目标达成一致。只有领导和员工认识清晰,目标一致,松散耦合的体制才能发挥作用。这种一致性能够驱动员工做出决策,以完成整个组织的使命和战略任务。所以,网飞一贯奉行的准则就是:认识一致,松散耦合

  • 当你的员工做了一些蠢事,不要指责他们。相反,你应该问问自己,你在情景设定上犯了什么错:在阐释战略目标的时候,你有没有讲得足够清晰并且让员工受到鼓舞?你有没有阐明所有的可能性和风险,从而帮助你的团队做出正确的决策?你和员工在观点和目标上有没有达成一致?

  • 无论哪个地区,哪个行业,大多数组织机构实行的都是这种金字塔形的决策模式。这种模式包括两个方面:一方面由老板做出决定,然后自上而下逐级传达,一直落实到金字塔底端;另一方面是低级别员工只能处理细枝末节的小问题,稍大一点的问题则需要层层上报。

  • 但在网飞,如同我们一贯所坚持的那样,知情指挥就是决策的制定者,不是任何事情都由老板决定。老板的工作是设定情景,帮助团队做出最有利于公司的决策。我们发现,这种管理模式不再像一座金字塔,而更像一棵大树。首席执行官就是树的根部,而伸展开去的知情指挥则位于树枝顶端,负责各项具体决策的制定。

  • 一个在松散耦合的体制下运作的机构,如果具备高人才密度,而且以创新作为首要目标,那么就不建议选择传统的控制型管理模式。与其通过监管流程减少错误,不如设定清晰的情景,统一认识,确定共同的奋斗目标,同时把决策自由交给知情指挥。

本章要点

  • ·要实施情景管理,你需要拥有高人才密度;你的目标应该是创新而不是防范错误;你需要构建一套松散耦合的体制。

  • ·一切要素到位之后,不要告诉员工应该做什么,而是应该通过讨论来设置情景,达成一致,最后让他们自己去做出正确的决定。

  • ·如果你的员工做了一些蠢事,不要指责他们。相反,你应该问问自己,你是否在情景设定上犯了什么错误?在阐释目标和战略意图的时候,你有没有讲得足够清晰并且让大家深受鼓舞?你有没有把所有的假设和风险讲清楚,从而帮助你的团队做出正确的决策?你和员工在观点和目标上有没有达成一致?

  • ·一个松散耦合体制下的机构应该是树形联系而非金字塔形联系。老板就像是树根,延伸出高管们组成的树干,最后支撑起做出决策的枝丫。

  • ·如果你的员工能够利用好你和你周围的人传递出来的信息,能够自己做出决定将团队带向预期的方向,那你的情景管理就取得了成功。

  • 我们探索了提高人才密度与坦诚度的基本要素,剔除了烦琐的政策和程序,从而为员工提供更多的自由;同时,也创造了一个高效、灵活的工作环境。大多数公司都有以下政策和流程,但网飞没有:休假制度决策审批制度经费审批制度绩效改进计划审批流程加薪池关键绩效指标目标管理差旅制度委员会决策制定合同签署相关政策工资级别薪资等级绩效奖金以上政策对员工是一种管控,而不是一种激励;但没有这样一些管控措施,企业又很容易陷入混乱。因此,你需要切实地提高员工的自律意识和责任意识;帮助他们获得足够的知识以做出明智的决定;建立反馈机制以激发员工的学习主动性。这样,企业将高效运转,给我们带来大大的惊喜。

10 走向全球的网飞文化

  • 我们的4A准则是:·目的在于帮助。·反馈具有可行性。·感激与赞赏。·接受或拒绝。现在再加上第五条:·调整、适应——根据你所处的文化环境,调整你提出和接受反馈的方式,以获得你所期待的效果。

  • 在文化中庸的国家,员工进行非正式反馈的可能性不大,可以实施更为正式的反馈机制,将反馈更多地纳入正式议程。

致谢

  • 人才密度和坦诚这两个概念贯穿本书始终,是本书两个最基本的概念。

总结

  • 人才密度 — 大前提!

  • 管理放权

  • “情景管理模式”

  • “人才重于流程,创新高于效率,自由多于管控”

  • 自由是通往责任的一条途径

  • “心理安全”原则?

  • “过期作废” 鼓励休假

  • 惊人的判断力……判断力几乎可以解决所有模棱两可的问题,而流程做不到

  • 营造一个完全由优秀员工组成的工作环境。

  • 坦诚的反馈;大多数人出于本能,还是能够理解真相的价值

  • 坦诚把握不好便会尽显丑陋

  • 厘清什么是无私的坦诚,什么是有才华的浑蛋

  • 坦诚的文化并不意味着不加考虑地说出自己的想法。相反,每个人都需要仔细地审视一下4A准则。

    • “还在气头上切勿发表批评意见”“在给予纠正性反馈时要注意语气平和”,等等
  • 创造性工作的价值不应当通过工作时长来衡量。

  • 人们关注的是你的成果,而不是你大量的付出

  • 休长假,领导要带头

  • 给员工更多自由,可以使他们更具归属感和责任感

  • 怎么花自己的钱,就怎么花公司的钱

  • 工作分成了操作型和创造型两类。

  • 有利于激发创造力的,是足够高的工资,而非绩效奖金

  • 成功了小声说,犯错了大声说

  • 情景管理

  • 迈向自由与责任的企业文化:人才密度 -> 坦诚


  • 评论:发展到今天,网飞的核心竞争力既不是技术,也不是运营,而是创意。创意领域和操作性领域有个最大的不同:在操作性领域,比如面包师、会计师,一流人才创造的价值可能是普通人才的两三倍;而在创意领域,比如程序员、作家、导演,一流人才创造的价值是普通人才的几十倍、几百倍。对网飞来说,聘用一流人才并不是一种高姿态,而是一种切切实实能带来更高收益的精打细算。网飞的最佳人才策略,就是花高价聘请一个明星员工,来替代掉十个资质平平的员工。
  • 除此之外还有一个原因,就是网飞面对的竞争环境。虽然网飞已经是硅谷巨头,但和超级巨无霸亚马逊、苹果、脸书相比,网飞显然还不是一个量级的。现在这些巨无霸也纷纷杀入内容领域,如果网飞不能开出市场最高薪酬,那么很可能面临人才大幅流失的危险。与其坐等员工被竞争对手挖走,不如主动出击,直接开出市场最高价。这样一来,至少能够保证一流人才不会单纯为了薪酬待遇而跳槽。
  • 说到这儿,你会不会觉得,天啊,网飞员工实在是太幸福了,没有绩效考核,没有末位淘汰,还能一直拿到市场最高薪酬。这样的公司谁不想去?但是你知道吗,不少网飞员工每天上班都提心吊胆,生怕被解雇,甚至有媒体说网飞是在搞“恐惧文化”。为什么呢?因为网飞的淘汰制度非常残酷。
  • 评论:不拘一格,总结我认为可以参考管理的段落,有些做法还是要看公司现状

《复盘网飞》-笔记

发表于 2021-08-09

网飞的创意来源

  • 顿悟是极其罕见的。出现在创业故事中的所谓的顿悟往往是被过分简化的,甚至是伪造的。我们之所以爱听这些故事,是因为它们契合了有关灵感和天才的浪漫想象。我们想让我们这个时代的“牛顿们”在苹果掉下来的时候坐在苹果树下;我们想要这个时代的“阿基米德们”坐在浴缸里。但真相往往比这复杂许多。真相是,每一个好主意背后,都有上千个馊主意。这两者有时很难区分。

最初的股权模式

  • 我的风险是我投入的时间。里德的风险是他投入的金钱。

OPM很难

  • OPM到底是什么呢?它其实只是创业界的一个俚语。它意为“别人的钱”(Other People’s Money,首字母缩写为OPM)。当企业家们恳求你记住OPM时,他们真正要表达的意思是:一旦涉及为你的梦想融资,你就只花别人的钱。创业有风险,你唯一的投资只能是时间,不要把真金白银砸进去。你反正会把一生都奉献给自己的创意,那就让别人贡献金钱吧。

企业文化不是说出来的

  • 企业文化就是我们做事的方式。我们上班不用打卡,没有强制的工作时间,想来的时候就来,想走的时候就走。评判你的唯一标准就是结果。只要你在解决问题、完成任务,我就不在乎你究竟身处何地,工作有多努力,或者你每天在办公室里待多久。

企业文化是做出来的

  • 事实上,我们的企业文化不是通过会议或者圆桌讨论制定的,也不是精心规划的产物。因为团队成员都在初创公司、大型企业,以及介于两者之间的很多公司有过丰富的工作经验,所以我们的文化实际上是在大家共同的价值观基础之上自然而然地衍生的。

  • 作为领导者,你的工作就是让他们明白这一点。你之所以选择这群人参加如此艰苦的越野旅行,大概率就是因为你相信他们的判断力,因为他们明白自己要做的工作。所以作为一个领导者,确保每个人都能到达营地的最好方法,是告诉他们去哪里,而不是告诉他们怎么去那里。给他们明确的坐标,让他们自己想办法。

  • 在一家公司规模还很小的时候,信任和效率是息息相关的。如果你给团队招到了合适的人选,你就不需要确切地告诉他们你希望他们以什么方式来做事——事实上,你甚至不需要告诉他们你想让他们做什么。你唯一要做的就是明确你想要达到的目标,以及这个目标背后的重要意义。如果你雇用了合适的人选,找到了聪明、能干、值得信赖的人,他们自己就会弄清楚自己需要做什么,也会勇往直前。他们会在你意识到问题存在之前就独立地加以解决。

  • 网飞的早期文化完全脱胎于我和里德对待彼此的方式。我们不会给对方一张我们期望对方完成的任务清单,然后频繁“查岗”以确保所有事情都完成了。我们只是确保每个人都了解公司的目标,以及我们各自负责的是哪些事。我们必须自己设法弄清楚需要做些什么才能实现目标。我们要对彼此开诚布公——绝对地坦诚。

  • 绝对的坦诚,以及自由与责任。这些都是非凡的理想,但在我们建立公司最初的几年里,它们并没有真正被确定。我们都是具体事情具体对待的。举个例子。1999年的某一天,我们的一位经理带着一个特殊的要求来找我。他的女朋友搬到了圣迭戈市,他想努力维持他们的恋爱关系。“如果我星期五提前下班,飞到圣迭戈去,您觉得可以吗?”他问道。他解释说,他星期一会在那边办公,星期一晚上飞回来,星期二早上能回到办公室。我的回答可能让他大吃一惊。“我不在乎你在哪里工作,也不在乎你工作多长时间。你就算在火星上工作,我也不管。如果你问的只是工作时间和地点的问题,那答案很简单:对我来说无所谓。”我话锋一转,接着说:“但是,如果你真正想问我的是,我是否愿意降低对你和你团队的期望,这样你就可以和你的女朋友待在一起?那这个问题的答案也很简单:不可能。”他犹豫地看着我。我可以看到他在圣迭戈过周末的梦想破灭了。“听着,工作地点和工作时间完全取决于你自己。如果你每周在办公室里只待3天半,还能有效地管理你的团队,那么所有的权力都属于你。你就去吧——那我就只能羡慕你,但愿我也能像你一样能干。不过你要记住:你是经理。你的部分工作就是确保你的团队知道你想要他们完成什么,知道这些任务为什么很重要。你觉得如果你不待在办公室里,他们也能做到吗?”不用说,他的女朋友在那之后不久就单身了。我给了那位经理选择的自由,但同时也提醒了他对团队的责任。我对他做到了绝对的坦诚,虽然我怀疑如果他每周提前下班去圣迭戈,他是否还能履行承诺、尽职尽责,但我最终还是把决定权交给了他。那位经理觉得自己被赋予了权力,可以自由地选择自己的生活方式,而公司最终也从他重新集中的精力中受益。每一方都是赢家。或者,应该说几乎是每一方。他在圣迭戈的那个前女友可能并不像我这么看。自由与责任也不仅仅属于管理者。

  • 拥有自由与责任的文化,加上绝对的坦诚,就仿佛有魔力一般所向披靡。我们不仅取得了很好的效果,员工们也很喜欢这种文化。具备判断力来做出负责任决定的人都喜欢他们有这样做的自由。

  • 他们喜欢被信任的感觉。

  • 这是有道理的,对吧?如果你的公司里到处都是缺乏敏锐判断力的人,那么你就必须制定各种各样的防范措施,让他们循规蹈矩。你必须为他们规定好每一件事:他们可以在办公用品上花多少钱,他们可以休多少天假,他们什么时候应该待在办公桌前。大多数公司最终都建立了这样的体系,来保护自己免受缺乏判断力的人的伤害。这只会让拥有判断力的人感到沮丧。

投公司就是跟对人

  • 当机会来敲门时,你不一定非得开门。但你至少应该透过钥匙孔朝外面看一眼。这就是我们在面对亚马逊时所做的。

  • 机构风险合伙公司资助我们,不是因为我们的预测结果很好,也不是因为我们的路演很完美,更不是因为我用幻灯片和热情打动了他们。机构风险合伙公司资助我们,是因为尽管这一切看起来那么不切实际,但里德是一个可以创造奇迹的人,而他入伙了。

成功会带来问题

  • 其实,大多数事情都是这样的。当你忙着把梦想变为现实的时候,在目标实现以前,掌声和鲜花都不属于你——而真的到了那个时刻,你也早已把注意力转移到解决其他问题上去了。
  • 但我在网飞创业的第一年中学到的一件事就是,成功会带来问题。增长当然很好,但伴随增长而来的将是一系列全新的复杂问题。你的团队有新成员之后,你如何让他们有身份认同感?如何平衡持续扩张与一贯的身份认同?在可能会有所失的情况下,你又如何确保自己能够继续冒险?
  • 这就是创业初期的典型运作方式:你雇用一群才华横溢的员工,让他们成为万事通。对于每件事,每个人都会做一点儿。你雇用的是一支团队,而不是一组职位。
  • 一旦创始团队开始适当放手,不再凡事亲力亲为,我们就需要把很多事情编成规定,以确保我们的业务继续平稳运转。

里德的坦诚

  • 在商业中有一种说话的策略,这种策略对传递坏消息很有用。这种策略名叫“狗屎三明治”。你用一连串的赞美开头,赞扬人们工作做得不错。这是你的第一片面包。结束了这段话以后,你就在上面堆上屎:坏消息,不够乐观的业绩报告,你的受众不那么喜欢听到的内容。接着你再用一片面包来收尾:未来的宏伟蓝图,以及一份着手处理所有糟糕情况的计划。
  • “所以我认为最好的结果是,我全职加入公司,我们一起来运营。我做首席执行官,你做总裁。”
  • 我敢肯定里德一定在想,为什么我没有像他那样清楚地、合乎逻辑地看待公司的情况。我知道里德没有——也不能——理解我在想些什么。那可谢天谢地了,因为我脑子里冒出来的词语并不礼貌。

放弃首席执行官头衔

  • 这是绝对的坦诚,这和我们一开始驾着沃尔沃开在17号公路上练习过的坦诚是一样的。里德既没有私心,也没有其他用心,他的动机就是为了公司的利益。
  • 我成功地组建了一支团队,打造了一种企业文化,把一个想法从一张信封的背面发展成为一家公司,一个办公室,一款真实存在于世界上的产品。但我们正在走出最初的阶段,现在我们必须快速发展壮大,而这个阶段需要的是一套完全不同的技能。
  • 我不得不扪心自问:看到我的梦想成真到底有多重要?甚至,它此刻还仅仅只是我的梦想吗?我们现在有了40个员工,每个人都像我一样渴望网飞大获成功。他们每天工作到很晚,周末也要加班,工作占用了他们本应与朋友和家人相处的时间,所有努力都是为了实现那个原本只是我一个人的梦想,但他们却早已把这个梦想当成了自己的梦想。为了他们,难道我不应该尽我所能确保公司生存吗?即便这意味着我扮演的角色将不再是我当初想象的那个角色。我的头衔与他们的工作,哪一个更重要?
  • 当你的梦想成真时,它就不再仅仅属于你了。它属于那些帮助过你的人——你的家人、你的朋友、你的同事。它属于全世界。
  • 为了我的员工、我的投资者,还有我自己,我应该确保公司在未来继续取得成功,即使这意味着我要辞去首席执行官一职。

重新定义团队

  • 通常,在创业初期适合这份工作的人,到了中期阶段就无法继续胜任了。有时,引进具有数十年经验和专业知识的人员是必要的。

  • 巴里的到来宣告了吉姆·库克在网飞职业生涯的结束。吉姆从一开始就想成为公司的首席财务官,而随着巴里的正式加入,他的梦想显然从此破灭了。吉姆的离开也算不上突然,这类事情基本都不能算是突然,但它凸显了那年春夏两季正在发生的事情:创业团队开始分崩离析,下一个阶段就是用新的人选取代他们。

  • 这是创业中的一个事实:变革。当你白手起家时,你仰仗的是才华横溢、热情澎湃的多面手:他们什么事情都会做一点儿,他们相信团队的使命,而你愿意把时间、金钱和想法都托付给他们。但是一旦你从0发展到1,你播下的种子开始茁壮成长,你就必须启动洗牌重组。通常,在创业初期适合这份工作的人,到了中期阶段就无法继续胜任了。有时,引进具有数十年经验和专业知识的人员是必要的。

订阅模式初见雏形

  • 如果这是一个馊主意,那么不管我们多关注测试中的细节问题,它也不会成为一个好主意。但如果这是一个好主意,那么不管有多少障碍或者我们这边的工作做得多马虎,大家都会争相涌入享用这项服务。

  • 我们一直试图避免落入创业的头号陷阱:在脑海里筑造想象中的城堡,精心设计好一切,包括塔楼、吊桥和护城河。过度的计划和过度的设计往往只是过度的思考,或者只是普通的拖延症而已。在创意方面,测试10个糟糕的想法要比花上好几天时间想出完美的创意更有效。

关键创新:订阅与算法推荐

  • 整件事给我们上了宝贵的一课:相信你的直觉,但也要检验它。在做任何具体的事情之前,数据必须先行。
  • 如果在网飞上线的那天,你让我描述一下网飞最终会是什么样子的,我可能永远也想不到包月订阅的服务模式。
  • 但威廉·戈德曼最著名的是他写下了3个词语:无人、知晓、一切。
  • 因为如果“无人知晓一切”——如果真的不可能事先知道哪些想法是好的,哪些不是,如果不可能知道谁会成功,而谁又不会——那么任何想法都有可能是成功的。如果“无人知晓一切”,那么你就必须相信你自己,你必须进行自我测试,你必须愿意承担失败的风险。
  • 硅谷的头脑风暴会议,通常会以一句“天下没有馊主意”开头。我始终无法苟同。肯定是有馊主意的。但如果你不去尝试一下,你永远都不会知道这个想法到底是好还是坏。
  • 订阅模式不但拯救了网飞,也迅速成了网飞最典型的特征。但这并不是我们从一开始就制定好的发展方向,也不是任何人可以事先预测的。这需要很多努力、很多思考。
  • 这也需要机缘巧合。其他人称之为幸运,我管它叫“无人知晓一切”。

为潜力付费

  • 消极选项的问题要更棘手一些。“你不能连问都不问一声就从别人的信用卡里扣费,”里德说,“这是完全不道德的。”

专注与放弃

  • 最主要的原因其实更简单。如果我们把开辟加拿大市场所需要的精力、人力和脑力应用到业务的其他方面,我们最终得到的回报要远远高于10%。拓展市场只是一个短期举措,它仅仅具有短期的效益,而且会分散我们的注意力。

  • 专注,这就是企业家的秘密武器,它在网飞的故事里出现了一次又一次——放弃DVD销售业务,放弃按件计价的租赁模式,以及最终放弃了网飞初创团队的众多成员——为了公司的未来,我们不得不抛弃一部分过去。有时候,这种极度的专注似乎有些冷酷无情——确实有点儿残酷。但这又绝不仅仅是残酷,它更是一种勇气。

数据先行

  • 次日送达对取消订阅率没有产生太大的影响,真正发生变化的是新用户注册率。

  • 我们运行测试的时间越长,就越能明显看出,次日送达能真正颠覆这个行业——只不过不是以我们预想的方式罢了。它不会影响用户留存率,它真正影响的是注册率。次日送达激发了真正的用户忠诚度,正是这种用户忠诚度让你自发地把自己正在使用的这项新服务告诉你所有的朋友。

  • 整件事给我们上了宝贵的一课:相信你的直觉,但也要检验它。在做任何具体的事情之前,数据必须先行。

  • 我们虽然猜测次日送达很重要,但是在分析测试结果时目光太过短浅,所以一直想不明白为什么会产生这样的结果。因此,需要进行一项额外的测试,一次真正跳脱定势思维的测试,来理解我们凭直觉认为是正确的事情。一旦理解了它背后的原因,我们就可以进一步完善这个想法,最大限度地发挥它的潜力——巨大的潜力。

开创算法匹配服务

  • 我们想要提供个性化的服务,但问题是,如果全部由人工完成这些工作,成本就会非常高昂,更不用说费时了。当我们只有900部电影时,创建匹配的内容还可行,但到1999年年底,我们已经有了近5000部电影。除了工作进度很难跟上,浏览甚至也会变得更加不方便。里德又以他的一贯的风格坚持推进自动化。

  • 最后我们才意识到,要想为用户提供他们想要的东西,最好的方法就是通过用户自身进行数据众包。

  • 我们决定让用户给每部电影打分,从1星到5星不等。让他们给喜欢的电影打5颗星,觉得观看纯粹是浪费时间的就打1颗星。事实证明,大家喜欢被征求意见。人人都是评论家。

  • 我创建的团队不断迸发着各种创意,致力于增进与用户的良好关系,而里德的团队则专注于简化我们的设想,提升效率。里德的高度专注帮助我们聚焦于未来,而我的目标则是确保无论我们发展得多快,无论我们变得多么高效,在根本上,我们始终寻求与用户建立良好的关系。

危机下的盈利计划

  • 网飞内部都有一个共同的期待,那就是一旦得出了显而易见的正确结论,就该着手实施了。争论是为了更好地合作,而不是为了自己的面子。谁是对的并不重要,重要的是我们做对了。

泡沫危机

  • 登上顶峰是可选的,安全下山是必须的
  • 我们花时间进行自我评估,然后毫不留情地剔除了所有不再有贡献价值的计划、测试、附加功能和增强功能。
  • 过去,我们主要关注的是等式的一边:获取更多的用户。而此刻,越来越明显的一点是,我们必须开始关注等式的另一边:减少经营企业的成本。

只关注棘手之事

  • 这是创业圈的一句格言:你肯定会犯错,但是你不能连犯两次同样的错。

裁员计划

  • 一名主要员工已经离开了:埃里克·迈耶,他在前一天就被劝退了。虽然他天赋异禀,但他的才干已不再适合应对我们面临的挑战。
  • “我们的一些朋友和同事将会离开这里。但这并不是因为他们做错了什么,这纯粹是因为要让公司变得更强大,我们别无他路。
  • 这一切结束后不久,我就把我分管的几个部门剩余的员工召集在了一起。我发表了一次简短的讲话,主要内容是我们要继续前进,我们有责任向我们自己,也向其他每一个人证明,这次裁员不是任性的、残忍的,裁员唯一的目标就是为了确保网飞能够挺过去。为了所有人,我们有责任保证实现这一目标。

上市

  • 这不是金钱的问题。这是一个人是否有用的问题,是价值观所带来的快乐的问题。对我来说,工作从来就不是为了发家致富。于我而言,工作是为了完成任务时的激动,是为了解决问题时的乐趣。在网飞,这些问题都非常复杂,而快乐就来自和优秀的人围坐在一起,努力解决这些问题。

明星团队

  • 在经历了9月痛苦的裁员之后,过了几周,乃至几个月,我们开始注意到一种变化。我们变得更好了。我们变得更高效、更有创造力,也更果断了。筛选员工让人员变得更精简,也更专注。我们没时间可以浪费了,所以我们没有浪费时间。虽然我们不得不解雇一些非常有天赋的员工,但留下的全部是超级明星选手。有了这些超级明星选手负责完成所有的工作,难怪我们的工作质量如此之高。这在成功的创业企业中颇为常见。公司能够起步,得益于一小群敬业奉献的人,得益于他们的专注、投入和创造力。公司不断招人、发展壮大,接着又进行收缩。它开始重新致力于自己的使命,而这往往依赖于其最宝贵的成员重新集中的注意力和精力。

  • 然而,雇用和留下超级明星选手远不只是工作质量的问题。这是文化问题。当你仅仅保留超级明星选手时,你就打造了一种“竞争、卓越”的文化。当你知道自己属于百里挑一的精英团队之后,每天上班就会变得更有意思。此外,如果你已经有了超级明星人才队伍的名声在外,那么吸引其他精英加入你的团队也会容易得多。

将“不”变成“是”

  • 我们挺了过来,正朝着目标稳步前进。但此时一切都变了,创始团队的很多成员都已经离开了。吉姆当时去了亚马逊旗下一家名为“葡萄酒买家”(WineShopper)的公司工作。特在一家互联网安全初创公司“地带实验室”(Zone Labs)工作。维塔和埃里克在9月被解雇了。克里斯蒂娜在1999年因为身体原因休假,后来便再也没有以全职身份回来工作过。最初那一批技术娴熟的多面手,如今已被超级明星专家取代。我当然很高兴能与全硅谷最杰出的人才共事,但作为与创始团队最后的纽带,我开始怀疑自己未来在公司的角色。我适合什么角色?更重要的是,我希望自己扮演什么样的角色?

  • 我知道我们的想法很好,即便现在或许无法实现,但总有一天能够实现。以下是我总结的经验:在实现梦想的道路上,你拥有的最强大的武器就是坚持不懈、义无反顾。坚决不接受“不”这个回答,绝对是值得的,因为在商界,“不”并不总是意味着“不”。

  • 我完全相信网飞一定会大获成功。我从未像现在这般确信,我们创建的这家公司注定会取得长期的成功。我只是想要有选择卖出的权利。

  • 要实现这一点,我就需要大大降低自己在投行和投资者面前的存在感。我不能以“总裁”的身份出现在我们的S-1申请表上。这意味着两件事:第一,我需要一个低调的头衔,以免让我看起来像是公司的负责人;第二,我需要放弃我在网飞董事会的席位。

  • 不过,离开董事会有点儿难。我为那个席位努力奋斗了很久,有一次还差点儿丢了席位。里德在担任首席执行官后不久,就要求我把董事会席位让给一位投资者。我坚决拒绝了。我告诉他,我可以放弃首席执行官的头衔,甚至可以放弃一些股权,但我绝不会放弃我在董事会的席位——那太过分了。我想要对公司的发展方向有一定的控制权,我同时也认为,由公司的一位创始成员留在董事会里制衡风险投资人的利益是很重要的。

  • “董事会上的每个人都说,他们只会为了公司的成功着想,”我告诉里德,“但你我都知道,在风险投资人和公司创始人的词典里,‘成功’这个词的含义并不完全相同。”顺便说一下,这句话是真的。我现在经常这么告诉创业者。风险投资人总是说他们与你的使命一致,他们想要的都是对公司最有利的。但他们真正想要的,其实是对他们的投资最有利的。这两者并不总是等同的。顺风顺水时,大家的方向都是一致的。只有当暴风雨来临时,你才会发现原来大家的目标其实各不相同。

其他

  • 创业是一次孤独的旅程,你在做一件没人愿意相信的事情,你一次又一次地被告知:那永远不会成功。是你在对抗全世界。但事实是,光靠自己是行不通的。你需要寻求帮助,说服别人接受你的想法,让他们也心怀如你一般的热情。给他们戴上神奇的眼镜,让他们也能看到你对未来的憧憬。

  • 大多数公司在寻求进入新业务领域的机会的时候,都会进行一项“自制或外购分析”。在分析的过程中,公司会衡量从头开始打造一项新业务的成本、时机和难度,再评估直接买下一家已经在从事这项业务的公司是否成本更低且更快、更好。

  • 我在网飞学到的一条重要经验就是,仅仅有创造性的想法,或者有合适的人在你身边工作还不够,你还需要保持专注。在一家初创公司,能把一件事情做好就已经够难了,更不用说要把一大堆事情都做好了。尤其是当你想做的那些事情不仅截然不同,而且会互相阻碍的时候。

后记 伦道夫的成功法则

  • 1.至少比要求的多做10%。2.在你不了解的领域永远不要把自己的观点当成事实说给别人听。要非常小心,严格自律。3.始终保持礼貌和体贴,对上对下都一样。4.不要非难,不要抱怨,而是坚持提出建设性的、严肃认真的批评意见。5.当你有事实依据时,不要害怕据此做出决定。6.一切尽可能量化。7.思想要开明,但也要保持怀疑。8.别迟到。

  • 随着年龄的增长,如果你有那么一点儿自知之明,你就会了解关于自己的两件重要的事情:第一,你喜欢什么;第二,你擅长做什么。每天能同时做这两件事情的人都是幸运儿。

  • 但这个故事太混乱了。当你和媒体、投资者或者商业伙伴交谈时,大家真的不愿意听你讲这些。他们想要的是一个干净整洁的版本,上面打着一个精巧的蝴蝶结。里德几乎立刻就意识到了这一点,所以他想出了一个故事。这是一个伟大的故事:简单、清晰,令人难忘。这个故事抓住了网飞的精髓,为我们解决了一个大问题。

  • 但随着季度业绩报告机械地反反复复,年复一年,我慢慢意识到,虽然我热爱这家公司,但我已经不再热爱在这里的工作了。

  • 这是真的:里德经常和我谈论我的感受。他太聪明了,肯定已经注意到我身上的技能不是网飞未来几年发展所需要的;他也太诚实了,瞒不了我多长时间。

  • 不过此刻,他看上去倒是松了一口气。在这样的安排下,他就不必主动找我进行一次不愉快的谈话了。他不必再做幻灯片,再做一块“狗屎三明治”——因为这个决定不是他做的。我在做最后一个项目。如果失败了,我就会离开——按照我自己选择的方式。

  • 那些艰难困苦的日子也一去不复返了。我想念那段日子;我想念那些深夜和清晨,草坪椅和折叠桌;我想念所有人同舟共济的那种感觉,那种对于每天都要处理一个与自己的职位描述无甚关联的问题的期待。

  • 有时候,你必须从你的梦想中后退一步,抽离出来,尤其是当你认为你已经实现了梦想的时候。那时你才能真正地看清它。就我而言,离开网飞是因为我意识到已经锻造为成品的网飞并不是我的梦想。我的梦想是打造事物,是创建网飞的过程。

  • 我真的为我们在网飞取得的成就而感到骄傲,它的成功已经远远超出了我的期望。但我逐渐意识到,成功不是由一家公司的成就来定义的。我有一个不同的定义:成功是你自己的成就。成功是你能做自己喜欢的事,做你最擅长的事,追求对你来说重要的事。

  • 从这个定义来看,我做得不错。但是成功也可以被定义得更加宽泛一点儿:你拥有一个梦想,你通过时间、天赋和毅力,见证梦想变为现实。我也完全符合这个定义,为此我很自豪。

  • 这就是伦道夫法则所指向的那种成功,是父亲一直期望我实现的那种成功:实现你的目标,让你的梦想成为现实,同时享受来自家庭的爱的滋养。不要总想着金钱,不要总想着股票期权。这才是成功。

  • 说句公道话,最初的设想确实是无法成功的。我们花了数年时间调整、改变策略,想出新点子,再加上运气好,才最终找到了一个可行的版本。

  • 要将梦想变为现实,任何人所能采取的最有力的一步都很简单:你只需要开始。要想知道你的想法到底好不好,唯一的方法就是去试试。你花一个小时去做一件事,学到的东西会比花一辈子去思考它还多。

  • 你必须学会热爱问题,而不是热爱解决方案。当事情耗费的时间比你预期的久时,这就是你保持专注的方式。相信我,事情耗费的时间绝对比你预期的久。如果你已经读到了这里,你就会发现,把梦想变成现实的过程有一个戏剧式结构,这个过程不会很快,也绝不容易,在整个过程中,你会遇到许多问题和困难。

  • 雅达利(Atari)的联合创始人诺兰·布什内尔说过一句话,这句话直到现在都能引起我的共鸣:“每个冲过澡的人都有过一个想法,但真正与众不同的是那些从浴室出来,用毛巾把自己擦干,然后采取行动的人。”


总结

  • 顿悟
  • 股权稀释过程
  • OPM创业
  • 作为领导者,给他们明确的坐标,让他们自己想办法
  • 团队壮大,编成规定
  • “狗屎三明治”
  • 绝对的坦诚
  • 不同阶段的需要
  • 重新定义团队
  • 绝对的坦诚,以及自由与责任
  • 具备判断力来做出负责任决定的人
  • 他们喜欢被信任的感觉
  • 充分快速试错
  • 数据必须先行
  • 无人、知晓、一切
  • 专注与放弃
  • 坚持推进自动化
  • 争论是为了更好地合作
  • 不连续犯同样的错误
  • 明星团队、“竞争、卓越”的文化
  • 制衡风险投资人
  • 你喜欢什么;擅长做什么
  • 整洁的讲故事
  • 从梦想中抽离
  • 有想法,更重要的是开始
  • 学会热爱问题,而不是热爱解决方案

  • 评论:想起了达维多定律:一家企业要在市场中总是占据主导地位,那么它就要永远做到第一个开发出新一代产品,第一个淘汰自己的产品
  • 不被替换,足够的股权,或匹配能力

方案设计时应该画什么图?

发表于 2021-07-30

思维导图

  • 思维导图又叫脑图,使用一个中央关键词或想法引起形象化的构造和分类的想法,是一种结构化思维的树形发散图。

UML

  • 基本上大多情况,类图、时序图、活动图、状态图 已经够用。

用例图

  • 从用户角度描述系统功能,并指出各功能的操作者。
  • 实际工作中,写功能说明,项目交接等情况可以使用。

类图

  • 复杂数据结构,方便说明时可以使用
  • 第一是领域模型设计,第二种是表实体关系设计。
  • 组合 > 聚合 > 依赖 > 关联

时序图(顺序图)

  • 按时间顺序描述系统之间的交互。
  • 在方案设计中,涉及外部服务的调用、新增新组件(Redis、DB)或设计中涉及组件的关联较多等情况,应该有时序图。

协作图

  • 可视化的组织对象及相互作用(和时序图类似,后者强调时间顺序)
  • 在方案设计中,需要描述多个对象之间的相互作用关系时,可以使用。

状态图 (状态机)

  • 描述类的对象所有可能的状态,以及事件发生时状态的转移条件
  • 在方案设计中,涉及数据状态的(比如订单的支付状态,UGC的审核状态等)可以使用。
  • 图不好表达双向状态转移,一般使用状态图+状态表来穷举描述状态变化。

活动图

  • 活动图和产品经理出的流程图差不多,都是动态图,活动图相对时序图关注更粗粒度的业务流程变化,其中一个活动可能包含多个时序。活动图可以通过纵向泳道和横向泳道划分,纵向泳道表示系统,横向泳道表示业务阶段。

组件图

  • 描述代码部件的物理结构及各部件之间的依赖关系,组件图有助于分析和理解部件之间的相互影响程度。
  • 方案设计中,类似于架构设计,组件逻辑关系。

部署图

  • 定义系统中软硬件的物理体系结构。
  • 灾备方案设计或新项目部署等场景,可以使用。

架构图

  • 架构图是为了抽象地表示软件系统的整体轮廓和各个组件之间的相互关系和约束边界,以及软件系统的物理部署和软件系统的演进方向的整体视图。

业务架构

  • 包括业务规划,业务模块、业务流程,对整个系统的业务进行拆分,对领域模型进行设计,把现实的业务转化成抽象对象。

应用架构

  • 硬件到应用的抽象,包括抽象层和编程接口。应用架构和业务架构是相辅相成的关系。业务架构的每一部分都有应用架构。
  • 应用作为独立可部署的单元,为系统划分了明确的边界,深刻影响系统功能组织、代码开发、部署和运维等各方面。应用架构定义系统有哪些应用、以及应用之间如何分工和合作。这里所谓应用就是各个逻辑模块或者子系统。
  • 逻辑分层、对外接口协议、RPC调用协议等

数据架构

  • 数据架构指导数据库的设计. 不仅仅要考虑开发中涉及到的数据库,实体模型,也要考虑物理架构中数据存储的设计。

代码架构

  • 子系统代码架构主要为开发人员提供切实可行的指导,如果代码架构设计不足,就会造成影响全局的架构设计。比如公司内不同的开发团队使用不同的技术栈或者组件,结果公司整体架构设计就会失控。
  • 框架、类库、配置、编码规范、代码分层等

技术架构

  • 确定组成应用系统的实际运行组件(lvs,nginx,tomcat,php-fpm等),这些运行组件之间的关系,以及部署到硬件的策略。
  • 平常所说的技术栈

部署拓扑架构图

  • 拓扑架构,包括架构部署了几个节点,节点之间的关系,服务器的高可用,网路接口和协议等,决定了应用如何运行,运行的性能,可维护性,可扩展性,是所有架构的基础。

其他

活动图与流程图的区别

  1. 流程图着重描述处理过程,它的主要控制结构是顺序、分支和循环,各个处理过程之间有严格的顺序和时间关系。而活动图描述的是对象活动的顺序关系所遵循的规则,它着重表现的是系统的行为,而非系统的处理过程。
  2. 活动图能够表示并发活动的情形,而流程图不行。
  3. 活动图是面向对象的,而流程图是面向过程的。

架构图的分类

  • 画一手好的架构图是工程师进阶的开始

4+1视图

  1. 场景视图
  2. 逻辑视图
  3. 物理视图
  4. 处理流程视图
  5. 开发视图

个人总结

  1. 需要对数据实体设计进行详细清晰的说明时,考虑使用类图;
  2. 新增功能涉及其他外部服务或组件的交互时,一般情况下都应该有时序图;
  3. 数据涉及多种状态且流转较复杂时,考虑使用状态图;
  4. 新服务搭建时,可以有部署图、组件图等;
  5. 较复杂的业务设计中,除了时序图,还可以考虑协作图、活动图等;
  6. 在需要对整体进行描述、规划时,可以使用架构图;
  7. 概要设计、详细设计
  • 图的分类很多种,也没有统一的归类;参考不同图的意义,画出能清晰表达的图才是重点。

  • 一份好的设计文档需要提供清晰的问题描述、整体的概要设计、涵盖各个细节的详细设计等。

  • 怎么写出一份令人惊叹的设计文档?

  • 如何写出一篇好的技术方案?

Reference

  • UML九种图的分类
  • 思维导图、UML图、架构图怎么画
  • 架构设计-谈谈架构
  • 如何画好一张架构图?
  • uml中活动图与流程图的区别
  • 架构制图:工具与方法论 – 抽象,没怎么看懂
  • 我是怎么画架构图的?

Kubernetes学习笔记

发表于 2021-07-13

整体架构





  • 图来源:k8s-整体概述和架构

关键名词

  • CNCF(Cloud Native Computing Foundation,云原生计算基金会)

关键总结

  1. 自动化
  2. Docker是其目前支持的底层容器技术之一
  3. 服务弹性扩容机制应对突发流量
  4. Kubernetes视为云计算时代的操作系统
  5. k8s解决进程(服务)部署、容器编排的问题,service mesh解决进程(服务)通信的问题

核心组件

Kubernetes API Server

  • Kubernetes API Server的核心功能是提供Kubernetes各类资源对象(如Pod、RC、Service等)的增、删、改、查及Watch等HTTP Rest接口,成为集群内各个功能模块之间数据交互和通信的中心枢纽,是整个系统的数据总线和数据中心。除此之外,它还有以下一些功能特性。
    1. 是集群管理的API入口。
    2. 是资源配额控制的入口。
    3. 提供了完备的集群安全机制。
  • 可以通过命令行工具kubectl来与Kubernetes API Server交互,它们之间的接口是RESTful API;另一种方式是通过编程方式调用Kubernetes API Server。
  • Kubernetes API Server本身也是一个Service,它的名称就是kubernetes,并且它的Cluster IP地址是Cluster IP地址池里的第1个地址。
  • 由于API Server是Kubernetes集群数据的唯一访问入口,因此安全性与高性能就成为API Server设计和实现的两大核心目标。通过采用HTTPS安全传输通道与CA签名数字证书强制双向认证的方式,API Server的安全性得以保障。此外,为了更细粒度地控制用户或应用对Kubernetes资源对象的访问权限,Kubernetes启用了RBAC访问控制策略。
  • API层:主要以REST方式提供各种API接口,除了有Kubernetes资源对象的CRUD和Watch等主要API,还有健康检查、UI、日志、性能指标等运维监控相关的API。
  • etcd数据库:用于持久化存储Kubernetes资源对象的KV数据库。
  • 借助etcd提供的Watch API接口,API Server可以监听(Watch)在etcd上发生的数据操作事件,比如Pod创建事件、更新事件、删除事件等,在这些事件发生后,etcd会及时通知API Server。
  • 为了让Kubernetes中的其他组件在不访问底层etcd数据库的情况下,也能及时获取资源对象的变化事件,API Server模仿etcd的Watch API接口提供了自己的Watch接口,这样一来,这些组件就能近乎实时地获取它们感兴趣的任意资源对象的相关事件通知了。
  • Kubernetes API Server最主要的REST接口是资源对象的增、删、改、查接口,除此之外,它还提供了一类很特殊的REST接口——Kubernetes Proxy API接口,这类接口的作用是代理REST请求,即Kubernetes API Server把收到的REST请求转发到某个Node上的kubelet守护进程的REST端口,由该kubelet进程负责响应。
  • Kubernetes Proxy API里关于Node的相关接口、关于Pod的相关接口
  • 在Kubernetes集群之外访问某个Pod容器的服务(HTTP服务)时,可以用Proxy API实现,这种场景多用于管理目的,比如逐一排查Service的Pod副本,检查哪些Pod的服务存在异常。Proxy API也有Service的Proxy接口,其接口定义与Pod的接口定义基本一样:/api/v1/proxy/namespaces/{namespace}/services/{name}。

Controller Manager

  • Kubernetes集群中,每个Controller都是这样的一个“操作系统”,它们通过API Server提供的(List-Watch)接口实时监控集群中特定资源的状态变化,当发生各种故障导致某资源对象的状态发生变化时,Controller会尝试将其状态调整为期望的状态。
  • Controller Manager是Kubernetes中各种操作系统的管理者,是集群内部的管理控制中心,也是Kubernetes自动化功能的核心。

kubelet

  • kubelet进程在启动时通过API Server注册自身的节点信息,并定时向API Server汇报状态信息,API Server在接收到这些信息后,会将这些信息更新到etcd中。在etcd中存储的节点信息包括节点健康状况、节点资源、节点名称、节点地址信息、操作系统版本、Docker版本、kubelet版本等。
  • 在Kubernetes集群中,在每个Node(又称Minion)上都会启动一个kubelet服务进程。该进程用于处理Master下发到本节点的任务,管理Pod及Pod中的容器。每个kubelet进程都会在API Server上注册节点自身的信息,定期向Master汇报节点资源的使用情况,并通过cAdvisor监控容器和节点资源。

kube-proxy

  • 每个Node上的kube-proxy进程获取每个Service的Endpoints,实现了Service的负载均衡功能。
  • Service只是一个概念,而真正将Service的作用落实的是它背后的kube-proxy服务进程
  • 在Kubernetes集群的每个Node上都会运行一个kube-proxy服务进程,我们可以把这个进程看作Service的透明代理兼负载均衡器,其核心功能是将到某个Service的访问请求转发到后端的多个Pod实例上。
  • Service的Cluster IP与NodePort等概念是kube-proxy服务通过iptables的NAT转换实现的,kube-proxy在运行过程中动态创建与Service相关的iptables规则,这些规则实现了将访问服务(Cluster IP或NodePort)的请求负载分发到后端Pod的功能。由于iptables机制针对的是本地的kube-proxy端口,所以在每个Node上都要运行kube-proxy组件,这样一来,在Kubernetes集群内部,我们可以在任意Node上发起对Service的访问请求。综上所述,由于kube-proxy的作用,在Service的调用过程中客户端无须关心后端有几个Pod,中间过程的通信、负载均衡及故障恢复都是透明的。
  • kube-proxy进程转发Service流量方式
    1. userspace(用户空间代理)模式。(是一个真实的TCP/UDP代理,类似HA Proxy,负责从Service到Pod的访问流量的转发。当某个Pod以Cluster IP方式访问某个Service的时候,这个流量会被Pod所在本机的iptables转发到本机的kube-proxy进程,然后由kube-proxy建立起到后端Pod的TCP/UDP连接,随后将请求转发到某个后端Pod上,并在这个过程中实现负载均衡功能。)
    2. iptables模式。Kubernetes从1.2版本开始,将iptables作为kube-proxy的默认模式。iptables模式下的kube-proxy不再起到Proxy的作用,其核心功能:通过API Server的Watch接口实时跟踪Service与Endpoint的变更信息,并更新对应的iptables规则,Client的请求流量则通过iptables的NAT机制“直接路由”到目标Pod。根据Kubernetes的网络模型,一个Node上的Pod与其他Node上的Pod应该能够直接建立双向的TCP/IP通信通道,所以如果直接修改iptables规则,则也可以实现kube-proxy的功能,只不过后者更加高端,因为是全自动模式的。与第1代的userspace模式相比,iptables模式完全工作在内核态,不用再经过用户态的kube-proxy中转,因而性能更强。
    3. IPVS模式。iptables模式虽然实现起来简单,但存在无法避免的缺陷:在集群中的Service和Pod大量增加以后,iptables中的规则会急速膨胀,导致性能显著下降,在某些极端情况下甚至会出现规则丢失的情况,并且这种故障难以重现与排查,于是Kubernetes从1.8版本开始引入第3代的IPVS(IP Virtual Server)模式,如图5.16所示。IPVS在Kubernetes 1.11中升级为GA稳定版。
      iptables与IPVS虽然都是基于Netfilter实现的,但因为定位不同,二者有着本质的差别:iptables是为防火墙而设计的;IPVS则专门用于高性能负载均衡,并使用更高效的数据结构(Hash表),允许几乎无限的规模扩张,因此被kube-proxy采纳为第三代模式。在IPVS模式下,kube-proxy又做了重要的升级,即使用iptables的扩展ipset,而不是直接调用iptables来生成规则链。ipset则引入了带索引的数据结构,因此当规则很多时,也可以很高效地查找和匹配。

Scheduler

  • Kubernetes Scheduler在整个系统中承担了“承上启下”的重要功能,“承上”是指它负责接收Controller Manager创建的新Pod,为其安排一个落脚的“家”——目标Node;“启下”是指安置工作完成后,目标Node上的kubelet服务进程接管后继工作,负责Pod生命周期中的“下半生”。
  • 在整个调度过程中涉及三个对象,分别是待调度Pod列表、可用Node列表,以及调度算法和策略。

命令

kubectl

  1. 创建RC: kubectl create -f mysql-rc.yaml; kubectl get rc
  2. 创建Service:kubectl create -f mysql-svc.yaml; kubectl get svc
  3. 查看在集群中有多少个Node:kubectl get nodes
  4. 查看某个Node的详细信息:kubectl describe node
  5. 修改RC的副本数量,来实现Pod的动态缩放(Scaling):kubectl scale rc redis-slave --replicas=3
  6. 创建Deployment:kubectl create -f tomcat-deployment.yaml
  7. 查看Deployment的信息: kubectl get deployments
  8. 查看对应的Replica Set: kubectl get rs
  9. 查看创建的Pod:kubectl get pods
  10. 查看Deployment的更新过程:kubectl rollout status
  11. 检查Deployment部署的历史记录:kubectl rollout history

Kubernetes

  • Kubernetes里的3种IP,这3种IP分别如下
    • Node IP:Node的IP地址。
    • Pod IP:Pod的IP地址。
    • Cluster IP:Service的IP地址。
  • node IP是Kubernetes集群中每个节点的物理网卡的IP地址,是一个真实存在的物理网络。,所有属于这个网络的服务器都能通过这个网络直接通信,不管其中是否有部分节点不属于这个Kubernetes集群。这也表明在Kubernetes集群之外的节点访问Kubernetes集群之内的某个节点或者TCP/IP服务时,都必须通过Node IP通信。(未打通Pod IP网络的情况下)
  • Pod IP是每个Pod的IP地址,它是Docker Engine根据docker0网桥的IP地址段进行分配的,通常是一个虚拟的二层网络。Kubernetes要求位于不同Node上的Pod都能够彼此直接通信,所以Kubernetes里一个Pod里的容器访问另外一个Pod里的容器时,就是通过Pod IP所在的虚拟二层网络进行通信的,而真实的TCP/IP流量是通过Node IP所在的物理网卡流出的。
  • Service的Cluster IP,它也是一种虚拟的IP,但更像一个“伪造”的IP网络,原因有以下几点。
    • Cluster IP仅仅作用于Kubernetes Service这个对象,并由Kubernetes管理和分配IP地址(来源于Cluster IP地址池)。
    • Cluster IP无法被Ping,因为没有一个“实体网络对象”来响应。
    • Cluster IP只能结合Service Port组成一个具体的通信端口,单独的Cluster IP不具备TCP/IP通信的基础,并且它们属于Kubernetes集群这样一个封闭的空间,集群外的节点如果要访问这个通信端口,则需要做一些额外的工作。
    • 在Kubernetes集群内,Node IP网、Pod IP网与Cluster IP网之间的通信,采用的是Kubernetes自己设计的一种编程方式的特殊路由规则,与我们熟知的IP路由有很大的不同。
    • Service的Cluster IP属于Kubernetes集群内部的地址,无法在集群外部直接使用这个地址。

ConfigMap

  • 把所有的配置项都当作key-value字符串,当然value可以来自某个文本文件,比如配置项password=123456、user=root、host=192.168.8.4用于表示连接FTP服务器的配置参数。这些配置项可以作为Map表中的一个项,整个Map的数据可以被持久化存储在Kubernetes的Etcd数据库中,然后提供API以方便Kubernetes相关组件或客户应用CRUD操作这些数据,上述专门用来保存配置参数的Map就是Kubernetes ConfigMap资源对象。
  • Kubernetes提供了一种内建机制,将存储在etcd中的ConfigMap通过Volume映射的方式变成目标Pod内的配置文件,不管目标Pod被调度到哪台服务器上,都会完成自动映射。进一步地,如果ConfigMap中的key-value数据被修改,则映射到Pod中的“配置文件”也会随之自动更新。于是,Kubernetes ConfigMap就成了分布式系统中最为简单(使用方法简单,但背后实现比较复杂)且对应用无侵入的配置中心。
  • ConfigMap供容器使用的典型用法如下。
    1. 生成为容器内的环境变量。
    2. 设置容器启动命令的启动参数(需设置为环境变量)。
    3. 以Volume的形式挂载为容器内部的文件或目录。
  • 不使用YAML文件,直接通过kubectl create configmap也可以创建ConfigMap,可以使用参数–from-file或–from-literal指定内容,并且可以在一行命令中指定多个参数。
  • 容器应用对ConfigMap的使用有以下两种方法。
    1. 通过环境变量获取ConfigMap中的内容。
    2. 通过Volume挂载的方式将ConfigMap中的内容挂载为容器内部的文件或目录。
  • Kubernetes从1.6版本开始,引入了一个新的字段envFrom,实现了在Pod环境中将ConfigMap(也可用于Secret资源对象)中所有定义的key=value自动生成为环境变量。

Master

  • 负责整个集群的管理和控制
  • 在Master上运行着以下关键进程
    • Kubernetes API Server(kube-apiserver)
    • Kubernetes Controller Manager(kube-controller-manager)
    • Kubernetes Scheduler(kube-scheduler)

Node

  • Pod运行在一个被称为节点(Node)的环境中,这个节点既可以是物理机,也可以是私有云或者公有云中的一个虚拟机,通常在一个节点上运行几百个Pod。
  • Node除了Master,Kubernetes集群中的其他机器被称为Node。与Master一样,Node可以是一台物理主机,也可以是一台虚拟机。
  • 每个Node上都运行着以下关键进程
    • kubelet
    • kube-proxy
    • Docker Engine(docker)
  • Node可以在运行期间动态增加到Kubernetes集群中,在默认情况下kubelet会向Master注册自己,这也是Kubernetes推荐的Node管理方式。

Pod

  • 由一组容器组成
  • 每个Pod都有一个特殊的被称为“根容器”的Pause容器
  • Kubernetes为每个Pod都分配了唯一的IP地址,称之为Pod IP,一个Pod里的多个容器共享Pod IP地址。
  • Kubernetes要求底层网络支持集群内任意两个Pod之间的TCP/IP直接通信,一个Pod里的容器与另外主机上的Pod容器能够直接通信。
  • Pod其实有两种类型:普通的Pod及静态Pod(Static Pod)。
  • Pod的IP加上这里的容器端口(containerPort),组成了一个新的概念——Endpoint,它代表此Pod里的一个服务进程的对外通信地址。(一个Pod也存在具有多个Endpoint的情况)
  • 在Kubernetes里,一个计算资源进行配额限定时需要设定以下两个参数。
    • Requests:该资源的最小申请量,系统必须满足要求。
    • Limits:该资源最大允许使用的量,不能被突破,当容器试图使用超过这个量的资源时,可能会被Kubernetes“杀掉”并重启。通常,我们会把Requests设置为一个较小的数值,符合容器平时的工作负载情况下的资源需求,而把Limit设置为峰值负载情况下资源占用的最大量。
  • Pod的管理对象,除了RC和Deployment,还包括ReplicaSet、DaemonSet、StatefulSet、Job等,分别用于不同的应用场景中。
  • Pod定义详解YAML格式的Pod定义文件的完整内容
  • 静态Pod是由kubelet进行管理的仅存在于特定Node上的Pod
    • 创建静态Pod有两种方式:配置文件方式和HTTP方式。
  • 在容器内获取Pod信息(Downward API)
    • Downward API有什么价值呢?
    • 在某些集群中,集群中的每个节点都需要将自身的标识(ID)及进程绑定的IP地址等信息事先写入配置文件中,进程在启动时会读取这些信息,然后将这些信息发布到某个类似服务注册中心的地方,以实现集群节点的自动发现功能。
  • Pod的重启策略(RestartPolicy)包括Always、OnFailure和Never,默认值为Always。
    • kubelet重启失效容器的时间间隔以sync-frequency乘以2n来计算,例如1、2、4、8倍等,最长延时5min,并且在成功重启后的10min后重置该时间。
    • Pod的重启策略与控制方式息息相关,当前可用于管理Pod的控制器包括ReplicationController、Job、DaemonSet及直接通过kubelet管理(静态Pod)。每种控制器对Pod的重启策略要求如下。◎ RC和DaemonSet:必须设置为Always,需要保证该容器持续运行。◎ Job:OnFailure或Never,确保容器执行完成后不再重启。◎ kubelet:在Pod失效时自动重启它,不论将RestartPolicy设置为什么值,也不会对Pod进行健康检查。
  • Pod健康检查和服务可用性检查
    • Kubernetes对Pod的健康状态可以通过两类探针来检查: LivenessProbe和ReadinessProbe,kubelet定期执行这两类探针来诊断容器的健康状况。
    • LivenessProbe探针:用于判断容器是否存活(Running状态);ReadinessProbe探针:用于判断容器服务是否可用(Ready状态)
    • LivenessProbe和ReadinessProbe均可配置以下三种实现方式。
      1. ExecAction:在容器内部执行一个命令,如果该命令的返回码为0,则表明容器健康。
      2. TCPSocketAction:通过容器的IP地址和端口号执行TCP检查,如果能够建立TCP连接,则表明容器健康。
      3. HTTPGetAction:通过容器的IP地址、端口号及路径调用HTTP Get方法,如果响应的状态码大于等于200且小于400,则认为容器健康。
    • Kubernetes的ReadinessProbe机制可能无法满足某些复杂应用对容器内服务可用状态的判断,所以Kubernetes从1.11版本开始,引入Pod Ready++特性对Readiness探测机制进行扩展,在1.14版本时达到GA稳定版,称其为Pod Readiness Gates。通过Pod Readiness Gates机制,用户可以将自定义的ReadinessProbe探测方式设置在Pod上,辅助Kubernetes设置Pod何时达到服务可用状态(Ready)。
  • init container(初始化容器)与应用容器在本质上是一样的,但它们是仅运行一次就结束的任务,并且必须在成功执行完成后,系统才能继续执行下一个容器

Pod的核心:pause容器

  • Kubernetes的Pod抽象基于Linux的namespace和cgroups,为容器提供了隔离的环境。从网络的角度看,同一个Pod中的不同容器犹如运行在同一个主机上,可以通过localhost进行通信。
  • Docker容器非常适合部署单个软件单元。但是当你想要一起运行多个软件时,尤其是在一个容器里管理多个进程时,这种模式会变得有点麻烦。Kubernetes非常不建议“富容器”这种方式,认为将这些应用程序部署在部分隔离并且部分共享资源的容器组中更为有用。为此,Kubernetes为这种使用场景提供了一个称为Pod的抽象。
  • 原则上,任何人都可以配置Docker来控制容器组之间的共享级别——只需创建一个父容器,并创建与父容器共享资源的新容器,然后管理这些容器的生命周期。在Kubernetes中,pause容器被当作Pod中所有容器的“父容器”,并为每个业务容器提供以下功能:·在Pod中,它作为共享Linux namespace(Network、UTS等)的基础;·启用PID namespace共享,它为每个Pod提供1号进程,并收集Pod内的僵尸进程。
  • 这个pause容器运行一个非常简单的进程,它不执行任何功能,一启动就永远把自己阻塞住了(见pause()系统调用)。正如你看到的,它当然不会只知道“睡觉”。它执行另一个重要的功能——即它扮演PID 1的角色,并在子进程成为“孤儿进程”的时候,通过调用wait()收割这些僵尸子进程。这样就不用担心我们的Pod的PID namespace里会堆满僵尸进程了。这也是为什么Kubernetes不随便找个容器(例如Nginx)作为父容器,让用户容器加入的原因。

Pod的调度

  • 在Kubernetes平台上,我们很少会直接创建一个Pod,在大多数情况下会通过RC、Deployment、DaemonSet、Job等控制器完成对一组Pod副本的创建、调度及全生命周期的自动控制任务。
  • RC也出现了新的继任者——Deployment,用于更加自动地完成Pod副本的部署、版本更新、回滚等功能。RC的继任者其实并不是Deployment,而是ReplicaSet,因为ReplicaSet进一步增强了RC标签选择器的灵活性。
  • 与RC不同,ReplicaSet被设计成能控制多个不同标签的Pod副本。一种常见的应用场景是,应用MyApp目前发布了v1与v2两个版本,用户希望MyApp的Pod副本数保持为3个,可以同时包含v1和v2版本的Pod,就可以用ReplicaSet来实现这种控制
  • Kubernetes的滚动升级就是巧妙运用ReplicaSet的这个特性来实现的,同时,Deployment也是通过ReplicaSet来实现Pod副本自动控制功能的。我们不应该直接使用底层的ReplicaSet来控制Pod副本,而应该使用管理ReplicaSet的Deployment对象来控制副本,这是来自官方的建议。
  • 在真实的生产环境中的确也存在一种需求:希望某种Pod的副本全部在指定的一个或者一些节点上运行,比如希望将MySQL数据库调度到一个具有SSD磁盘的目标节点上,此时Pod模板中的NodeSelector属性就开始发挥作用了。
  • 如果NodeSelector选择的Label不存在或者不符合条件,比如这些目标节点此时宕机或者资源不足,该怎么办?如果要选择多种合适的目标节点,比如SSD磁盘的节点或者超高速硬盘的节点,该怎么办?Kubernates引入了NodeAffinity(节点亲和性设置)来解决该需求。
  • 与单独的Pod实例不同,由RC、ReplicaSet、Deployment、DaemonSet等控制器创建的Pod副本实例都是归属于这些控制器的,这就产生了一个问题:控制器被删除后,归属于控制器的Pod副本该何去何从?在Kubernates 1.9之前,在RC等对象被删除后,它们所创建的Pod副本都不会被删除;在Kubernates 1.9以后,这些Pod副本会被一并删除。如果不希望这样做,则可以通过kubectl命令的–cascade=false参数来取消这一默认特性。
  • Deployment或RC:全自动调度:Deployment或RC的主要功能之一就是自动部署一个容器应用的多份副本,以及持续监控副本的数量,在集群内始终维持用户指定的副本数量。
  • 除了使用系统自动调度算法完成一组Pod的部署,Kubernetes也提供了多种丰富的调度策略,用户只需在Pod的定义中使用NodeSelector、NodeAffinity、PodAffinity、Pod驱逐等更加细粒度的调度策略设置,就能完成对Pod的精准调度。
  • 亲和性调度功能包括节点亲和性(NodeAffinity)和Pod亲和性(PodAffinity)两个维度的设置。NodeAffinity意为Node亲和性的调度策略,是用于替换NodeSelector的全新调度策略。亲和性的操作符也包括In、NotIn、Exists、DoesNotExist、Gt、Lt。

Pod的扩缩容

  • Kubernetes对Pod的扩缩容操作提供了手动和自动两种模式,手动模式通过执行kubectl scale命令或通过RESTful API对一个Deployment/RC进行Pod副本数量的设置,即可一键完成。自动模式则需要用户根据某个性能指标或者自定义业务指标,并指定Pod副本数量的范围,系统将自动在这个范围内根据性能指标的变化进行调整。
  • 自动扩缩容机制:HPA控制器基于Master的kube-controller-manager服务启动参数–horizontal-pod-autoscaler-sync-period定义的探测周期(默认值为15s),周期性地监测目标Pod的资源性能指标,并与HPA资源对象中的扩缩容条件进行对比,在满足条件时对Pod副本数量进行调整。
  • Kubernetes从1.11版本开始,弃用基于Heapster组件完成Pod的CPU使用率采集的机制,全面转向基于Metrics Server完成数据采集。从1.10版本开始,Kubernetes引入了对外部系统指标的支持。

Label

  • 一个Label是一个key=value的键值对,其中key与value由用户自己指定。Label可以被附加到各种资源对象上,例如Node、Pod、Service、RC等,一个资源对象可以定义任意数量的Label,同一个Label也可以被添加到任意数量的资源对象上。Label通常在资源对象定义时确定,也可以在对象创建后动态添加或者删除。
  • 过Label Selector(标签选择器)查询和筛选拥有某些Label的资源对象
  • 有两种Label Selector表达式:基于等式的(Equality-based)和基于集合的(Set-based)
  • 使用Label可以给对象创建多组标签,Label和Label Selector共同构成了Kubernetes系统中核心的应用模型,使得被管理对象能够被精细地分组管理,同时实现了整个集群的高可用性。

Annotation

  • Annotation(注解)与Label类似,也使用key/value键值对的形式进行定义。不同的是Label具有严格的命名规则,它定义的是Kubernetes对象的元数据(Metadata),并且用于Label Selector。Annotation则是用户任意定义的附加信息,以便于外部工具查找。在很多时候,Kubernetes的模块自身会通过Annotation标记资源对象的一些特殊信息。通常来说,用Annotation来记录的信息如下。◎ build信息、release信息、Docker镜像信息等,例如时间戳、release id号、PR号、镜像Hash值、Docker Registry地址等。◎ 日志库、监控库、分析库等资源库的地址信息。◎ 程序调试工具信息,例如工具名称、版本号等。◎ 团队的联系信息,例如电话号码、负责人名称、网址等。

Service

  • Service是分布式集群架构的核心,一个Service对象拥有如下关键特征。
  • 拥有一个虚拟IP(Cluster IP、Service IP或VIP)和端口号。
  • 通过Label关联Pod和Service。
  • 并不是每个Pod和它里面运行的容器都能被映射到一个Service上,只有提供服务(无论是对内还是对外)的那组Pod才会被映射为一个服务。
  • 通常,Cluster IP是在Service创建后由Kubernetes系统自动分配的,其他Pod无法预先知道某个Service的Cluster IP地址,因此需要一个服务发现机制来找到这个服务。
  • Kubernetes的Service定义了一个服务的访问入口地址,前端的应用(Pod)通过这个入口地址访问其背后的一组由Pod副本组成的集群实例,Service与其后端Pod副本集群之间则是通过Label Selector来实现无缝对接的。RC的作用实际上是保证Service的服务能力和服务质量始终符合预期标准。
  • 运行在每个Node上的kube-proxy进程其实就是一个智能的软件负载均衡器,负责把对Service的请求转发到后端的某个Pod实例上,并在内部实现服务的负载均衡与会话保持机制。
  • Service没有共用一个负载均衡器的IP地址,每个Service都被分配了一个全局唯一的虚拟IP地址,这个虚拟IP被称为Cluster IP。这样一来,每个服务就变成了具备唯一IP地址的通信节点,服务调用就变成了最基础的TCP网络通信问题。
  • Pod的Endpoint地址会随着Pod的销毁和重新创建而发生改变,因为新Pod的IP地址与之前旧Pod的不同。而Service一旦被创建,Kubernetes就会自动为它分配一个可用的Cluster IP,而且在Service的整个生命周期内,它的Cluster IP不会发生改变。
  • Kubernetes提供了Headless Service,即不为Service设置ClusterIP(入口IP地址),仅通过Label Selector将后端的Pod列表返回给调用的客户端。
  • Service只是一个概念,而真正将Service的作用落实的是它背后的kube-proxy服务进程

服务发现

  • 每个Kubernetes中的Service都有唯一的Cluster IP及唯一的名称。
  • 如何通过Service的名称找到对应的Cluster IP。Kubernetes通过Add-On增值包引入了DNS系统,把服务名作为DNS域名,这样程序就可以直接使用服务名来建立通信连接了。目前,Kubernetes上的大部分应用都已经采用了DNS这种新兴的服务发现机制。
  • Service具有稳定的IP地址(区别于容器不固定的IP地址)和端口,并会在一组匹配的后端Pod之间提供负载均衡,匹配的条件就是Service的Label Selector与Pod的Labels相匹配。
  • Kubernetes的Service代表的是Kubernetes后端服务的入口,它主要包含服务的访问IP(虚IP)和端口,因此工作在L4。既然Service只存储服务入口信息,那如何关联后端Pod呢?前文已经提到Service通过Label Selector选择与之匹配的Pod。那么被Service选中的Pod,当它们运行且能对外提供服务后,Kubernetes的Endpoints Controller会生成一个新的Endpoints对象,记录Pod的IP和端口,这就解决了前文提到的后端实例健康检查问题。
  • Kubernetes会从集群的可用服务IP池中为每个新创建的服务分配一个稳定的集群内访问IP地址,称为Cluster IP。Kubernetes还会通过添加DNS条目为Cluster IP分配主机名。Cluster IP和主机名在集群内是独一无二的,并且在服务的整个生命周期内不会更改。只有将服务从集群中删除,Kubernetes才会释放Cluster IP和主机名。用户可以使用服务的Cluster IP或主机名访问正常运行的Pod。
  • Kubernetes使用Kube-proxy组件管理各服务与之后端Pod的连接,该组件在每个节点上运行。
  • Kube-proxy是一个基于出站流量的负载平衡控制器,它监控Kubernetes API Service并持续将服务IP(包括Cluster IP等)映射到运行状况良好的Pod,落实到主机上就是iptables/IPVS等路由规则。访问服务的IP会被这些路由规则直接DNAT到Pod IP,然后走底层容器网络送到对应的Pod。
  • 服务分配的Cluster IP是一个虚拟IP,刚接触Kubernetes Service的人经常犯的错误是试图ping这个IP,然后发现它没有任何响应。实际上,这个虚拟IP只有和它的port一起使用才有作用,直接访问该IP或者想访问该IP的其他端口都是徒劳。
  • Kubernetes Service能够支持TCP、UDP和SCTP三种协议,默认是TCP协议。
  • 当Service的后端Pod准备就绪后,Kubernetes会生成一个新的Endpoints对象,而且这个Endpoints对象和Service同名。
  • Service的三个port先来看一个最简单的Service定义:Service的几个port的概念很容易混淆,它们分别是port、targetPort和NodePort。port表示Service暴露的服务端口,也是客户端访问用的端口,例如Cluster IP:port是提供给集群内部客户访问Service的入口。需要注意的是,port不仅是Cluster IP上暴露的端口,还可以是external IP和Load Balancer IP。Service的port并不监听在节点IP上,即无法通过节点IP:port的方式访问Service。NodePort是Kubernetes提供给集群外部访问Service入口的一种方式(另一种方式是Load Balancer),所以可以通过Node IP:nodePort的方式提供集群外访问Service的入口。需要注意的是,我们这里说的集群外指的是Pod网段外,例如Kubernetes节点或因特网。targetPort很好理解,它是应用程序实际监听Pod内流量的端口,从port和NodePort上到来的数据,最终经过Kube-proxy流入后端Pod的targetPort进入容器。在配置服务时,可以选择定义port和targetPort的值重新映射其监听端口,这也被称为Service的端口重映射。Kube-proxy通过在节点上iptables规则管理此端口的重新映射过程。
  • Kubernetes Service有几种类型:Cluster IP、Load Balancer和NodePort。
  • 其中,Cluster IP是默认类型,自动分配集群内部可以访问的虚IP——Cluster IP。
  • Cluster IP的主要作用是方便集群内Pod到Pod之间的调用。
  • Cluster IP主要在每个node节点使用iptables,将发向Cluster IP对应端口的数据转发到后端Pod中
  • NodePort的实现机制是Kube-proxy会创建一个iptables规则,所有访问本地NodePort的网络包都会被直接转发至后端Port。
  • NodePort的问题集中体现在性能和可对宿主机端口占用方面。一旦服务多起来,NodePort在每个节点上开启的端口会变得非常庞大且难以维护
  • 最早的时候,Kubernetes采用了Docker曾经使用过的方法——环境变量。Kubelet创建每个Pod时,会把系统当前所有服务的IP和端口信息都通过环境变量的方式注入容器。这样Pod中的应用可以通过读取环境变量获取所需服务的地址信息。
  • 但这种方式的缺点也很明显:·容易环境变量洪泛,Docker启动参数过长会影响性能,甚至直接导致容器启动失败;·Pod想要访问的任何Service必须在Pod自己被创建之前创建,否则这些环境变量就不会被注入。更理想的方案是,应用能够直接使用服务的名字,不需要关心它实际的IP地址,中间的转换能够自动完成。名字和IP之间的转换即DNS,DNS的方式并没有以上两个限制。在Kubernetes中使用域名服务,即假设Service(my-svc)在namespace(my-ns)中,暴露名为http的TCP端口,那么在Kubernetes的DNS服务器中会生成两种记录,分别是A记录:域名(my-svc.my-ns)到Cluster IP的映射和SRV记录。
  • 所谓的无头(headless)Service即没有selector的Service。Servcie抽象了该如何访问Kubernetes Pod,也能够抽象其他类型的backend

Kubernetes Service的工作原理

  • 主要涉及的Kubernetes组件有Controller Manager(包括Service Controller和Endpoints Controller)和Kube-proxy
  • IPVS是LVS的负载均衡模块,亦基于netfilter,但比iptables性能更高,具备更好的可扩展性。
  • IPVS支持三种负载均衡模式:Direct Routing(简称DR)、Tunneling(也称ipip模式)和NAT(也称Masq模式)。
  • Kube-proxy实现的是分布式负载均衡,而非集中式负载均衡。何谓分布式负载均衡器呢?就是每个节点都充当一个负载均衡器,每个节点上都会被配置一模一样的转发规则。上文提到,受制于iptables的实现,iptables模式的转发策略底层实现其实就是随机法,即将请求随机地分配到各个后端Pod(可能在不同节点上)。由概率统计理论得知,随着客户端调用服务端次数的增加,其实际效果越来越接近评价分配,也就是轮询(rr)的结果。缺点也比较明显,就是没有考虑机器的性能问题。根据木桶原理,Service的性能瓶颈会受性能最差的节点影响。那么,支持多种Load Balancer算法的IPVS模式呢?例如,lc(最小连接数)策略能否奏效?受制于Kube-proxy的分布式负载均衡架构,恐怕很难。同一个后端Pod可能有不同的Kube-proxy把请求转发给它,因此任何一个Kube-proxy都无法准确估计其后端Pod的连接数,故最小连接数这种转发策略无法派上用场。不过,可以尝试IPVS模式的sed(最短时延)转发策略。

Ingress

  • Ingress Controller将Ingress入口地址和后端Pod地址的映射关系(规则)实时刷新到Load Balancer的配置文件中,再让负载均衡器重载(reload)该规则,便可实现服务的负载均衡和自动发现。
  • Kubernetes为什么要发明Ingress这个概念呢?笔者认为,其中一个重要的原因便是服务动态发现和负载均衡。在微服务的开发模式下,外部网络要通过域名、UR路径、负载均衡等转发到后端私有网络中,微服务之所以称为微,是因为它是动态变化的,它会经常被增加、删除或更新。传统的反向代理对服务动态变化的支持不是很方便,也就是服务变更后,不是很容易马上改变配置和热加载。Nginx Ingress Controller的出现就是为了解决这个问题,它可以时刻监听服务注册或服务编排API,随时感知后端服务变化,自动重新更改配置并重新热加载,期间服务不会暂停或停止,这对用户来说是无感知的。
  • 因为微服务架构及Kubernetes等编排工具最近几年才开始逐渐流行,所以一开始的反向代理服务器(例如Nginx和HA Proxy)并未提供对微服务的支持,才会出现Nginx Ingress Controller这种中间层做Kubernetes和负载均衡器(例如Nginx)之间的适配器(adapter)。Nginx Ingress Controller的存在就是为了与Kubernetes交互,同时刷新Nginx配置,还能重载Nginx。而号称云原生边界路由的Traefik设计得更彻底,首先它是个反向代理,其次原生提供了对Kubernetes的支持,也就是说,Traefik本身就能跟Kubernetes打交道,感知Kubernetes集群服务的更新。Traefik是原生支持Kubernetes Ingress的,因此用户在使用Traefik时无须再开发一套Nginx Ingress Controller,受到了广大运维人员的好评。相比Nginx和HA Proxy这类老古董,Traefik设计思想比较先进,有点“Envoy+Istio”降维打击Nginx的意思。

RC(Replication Controller)

  • 为需要扩容的Service关联的Pod创建一个RC(定义Pod副本数量,Label等)
  • RC是Kubernetes系统中的核心概念之一,简单来说,它其实定义了一个期望的场景,即声明某种Pod的副本数量在任意时刻都符合某个预期值。
  • RC的定义包括如下几个部分。
    • Pod期待的副本数量。
    • 用于筛选目标Pod的Label Selector。
    • 当Pod的副本数量小于预期数量时,用于创建新Pod的Pod模板(template)。
  • 定义了一个RC并将其提交到Kubernetes集群中后,Master上的Controller Manager组件就得到通知,定期巡检系统中当前存活的目标Pod,并确保目标Pod实例的数量刚好等于此RC的期望值。
  • 最佳的系统升级方式是旧版本的Pod每停止一个,就同时创建一个新版本的Pod,在整个升级过程中此消彼长,而运行中的Pod数量始终是10个,几分钟以后,当所有的Pod都已经是新版本时,系统升级完成。通过RC机制,Kubernetes很容易就实现了这种高级实用的特性,被称为“滚动升级”(Rolling Update)
  • 总结一下Replication Controller的职责,如下所述。
    1. 确保在当前集群中有且仅有N个Pod实例,N是在RC中定义的Pod副本数量。
    2. 通过调整RC的spec.replicas属性值来实现系统扩容或者缩容。
    3. 通过改变RC中的Pod模板(主要是镜像版本)来实现系统的滚动升级。

Replica Set

  • Replication Controller由于与Kubernetes代码中的模块Replication Controller同名,同时“Replication Controller”无法准确表达它的本意,所以在Kubernetes 1.2中,升级为另外一个新概念——Replica Set,官方解释其为“下一代的RC”。Replica Set与RC当前的唯一区别是,Replica Sets支持基于集合的Label selector(Set-based selector),而RC只支持基于等式的Label Selector(equality-based selector),这使得Replica Set的功能更强。

  • kubectl命令行工具适用于RC的绝大部分命令同样适用于Replica Set。此外,我们当前很少单独使用Replica Set,它主要被Deployment这个更高层的资源对象所使用,从而形成一整套Pod创建、删除、更新的编排机制。我们在使用Deployment时,无须关心它是如何创建和维护Replica Set的,这一切都是自动发生的。

  • Replica Set与Deployment这两个重要的资源对象逐步替代了之前RC的作用,是Kubernetes 1.3里Pod自动扩容(伸缩)这个告警功能实现的基础

  • 总结一下RC(Replica Set)的一些特性与作用。

    • 在大多数情况下,我们通过定义一个RC实现Pod的创建及副本数量的自动控制。
    • 在RC里包括完整的Pod定义模板。
    • RC通过Label Selector机制实现对Pod副本的自动控制。
    • 通过改变RC里的Pod副本数量,可以实现Pod的扩容或缩容。
    • 通过改变RC里Pod模板中的镜像版本,可以实现Pod的滚动升级。

Deployment

  • Deployment是Kubernetes在1.2版本中引入的新概念,用于更好地解决Pod的编排问题。为此,Deployment在内部使用了Replica Set来实现目的,无论从Deployment的作用与目的、YAML定义,还是从它的具体命令行操作来看,我们都可以把它看作RC的一次升级,两者的相似度超过90%。Deployment相对于RC的一个最大升级是我们可以随时知道当前Pod“部署”的进度。
  • Deployment的典型使用场景有以下几个。
    • 创建一个Deployment对象来生成对应的Replica Set并完成Pod副本的创建。
    • 检查Deployment的状态来看部署动作是否完成(Pod副本数量是否达到预期的值)。
    • 更新Deployment以创建新的Pod(比如镜像升级)。
    • 如果当前Deployment不稳定,则回滚到一个早先的Deployment版本。
    • 暂停Deployment以便于一次性修改多个PodTemplateSpec的配置项,之后再恢复Deployment,进行新的发布。
    • 扩展Deployment以应对高负载。
    • 查看Deployment的状态,以此作为发布是否成功的指标。
    • 清理不再需要的旧版本ReplicaSets。除了API声明与Kind类型等有所区别,Deployment的定义与Replica Set的定义很类似。
  • Pod的升级和回滚:如果Pod是通过Deployment创建的,则用户可以在运行时修改Deployment的Pod定义(spec.template)或镜像名称,并应用到Deployment对象上,系统即可完成Deployment的自动更新操作。如果在更新过程中发生了错误,则还可以通过回滚操作恢复Pod的版本。
    • 通过kubectl set image命令为Deployment设置新的镜像名称
    • 使用kubectl rollout status命令查看Deployment的更新过程
    • 用kubectl rollout history命令检查这个Deployment部署的历史记录
    • 撤销本次发布并回滚到上一个部署版本: kubectl rollout undo deployment/nginx-deployment
    • 使用–to-revision参数指定回滚到的部署版本号:kubectl rollout undo deployment/nginx-deployment –to-revision=2
    • 运行kubectl rolling-update命令完成Pod的滚动升级
    • 如果在更新过程中发现配置有误,则用户可以中断更新操作,并通过执行kubectl rolling- update –rollback完成Pod版本的回滚

RC、Deployment、ReplicaSet

StatefulSet

  • StatefulSet从本质上来说,可以看作Deployment/RC的一个特殊变种,它有如下特性。
    • StatefulSet里的每个Pod都有稳定、唯一的网络标识,可以用来发现集群内的其他成员。假设StatefulSet的名称为kafka,那么第1个Pod叫kafka-0,第2个叫kafka-1,以此类推。
    • StatefulSet控制的Pod副本的启停顺序是受控的,操作第n个Pod时,前n-1个Pod已经是运行且准备好的状态。
    • StatefulSet里的Pod采用稳定的持久化存储卷,通过PV或PVC来实现,删除Pod时默认不会删除与StatefulSet相关的存储卷(为了保证数据的安全)。
  • StatefulSet除了要与PV卷捆绑使用以存储Pod的状态数据,还要与Headless Service配合使用,即在每个StatefulSet定义中都要声明它属于哪个Headless Service。Headless Service与普通Service的关键区别在于,它没有Cluster IP,如果解析Headless Service的DNS域名,则返回的是该Service对应的全部Pod的Endpoint列表。
  • StatefulSet的更新策略Kubernetes从1.6版本开始,针对StatefulSet的更新策略逐渐向Deployment和DaemonSet的更新策略看齐,也将实现RollingUpdate、Paritioned和OnDelete这几种策略,以保证StatefulSet中各Pod有序地、逐个地更新,并且能够保留更新历史,也能回滚到某个历史版本。
  • 使用StatefulSet搭建MongoDB集群
    • 在创建StatefulSet之前,需要确保在Kubernetes集群中管理员已经创建好共享存储,并能够与StorageClass对接,以实现动态存储供应的模式。
    • 创建StatefulSet为了完成MongoDB集群的搭建,需要创建如下三个资源对象。
      • 一个StorageClass,用于StatefulSet自动为各个应用Pod申请PVC。
      • 一个Headless Service,用于维护MongoDB集群的状态。
      • 一个StatefulSet。
    • MongoDB集群的扩容:假设在系统运行过程中,3个mongo实例不足以满足业务的要求,这时就需要对mongo集群进行扩容。仅需要通过对StatefulSet进行scale操作,就能实现在mongo集群中自动添加新的mongo节点。使用kubectl scale命令将StatefulSet设置为4个实例: # kubectl scale –replicas=4 statefulset mongo,同时,系统也为mongo-3分配了一个新的PVC用于保存数据
    • 自动故障恢复(MongoDB集群的高可用):Kubernetes使用StatefulSet来搭建有状态的应用集群(MongoDB、MySQL等),同部署无状态的应用一样简便。Kubernetes能够保证StatefulSet中各应用实例在创建和运行的过程中,都具有固定的身份标识和独立的后端存储;还支持在运行时对集群规模进行扩容、保障集群的高可用等非常重要的功能。

HPA

  • Horizontal Pod Autoscaling(Pod横向自动扩容,HPA)。HPA与之前的RC、Deployment一样,也属于一种Kubernetes资源对象。。
  • HPA有以下两种方式作为Pod负载的度量指标。
    • CPUUtilizationPercentage。
    • 应用程序自定义的度量指标,比如服务在每秒内的相应请求数(TPS或QPS)。

Job

  • Job所控制的Pod副本是短暂运行的,可以将其视为一组Docker容器,其中的每个Docker容器都仅仅运行一次。当Job控制的所有Pod副本都运行结束时,对应的Job也就结束了。Job在实现方式上与RC等副本控制器不同,Job生成的Pod副本是不能自动重启的,对应Pod副本的RestartPoliy都被设置为Never。因此,当对应的Pod副本都执行完成时,相应的Job也就完成了控制使命,即Job生成的Pod在Kubernetes中是短暂存在的。Kubernetes在1.5版本之后又提供了类似crontab的定时任务——CronJob,解决了某些批处理任务需要定时反复执行的问题。
  • Job所控制的Pod副本的工作模式能够多实例并行计算,以TensorFlow框架为例,可以将一个机器学习的计算任务分布到10台机器上,在每台机器上都运行一个worker执行计算任务,这很适合通过Job生成10个Pod副本同时启动运算。

DaemonSet

  • 在每个Node上调度并且仅仅创建一个Pod副本。这种调度通常用于系统监控相关的Pod,比如主机上的日志采集、主机性能采集等进程需要被部署到集群中的每个节点,并且只能部署一个副本,这就是DaemonSet这种特殊Pod副本控制器所解决的问题。
  • 这种用法适合有这种需求的应用。◎ 在每个Node上都运行一个GlusterFS存储或者Ceph存储的Daemon进程。◎ 在每个Node上都运行一个日志采集程序,例如Fluentd或者Logstach。◎ 在每个Node上都运行一个性能监控程序,采集该Node的运行性能数据,例如Prometheus Node Exporter、collectd、New Relic agent或者Ganglia gmond等。
  • 目前DaemonSet的升级策略包括两种:OnDelete和RollingUpdate。

Volume

  • Volume(存储卷)是Pod中能够被多个容器访问的共享目录。Kubernetes的Volume概念、用途和目的与Docker的Volume比较类似,但两者不能等价。首先,Kubernetes中的Volume被定义在Pod上,然后被一个Pod里的多个容器挂载到具体的文件目录下;其次,Kubernetes中的Volume与Pod的生命周期相同,但与容器的生命周期不相关,当容器终止或者重启时,Volume中的数据也不会丢失。最后,Kubernetes支持多种类型的Volume,例如GlusterFS、Ceph等先进的分布式文件系统。
  • Volume的使用也比较简单,在大多数情况下,我们先在Pod上声明一个Volume,然后在容器里引用该Volume并挂载(Mount)到容器里的某个目录上。
  • Kubernetes的Volume还扩展出了一种非常有实用价值的功能,即容器配置文件集中化定义与管理,这是通过ConfigMap这种新的资源对象来实现的。
  • Volume是被定义在Pod上的,属于计算资源的一部分,而实际上,网络存储是相对独立于计算资源而存在的一种实体资源。
  • 同一个Pod中的多个容器能够共享Pod级别的存储卷Volume。
  • PV可以被理解成Kubernetes集群中的某个网络存储对应的一块存储,它与Volume类似,但有以下区别。
    • PV只能是网络存储,不属于任何Node,但可以在每个Node上访问。
    • PV并不是被定义在Pod上的,而是独立于Pod之外定义的。
    • PV目前支持的类型包括:gcePersistentDis。

PV、PVC、StorageClass

  • k8s之PV、PVC、StorageClass详解
  • PV是对底层网络共享存储的抽象,将共享存储定义为一种“资源”,比如Node也是容器应用可以消费的资源。PV由管理员创建和配置,与共享存储的具体实现直接相关。
  • PVC则是用户对存储资源的一个“申请”,就像Pod消费Node资源一样,PVC能够消费PV资源。PVC可以申请特定的存储空间和访问模式。(PVC 的全称是:PersistentVolumeClaim(持久化卷声明),PVC 是用户存储的一种声明)
  • StorageClass,用于标记存储资源的特性和性能,管理员可以将存储资源定义为某种类别,正如存储设备对于自身的配置描述(Profile)。根据StorageClass的描述可以直观的得知各种存储资源的特性,就可以根据应用对存储资源的需求去申请存储资源了。存储卷可以按需创建。
  • 数据的容灾由具体安装部署的服务自行实现,k8s只负责资源的分配。

Namespace

  • Namespace在很多情况下用于实现多租户的资源隔离。Namespace通过将集群内部的资源对象“分配”到不同的Namespace中,形成逻辑上分组的不同项目、小组或用户组,便于不同的分组在共享使用整个集群的资源的同时还能被分别管理。
  • Kubernetes集群在启动后会创建一个名为default的Namespace,通过kubectl可以查看: kubectl get namespaces
  • 如果不特别指明Namespace,则用户创建的Pod、RC、Service都将被系统创建到这个默认的名为default的Namespace中。
  • 一旦创建了Namespace,我们在创建资源对象时就可以指定这个资源对象属于哪个Namespace。
  • 当给每个租户创建一个Namespace来实现多租户的资源隔离时,还能结合Kubernetes的资源配额管理,限定不同租户能占用的资源,例如CPU使用量、内存使用量等

CRI

  • CRI(容器运行时接口)详解
  • 归根结底,Kubernetes Node(kubelet)的主要功能就是启动和停止容器的组件,我们称之为容器运行时(Container Runtime),其中最知名的就是Docker了。为了更具扩展性,Kubernetes从1.5版本开始就加入了容器运行时插件API,即Container Runtime Interface,简称CRI。
  • kubelet使用gRPC框架通过UNIX Socket与容器运行时(或CRI代理)进行通信
  • Protocol Buffers API包含两个gRPC服务:ImageService和RuntimeService。
  • kubelet的职责在于通过RPC管理容器的生命周期,实现容器生命周期的钩子,存活和健康监测,以及执行Pod的重启策略等。
  • 要尝试新的Kubelet-CRI-Docker集成,只需为kubelet启动参数加上–enable-cri=true开关来启动CRI。这个选项从Kubernetes 1.6开始已经作为kubelet的默认选项了

CRI介绍

  • 从零开始入门 K8s:理解容器运行时接口 CRI
  • 在 CRI 出现之前(也就是 Kubernetes v1.5 之前),Docker 作为第一个容器运行时,Kubelet 通过内嵌的 dockershim 操作 Docker API 来操作容器,进而达到一个面向终态的效果。在这之后,又出现了一种新的容器运行时 - rkt,它也想要成为 Kubernetes 支持的一个容器运行时,当时它也合到了 Kubelet 的代码之中。这两个容器运行时的加入使得 Kubernetes 的代码越来越复杂、难以维护。之后 hyber.sh 加入社区,也想成为第三个容器运行时。
  • 此时就有人站出来说,我们能不能对容器运行时的操作抽象出一个接口,将 Kubelet 代码与具体的容器运行时的实现代码解耦开,只要实现了这样一套接口,就能接入到 Kubernetes 的体系中,这就是我们后来见到的 Container Runtime Interface (CRI)。

监控

  • 所有以非API Server方式创建的Pod都叫作Static Pod
  • 在新的Kubernetes监控体系中,Metrics Server用于提供Core Metrics(核心指标),包括Node和Pod的CPU和内存使用数据。其他Custom Metrics(自定义指标)则由第三方组件(如Prometheus)采集和存储。

网络部分

  • Docker时代,需要将容器的端口映射到宿主机端口,以便在外部访问。
    • k8s: type=NodePort和nodePort=30001的两个属性表明此Service开启了NodePort方式的外网访问模式。
  • Kubernetes集群的网络配置在多个Node组成的Kubernetes集群内,跨主机的容器间网络互通是Kubernetes集群能够正常工作的前提条件。
  • Kubernetes本身并不会对跨主机的容器网络进行设置,这需要额外的工具来实现。除了谷歌公有云GCE平台提供的网络设置,一些开源的工具包括Flannel、Open vSwitch、Weave、Calico等都能够实现跨主机的容器间网络互通。随着CNI网络模型的逐渐成熟,Kubernetes将优先使用CNI网络插件打通跨主机的容器网络。
  • Kubernetes网络模型设计的一个基础原则是:每个Pod都拥有一个独立的IP地址,并假定所有Pod都在一个可以直接连通的、扁平的网络空间中。所以不管它们是否运行在同一个Node(宿主机)中,都要求它们可以直接通过对方的IP进行访问。设计这个原则的原因是,用户不需要额外考虑如何建立Pod之间的连接,也不需要考虑如何将容器端口映射到主机端口等问题。
  • Kubernetes的网络依赖于Docker,Docker的网络又离不开Linux操作系统内核特性的支持

IP-per-Pod模型

  • 将IP地址和端口在Pod内部和外部都保持一致,也就不需要使用NAT来进行地址转换了
  • IP-per-Pod模式和Docker原生的通过动态端口映射方式实现的多节点访问模式有什么区别呢?主要区别是后者的动态端口映射会引入端口管理的复杂性,而且访问者看到的IP地址和端口与服务提供者实际绑定的不同(因为NAT的缘故,它们都被映射成新的地址或端口了),这也会引起应用配置的复杂化。
  • 同时,标准的DNS等名字解析服务也不适用了,甚至服务注册和发现机制都将迎来挑战,因为在端口映射情况下,服务自身很难知道自己对外暴露的真实的服务IP和端口,外部应用也无法通过服务所在容器的私有IP地址和端口来访问服务。总的来说,IP-per-Pod模型是一个简单的兼容性较好的模型。从该模型的网络的端口分配、域名解析、服务发现、负载均衡、应用配置和迁移等角度来看,Pod都能够被看作一台独立的虚拟机或物理机。
  • 按照这个网络抽象原则,Kubernetes对网络有什么前提和要求呢?Kubernetes对集群网络有如下要求。
    1. 所有容器都可以在不用NAT的方式下同别的容器通信。
    2. 所有节点都可以在不用NAT的方式下同所有容器通信,反之亦然。
    3. 容器的地址和别人看到的地址是同一个地址。

Docker网络基础

  • 网络命名空间(Network Namespace)、Veth设备对、网桥、Iptables和路由。
  • 标准的Docker支持以下4类网络模式
    1. host模式:使用–net=host指定。
    2. container模式:使用–net=container:NAME_or_ID指定。(同一个Pod内的容器)
    3. none模式:使用–net=none指定。
    4. bridge模式:使用–net=bridge指定,为默认设置。
  • Docker的网络局限从Docker对Linux网络协议栈的操作可以看到,Docker一开始没有考虑到多主机互联的网络解决方案。
  • 之后,Docker开启了一个宏伟的虚拟化网络解决方案——Libnetwork,这个概念图没有了IP,也没有了路由,已经颠覆了我们的网络常识,对于不怎么懂网络的大多数人来说,它的确很有诱惑力,未来是否会对虚拟化网络的模型产生深远冲击,我们还不得而知,但它仅仅是Docker官方当前的一次“尝试”。
  • Docker容器端口映射原理都是在本地的iptable的nat表中添加相应的规则,将访问本机IP地址:hostport的网包进行一次DNAT,转换成容器IP:containerport。
  • 怎么从容器内访问外网呢?一般情况下需要两个因素:ip_forward和SNAT/MASQUERADE。
    在默认情况下,容器可以访问外部网络的连接,因为容器的默认网络接口为docker0网桥上的接口,即主机上的本地接口。其原理是通过Linux系统的转发功能实现的(把主机当交换机)。至于SNAT/MASQUERADE,Docker会自动在iptables的POSTROUTING链上创建形如下面的规则:即从容器网段出来访问外网的包,都要做一次MASQUERADE,即出去的包都用主机的IP地址替换源地址。

Network Namespace

  • network namespace的增删改查功能已经集成到Linux的ip工具的netns子命令中

  • 为了支持网络协议栈的多个实例,Linux在网络栈中引入了网络命名空间,这些独立的协议栈被隔离到不同的命名空间中。处于不同命名空间中的网络栈是完全隔离的,彼此之间无法通信,就好像两个“平行宇宙”。通过对网络资源的隔离,就能在一个宿主机上虚拟多个不同的网络环境。Docker正是利用了网络的命名空间特性,实现了不同容器之间的网络隔离。

  • 在Linux的网络命名空间中可以有自己独立的路由表及独立的iptables设置来提供包转发、NAT及IP包过滤等功能。为了隔离出独立的协议栈,需要纳入命名空间的元素有进程、套接字、网络设备等。进程创建的套接字必须属于某个命名空间,套接字的操作也必须在命名空间中进行。同样,网络设备也必须属于某个命名空间。因为网络设备属于公共资源,所以可以通过修改属性实现在命名空间之间移动。当然,是否允许移动与设备的特征有关。

  • Linux的网络协议栈是十分复杂的,为了支持独立的协议栈,相关的这些全局变量都必须被修改为协议栈私有。最好的办法就是让这些全局变量成为一个Net Namespace变量的成员,然后为协议栈的函数调用加入一个Namespace参数。这就是Linux实现网络命名空间的核心。同时,为了保证对已经开发的应用程序及内核代码的兼容性,内核代码隐式地使用了命名空间中的变量。程序如果没有对命名空间有特殊需求,就不需要编写额外的代码,网络命名空间对应用程序而言是透明的。

tun/tap

  • 它是一组通用的虚拟驱动程序包,里面包含了两个设备,分别是用于网络数据包处理的虚拟网卡驱动,以及用于内核空间与用户空间交互的字符设备(Character Devices,这里具体指/dev/net/tun)驱动。

  • tun和tap是两个相对独立的虚拟网络设备,其中tap模拟了以太网设备,操作二层数据包(以太帧),tun则模拟了网络层设备,操作三层数据包(IP报文)。

  • 使用tun/tap设备的目的是实现把来自协议栈的数据包先交由某个打开了/dev/net/tun字符设备的用户进程处理后,再把数据包重新发回到链路中。你可以通俗地将它理解为这块虚拟化网卡驱动一端连接着网络协议栈,另一端连接着用户态程序,而普通的网卡驱动则是一端连接则网络协议栈,另一端连接着物理网卡。只要协议栈中的数据包能被用户态程序截获并加工处理,程序员就有足够的舞台空间去玩出各种花样,譬如数据压缩、流量加密、透明代理等功能都能够以此为基础来实现,比如典型的VPN应用程序。

  • 使用tun/tap设备传输数据需要经过两次协议栈,不可避免地会有一定的性能损耗,如果条件允许,容器对容器的直接通信并不会把tun/tap作为首选方案,一般是基于稍后介绍的veth来实现的。但是tun/tap没有veth那样要求设备成对出现、数据要原样传输的限制,数据包到用户态程序后,程序员就有完全掌控的权力,要进行哪些修改,要发送到什么地方,都可以编写代码去实现,因此tun/tap方案比起veth方案有更广泛的适用范围。

  • tun/tap设备到底是什么?从Linux文件系统的角度看,它是用户可以用文件句柄操作的字符设备;从网络虚拟化角度看,它是虚拟网卡,一端连着网络协议栈,另一端连着用户态程序。

  • tun表示虚拟的是点对点设备,tap表示虚拟的是以太网设备

  • tun/tap设备可以将TCP/IP协议栈处理好的网络包发送给任何一个使用tun/tap驱动的进程,由进程重新处理后发到物理链路中。tun/tap设备就像是埋在用户程序空间的一个钩子,我们可以很方便地将对网络包的处理程序挂在这个钩子上,OpenVPN、Vtun、flannel都是基于它实现隧道包封装的。

  • 从网络协议栈的角度看,tun/tap设备这类虚拟网卡与物理网卡并无区别。只是对tun/tap设备而言,它与物理网卡的不同表现在它的数据源不是物理链路,而是来自用户态!这也是tun/tap设备的最大价值所在

  • flannel的UDP模式的技术要点就是tun/tap设备。

  • tun/tap设备其实就是利用Linux的设备文件实现内核态和用户态的数据交互,而访问设备文件则会调用设备驱动相应的例程,要知道设备驱动也是内核态和用户态的一个接口。

  • 普通的物理网卡通过网线收发数据包,而tun设备通过一个设备文件(/dev/tunX)收发数据包。所有对这个文件的写操作会通过tun设备转换成一个数据包传送给内核网络协议栈。当内核发送一个包给tun设备时,用户态的进程通过读取这个文件可以拿到包的内容。当然,用户态的程序也可以通过写这个文件向tun设备发送数据。tap设备与tun设备的工作原理完全相同,区别在于:·tun设备的/dev/tunX文件收发的是IP包,因此只能工作在L3,无法与物理网卡做桥接,但可以通过三层交换(例如ip_forward)与物理网卡连通;·tap设备的/dev/tapX文件收发的是链路层数据包,可以与物理网卡做桥接。

  • tun/tap设备的用处是将协议栈中的部分数据包转发给用户空间的应用程序,给用户空间的程序一个处理数据包的机会。常见的tun/tap设备使用场景有数据压缩、加密等,最常见的是VPN,包括tunnel及应用层的IPSec等。我们将使用tun设备搭建一个基于UDP的VPN

veth pair(Veth设备对)

  • veth全称是(Virtual Ethernet)虚拟以太网,其原理类似linux管道,在一个veth设备写入网络包,其对端的veth设备可以读取到对应的网络包。
  • veth实际上不是一个设备,而是一对设备,因而也常被称作veth pair。要使用veth,必须在两个独立的网络名称空间中进行才有意义,因为veth pair是一端连着协议栈,另一端彼此相连的,在veth设备的其中一端输入数据,这些数据就会从设备的另外一端原样不变地流出。
  • 引入Veth设备对是为了在不同的网络命名空间之间通信,利用它可以直接将两个网络命名空间连接起来。
  • 创建Veth设备对: ip link add veth0 type veth peer name veth1
  • veth pair的缺点
    • veth pair相当于网线连接两个网口,打个比喻,我们平时使用电脑插路由器网线,在你电脑的网口和路由器的lan口就是veth pair。
    • 其不足也在这里,只能连接两个network namespace,如果要多个network namespace进行通信,会非常复杂,你会建立一系列的veth pair,整个关系网是点对点的,也就是任意两个network namespace都需要veth pair来通信。
    • 这个问题的解决办法需要依赖linux网桥(bridge),利用网桥来将多个veth设备连接起来。
  • 由于两个容器之间采用veth通信不需要反复多次经过网络协议栈,这让veth比起tap/tun具有更好的性能,也让veth pair的实现变的十分简单。

bridge(网桥)

  • 网桥是一个二层的虚拟网络设备,把若干个网络接口“连接”起来,以使得网络接口之间的报文能够互相转发。网桥能够解析收发的报文,读取目标MAC地址的信息,和自己记录的MAC表结合,来决策报文的转发目标网络接口。为了实现这些功能,网桥会学习源MAC地址(二层网桥转发的依据就是MAC地址)。在转发报文时,网桥只需要向特定的网口进行转发,来避免不必要的网络交互。如果它遇到一个自己从未学习到的地址,就无法知道这个报文应该向哪个网络接口转发,就将报文广播给所有的网络接口(报文来源的网络接口除外)。在实际的网络中,网络拓扑不可能永久不变。设备如果被移动到另一个端口上,却没有发送任何数据,网桥设备就无法感知到这个变化,网桥还是向原来的端口转发数据包,在这种情况下数据就会丢失。所以网桥还要对学习到的MAC地址表加上超时时间(默认为5min)。如果网桥收到了对应端口MAC地址回发的包,则重置超时时间,否则过了超时时间后,就认为设备已经不在那个端口上了,它就会重新广播发送。

  • 在Linux的内部网络栈里实现的网桥设备,作用和上面的描述相同。过去Linux主机一般都只有一个网卡,现在多网卡的机器越来越多,而且有很多虚拟的设备存在,所以Linux的网桥提供了在这些设备之间互相转发数据的二层设备。Linux内核支持网口的桥接(目前只支持以太网接口)。但是与单纯的交换机不同,交换机只是一个二层设备,对于接收到的报文,要么转发,要么丢弃。运行着Linux内核的机器本身就是一台主机,有可能是网络报文的目的地,其收到的报文除了转发和丢弃,还可能被送到网络协议栈的上层(网络层),从而被自己(这台主机本身的协议栈)消化,所以我们既可以把网桥看作一个二层设备,也可以把它看作一个三层设备。

  • 容器先创建veth设备对(veth0、veth1),再把veth1连到同一个bridge。

  • 配置例子:

    网桥br0:分配IP地址192.168.31.1;
    容器:三个网络名称空间(容器),分别编号为1、2、3,均使用veth pair接入网桥,且有如下配置:
    在容器一端的网卡名为veth0,在网桥一端网卡名为veth1、veth2、veth3;
    三个容器中的veth0网卡分配IP地址:192.168.1.10、192.168.1.11、192.168.1.12;
    三个容器中的veth0网卡设置网关为网桥,即192.168.31.1;
    网桥中的veth1、veth2、veth3无IP地址;
    物理网卡eth0:分配的IP地址14.123.254.86;
    外部网络:外部网络中有一台服务器,地址为122.246.6.183
    
  • 混杂模式(Promiscuous mode),简称Promisc mode,俗称“监听模式”

    • 混杂模式是指一个网卡会把它接收的所有网络流量都交给CPU,而不是只把它想转交的部分交给CPU。
    • 在IEEE 802定的网络规范中,每个网络帧都有一个目的MAC地址。在非混杂模式下,网卡只会接收目的MAC地址是它自己的单播帧,以及多播及广播帧;在混杂模式下,网卡会接收经过它的所有帧
    • 将网络设备加入Linux bridge后,会自动进入混杂模式。

iptables

  • 在Linux网络协议栈中有一组回调函数挂接点,通过这些挂接点挂接的钩子函数可以在Linux网络栈处理数据包的过程中对数据包进行一些操作,例如过滤、修改、丢弃等。整个挂接点技术叫作Netfilter和iptables。
  • Netfilter负责在内核中执行各种挂接的规则,运行在内核模式中;而iptables是在用户模式下运行的进程,负责协助和维护内核中Netfilter的各种规则表。二者互相配合来实现整个Linux网络协议栈中灵活的数据包处理机制。

路由

  • 路由功能由IP层维护的一张路由表来实现。当主机收到数据报文时,它用此表来决策接下来应该做什么操作。当从网络侧接收到数据报文时,IP层首先会检查报文的IP地址是否与主机自身的地址相同。如果数据报文中的IP地址是主机自身的地址,那么报文将被发送到传输层相应的协议中。如果报文中的IP地址不是主机自身的地址,并且主机配置了路由功能,那么报文将被转发,否则,报文将被丢弃。
  • Linux的路由表至少包括两个表(当启用策略路由时,还会有其他表):一个是LOCAL,另一个是MAIN。在LOCAL表中会包含所有的本地设备地址。LOCAL路由表是在配置网络设备地址时自动创建的。LOCAL表用于供Linux协议栈识别本地地址,以及进行本地各个不同网络接口之间的数据转发。可以通过下面的命令查看LOCAL表的内容: ip route show table local type local
  • MAIN表用于各类网络IP地址的转发。它的建立既可以使用静态配置生成,也可以使用动态路由发现协议生成。动态路由发现协议一般使用组播功能来通过发送路由发现数据,动态地交换和获取网络的路由信息,并更新到路由表中。路由表的查看我们可以使用ip route list命令查看当前的路由表: ip route list
  • Netstat -rn 是另一个查看路由表的工具:
    在它显示的信息中,如果标志是U,则说明是可达路由;如果标志是G,则说明这个网络接口连接的是网关,否则说明这个接口直连主机。

Linux隧道:ipip

  • tun设备也叫作点对点设备,之所以叫这个名字,是因为tun经常被用来做隧道通信(tunnel)。
  • Linux原生支持下列5种L3隧道:
    • ipip:即IPv4 in IPv4,在IPv4报文的基础上封装一个IPv4报文;
    • GRE:即通用路由封装(Generic Routing Encapsulation),定义了在任意一种网络层协议上封装其他任意一种网络层协议的机制,适用于IPv4和IPv6;
    • sit:和ipip类似,不同的是sit用IPv4报文封装IPv6报文,即IPv6 over IPv4;
    • ISATAP:即站内自动隧道寻址协议(Intra-Site Automatic Tunnel Addressing Protocol),与sit类似,也用于IPv6的隧道封装;
    • VTI:即虚拟隧道接口(Virtual Tunnel Interface),是思科提出的一种IPSec隧道技术。下面我们以ipip为例,介绍Linux隧道通信的基本原理。:
  • Linux L3隧道底层实现原理都基于tun设备。我们熟知的各种VPN软件,其底层实现都离不开这5种隧道协议。其他隧道实现方式与ipip隧道的大同小异。

VXLAN

  • 目前比较常见的封装报文的技术有VxLAN和隧道(gre、ipip等),其中vxlan是通过udp协议进行封装的,隧道方式是通过ip层封装的。
  • VXLAN(Virtual eXtensible LAN,虚拟可扩展的局域网),是一种虚拟化隧道通信技术。它是一种overlay(覆盖网络)技术,通过三层的网络搭建虚拟的二层网络。
  • 简单来讲,VXLAN是在底层物理网络(underlay)之上使用隧道技术,依托UDP层构建的overlay的逻辑网络,使逻辑网络与物理网络解耦,实现灵活的组网需求。
  • 它不仅能适配虚拟机环境,还能用于容器环境。由此可见,VXLAN这类隧道网络的一个特点是对原有的网络架构影响小,不需要对原网络做任何改动,就可在原网络的基础上架设一层新的网络。
  • 不同于其他隧道协议,VXLAN是一个一对多的网络,并不仅是一对一的隧道协议。一个VXLAN设备能通过像网桥一样的学习方式学习到其他对端的IP地址,也可以直接配置静态转发表。
  • VLAN技术的缺陷是VLAN Header预留的长度只有12 bit,故最多只能支持2的12次方(4096)子网的划分,无法满足云计算场景下主机数量日益增长的需求。VXLAN能突破VLAN的最多4096个子网的数量限制,以满足大规模云计算数据中心的需求。
  • VXLAN的报文就是MAC in UDP,即在三层网络的基础上构建一个虚拟的二层网络。为什么这么说呢?VXLAN的封包格式显示原来的二层以太网帧(包含MAC头部、IP头部和传输层头部的报文),被放在VXLAN包头里进行封装,再套到标准的UDP头部(UDP头部、IP头部和MAC头部),用来在底层网络上传输报文。
  • 总的来说,VXLAN报文的转发过程就是:原始报文经过VTEP,被Linux内核添加上VXLAN包头及外层的UDP头部,再发送出去,对端VTEP接收到VXLAN报文后拆除外层UDP头部,并根据VXLAN头部的VNI把原始报文发送到目的服务器。
  • 传统的局域网是怎么构建的(非虚拟lan),要构成一个局域网,需要一台路由器设备作为网关和其他接入该路由器的设备,这时候lan是这样定义的:接在同一台路由器上(物理)且具有相同网络号(逻辑)的设备在同一个局域网下。那么vxlan可以使得局域网的构建可以打破物理上的限制,构建逻辑上的lan——评论

Macvlan

  • 通常,我们在自定义Docker与外部网络通信的网络时会用到NAT,还有Linux bridge、Open vSwitch、Macvlan几种选择,相比之下,Macvlan拥有更好的性能。
  • 在Macvlan出现之前,我们可以通过网卡别名(例如eth0:1)的方式为一块以太网卡添加多个IP地址,却不能为其添加多个MAC地址。原因是以太网卡是以MAC地址为唯一识别的,而网卡别名并没有改变这些网卡的MAC地址。Macvlan接口可以看作是物理以太网接口的虚拟子接口。Macvlan允许用户在主机的一个网络接口上配置多个虚拟的网络接口,每个Macvlan接口都有自己的区别于父接口的MAC地址,并且可以像普通网络接口一样分配IP地址。因此,使用Macvlan技术带来的效果是一块物理网卡上可以绑定多个IP地址,每个IP地址都有自己的MAC地址。
  • 使用Macvlan的虚拟机或者容器网络与主机在同一个网段,即同一个广播域中。
  • Macvlan支持5种模式,分别是bridge、VEPA、Private、Passthru和Source模式。
  • 在Macvlan虚拟网络世界中,物理网卡(父接口)相当于一个交换机,对于进出其子Macvlan网卡的数据包,物理网卡只转发数据包而不处理数据包,于是也就造成了使用本机Macvlan网卡的IP无法和物理网卡的IP通信。总结,Macvlan只为虚拟机或容器提供访问外部物理网络的连接。
  • Macvlan是将虚拟机或容器通过二层连接到物理网络的一个不错的方案,但它也有一些局限性,例如:·因为每个虚拟网卡都要有自己的MAC地址,所以Macvlan需要大量的MAC地址,而Linux主机连接的交换机可能会限制一个物理端口的MAC地址数量上限,而且许多物理网卡的MAC地址数量也有限制,超过这个限制就会影响到系统的性能;·IEEE 802.11标准(即无线网络)不喜欢同一个客户端上有多个MAC地址,这意味着你的Macvlan子接口没法在无线网卡上通信。我们可以通过复杂的办法突破以上这些限制,但还有一种更简单的办法。那就是使用IPvlan。

IPvlan

  • Macvlan和IPvlan虚拟网络模型提供的功能看起来差不多,那么,什么时候需要用到IPvlan呢?要回答这个问题,先来看看Macvlan先天存在的不足:·无法支持大量的MAC地址;·无法工作在无线网络环境中。
  • 与Macvlan类似,IPvlan也是从一个主机接口虚拟出多个虚拟网络接口。区别在于IPvlan所有的虚拟接口都有相同的MAC地址,而IP地址却各不相同。因为所有的IPvlan虚拟接口共享MAC地址,所以特别需要注意DHCP使用的场景。DHCP分配IP地址的时候一般会用MAC地址作为机器的标识。因此,在使用Macvlan的情况下,客户端动态获取IP的时候需要配置唯一的Client ID,并且DHCP服务器也要使用该字段作为机器标识,而不是使用MAC地址。
  • 外部网络默认情况下是不知道IPvlan虚拟出来的网络的,如果不在外部路由器上配置好对应的路由规则,那么IPvlan的网络是不能被外部直接访问的。
  • 我们将IPvlan称为Macvlan的“救护员”是因为IPvlan除了能够完美解决以上问题,还允许用户基于IPvlan搭建比较复杂的网络拓扑,不再基于Macvlan的简单的二层网络,而是能够与BGP(Boader Gateway Protocol,边界网关协议)等协议扩展我们的网络边界。

Docker的四大网络模式

  • 从网络的角度看容器,就是network namespace+容器的组网方案。利用network namespace,可以为Docker容器创建隔离的网络环境。容器具有完全独立的网络栈,与宿主机隔离。用户也可以让Docker容器共享主机或者其他容器的network namespace。
  • 容器的网络方案可以分为三大部分:
    1. 单机的容器间通信;
    2. 跨主机的容器间通信;
    3. 容器与主机间通信。
  • Docker有以下4种网络模式:
    1. bridge模式,通过–network=bridge指定;(Docker容器的默认组网模式)
      • 连接在docker0上的所有容器的默认网关均为docker0,即访问非本机容器网段要经过docker0网关转发,而同主机上的容器(同网段)之间通过广播通信。
      • bridge模式为Docker容器创建独立的网络栈,保证容器内的进程使用独立的网络环境,使容器和容器、容器和宿主机之间能实现网络隔离。
    2. host模式,通过–network=host指定;
      • 连接到host网络的容器共享Docker host的网络栈,容器的网络配置与host完全一样。host模式下容器将不会获得独立的network namespace,而是和宿主机共用一个network namespace。容器将不会虚拟出自己的网卡,配置自己的IP等,而是使用宿主机的IP和端口。
      • host模式下的容器可以看到宿主机的所有网卡信息,甚至可以直接使用宿主机IP地址和主机名与外界通信,无须额外进行NAT,也无须通过Linux bridge进行转发或者进行数据包的封装。
      • 当然,host模式有利有弊,优点是没有性能损耗且配置方便,缺点也很明显,例如:·容器没有隔离、独立的网络栈:容器因与宿主机共用网络栈而争抢网络资源,并且容器崩溃也可能使主机崩溃,导致网络的隔离性不好;
      • 端口资源冲突:宿主机上已经使用的端口就不能再用了。
    3. container模式,通过–network=container:NAME_or_ID指定,即joiner容器;
      • 创建容器时使用–network=container:NAME_or_ID模式,在创建新的容器时指定容器的网络和一个已经存在的容器共享一个network namespace,但是并不为Docker容器进行任何网络配置,这个Docker容器没有网卡、IP、路由等信息,需要手动为Docker容器添加网卡、配置IP等。需要注意的是,container模式指定新创建的容器和已经存在的任意一个容器共享一个network namespace,但不能和宿主机共享。新创建的容器不会创建自己的网卡,配置自己的IP,而是和一个指定的容器共享IP、端口范围等。同样,两个容器除了网络方面,其他的如文件系统、进程列表等还是隔离的。两个容器的进程可以通过lo网卡设备通信。
      • Kubernetes的Pod网络采用的就是Docker的container模式网络,我们将在后面的章节详细介绍。
    4. none模式,通过–network=none指定。
  • 连接在docker0上的所有容器的默认网关均为docker0,即访问非本机容器网段要经过docker0网关转发,而同主机上的容器(同网段)之间通过广播通信。
    • none模式下的容器只有lo回环网络,没有其他网卡
    • none模式网络可以在容器创建时通过–network=none指定。这种类型的网络没有办法联网,属于完全封闭的网络。唯一的用途是客户有充分的自由度做后续的配置。这种模式下的Docker容器拥有自己的network namespace,但是并不为Docker容器进行任何网络配置。也就是说,这个Docker容器没有网卡、IP、路由等信息,需要我们自己为Docker容器添加网卡、配置IP等。

容器组网的挑战

  • 容器网络的挑战:“以隔离的方式部署容器,在提供隔离自己容器内数据所需功能的同时,保持有效的连接性”,提炼两个关键词便是“隔离”和“连接”。

  • 容器网络最大的挑战在跨容器通信层面,具体有以下几点:

    1. 虚拟机拥有较强的隔离机制,虚拟网卡与硬件网卡在使用上没有什么区别。由于容器基于网络隔离能力较弱的network namespace,容器网络的设计首要考虑隔离性和安全性;
    2. 出于安全考虑,很多情况下容器会被部署在虚拟机内部,这种嵌套部署对应一个新的组网模型;
    3. 原生Docker容器不解决跨主机通信问题,而大规模的容器部署势必涉及不同主机上的网络通信;
    4. 容器相较虚拟机生命周期更短,重启更加频繁,重启后IP地址将发生变化,需要进行高效的网络地址管理,因此静态的IP地址分配或DHCP(耗费数秒才能生效)将不起作用;
    5. 在创建虚拟机的时候,IP地址的分配是一种静态方式。但是在容器上面,IP地址的分配通常是动态的。在创建容器时,通常我们不知道它的IP是什么,只有真正运行后才能知道;
    6. 容器相较虚拟机发生迁移的频率更高,且原生的Docker容器不支持热迁移,迁移后的容器面临IP地址漂移,而且经常出现不在同一网段的情况,对应的网络策略的刷新要及时跟上;
    7. 容器的部署数量远大于虚拟机,如果为每个容器分配一个主机网络的IP地址,则可能会导致不够分配;
    8. 大规模跨机部署带来多主机间的ARP洪泛,造成大量的资源浪费;
    9. 大部分的容器网络方案都会使用iptables和NAT,NAT的存在会造成通信两端看不到彼此的真实IP地址,并且iptable和NAT的结合使用限制了可扩展性和性能,这让使用容器的主要优势之一荡然无存;
    10. 大规模部署使用veth设备会影响网络性能,最严重时甚至会比裸机降低50%;
    11. 过多的MAC地址。当MAC地址超过限制时,主机中的物理网络接口卡虽然可以切换到混杂模式,但会导致性能下降,而且顶级机架(ToR)交换机可支持的MAC地址数量也有上限。
  • 针对以上挑战,有一些针对性的解决方案。例如,可以使用Macvlan和IPvlan替代veth,以降低处理veth网络的性能开销。当使用IPvlan驱动时,IPvlan工作在L3,而L3网络不会受到洪泛影响,因此只有主机的物理网卡MAC地址可见,容器MAC地址不会暴露在网络中,从而解决数据中心可见MAC地址过多给ToR交换机带来的影响。容器网络对NAT的依赖及NAT自身的局限性,而避免NAT的基本思想是将容器和虚拟机、主机同等对待。我们可以直接将容器连接到主机的网络接口来实现这一点,即容器与主机共享本地局域网(LAN)的资源,通过DHCP服务器或静态地址分配的方式给容器分配LAN的IP地址。这样一来,所有L4的容器网络端口完全暴露,虽然这种直接的暴露比管理映射端口更好,但保证安全状态需要网络策略的加持。直接连接到物理接口,即把容器的veth/Macvlan网卡与连通互联网的物理网卡桥接。不过,这种方法需要修改物理接口,即移除IP地址并将其分配到桥接接口上。

  • 消除NAT的第二种方法是把主机变成全面的路由器,甚至是使用BGP的路由器。主机会将前缀路由到主机中的容器,每个容器会使用全球唯一的IP地址。尽管在IPv4地址空间已经耗尽的今天,给每个容器提供一个完全可路由的IPv4地址有些不太实际,但IPv6将使这种主机作为路由器的容器组网技术变得可能,事实上很多公有云都已经支持IPv6。以上这些都只是针对某个具体问题的,而非整体的端到端的方案,例如IP地址管理、跨主机通信、消除NAT、安全策略等。Docker最初并没有提供必要的此类能力,在开始的很长一段时间内,只支持使用Linux bridge+iptables的单机网络部署方式,这种方式下容器的可见性只存在于主机内部,这严重地限制了容器集群的规模及可用性!

  • Docker目前拥有两种网络解决方案

    • 一种是原生方式,即Docker官方支持且无须配置的开箱即用。
    • 允许第三方网络管理工具以插件方式替代Docker中内置的网络功能,接口标准便是CNM。
    • 所以docker + calico + k8s 使用的是CNM还是CNI??TODO
  • 对相对成熟的第三方容器解决方案进行分类,则大致可以分为隧道方案和路由方案。

  • 当前主流的容器网络虚拟化技术有Linux bridge、Macvlan、IPvlan、Open vSwitch、flannel、Weave、Calico等。而容器网络最基础的一环是为容器分配IP地址,主流的方案有本地存储+固定网段的host-local,DHCP,分布式存储+IPAM的flannel和SDN的Weave等。任何一个主流的容器组网方案无非就是网络虚拟机+IP地址分配,即Linux bridge、Macvlan、IPvlan、Open vSwitch、flannel、Weave、Calico等虚拟化技术和host-local、DHCP、flannel、Weave任取两样的排列组合。

  • calico方案是使用哪种IP地址分配方案???k8s pod ip是k8s分配的,使用calico只需要规划好node ip的分配。

  • overlay网络

    • 隧道网络也称为overlay网络,有时也被直译为覆盖网络。
    • overlay网络最大的优点是适用于几乎所有网络基础架构,它唯一的要求是主机之间的IP连接。但overlay网络的问题是随着节点规模的增长,复杂度也会随之增加,而且用到了封包,因此出了网络问题定位起来比较麻烦。
    • 典型的基于overlay的网络插件有:
      • Weave:源自Weaveworks,包括Weave Net、Weave Scope和Weave Flux。Weave Net是一种用于构建和部署Docker容器的网络工具;
      • Open vSwitch(OVS):基于VXLAN和GRE协议,但是性能方面损失比较严重;
      • flannel:源自CoreOS,支持自研的UDP封包及Linux内核的VXLAN协议。
    • Weave和flannel用到的封包技术特点类似,不过使用的是VXLAN。另外,Weave的思路是共享IP而非绑定。在传输层先找到目的地址,然后把包发到对端,节点之间互相通过协议共享信息。使用UDP进行封包的时候性能损失在50%以上,使用VXLAN也会有20%~30%的损耗。
  • 路由方案

    • 路由方案回过头来想一下,我们为什么要封包?其实它是改包,主要解决的问题是同一个问题,即在容器网络里,主机间不知道对方的目的地址,没有办法把IP包投递到正确的地方。传统的三层网络是用路由来互相访问的,不需要封包。至于路由规则怎么维护?传统的网络解决方案是利用BGP部署一个分布式的路由集群。 传统BGP分布式路由集群方案通过路由来实现,比较典型的网络插件有:
    • Calico:源自Tigera,基于BGP的路由方案,支持很细致的ACL控制,对混合云亲和度比较高;
    • Macvlan:从逻辑和Kernel层来看,是隔离性和性能最优的方案,基于二层隔离,需要二层路由器支持,大多数云服务商不支持,因此混合云上比较难以实现;
    • Metaswitch:容器内部配一个路由指向自己宿主机的地址,这是一个纯三层的网络不存在封包,因此性能接近原生网络。路由方案的另一个优点是出了问题也很容易排查。路由方案往往需要用户了解底层网络基础结构,因此使用和运维门槛较高。
    • Calico是一个纯三层网络方案。不同主机上的每个容器内部都配一个路由,指向自己所在的IP地址;每台服务器变成路由器,配置自己的路由规则,通过网卡直接到达目标容器,整个过程没有封包。
    • 那么,路由交换是不是很难呢?用传统的BGP技术就可以实现。这个协议在大规模应用下是一个很好的场景,而且BGP有一个自治域的概念。在这个场景下会有一个问题,路由之间的信息交换实际上基于TCP,每两个之间都有一个TCP连接,规模大了维护这些连接的开销会非常高。
    • Calico的设计灵感源自通过将整个互联网的可扩展IP网络原则压缩到数据中心级别。Calico在每一个计算节点,利用Linux Kernel实现高效的vRouter来负责数据转发,而每个vRouter通过BGP把自己节点上的工作负载的路由信息向整个Calico网络传播。小规模部署可以直接互联,大规模下可通过指定的BGP Route Reflector完成。保证最终所有的容器之间的数据流量都是通过IP路由的方式完成互联的。

容器网络组网类型

  • 关于容器网络对overlay和underlay的分类,常见的非主机网络(host network)的容器组网类型有L2 overlay、L3 overlay、L2 underlay和L3 underlay。
  1. overlay网络

    • overlay网络是在传统网络上虚拟出一个虚拟网络,承载的底层网络不再需要做任何适配。在容器的世界里,物理网络只承载主机网络通信,虚拟网络只承载容器网络通信。overlay网络的任何协议都要求在发送方对报文进行包头封装,接收方剥离包头。
    1. L2 overlay
      • 传统的二层网络的范围有限,L2 overlay网络是构建在底层物理网络上的L2网络,相较于传统的L2网络,L2 overlay网络是个“大二层”的概念,其中“大”的含义是可以跨越多个数据中心(即容器可以跨L3 underlay进行L2通信),而“二层”指的是通信双方在同一个逻辑的网段内,例如172.17.1.2/16和172.17.2.3/16。VXLAN就是L2 overlay网络的典型实现,其通过在UDP包中封装原始L2报文,实现了容器的跨主机通信。L2 overlay网络容器可在任意宿主机间迁移而不改变其IP地址的特性,使得构建在大二层overlay网络上的容器在动态迁移时具有很高的灵活性。
    2. L3 overlay
      • L3 overlay组网类似L2 overlay,但会在节点上增加一个网关。每个节点上的容器都在同一个子网内,可以直接进行二层通信。跨节点的容器间通信只能走L3,都会经过网关转发,性能相比于L2 overlay较弱。牺牲的性能获得了更高的灵活性,跨节点通信的容器可以存在于不同的网段中,例如192.168.1.0/24和172.17.16.0/24。flannel的UDP模式采用的就是L3 overlay模型。L3 overlay网络容器在主机间迁移时可能需要改变其IP地址。
  2. underlay网络

    • underlay网络一般理解为底层网络,传统的网络组网就是underlay类型,区别于上文提到的overlay网络。
    1. L2 underlay
      • L2 underlay网络就是链路层(L2)互通的底层网络。IPvlan L2模式和Macvlan属于L2 underlay类型的网络。
    2. L3 underlay
      • 在L3 underlay组网中,可以选择IPvlan的L3模式,该模式下IPvlan有点像路由器的功能,它在各个虚拟网络和主机网络之间进行不同网络报文的路由转发工作。只要父接口相同,即使虚拟机/容器不在同一个网络,也可以互相ping通对方,因为IPvlan会在中间做报文的转发工作。IPvlan的L3模式,flannel的host-gw模式和Calico的BGP组网方式都是L3 underlay类型的网络。

Kubernetes网络

  • Kubernetes网络包括网络模型、CNI、Service、Ingress、DNS等。在Kubernetes的网络模型中,每台服务器上的容器有自己独立的IP段,各个服务器之间的容器可以根据目标容器的IP地址进行访问。

  • 为了实现这一目标,重点解决以下两点:

    • 各台服务器上的容器IP段不能重叠,所以需要有某种IP段分配机制,为各台服务器分配独立的IP段;
    • 从某个Pod发出的流量到达其所在服务器时,服务器网络层应当具备根据目标IP地址,将流量转发到该IP所属IP段对应的目标服务器的能力。总结起来,实现Kubernetes的容器网络重点需要关注两方面:IP地址分配和路由。
  • Kubernetes网络策略

    • 与Kubernetes Ingress API类似,Kubernetes只提供了Network Policy的API定义,不负责具体实现。
    • 通常,Policy Controller是由Kubernetes网络插件提供的。支持Network Policy的网络插件有Calico、Cilium、Weave Net、Kube-router、Romana等。需要注意的是,flannel不在这个名单中。

IP地址分配

  • Kubernetes使用各种IP范围为节点、Pod和服务分配IP地址。
  • 系统会从集群的VPC网络为每个节点分配一个IP地址。该节点IP用于提供从控制组件(如Kube-proxy和Kubelet)到Kubernetes Master的连接;
  • 系统会为每个Pod分配一个地址块内的IP地址。用户可以选择在创建集群时通过–pod-cidr指定此范围;
  • 系统会从集群的VPC网络为每项服务分配一个IP地址(称为ClusterIP)。
    大部分情况下,该VPC与节点IP地址不在同一个网段,而且用户可以选择在创建集群时自定义VPC网络。

Pod出站流量

  • Kubernetes处理Pod的出站流量的方式主要分为以下三种:
  • Pod到Pod
    • 在Kubernetes集群中,每个Pod都有自己的IP地址,运行在Pod内的应用都可以使用标准的端口号,不用重新映射到不同的随机端口号。所有的Pod之间都可以保持三层网络的连通性,比如可以相互ping对方,相互发送TCP/UDP/SCTP数据包。CNI就是用来实现这些网络功能的标准接口。
  • Pod到Service
    • Pod的生命周期很短暂,但客户需要的是可靠的服务,因此Kubernetes引入了新的资源对象Service,其实它就是Pod前面的4层负载均衡器。Service总共有4种类型,其中最常用的类型是ClusterIP,这种类型的Service会自动分配一个仅集群内部可以访问的虚拟IP。Kubernetes通过Kube-proxy组件实现这些功能,每台计算节点上都运行一个Kubeproxy进程,通过复杂的iptables/IPVS规则在Pod和Service之间进行各种过滤和NAT。
  • Pod到集群外
    • 从Pod内部到集群外部的流量,Kubernetes会通过SNAT来处理。SNAT做的工作就是将数据包的源从Pod内部的IP:Port替换为宿主机的IP:Port。当数据包返回时,再将目的地址从宿主机的IP:Port替换为Pod内部的IP:Port,然后发送给Pod。当然,中间的整个过程对Pod来说是完全透明的,它们对地址转换不会有任何感知。

Kubernetes网络架构综述

  • 谈到Kubernetes的网络模型,就不能不提它著名的“单Pod单IP”模型,即每个Pod都有一个独立的IP,Pod内所有容器共享network namespace(同一个网络协议栈和IP)。
  • “单Pod单IP”网络模型为我们勾勒了一个Kubernetes扁平网络的蓝图,在这个网络世界里:容器是一等公民,容器之间直接通信,不需要额外的NAT,因此不存在源地址被伪装的情况;Node与容器网络直连,同样不需要额外的NAT。扁平化网络的优点在于:没有NAT带来的性能损耗,而且可追溯源地址,为后面的网络策略做铺垫,降低网络排错的难度等。
  • 总体而言,集群内访问Pod,会经过Service;集群外访问Pod,经过的是Ingress。Service和Ingress是Kubernetes专门为服务发现而抽象出来的相关概念。
  • Kubernetes Ingress提供了负载平衡器的典型特性:HTTP路由、黏性会话、SSL终止、SSL直通、TCP和UDP负载平衡等
  • 与CRI之于Kubernetes的runtime类似,Kubernetes使用CNI作为Pod网络配置的标准接口。需要注意的是,CNI并不支持Docker网络,也就是说,docker0网桥会被大部分CNI插件“视而不见”。
  • 当然也有例外,Weave就是一个会处理docker0的CNI插件。

  • 图中描绘了当用户在Kubernetes里创建了一个Pod后,CRI和CNI协同创建Pod所属容器,并为它们初始化网络协议栈的全过程。具体过程如下:
    1. 当用户在Kubernetes的Master里创建了一个Pod后,Kubelet观察到新Pod的创建,于是首先调用CRI(后面的runtime实现,比如dockershim、containerd等)创建Pod内的若干个容器。
    2. 在这些容器里,第一个被创建的pause容器是比较特殊的,这是Kubernetes系统“赠送”的容器,也称pause容器。里面运行着一个功能十分简单的C程序,具体逻辑是一启动就把自己永远阻塞在那里。一个永远阻塞而且没有实际业务逻辑的pause容器到底有什么用呢?用处很大。我们知道容器的隔离功能利用的是Linux内核的namespace机制,而只要是一个进程,不管这个进程是否处于运行状态(挂起亦可),它都能“占”用着一个namespace。因此,每个Pod内的第一个系统容器pause的作用就是占用一个Linux的network namespace。
    3. Pod内其他用户容器通过加入这个network namespace的方式共享同一个network namespace。用户容器和pause容器之间的关系有点类似于寄居蟹和海螺。因此,Container runtime创建Pod内的用户容器时,调用的都是同一个命令:docker run–net=none。意思是只创建一个network namespace,不初始化网络协议栈。如果这个时候通过nsenter方式进入容器,会看到里面只有一个本地回环设备lo。
    4. 容器的eth0是怎么创建出来的呢?答案是CNI。CNI主要负责容器的网络设备初始化工作。Kubelet目前支持两个网络驱动,分别是Kubenet和CNI。Kubenet是一个历史产物,即将废弃,因此本节不过多介绍。CNI有多个实现,官方自带的插件就有p2p、bridge等,这些插件负责初始化pause容器的网络设备,也就是给pause容器内的eth0分配IP等,到时候,Pod内其他容器就使用这个IP与其他容器或节点进行通信。Kubernetes主机内容器的默认组网方案是bridge。flannel、Calico这些第三方插件解决容器之间的跨机通信问题,典型的跨机通信解决方案有bridge和overlay等。

Kubernetes主机内组网模型

  • Kubernetes经典的主机内组网模型是veth pair+bridge的方式。
  • 当Kubernetes调度Pod在某个节点上运行时,它会在该节点的Linux内核中为Pod创建network namespace,供Pod内所有运行的容器使用。从容器的角度看,Pod是具有一个网络接口的物理机器,Pod中的所有容器都会看到此网络接口。因此,每个容器通过localhost就能访问同一个Pod内的其他容器。
  • Kubernetes使用veth pair将容器与主机的网络协议栈连接起来,从而使数据包可以进出Pod。容器放在主机根network namespace中veth pair的一端连接到Linux网桥,可让同一节点上的各Pod之间相互通信。

  • 如果Kubernetes集群发生节点升级、修改Pod声明式配置、更新容器镜像或节点不可用,那么Kubernetes就会删除并重新创建Pod。在大部分情况下,Pod创建会导致容器IP发生变化。也有一些CNI插件提供Pod固定IP的解决方案,例如Weave、Calico等。

  • 使用新建的bridge网桥(CNI bridge)代替docker0网桥(docker0也可以继续保留,常规容器还是用docker0,而需要互通的容器可以借助于这个工具给docker容器新建虚拟网卡并绑定IP桥接到bridge)
  • bridge和主机eth0之间是也是利用veth pair这个技术。

Kubernetes跨节点组网模型

  • Kubernetes典型的跨机通信解决方案有bridge、overlay等。
  • bridge网络本身不解决容器的跨机通信问题,需要显式地书写主机路由表,映射目标容器网段和主机IP的关系,集群内如果有N个主机,需要N-1条路由表项。
  • 至于overlay网络,它是构建在物理网络之上的一个虚拟网络,其中VXLAN是主流的overlay标准。VXLAN就是用UDP包头封装二层帧,即所谓的MAC in UDP。和bridge网络类似,Pod同样接在Linux网桥上,目的地址落在本机Pod网段的网络包同样发给Linux网桥cni0。不同的是,目的Pod在其他节点上的路由表规则。
  • bridge和overlay是Kubernetes最早采用的跨机通信方案,但随着集成Weave和Calico等越来越多的CNI插件,Kubernetes也支持虚拟路由等方式。

Pod的hosts文件

  • 与宿主机一样,容器也有/etc/hosts文件,用来记录容器的hostname和IP地址的映射关系。通过向Pod的/etc/hosts文件中添加条目,可以在Pod级别覆盖对hostname的解析。
  • 一个Pod内如果有多个容器,修改任意一个容器的hostname都会影响其他容器,因为Pod共享UTS namespace。

打通CNI与Kubernetes:Kubernetes网络驱动

  • Kubernetes支持两种网络驱动,分别是Kubenet和CNI,其中:
    ·CNI plugins:遵守appc/CNI规范,允许自由接入多个符合CNI标准的网络插件;
    ·Kubenet plugins:基于cbr0的一个单机容器网络方案,同时使用CNI的bridge和host-local插件实现一些功能。
  • CNI是容器网络的标准化,试图通过JSON描述一个容器网络配置。CNI是Kubernetes与底层网络插件之间的一个抽象层,为Kubernetes屏蔽了底层网络实现的复杂度,同时解耦了Kubernetes的具体网络插件实现。
  • CNI主要有两类接口:分别是在创建容器时调用的配置网络接口:[插图]和删除容器时调用的清理网络接口
  • 不论是配置网络接口还是清理网络接口,都有两个入参,分别是网络配置和runtime配置。网络配置很好理解,runtime配置则主要是容器运行时传入的网络namespace信息。
  • 可以简单理解就是cni就是一个开发网络插件的规范,这个规范规定了这个插件要实现的子命令(add delete)。所以如果你自己开发了一个k8网络同时实现了针对自己网络的cni插件,就是一个可执行命令,那么你的网络方案就可以被kubernetes使用。——评论

Kubernetes网络策略

  • 网络策略就是基于Pod源IP(所以Kubernetes网络不能随随便便做SNAT)的访问控制列表,限制的是Pod之间的访问。通过定义网络策略,用户可以根据标签、IP范围和端口号的任意组合限制Pod的入站/出站流量。网络策略作为Pod网络隔离的一层抽象,用白名单实现了一个访问控制列表(ACL),从Label Selector、namespace selector、端口、CIDR这4个维度限制Pod的流量进出。
  • Egress表示出站流量,即Pod作为客户端访问外部服务,Pod地址作为源地址。策略可以定义目的地址和目的端口,可以根据ports和to定义规则。ports字段用来指定目的端口和协议。to(目的地址)分为IP地址段、Pod selector和Kubernetes namespace selector;
  • Ingress表示入站流量,Pod地址和服务作为服务端(目的地址),提供外部访问。与Egress类似,策略可以定义源地址和端口,可以根据ports和from定义规则。ports字段同样用来指定目的端口和协议。from(源地址)分为IP地址段、Pod selector和Kubernetes namespace selector

Kubernetes网络故障定位指南

  • Kubernetes网络利用Linux内核Netfilter模块设置低级别的集群IP负载均衡,除了iptables和IPVS(前面已经详细解析过,这里不再赘述),还需要用到两个关键的模块:IP转发(IP forward)和桥接。

  • IP转发是一种内核态设置,允许将一个接口的流量转发到另一个接口,该配置是Linux内核将流量从容器路由到外部所必需的。

  • 如果一个容器请求外部的服务,由于容器IP是不可路由的,则远程服务器不知道应该把响应发到哪里。但事实上,只要每个主机对容器到外部的连接做一次SNAT或是Masquerade就能实现。

  • 我们的Docker主机能够和数据中心的其他机器通信,它们有可路由的IP。当一个容器尝试访问一个外部服务时,运行容器的主机将网络包中的容器IP用它本身的IP替换,即Masquerade(SNAT的一种)。对于外部服务,看起来像是和主机建立了连接。当响应返回到主机的时候,它进行一个逆转换(把网络包中的主机IP替换成容器IP)。对于容器,这个操作完全是透明的,它不知道发生了这样的一个转换。Kubernetes NodePort的实现默认就开启了Kube-proxy的–masq-all=true选项。

  • SNAT导致Linux内核丢包的原因在于其conntrack的实现。SNAT代码在POSTROUTING链上被调用两次。

  • 解决方法需要在masquerade规则中设置flag NF_NAT_RANGE_PROTO_RANDOM_FULLY。

  • 通过使用打了补丁的flannel和Kube-proxy,能够显著降低conntrack表的插入错误,使整个集群中的丢包错误的数目从每几秒一次下降到每几个小时一次。需要注意的是,iptabels的–ramdom-fully选项只能缓解集群SNAT带来的这个问题,而并不能根治。因此,并不推荐在生产环境使用NodePort。

  • pod 访问集群外的pod和访问集群外的服务ip转化过程区别,是否NAT?TODO

    • pod 访问集群外的pod不需要NAT,访问集群外的服务取决于是否打通网络和如何配置。

Kubernetes DNS架构演进之路

  • Kubernetes DNS服务目前有两个实现,分别是Kube-dns和CoreDNS。
  • 无论是Kube-dns还是CoreDNS,基本原理都是利用watch Kubernetes的Service和Pod,生成DNS记录,然后通过重新配置Kubelet的DNS选项让新启动的Pod使用Kube-dns或CoreDNS提供的Kubernetes集群内域名解析服务。

Kubernetes网络插件生态

  • Docker自己的网络方案比较简单,就是每个宿主机上会跑一个非常纯粹的Linux bridge,这个bridge可以认为是一个二层的交换机,但它的能力有限,只能做一些简单的学习和转发。出网桥的流量会经过iptables,经过NAT,最后通过路由转发在宿主之间进行通信。当真正用Docker原生的网络模型部署一个比较复杂的业务时,会遇到诸如:容器重启之后IP就变了;每台宿主机会分配固定的网段,因此同一个容器迁到不同宿主机时,除了IP发生变化,网段也会变化,随之而来的网络策略都需要调整等问题。另外,NAT的存在会造成两端在通信时看到对方的地址是不真实的,而且NAT本身也有性能损耗。这些问题都对Docker自身网络方案的应用造成了障碍。
  • 一些最常见的术语包括:
    • 第2层网络:OSI(Open Systems Interconnections,开放系统互连)网络模型的“数据链路”层。第2层网络会处理网络上两个相邻节点之间的帧传递。第2层网络的一个典型示例是以太网。
    • 第3层网络:OSI网络模型的“网络”层。第3层网络的主要关注点,是在第2层连接之上的主机之间路由数据包。IPv4、IPv6和ICMP是第3层网络协议的示例。
    • VXLAN:即虚拟可扩展的LAN。首先,VXLAN用于通过在UDP数据包中封装第2层以太网帧帮助实现大型云部署。VXLAN虚拟化与VLAN类似,但提供更大的灵活性和功能(VLAN仅限于4096个网络ID)。VXLAN是一种overlay协议,可在现有网络之上运行。
    • overlay网络:是建立在现有网络之上的虚拟逻辑网络。overlay网络通常用于在现有网络之上提供有用的抽象,并分离和保护不同的逻辑网络。
    • 封装:是指在附加层中封装网络数据包以提供其他上下文和信息的过程。
    • 在overlay网络中,封装被用于从虚拟网络转换到底层地址空间,从而能路由到不同的位置(数据包可以被解封装,并继续到其目的地)。
    • 网状网络:是指每个节点连接到许多其他节点以协作路由,并实现更大连接的网络。
    • 网状网络(mesh network)允许通过多个路径进行路由,从而提供更可靠的网络。网状网格的缺点是每个附加节点都会增加大量开销。
    • BGP:代表“边界网关协议”,用于管理边缘路由器之间数据包的路由方式。BGP通过考虑可用路径、路由规则和特定网络策略等因素,将数据包从一个网络发送到另一个网络。BGP有时被用作容器网络的路由机制,但不会用在overlay网络中。

CNI标准的胜出:从此江湖没有CNM

  • CNI即容器网络接口(Container Network Interface)。Kubernetes采用CNI而非CNM(容器网络模型),这背后有很长的一段故事,核心的原因就是CNI对开发者的约束更少,更开放,不依赖于Docker工具,而CNM对Docker有非常强的依赖,无法作为通用的容器网络标准。在CNI标准中,网络插件是独立的可执行文件,被上层的容器管理平台调用。网络插件只有两件事情要做:把容器加入网络或把容器从网络中删除。调用插件的配置通过两种方式传递:环境变量和标准输入。

  • CNI很简单,只需要:

    • 1个配置文件,配置文件描述插件的版本、名称、描述等基本信息;
    • 1个可执行文件,可执行文件就是CNI插件本身会在容器需要建立网络和需要销毁容器时被调用;
    • 读取6个环境变量,获得需要执行的操作、目标网络Namespace、容器的网卡必要信息;
    • 接受1个命令行参数,同样用于获得需要执行的操作、目标网络Namespace、容器的网卡必要信息;
    • 实现2个操作(ADD/DEL)。
  • Kubernetes使用CNI网络插件的工作流程

    • Kubernetes调用CRI创建pause容器,生成对应的network namespace;
    • 调用网络driver(因为配置的是CNI,所以会调用CNI的相关代码);
    • CNI driver根据配置调用具体的CNI插件;
    • CNI插件给pause容器配置正确的网络,Pod中的其他容器都是用pause容器的网络栈。
  • CNI的初衷是创建一个框架,用于在配置或销毁容器时动态配置适当的网络配置和资源。CNI规范概括了用于配制网络的插件接口,这个接口可以让容器运行时与插件进行协调。CNI插件负责为每个容器分配IP地址,为容器接口配置和管理IP地址,以及多主机连接相关的功能。容器运行时(runtime)会调用网络插件,从而在容器启动时分配IP地址并配置网络,并在删除容器时再次调用它以清理这些资源。容器运行时决定了容器应该加入哪个网络及它需要调用哪个插件。然后,插件会将网络接口添加到容器网络命名空间中(例如,作为一个veth pair的一端)。接着,它会在主机上进行相关配置(例如,将veth的其他部分连接到网桥上)。最后,CNI会通过调用单独的IPAM(IP地址管理)插件分配IP地址并设置路由。在Kubernetes中,Kubelet可以在适当的时间调用它找到的插件,为通过Kubelet启动的Pod进行自动的网络配置。

  • CNI的最大价值在于提供了一致的容器网络操作界面,不论是什么网络插件都使用一致的API,提高了网络配置的自动化程度和一致性的体验。

  • Kubernetes是一个支持多容器的运行环境,而Docker只是其中一个容器而已。每一个运行环境都会配置网络环境,所以当人们问“Kubernetes会支持CNM吗?”时,他们真正的意思是“Kubernetes是否在Docker运行时下支持CNM?”。当然,我们希望同一个网络插件支持所有的运行环境,但这并不是一个绝对目标。

  • Kubernetes认为CNI更适合快速开发和迭代。早期的实验尝试证明,Kubernetes可以利用CNI插件替代几乎所有Kubelet中硬编码的网络逻辑。

  • 考虑到Kubernetes和Docker项目的独立性等种种原因,促使Kubernetes选择CNI作为网络模型。这将带来诸如:docker inspect命令显示不了Pod的IP地址,直接被Docker启动的容器可能无法和被Kubernetes启动的容器通信等问题。

  • 但Kubernetes必须做一个权衡:选择CNI,使Kubernetes网络配置更简单、灵活并且不需要额外的配置(例如,配置Docker使用Kubernetes或其他网络插件的网桥)。

SDN

  • 软件定义网络(Software Defined Network,SDN)

  • 有了虚拟化网络设备后,下一步就是要使用这些设备组成网络,容器分布在不同的物理主机上,每一台物理主机都有物理网络相互联通,然而这种网络的物理拓扑结构是相对固定的,很难跟上云原生时代的分布式系统的逻辑拓扑结构变动频率,譬如服务的扩缩、断路、限流,等等,都可能要求网络跟随做出相应的变化。正因如此,软件定义网络(Software Defined Network,SDN)的需求在云计算和分布式时代变得前所未有地迫切。

  • SDN的核心思路是在物理的网络之上再构造一层虚拟化的网络,将控制平面和数据平面分离开来,实现流量的灵活控制,为核心网络及应用的创新提供良好的平台。SDN里位于下层的物理网络被称为Underlay,它着重解决网络的连通性与可管理性,位于上层的逻辑网络被称为Overlay,它着重为应用提供与软件需求相符的传输服务和网络拓扑。

  • VLAN的全称是“虚拟局域网”(Virtual Local Area Network)

  • VXLAN,这是三层虚拟化网络(Network Virtualization over Layer 3,NVO3)的标准技术规范之一,是一种典型的Overlay网络。

  • 副本网卡:MACVLAN

Kubernetes的网络实现

  • Kubernetes网络的设计主要致力于解决以下问题。
    1. 容器到容器之间的直接通信。
    2. 抽象的Pod到Pod之间的通信。
    3. Pod到Service之间的通信。
    4. 集群外部与内部组件之间的通信。

容器到容器之间的直接通信

  • 同一个Pod内的容器(Pod内的容器是不会跨宿主机的)共享同一个网络命名空间,共享同一个Linux协议栈。所以对于网络的各类操作,就和它们在同一台机器上一样,它们甚至可以用localhost地址访问彼此的端口。

Pod到Pod之间的通信

  • 每一个Pod都有一个真实的全局IP地址,同一个Node内的不同Pod之间可以直接采用对方Pod的IP地址通信,而且不需要采用其他发现机制,例如DNS、Consul或者etcd。Pod容器既有可能在同一个Node上运行,也有可能在不同的Node上运行,所以通信也分为两类:同一个Node内Pod之间的通信和不同Node上Pod之间的通信。
  • 同一个Node内Pod之间的通信
    • 同一个Node内两个Pod之间的关系可以看出,Pod1和Pod2都是通过Veth连接到同一个docker0网桥上的,它们的IP地址IP1、IP2都是从docker0的网段上动态获取的,它们和网桥本身的IP3是同一个网段的。另外,在Pod1、Pod2的Linux协议栈上,默认路由都是docker0的地址,也就是说所有非本地地址的网络数据,都会被默认发送到docker0网桥上,由docker0网桥直接中转。综上所述,由于它们都关联在同一个docker0网桥上,地址段相同,所以它们之间是能直接通信的。
  • 不同Node上Pod之间的通信
    • Pod的地址是与docker0在同一个网段的,我们知道docker0网段与宿主机网卡是两个完全不同的IP网段,并且不同Node之间的通信只能通过宿主机的物理网卡进行,因此要想实现不同Node上Pod容器之间的通信,就必须想办法通过主机的这个IP地址进行寻址和通信。
    • 另一方面,这些动态分配且藏在docker0之后的所谓“私有”IP地址也是可以找到的。Kubernetes会记录所有正在运行的Pod的IP分配信息,并将这些信息保存在etcd中(作为Service的Endpoint)。这些私有IP信息对于Pod到Pod的通信也是十分重要的,因为我们的网络模型要求Pod到Pod使用私有IP进行通信。所以首先要知道这些IP是什么。
    • 之前提到,Kubernetes的网络对Pod的地址是平面的和直达的,所以这些Pod的IP规划也很重要,不能有冲突。只要没有冲突,我们就可以想办法在整个Kubernetes的集群中找到它。
    • 支持不同Node上Pod之间的通信,就要满足两个条件:
      1. 在整个Kubernetes集群中对Pod的IP分配进行规划,不能有冲突;
      2. 找到一种办法,将Pod的IP和所在Node的IP关联起来,通过这个关联让Pod可以互相访问。
    • 根据条件1的要求,我们需要在部署Kubernetes时对docker0的IP地址进行规划,保证每个Node上的docker0地址都没有冲突。我们可以在规划后手工配置到每个Node上,或者做一个分配规则,由安装的程序自己去分配占用。例如,Kubernetes的网络增强开源软件Flannel就能够管理资源池的分配。
    • 根据条件2的要求,Pod中的数据在发出时,需要有一个机制能够知道对方Pod的IP地址挂在哪个具体的Node上。也就是说先要找到Node对应宿主机的IP地址,将数据发送到这个宿主机的网卡,然后在宿主机上将相应的数据转发到具体的docker0上。一旦数据到达宿主机Node,则那个Node内部的docker0便知道如何将数据发送到Pod。
    • 在实际的私有云环境中,除了需要部署Kubernetes和Docker,还需要额外的网络配置,甚至通过一些软件来实现Kubernetes对网络的要求。做到这些后,Pod和Pod之间才能无差别地进行透明通信。为了达到这个目的,开源界有不少应用增强了Kubernetes、Docker的网络,几个常用的组件及其组网原理。

Pod和Service网络实战

  • Kubernetes的网络模型要求每个Node上的容器都可以相互访问。默认的Docker网络模型提供了一个IP地址段是172.17.0.0/16的docker0网桥。每个容器都会在这个子网内获得IP地址,并且将docker0网桥的IP地址(172.17.42.1)作为其默认网关。需要注意的是,Docker宿主机外面的网络不需要知道任何关于这个172.17.0.0/16的信息或者知道如何连接到其内部,因为Docker的宿主机针对容器发出的数据,在物理网卡地址后面都做了IP伪装MASQUERADE(隐含NAT)。也就是说,在网络上看到的任何容器数据流都来源于那台Docker节点的物理IP地址。这里所说的网络都指连接这些主机的物理网络。这个模型便于使用,但是并不完美,需要依赖端口映射的机制。在Kubernetes的网络模型中,每台主机上的docker0网桥都是可以被路由到的。也就是说,在部署了一个Pod时,在同一个集群内,各主机都可以访问其他主机上的Pod IP,并不需要在主机上做端口映射。综上所述,我们可以在网络层将Kubernetes的节点看作一个路由器。如果将实验环境改画成一个网络图,那么它看起来如图7.12所示。
  • 每一个新部署的容器都将使用这个Node(docker0的网桥IP)作为它的默认网关。而这些Node(类似路由器)都有其他docker0的路由信息,这样它们就能够相互连通了。
  • 首先,一个Pod内的所有容器都需要共用同一个IP地址,这就意味着一定要使用网络的容器映射模式(container模式)。然而,为什么不能只启动第1个Pod中的容器,而将第2个Pod中的容器关联到第1个容器呢?我们认为Kubernetes是从两方面来考虑这个问题的:首先,如果在Pod内有多个容器的话,则可能很难连接这些容器;其次,后面的容器还要依赖第1个被关联的容器,如果第2个容器关联到第1个容器,且第1个容器死掉的话,第2个容器也将死掉。启动一个基础容器(pause容器),然后将Pod内的所有容器都连接到它上面会更容易一些。
  • Kubernetes的kube-proxy作为一个全功能的代理服务器管理了两个独立的TCP连接:一个是从容器到kube-proxy:另一个是从kube-proxy到负载均衡的目标Pod。总结:跨node的pod到pod请求,经过自身node的kube-proxy(因为要通过etcd定位目标pod属于到哪个node),不经过目标node的kube-proxy,因为连接是直接通过目标node的eth0的(是否NAT方式,看k8s具体使用的组网实现)。

容器网络模型

  • 随着容器技术在企业生产系统中的逐步落地,用户对容器云的网络特性要求也越来越高。跨主机容器间的网络互通已经成为基本要求,更高的要求包括容器固定IP地址、一个容器多个IP地址、多个子网隔离、ACL控制策略、与SDN集成等。目前主流的容器网络模型主要有Docker公司提出的Container Network Model(CNM)模型和CoreOS公司提出的Container Network Interface(CNI)模型。
  • CNM模型:CNM模型主要通过Network Sandbox、Endpoint和Network这3个组件进行实现
  • CNI 模型:CNI提供了一种应用容器的插件化网络解决方案,定义对容器网络进行操作和配置的规范,通过插件的形式对CNI接口进行实现。CNI是由rkt Networking Proposal发展而来的,试图提供一种普适的容器网络解决方案。CNI仅关注在创建容器时分配网络资源,和在销毁容器时删除网络资源,这使得CNI规范非常轻巧、易于实现,得到了广泛的支持。
    • 在CNI模型中只涉及两个概念:容器和网络。
      1. 容器(Container):是拥有独立Linux网络命名空间的环境,例如使用Docker或rkt创建的容器。关键之处是容器需要拥有自己的Linux网络命名空间,这是加入网络的必要条件。
      2. 网络(Network):表示可以互连的一组实体,这些实体拥有各自独立、唯一的IP地址,可以是容器、物理机或者其他网络设备(比如路由器)等。对容器网络的设置和操作都通过插件(Plugin)进行具体实现,CNI插件包括两种类型:CNI Plugin和IPAM(IP Address Management)Plugin。CNI Plugin负责为容器配置网络资源,IPAM Plugin负责对容器的IP地址进行分配和管理。IPAM Plugin作为CNI Plugin的一部分,与CNI Plugin一起工作。
  • CNI和CNM并非是完全不可调和的两个模型,二者是可以进行转化的。
  • CNM阵营支持CNM标准的网络插件有:
    • Docker Swarm overlay;Macvlan&IP network drivers;Calico;Contiv。
    • Docker Libnetwork的优势就是Docker原生与Docker容器生命周期结合紧密,缺点是与Docker耦合度过高。
  • CNI阵营支持CNI标准的网络插件:
    • Weave;Macvlan;flannel;Calico;Contiv;Mesos CNI。
    • CNI的优势是兼容其他容器技术(例如rkt)及上层编排系统(Kubernetes&Mesos),而且社区活跃势头迅猛,再加上Kubernetes主推,迅速成为容器网络的事实标准。

Kubernetes网络策略

  • Network Policy的主要功能是对Pod间的网络通信进行限制和准入控制,设置方式为将Pod的Label作为查询条件,设置允许访问或禁止访问的客户端Pod列。策略控制器由第三方网络组件提供,目前Calico、Cilium、Kube-router、Romana、Weave Net等开源项目均支持网络策略的实现。
  • Network Policy的工作原理如图7.19所示,policy controller需要实现一个API Listener,监听用户设置的NetworkPolicy定义,并将网络访问规则通过各Node的Agent进行实际设置(Agent则需要通过CNI网络插件实现)。
  • egress:定义目标Pod允许访问的“出站”白名单规则,目标Pod仅允许访问满足to条件的服务端IP范围和ports定义的端口号。
  • Namespace级别还可以设置一些默认的全局网络策略,以方便管理员对整个Namespace进行统一的网络策略设置。

开源的网络组件

  • Kubernetes的网络模型假定了所有Pod都在一个可以直接连通的扁平网络空间中。这在GCE里面是现成的网络模型,Kubernetes假定这个网络已经存在。而在私有云里搭建Kubernetes集群,就不能假定这种网络已经存在了。我们需要自己实现这个网络假设,将不同节点上的Docker容器之间的互相访问先打通,然后运行Kubernetes。目前已经有多个开源组件支持容器网络模型。有几个常见的网络组件及其安装配置方法,包括Flannel、Open vSwitch、直接路由和Calico等。

Flannel

  • Flannel之所以可以搭建Kubernetes依赖的底层网络,是因为它能实现以下两点。

    1. 它能协助Kubernetes,给每一个Node上的Docker容器都分配互相不冲突的IP地址。
    2. 它能在这些IP地址之间建立一个覆盖网络(Overlay Network),通过这个覆盖网络,将数据包原封不动地传递到目标容器内。
  • Flannel之间的底层通信协议的可选技术包括UDP、VxLan、AWS VPC等多种方式。通过源flanneld封包、目标flanneld解包,最终docker0收到的就是原始的数据,对容器应用来说是透明的,感觉不到中间Flannel的存在。

  • 根据不同的封包方式,flannel提供了UDP和VXLAN两种传输方法。

  • 事实上,flannel的Gateway模式的性能甚至要比Calico好。然而,由于flannel只能修改各个主机的路由表,一旦主机之间隔了其他路由设备,比如三层路由器,这个包就会在路由设备上被丢掉。这样一来,Host-Gateway的模式就只能用于二层直接可达的网络,由于广播风暴的问题,这种网络通常是比较小规模的。

  • flannel的底层实现实质上是一种overlay网络(除了Host-Gateway模式),即把某一协议的数据包封装在另一种网络协议中进行路由转发。

  • 总的来说,flannel是大多数用户的不错选择。从管理角度来看,它提供了一个简单的网络模型,用户只需要一些基础知识,就可以设置适合大多数用例的环境。与其他方案相比,flannel相对容易安装和配置,许多常见的Kubernetes集群部署工具和许多Kubernetes发行版都可以默认安装flannel。

  • Open vSwitch是一个开源的虚拟交换机软件,有点儿像Linux中的bridge,但是功能要复杂得多。Open vSwitch的网桥可以直接建立多种通信通道(隧道)

  • 无论是OVS还是Flannel,通过覆盖网络提供的Pod到Pod通信都会引入一些额外的通信开销,如果是对网络依赖特别重的应用,则需要评估对业务的影响。

Calico

  • Calico是一个基于BGP的纯三层的网络方案,与OpenStack、Kubernetes、AWS、GCE等云平台都能够良好地集成。Calico在每个计算节点都利用Linux Kernel实现了一个高效的vRouter来负责数据转发。每个vRouter都通过BGP1协议把在本节点上运行的容器的路由信息向整个Calico网络广播,并自动设置到达其他节点的路由转发规则。Calico保证所有容器之间的数据流量都是通过IP路由的方式完成互联互通的。Calico节点组网时可以直接利用数据中心的网络结构(L2或者L3),不需要额外的NAT、隧道或者Overlay Network,没有额外的封包解包,能够节约CPU运算,提高网络效率,如图7.24所示。 Calico不使用额外的封包解包Calico在小规模集群中可以直接互联,在大规模集群中可以通过额外的BGP route reflector来完成。

  • 此外,Calico基于iptables还提供了丰富的网络策略,实现了Kubernetes的Network Policy策略,提供容器间网络可达性限制的功能。

  • Calico的主要组件如下。

    1. Felix:Calico Agent,运行在每个Node上,负责为容器设置网络资源(IP地址、路由规则、iptables规则等),保证跨主机容器网络互通。
    2. etcd:Calico使用的后端存储。
    3. BGP Client:负责把Felix在各Node上设置的路由信息通过BGP协议广播到Calico网络。
    4. Route Reflector:通过一个或者多个BGP Route Reflector来完成大规模集群的分级路由分发。
    5. CalicoCtl:Calico命令行管理工具。
  • IP Pool可以使用两种模式:BGP或IPIP。

  • Calico的设计比较新颖,flannel的host-gw模式之所以不能跨二层网络,是因为它只能修改主机的路由,Calico把改路由表的做法换成了标准的BGP路由协议。相当于在每个节点上模拟出一个额外的路由器,由于采用的是标准协议,Calico模拟路由器的路由表信息可以被传播到网络的其他路由设备中,这样就实现了在三层网络上的高速跨节点网络。

  • 现实中的网络并不总是支持BGP路由,因此Calico也设计了一种ipip模式,使用overlay的方式传输数据。ipip的包头非常小,而且是内置在内核中的,因此它的速度理论上要比VXLAN快,但是安全性更差。

  • 虽然flannel被公认为是最简单的选择,但Calico以其性能、灵活性闻名于Kubernetes生态系统。Calico的功能更全面,正如Calico的slogan提到的:为容器和虚拟机工作负载提供一个安全的网络连接。

  • Calico以其丰富的网络功能著称,不仅提供容器的网络解决方案,还可以用在虚拟机网络上。除了网络连接,网络策略是Calico最受追捧的功能之一。使用Calico的策略语言,可以实现对容器、虚拟机工作负载和裸机主机各节点之间网络通信进行细粒度和动态的安全规则控制。Calico基于iptables实现了Kubernetes的网络策略,通过在各个节点上应用ACL(访问控制列表)提供工作负载的多租户隔离、安全组及其他可达性限制等功能。此外,Calico还可以与服务网格Istio集成,以便在服务网格层和网络基础架构层中解释和实施集群内工作负载的网络策略。

  • Calico还支持容器漂移。因为Calico配置的三层网络使用BGP路由协议在主机之间路由数据包。BGP路由机制可以本地引导数据包,这意味着无须overlay网络中额外的封包解包操作。由于免去了额外包头(大部分情况下依赖宿主机IP地址)封装数据包,容器在不同主机之间迁移没有网络的限制。

  • Calico在每一个计算节点利用Linux内核的一些能力实现了一个高效的vRouter负责数据转发,而每个vRouter通过BGP把自己运行的工作负载的路由信息向整个Calico网络传播。

  • BGP Route Reflector(BIRD)简单的BGP可能成为较大规模部署的性能瓶颈,因为它要求每个BGP客户端连接到网状拓扑中的每一个其他BGP客户端。随着集群规模的增大,一些设备的路由表甚至会被撑满。

  • 因此,在较大规模的部署中,Calico建议使用BGP Route Reflector(路由器反射器)。互联网中通常使用BGP Route Reflector充当BGP客户端连接的中心点,从而避免与互联网中的每个BGP客户端进行通信。Calico使用BGP Route Reflector是为了减少给定一个BGP客户端与集群其他BGP客户端的连接。用户也可以同时部署多个BGP Route Reflector服务实现高可用。Route Reflector仅仅是协助管理BGP网络,并没有工作负载的数据包经过它们。

  • 然而在需要使用overlay网络的环境中,Calico也提供了IP-in-IP(简称ipip)的隧道技术

  • 为Pod分配固定IP很多传统的应用程序,在上容器云时都会有使用固定IP地址的需求。虽然这种要求并不符合Kubernetes对网络的基本假定,但Calico IPAM却支持为Pod分配固定IP。

  • 为什么Calico网络选择BGP

    • 我们都知道Calico使用了BGP,那么为什么Calico要选择BGP呢?为什么选择BGP而不是一个IGP协议(如OSPF或者IS-IS)?
    • 要弄清楚原因,我们需要先明确目前BGP和IGP是如何在一个大规模网络中工作的。任何网络,尤其是大型网络,都需要处理两个不同的路由问题:
      1. 发现网络中路由器之间的拓扑结构。
      2. 发现网络中正在工作的节点,以及可到达该网络的外部连接。
    • 网络中的端点(Endpoint)没有运行路由协议的能力,而路由器(Router)可以。同一个网络内的两个网络之间通过路由器连接。IGP需要执行大量复杂计算,才能让每台设备在同一时刻都能得到对所处网络拓扑的相同认知。这其实就限制了IGP所能运行的规模。在一个IGP的单一视图内应该只包含几十个(极端情况下可能是小几百个)路由器,以满足这些规模和性能的需求。虽然存在一些可以突破规模限制的技术[比如在OSPF中使用area(区域)或者在IS-IS中使用Level(层),两者都可以认为是将网络切割成若干个单一视图],但这些技术也带来了其他架构上的限制(超出本书范畴这里不再展开)。IGP也被限制在它们所能通告的最大Endpoints数量上,这个数量浮动范围比较大,但是上限也就在几千到1万多。当IGP中的路由数量超过5000或6000条时,许多大型网络的管理人员都会感到紧张。那么BGP呢?截至2015年1月,在公共互联网上宣告的路由超过526000条,一些网络中甚至拥有超过100万的节点。为了解决大规模网络中的路由可扩展性问题,BGP被开发出来。BGP可以在一个网络中扩容到几百台路由器的规模,而如果使用BGP Route Reflection这一数量更是可以到达数万台。如果需要的话,BGP可以宣告数百万条路由信息,并通过非常灵活的策略加以管理。因此,我们就能理解为什么Calico使用BGP宣告网络端点的路由了。一言以蔽之,就是提高路由规则的可扩展性以满足大规模组网的需求。在Calico设计的网络中,可能会出现几万台路由器,以及潜在的几百万条路由信息或者Endpoints,这种数量级是与IGP的设计不匹配的,但是BGP可以,特别是当我们使用BGP Route Reflection扩展路由器数量的时候。Calico的核心设计思想是:使用通用的互联网工具和技术实现大规模网络互连结构。产生这种设计思想的主要原因是互联网行业在运营真正大型网络上已经积累了几十年的经验,使用的工具和技术(例如BGP)也已经过长时间的磨炼,如果把这些工作全部丢进垃圾桶,重新造轮子就不够明智了。对于那些数据终端规模已经接近互联网规模的公有云环境,一个云上可用区就可以轻松承载数千到数万个服务器,并运行着几万甚至小几十万个虚拟机(在Calico中称为Endpoints)。如果这些虚拟机中再运行容器,则Endpoints的数量可能还会增加一个或两个数量级。因此,从规模角度,我们应该尽量复用互联网运营商的成功经验。总结Calico网络使用BGP的原因有:(1)BGP是一种简单的路由协议。(2)拥有当前行业的最佳实践。(3)唯一能够支撑Calico网络规模的协议。
  • Calico是一个比较有意思的虚拟网络解决方案,完全利用路由规则实现动态组网,通过BGP通告路由。由于Calico利用宿主机协议栈的三层确保容器之间跨主机的连通性,报文的流向完全通过路由规则控制,没有overlay,没有NAT,直接经过宿主机协议栈处理,因此我们称Calico是一个纯三层的组网方案,转发效率也较高。Calico的核心设计思想就是Router,它把每个操作系统的协议栈看成一个路由器,然后把所有的容器看成连在这个路由器上的网络终端,在路由器之间运行标准的BGP,并让节点自己学习这个网络拓扑该如何转发。然而,当网络端点数量足够大时,自我学习和发现拓扑的收敛过程非常耗费资源和时间。Calico的缺点是网络规模会受到BGP网络规模的限制。Calico路由的数目与容器数目相同,极易超过三层交换、路由器或节点的处理能力,从而限制了整个网络的扩张,因此Calico网络的瓶颈在于路由信息的容量。Calico会在每个节点上设置大量的iptables规则和路由规则,这将带来极大的运维和故障排障难度。Calico的原理决定了它不可能支持VPC,容器只能从Calico设置的网段中获取IP。Calico目前的实现没有流量控制的功能,会出现少数容器抢占节点多数带宽的情况。当然,我们可以结合CNI的bandwidth插件实现流量整形。Calico的应用场景主要在IDC内部,Calico官方推荐将其部署在大二层网络上,这样所有路由器之间是互通的。所谓大二层就是没有任何三层的网关,所有的机器、宿主机、物理机在二层是可达的。大二层主要的问题是弹性伸缩的问题。频繁开关机的时候,容器启停虽然不影响交换机,但容易产生广播风暴。事实上,在很多有经验的网络工程师眼里,大二层存在单一故障问题,也就是说,任何一个都会有一定的硬件风险让整个大二层瘫痪。因此,实际场景经常会把集群划分成多个网段,对外是三层网络的结构。如图5-18所示,从架构上看,Calico在每个节点上会运行两个主要的程序,一个是Felix,另一个是BIRD。Felix会监听etcd的事件并负责配置节点上容器的网络协议栈和主机上的iptables规则及路由表项。BIRD会从内核里获取IP的路由发生了变化的信息,并告知Route Reflector。Route Reflector是一个路由程序,它会通过标准BGP的路由协议扩散到其他宿主机上,让集群的其他容器都知道这个IP。 Calico网络拓扑参考资料:https://www.lijiaocn.com/%E9%A1%B9%E7%9B%AE/2017/04/11/calico-usage.html.

  • Calico 不足:

    • BGP 支持问题:需要网路设备支持 BGP 协议,否则需要追加 IPIP 隧道;
    • 规划 2 层直连:需要节点做良好的规划实现 2 层网络直接互联;
    • 大规模配置复杂:网络规划,手动部署 Route Reflector,增加 API 代理。

Weave

  • 支持数据加密的网络插件Weave是CNCF的官方子项目,是其第一个也是目前唯一一个容器网络插件项目。用一个词评价Weave,就是“功能齐全”,有网络通信、有安全策略、有域名服务、有加密通信还有监控。Weave的工作模式与flannel相似,它最早只提供了UDP(称为sleeve模式)的网络模式,后来又加上了fastpath方式(基于VXLAN和OVS),不过Weave消除了flannel中用来存储网络地址的额外组件etcd,自己集成了高可用的数据存储功能。
  • Weave控制面在实现上与Calico类似,数据面在1.2版本前使用userspace实现,即通过UDP封装实现L2 overlay。Weave在1.2版本后,结合了Linux内核的Open vSwitch datapata(odp)和VXLAN,在网络性能上有较大的提升。由于odp和VXLAN与内核相关模块结合较为紧密,因此在实际使用过程中可能会遇到一些和内核相关的问题

Cilium

  • 为微服务网络连接安全而生什么是Cilium?Cilium是“纤毛”的意思,它十分细小又无处不在。Cilium官方的定位是:API-aware Networking and Security plugin,helping Linux Secure Microservices.Cilium是一个具备API感知的网络和安全的开源软件,该软件用于透明地保护使用Linux容器管理平台(如Docker和Kubernetes)部署的应用程序服务之间的网络连接。
  • 为什么使用Cilium在回答为什么使用Cilium这个问题之前,我们先来探讨为什么从单机时代便广泛应用的iptables在微服务时代显得有些力不从心?
    1. iptables在微服务时代的限制作为通用操作系统的一个组件,iptables专注于为Linux管理员提供系统安全性管理的“瑞士军刀”,即基于静态环境的IP和端口配置网络转发、过滤等规则。然而,随着iptables在大规模、高度动态的微服务环境中投入使用,原始设计目标与现代基础设施需求之间的不匹配愈发明显。前文在对Kube-proxy转发模式讨论时就提到了基于iptables服务负载均衡的严重性能瓶颈。除了性能和规模,由于容器的创建和销毁非常频繁,基于IP做身份关联的故障排除和安全审计等功能很难实现。现代数据中心应用程序的开发已经转向面向服务的体系结构(SOA),即我们常说的微服务。基于微服务的应用程序被拆分为一个个独立的小型服务,这些服务使用HTTP、gRPC和Kafka等轻量级协议,通过API相互通信。但是,现有的Linux网络安全机制(例如iptables)仅在网络和传输层(即IP地址和端口)上运行,并且缺乏对微服务层的可见性(visibility)。微服务架构下的应用程序,尤其是通过容器部署的是高度动态变化的。在向高度动态的微服务架构的转变过程中,确实给微服务之间的连接安全性提出了挑战和机遇。具体表现为:传统的Linux网络安全方法(例如iptables)过滤IP地址和TCP/UDP端口,但容器高度不稳定的生命周期导致这些方法难以与应用程序并排扩展。因为负载均衡和访问控制列表要不断更新,系统可能要维护成千上万条规则,这给运维带来了较大负担。出于更精细的安全考虑,协议端口不能再用于区分应用流量,因为同一端口可能承载跨服务的各种消息。另一个挑战是提供准确的可见性,因为传统系统使用IP地址作为主要识别工具,而IP在微服务架构中的寿命可能才几秒。归根结底,通用的基于IP/端口的防火墙方式在微服务架构中的网络和安全面临一系列限制。因此我们不禁发问:如果在微服务时代从头开始设计内核Linux网络和安全方法会是什么样子?
    2. BPF:让Linux感知微服务幸运的是,我们拥有BPF(Berkeley Packet Filter)。一句话总结BPF就是:BPF是Linux内核中的一个高性能沙箱虚拟机,它将内核变成了可编程的。作为一种Linux内核的黑科技,BPF可以在不影响安全性或性能的情况下扩展Linux内核,提供了内核的数据包过滤机制。跟netfilter和tc等一样,BPF是一个框架,用于在内核中的各个挂钩点运行自定义逻辑,这其中就包括Linux网络协议栈中的多处挂载点。这也是那些基于BPF实现的profiling和tracing(例如tcpdump)工具的工作原理。BPF给用户提供两种SOCKET选项:SO_ATTACH_FILTER和SO_ATTACH_BPF,允许用户在sokcet上添加自定义的filter,只有满足该filter指定条件的数据包才会上发到用户空间。SO_ATTACH_FILTER插入的是cBPF(classic Berkeley Packet Filter,即经典BPF,我们说的BPF值就是cBPF)代码,SO_ATTACH_BPF插入的是eBPF(extended Berkeley Packet Filter,即扩展BPF)代码。从Linux 3.15开始,eBPF被引入内核,eBPF扩充了cBPF的功能,丰富了指令集但保留了对cBPF的兼容。例如,tcpdump还是用的cBPF,但cBPF字节码被加载到内核后会被内核自动转换为eBPF字节码。注:若不特殊说明,本书不区分cBPF和eBPF,统一用BPF指代。BPF的工作原理是在内核提供了一个虚拟机,用户态将过滤规则以虚拟机指令的形式传递到内核,由内核根据这些指令来过滤网络数据包。BPF逻辑被编写为简单的“BPF程序”,它们在运行前先要通过验证,以确保它们在任何情况下都不会导致运行它的内核崩溃。验证之后,这些程序被一个JIT(just in time)编译器编译成CPU相关的代码(例如X86)在内核态运行,这意味着它们以与编译到内核中的代码相同的速度运行。最重要的是,任何网络报文都没法绕过BPF在内核态的限制。要理解BPF的作用,首先要意识到Linux内核本质上是事件驱动的!写数据到磁盘,读写socket,请求定时器等,这些过程都是系统调用,都是事件驱动的。世界上最大的单体应用,有着1000万行代码量的Linux无时无刻不在处理各种事件。BPF给我们提供了在事件发生时运行指定的BPF程序的能力。例如,我们可以在以下事件发生时运行我们的BPF程序:·应用发起read/write/connect等系统调用;·TCP发生重传;·网络包达到网卡。BPF在过去几年中发展迅速,Netflix、Facebook和Google等在Linux上做了大量投资的公司,都在积极探索使用BPF作为内核的可扩展性机制,把Linux打造成一个可感知微服务的操作系统。为什么这么说呢?BPF能够使Linux内核感知到API层。当内核能够理解两个应用程序通信过程中调用了哪些API时,它便能够为API调用提供安全保障,并以此构建一个基于身份认证的机制。因此,不同于以前简单的IP+Port过滤网络包的方式,有了BPF加持的Linux内核可以理解什么是一个微服务,微服务的标签有哪些,这个微服务的安全性是怎么样的。
    3. Cilium:把BPF带到Kubernetes首先,Cilium是一个CNI插件,它提供网络连通性、服务负载均衡等功能,但主打的功能还是安全。例如,Cilium实现了Kubernetes的网络策略API,还提供了基于身份认证的微服务安全机制。从一开始,Cilium就是为大规模、高度动态的容器环境而设计的。Cilium在3/4层运行,除了提供传统的网络和安全服务,还有一些L7的负载均衡和流量过滤功能。区别于传统系统中的IP地址识别,Cilium原生地了解服务/容器/Pod标识,并解析HTTP、gRPC和Kafka等协议,提供比传统防火墙更简单、更强大的可见性和安全性。BPF的高效灵活是Cilium的基础。Cilium支持在各种集成点(例如,网络IO、应用程序套接字和跟踪点)中将BPF字节码动态插入Linux内核,为工作负载(包括进程和容器)提供透明的网络连接保护、负载均衡、安全和可观测性支持。最关键的是,BPF的使用使得Cilium能够以高度可扩展的方式实现以上功能,尤其能够应对大规模的微服务场景。Cilium基于BPF,但为用户隐藏了BPF的复杂性,提供了与通用编排框架(例如Kubernetes等)Mesos的集成。Cilium的工作原理如图5-26所示。[插图]图5-26 Cilium的工作原理BPF的强大功能可实现高效的内核数据转发,为常见的微服务用例提供巨大的性能优势,例如Cilium就可以作为Kubernetes服务负载均衡(iptables或IPVS)和Istio本地代理(Envoy)的可选项。
    4. Cilium功能一览具体来说,Cilium实现了以下功能。1)容器的网络连接Cilium的网络模型较简单,即一个三层网络空间为所有服务端点提供链接,并通过策略层实现安全控制。Cilium支持以下跨节点网络模型。·overlay:基于封装的虚拟网络产生所有主机。目前,已支持基于VXLAN和Geneve等封包协议;·直接路由:使用Linux主机内置或云提供商的路由表,通过底层网络路由应用程序容器的IP地址。从架构上看,Cilium将安全与网络寻址进行解耦,极大简化了网络模型,也提高了扩展性,降低了排错难度。2)基于策略的网络安全Cilium同时提供基于数据包和API的网络安全与认证,为传统部署和微服务架构提供安全的网络连接。Cilium的网络策略分为以下几大类。·基于身份:在每个包内,Cilium将负载和身份信息打包在一起(而不是依靠源IP地址),提供高可扩展安全性;·基于IP/CIDR:如果基于身份的方式不适用,那么可以采用基于IP/CIDR安全的方式控制安全访问。Cilium建议在安全策略中尽量采用抽象方式,避免写入具体IP地址。例如,使用Kubernetes的Service(通过label selector选择后端Pod)名;·基于API:HTTP/REST、gRPC和Kafka通过暴露IP和端口对外提供服务,其安全机制明显不足。基于API的网络策略允许使用更细粒度的安全限制,例如REST方法等。下文会详细展开介绍Cilium的安全机制,这里不再赘述。3)分布式可扩展负载均衡BPF提供高性能L3/L4的服务转发与负载均衡,转发策略有rr、wrr、源hash等。基于散列表实现的BPF提供O(1)时间复杂度的路由性能(这一点与IPVS很像),也就是说,Cilium可以替换Kube-proxy实现Kubernetes Service机制。这样所有Kubernetes集群IP服务会自动在BPF中得到高效地实现,而且性能不会随着服务数量的增加而下降。4)可视化与网络策略类似,Cilium也同时在网络包和API调用两个层面实现了可视化。所有可视化信息,不仅是IP地址和端口号,还包括丰富的工作流元数据,例如容器和Pod标签、服务名等。这样一来,Cilium就能提供基于标签、安全身份和事件类型的过滤和可视化。Cilium的可视化基于BPF高性能循环缓冲区(perf ring buffer),可以追踪每秒百万级的应用事件。除此之外,还利用BPF可编程性的高效通道允许数据可视化同时降低额外负担。最后,Cilium的所有可视化都对外提供API,可以嵌入现有系统中。5)监控Cilium周期性地监控集群连接状态,包括节点之间延迟、节点失效和底层网络问题等,并且可以将Cilium整合到Prometheus监控系统中。
  • 总的来说,Cilium使用BPF作为底层引擎,创建了一个精确优化的网络堆栈,用于在Kubernetes等平台上运行API驱动的微服务。下文将重点讨论使用Cilium带来的两个主要好处:·不只简单关注数据包、IP地址和端口,而是将服务标识和API协议(例如HTTP、gRPC和Kafka)视为平台中的一等公民;·针对在微服务环境中越来越常见的规模、动态和部署模式(例如服务网格代理)优化Linux网络转发、可见性和过滤。
  • 然而在同一主机上,将数据从一个Linux套接字复制到另一个Linux套接字可以做到非常高效。因此,一个直观的想法是如果服务和sidecar运行在同一台宿主机上,那么我们可以直接在两个socket之间复制数据,这将带来极大的性能提升(3~4倍)。这也是Cilium和BPF使Linux内核可感知微服务的一个例子。
  • 简而言之,如果采用Service Mesh这种架构,那么使用Cilium+Sockmap应该是减少CPU/内存使用和降低延迟的一种简单方法。
  • 在云原生带来的微服务浪潮下,尽管几乎所有关于如何设计和运行应用程序的内容都在变化,但像iptables(基于内核的netfilter)这样的内核功能仍然是Kubernetes、Mesos、Docker等现代微服务环境中网络路由、包过滤、安全性和记录网络数据的最常用工具。然而,在高度动态和复杂的微服务世界中,仅仅通过IP地址和端口的传统镜头来思考网络和安全性会导致实现效率非常低,只能实现过程可见性和过滤,并且通常非常复杂且难以排查。由于BPF是Linux内部强大的新内核可扩展性机制,使我们有机会在微服务时代重新思考Linux网络和安全堆栈并解决这些问题。Cilium通过将安全性与寻址分离,不仅可以在高度动态的环境中应用安全策略,还提供了除传统的L3/L4网络隔离外的应用层安全限制。Cilium将不再基于传统的“IP+Port”的方式做网络策略,而是基于“身份”,这将带来可见性和安全性,使用起来也更简单,而且功能上更加强大(支持对单个RPC调用的细粒度控制)。参考资料:http://Cilium.readthedocs.io/en/stable/bpf/.

CNI-Genie

  • 一个直观的想法是能不能在同一个容器集群中集成多个网络插件,博采众长?下面介绍一个由华为开源的多网络插件:CNI-Genie。CNI-Genie是一个集成引导插件,本身无具体功能,由引用的插件完成网络功能,支持flannel、Calico、Weave Net、Canal、Romana等CNI插件,还支持SR-IOV、DPDK等。值得一提的是,CNI-Genie本身也是一个符合CNI标准的容器网络插件。
  • CNI-Genie本质上就是Kubernetes和底层多个CNI插件之间的适配器(adapter)。
  • CNI-Genie使用户能够在同一个集群中运行多个CNI,并有助于为每个容器创建多个接口。事实上,多网络平面是CNI-Genie的一个重要功能。需要注意的是,使用CNI-Genie所集成的网络插件才是容器多网络平面和多网卡(IP地址)的真正提供者。

提供给Kubernetes集群外访问

  1. 采用NodePort是解决上述问题的最直接、有效的常见做法。(将Service的端口号映射到物理机)
    • NodePort的实现方式是在Kubernetes集群里的每个Node上都为需要外部访问的Service开启一个对应的TCP监听端口,外部系统只要用任意一个Node的IP地址+具体的NodePort端口号即可访问此服务
    • 在任意Node上运行netstat命令,就可以看到有NodePort端口被监听: netstat -tlp | grep 31002
    • NodePort没有完全解决外部访问Service的所有问题,比如负载均衡问题。
  2. 将容器应用的端口号映射到物理机。
    • 设置容器级别的hostPort,将容器应用的端口号映射到物理机上。
    • 通过设置Pod级别的hostNetwork=true,该Pod中所有容器的端口号都将被直接映射到物理机上。
  3. DNS服务搭建
    • 在集群内需要能够通过服务名对服务进行访问,这就需要一个集群范围内的DNS服务来完成从服务名到ClusterIP的解析。
  4. Ingress:HTTP 7层路由机制
    • 在Kubernetes中,Ingress Controller将以Pod的形式运行,监控API Server的/ingress接口后端的backend services,如果Service发生变化,则Ingress Controller应自动更新其转发规则。

容器 vs 虚拟机

  • 容器是进程级别的隔离技术,因此相比虚拟机有启动快、占用资源少、体积小等优点。
  • 容器与虚拟机对比传统的虚拟机需要模拟整台机器,包括硬件(因此,虚拟机方案需要硬件的支持,例如VT-X),每台虚拟机都需要有自己的操作系统。虚拟机一旦被开启,预分配给它的资源将全部被占用。每台虚拟机包括应用程序、必要的依赖库,以及一个完整的用户操作系统。容器和宿主机共享操作系统,而且可以实现资源的动态分配。容器包含应用程序和所依赖的软件包,并且不同容器之间共享内核,这与虚拟机相比大大节省了额外的资源占用。在宿主机操作系统中,不同容器在用户空间以隔离的方式运行着各自的进程。虚拟机和容器最大的区别在于没有Guest OS(客户虚拟机)这一层。
  • 虚拟机和容器都是在硬件和操作系统以上的,虚拟机有Hypervisor层,Hypervisor即虚拟化管理软件,是整个虚拟机的核心所在。它为虚拟机提供了虚拟的运行平台,管理虚拟机的操作系统运行。每台虚拟机都有自己的操作系统及系统库。容器没有Hypervisor这一层,也没有Hypervisor带来性能的损耗,每个容器和宿主机共享硬件资源及操作系统。
  • Docker容器有Docker Engine这一层。其实Docker Engine远远比Hypervisor轻量,它只负责对Linux内核namespace API的封装和调用,真正的内核虚拟化技术是由Linux提供的。容器的轻量带来的一个好处是资源占用远小于虚拟机。同样的硬件环境,可以运行容器的数量远大于虚拟机,这对提供系统资源利用率非常有用。每台虚拟机都有一个完整的Guest OS,Guest OS能为应用提供一个更加隔离和安全的环境,不会因为应用程序的漏洞给宿主机造成任何威胁。
  • 从虚拟化层面来看,传统虚拟化技术是对硬件资源的虚拟,容器技术则是对进程的虚拟,从而提供更轻量级的虚拟化,实现进程和资源的隔离。从架构来看,容器比虚拟机少了Hypervisor层和Guest OS层,使用Docker Engine进行资源分配调度并调用Linux内核namespace API进行隔离,所有应用共用主机操作系统。因此在体量上,Docker较虚拟机更轻量级,在性能上优于虚拟化,接近裸机性能。

NAT

  • SNAT: Source Network Address Translation,是修改网络包源ip地址的。
  • DNAT: Destination Network Address Translation,是修改网络包目的ip地址的。

Istio

Service Mesh

  • 在Kubernetes逐渐普及的时代,Service Mesh技术已完全取代了使用软件库实现网络运维的方式。严格来说,Service Mesh并不在Kubernetes的核心范围之内。但是,在Kubernetes的帮助下,应用上云后,还面临着服务治理的难题。现在,大多数云原生的应用都是微服务架构,微服务的注册。服务之间的相互调用关系,服务异常后的熔断、降级,调用链的跟踪、分析等一系列现实问题摆在各机构面前。Service Mesh就是解决这类微服务发现和治理问题的一个概念。
  • 在我看来,Service Mesh之于微服务架构就像TCP之于Web应用。
    Istio提供了真正可供操作、非侵入式的方案,相对于Spring Cloud、Dubbo这些SDK方式让人有种耳目一新的感觉。
  • 只有在服务数量和服务间调用的复杂度上升到一定程度后,Service Mesh才会真正派上用场。
  • 所谓sidecar模式,翻译过来就是边车模式,是一种分布式和微服务架构的设计模式,目的是实现了控制和逻辑的分离与解耦。
  • 软件设计中的sidecar模式通过给应用服务加装一个“边车”达到控制和逻辑分离的目的。该设计模式通过给应用程序加上一个“边车”的方式拓展应用程序现有的功能,例如日志记录、监控、流量控制、服务注册、服务发现、服务限流、服务熔断等在业务服务中不需要实现的控制面功能,可以交给“边车”,业务服务只需要专注于实现业务逻辑即可。
  • sidecar模式一般有两种实现方式:·通过SDK的形式,在开发时引入该软件包依赖,使其与业务服务集成起来。这种方法可以与应用密切集成,提高资源利用率并且提高应用性能,但也对代码有侵入,受到编程语言和软件开发人员水平的限制;·agent形式。服务所有的通信都是通过这个agent代理的,这个agent同服务一起部署,和服务一起有着相同的生命周期创建。这种方式对应用服务没有侵入性,不受编程语言和开发人员水平的限制,做到了控制与逻辑分开部署。但是会增加应用延迟,并且管理和部署的复杂度会增加。
  • Service Mesh作为sidecar运行时,对应用程序来说是透明的,所有应用程序间的流量都会通过sidecar,然后由sidecar转发给应用程序。换句话说,由于sidecar劫持了流量,所以对应用程序流量的控制都可以在sidecar中实现。
  • William Morgan在What’s a service mesh?And why do I need one文章中指出Service Mesh有以下几个特点:·应用程序间通信的中间层;·轻量级网络代理;·应用程序无感知;·解耦应用程序的重试/超时、监控、追踪和服务发现。Service Mesh将底层那些难以控制的网络通信统一管理,诸如流量管控、丢包重试、访问控制等。而上层的应用层协议只须关心业务逻辑。Service Mesh是一个用于处理服务间通信的基础设施层,它负责为构建复杂的云原生应用传递可靠的网络请求。
  • Kube-proxy实现了流量在Kubernetes Service的负载均衡,但是没法对流量做细粒度的控制,例如灰度发布和蓝绿发布(按照百分比划分流量到不同的应用版本)等。Kubernetes社区提供的蓝绿发布案例其实是针对Deployment的,但不支持Service。
  • Istio Service Mesh把Kubernetes看作服务注册机构,通过控制平面生成数据平面的配置,数据平面的透明代理以sidecar容器的方式部署在每个应用服务的Pod中。之所以说是透明代理,是因为应用程序容器完全无感知代理的存在。区别在于Kube-proxy拦截的是进出Kubernetes节点的流量,而Istio sidecar拦截的是进出该Pod的流量。

Istio

  • 什么是Istio?官方给出的定义是:An open platform to connect,secure,control and control services.即一个提供微服务连接的、安全的、流量控制的和可观察性的开放平台。Istio分为两个平面:数据平面和控制平面。数据平面由一组sidecar的代理(Envoy)组成。这些代理调解和控制微服务之间的所有网络通信,并且与控制平面的Mixer通信,接受调度策略。控制平面通过管理和配置Envoy管理流量。此外,控制平面配置Mixers来实施路由策略并收集检测到的监控数据
  • 在安装Istio核心组件之前,需要安装一个“服务注册器”,这个“服务注册器”既可以是Kubernetes,也可以是Nomad & Consul。下面笔者以Kubernetes为例,讲解如何在Kubernetes集群中安装Istio控制平面。
  • Istio提供多种安装路径,具体取决于你环境中的Kubernetes平台。但不论平台如何,基本流程都是相同的,即:(1)确认Istio对Pod和服务的要求。(2)安装Kubernetes。(3)在Kubernetes上安装Istio。

Istio sidecar透明注入

  • 网格中的每个Pod都必伴随着一个Istio的sidecar一同运行。下文中将会介绍两种把sidecar注入Pod的方法:使用istioctl客户端工具进行注入,或者使用Istio sidecar injector自动完成注入过程,并且深入sidecar内部解析其工作原理。
  • 需要注意的是,跟手工注入不同,自动注入过程是发生在Pod级别的,因此不会看到Deployment本身发生什么变化。但是可以使用kubectl describe观察单独的Pod,在其中能看到注入sidecar的相关信息。1. 验证sidecar注入部署sleep应用,检查是不是只产生了一个容器。

Istio CNI插件

  • Istio当前默认使用特权init容器istio-init将访问用户容器的流量转发到Envoy。istioinit的主要作用是运行脚本配置容器内的iptables规则。Istio CNI插件的主要设计目标是消除这个特权init container,使用Kubernetes CNI机制实现相同功能的替代方案,因此它是Kubernetes CNI的一个具体实现。

云原生架构

  • 根据云计算服务提供的内容,业界把云计算分成三层:基础架构即服务(IaaS)、平台即服务(PaaS)和软件即服务(SaaS)。根据云计算服务提供的来源和服务对象,云计算分为公有云和私有云。
  • 在虚拟化计算和云计算服务蓬勃发展的阶段,人们也意识到了虚拟化技术的弊端。虚拟化技术虚拟出来的是一个完整操作系统,它的底层包括宿主机操作系统和虚拟化层,势必导致虚拟机的性能低于物理机的性能。此外,完整的操作系统所占用的存储空间较大,而且启动一个虚拟机,等同于启动一个完整操作系统。但是往往在虚拟服务器中可能仅仅是为了运行某一个软件而已。为此,LXC(Linux Container)技术和Docker技术开始出现。它摒弃了启动完整系统的弊端,在现有操作系统上对任务进行隔离,并实现资源按需分配。它允许多个容器共享一个操作系统内核,容器内存储的仅仅是与某个应用紧密相关的资源,其空间占用往往只有几十到几百MB。单独容器化如同虚拟PC一样会面临高可用性不足、管理低级等问题。为此,业界推出了容器编排技术。

什么是云原生

  • 云原生(Cloud Native)概念是由Pivotal的Matt Stine在2013年首次提出的。这个概念得到了社区的不断完善,内容越来越丰富,目前已经包括了DevOps(Development和Operations的组合)、持续交付(Continuous Delivery,CD)、微服务(MicroServices)、敏捷基础设施(Agile Infrastructure)和十二要素(The Twelve-Factor App)等几大主题。这个概念不但包括根据业务能力对企业(高校)进行文化、组织架构的重组与建设,也包括方法论和原则,以及具体的操作工具。采用基于云原生的技术和管理方法,可以更好地从云中诞生业务,也可以把业务迁移到不同的云中,从而享受云的高效与持续服务的能力。
  • 2015年云原生计算基金会(CNCF)成立,对云原生定义进行了修改,认为云原生需要包含应用容器化、面向微服务架构以及支持容器编排调度等方面的内容。
  • 云三层模型与云原生架构的对比图所示,原先的IaaS层升级为敏捷基础设施,而PaaS和SaaS层则合并为微服务架构。敏捷基础设施和微服务都属于技术范畴的架构。在整个云原生架构中,也少不了自动化的持续交付和DevOps等管理措施。
  • 在传统的应用系统开发过程中,软件开发商喜欢聚焦在业务系统,专注于系统如何开发、如何闭源成一个独立的整体系统。但是随着开源软件的盛行,全球合作背景下的分工细化,再加之GitHub的影响力越来越大,一个软件开发商很难在短时间内处理所有问题。软件开发商应该充分利用第三方开源或不开源的组件,自己仅仅实现必要的代码,再借助敏捷基础架构进行灵活多变的必要集成,从而节省大量人力、物力和时间,以便更加聚焦业务开发,同时又能利用整体协作快速部署业务。云原生的意义就在于此,按照云原生的理念进行顶层架构设计、实施、部署,即可实现快速迭代,投入生产使用。云原生主要包括两部分内容:云原生基础架构和云原生应用。

云原生基础架构

  • Kubernetes也不能简单地称为云原生基础架构。Kubernetes的容器编排技术为云原生基础架构提供了必要的平台支撑功能。是否是云原生基础架构的关键在于是否使用自动化处理的方式。

云原生应用

  • 云原生应用程序的关键在于提供弹性、敏捷性、可操作性和可观察性。

  • 快速了解云原生架构

  • 概念随着新的技术发展而演化。

    • 第一阶段:容器化封装 +自动化管理 + 面向微服务
    • 第二阶段:DevOps、持续交付、微服务、容器
    • 第三阶段:DevOps、持续交付、容器、服务网格、微服务、声明式API对云原生的解构
  • 云原生应用:docker 应用打包、发布、运行,Kubernetes 服务部署和集群管理,Istio 构建服务治理能力。


TODO

  1. Netfilter、BPF、SNAT
  2. 虚拟网络相关基础知识(Veth、Linux Bridge、Open vSwitch等)
  3. VxLan、vlan、Open vSwitch、Macvlan、IPvlan等的分类、区别、关系
  4. NVIDIA和AMD两个厂商的GPU、NUMA、Huge Page
  5. 现在是用veth还是IPvlan?
  6. 学习各种网络插件,比对和总结。
  7. istio、Service Mesh

Reference

  • Kubernetes权威指南:从Docker到Kubernetes实践全接触(第4版)
  • Kubernetes网络权威指南:基础、原理与实践
  • 云原生架构进阶实战
  • docker网络之veth设备
  • Linux虚拟网络设备之bridge(桥)
  • 一文详细讲述—Linux网络虚拟化
  • K8S 网络详解 3 CNI 与 CNM 网络模型
  • Kubernetes利用CNI-bridge插件打通网络
  • 60道重要的Kubernetes面试题
  • 一文带你理解云原生 – 很全面的总结!!!
  • 一文深入理解 Kubernetes

IT中的生活哲学

发表于 2021-07-06

前言

  • 最近看完了《SRE:Google运维解密》之后,越来越觉得IT届的很多解决方案、流程设计、技术原理都来源于生活,或者说跟生活上的很多场景很类似。
  • 本文的标题或许不太正确,并没字面上的包含关系,两者有时是相互借鉴的。
  • 本文只是草稿,将持续记录个人认为的一些”生活哲学“。

生活哲学

混沌工程

  1. 在生活中总会出现一些小问题或事件,我们也会定时做一些演练,这样一旦发生大的突变事件时,我们才能更好的应对。

数据库落盘

  1. 为了保证数据不丢,在数据落盘时是现写日志,而不是直接修改数据行。这跟生活中点菜时记录客户的订单或者平常待办事件的备忘一个道理。既可以保证数据不丢,又可以提升处理时间。

《SRE:Google运维解密》-笔记

发表于 2021-07-05

序言

  • 可靠性就像安全性,越早关注越好。
  • 这就意味着一些小型创业公司,在应付日常面临的种种挑战时,也应该抽出一部分精力来面对可靠性这个话题。这与盖房子有些类似,如果一开始将整个地基打好并保持继续修缮,要比盖好房子之后再重新修改设计要容易得多
  • Margaret 曾经说过:“无论对一个软件系统运行原理掌握得多么彻底,也不能阻止人犯意外错误。”
  • 只有靠着对细节的不懈关注,做好充足的灾难预案和准备工作,时刻警惕着,不放过一切机会去避免灾难发生。这就是SRE 最重要的理念!

第1章 介绍

  • 不能将碰运气当成战略。

Google的解决之道:SRE

  • SRE就是让软件工程师来设计一个新型运维团队的结果。

  • 目前来看,UNIX 系统内部细节和1~3层网络知识是Google最看重的两类额外的技术能力。

    • (a)对重复性、手工性的操作有天然的排斥感。
    • (b)有足够的技术能力快速开发出软件系统以替代手工操作。
  • 从本质上来说,SRE 就是在用软件工程的思维和方法论完成以前由系统管理员团队手动完成的任务。这些SRE倾向于通过设计、构建自动化工具来取代人工操作。

  • Google的经验法则是,SRE团队必须将50%的精力花在真实的开发工作上。

  • 由于SRE模型中为了提高可靠性需要采取一些与常规做法违背的做法,所以需要强有力的管理层支持才能推行下去。例如:由于一个季度内的错误预算耗尽而停止发布新功能的决定,可能需要管理层的支持才能让产品研发部门重视起来。

  • 我们可以认为DevOps是SRE核心理念的普适版,可以用于更广范围内的组织结构、管理结构和人员安排。同时,SRE是DevOps模型在Google的具体实践,带有一些特别的扩展。

SRE方法论

  • 一般来说,SRE团队要承担以下几类职责:可用性改进,延迟优化,性能优化,效率优化,变更管理,监控,紧急事务处理以及容量规划与管理。

  • 事后总结应该包括以下内容:事故发生、发现、解决的全过程,事故的根本原因,预防或者优化的解决方案。

  • Google 的一项准则是“对事不对人”,事后总结的目标是尽早发现和堵住漏洞,而不是通过流程去绕过和掩盖它们。

  • 一般来说,任何软件系统都不应该一味地追求100% 可靠。因为对最终用户来说,99.999% 和 100% 的可用性是没有实质区别的

  • 如果100% 不是一个正确的可靠性目标,那么多少才是呢?这其实并不是一个技术问题,而是一个产品问题。

  • 如果一个服务的可靠性目标是99.99%,那么错误预算就是 0.01%。这意味着产品研发部门和SRE部门可以在这个范围内将这个预算用于新功能上线或者产品的创新等任何事情。错误预算可以用于什么范畴呢?研发团队需要用这个预算上线新功能,吸引新用户。理想情况下,我们应该使用错误预算来最大化新功能上线的速度,同时保障服务质量。这个基本模型建立起来之后,许多常见的战术策略,例如灰度发布、1% AB测试等就全说得通了。这些战术性手段都是为了更合理地使用整个服务的错误预算。通过引进“错误预算”的概念,我们解决了研发团队和SRE团队之间的组织架构冲突。SRE团队的目标不再是 “零事故运行”,SRE团队和产品研发团队目标一致,都是在保障业务服务可靠性需求的同时尽可能地加快功能上线速度。这个改动虽小,意义却很大。一次“生产事故”不再是一件坏事,而仅仅是创新流程中一个不可避免的环节,两个团队通过协作共同管理它。

  • 一个需要人工阅读邮件和分析警报来决定目前是否需要采取某种行动的系统从本质上就是错误的。监控系统不应该依赖人来分析警报信息,而是应该由系统自动分析,仅当需要用户执行某种操作时,才需要通知用户。

  • 一个监控系统应该只有三类输出。紧急警报(alert)意味着收到警报的用户需要立即执行某种操作,目标是解决某种已经发生的问题,或者是避免即将发生的问题。工单(ticket)意味着接受工单的用户应该执行某种操作,但是并非立即执行。系统并不能自动解决目前的情况,但是如果一个用户在几天内执行这项操作,系统不会受到任何影响。日志(logging)平时没有人需要关注日志信息,但是日志信息依然被收集起来以备调试和事后分析时使用。正确的做法是平时没人会去主动阅读日志,除非有特殊需要。

  • 任何需要人工操作的事情都只会延长恢复时间。一个可以自动恢复的系统即使有更多的故障发生,也要比事事都需要人工干预的系统可用性更高。当不可避免地需要人工介入时,我们也发现与“船到桥头自然直”的态度相比,通过事先预案并且将最佳方法记录在“运维手册(playbook)”上通常可以使MTTR 降低3倍以上。初期几个万能的工程师的确可以解决生产问题,但是长久看来一个手持“运维宝典”经过多次演习的on-call工程师才是正确之路。

  • 虽然不论多么完备的“运维手册”也无法替代人的创新思维,但是在巨大的时间压力和产品压力下,运维手册中记录的清晰调试步骤和分析方法对处理问题的人是不可或缺的。因此,Google SRE将大部分工作重心放在“运维手册”的维护上,同时通过“Wheel of Misfortune”等项目[2]不断培训团队成员。

  • 变更管理SRE的经验告诉我们,大概 70% 的生产事故由某种部署的变更而触发。变更管理的最佳实践是使用自动化来完成以下几个项目:● 采用渐进式发布机制。● 迅速而准确地检测到问题的发生。● 当出现问题时,安全迅速地回退改动。

  • 容量规划有几个步骤是必需的:● 必须有一个准确的自然增长需求预测模型,需求预测的时间应该超过资源获取的时间。● 规划中必须有准确的非自然增长的需求来源的统计。● 必须有周期性压力测试,以便准确地将系统原始资源信息与业务容量对应起来。

个人总结

  1. 尽早关注安全性;关注细节和充分的准备
  2. 自动化工具替代人工操作
  3. 事后总结的若干要点;事后总结“对事不对人”
  4. 使用错误预算来最大化新功能上线的速度;目标不再是 “零事故运行”
  5. 自动恢复的系统;事先预案和“运维手册;自动化减少人为事故
  6. 容量规划;周期性压力测试和调整

第3章 拥抱风险

  • 你可能认为Google会试图构建一个百分之百可靠的服务。事实证明,超过一定值后,再提高可靠性对于一项服务(和它的用户)来说,结果可能会更差而不是更好!极端的可靠性会带来成本的大幅提升:过分追求稳定性限制了新功能的开发速度和将产品交付给用户的速度,并且很大程度地增加了成本,这反过来又减少了一个团队可以提供的新功能的数量。

  • 此外,用户通常不会注意到一项服务在高可靠性和极端可靠性之间的差异,因为用户体验主要是受较不可靠的组件主导,例如手机移动网络或者他们正在使用的设备。简单地说,用户在一个有着99%可靠性的智能手机上是不能分辨出99.99%和99.999%的服务可靠性的区别的!基于这一点,SRE旨在寻求快速创新和高效的服务运营业务之间的风险的平衡,而不是简单地将服务在线时间最大化。这样一来,我们可以优化用户的整体幸福感,平衡系统的功能、服务和性能。

管理风险

  • 不可靠的系统会很快侵蚀用户的信心,所以我们想要减少系统出故障的几率。然而,经验表明,在构建系统的过程中,可靠性进一步提升的成本并不是线性增加的—可靠性的下一个改进可能比之前的改进成本增加100倍。

度量服务的风险

  • Google标准做法是通过一个客观的指标来体现一个待优化的系统属性。

  • 可用性=系统正常运行时间/(系统正常运行时间+停机时间)

  • 在Google内部,基于时间的可用性通常是毫无意义的。
    我们通过请求成功率来定义服务可用性。
    可用性=成功请求数/总的请求数

  • 使用请求成功率指标量化计划外停机时间使得这种指标更适合在不直接服务终端用户的系统中使用。

  • 通常,我们会为一项服务设定季度性的可用性目标,每周甚至每天对性能进行跟踪。我们通过寻找、跟踪和调整重要的、不可避免的偏差来使服务达到一个高层次的可用性目标。

服务的风险容忍度

  • 为了辨别服务的风险容忍度,SRE必须与产品负责人一起努力,将一组商业目标转化为明确的可以实现的工程目标。

  • 尽管当时YouTube已经有了一个很出色的产品,但它仍然在不断变化和快速发展着。因此,我们为YouTube设定了一个相比我们企业的产品更低的可用性目标,因为快速发展更加重要。

  • 对于一项给定的服务的故障预期是另一个需要重点考虑的因素。我们的业务对于服务的停机时间的容忍程度有多高?持续的低故障率或者偶尔发生的全网中断哪一个会更糟糕?这两种类型的故障可能会导致绝对数量上完全相同的错误被返回,但可能对于业务的影响相差很大。下面这个例子说明了一个提供私人信息的系统中自然发生的完全和部分服务中断的区别。假设有一个联系人管理应用程序,一种情况是导致用户头像显示失败的间歇性故障,另一种情况是将A用户的私人联系列表显示给B用户的故障。第一种情况显然是一个糟糕的用户体验,SRE会努力去快速地解决这个问题。然而,在第二种情况下,暴露私人数据的风险可能会破坏基本的用户信任。因此,在第二种情况下,在进行调试和事后的数据清理时,完全停止该服务更加恰当。

  • 对于Google提供的其他服务,有时候,我们可以接受计划内的常规的服务中断。几年前,Ads前端曾经就是这样的一种服务。它是广告商和网站创建者用来建立、配置、运行和监控他们的广告活动的服务。因为这项工作大部分发生在正常工作时间内,我们认为维修窗口中发生的偶然的、正常的、计划之中的故障是可以接受的,并且我们把这些故障看作计划内停机时间,而不是计划外停机时间。

使用错误预算的目的

  • 产品研发的绩效是如何很大程度通过产品研发速度体现的,这会激励员工尽可能快地创建新的代码。同时, SRE 的绩效表现取决于该服务的可靠性,这意味着SRE 会对高频率的更改提出抗议。两个团队之间的信息不对称进一步加剧了这种内在的紧张局势。产品开发者更了解编写和发布他们的代码所需的时间,而SRE则更关注服务可靠性程度(以及生产环境中的其他相关事项)。

  • 软件对故障的容忍度对意外事件的容忍程度有多高?做得太少,我们就只能设计出一个脆弱无用的产品。做得太多,我们的产品可能没有人会使用(但运行非常稳定)。

  • 测试新发布的代码的最好做法就是在一个典型工作负载的服务子集中进行测试,这种做法通常被称为金丝雀测试。

  • 一项决策越是基于数据做出的,常常就越好。

  • 错误预算提供了一个明确的、客观的指标来决定服务在一个单独的季度中能接受多少不可靠性。这个指标在SRE与产品研发部门的谈判中将政治因素排除。

  • 我们的实际做法如下:● 产品管理层定义一个SLO,确定一项服务在每个季度预计的正常运行时间。● 实际在线时间是通过一个中立的第三方来测算的:我们的监控系统。● 这两个数字的差值就是这个季度中剩余的不可靠性预算。● 只要测算出的正常在线时间高于SLO,也就是说,只要仍然有剩余的错误预算,就可以发布新的版本。

  • 错误预算的主要好处就是它能够激励产品研发和SRE一起找出创新和可靠性之间合理的平衡点。

  • 只要系统符合SLO,就可以继续发行新版本。如果频繁地违反SLO 导致错误预算被耗尽,那么发布就会暂停,同时需要在系统测试和开发环节投入更多资源使得系统更有弹性,以使性能得到提升。

  • 有比这种简单的开/关技术更巧妙和有效的方法:[2]例如,当SLO 违规导致错误预算接近耗尽时,将发布的速度减慢,或者回退到上一版本。

  • 例如,如果产品研发人员想要在测试上节约时间或者想要提高发布速度并且SRE 表示反对时,那么就可以通过错误预算指导决策。当预算剩余很多时,产品研发人员就可以承担更多的风险。如果预算接近耗尽,产品研发人员自身将会推动更多的测试或者放慢发布的速度,因为他们不想冒着用尽预算的风险和拖延他们的程序上线。实际上,产品开发团队这样就开始进行自我监管。他们知道预算还剩多少,并且可以控制自己的风险。(当然,这要求SRE在SLO达不到的时候有权停止程序的发布。)如果网络中断或者数据中心发生故障影响了SLO,怎么办?这样的事件也会给错误预算带来不良的影响,会使本季度剩余部分的发布将会减少。整个团队会支持这种发布频率的降低,因为每个人都有义务保障服务正常运行。

  • 利用错误预算可以同时找到制定得过高的可用性目标,显示出它们所导致的灵活性和创新速度方面的问题。如果团队无法发布新的功能,他们可以选择降低SLO(从而增加错误预算)来提高创新速度。

  • 管理服务的可靠性主要在于管理风险,而且管理风险的成本可能很高。

  • ● 100%可能永远都不是一个正确的可靠性目标:不仅是不可能实现的,而且它通常比一项服务的用户期望的可靠性大得多。我们要将服务风险和愿意承担的业务风险相匹配。● 错误预算在SRE和产品研发团队之间调整激励,同时强调共同责任。错误预算使得讨论发布速率更容易,同时可有效地减少任何关于事故的讨论。这样,多个团队可以毫无怨言地对生产环境风险度达成一致。


第4章 服务质量目标

  • 如果不详细了解服务中各种行为的重要程度,并且不去度量这些行为的正确性的话,就无法正确运维这个系统,更不要说可靠地运维了

  • 我们需要利用一些主观判断结合过去的经验以及对服务的理解来定义一些服务质量指标(SLI)、服务质量目标(SLO),以及服务质量协议(SLA)。

服务质量术语

  • SLO的选择和公布可以帮助设立用户对服务质量的预期。

  • 该策略可以应对那些没有根据的抱怨—“服务太慢了”。如果没有一个明确的SLO,用户经常会按照自己的理解设置一个服务性能的预期,即使这可能跟运维人员或者设计者所想的完全不同。这种问题可能会导致对某个服务的过度依赖—用户错误地认为这个服务会比实际情况更可靠

  • 由于真正的全球Chubby服务故障出现的频率太低,以至于其他服务负责人开始认为全球Chubby服务永远不会出故障,从而不停地将更多的服务依赖于此。
    Chubby全球服务的高可靠性实际上提供了一种安全假象,因为这些服务实际上在Chubby全球服务不可用的时候不能正常工作,不管这种情况是多么罕见。

  • 每个季度,如果真实故障没有将可用性指标降低到SLO之下,SRE会有意安排一次可控的故障,将服务停机。利用这种方法,我们可以很快找出那些对Chubby全球服务的不合理依赖,强迫服务的负责人尽早面对这类分布式系统的天生缺陷。

  • 不管某个服务是否具有SLA,定义SLI与SLO,并且用它们来管理服务质量都是很有价值的。

指标在实践中的应用

  • 只有理解用户对系统的真实需求才能真正决定哪些指标是否有用。指标过多会影响对那些真正重要的指标的关注,而选择指标过少则会导致某些重要的系统行为被忽略。一般来说,四五个具有代表性的指标对系统健康程度的评估和关注就足够了。

  • 常见的服务,根据它们的相关SLI通常会归类为以下几个大类。● 用户可见的服务系统,例如莎士比亚搜索服务的前端服务器通常关心可用性、延迟,以及吞吐量。换句话说:是否能正常处理请求?每个请求花费的时间是多少?多少请求可以被处理?● 存储系统通常强调:延迟、可用性和数据持久性。换句话说:读写数据需要多少时间?我们是否可以随时访问数据?数据是否一段时间内还能被读取?扩展讨论参见第26章。● 大数据系统,例如数据处理流水线系统,一般来说关心吞吐量和端到端延迟。换句话说:处理了多少数据?数据从输入到产出需要多少时间?(某些流水线任务还会关注某个单独处理阶段的延迟。)● 所有的系统都应该关注:正确性。是否返回了正确的回复,是否读取了正确的数据,或者进行了正确的数据分析操作。正确性是系统健康程度的一个重要指标,但是它更关注系统内部的数据,而不是系统本身,所以这通常不是SRE直接负责的。指标的收集利用某种监控系统,大部分指标数据都在服务器端被收集,例如Borgmon(具体参见第10章)或者Prometheus。或者利用某种日志分析系统,例如分析日志中HTTP 500回复所占的比例。然而,某些系统可以加入对客户端数据的收集,否则可能会错失一些不影响服务器端指标,但是对用户产生影响的问题。例如,只关注莎士比亚服务器搜索后端的延迟可能会错失由页面JavaScript脚本导致的用户可见的延迟问题。在这个例子中,度量页面在浏览器中可用的延迟是度量用户体验的一个更好的指标。

  • 平均请求延迟可能看起来很简单,但是却掩盖了一个重要的细节;很可能大部分请求都是很快的,但是长尾请求速度却很慢。

  • 大部分指标都应该以“分布”,而不是平均值来定义。

  • 响应时间的分布越分散,意味着普通用户受到长尾请求延迟的影响就越明显,这可能预示了负载过高情况下出现的排队问题。

  • 一般来说,SRE更倾向于分析一组数据的百分比分布,而非其算术平均值。长尾效应比算术平均值更有特点,使用百分比分布能够更清晰地进行分析。因为计算机系统的本身特质决定,数据是具有特定分布特点的 ——例如,请求延迟必须大于0,同时如果超时设置为1000ms,则不可能有成功请求超过这个时间。因此,我们不能假设算术平均值和中位数是相等的——它们甚至可能相差甚远!

  • 汇总间隔:每1分钟汇总一次● 汇总范围:集群中的全部任务● 度量频率:每10秒一次● 包含哪些请求:从黑盒监控任务发来的HTTP GET请求● 数据如何获取:通过监控系统获取服务器端信息得到● 数据访问延迟:从收到请求到最后一个字节被发出

目标在实践中的应用

  • 我们应该从思考(或者调研)用户最关心的方面入手,而非从现在能度量什么入手。

  • 不要仅以目前的状态为基础选择目标

  • 了解系统的各项指标和限制非常重要,但是仅仅按照当前系统的标准制定目标,而不从全局出发,可能会导致团队被迫长期运维一个过时的系统,没有时间去推动架构重构等任务。

  • 避免绝对值虽然要求系统可以在没有任何延迟增长的情况下无限扩张,或者“永远”可用是很诱人的,但是这样的要求是不切实际的。就算有一个系统能够做到这一点,它也需要花很长时间来设计和构建,同时运维也很复杂—最关键的是,这可能比用户可以接受的(甚至是很开心地接受的)标准要高太多。

  • 不要追求完美我们可以随着时间流逝了解系统行为之后优化SLO的定义。刚开始可以以一个松散的目标开始,逐渐收紧。这比一开始制定一个困难的目标,在出现问题时放松要好得多。

  • 如果服务一切正常,可能力量应该花在其他的优先级上,例如消除技术债务、增加新功能,或者引入其他产品等。

个人总结

  1. 没有根据的抱怨—“服务太慢了”;指标制定
  2. 大部分指标都应该以“分布”,而不是平均值来定义。
  3. 如果服务一切正常,可能力量应该花在其他的优先级上,例如消除技术债务、增加新功能,或者引入其他产品等。

第5章 减少琐事

  • 如果系统正常运转中需要人工干预,应该将此视为一种Bug。

  • “正常”的定义会随系统的进步而不断改变。要把更多的时间花费在长期项目研发上而非日常运维中。因为术语日常运维可能会被误解,我们在这里使用一个专门的词语——琐事(toil)。

琐事的定义

  • 琐事不仅仅代表“我不喜欢做的工作”。

  • 到底什么是琐事?琐事就是运维服务中手动性的,重复性的,可以被自动化的,战术性,没有持久价值的工作。而且,琐事与服务呈线性关系的增长。并不是每件琐事都有以上全部特性,但是,每件琐事都满足下列一个或多个属性:手动性

  • 例如手动运行脚本以便自动执行一些任务。运行一个脚本可能比手动执行脚本中的每一步要快,但具体运行脚本所花费的手动的时间(而非脚本所需要的运行时间)应该被认为是琐事。重复性的如果某件事是第一次做,甚至第二次做,都不应该算作琐事。琐事就是不停反复做的工作。如果你正在解决一个新出现的问题或者寻求一种新的解决办法,不算作琐事。

  • 可以被自动化的如果计算机可以和人类一样能够很好地完成某个任务,或者通过某种设计变更来彻底消除对某项任务的需求,这项任务就是琐事。如果主观判断是必需的,那么很大程度上这项任务不属于琐事。[8]

  • 战术性的琐事是突然出现的、应对式的工作,而非策略驱动和主动安排的。处理紧急警报是琐事。我们可能永远无法完全消除这种类型的工作,但我们必须继续努力减少它。没有持久价值如果在你完成某项任务之后,服务状态没有改变,这项任务就很可能是琐事。如果这项任务会给服务带来永久性的改进,它就不是琐事。一些繁重的工作—比如挖掘遗留代码和配置并且将它们清理出去也不是琐事。与服务同步线性增长如果在工作中所涉及的任务与服务的大小、流量或用户数量呈线性增长关系,那这项任务可能属于琐事。一个良好管理和设计的服务应该至少可以应对一个数量级的增长,而不需要某些一次性工作(例如增加资源)之外的额外工作。

为什么琐事越少越好

  • SRE的一个公开目标是保持每个SRE的工作时间中运维工作(即琐事)的比例低于50%。SRE至少花50%的时间在工程项目上,

  • 以减少未来的琐事或增加服务功能。增加服务功能包括提高可靠性、性能,或利用率,同时也会进一步消除琐事。

什么算作工程工作

  • 工程工作通常是有创新性和创造性的,着重通过设计来解决问题,解决方案越通用越好。

琐事繁多是不是一定不好

  • 琐事不会总是让每个人都不开心,特别是不太多的时候。已知的和重复性的工作有一种让人平静的功效。完成这些事可以带来一种满足感和快速胜利感。琐事可能是低风险低压力的活动,有些员工甚至喜欢做这种类型的工作。

  • 琐事的存在并不总是坏事,但是每个人都必须清楚,在SRE所扮演的角色中,一定数量的琐事是不可避免的,这其实是任何工程类工作都具有的特点。少量的琐事存在不是什么大问题。但是一旦琐事的数量变多,就会有害了。如果琐事特别繁重,那就应该非常担忧,大声抱怨。在许多琐事有害的原因中,有如下因素需要考虑:职业停滞如果花在工程项目上的时间太少,你的职业发展会变慢,甚至停滞。Google确实会奖励做那些脏活累活的人,但是仅仅是该工作是不可避免,并有巨大的正面影响的时候才会这样做。没有人可以通过不停地做脏活累活满足自己的职业发展。

  • 士气低落每个人对自己可以承担的琐事限度有所不同,但是一定有个限度。过多的琐事会导致过度劳累、厌倦和不满。另外,牺牲工程实践而做琐事会对SRE组织的整体发展造成损害。

  • 原因如下:造成误解。我们努力确保每个SRE以及每个与SRE一起工作的人都理解SRE是一个工程组织。如果个人或者团队过度参与琐事,会破坏这种角色,造成误解。进展缓慢琐事过多会导致团队生产力下降。如果SRE团队忙于为手工操作和导出数据救火,新功能的发布就会变慢。开创先例如果SRE过于愿意承担琐事,研发同事就更倾向于加入更多的琐事,有时候甚至将本来应该由研发团队承担的运维工作转给SRE来承担。其他团队也会开始指望SRE接受这样的工作,这显然是不好的。促进摩擦产生即使你个人对琐事没有怨言,你现在的或未来的队友可能会很不开心。如果团队中引入了太多的琐事,其实就是在鼓励团队里最好的工程师开始寻找其他地方提供的更有价值的工作。违反承诺那些为了项目工程工作而新入职的员工,以及转入SRE的老员工会有被欺骗的感觉,这非常不利于公司的士气。

小结

  • 如果我们都致力于每一周通过工程工作消除一点琐事,就可以持续性地整顿服务。我们就可以将更多的力量投入到扩大服务规模的工程工作上去,或者是进行下一代的服务的架构设计,又或者是建立一套跨SRE使用的工具链。让我们多创新,少干琐事吧!

个人总结

  • 琐事应该尽量自动化,把时间创造性的事情上!

为什么要监控

  • 紧急警报的处理会占用员工的宝贵时间。如果该员工正在工作时间段,该警报的处理会打断他原本的工作流程。如果该员工正在家,紧急警报的处理则会影响他的个人生活,甚至是把他从睡眠中叫醒。当紧急警报出现得太频繁时,员工会进入“狼来了”效应,怀疑警报的有效性甚至忽略该警报,有的时候在警告过多的时候甚至会忽略掉真实发生的故障。由于无效信息太多,分析和修复可能会变慢,故障时间也会相应延长。高效的警报系统应该提供足够的信息,并且误报率非常低。

对监控系统设置合理预期

  • 但是监控系统中最重要的一点就是整个“生产故障,人工处理紧急警报,简单定位和深入调试”过程必须要保持非常简单,必须能被团队中任何一个人所理解。

现象与原因

  • 监控系统应该解决两个问题:什么东西出故障了,以及为什么出故障。

  • “现象”和“原因”的区分是构建信噪比高的监控系统时最重要的概念

黑盒监控与白盒监控

  • 黑盒监控是面向现象的,代表了目前正在发生的—而非预测会发生的—问题,即“系统现在有故障”。白盒监控则大量依赖对系统内部信息的检测,如系统日志、抓取提供指标信息的HTTP节点等。白盒监控系统因此可以检测到即将发生的问题及那些重试所掩盖的问题等。

  • 这里应该注意,在一个多层系统中,某一个服务的现象是另外一个服务的原因。

  • 白盒监控有时是面向现象的,有时是面向原因的,这取决于白盒系统所提供的信息。

  • 黑盒监控可以保证系统只在某个问题目前正在发生,并且造成了某个现象时才会发出紧急警报。

4个黄金指标

  • 监控系统的4个黄金指标分别是延迟、流量、错误和饱和度(saturation)。

  • 延迟增加是饱和度的前导现象。99% 的请求延迟(在某一个小的时间范围内,例如一分钟)可以作为一个饱和度早期预警的指标。

  • 如果我们度量所有这4个黄金指标,同时在某个指标出现故障时发出警报(或者对于饱和度来说,快要发生故障时),能做到这些,服务的监控就基本差不多了。

关于长尾问题

  • 区分平均值的“慢”和长尾值的“慢”的一个最简单办法是将请求按延迟分组计数(可以用来制作直方图):延迟为0~10ms之间的请求数量有多少,30~100ms之间,100~300ms之间等。

度量指标时采用合适的精度

  • 如果我们的监控目标需要高精度数据,但是却不需要极低的延迟,可以通过一些内部采样机制外部汇总的方式降低成本。

  • 这种方式使我们可以观测到短暂的CPU热点,但是又不需要为此付出高额成本进行收集和保留高精度数据

简化,直到不能再简化

  • 那些不常用的数据收集、汇总,以及警报配置应该定时删除(某些SRE团队的标准是一个季度没有用到一次即将其删除)。

将上述理念整合起来

  • ● 每当收到紧急警报时,应该立即需要我进行某种操作。每天只能进入紧急状态几次,太多就会导致“狼来了”效应。● 每个紧急警报都应该是可以具体操作的。● 每个紧急警报的回复都应该需要某种智力分析过程。如果某个紧急警报只是需要一个固定的机械动作,那么它就不应该成为紧急警报。● 每个紧急警报都应该是关于某个新问题的,不应该彼此重叠。

小结

  • E-mail警报的价值通常极为有限,很容易变成噪声。我们应该倾向于构建一个良好的监控台页面,直接显示所有的非紧急的异常情况。

个人总结

  1. 避免太多“紧急警报”造成“狼来了”效应
  2. 监控系统应该解决两个问题:什么东西出故障了,以及为什么出故障。
  3. 监控系统的4个黄金指标分别是延迟、流量、错误和饱和度(saturation)。

自动化的价值

  • 任何一个人或者一群人执行数百次动作时,不可能保证每次都用同样的方式进行:没有几个人能像机器一样永远保持一致。这种不可避免的不一致性会导致错误、疏漏、数据质量的问题和可靠性问题。在这个范畴内—一致性地执行范围明确、步骤已知的程序—是自动化的首要价值。

自动化的应用案例

  • 广泛使用的工具有Puppet、Chef、cfengine,甚至 Perl都提供了自动化完成特定任务的方法,主要区别在于对帮助进行自动化的组件的抽象层次不同。

可靠性是最基本的功能

  • 民航[20]或工业应用中—经常会指出高效的自动化的缺点[21]:随着时间的推移,操作员与系统的有用的、直接接触会逐渐减少,因为自动化会覆盖越来越多的日常活动。不可避免的,当自动化系统出现问题时,操作员将无法成功地操作该系统。

小结

  • 团队应该在开发流程开始时就留出一定资源进行发布工程工作。尽早采用最佳实践和最佳流程可以降低成本,以免未来重新改动这些系统。

个人总结

  1. 自动化很重要,但也要避免自动化工具出问题时,人工无法处理

第9章 简单化

  • 可靠性只有靠对最大程度的简化不断追求而得到。

  • 软件系统本质上是动态的和不稳定的。[25]只有真空中的软件系统才是永远稳定的。如果我们不再修改代码,就不会引入新的Bug。如果底层硬件或类库永远不变,这些组件也就不会引入Bug。如果冻结当前用户群,我们将永远不必扩展系统。事实上,一个对SRE管理系统的方法不错的总结是:“我们的工作最终是在系统的灵活性和稳定性上维持平衡。”

乏味是一种美德

  • 必要复杂度是一个给定的情况所固有的复杂度,不能从该问题的定义中移除,而意外复杂度则是不固定的,可以通过工程上的努力来解决。
  • 例如,编写一个Web服务器需要处理快速提供Web页面的必要复杂度。但是,如果我们用Java编写该服务器,试图减少GC的影响就可能会引入意外复杂度。

我绝对不放弃我的代码

  • 那些由于功能开关没有启用而没有被执行的代码,就像一个定时炸弹一样等待爆炸,正如Knight Capital的痛苦经历
  • 审查代码以确保它确实符合商业目标,定期删除无用代码,并且在各级测试中增加代码膨胀检测。

“负代码行”作为一个指标

  • 我曾经做过的一些最令人满意的编码工作就是删除了数千行已经没用的代码。

最小 API

  • 法国诗人 Antoine de Saint Exupery 曾写道,“不是在不能添加更多的时候,而是没有什么可以去掉的时候,才能达到完美。”
    (参见文献[Sai39])这个原则同样适用于软件的设计和构建。API是这个规则应该遵循的一个清晰的例子。

  • 书写一个明确的、最小的API 是管理软件系统管理简单性必要的部分。我们向API消费者提供的方法和参数越少,这些API就越容易理解,我们就能用更多的精力去尽可能地完善这些方法。同时,一个反复出现的主题是:有意识地不解决某些问题可以让我们能够更专注核心问题,使得我们已有的解决方案更好。在软件工程上,少就是多!一个很小的,很简单的API通常也是一个对问题深刻理解的标志。

个人总结

  1. 我们的工作最终是在系统的灵活性和稳定性上维持平衡
  2. 必要复杂度是业务固有的,而意外复杂度则是不固定的,可以通过工程上的努力来解决
  3. “不是在不能添加更多的时候,而是没有什么可以去掉的时候,才能达到完美。”

第Ⅲ部分 具体实践

  • 正确的解决方案不一定是当场把问题一次性修复好,而可以靠降低系统准确度、关闭一些不重要的功能,或者将用户流量导向其他没有问题的任务实例等手段暂时缓解问题。解决方案的细节肯定是和每个服务和团队相关的。但是如何有效地应对紧急问题的方法论是每个团队都适用的。

第10章 基于时间序列数据进行有效报警

  • 一个大型系统不应该要求运维人员持续关注其中使用的无数个小组件,而是应该自动汇总所有的信息,自动抛弃其中的异常情况。监控系统应该主要从高级服务质量目标层面进行报警,但是也应该保持足够的粒度,可以追踪到某个具体组件。

Borgmon的起源

  • Prometheus 都是开源软件中与Borgmon 基于时间序列数据报警理念类似的系统。
  • Prometheus[3]与Borgmon十分类似,尤其是当你对比其中的规则计算语法时。变量收集和规则计算的理念在所有这些项目中都很类似。希望借助这些工具,读者可以自行试验、部署本章内描述的一些想法。

黑盒监控

  • 白盒监控只能看到已经接收到的请求,并不能看到由于DNS故障导致没有发送成功的请求,或者是由于软件服务器崩溃而没有返回的错误
  • 探针程序使用应用级别的自动请求探测目标是否成功返回。

配置文件的维护

  • Borgmon提供一种类似于宏的模板机制,允许用户创造出一些可重用的规则库,进一步减小了配置文件中的重复程度,降低配置文件出错的可能性。

个人总结

  1. Prometheus
  2. 黑盒监控(类似APM)
  3. 保证监控系统的维护成本与服务的部署规模呈非线性相关增长是非常关键的。

  • SRE团队和纯运维团队十分不一样的地方在于,SRE团队非常强调用工程化手段来应对运维问题。而这些运维问题,当达到一定规模时,也确实只有采用软件工程化手段才能解决。

  • 我们为SRE花在纯运维事务上的时间设立了50%的上限。SRE至少要花50%的时间进行工程项目研发,以便能够研发出更好的自动化和服务优化手段来更好地服务整个业务。

安全感

  • 现代理论研究指出,在面临挑战时,一个人会主动或非主动(潜意识)地选择下列两种处理方法之一(参见文献[Kah11]):● 依赖直觉,自动化、快速行动。● 理性、专注、有意识地进行认知类活动。

  • 当处理复杂系统问题时,第二种行事方式是更好的,可能会产生更好的处理结果,以及计划更周全的执行过程。为了确保on-call工程师可以保持在第二种处理方式范围内,我们必须要减轻on-call所带来的压力感。

  • 在应急事故处理过程中,凭直觉操作和快速反应(例如服务出现问题就先重启服务器)看起来都是很有用的方法,但是这些方法都有自己的缺点。直觉很可能是错误的,而且直觉一般都不是基于明确的数据支持的。因此,在处理问题的过程中,on-call工程师很有可能由于凭直觉去解释问题产生的原因而浪费宝贵的时间。快速反应主要是由习惯而产生的,习惯性的快速反应的动作后果一般都没有经过详细考虑,这可能会将灾难扩大。在应急事件处理过程中,最理想的方法论是这样的:在有足够数据支撑的时候按步骤解决问题,同时不停地审视和验证目前所有的假设。

  • 让on-call SRE知道他们可以寻求外部帮助,对减轻on-call压力也很有帮助。最重要的资源有:● 清晰的问题升级路线。● 清晰定义的应急事件处理步骤。● 无指责,对事不对人的文化氛围(参见文献[Loo10]和[All12])

  • 最后,在应急事件处理结束时,仔细评估哪些地方有问题,哪些地方做得好是非常关键的。而且应该采取措施避免再次发生同样的问题。SRE团队必须在大型应急事件发生之后书写事后报告,详细记录所有事件发生的时间线。这些事后报告都是对事不对人的,为日后系统性地分析问题产生的原因提供了宝贵数据。犯错误是不可避免的,软件系统应该提供足够的自动化工具和检查来减少人为犯错误的可能性(参见文献[Loo10])。

避免运维压力过大

  • 控制on-call工程师收到的针对同一起事故的报警总数也很重要。有的时候,一个异常情况可能会触发多条报警,所以合理地分组汇总报警信息是很重要的。

  • 奸诈的敌人—运维压力不够虽然给一个非常安静的系统on-call值班是很幸福的事情,但是当一个系统太稳定,或者SRE on-call的周期太长会发生什么呢?SRE团队运维压力不够也是一个不良现象。长时间不操作生产环境会导致自信心问题,包括自信心太强以及自信心不够。这些现象只有在下一次发生问题时,才会显现出来。为了避免这种问题,应该控制SRE团队的大小,保证每个工程师每个季度至少参与oncall一次,最好两次。这样可以保证团队成员有足够的生产环境操作经验。“命运之轮”(见第28章)也是一种有助提高技能和共享知识的团队活动。同时,Google每年举办一次持续数天的全公司灾难恢复演习(DiRT),针对理论性和实际性的灾难进行演练。

个人总结

  1. 理性,逐步验证解决
  2. 清晰定义的应急事件处理步骤和升级流程
  3. 定期演练避免一旦突发时生疏

第12章 有效的故障排查手段

  • 值得警惕的是,理解一个系统应该如何工作并不能使人成为专家。只能靠调查系统为何不能正常工作才行。

  • 新手们常常不能有效地进行故障排查,是因为这个过程理想情况下同时需要两个条件。

    • 1.对通用的故障排查过程的理解(不依靠任何特定系统)。2.对发生故障的系统的足够了解。

理论

  • 我们不断提出一个造成系统问题的假设,进而针对这些假设进行测试和排除。

实践

  • 有效的故障报告应该写清预期是什么,实际的结果是什么,以及如何重现

  • 合理判定一个问题的严重程度需要良好的工程师判断力,同时,也需要一定程度的冷静。

  • 在大型问题中,你的第一反应可能是立即开始故障排查过程,试图尽快找到问题根源。这是错误的!不要这样做。正确的做法应该是:尽最大可能让系统恢复服务。

  • 这可能需要一些应急措施,比如,将用户流量从问题集群导向其他还在正常工作的集群,或者将流量彻底抛弃以避免连锁过载问题,或者关闭系统的某些功能以降低负载。缓解系统问题应该是你的第一要务。在寻找问题根源的时候,不能使用系统的用户并没有得到任何帮助。当然,快速定位问题时仍应该及时保存问题现场,比如服务日志等,以便后续进行问题根源分析时使用。

  • 这种方法也同样适用于计算机系统:如果一个Bug有可能导致不可恢复的数据损坏,停止整个系统要比让系统继续运行更好。对新SRE来说,这个想法是反直觉,令人不安的。对以前曾经有过产品研发背景的人来说更有点难以接受

  • 在日志中支持多级记录是很重要的,尤其是可以在线动态调整日志级别。

最后一个修改

  • 计算机系统有惯性存在:我们发现,一个正常工作的计算机系统会维持工作,直到某种外力因素出现,例如一个配置文件的修改,用户流量的改变等。检查最近对系统的修改可能对查找问题根源很有帮助。

  • 某项测试可能产生有误导性的结果。例如:防火墙规则可能只允许某些特定IP访问,所以在你的工作机上ping 数据库可能会失败,而实际从应用服务器上ping数据库可能是成功的。

  • 将你的想法明确地记录下来,包括你执行了哪些测试,以及结果是什么。

  • 尤其是当你处理更加复杂的问题时,良好的文档可以让你记住曾经发生过什么,可避免重复执行。

神奇的负面结果

  • 我们在设计实验的时候应该将可能的负面结果考虑在内,因为一个可靠的、应用广泛的负面结果对其他人更有帮助。

  • 公布负面结果有助于提升整个行业的数据驱动风气。

  • 公布结果。如果你对一项测试的结果感兴趣,那么很有可能其他人也感兴趣。当你公布结果的时候,其他人不需要再重新设计和运行一套类似试验。

  • 更多的试验压根没有公布数据,因为很多人错误地认为负面结果意味着没有价值。

使故障排查更简单

  • 使故障排查更简单有很多方法可以简化和加速故障排查过程。可能最基本的是:● 增加可观察性。在实现之初就给每个组件增加白盒监控指标和结构化日志。● 利用成熟的、观察性好的组件接口设计系统。

个人总结

  1. 理解一个系统应该如何工作并不能使人成为专家。只能靠调查系统为何不能正常工作才行。
  2. 有效的故障报告应该写清预期是什么,实际的结果是什么,以及如何重现
  3. 先让系统恢复服务,再排查问题,尽量保存现场相关数据或日志等。
  4. 发生问题时,留意“最后一个修改”
  5. 排查问题后文档记录,避免以后重复执行
  6. 实验的负面结果也有价值

第13章 紧急事件响应

  • 东西早晚要坏的,这就是生活。
  • 将紧急事件的处理过程变成一个学习机会。

当系统出现问题时怎么办

  • 当系统出现问题时怎么办首先,别惊慌失措!这不是世界末日,你也并不是一个人在战斗!作为一个专业人士,你已经接受过如何正确处理这样的情况的训练。通常来说,我们处理的问题一般不涉及真实的物理危险,只是计算机系统出现了问题。最差的情况下,也只是半个互联网都停止运转了(Google网络规模已经是全球排名前三)。所以请深吸一口气,慢慢来。

测试导致的紧急事故

  • 这次测试带来的严重后果促使开发者对代码类库中的问题进行了一次彻底的修复,同时制定了一个周期性测试机制来保证这类严重问题不再重现。

  • 由于我们没有在测试环境中测试回滚机制,没有发现这些机制其实是无效的,导致了事故总时长被延长了。我们现在要求在大型测试中一定先测试回滚机制。

变更部署带来的紧急事故

  • 在这些带外通信系统之外,Google还有命令行工具和其他的访问方式确保我们能够在其他条件无法访问的时候进行更新和变更回滚。这些工具和访问方式在这次事故中起到了重大作用,但是工程师应该更加频繁地测试,以便更为熟悉它们。

  • 具有讽刺意味的是,我们本来计划在下个季度提高部署测试流程和自动化的优先级。这次事故的发生直接将他们的优先级提高了,同时强调不管风险看起来有多小,也要经过严格完整的部署测试。

流程导致的严重事故

  • 我们在机器管理自动化上投入了大量的时间和精力,以使我们能很轻松地在整个集群里运行、停止和重新配置大量任务。但是当意外来临时,自动化的效率有时可能是很可怕的。

所有的问题都有解决方案

  • 时间和经验一再证明,系统不但一定会出问题,而且会以没有人能够想到的方式出问题。Google学到的最关键的一课是,所有的问题都有对应的解决方案,虽然对一个面对着疯狂报警的工程师来说,它可能不是那么显而易见。如果你想不到解决办法,那么就在更大的范围内寻求帮助。找到更多团队成员,寻求更多的帮助,做你需要做的一切事情,但是要快。最高的优先级永远是将手头问题迅速解决。很多时候,触发这个事故的人对事故了解得最清楚,一定要充分利用这一点。

  • 非常重要的是,一旦紧急事件过去之后,别忘了留出一些时间书写事后报告。

向过去学习,而不是重复它

  • 向过去学习,而不是重复它为事故保留记录没有什么比过去的事故记录是更好的学习资料了。历史就是学习其他人曾经犯的错误。在记录中,请一定要诚实,一定要事无巨细。尤其重要的是,提出关键的问题。时刻寻找如何能在战术及战略上避免这项事故的发生。公布和维护事后报告,确保全公司的每个人都能从中学到你所学到的知识。在事故结束后,确保自己和其他人切实完成事故中总结的待办事项。这样能够避免未来再次发生以同样的因素触发的同样的事故。一旦开始仔细学习过去的事故,我们就能更好地避免未来的事故。

  • 鼓励主动测试面对失败,理论和实践是两个完全不同的领域。直到你的系统真的失败的那一刻,你并不真的了解它,以及依赖它的系统,或者它的用户会如何应对。不要预设任何假设,也不要依赖任何没有经过测试的假设。你是希望这个系统在星期六凌晨两点钟,公司大部分同事都还在参加黑森林中的团建时出现故障,还是希望和最可靠和最聪明的同事在一起仔细监控着它们上周详细评审过的测试时出现故障呢?

一次流程管理良好的事故

  • Josephine上线之后,发现Robin也自愿加入进来。Sabrina 提醒Robin和Josephine,他们应该优先处理任何Mary交给他们的工作,同时他们必须告知Mary他们进行的任何操作。Robin和Josephine 通过阅读实时事故状况文档,很快熟悉了目前的情况。到下午5点时,Sabrina开始寻找接下来负责处理事故的替代人,因为她和她的同事们快要到下班时间了。她更新了事故状况文档。在5点45分时,她召开了一个简短的电话会议,让所有人都清楚目前的情况。在6点时,他们与他们的姐妹团队(另外一个办公室同团队的人)进行了职责交接。Mary第二天早上回到公司时,发现她身处大西洋另一端的同事已经定位了具体问题,缓解了问题,同时将事故做了了结,已经开始写事后总结了。问题解决了!她冲了点咖啡,开始规划一些结构性改变,使得这类问题在未来不会再重现。

什么时候对外宣布事故

  • 先宣布事故发生,随后找到一个简单解决方案,然后宣布事故结束,要比在问题已经持续几个小时之后才想起流程管理更好。应当针对事故设立一个明确的宣布条件。Google团队依靠下面几个宽松的标准——如果下面任何一条满足条件,这次事故应该被及时宣布。● 是否需要引入第二个团队来帮助处理问题?● 这次事故是否正在影响最终用户?● 在集中分析一小时后,这个问题是否依然没有得到解决?

  • 如果平时不经常使用,事故流程管理的可靠性萎缩得很快。所以怎么使工程师不忘记他们的流程管理技能呢?难道一定要制造更多事故吗?幸运的是,事故流程管理框架常常也适用于其他的跨时区、或者跨团队的常规运维变更实施。如果我们经常使用流程管理框架处理生产变更请求,那么在事故来临时,就可以很好地利用流程管理框架管理它。如果你的组织经常进行灾难恢复演习(你应该这样做!参见文献[Kir12]),事故流程管理应该包含在其中。Google经常针对之前发生的灾难进行角色扮演式演习,比如演习另外一个地区的团队处理过的问题,以更好地熟悉事故流程管理体系。

小结 !!!

  • 我们发现,通过事前准备一个事故流程管理策略,并确保平稳实施,以及经常测试,我们能够降低事故的平均恢复时间(MTTR),同时减轻处理紧急事故的人的工作压力。任何对服务可靠性关注的组织团队都会从类似策略上获得帮助。事故流程管理最佳实践划分优先级:控制影响范围,恢复服务,同时为根源调查保存现场。事前准备:事先和所有事故处理参与者一起准备一套流程。信任:充分相信每个事故处理参与者,分配职责后让他们自主行动。反思:在事故处理过程中注意自己的情绪和精神状态。如果发现自己开始惊慌失措或者感到压力难以承受,应该寻求更多的帮助。考虑替代方案:周期性地重新审视目前的情况,重新评估目前的工作是否应该继续执行,还是需要执行其他更重要或者更紧急的事情。练习:平时不断地使用这项流程,直到习惯成自然。换位思考:上次你是事故总控负责人吗?下次可以换一个职责试试。鼓励每个团队成员熟悉流程中的其他角色。

个人总结

  1. 东西早晚要坏的,这就是生活。
  2. 将紧急事件的处理过程变成一个学习机会。
  3. 在大型测试中一定先测试回滚机制。
  4. 时间和经验一再证明,系统不但一定会出问题,而且会以没有人能够想到的方式出问题。所有的问题都有对应的解决方案。
  5. 向过去学习,而不是重复它(事后报告)
  6. 事前准备一个事故流程管理策略

第15章 事后总结:从失败中学习

  • 一篇事后总结是一次事故的书面记录,包括该事故造成的影响,为缓解该事故采取的措施,事故的根本原因,以及防止未来问题重现的后续任务。

Google的事后总结哲学

  • 书写事后总结的主要目的是为了保证该事故被记录下来,理清所有的根源性问题,同时最关键的是,确保实施有效的措施使得未来重现的几率和影响得到降低,甚至避免重现。

  • 书写事后总结不是一种惩罚措施,而是整个公司的一次学习机会。但是书写事后总结的过程确实需要消耗团队的一定时间和精力,所以我们在选择上很严格。每个团队都有一些内部灵活性,但是基本的事后总结条件为:● 用户可见的宕机时间或者服务质量降级程度达到一定标准。● 任何类型的数据丢失。● on-call 工程师需要人工介入的事故(包括回滚、切换用户流量等)。● 问题解决耗时超过一定限制。● 监控问题(预示着问题是由人工发现的,而非报警系统)。

  • 在SRE的文化中,最重要的就是事后总结“对事不对人”。一篇事后总结必须重点关注如何定位造成这次事件的根本问题,而不是指责某个人或某团队的错误或者不恰当的举动。一篇对事不对人的事后总结假设所有参与事件处理的人都是善意的,他们在自己当时拥有的信息下做了正确的举动。如果因为某些“错误的”举动就公开指责或者羞辱某个人或团队,那么人们就会自然地逃避事后总结。

  • 我们不能“修好”某个人,但是可以通过改善系统和流程从而更好地协助他在设计和维护大型复杂系统时,做出更多“正确”的判断。

  • 当一次事故发生时,我们不能把事后总结当成例行公事。我们的工程师将事后总结看作一个修复问题,一个使Google变得更可靠的机会。一篇“对事不对人”的事后总结不应该简单地指责或者抱怨某个团队,而应该确实提出服务如何能够获得进步。

  • 下面是两个例子。指责“我们需要重写整个复杂后端系统。在过去三个季度中,它每周都在出问题。我们对一点一点修复它的问题已经烦透了!真的,如果我再收到一个报警,那我就自己重写了。”对事不对人“通过重写整个后端系统可能可以避免这些烦人的报警信息继续发生,目前版本的维护手册非常冗长,学习成本很高。相信通过重写,可以减少报警信息,未来的oncall工程师会感谢我们的。”

  • 最佳实践:避免指责,提供建设性意见。

  • 对事不对人的事后总结有的时候比较难写,因为事后总结的格式清晰地表明了触发事故的原因。从事后总结中排除指责的因素可以使人们在向上级汇报问题的时候更有自信。同时我们也不应该因为某个团队和个人经常写事后总结而对他们产生怀疑。一个充满相互指责风气的环境很容易让人将事故和问题掩盖起来,从而对整个组织酿成更大的灾难。

协作和知识共享

  • 最佳实践:所有的事后总结都需要评审

  • 一旦所有的事故参与者都对文档和其中的代办事项表示了肯定,这篇事后总结会被添加到该团队或者整个组织的文档汇总中。

  • 透明化的共享机制保证了每个人都可以很容易地找到和学习以前的事故。

建立事后总结文化

  • 最佳实践:公开奖励做正确事的人

  • 最佳实践:收集关于事后总结有效性的反馈

个人总结

  • 最佳实践:避免指责,提供建设性意见。
  • 一篇“对事不对人”的事后总结不应该简单地指责或者抱怨某个团队,而应该确实提出服务如何能够获得进步。
  • 一个充满相互指责风气的环境很容易让人将事故和问题掩盖起来,从而对整个组织酿成更大的灾难。

第16章 跟踪故障

  • 提高可靠性的唯一可靠的方法论是建立一个基线(baseline),同时不断跟踪改变。

  • 通过找到基础设施中造成故障最多的一部分,可以更好地知道如果提高该部分的稳定性或性能会带来多大帮助。

  • 甚至有的时候,我们需要人为制造一些宕机时间,以免给内部用户造成某种假象(通常指某些服务设计的架构已经决定发生故障会耗时很久才能解决,但是经常由于运气因素而造成非常稳定的假象。某些团队选择定期制造人为宕机时间,以避免用户过于依赖该服务)。

个人总结

  1. 定时宕机,避免把依赖当作理所当然。

第17章 测试可靠性

  • 如果你还没有亲自试过某件东西,那么就假设它是坏的。

  • 测试的数量直接取决于所服务系统的可靠性要求。随着代码的测试覆盖度上升,每次改动的不确定性和降低系统可靠度的可能性都降低了。足够的代码测试覆盖度可以让我们对系统做出更多的改动,而不会使系统可靠度下降到可接受水平之下

  • 测试和平均修复时间的关系系统通过某项测试或者一系列测试并不一定能证明系统是稳定的,但是失败的测试通常证明了系统不可靠。

软件测试的类型

  • 软件测试基本分为两大类:传统测试和生产测试。传统测试在软件开发过程中很常见,主要用来在开发过程中离线评估软件的正确性。生产测试在生产Web服务器上进行,用来评估一个已经部署的软件系统工作是否正常。

  • 单元测试单元测试(unit test)是最小、最简单的软件测试形式。这些测试用来评估某一个独立的软件单元,比如一个类,或者一个函数的正确性。这些测试不考虑包含该软件单元的整体系统的正确性。单元测试同时也是一种规范,用来保证某个函数或者模块完全符合系统对其的行为要求。单元测试经常被用来引入测试驱动开发的概念。集成测试通过独立的单元测试的软件组件被组装成大的系统组件。工程师通过在这个组件中运行一个集成测试(integration test)来检验该组件的功能的正确性。依赖注入(dependency injection),利用类似Dagger[53]这样的工具,我们可以创建出复杂依赖的mock(测试中替代真实逻辑的伪组件),用以方便地测试某个系统组件。一个常见的例子是通过依赖注入来用轻便的mock替换一个有状态的数据库,同时保持一模一样的行为特征。

  • 系统测试系统测试(system test)是一个在未部署的系统上运行的大型测试。某个组件的所有模块(Module)都会被装载到系统中(例如通过集成测试的软件服务器)。接下来工程师可以端到端地测试系统功能。系统测试包括以下几种类型。冒烟测试(smoke test)工程师在冒烟测试中可检测非常简单但是非常重要的系统行为。这是最简单的一种系统测试形式。冒烟测试有时也被称为理性测试,如果该测试不通过,那么其他更昂贵的测试可以不用运行了。性能测试(performance test)一旦冒烟测试通过,系统基本的正确性已经得到了保障。下一步通常是通过某个系统测试的变形来保证整个系统的性能自始至终保持在可接受范围内。因为系统的响应时间和资源要求可能在开发过程中大量改变,该系统必须接受某些测试以确保它不会在没人知道的情况下逐渐变慢(在发布到最终用户之前)。例如,一个程序可能随着改变开始需要32GB内存,而以前只需要8GB。或者该程序的响应时间由10ms变成了50ms,随后变成了100ms。性能测试可以保证随着时间推移系统性能不会下降,或者资源要求不会升高。回归测试(regression test)另外一种系统测试可保证之前的Bug不会重现。回归测试可以被理解为曾经发生过的,导致系统故障或产生错误信息的Bug列表。通过将这些Bug记录为系统测试或者集成测试,重构代码的工程师可以保证他们不会偶然间将他们曾经辛苦调查和修复的Bug又带回来。很重要的是,每个测试都有成本,时间成本和计算资源成本。在一个极限上,单元测试非常便宜,通常可以在毫秒级和很少的资源上(例如一个笔记本电脑上)完成。而在另一个极限上,将一个完整的软件服务器设立起来,同时包括它所有的依赖系统(或者是mock),然后运行相关的测试可能会需要很长时间—几分钟到几小时—一般还需要专属的运算资源。时刻关注这些测试的成本,是软件开发效率提升的重要因素,同时也鼓励程序员更有效地利用我们的测试资源。生产测试生产测试和一个已经部署在生产环境中的业务系统直接交互,而不是运行在密闭的测试环境中。这些测试和黑盒监控在很多地方十分类似(参见第6章),有的时候也被称为黑盒测试。生产测试对运行一个可靠的生产环境来说是必要的。

小结

  • 测试是工程师提高可靠性投入回报比最高的一种手段。

个人总结

  • 测试分类查资料总结

基于意图的容量规划

  • 向大型团队推广内部软件工具需要以下几点:● 持续的和完整的推广方案。● 用户的拥护。● 资深工程师和管理层的赞助,因为他们看到了项目的实用潜力。

使用DNS进行负载均衡

  • DNS负载均衡。最简单的方案是在DNS回复中提供多个A记录或者AAAA记录,由客户端任意选择一个IP地址使用

负载均衡:虚拟IP

  • 虚拟IP地址(VIP)不是绑定在某一个特定的网络接口上的,它是由很多设备共享的。

识别异常任务:流速控制和跛脚鸭任务

  • 从一个客户端的视角来看,某个后端任务可能处于下列任一种状态中:健康后端任务初始化成功,正在处理请求。拒绝连接后端任务处于无响应状态。这可能是因为任务正在启动或者停止,或者是因为后端正处于一种异常状态(虽然很少有后端任务在非停止状态下停止监听端口)。

  • 跛脚鸭状态后端任务正在监听端口,并且可以服务请求,但是已经明确要求客户端停止发送请求。

  • 当某个请求进入跛脚鸭状态时,它会将这个状态广播给所有已经连接的客户端。但是那些没有建立连接的客户端呢?

  • 在Google的RPC框架实现中,不活跃的客户端(没有建立TCP连接的客户端)也会定期发送UDP健康检查包。这就使跛脚鸭状态可以相对较快地传递给所有的客户端—通常在一到两个RTT周期内—无论它们处于什么状态下。

  • 允许任务处于这种半正常的跛脚鸭状态的好处就是让无缝停止任务变得更容易,处于停止过程中的任务不会给正在处理的请求返回一个错误值。能够无影响地停止一个活跃的后端任务可以让处理代码推送、设备维护活动,和机器故障问题导致的任务重启变得对用户透明。这个停止过程通常按照以下步骤进行:1.任务编排系统发送一个SIGTERM信号给该任务。2.后端任务进入跛脚鸭状态,同时请求它的所有客户端发送请求给其他后端任务。这通过SIGTERM信号处理程序中调用RPC实现中的API完成。3.任何在后端进入跛脚鸭状态时正在进行的请求(或者在进入状态之后,但是其他客户端收到通知之前)仍会继续进行。4.随着请求回复被发送回客户端,该后端任务的活跃请求逐渐降低为0。5.在配置的时间过后,该后端程序要么自己干净地退出,要么任务编排系统主动杀掉它。该时间应该被设置为一个足够大的值,以便一般的请求可以有足够的时间完成。每个服务的该数值都不同,一般来说取决于客户端的复杂程度,10s到150s是一个不错的选择。这个策略使客户端可以在后端程序进行耗时较长的初始化过程中(这时后端程序还不能服务请求)就建立连接。如果后端程序等到服务可以接受请求的时候才建立链接,就增加了一些不必要的延迟。一旦后端程序可以提供服务了,它就会主动通知所有客户端。

利用划分子集限制连接池大小

  • 利用划分子集限制连接池大小在健康管理之外,负载均衡另外要考虑的一个因素就是子集划分:限制某个客户端任务需要连接的后端任务数量。我们的RPC系统中的每个客户端都会针对后端程序维持一个长连接发送请求。这些连接通常在客户端启动的时候就建立完成,并且保持活跃状态,不停地有请求通过它们,直到客户端终止。另外一个方案是针对每个请求建立和销毁后端连接,这样会带来极大的资源成本和造成延迟问题。在极端情况下,如果某个连接闲置时间非常长,我们的RPC实现可以自动将该连接转为“不活跃”状态,转为UDP模式连接,而非TCP模式。

QPS陷阱

  • Google在多年的经验积累中得出:按照QPS来规划服务容量,或者是按照某种静态属性(认为其能指代处理所消耗的资源:例如某个请求所需要读取的键值数量)一般是错误的选择。就算这个指标在某一个时间段内看起来工作还算良好,早晚也会发生变化。有些变动是逐渐发生的,有些则是非常突然的(例如某个软件的新版本突然使得某些请求消耗的资源大幅减少)。这种不断变动的目标,使得设计和实现良好的负载均衡策略使用起来非常困难。更好的解决方案是直接以可用资源来衡量可用容量。

客户端侧的节流机制

  • 拒绝一个执行简单内存查询的请求可能跟实际执行该请求消耗内存差不多(因为这里主要的消耗是在应用层协议解析中,结果的产生部分很简单)。就算在某些情况下,拒绝请求可以节省大量资源,发送这些拒绝回复仍然会消耗一定数量的资源。

  • 当某个客户端检测到最近的请求错误中的一大部分都是由于“配额不足”错误导致时,该客户端开始自行限制请求速度,限制它自己生成请求的数量。超过这个请求数量限制的请求直接在本地回复失败,而不会真正发到网络层。

  • 我们使用一种称为自适应节流的技术来实现客户端节流。具体地说,每个客户端记录过去两分钟内的以下信息:请求数量(requests)应用层代码发出的所有请求的数量总计(指运行于自适应节流系统之上的应用代码)。请求接受数量(accepts)后端任务接受的请求数量。

重要性

  • 重要性重要性(criticality)是另外一个在全局配额和限制机制中比较有用的信息。某个发往后端的请求都会被标记为以下4类中的一种,这说明了请求的重要性。最重要 CRITICAL_PLUS为最重要的请求预留的类型,拒绝这些请求会造成非常严重的用户可见的问题。重要 CRITICAL生产任务发出的默认请求类型。拒绝这些请求也会造成用户可见的问题,但是可能没有CRITICAL_PLUS那么严重。我们要求服务必须为所有的CRITICAL和CRTICAL_PLUS流量配置相应的资源。可丢弃的SHEDDABLE_PLUS这些流量可以容忍某种程度的不可用性。这是批量任务发出的请求的默认值。这些请求通常可以过几分钟,或者几小时之后重试。可丢弃的SHEDDABLE这些流量可能会经常遇到部分不可用情况,偶尔会完全不可用。

  • 我们同时增强了RPC系统,可以自动传递重要性信息。如果后端接收到请求A,在处理过程中发出了请求B 和C给其他后端,请求B和C会使用与A相同的重要性属性。

  • 在过去一段时间内,Google内部的许多系统都逐渐产生了一种与重要性类似的属性,但是通常不能跨服务兼容。通过标准化和在RPC系统中自动传递,我们现在可以在某些特定节点处统一设置重要性属性。这意味着,我们相信依赖的服务在过载情况下可以按正确的优先级来拒绝请求,不论它们处于整个处理栈中多深的位置。于是我们一般在离浏览器或者客户端最近的地方设置优先级—通常在HTTP前端服务器上。同时,我们可以在特殊情况下在处理栈的某处覆盖优先级设置。

资源利用率信号

  • 资源利用率信号我们的任务过载保护是基于资源利用率(utilization)实现的。

  • 在多数情况下,资源利用率仅仅是指目前CPU的消耗程度(目前CPU使用量除以全部预留CPU数量)。但是在某些情况下,同时也会考虑内存的使用率。随着资源利用率的上升,我们开始根据请求的重要性来拒绝一些请求(高重要性的请求对应高阈值)。

处理过载错误

  • 决定何时重试

连接造成的负载

  • 连接造成的负载连接造成的负载是最后一个值得一提的因素。有时候我们仅仅考虑后端处理接收的请求所造成的负载(这也是用QPS来建模负载的一个问题),然而却忽略了其他因素,比如维护一个大型连接池的CPU和内存成本,或者是连接快速变动的成本。这样的问题在小型系统中可以忽略不计,但是在大型RPC系统中很快就会造成问题。

  • 我们的RPC协议需要不活跃的客户端定期执行健康检查。当某个连接空闲一段可配置的时间后,客户端放弃TCP连接,转为UDP健康检查。不幸的是,这种行为会对大量请求率很低的客户端造成问题:健康检查需要比实际处理请求更多的资源。

  • 通过仔细调节连接参数(如,大幅降低健康检查频率)或者动态创建和销毁连接可以优化这些场景。

小结

  • 利用不同的技术手段(确定性算法、加权轮询、客户端侧的节流、用户配额等)更平均地将负载分散到数据中心中。然而这些手段都依赖于在分布式下的状态传递机制。虽然大部分情况下都表现良好,但是在真实情况下,某些应用遇到了一些困难。所以,我们认为保护某个具体任务,防止过载是非常重要的。简单地说:一个后端任务被配置为服务一定程度的流量,不管多少额外流量被指向这个任务,它都应该保证服务质量。

  • 一个常见的错误是认为过载后端应该拒绝和停止接受所有请求。然而,这个假设实际上是与可靠的负载均衡目标相违背的。我们实际上希望客户端可以尽可能地继续接受请求,然后在有可用资源时才处理。某个设计良好的后端程序,基于可靠的负载均衡策略的支持,应该仅仅接受它能处理的请求,而优雅地拒绝其他请求。

个人总结

  • RPC框架设计的重要参考 TODO
  • 如何优雅地重试:https://mp.weixin.qq.com/s/6IkTnUbBlHjM3GM_bT35tA 这个实现类似
    策略 说明
    重试熔断 请求失败 / 成功 > 0.1 时停止重试
    链路上传错误标志 下层重试失败后上传错误标志,上层不再重试
    链路下传重试标志 重试请求特殊标记,下层对重试请求不会重试
    DDL 当剩余时间不够时不再发起重试请求
    框架熔断 微服务框架本身熔断、过载保护等机制也会影响重试效果


第22章 处理连锁故障

  • 如果请求没有成功,以指数型延迟重试。

  • 连锁故障是由于正反馈循环(positive feedback)导致的不断扩大规模的故障。[72]连锁故障可能由于整个系统的一小部分出现故障而引发,进而导致系统其他部分也出现故障。例如,某个服务的一个实例由于过载出现故障,导致其他实例负载升高,从而导致这些实例像多米诺骨牌一样一个一个全部出现故障。

连锁故障产生的原因和如何从设计上避免

  • RPC超时服务器过载时,对客户端RPC的回复会变慢,最终会超过客户端所设置的超时时间。这会导致服务器对请求实际进行的处理都被浪费了,而客户端可能会重试RPC,造成更严重的过载。

  • CPU缓存效率下降CPU使用得越多,任务被分配到多个CPU核心上的几率越大,从而导致CPU核心本地缓存的失效,进而降低CPU处理的效率。

  • 缓存命中率下降可用内存的减少可能会导致应用层缓存的命中率降低,导致向后端发送更多的RPC,可能会导致后端任务过载。

  • 线程线程不足可能会导致错误或者导致健康检查失败。如果服务器为此增加更多线程,这些线程可能会占用更多内存。在极端情况下,线程不足可能会导致进程ID数不足(Linux的进程ID数是有限的)。文件描述符文件描述符(file descriptor)不足可能会导致无法建立网络连接,进而导致健康检查失败。

  • 假设如下场景:1.某Java前端服务器GC参数没有被调优。2.在高负载(但是在期待范围内)情况下,前端由于GC问题导致CPU不足。3.CPU不足导致请求处理变慢。4.同时处理的请求增多导致内存使用上升。5.内存压力上升,同时由于固定内存分配比例的原因,用于缓存的内存数量减少。6.缓存数量降低意味着缓存中键值数量下降,从而导致命中率下降。7.缓存命中率下降导致更多的请求被发往后端进行处理。8.后端服务器CPU或者线程不足。9.CPU不足导致健康检查失败,从而触发了连锁故障。在上述这个复杂情景下,发生故障时可能没有时间仔细分析因果关系。尤其是在前端和后端由不同团队运维时,判断后端崩溃是由于前端缓存命中率下降可能非常困难。

  • 会自动避免产生错误的软件服务器的负载均衡策略会将这个问题加剧—某几个后端任务产生了错误,会导致负载均衡器不再向它们发送请求,进而使得其余软件服务器的负载上升,从而再次触发滚雪球效应。

防止软件服务器过载

  • 防止软件服务器过载下面描述了避免过载的几种策略,大致以优先级排序。使用负载压力测试得出服务器的极限,同时测试过载情况下的失败模式

  • 提供降级结果给用户返回低质量的,但是更容易计算的结果。

  • 在过载情况下主动拒绝请求软件服务器应该保护自己不进入过载崩溃状态。

上层系统应该主动拒绝请求

  • 在反向代理层,通过针对请求的某种特性进行数量限制(如IP地址),来缓解和避免拒绝服务攻击,避免攻击性客户端的影响。● 在负载均衡器层,在服务进入全局过载时主动丢弃请求。

  • 进行容量规划好的容量规划可以降低连锁反应发生的可能性。容量规划应该伴随着性能测试进行,以确定可能导致服务失败的负载程度。

  • 进行容量规划只能减少触发连锁反应的可能性,但是并不能完全避免。当一个计划内或者计划外的事件导致大部分集群容量同时下线时,连锁反应是不可避免的。负载均衡问题、网络分区事件,或者突发性流量增长,都会创造意料之外的负载问题。有些系统可以根据需要动态增加容量,这可能防止过载发生,但是适当地进行容量规划还是必要的。

  • 在这个理想化的情景下,只有在请求速率超过单个请求的处理速率时,请求才会进入队列,这种情况会导致线程池和队列的同时饱和。

  • 对一个流量基本稳定的服务来说,队列长度比线程池大小更小会更好(如 50% 或更小)。当服务处理速度无法跟上请求到达速率时,尽早拒绝请求会更好。

  • 流量抛弃和优雅降级流量抛弃(load shedding)是指在软件服务器临近过载时,主动抛弃一定量的负载。

  • 一种简单的流量抛弃实现方式是根据CPU使用量、内存使用量及队列长度等进行节流。

  • 简化了一些细节,[75]但是这里很好地展现了重试是如何摧毁一个系统的。注意临时性的过载升高,或者使用量的缓慢增加都有可能造成这种情况。

  • 一定要使用随机化的、指数型递增的重试周期。

  • 如果重试不是随机分布在重试窗口里的,那么系统出现的一个小故障(某个网络问题)就可能导致重试请求同时出现,这些请求可能会逐渐放大

  • 限制每个请求的重试次数。不要将请求无限重试。● 考虑使用一个全局重试预算。例如,每个进程每分钟只允许重试60次,如果重试预算耗尽,那么直接将这个请求标记为失败,而不真正发送它。这个策略可以在全局范围内限制住重试造成的影响,容量规划失败可能只是会造成某些请求被丢弃,而不会造成全球性的连锁故障。

  • 从多个视角重新审视该服务,决定是否需要在某个级别上进行重试。这里尤其要避免同时在多个级别上重试导致的放大效应:高层的一个请求可能会造成各层重试次数的乘积数量的请求。如果服务器由于过载不能提供服务,后端、前端、JavaScript层各发送3次重试(总计4次请求),那么一个用户的动作可能会造成对数据库的64 次请求(43)。在数据库由于过载返回错误时,这种重试只会加重问题。

  • 使用明确的返回代码,同时详细考虑每个错误模式应该如何处理。例如,将可重试错误和不可重试错误分开。

  • 设置一个截止时间通常是明智的。不设置截止时间,或者设置一个非常长的截止时间通常会导致某些短暂的、已经消失的问题继续消耗服务器资源,直到重启。

  • 截止时间设置得太长可能会导致框架中的高层级部分由于低层级的问题而持续消耗资源。截止时间设置得太短可能会导致某些比较重型的请求持续失败。恰当的截止时间设置,需要在多个限制条件中选择一个平衡点。

  • 截止时间传递与其在发送RPC给后端服务器时自拟一个截止时间,不如让软件服务器采用截止时间传递和取消传递的策略。

  • 可使用截止时间传递机制,截止时间在整个服务栈的高层设置(如,前端服务器)。由初始请求触发的整个RPC树会设置同样的绝对截止时间。

  • 同时,我们可能还会将传递出去的截止时间减少一点(如几百毫秒),以便将网络传输时间和客户端收到回复之后的处理时间考虑在内。

  • RPC取消的传递可以避免某些泄露情况,如果某个初始RPC设置了一个很长的截止时间,但是底层之间的RPC只有短暂的截止时间,超时失败了。使用简单的截止时间传递可能会导致初始RPC虽然无法继续处理,却继续消耗服务器资源直到超时。

  • 请求延迟的双峰分布(Bimodal)

  • 使用100s的截止时间,5%的请求会消耗5000个线程(50QPS * 100 seconds),但是前端服务器并没有这么多可用线程。忽略副作用,前端也仅能够处理19.6%的请求(1000可用线程 /(5000+95)线程工作),这会造成 80.4%的错误率。因此,不仅 5% 的请求受到了影响(那些由于后端问题不可能成功的请求),实际上大部分的请求都受到了影响。

  • 如果无法完成的请求能够尽早返回一个错误而不是等完整个截止时间,我们就可以避免这个问题。例如,如果一个后端服务器不可用,经常立刻返回一个错误值是最好的,而不是等待这个后端服务器变得可用。如果RPC层支持快速失败的选项,一定要启用它。

  • 假设你的后端要处理来自不同客户端的性能和特征各异的请求,我们可以考虑限制一个客户端只能占用25% 的线程总数,以便在某个异常客户端大量产生负载的情况下提供一些公平性。

慢启动和冷缓存

  • 慢启动和冷缓存进程在刚刚启动之后通常要比稳定状态下处理请求的速度慢一点。慢的原因可能是由下列一个或多个原因导致:必需的初始化过程在接收到第一个请求后,需要跟后端服务器建立连接。运行时性能优化,尤其是JavaJIT 编译过程,热点优化,以及类延迟加载机制。同样的,有些服务器会在缓存没有充满之前效率很低。

  • 过量配备(overprovision)该服务。区分延迟类缓存和容量类缓存是很重要的:当使用延迟类缓存时,服务器可以在空缓存的情况下仍然处理预期的请求负载,但是使用容量类缓存时,该服务将不能够在空缓存下处理请求负载。

保持调用栈永远向下

  • 假设一个用户有一个主后端和一个预先选择好的另外集群中的一个热备后端,主后端在底层出现错误或者延迟上升的情况下将请求代理给热备后端。如果整个系统都处于过载状态,那么从主到副的这种代理可能会增多,会给系统带来更多的负载,因为请求通常要被解析两次,还需要主后端消耗资源等待副后端任务。

  • 在用户的请求路径中最好能够避免使用同层通信—也就是避免通信路径中出现环。

  • 应该由客户端来进行这种通信。例如,如果一个前端需要和后端通信,但是猜错了后端任务,后端不会代理请求给正确的后端,而是通过返回错误使得前端在正确的后端任务上重试它的请求。

连锁故障的触发条件

  • 根据请求数量和可用容量来动态调节任务的同时更新数量可能是个好办法。

  • 在发生连锁故障时,检查最近的改变以及回滚通常是明智的,尤其在这些改变会影响容量或者更改请求特点的情况下。

  • 自然增长在很多情况下,连锁故障不是由于某个特定的服务改变导致的,而是由于使用量的天然上升,却没有进行对应的容量调整导致的。

连锁故障的测试

  • 设计良好的组件应该可以拒绝一小部分请求而继续存活。

解决连锁故障的立即步骤

  • 莎士比亚搜索服务的连锁故障某个关于莎士比亚作品的纪录片在日本上映了,同时特别指明了莎士比亚搜索服务是进行进一步研究的最佳工具。随着这次广播,亚洲数据中心的流量激增,超过了服务容量。服务容量的问题伴随着当时正在进行的大型更新而变得更严重了。幸运的是,一些安全防护措施帮助缓解了可能的故障。生产环境准备评审(production readiness review)流程指出了一些问题,开发团队已经解决。例如,开发者为服务加入了优雅降级功能。当容量不够时,服务不再返回照片,或者不再返回解释故事发生位置的小地图。取决于RPC的目的,超时的RPC要么不再重试(例如,之前提到的图片),要么采用随机指数型延迟进行重试。即使有这些保护措施的存在,任务还是一个接一个地失败了,然后被Borg系统重启,这导致正常工作的任务数量持续减少。由于这个原因,服务监控页面上的某些图表变成了红色,并且SRE收到了紧急警报。为了解决这个问题,SRE临时向亚洲数据中心增加了一些服务容量,调整了莎士比亚搜索任务的任务数量。通过这种操作,成功恢复了亚洲数据中心的莎士比亚搜索服务。接下来,SRE书写了一篇事后总结,详细说明了触发问题的事件,哪些做得好,哪些可以做得更好,和一系列待办事项来避免这个情景重现。例如,在服务过载的情况下,GSLB负载均衡器可以将一些流量导入邻近的数据中心。同时,SRE团队启用了自动伸缩机制,于是任务的数量可以自动跟着流量增长,这样他们就不用再操心这类问题了。

小结

  • 小结当一个系统过载时,某些东西总是要被牺牲掉。一旦一个服务越过了临界点,服务一些用户可见错误,或者低质量结果要比尝试继续服务所有请求要好。理解这些临界点所在,以及超过临界点系统的行为模式,是所有想避免连锁故障的运维人员所必需的。如果不加小心,某些原本为了降低服务背景错误率或者优化稳定状态的改变反而会让服务更容易出现事故。在请求失败的时候重试、负载自动转移、自动杀掉不健康的服务器、增加缓存以提高性能或者降低延迟:这些手段原本都是为了优化正常情况下的服务性能,但是也可能会提高大规模的服务故障的几率。一定要小心评估这些改变,否则灾难就会接踵而至。

第22章 处理连锁故障

  • 如果请求没有成功,以指数型延迟重试。

  • 连锁故障是由于正反馈循环(positive feedback)导致的不断扩大规模的故障。[72]连锁故障可能由于整个系统的一小部分出现故障而引发,进而导致系统其他部分也出现故障。例如,某个服务的一个实例由于过载出现故障,导致其他实例负载升高,从而导致这些实例像多米诺骨牌一样一个一个全部出现故障。

连锁故障产生的原因和如何从设计上避免

  • RPC超时服务器过载时,对客户端RPC的回复会变慢,最终会超过客户端所设置的超时时间。这会导致服务器对请求实际进行的处理都被浪费了,而客户端可能会重试RPC,造成更严重的过载。

  • CPU缓存效率下降CPU使用得越多,任务被分配到多个CPU核心上的几率越大,从而导致CPU核心本地缓存的失效,进而降低CPU处理的效率。

  • 缓存命中率下降可用内存的减少可能会导致应用层缓存的命中率降低,导致向后端发送更多的RPC,可能会导致后端任务过载。

  • 线程线程不足可能会导致错误或者导致健康检查失败。如果服务器为此增加更多线程,这些线程可能会占用更多内存。在极端情况下,线程不足可能会导致进程ID数不足(Linux的进程ID数是有限的)。文件描述符文件描述符(file descriptor)不足可能会导致无法建立网络连接,进而导致健康检查失败。

  • 假设如下场景:1.某Java前端服务器GC参数没有被调优。2.在高负载(但是在期待范围内)情况下,前端由于GC问题导致CPU不足。3.CPU不足导致请求处理变慢。4.同时处理的请求增多导致内存使用上升。5.内存压力上升,同时由于固定内存分配比例的原因,用于缓存的内存数量减少。6.缓存数量降低意味着缓存中键值数量下降,从而导致命中率下降。7.缓存命中率下降导致更多的请求被发往后端进行处理。8.后端服务器CPU或者线程不足。9.CPU不足导致健康检查失败,从而触发了连锁故障。在上述这个复杂情景下,发生故障时可能没有时间仔细分析因果关系。尤其是在前端和后端由不同团队运维时,判断后端崩溃是由于前端缓存命中率下降可能非常困难。

  • 会自动避免产生错误的软件服务器的负载均衡策略会将这个问题加剧—某几个后端任务产生了错误,会导致负载均衡器不再向它们发送请求,进而使得其余软件服务器的负载上升,从而再次触发滚雪球效应。

防止软件服务器过载

  • 防止软件服务器过载下面描述了避免过载的几种策略,大致以优先级排序。使用负载压力测试得出服务器的极限,同时测试过载情况下的失败模式。

  • 提供降级结果给用户返回低质量的,但是更容易计算的结果。

  • 在过载情况下主动拒绝请求软件服务器应该保护自己不进入过载崩溃状态。

上层系统应该主动拒绝请求

  • 在反向代理层,通过针对请求的某种特性进行数量限制(如IP地址),来缓解和避免拒绝服务攻击,避免攻击性客户端的影响。● 在负载均衡器层,在服务进入全局过载时主动丢弃请求。

  • 进行容量规划好的容量规划可以降低连锁反应发生的可能性。容量规划应该伴随着性能测试进行,以确定可能导致服务失败的负载程度。

  • 进行容量规划只能减少触发连锁反应的可能性,但是并不能完全避免。当一个计划内或者计划外的事件导致大部分集群容量同时下线时,连锁反应是不可避免的。负载均衡问题、网络分区事件,或者突发性流量增长,都会创造意料之外的负载问题。有些系统可以根据需要动态增加容量,这可能防止过载发生,但是适当地进行容量规划还是必要的。

  • 在这个理想化的情景下,只有在请求速率超过单个请求的处理速率时,请求才会进入队列,这种情况会导致线程池和队列的同时饱和。

  • 对一个流量基本稳定的服务来说,队列长度比线程池大小更小会更好(如 50% 或更小)。当服务处理速度无法跟上请求到达速率时,尽早拒绝请求会更好。

  • 流量抛弃和优雅降级流量抛弃(load shedding)是指在软件服务器临近过载时,主动抛弃一定量的负载。

  • 一种简单的流量抛弃实现方式是根据CPU使用量、内存使用量及队列长度等进行节流。

  • 简化了一些细节,[75]但是这里很好地展现了重试是如何摧毁一个系统的。注意临时性的过载升高,或者使用量的缓慢增加都有可能造成这种情况。

  • 一定要使用随机化的、指数型递增的重试周期。

  • 如果重试不是随机分布在重试窗口里的,那么系统出现的一个小故障(某个网络问题)就可能导致重试请求同时出现,这些请求可能会逐渐放大

  • 限制每个请求的重试次数。不要将请求无限重试。● 考虑使用一个全局重试预算。例如,每个进程每分钟只允许重试60次,如果重试预算耗尽,那么直接将这个请求标记为失败,而不真正发送它。这个策略可以在全局范围内限制住重试造成的影响,容量规划失败可能只是会造成某些请求被丢弃,而不会造成全球性的连锁故障。

  • 从多个视角重新审视该服务,决定是否需要在某个级别上进行重试。这里尤其要避免同时在多个级别上重试导致的放大效应:高层的一个请求可能会造成各层重试次数的乘积数量的请求。如果服务器由于过载不能提供服务,后端、前端、JavaScript层各发送3次重试(总计4次请求),那么一个用户的动作可能会造成对数据库的64 次请求(43)。在数据库由于过载返回错误时,这种重试只会加重问题。

  • 使用明确的返回代码,同时详细考虑每个错误模式应该如何处理。例如,将可重试错误和不可重试错误分开。

  • 设置一个截止时间通常是明智的。不设置截止时间,或者设置一个非常长的截止时间通常会导致某些短暂的、已经消失的问题继续消耗服务器资源,直到重启。

  • 截止时间设置得太长可能会导致框架中的高层级部分由于低层级的问题而持续消耗资源。截止时间设置得太短可能会导致某些比较重型的请求持续失败。恰当的截止时间设置,需要在多个限制条件中选择一个平衡点。

  • 截止时间传递与其在发送RPC给后端服务器时自拟一个截止时间,不如让软件服务器采用截止时间传递和取消传递的策略。

  • 可使用截止时间传递机制,截止时间在整个服务栈的高层设置(如,前端服务器)。由初始请求触发的整个RPC树会设置同样的绝对截止时间。

  • 同时,我们可能还会将传递出去的截止时间减少一点(如几百毫秒),以便将网络传输时间和客户端收到回复之后的处理时间考虑在内。

  • RPC取消的传递可以避免某些泄露情况,如果某个初始RPC设置了一个很长的截止时间,但是底层之间的RPC只有短暂的截止时间,超时失败了。使用简单的截止时间传递可能会导致初始RPC虽然无法继续处理,却继续消耗服务器资源直到超时。

  • 请求延迟的双峰分布(Bimodal)

  • 使用100s的截止时间,5%的请求会消耗5000个线程(50QPS * 100 seconds),但是前端服务器并没有这么多可用线程。忽略副作用,前端也仅能够处理19.6%的请求(1000可用线程 /(5000+95)线程工作),这会造成 80.4%的错误率。因此,不仅 5% 的请求受到了影响(那些由于后端问题不可能成功的请求),实际上大部分的请求都受到了影响。

  • 如果无法完成的请求能够尽早返回一个错误而不是等完整个截止时间,我们就可以避免这个问题。例如,如果一个后端服务器不可用,经常立刻返回一个错误值是最好的,而不是等待这个后端服务器变得可用。如果RPC层支持快速失败的选项,一定要启用它。

  • 假设你的后端要处理来自不同客户端的性能和特征各异的请求,我们可以考虑限制一个客户端只能占用25% 的线程总数,以便在某个异常客户端大量产生负载的情况下提供一些公平性。

慢启动和冷缓存

  • 慢启动和冷缓存进程在刚刚启动之后通常要比稳定状态下处理请求的速度慢一点。慢的原因可能是由下列一个或多个原因导致:必需的初始化过程在接收到第一个请求后,需要跟后端服务器建立连接。运行时性能优化,尤其是JavaJIT 编译过程,热点优化,以及类延迟加载机制。同样的,有些服务器会在缓存没有充满之前效率很低。

  • 过量配备(overprovision)该服务。区分延迟类缓存和容量类缓存是很重要的:当使用延迟类缓存时,服务器可以在空缓存的情况下仍然处理预期的请求负载,但是使用容量类缓存时,该服务将不能够在空缓存下处理请求负载。

保持调用栈永远向下

  • 假设一个用户有一个主后端和一个预先选择好的另外集群中的一个热备后端,主后端在底层出现错误或者延迟上升的情况下将请求代理给热备后端。如果整个系统都处于过载状态,那么从主到副的这种代理可能会增多,会给系统带来更多的负载,因为请求通常要被解析两次,还需要主后端消耗资源等待副后端任务。

  • 在用户的请求路径中最好能够避免使用同层通信—也就是避免通信路径中出现环。

  • 应该由客户端来进行这种通信。例如,如果一个前端需要和后端通信,但是猜错了后端任务,后端不会代理请求给正确的后端,而是通过返回错误使得前端在正确的后端任务上重试它的请求。

连锁故障的触发条件

  • 根据请求数量和可用容量来动态调节任务的同时更新数量可能是个好办法。

  • 在发生连锁故障时,检查最近的改变以及回滚通常是明智的,尤其在这些改变会影响容量或者更改请求特点的情况下。

  • 自然增长在很多情况下,连锁故障不是由于某个特定的服务改变导致的,而是由于使用量的天然上升,却没有进行对应的容量调整导致的。

连锁故障的测试

  • 设计良好的组件应该可以拒绝一小部分请求而继续存活。

小结

  • 小结当一个系统过载时,某些东西总是要被牺牲掉。一旦一个服务越过了临界点,服务一些用户可见错误,或者低质量结果要比尝试继续服务所有请求要好。理解这些临界点所在,以及超过临界点系统的行为模式,是所有想避免连锁故障的运维人员所必需的。如果不加小心,某些原本为了降低服务背景错误率或者优化稳定状态的改变反而会让服务更容易出现事故。在请求失败的时候重试、负载自动转移、自动杀掉不健康的服务器、增加缓存以提高性能或者降低延迟:这些手段原本都是为了优化正常情况下的服务性能,但是也可能会提高大规模的服务故障的几率。一定要小心评估这些改变,否则灾难就会接踵而至。

个人总结

  1. 使用负载压力测试得出服务器的极限以及过载降级策略
  2. 一定要使用随机化的、指数型递增的重试周期。
  3. 使用明确的返回代码,同时详细考虑每个错误模式应该如何处理。例如,将可重试错误和不可重试错误分开。
  4. 设置一个截止时间通常是明智的。RPC之间传递。
  5. 限制一个客户端对线程总数占有比例,以便在某个异常客户端大量产生负载的情况下提供一些公平性。
  6. 慢启动和冷缓存;区分延迟类缓存和容量类缓存。
  7. 保持调用栈永远向下(减少转发次数和后端资源消耗)。
  8. 设计良好的组件应该可以拒绝一小部分请求而继续存活。

第23章 管理关键状态:利用分布式共识来提高可靠性

  • 在构建可靠的、高可用的系统过程时,我们发现分布式共识系统适合用来维护某个强一致的系统状态

  • 当你需要实现领头人选举(leader election)、关键性共享状态或分布式锁等时,我们建议采用某种正式证明过的、详尽测试过的分布式共识系统来实现。如果不严肃对待这个问题很有可能会导致事故,在更糟的情况下,将造成某种非常难以修复的数据一致性问题,这可能会大大加长系统事故的处理时间。

  • 网络分区问题迟早会发生(光缆被切断,数据包由于拥塞造成丢失或延迟升高,硬件故障,网络组件配置错误等),理解如何构建分布式共识实际上就是理解某个服务应用如何实现强一致性和高可用。由于商业的压力,很多服务都需要很高的可用性,这些应用程序通常都需要对数据访问的强一致性。

  • 最终一致可能会带来意想不到的问题(参见文献[Lu15]),尤其是当时钟漂移(在分布式系统中,这是不可避免的),或者网络分区(参见文献[Kin15])发生的时候。

  • Jeff Shute(参见文献[Shu13])曾经说过,“我们发现开发者通常花费了大量的时间构建一个极为复杂和容易出错的机制来应对最终一致性下可能过时的数据。我们认为对开发者来说这是一种无法接受的负担,数据一致性的问题应该在数据库层解决。”

使用共识系统的动力:分布式系统协调失败

  • 在网络分区情况下—造成问题不一定是由完全分区导致的,而是:● 网络非常慢。● 某些消息可以通过,但是某些消息被丢弃了。● 单方面的节流措施。

  • 如果网络质量真的很差,分布式共识系统也无法正确选举出主实例时,作为工程师可能也没有什么好办法来做出正确决策。

分布式共识是如何工作的

  • 拜占庭式问题指的是当某个进程由于程序Bug或者恶意行为发送不正确的消息的问题,这种问题相对来说处理成本更高,同时也更少见。

  • 严格来讲,在有限时间内解决异步式分布式共识问题是不可能的。正如Dijkstra 的获奖文章—FLP impossibility result(参见文献[Fis85])写的那样,在不稳定的网络条件下,没有任何一种异步式分布式共识算法可以保证一定能够达成共识。在实际操作中,我们通过保证给系统提供足够的健康的副本,以及良好的网络连接状态来保障分布式共识算法在大多数情况下是可以在有限时间内达成共识的。同时整个系统还需要加入随机指数型延迟。

  • 这样,我们可以保障重试不会造成连锁反应,以及本章后面会提到的角斗士(dueling proposers)问题。

  • 这个协议在一个有利环境下能够保障安全性,同时提供足够的冗余度。

  • 最初的分布式共识问题的解决方案是Lamport的Paxos 协议(参见文献[Lam98]),但是也有其他的协议能够解决这个问题,包括 Raft(参见文献[Ong14])、Zab(参见文献[Jun11]),以及 Mencius(参见文献[Mao08])。Paxos本身也有很多变种尝试提升性能(参见文献[Zoo14])。这些变种常常只是在某一个小细节上不同,例如给某个进程一个特殊的领头人角色以简化协议实现等。

分布式共识的系统架构模式

  • 可靠的复制状态机一个复制状态机(replicated state machine,RSM)是一个能在多个进程中用同样顺序执行同样的一组操作的系统。RSM 是一个有用的分布式系统基础组件,可以用来构建数据和配置存储,执行锁机制和领头人选举(接下来会详细描述)。

  • 在RSM系统上进行的操作是通过共识算法来全局排序的。

  • 时间戳在分布式系统中问题非常大,因为在多个物理机器上保证时间同步是不可能的。Spanner(参见文献[Cor12])通过针对最差情况下的不确定性建模,同时在必要时减慢处理速度来解决这个问题。

  • 分布式系统的领头人选举是跟分布式共识等价的问题。复制多份服务并使用一个唯一的领头人(leader)来进行某种类型的工作是很常见的设计。唯一的领头人是一种保证粗粒度互斥性的方法。

分布式共识系统的性能问题

  • 世界上并没有某个性能“最优”的分布式共识和状态机复制算法,因为算法性能通常取决于与系统负载有关的多个因子,以及系统性能的目标和系统的部署情况。

  • 复合式Paxos:消息流过程详解复合式Paxos(Multi-Paxos)协议采用了一个强势领头人(strong leader)进程的概念:除非目前没有选举出任何的领头人进程,或者发生了某种错误,在正常情况下,提议者只需要对满足法定人数的进程发送一次消息就可以保障系统达成共识。这种强势领头人在很多共识协议中都适用,在系统需要传递大量消息的时候非常合适。

  • 很多共识系统使用TCP/IP作为通信协议。TCP/IP是面向连接的,同时针对消息的先进先出(FIFO)顺序提供了强保障。但是在发送任何数据之前,都需要先建立新的TCP/IP连接,也就是一次网络往返需要进行三次握手协议。TCP/IP慢启动机制也限制了连接的初始带宽。常见的TCP/IP窗口大小在4~15KB之间。

  • TCP/IP的慢启动问题对共识组中的进程来说可能不是问题:因为这些进程会互相建立一个连接而且保持这些连接,因为通信将会很频繁。但是,对拥有非常多的客户端的系统来说,可能不能做到让每个客户端都和共识组所有进程都保持一个活动连接。因为TCP/IP需要消耗一定资源,包括文件描述符,以及对应的KeepAlive数据流量。对高度分片、拥有几千个副本的数据存储系统,以及更大规模的客户端来说,这种成本是无法接受的。一个解决方案是使用地域性的代理池,如图23-9所示。该代理池的进程与共识组建立持久的TCP/IP进程,以降低客户端的开销。同时代理池也是包装分片逻辑和负载均衡逻辑,以及共识系统的服务发现逻辑的好地方。

  • 快速Paxos协议:性能优化快速Paxos协议(Fast Paxos,参见文献[Lam06])是Paxos协议的一个变种,意在优化Paxos算法在广域网络中的性能。使用该协议的每个客户端可以直接向组内的接收者们发送Propose消息,而不需要像经典Paxos和复合Paxos那样通过一个领头人进程发送。

  • 稳定的领头人机制上文已经描述过,复合Paxos是如何通过选举一个稳定的领头人进程来提高性能的。Zab(参见文献[Jun11])和Raft(参见文献[Ong14])协议是其他两个例子,它们也通过选举稳定的领头人进程来提高性能。这种方案可以进一步进行读操作优化,因为领头人始终拥有最新信息,但是也存在以下几个问题:● 所有的改变状态的操作都必须经由该领头人,这个要求使得距离领头人进程较远的客户端必须要增加额外的网络延迟。● 领头人进程的外发网络带宽是系统性能的瓶颈(参见文献[Mao08]),因为该领头人的Accept消息包含了每个提议的所有数据,而其他进程发送的消息仅仅包含了交易数字,而没有真正的数据。● 如果领头人进程恰好处于一台有性能问题的机器上,整个系统的吞吐量都会受到影响。几乎在所有关注性能的分布式共识系统设计中,都采用了单个稳定领头人进程机制,或者是某种领头人轮换机制—预先分配给每个副本一个独立的分布式算法编号(通常是对交易编号取模)。使用轮换机制的算法包括Mencius(参见文献[Mao08])和Egalitarian Paxos(参见文献[Mor12a0])。

  • 复合Paxos的信息流的过程在本章前面“复合式Paxos:消息流过程详解”一节中描述过了,但是并没有展示在什么情况下协议要求必须在磁盘中记录状态改变。在进程做任何一个承诺之前都必须进行磁盘写操作。在复合Paxos性能的关键点—协议的第二部分中,这些操作点处于接收者针对某个提议发送Accepted消息之前,以及提议者发送Accept消息之前—因为这条Accept消息其实是一个隐含的Accepted消息

  • 这就意味着在单个共识操作的延迟中,有以下几个操作:● 提议者的一次硬盘写入操作。● 并行消息发送给接收者。● 每个接收者的磁盘写操作(并行)。● 回复消息的传递。

  • 分布式共识算法经常用来实现一个复制状态机。RSM需要保留一份交易日志,以便用于灾难恢复(正如其他数据存储那样)。共识算法的日志可以和RSM的交易日志合为一个。将这两个日志合并可以避免不停地向磁盘上的两个不同位置交替写入(参见文献[Bol11]),也就降低了磁盘寻址操作消耗的时间。这些磁盘于是可以每秒处理更多操作,该系统也可以每秒处理更多操作。

  • 在数据存储系统中,磁盘在维护日志之外还有其他用处:数据通常也是存放于磁盘中的。日志的写操作必须直接刷写到硬盘,但是数据的改变通常可以写入内存缓存中,稍后再写入磁盘,可以进行重新排序以便更有效地写入(参见文献[Bol11])。

分布式共识系统的部署

  • 系统设计者部署共识系统时,最重要的决策在于选择副本的数量和对应的部署位置。

  • 副本的数量一般来说,共识系统都要采用“大多数”的法定过程。也就是说,一组2f+1副本组成的共识组可以同时承受f个副本失败而继续运行(如果需要容忍拜占庭式失败,也就是要能够承受副本返回错误结果的情况,则需要3f+1个副本来承受f个副本失败(参看文献[Cas99]))。针对非拜占庭式失败的情况,最小的副本数量是3—如果仅仅部署两个副本,则不能承受任何一个副本失败)。3个副本可以承受1个副本失败。大部分系统的不可用时间都是由计划内维护造成的

  • 3个副本使该系统可以在1个副本维护时继续工作(这里假设其余两个副本可以承受系统负载)。如果某个非计划内的失败情况在常规维护时发生了,那么共识系统就会不可用。而共识系统的不可用通常是不可接受的,于是在这种情况下,我们需要5个副本同时运行,于是可以允许系统在两个副本不可用的情况下继续运行。如果5个副本中有4个能正常运行,则不需要进行任何人工干预操作,但是如果仅仅只有3个能正常运行,那么我们需要立刻增加一个或者两个额外的副本。

  • 如果共识系统中大部分的副本已经无法访问,以至于无法完成一次法定进程,那么该系统理论上来说已经进入了一个无法恢复的状态。因为至少有一个副本的持久化日志无法访问。在这个状态下,有可能某个操作仅仅只在无法访问的副本上执行了。这时,系统管理员可以强行改变小组成员列表,将新的副本添加到小组中,使用其他可用成员的信息来填充。但是数据丢失的可能性永远存在—这种情况应该全力避免。在灾难处理过程中,系统管理员需要决定是否需要进行这种强制性的重新配置,或者仅仅是等待那些机器恢复可用。当决策做出后,对系统日志的处理(以及对应的监控)就变得非常重要。

  • 针对任何系统的副本数量的考虑都是基于以下几个因素中的一个进行妥协:● 对可靠性的要求● 计划内维护操作的频率● 危险性● 性能● 成本最后的决策每个系统都会不同:每个系统都有不同的可用性服务水平目标;某些组织会更频繁地进行维护性操作;而某些组织使用的硬件成本、质量和可靠性都有所不同。

  • 一个故障域(failure domain)是指系统中可能由于一个故障而同时不可用的一组组件。常见的故障域包括:● 物理机器。● 数据中心中用同一个供电设施的一个机柜。● 数据中心中的数个机柜,使用同一个网络设备连接。● 受单个光纤影响的一个数据中心。● 处于同一个地理区域的一组数据中心,可能被同一个自然灾害所影响。

  • 一般来说,随着副本之间的距离增加,副本之间的网络往返时间也会增加,但是系统能够承受的失败程度也会增加。对多数共识系统来说,网络往返时间的增加会导致操作延迟的增加。

  • 对延迟的敏感性和对灾难的承受程度,每个系统很不一样。在某些共识系统架构下并不需要特别高的吞吐量,或者特别低的延迟:例如,某个共识系统如果只是为了提供成员信息和领头人选举,服务一般不会负载很高,如果共识操作时间仅仅是领头人租约的一小部分时,性能并不是关键点。批处理居多的系统也很少会被延迟所影响:可以通过批处理数量的增加来提高吞吐量。

  • 当处理关键数据时,即使已经有了可靠的共识系统部署在不同的故障域范围内,我们也必须经常将数据备份在其他地方。因为有两个故障域是我们永远无法逃避的:软件本身和系统管理员的人为错误。软件系统中的Bug可能会在罕见情况下浮现造成数据丢失,而错误的系统配置也可能会造成类似情况。而人工操作可能会执行错误的命令,从而造成数据损坏。

  • 当决定副本位置的时候,记得最关键的因素是客户端可见性能:理想情况下,客户端和共识系统之间的RTT应该是最小化的。在广域网络中,无领头人的协议,例如 Mencius和 Egalitarian Paxos可能有一定的性能优势,尤其是当应用程序设计为可以对任意副本进行读操作而不需要进行共识操作时。

  • 向一个采取“大多数”法定仲裁过程系统中增加新的副本可能会降低系统的可用性,如图23-10所示。对ZooKeeper和Chubby来说,一个常见的部署使用5个副本,一个法定仲裁过程需要3个副本参与。整个系统可以在两个副本,也就是40% 不可用的情况下仍然正常工作。当使用6个副本时,仲裁过程需要4个副本:也就是超过33% 的副本不可用就会导致系统无法工作。

  • 为了将系统流量分布得更为均匀,系统设计者可以考虑采用5个副本,将其中2个副本放置于美国中心地带,1个放置于东海岸,另外两个放置于欧洲。这样分布可基本保证共识过程在北美洲的副本上完成,而不需要等待欧洲的回复。而从欧洲起始的共识过程可以仅仅跟美国东海岸的副本完成。东海岸的副本就像两个可能的仲裁组的关键轴,将两个组连接在了一起。

个人总结

  1. 复合式Paxos、快速Paxos协议
  2. 一个常见的部署使用5个副本,一个法定仲裁过程需要3个副本参与。
  3. 分布式共识协议学习参考 TODO

数据完整性的强需求

  • 数据完整性意味着用户可以维持对云服务的访问,用户对数据的访问能力是最重要的,所以这种访问能力的完整性非常重要。

  • 大多数云计算应用都是优化以下5项的某种组合:在线时间、延迟、规模、创新速度和隐私。

  • 为了避免应用程序的数据出现问题,影响到最终用户,一个带外系统、专门负责检查和平衡各种数据存储的系统就很有必要了。

  • 一般来说,公司会采用某种备份策略来“预防”数据丢失。然而,真正应该被关注的重点其实是数据恢复,这是区分备份与存档的重要区别。就像一句流行语说的那样:没有人真的想要备份数据,他们只想恢复数据。

  • 备份与存档最重要的区别就是,备份是可以直接被应用程序重新加载的。因此备份和存档的使用场景非常不同。

  • 存档的目的是将数据长时间安全保存,以满足审核、取证和合规要求。在这些情况下的数据恢复通常不需要满足服务的在线率要求。例如,我们可能需要将财务交易数据保存七年时间。为了达到这个目标,我们可以每个月将累积的审核日志迁移到离线的、异地的长期存档存储系统上。读取和恢复这样的信息可能需要一周时间,在长达一个月的财务审核过程中,这是可以接受的。相对存档,当灾难来临的时候,数据必须从真实的备份中快速恢复,最好能维持服务在线率的要求。否则的话,受影响的用户将由于数据问题无法使用应用程序,直到数据恢复过程完成。

  • 尤其考虑到大多数最新产生的数据直到安全备份结束之前都存在丢失的风险,这就意味着备份(而不是存档)应该至少每天进行一次,或者每小时,甚至更短时间内进行一次。备份应该同时使用完整备份、增量备份,或者甚至是流式持续备份手段。因此,当选择备份策略时,一定要考虑针对某个问题需要的恢复时间,以及可以丢失多少最新数据。

保障数据完整性和可用性:Google SRE的目标

  • 数据完整性是手段,数据可用性是目标数据完整性指的是在其生命周期中,数据的准确性和一致性。从数据被记录的那一刻开始,一直到数据被访问的时候,数据应该保持正确,不会以某种未预知的方式改变。

  • 从用户的角度来看,仅仅保障数据完整性,而没有保障数据的可用性是没有意义的。

  • 交付一个恢复系统,而非备份系统针对系统进行备份是一个长期被忽视、被拖延的系统管理任务。任何人都不会将备份作为一个高优先级任务进行—备份需要长期消耗时间和资源,却不能带来任何现在可见的好处。由于这个原因,在备份策略具体实施中如果出现了某些被忽视的细节问题,大家一般都能理解。甚至有的人说,就像其他针对低可能性的危险的保护措施那样,这样的态度是必然发生的。这里的核心问题其实是,我们没有意识到备份措施防护的问题虽然是不太可能发生的,但却是会造成严重问题的。当该服务的数据不可用时,备份系统对整个服务、整个产品,甚至整个公司来说,都是生死攸关的。

  • 相对于关注没人愿意做的备份任务来说,我们应该通过关注更重要的(但并不是更简单的)恢复任务来鼓励用户进行备份。备份其实就像纳税一样,是一个服务需要持久付出的代价,来保障其数据的可用性。我们不应该强调应该纳多少税,而是应该强调这些税会用来提供什么服务:数据可用性的保障。因此,我们不会强迫团队进行“备份”,而是要求:● 各团队需要为不同的失败场景定义一系列数据可用性SLO。● 各团队需要定期进行演练,以确保他们有能力满足这些SLO。

造成数据丢失的事故类型

  • 根源问题某种无法恢复的用户数据丢失是由以下几个因素造成的:用户行为、管理员的错误、应用程序的Bug、基础设施中的问题、硬件故障和部署区的大型事故。

  • Google根据一次针对19次数据恢复过程的研究得出,最常见的用户可见数据丢失场景是由于数据删除和软件Bug造成的引用完整性问题。

  • 在设计数据完整性保障机制时,必须要认识到复制机制和冗余并不意味着可恢复性。

扩展性问题:全量、增量以及相互竞争的备份和恢复机制

  • 当别人问你“是否有备份”的时候,一个经典的错误回答是“我们有比备份更好的机制—复制机制!”复制机制很重要,包括提高数据的本地性(locality),保护某个部署点单点事故等。但是有很多数据丢失场景是复制机制无法保护的。一个自动同步多个副本的数据存储多半会在你发现问题之前,将损坏的数据记录以及错误的删除动作更新到多个副本上。

Google SRE保障数据完整性的手段

  • 24种数据完整性的事故组合

  • 由于数据丢失类型很多(如上文所述),没有任何一种银弹可以同时保护所有事故类型,我们需要分级进行。分级防护会引入多个层级,随着层级增加,所保护的数据丢失场景也更为罕见。

  • 软删除机制可以大幅度减少支持人员的压力。软删除意味着被删除的数据立刻被标记为已删除,这样除了管理后台代码之外其他代码不可见。

  • 软删除还意味着一旦数据被标记为已删除,在某段时间以后会真的被删除。这个时间的长度,尤其是在有很多短期数据的情况下,取决于某个组织的政策和相关的法律条文、可用的存储空间和存储成本,以及产品的价格和市场定位等。常见的软删除时间是15、30、45或者60天。在Google的经验中,大部分账号劫持和数据完整性问题都会在60天内汇报或者检测到。因此,为软删除保存超过60天的需求可能没有那么强烈。

  • 应用中实现一个回收站机制,作为用户错误的主要防护手段。

  • 就算不考虑成本问题,频繁地进行全量备份也是非常昂贵的。最主要的是,这会给线上服务用户的数据存储造成很大的计算压力,以至于影响服务的扩展性和性能。为了缓解这种压力,我们需要在非峰值时间段进行全量备份,同时在繁忙时间段进行增量备份。

  • 没有任何办法可以100% 确定数据是正确的:虽然我们相信存储系统的实现,但是还是要校验!使这个问题变得更复杂的是,为了从低级数据损坏或者删除场景中恢复数据,我们必须从不同恢复点的不同备份中恢复不同子集的数据。同时,我们还要考虑到在一个不断变化的环境中,代码和数据结构的变化对老的备份数据的影响。

  • 安排一些开发者来开发一套数据校验流水线可能会在短期内降低业务功能开发的速度。然而,在数据校验方面投入的工程资源可以在更长时间内保障其他业务开发可以进行得更快。因为工程师们可以放心数据损坏的Bug没那么容易流入生产环境。和在项目早期引入单元测试效果类似,数据校验流水线可以在整个软件开发过程中起到加速作用。

  • 举一个具体的例子:Gmail拥有一系列数据校验器,每一个校验器都曾经在生产环境中检测到真实的数据完整性问题。Gmail开发者由于知道每个新引入的数据一致性问题都可以在24小时之内被检测到而感到非常安心。这些数据校验器的存在,结合单元测试及回归测试的文化使得Gmail开发者有足够的勇气每周多次修改Gmail生产存储系统的实现。

  • 带外数据校验比较难以正确实现。当校验规则太严格的时候,一个简单的合理的修改就会触发校验逻辑而失败。这样一来,工程师就会抛弃数据校验逻辑。如果规则不够严格,那么就可能漏过一些用户可见的数据问题。为了在两者之间取得恰当的平衡,我们应该仅仅校验那些对用户来说具有毁灭性的数据问题。

  • 大规模部署带外检测器可能成本较高。Gmail计算资源的很大一部分都被用来支持每日数据检测的运行。使这个问题变得更严重的是,校验器本身可能会造成软件服务器缓存命中率的下降,这就会造成用户可见响应速度的下降。为了避免这种问题,Gmail提供了一系列针对校验器的限速机制,同时定期重构这些校验器,以降低磁盘压力。

  • 我们可以保证每天运行的校验器持续寻找毁灭性的问题,而其他更严格的校验器可以以更低频率运行,以便满足延迟和成本的要求。

  • 大部分高速进行的小型开发团队都负担不起设计、构建和维护这样的系统所需的资源。就算强迫他们进行,最后的结果也经常是不可靠、不全面的,以及浪费性、一次性的方案,很快就会被抛弃。因此,最好的办法是单独组织一个中央基础设施组为多个产品和工作组提供一套数据校验框架。该基础设施组负责维护带外数据校验框架,而产品开发组负责维护对应的业务逻辑,以便和他们不断发展的产品保持一致。

  • 数据恢复流程中任何一个环节都有可能出问题,只有建立一个完善的端到端测试才能让人晚上放心睡觉。即使最近刚刚成功进行过一次数据恢复,下次执行时某些步骤仍然可能出问题。

  • 只有真正进行恢复操作之后,才能确定到底能否恢复最新数据。

  • 失败是不可避免的,不去主动寻找这些数据恢复失败的场景等于玩火自焚。只有通过进行测试来主动寻找这些弱点,才能够提前修复,关键时刻才不至于追悔莫及!

SRE的基本理念在数据完整性上的应用

  • 信任但要验证我们依赖的任何API都不可能一直完美工作。不管测试有多么复杂,工程质量有多么高,API都一定会存在某些问题。应该通过使用带外数据检测器来检查数据最关键、最核心的部分,哪怕API语义中说明了这些问题不存在。要记住,完美的理论不代表实现也是完美的。

个人总结

  • 备份与存档最重要的区别就是,备份是可以直接被应用程序重新加载的。
  • 通过使用带外数据检测器来检查数据最关键、最核心的部分。

不完美的机器

  • 不完美的机器从某种意义上讲,人类可以被称为不完美的机器。人会感觉无聊,人的处理器(指思维方式)工作原理不清楚,用户界面(指沟通方式)也不太友好,效率也不高。在工作安排中,能够认识到人的这些缺陷,取长补短是最好的。

  • 分心指数工程师能够被分心的方法太多了。例如,某个名字是Fred的SRE 星期一正常上班,他今天不是on-call,也不负责处理中断性事务。很明显,他希望能够进行他自己的项目工作。倒一杯咖啡,带上“不要打扰”的耳机,开始干活,一切正常,对吗?但是,下列任何一件事情都可能随时发生:● Fred的团队使用随机工单分配系统,某个需要今天处理的工单被分配给了他。● Fred的同事目前负责on-call,收到了一个紧急警报。发出警报的组件Fred最熟悉,所以这个同事过来咨询意见。● 某个用户将某个工单的优先级提高了,这个工单是上周Fred轮值on-call的时候就在处理的。● 某个已经持续了三四周的发布到Fred的时候突然出现了问题,Fred需要停下手中的事情检查发布的状态,回滚这个发布等。● 某个用户过来咨询一个问题,因为Fred为人和善。● 等等。最后结果是

  • 虽然Fred有一整天的时间分配给项目工作,他还是很容易被分心。他可以通过关闭E-mail、关掉IM,或者其他手段来减少一定的干扰。但是某些干扰是政策造成的,以及一些无法避免的责任造成的。我们可以说某种程度的干扰是不可避免的,也是有意为之的。这是正确的:每个人都有甩不掉的Bug,同事之间也会有关系的产生与职责的分配。然而,作为团队来说,是有一些管理方式能使更多的员工不受干扰的。

  • 极化时间为了限制干扰数量,我们应该减少上下文切换(指工作类型、环境等的改变)。某些中断性任务是无法避免的。然而,将工程师当成是可以随时中断、上下文切换没有成本是不正确的。给每次上下文切换加上成本的考虑。在项目工作中,一次20分钟的中断性任务需要进行两次上下文切换,而这种切换会造成数个小时的生产力的丧失。为了避免这种经常性的生产力丧失,我们应该延长每种工作模式的时间,一天甚至半天都可以。这种策略与“挤时间”(参见文献[Gra09])策略工作得很好。

  • 极化时间意味着当每个人来上班时,他们应该清晰地知道自己今天是否只是做项目工作,还是只是做中断性工作。这意味着他们可以更长时间地专注于手上的工作,不会不停地被那些他们本不应该负责的事情所打扰。

  • 一般性建议不论哪种中断性任务,如果中断性任务的量对一个人来说太高,那么应该增加一个人负责。这个概念很显然适用于工单,但是也适用于紧急警报。on-call工程师可以将紧急警报降级为工单,也可以找副on-call来共同处理。

  • 主on-call工程师应该专注于on-call工作。如果目前紧急警报较少,那么一些可以随时放下的工单,或者其他中断性事务应该由on-call人员处理。当某个工程师on-call一周时,这一周他应该完全排除在项目进度之外。如果某个项目非常重要,不能等待一周,那么这个人就不应该参与on-call。管理层应该介入,安排其他人替代on-call。管理层不应该期望员工在on-call的同时还能在项目上有所进展(或者其他高上下文切换成本的活动)。

  • 另外:总有一些清理工作要做,工单数量可能是0,但是总会有需要更新的文档,需要整理的配置文件等。未来的on-call工程师可以从这些活动中受益。他们就不会再来打扰你了!

  • 工单如果目前你是随机分配工单给团队成员,请立刻停止。这样做对团队的时间非常不尊重,和让成员尽可能不被打扰的目标背道而驰。工单处理应该由全职人员负责,同时保证占用合理的时间。如果团队目前的工单主oncall和副on-call都处理不完,那么需要重新架构整个工单的处理流程,保障任何时间都有两个全职员工处理工单。不要将复杂分散到整个团队中去。人不是机器,这样做只会干扰员工,降低他们的工作效率。

  • 持续性的运维工作尽可能地让整个团队共同负责这件事。例如变更发布、配置文件修改等,如果这项工作有非常清晰的流程定义,那么就没有任何理由让任何一个人专门负责这件事情。定义一个发布管理者的角色来由on-call和其他中断性任务处理者来承担。同时应该进行正式化交接过程—这有一定成本,但是却保障了接下来的人不受打扰。负责中断性事务,或者不负责有的时候,某个中断性任务只有某个目前不在值班的成员能够妥善处理。虽然理想情况下,这种情况不应该发生,但是还是偶尔会发生,我们应该尽一切努力避免这种情况。

个人总结

  1. 极化时间,减少干扰;整理必要的文档。
  2. 不要将复杂分散到整个团队中去。人不是机器,这样做只会干扰员工,降低他们的工作效率。

第30章 通过嵌入SRE的方式帮助团队从运维过载中恢复

  • Google的SRE团队的标准政策要求团队在项目研发和被动式工作之间均匀分配时间。在实际工作中,这种平衡可能会被每天工单数量的不断增加而被打乱,甚至持续数月之久。长时间承担非常繁重的Ops类型的工作是非常危险的,团队成员可能会劳累过度,不能在项目工作上取得任何进展。当一个团队被迫花费过多时间解决工单的时候,他们就会减少花在改进服务上的时间,服务的可扩展性和可靠性肯定会随之变差。解决这个问题的一种方式是给处于过载状态的团队临时调入一个SRE。调入该团队后,该SRE应该关注于改善这个团队的行事方式,而不是简单地帮助团队清理积压的工单。通过观察团队的日常工作,提出改善性意见,该SRE可以帮助团队用全新的视角来审视自己的日常工作。这往往是团队本身的成员做不到的。注意,当采用这个方法的时候,只需要调入一名SRE就够了。同时调入两名SRE不一定会产生更好的结果。对那些防御性比较强的团队来说,可能会激发问题的发生。

  • 当你开始构建自己的第一个SRE团队时,采用本章中介绍的方法可以帮助避免团队走入歧途,退化成那种只专注于工单处理的传统运维团队。

  • 在决定采用这种嵌入SRE的方式之前,你应该花一些时间来回顾一下Ben Treynor Sloss的介绍中提到的SRE理念和实践经验,同时应该看一下本书的第6章。

第一阶段:了解服务,了解上下文

  • 第一阶段:了解服务,了解上下文在嵌入团队的过程中,你的主要工作是清楚地表达团队目前的流程和工作习惯对于该服务的可扩展性有利或者有弊的原因。你应该提醒该团队,日益增加的工单不应需要更多的SRE来处理。SRE模型的目标是仅仅在系统复杂度上升的时候才增加新人。你应该尝试引导团队建立健康的工作习惯,这样能够减少花费在工单上的时间。这与指出该服务目前还可以自动化或者进一步简化同样重要。

  • SRE团队陷入Ops模式的原因是过分关注如何快速解决紧急事件而不是如何减少紧急事件的数量。

第二阶段:分享背景知识

  • 第二阶段:分享背景知识在了解团队内部动态和找到痛点之后,你应该通过建立一些最佳实践来为改善状况做准备。例如,建立书写事后总结的机制,辨别琐事的来源,确定每个琐事的最佳解决方案等。书写一个好的事后总结作为示范

  • 你应该将团队遇到的紧急事件分为琐事的和非琐事的。最后,将这个列表交给团队并且清晰地解释哪些紧急事件应该被自动化,而其他的紧急事件则是运维服务必须承担的工作。

第三阶段:主导改变

  • 第三阶段:主导改变保持团队健康是一个持续的过程。正因为此,这不是你可以通过个人英雄主义来解决的问题。为了确保团队在未来可以进行自我调节,我们需要帮助他们建立一个良好的SRE心理模型。

  • 对某项决策进行充分解释的例子:●“我反对最新版本的原因不是因为测试结果有问题,而是因为我们对发布所制定的错误预算已经耗尽了。”● “发布需要能够安全回退,因为我们的SLO非常高。要想达到SLO的要求,平均恢复时间必须非常短,这样在回退之前进行深入的现场调查是不现实的。”对于某项决策不充分的解释的例子:●“我不认为每一个服务器自己生成自身的路由配置是安全的,因为我不信。”这一决策可能是正确的,但是分析论证上是很薄弱的(也没有详细解释)。团队无法知道你心中所想,所以他们很可能模拟你这种不良行为。相反,尝试“[……]不安全,因为在该代码中的一个错误会导致其他服务同时受到影响,同时额外的代码也可能带来减慢回滚的速度的问题。”●“自动化机制在遇到部署不一致的情况时应该放弃进行。”

  • 和之前的例子一样,这一决策可能是正确的,但是是不充分的。相反,尝试“[……]因为我们这里的一个简化假设是全部的改变都通过自动化进行,这种情况的发生说明了有什么东西违反了这一规则。如果这种情况经常发生,我们应该找到和消除造成这种变化的根源。”

  • 提出引导性问题应该提出引导性问题,而非指责性的问题。当你和SRE团队交流时,试着用一种可以鼓励别人思考基本理念的方式来提出问题。

  • 引导性问题的例子:● “我看到任务失败的警报经常发生,但是on-call工程师通常什么都不做。这样会对SLO有什么影响?”● “这个上线过程看起来非常复杂。你知道为什么创建一个新的服务实例需要这么多的配置文件的更新吗?”引导性问题的反例:●“这些旧的、停滞的发布是什么情况?”●“为什么某个组件要做这么多的事情?”

小结

  • 你的最后一个任务是书写一份报告。报告中应该重申你的观点、例子和逻辑推理过程。同时,该报告应该向团队提供一些待办事项,来保证他们会实践你所传授的东西。你应该将报告组织成一份检查报告[12],解释成功路上的每一个重要的决策。大部分的工作现在已经完成了。虽然你的嵌入式工作正式结束,但仍然应该参与设计评审和代码评审。在未来几个月中持续关注这个团队,确保他们正在慢慢提高自己的容量规划能力、紧急事件处理能力和部署发布过程。

第31章 SRE与其他团队的沟通与协作

沟通:生产会议

  • 在两个SRE团队视频会议时,如果一个团队比另外一个大很多,我们建议从较小的团队一边选择主席。更大的一方会自然而然地安静下来,一些不平衡的团队规模所造成的不良影响(这些会由于视频会议的延迟而变得更糟)将得到改善。[14]我们不知道这是否有任何科学依据,但它确实有效。

SRE的内部协作

  • 不管这些角色定义得是否清晰,基本来说,技术负责人是负责团队技术方向的,可以以多种方式进行领导。他可以通过仔细评审每个人的代码,组织进行季度工作汇报,或者是在团队中推进共识的建立来引领团队方向等方式来领导团队。

  • 在Google内部,技术负责人和SRE经理的工作几乎一样,因为我们的SRE经理也具有高度的技术能力。但SRE经理在这之外还有两个特殊责任:绩效管理,以及其他一切其他人不能处理的事情。好的技术负责人、SRE经理以及项目经理组成了一个完整的管理团队,可以很好地组织项目进行讨论设计文档,必要的时候甚至亲自书写代码。

  • 建议只有在不得已的情况下才应该跨地域开发项目,但是这同时也会带来一定的好处。跨地域工作需要更多的沟通,工作完成得也更慢;但是好处是—如果你能协调好的话—则会有更高的产能。单地项目其实也可能会导致其他人不知道你正在做什么,所以这两种做法其实都有一定成本。


第33章 其他行业的实践经验

  • 在本章中,我们会讨论到许多SRE的核心指导思想。为了简化与其他行业最佳实践的比较,我们将这些理念分为4大类:● 灾难预案与演习● 书写事后总结的文化● 自动化与降低日常运维负载● 结构化的、理智的决策

附录A 系统可用性

附录B 生产环境运维过程中的最佳实践

附录C 事故状态文档示范

附录D 事后总结示范

附录E 发布协调检查列表


其他


看见有人不赞同,其实这些问题你们没有遇见过,产品着急上线,出现了一个极低概率的case,修复成本很高。上线后的回报很大,而这个case带来的损失很小,这个时候肯定带着bug上线啊。很多时候产品上线发现商业模式跑不通,这个bug就不用修了,或者迭代过程中才用了另外的实现方式避免了这个问题。错误预算都不一定能用上。
——评论

阿里的要求:可灰度、可监控、可回滚,大厂的思路基本都是一致的。
——评论

产品在不同的发展阶段,可用性目标应该是动态变化的,快速发展期的可用性往往低于成熟稳定期,以保证新功能的发布、试错等顺利进行。——评论

打天下和守天下,需要不同的节奏,优先级不同,方案和指标也不同。——评论

量化风险成本,建立一致性目标,风险共担,减少非必要的沟通扯皮成本,比较好的双赢模式。
对照着看传统运维和开发的独立承担指标就属于零和博弈了,只有政治斗争能化解。——评论

  • 一文帮你理解 Google SRE 体系
  • 我对 SRE 的理解
  • 云原生背景运维转型之 SRE 实践
  • SRE的主要职责是什么?
<1…101112…17>

163 日志
180 标签
RSS
© 2025 Kingson Wu
由 Hexo 强力驱动
|
主题 — NexT.Pisces v5.1.4