Kubernetes:微内核的分布式操作系统


如今,Kubernetes已经成为分布式集群管理系统和公有云/私有云的事实标准。实际上,Kubernetes是一个分布式操作系统,它是Google在分布式操作系统领域十余年工程经验和智慧的结晶,而Google一直以来都管理着世界上最大的分布式集群,在分布式操作系统领域的研究和认识领先于全世界。因此,2014年发布的Kubernetes能在短短几年时间内就超越了诸多前辈,大获成功。

作为分布式操作系统,Kubernetes(包括其前代产品Google Borg)的出现远远晚于UNIX、Linux、Windows等著名的单机操作系统,Kubernetes架构设计自然地继承了很多单机操作系统的珍贵遗产,微内核架构就是这些遗产中最重要的一份。在本文接下来的部分,我们将专注于微内核(microkernel)这个概念及其对Kubernetes架构的影响。

什么是微内核?

在介绍微内核的时候,我们有必要同时回顾一下单机操作系统的历史,以理解其价值所在。本章中以「操作系统」指代「单机操作系统」。

UNIX的兴起

电子计算机诞生之后,在上个世纪70年代以前,出现过许许多多的操作系统,DOS、OS/360、Multics是其中的知名代表,这是操作系统领域的拓荒时代。20年来的拓荒孕育出了伟大的成果:随着CPU技术的发展,UNIX于1969年诞生了,这是一个真正意义上的分时操作系统。

借助新的CPU技术的支持,UNIX将软件系统划分为内核(kernel)和用户态程序(userland programs)两部分。内核是一组中断处理程序的集合,把硬件的能力封装为操作系统功能调用(system calls),用户态程序通过系统调用使用硬件功能,用户态程序运行于各自的进程中,所有用户态进程都共享同一个内核,每当系统调用或中断发生,UNIX便陷入(trap)内核,内核执行系统调用,与此同时,内核中的分时调度算法将决定把CPU交给哪个进程,并管理进程的上下文切换。另外,UNIX把(几乎)所有硬件都封装为文件。UNIX还提供了一个特殊的用户态程序shell,供用户直接使用系统,通过内核提供的进程间通信能力,shell让用户可以把一系列应用程序组合起来,处理复杂的需求,作者称这个设计思想为「KISS」(Keep It Simple and Stupld)。UNIX的所有设计思想在当时是都是非常了不起的创举。

UNIX不但自身对业界产生了巨大的直接贡献,还成为所有现代操作系统的蓝本,两位作者Ken Tompson和Dennis Ritchie因此荣获1983年度的图灵奖。

UNIX诞生于贝尔实验室,该实验室属于美国国家电信电报公司(AT&T),见识到UNIX的强大威力之后,AT&T做出了一个看似无私的决定:将UNIX开源(初期只对大学开源),这使得所有现代操作系统得以诞生。虽然AT&T最终被分拆,辉煌不再,但这个决定对人们的贡献绵延至今。在21世纪20年代的今天,无论是MacOS、Windows、Linux,都直接受到UNIX的影响,而iOS来自MacOS,Android来自Linux,因此UNIX的灵魂仍然活在每个人的手机中、活在每个手机App后台的服务中。

此外,UNIX诞生之时,还附送了一项比操作系统本身价值更大的副产品:Dennis Ritchie为开发UNIX设计了C语言,C语言成为了所有流行的现代编程语言的主要设计来源,不仅如此,C语言在其诞生近40年后的今天,仍然是最重要的编程语言之一。

值得一提的是,当时UNIX的主要开放对象是伯克利、卡内基梅隆等研究型大学,文理学院规模较小,没有研究生项目,不属于AT&T的主要开放目标,因此Olivet College毕业的一位小哥未受到UNIX思潮的影响。这位名叫David Cutler的软件天才于1975年在DEC设计了VMS操作系统,VMS和最初的UNIX一样,运行在PDP-11上,但并不是基于UNIX,而是独立设计的。VMS在业界没有掀起大浪,以兼容UNIX告终。后来David Cutler离开DEC,加入微软,在那里谱写了属于他自己的传奇。有趣的是,乔布斯也曾在文理学院就读,看来美国文理学院的学生是不走寻常路的。

微内核的兴起

UNIX「一切皆文件」的设计带来了用户程序设计的很多便利,但它要求所有对硬件的封装都要在内核态,因此内核中模块的bug会让整个系统受到影响,比如说,如果某个设备驱动有内存泄漏,所有使用该设备的用户态进程都会有内存泄漏,如果某个内核模块有安全漏洞,整个系统的安全性将不再可控。

为了解决这类问题,上个世纪70年代,操作系统研究者们开始发展「微内核」的概念,微内核的本质是让操作系统的内核态只保留内存地址管理、线程管理和进程间通讯(IPC)这些基本功能,而把其它功能如文件系统、设备驱动、网络协议栈、GUI系统等都作为单独的服务,这类服务一般是单独的用户态daemon进程。

用户态应用程序通过IPC访问这些服务,从而访问操作系统的全部功能,如此一来,需要陷入内核的系统调用数量将大大减少,系统的模块化更加清晰。同时系统更加健壮,只有内核中的少量系统调用才有权限访问硬件的全部能力,如设备驱动的问题只会影响对应服务,而不是影响整个系统。和micro kernel相对,UNIX的设计被称为monolithic kernel。

UNIX开放后,AT&T继续着版本迭代,而各大学基于AT&T的UNIX开发了很多新的操作系统内核,其中较为知名的有:
  1. BSD,monolithic,由伯克利的传奇人物Bill Joy于1974年发布(据说Bill Joy花三天便完成了BSD内核的第一个版本开发,Bill Joy的作品还包含第一个TCP/IP协议栈、vi、Solaris、SPARK芯片等等)。该内核对业界影响非常之大,后来发展为FreeBSD、OpenBSD、NetBSD等分支。现代操作系统如Solaris、MacOS X、Windows NT对其多有借鉴。
  2. Mach,微内核,由卡内基梅隆大学于1984年发布,主要作者是CMU的两位研究生Avie Tevanian和Rick Rashid。该内核对业界影响也很大,GNU Hurd、MacOS X对其多有借鉴,但该项目本身以失败告终。
  3. MINIX,微内核,由阿姆斯特丹自由大学(Vrije Universiteit Amsterdam)的Andrew Tanenbaum教授于1987年发布。无数计算机系学生通过MINIX及其配套教材掌握了操作系统的设计原理,Linux的初始版本就是基于MINIX复刻的。MINIX虽然著名,但主要用于教学,从未在工业界获得一席之地。


微内核的沉寂

从上世纪90年代至本世纪10年代,UNIX和VMS的后裔们展开了一场混战,从结果来看,微内核的概念虽然美好,但现实非常残酷:
  • MINIX仅限于教学,而基于MINIX设计的Linux是monolithic系统,反而大获成功。Mach对业界影响深远,但本身并未得到大规模应用,其继承者GNU Hurd一直在开发中,从未能应用。
  • Windows的NTOS内核是David Cutler基于他原来在DEC独立设计的系统VMS设计的(VMS和UNIX无关)。NTOS借鉴了微内核的思想和BSD的一些代码,但最终David Cutler决定将所有服务(如GUI)都放到内核态而非用户态,因此Windows NT在软件架构上和微内核一致,而实际运行和monolithic内核一致,被称为hybrid kernel。
  • MacOS X基于NextStep OS设计,NextStep是Avie Tevanian设计的,Avie Tevanian是Mach的主要设计者,博士毕业后,盖茨和乔布斯都邀请过他,他去了Next,他在CMU的好友Rick Rashid则去微软作为David Cutler的首席助手,据说Avie Tevanian在Next每天用计算器算自己没去微软而损失的股票增值。跟乔布斯回到苹果后,Avie基于NextStep和BSD的代码设计了OS X,巧的是,OS X也采用了hybrid kernel的架构,最终大获成功,还能在PowerPC和x86两种指令架构间无缝切换。


在几位操作系统技术巨擎中,除Linus Torvalds外,无论是David Cutler和Andrew Tanenbaum,还是Avie Tevanian和Rick Rashid,都是微内核架构的领袖级人物,但最终他们都没有将微内核彻底落地,这是有原因的。

微内核操作系统访问系统服务的效率比monolithic操作系统要低得多,举例而言,在Linux中,系统调用(比如open)只要陷入内核一次,也就是先切换CPU到高权限模式,再切回低权限模式。如果在一个微内核操作系统中,用户调用open就需要先拼装一条IPC请求消息,发送给对应的文件系统服务进程,随后从文件系统服务进程获取IPC响应消息并解包,拿到调用结果,这样一来,消息带来的数据拷贝和进程上下文切换都会带来很多开销。消息需要拷贝是因为用户态进程间不能相互访问内存地址,而内核的代码可以访问任何用户态进程的任何内存地址。

正是因为性能原因,OS X和Windows都选择了hybrid kernel的架构,NTOS甚至在内核中集成了GUI子系统,以带来更好的用户体验。简单来说,在电脑性能不佳的情况下,我们会发现Windows的鼠标箭头更加“跟手”,即使系统接近死机,Windows系统的鼠标箭头仍然可以活动。Windows XP能在Windows 98这样「珠玉在前」的上代产品后获得更大的成功,和NTOS对性能的密切关注是分不开的,相比之下,苹果固然在1980年代中期就有初代Machintosh这样的壮举,但因为乔布斯无法说服销售团队换一根更强的内存条,因此初代Mac的性能较差,运行程序非常之慢,未能获得应得的蓝海成功。

Kubernetes和微内核

性能问题对单机操作系统来说可能是至关重要的,但对分布式操作系统并非如此,分布式操作系统作为「幕后功臣」,不需要直接面对用户,而单机性能上的小小损失可以用更多机器来弥补,在这个前提下,更好的架构往往更加重要。

Borg的诞生

在单机操作系统大战快要分出胜负之时,Google这家行业新宠正准备IPO,用现在的话来说,Google那时是一家「小巨头」:已经初露锋芒,不容小觑,但巨头们彼时正陷入战争泥潭,无暇顾及之。2003年,为了更好地支持新版本的搜索引擎(基于MapReduce),使其能服务好亿万用户,Google开始了大规模集群管理系统的开发,这个系统叫做Borg,它的目标是管理以万台为单位的计算机集群。虽然刚开始只有3、4个人的小团队,但Borg还是跟上了Google的飞速发展,证明了它的潜力,最终Google的全部机器都由Borg管理,MapReduce、Pregel等著名系统都建立于Borg之上。从操作系统的角度来看,Borg是一个monolithic系统,任何对系统的功能升级都需要深入到Borg底层代码来修改支持。在Google这样成熟的技术型公司中,有很多优秀的工程师,因此这个问题在内部系统中并不算严重。但如果是公有云,必然要接入许多第三方应用的需求,一家公司的工程师团队再强大,也无法把业界所有其他系统都接入Borg,这时系统的可扩展性将非常重要。

在2010年左右,随着Google中国部门的撤销,很多优秀的Google工程师加入了BAT等中国公司,其中一部分加入了腾讯搜搜。这些前Googler加入腾讯后,复刻了Google的许多系统,技术上也很出色,其中Borg的复制品叫做TBorg,后来改名为Torca。Torca在搜搜的广告业务中起到了非常重要的作用,后来由于腾讯业务调整,搜搜与搜狗合并,Torca在腾讯内部失去用户,逐渐停止了维护。

在Borg上线几年后,Google意识到monolithic架构的问题和瓶颈,于是又一支小团队开始了Omega系统的研发。Omega系统继承的是微内核的思想,新的功能升级几乎不需修改底层代码就能完成,它比Borg更加灵活,有更好的伸缩性。但因为当时Google的全部系统已经搭建在Borg之上了,由于Borg的monolithic特性,MapReduce等系统都紧密绑定到Borg核心代码,不但无缝迁移到Omega系统是不可能的,迁移还要花巨大的人力、时间和试错成本,因此即使核心成员坚持不懈地推动,Omega系统在Google仍未能取得成功。

有趣的是,Omega项目的核心成员之一Brendan Burns的职业轨迹和操作系统领域的大前辈David Cutler有不少相似之处。
  • 他们同样毕业于文理学院:David Cutler毕业于Olivet College,Brendan Burns毕业于Williams College。
  • 他们同样在毕业后加入了一家传统行业的巨头:David Cutler毕业后加入杜邦,Brendan Burns毕业后加入汤姆森金融。
  • 正如教父所说,一个男人只能有一种命运,Cutler和Burns在这两家传统巨头学会了写代码,也许就是在那时,他们发现了自己在软件上的天分,发现了自己的命运是构建新一代操作系统。因此他们同样在第二份工作中选择了当时最炙手可热的科技巨头:David Cutler加入DEC,Brendan Burns加入Google。
  • 他们同样在微软到达了职业生涯的顶峰:Brendan Burns现如今已是微软的Corporate VP,而David Cutler老爷子早已是微软唯一的Senior Technical Fellow,据传微软甚至有条规定,Cutler的技术职级必须是全公司最高的,任何人升到Cutler的level,Cutler就自动升一级。


Kubernetes的诞生

在单机操作系统时代,hybrid kernel盛行一时,这证明了微内核在软件架构上的成功,但因为性能问题,又没有任何一个成功的内核采用「纯粹的」微内核架构,因此微内核从实用角度上来说是失败的。

和单机操作系统时代中微内核架构的失败原因不同,Omega在Google公司内部的失败和性能问题无关,只是历史遗留问题的影响。对开源社区和大部分公司来说,尚无能和Borg相媲美的系统,也没有历史负担,因此几年后,Google决定开源Omega这一超越Borg的新一代分布式操作系统,将其命名为Kubernetes。

为了介绍清楚Kubernetes和微内核的关系,以及微内核架构为Kubernetes带来的优势,这里有必要引入一些技术细节。

上文中提到,单机操作系统的系统调用需要「陷入」内核,所谓的陷入(trap)也叫做中断(interrupt),无论内核是什么类型,单机操作系统都需要在启动时将系统调用注册到内存中的一个区域里,这个区域叫做中断向量(Interrupt Vector)或中断描述符表(IDT,Interrupt Descriptor Table)。当然,现代操作系统的中断处理非常复杂,系统调用也很多,因此除了IDT之外,还需要一张系统调用表(SCV,System Call Vector),系统调用通过一个统一的中断入口(如INT 80)调用某个中断处理程序,由这个中断处理程序通过SCV把系统调用分发给内核中不同的函数代码。因此SCV在操作系统中的位置和在星际争霸中的位置同样重要。对微内核架构来说,除了SCV中的系统调用之外,用户态服务提供什么样的系统能力,同样需要注册到某个区域。

与此类似,Kubernetes这样的分布式操作系统对外提供服务是通过API的形式,分布式操作系统本身提供的API相当于单机操作系统的系统调用,每个API也需要能够注册到某个位置。对Kubernetes来说,API会注册到ectd里。Kubernetes本身提供的相当于系统调用的那些API,通过名为Controller的组件来支持,由开发者为Kubernetes提供的新的API,则通过Operator来支持,Operator本身和Controller基于同一套机制开发。这和微内核架构的思想一脉相承:Controller相当于内核态中运行的服务,提供线程、进程管理和调度算法等核心能力,Operator则相当于微内核架构中GUI、文件系统、打印机等服务,在用户态运行。

因此,Kubernetes的工作机制和单机操作系统也较为相似,etcd提供一个watch机制,Controller和Operator需要指定自己watch哪些内容,并告诉etcd,这相当于是微内核架构在IDT或SCV中注册系统调用的过程。

以Argo为例,Argo是个Operator,提供在Kubernetes中执行一个DAG工作流的能力。用户在使用kubectl命令提交Argo任务时,实际是让kubectl将Argo的yaml提交给Kubernetes的API Server,API Server将yaml中的Key-Value数据写入etcd,etcd将会提醒那些正在watch指定Key的服务。在我们的例子中,这个服务也就是Argo。这正像是微内核架构里用户进程请求用户态服务的过程。

Argo得到etcd watch的http请求,去etcd读出yaml中的数据并解析, 然后知道要去启动什么容器,并通过API要求Kubernetes启动相应的容器。Kubernetes scheduler是一个Controller,在收到启动容器请求后,分配资源,启动容器。这是微内核架构中用户进程通过系统调用启动另一个进程的过程。

当然,Kubernetes和单机操作系统也有不同之处:Kubernetes没有明确的「陷入」过程,而微内核架构的单机操作系统在访问系统调用时需要陷入,在访问用户态服务时则不需要陷入。但是,Kubernetes可以为不同的服务设置不同的权限,这一点在一定程度上类似于单机操作系统中内核态和用户态的CPU权限的区别。

微内核在架构上的优势在Kubernetes中显露无遗:在Borg中,开发者想要添加新的子系统是非常复杂的,往往需要修改Borg底层代码,而新系统也因此会绑定到Borg上。而对Kubernetes来说,开发者只需要基于Kubernetes提供的SDK实现一个Operator,就能够添加一组新的API,而不需要关注Kubernetes的底层代码。Argo、Kubeflow都是Operator的应用。任何已有软件都可以方便地通过Operator机制集成到Kubernetes中,因而Kubernetes非常适合作为公有云的底层分布式操作系统,正因如此,Kubernetes在2014年年中发布,经过2015年一年的成长,在2016年便成为业界主流,对于没有历史负担的公司,也将Kubernetes作为内部云的底层系统使用。

尾声

在这篇文章中,我们介绍了单机操作系统的发展简史,介绍了微内核架构在这个历史进程中从兴起到衰落的过程,也介绍了微内核架构在Kubernetes中重新焕发生机的过程。总的来说,显著超前于时代的技术虽然未必能在被提出的时代取得成功,但一定会在多年后,在时代跟上来之后,拿回属于自己的荣耀。微内核架构在单机操作系统的时代和云计算的时代的不同遭遇证明了这一点,深度学习在低算力时代和高算力时代的不同遭遇也证明了这一点。

值得一提的是,在Kubernetes之后,Google推出了Fuchsia作为Android可能的替代品。而Fuchsia基于Zircon内核开发,Zircon基于C++开发,正是微内核架构。在算力井喷的现代,除了在分布式操作系统领域,微内核能否也在手机/物联网操作系统领域复兴,让我们拭目以待。

本文内容主要基于王益最近给SQLFlowElasticDL团队的分享。沈凋墨和章海涛、武毅、闫旭、张科等一起总结。这个总结解释了 SQLFlow作为一个Kubernetes-native的分布式编译器的设计思路基础,也解释了ElasticDL只针对Kubernetes平台做分布式AI的原因。本文作者中包括百度Paddle EDL的作者。Paddle EDL是基于PaddlePaddle和Kubernetes的分布式计算框架,于2018年贡献给Linux Foundation。

原文链接:https://zhuanlan.zhihu.com/p/148782297

0 个评论

要回复文章请先登录注册