在过去几年里,Docker 和容器已成为全球开发界和企业最热门的话题之一。去年秋天发布的 Windows Server 2016 支持 Windows 开发者使用容器,使得这一热门话题再次升温。Windows 和 Docker 是如何走到一起的? 一切始于 2014 年隆重举办的普吉特海湾夏令营,当时 Windows Base 团队启动了一个新项目,最终推出了 Windows Server Containers。这是代码背后的故事,让我们得以瞥见 Windows Server 2016 中一项热门新功能的推出过程。
容器的历史和 Docker 的起源
2013 年,容器迅速引起了 Solomon Hykes 的兴趣,他当时是平台即服务 (PaaS) 创业公司 DotCloud 的 CTO 和创始人。Hykes 选择了一组相对复杂难懂且难用的 Linux 内核功能,并使用他称为 Docker 的开放源代码工具将它们汇集到一起。他并不是有意成为容器方面的佼佼者,只是为了寻找解决方案来解决困扰 DotCloud 的问题: 开发者如何确保代码在服务器上的运行方式与其在工作环境中的一样?
困扰 DotCloud 等服务的真正问题是由客户想要部署大量各种各样的软件应用程序引起的,这些软件根据不同的开发流程、修补程序周期和要求用不同的语言(代码和口头语言)编写而成,包含各种依赖项。硬件虚拟化(即虚拟机 (VM))是可用的最佳工具,但对将软件从开发者笔记本电脑交付到生产环境提出了挑战。要么必须使用开发者的完全配置 VM,但这会导致扩展和管理难以进行;要么必须让部署工具和脚本能够评估 VM 并安装开发者的应用程序,但这样做的灵活性不是很高且不可靠。
Hykes 认为 Docker 可以解决此问题,现在回想起来,他当时的想法确实很有意义。不过,他的公司并不是第一家涉足容器的云服务公司;事实上是另一家云服务公司 Google 根据需要开创了整个概念。2006 年,Google 工程师 Rohit Seth 提交了一个 Linux 内核修补程序,其支持借助他称为 cgroups 功能中的一系列常见资源控制将进程汇集到一起。Seth 开始对此修补程序的描述如下: “商品硬件越来越强大。这样一来,可以在同一平台上运行不同的工作负载,从而更好地利用硬件资源”(bit.ly/2mhatrp)。虽然 cgroups 解决了资源隔离的问题,但并没有解决分发不一致的问题。正因为如此,Docker 不仅使用 cgroups,还使用另一种 Linux 技术,就是命名空间。
命名空间于 2002 年被引入 Linux 内核,提供了一种用于控制进程可以查看的资源以及控制调用哪些资源的方法。命名空间与访问控制大不相同,因为进程甚至不知道资源是否存在或是否在使用它们的某个版本。与此相关的一个简单例子是进程列表:服务器上可以运行 20 个进程,而在命名空间中运行的进程可能只看到其中五个进程,其余的进程均已隐藏。再例如,某进程可能会认为它正从根目录读取内容,而实际上它是从另一独立位置虚拟化而来。易用的开放源代码产品中结合了 cgroups、命名空间和写入时复制 (CoW) 文件系统技术,这成为了 Docker 的基础所在。
到 2013 年中旬,Hykes 及其团队生成的 Docker 工具集开始流行,成为 GitHub 上的热门趋势项目之一,并正式推出了 Docker 品牌。Hykes 的工作重心从 DotCloud 转移到 Docker 上,他最终从 DotCloud 业务中独立出来,但仍是 Docker Inc. 的 CTO。
Windows Server Containers
在 Docker 受到 Linux 界重视的同一时期,Windows Base 团队一直在想方设法隔离执行客户或第三方代码的 Microsoft Azure 服务并提高其效率。代号为“Drawbridge”的 Microsoft 研究原型提供了一种研究渠道;此项目生成了可利用库操作系统的进程隔离容器 (bit.ly/2aCOQxP)。遗憾的是,Drawbridge 在可维护性、性能和应用程序兼容性方面存在限制,导致其不适合作为常规用途解决方案。另一项称为“服务器接收器”的更早原型技术一开始好像还是值得研究的。接收器扩展了现有的 Windows 作业对象方法,此方法可提供进程分组和资源控制(与 Linux 中的 cgroups 类似)(bit.ly/2lK1AbI)。服务器接收器原型新增的是独立执行环境,包括文件系统、注册表和对象命名空间(类似于 Linux 中的命名空间)。由于出现了 VM,服务器接收器原型被搁置多年,但现在被重新定义为 Windows Server Containers 的基础。
服务器接收器原型代码多年无人问津。甚至没有进行编译,更别提运行了。编写此原型代码的初衷是为了证明这项技术在 Windows 中是可行的,但距离生产还有很大一段距离。Windows Base 团队面临的选择是,从头开始还是尝试恢复原型并继续研究下去。我们选择了后者。最初开发此原型时,只安排了一小组的开发者来证明这项技术的可行性,但现在是集 Windows 工程团队的全部力量来开发此项目。来自 Windows 的架构师和工程师们应征而来助阵。存储团队生成了文件系统虚拟化;网络团队生成了网络隔离;内核团队生成了内存管理和计划抽象;等等。
一些大的架构性问题依然存在;尤其是我们该如何处理系统进程? 在 Linux 中,容器通常只运行一个进程,用于将内核中的系统服务与主机和其他容器共享。然而,为了提高可维护性和安全性,Windows 多年来一直在努力将代码从内核移到用户模式进程中。这就给我们的团队带来了一个问题: 要么共享所有系统服务(必须将所有系统服务更改为能够发现容器),要么在每个容器中生成用户模式系统服务的新副本。这是一个很为难的决定,因为我们担心在每个容器中生成所有用户模式服务的新实例会对密度和启动时间造成影响。另一方面,我们还担心在 Windows 中更新所有系统服务不仅操作起来十分复杂,而且还要承担持续成本,无论是我们还是 Windows 外部开发者。最后,我们综合了这两种方法,精选了一组服务使其能够发现容器,但大多数服务仍在每个容器中运行。
对密度造成的影响是最小的,因为容器彼此之间以及与主机共享只读内存,所以只有专用内存是按容器分配的。不过,启动时间是我们面临的巨大挑战,我们也多次质疑了这项决定;当我们在 Build 2015 开发者大会的主题发言阶段首次展示 Windows Server Containers 时,启动时间为几秒钟,主要是由于系统服务的启动时间所致。不过,Windows Server 性能团队也加入进来。他们进行了剖析和分析,并与 Windows 团队协作,共同加快服务运行速度,并减少依赖项,从而提高并行度。这项工作的成果是,不仅容器可以更快启动,实际上还缩短了 Windows 启动时间。(如果你的 Xbox 或 Surface 从去年开始启动速度更快了,这就要归功于容器了。) 在不到一年的时间里,容器启动时间从大约七八秒缩短为亚秒级,而且缩短启动时间的趋势即使今天也仍在延续。
Hyper-V 隔离
关于 Hyper-V 隔离,提出的第一个问题经常是:“容器还没有提供隔离吗? 那我还需要 Hyper-V 做什么?” 容器确实提供隔离,并且在大多数情况下隔离很可能完全够用。不过,存在以下风险:如果攻击者能够入侵内核,就可能会脱离容器,并影响其他容器或主机。由于内核攻击在 Windows 中相对常见(每年通常都会有几次),因此对于在共享的基础结构上使用和执行最终用户或第三方代码的服务(如 Azure 自动化或 Azure 机器学习)而言,风险就太高了,不能只依赖内核隔离。生成和运行这些类型服务的团队要么必须管理所有 VM 的密度和启动成本,要么必须生成不同的安全和隔离技术。需要的是常规用途隔离机制,不仅能够防御入侵者,还提供多租户安全性: 即提供 Hyper-V 隔离的 Windows Server Containers。
我们的团队为推出 Windows Server Containers 而不懈努力,这为生成这些服务的团队提供了很好的体验和管理模型。通过将技术与经过良好测试的 Hyper-V 隔离相结合,我们可以提供所需的安全性。不过,我们需要应对 VM 历来都会带来的启动时间和密度挑战。
与大多数虚拟化平台一样,Hyper-V 旨在通过各种操作系统(无论新旧)来运行来宾。目标是让行为尽可能与硬件类似。为了实现这些目标,大多数虚拟化平台选择的解决方案都是仿真常见硬件。然而,随着虚拟化的普及化,操作系统变得“开放”(经过具体修改,可作为来宾 VM 良好运行),这样也就不再需要那么多的仿真。与此有关的一个良好示例是 Hyper-V 第 2 代 VM,它为了缩短启动时间并提升性能而放弃了仿真,但仍实现了行为与直接在硬件上运行来宾一样的同一目标 (bit.ly/2lPpdAg)。
对于容器,我们的需求和目标都不同: 我们不需要运行任何旧版操作系统,我们明确知道 VM 内将要执行的工作负载,就是容器。因此,我们生成了一种新型 VM,即旨在运行容器的 VM。为了满足快速启动的需求,我们生成了克隆技术。对于传统 VM 来说,这一直都是一项挑战,因为操作系统专精于主机名和标识等方面,所以如果不重启,就无法轻松进行更改。不过,由于容器有自己的主机名和标识,因此这不再是问题。克隆也有助于应对密度挑战,但我们必须深入下去: 我们需要的是内存共享。
共享内存的方法有两种。可以查找多个 VM 共用的内存,并有效删除重复内存(尽管大多数内核中的内存随机化技术让这难以实现)。也可以采用内核使用的方法,将只读(公共)内存与读写(专用)内存区分开来。后一种方法通常要求来宾 VM 中的内存管理程序彼此交互,但这与隔离要求相背。不过,通过更改 VM 启动和访问文件的方式,我们发现了一种方法,使得主机不必信任来宾,来宾也不必相互信任。VM 不是从虚拟硬盘启动和访问文件,而是直接从主机文件系统启动和访问文件。也就是说,主机可以提供相同的只读(公共)内存共享功能。这是将密度扩大多个数量级的关键所在,以便我们可以在未来几年里继续扩大密度。
我们发现 Hyper-V 隔离具有另一价值,即通过为在 Windows 10 计算机上生成容器化应用程序的开发者对容器运行不同的内核,我们仍可以运行服务器内核,以确保这些开发者的应用程序在生产环境和开发计算机中的运行方式相同。因此,在 Windows 10 周年更新中,我们启用了提供 Hyper-V 隔离的 Windows Server Containers,并在用于 Windows 的 Docker 中结合使用 Docker,以便充分利用这一面向开发者的新技术。
Docker 和 Windows Server Containers
还有一个问题仍未解决,就是用户该如何与这种新平台技术进行交互? 在 Linux 界,Docker 备受赞誉,迅速成为约定俗成的容器管理标准。为什么不让用户以同样的方式使用 Windows Server Containers 呢? 在飞到旧金山初次接触到 Docker 的那个秋天,我完全不确定那家公司是否会想到基于 Windows 的容器,也不确定它是否有兴趣在 Windows 基础之上进行生成。让我感到惊讶的是: Solomon 认为 Windows 容器的想法非常棒! 但这家公司是否会在 Windows 基础之上进行生成呢? 那次谈话彻底改变了项目面貌。Solomon 直接回应说:“大家都知道 Docker 是一款开放源代码工具,当然可以添加代码,使之适用于 Windows,我们将会提供帮助。”然后,我们就这样做了。从那以后,Hyper-V 团队的软件工程师 John Howard 就成为了 Docker 项目的维护人员,他实际上已上升到排名第四的全职代码参与者 (bit.ly/2lAmaZX)。图 1 展示了容器和 Docker 在 Windows 和 Linux 上的基本体系结构。
图 1:比较容器和 Docker 在 Windows 和 Linux 上的基本体系结构
综述
四个月前,在 Microsoft Ignite,我们推出了 Windows Server 2016,并宣布扩展了与 Docker 的合作伙伴关系。也就是说,Docker 将为 Windows Server 客户提供其商业版 Docker Engine,而不额外收取任何费用。从那以后,事务应接不暇。Tyco 等客户一直在使用 Docker 和 Windows Server Containers 来彻底改变他们生成软件的方式,并使现有应用程序现代化,所有这些工作都是在同一平台上完成的 (bit.ly/2dWqIFM)。Visual Studio 2017 完全集成了 Windows 和 Linux 容器工具,包括 F5 调试;Visual Studio Code 内置了 Dockerfile 和撰写支持。Azure 和 Amazon 的容器服务都新增了对 Windows Server Containers 的支持,从 Docker Hub 拉取的基于 Windows 的容器映像已超过 1 百万个。为了实现端到端安全性和业务流程,Docker 数据中心为开发者和系统管理员提供了可随时随地生成、交付和运行已分发应用程序的平台。借助 Docker,组织可以将应用程序交付时间从几个月缩短到几分钟、在数据中心和云之间顺畅地移动工作负载,并让计算资源的使用效率提高超过 20 倍。
在接手容器时,我就知道这将是一个高压项目。我知道将要加班到很晚,甚至还要在周末加班,需要付出巨大努力,但这一切都是值得的,因为可以帮助数以百万计的开发者更快速地生成更多应用。我也知道自己会乐在其中,因为有机会真正改变人们在 Windows 上开发和运行应用程序的方式。这比我预想的要更有趣,但同时付出的努力也超出预期,这段经历对我来说是无价的。回忆起此项目初期的一个周末,我正在办公室加班,从窗户向外望去,外面夏日炎炎,这时我在心里对自己说:“我相信并希望人们会使用这款产品…”
Taylor Brown 是 Microsoft Windows 和设备组的项目管理主要负责人。作为 Windows Base 工程团队的一员,他负责制定 Windows Server 开发者策略,尤其是专精于容器技术,包括 Windows Server Containers。Brown 的职业生涯始于 Windows,最开始负责研发 Windows 2003 的 1394/Firewire 堆栈,在加入新组建的虚拟机团队之前,他致力于 Windows Server 2003 SP1 的 ACPI/电源管理。从那以后,他参与了 Microsoft 发布的每一项 VM 技术(包括虚拟 PC、虚拟服务器和每一版 Hyper-V)的开发工作,这让他成为虚拟化技术领域的行业专家。可通过 [email protected] 与他联系。