Hannes Mehnert 谈 MirageOS 和 OCaml:“函数式编程是为了更好地维护代码和理解程序”
简介
我们的后端工程师 Pavel Argentov 曾前往摩洛哥的马拉喀什,参加于 2020 年 3 月 13 日至 19 日举行的第九届 MirageOS 静修旅行。 该活动的目的是将经验丰富的 MirageOS 用户和新用户聚集在一起,以协作和同步各种 MirageOS 子项目,启动新的子项目,并帮助彼此修复错误。
MirageOS 是一个库操作系统,可为各种云计算和移动平台上的安全、高性能网络应用构建 unikernel。 代码可以在 Linux 或 Mac OS X 上开发,然后将其编译为在 Xen 或 KVM 管理程序下运行的完全独立的专用 unikernel。
在这次活动中,Pavel 与 MirageOS 的联合作者,同时也是这次活动的主持人 Hannes Mehnert 谈论了他在 MirageOS 和 OCaml 方面的工作。 他向我们详细介绍了他对 MirageOS 的贡献以及加入该项目的原因。 他还解释了函数式编程的好处以及最初被它所吸引的原因。 此外,他还分析了 MirageOS 和 OCaml 的潜力和局限性,并向我们介绍了一些新的发展和未来情况的信息。 我们在下面附上了完整的采访记录,因此您可以直接通过最佳来源获得最新信息。
采访
Pavel: 我想我们应该从 OCaml 开始谈起。 你是如何以及为什么开始使用 OCaml 的?
Hannes: S六年前,当我刚刚完成软件形式化验证的博士学位时, 我习惯随机选取一些已经开发的软件,应用一些规范,然后编写一些验算,证实该程序确实是正确的。 事实证明,这个过程是相当复杂和费力的,因为大家普遍在使用共享可变状态。 在相当长的一段时间里,我对系统编程非常感兴趣,这通常意味着要使用 C 语言并用它来编写操作系统。 但是考虑到我的语义学背景,我更希望使用高级语言来编写操作系统。 因此,在完成博士学位后,我和我的朋友 David Kaloper 偶然发现了 MirageOS。
MirageOS 是用 OCaml 编写的,OCaml 是一种多范式语言,它有一个模块系统,用于函数式编程。 这意味着你可以避免共享可变状态,并实际验证操作系统上的程序。 我大约是在六年前开始接触 MirageOS 的工作,当时它在某种程度上已经是可用状态了,我所做的第一个贡献是 TLS 堆栈和加密算法。
Pavel: MirageOS 该如何使用,我们能从中得到什么?
Hannes: MirageOS 最初是一个研究项目。 我们当时有一个原型,也有关于如何对操作系统使用不同风格的编程的想法。 我在安全性方面的背景也很深厚,这也是我要为 MirageOS 做出贡献并尝试将其投入生产的主要动机。 从安全的角度来看,MirageOS 的可变状态较少,你可以使用 TLS 运行 HTTPS 或 Web 服务器。 而且你的代码也更少,这意味着出现的错误和使用的资源也更少,因为如果你不用运行那么多代码,你就不会浪费那么多 CPU 周期和那么多内存。
Pavel: 我们来谈谈 TLS 吧。 很多时候,你可能会遇到硬件方面的限制,而且因为加密算法很慢,导致一切操作都变慢。 OCaml 是如何解决这个问题的,它是否完全解决了速度方面的问题? 你可以通过 OCaml 快速编码吗?
Hannes: 是的,OCaml 本身的运行时非常快。 我们有一个垃圾回收器(内存管理器),它的回收速度非常快。 问题基本上就是 OCaml 是否允许你编写一个足够好的接口来正确传递参数,而且不会浪费太多的 CPU 时间。 事实证明,它的速度足够快。 我很乐意使用合理的编程语言,而不是低级的微型汇编器。
TLS 的另一方面是握手。 它是非对称加密,为了加快速度,我们使用了一个名为 GMP/GNU Multi-Precision 的库。 在 OCaml 中,我们只有针对这方面的绑定,但是它们是例外。 通常我们尽量不编写绑定,也不使用太多的 C 代码。 大多数解密和加密的复杂部分仍然在 OCaml 中,而不是在 C 中。
Pavel: Haskell 程序员和其他高级语言程序员对垃圾回收器的性能感到担忧,说它会拖慢进度。 在 Haskell 中,他们无法编写任何形式的“软实时应用”。 你觉得 OCaml 能做到这一点吗? OCaml 的垃圾回收器是否快到可以在对速度有要求的用例中执行?
Hannes: 是,我认为是这样。 Haskell 具有完全不同的运行时,默认会进行惰性求值 。 而 OCaml 是严格求值,我们需要在运行过程中进行计算。 垃圾回收器针对工作负载进行了很好的调整,它的速度真的非常快,而且我相信,在 OCaml 中,“软实时应用”是可行的。
Pavel: 据我所知,“Unikernel” 这个概念已经不再是 OCaml 所独有的了。 Unikernel 的发展历程是什么样的? 这个概念最开始的名称与现在相比是否有所不同? 人们究竟是怎么想到 Unikernel 的?
Hannes: 我想这一切都始于剑桥大学,从关于所谓的 Exokernel 的那篇理论论文开始。 人们需要一种工具,一个以任务为中心、资源消耗较少、易于编写、易于适应的系统。
Pavel: 嗯。 据我所知,MirageOS 使用的是 Lwt 库。 如果你有一个 DNS 服务器,它必须同时在多个方向上快速响应,那 Lwt 的性能是否足够承担一些合理的负载? 它的工作速度够快吗?
Hannes: 我认为它的效果相当不错。 MirageOS 有一个很好的应用示例,那就是被集成到 Qubes OS 中的防火墙。 Qubes OS 是一种使用 Xen 的操作系统。 举例来说,Qubes OS 的目标就是让你的邮件应用与 PDF 渲染器分离。 因此,如果你收到的电子邮件中包含恶意 PDF 文件,则你在查看该邮件后,它就无法访问你所有的邮件。 相反,你可以保存 PDF 并将它推送到其他虚拟机。 而那台不同的虚拟机拥有运行 PDF 渲染器的代码。
如此一来,该 PDF 只能在隔离的环境中打开和呈现。 MirageOS 非常适用于这种情况,因为它的内存占用要小得多。 我们只需要把防火墙设置成 Qubes OS 环境中其中一台虚拟机里面的其中一个组件,然后就能接收其他有权访问互联网的虚拟机发送的数据包。 MirageOS unikernel 作为路由器,会对数据包进行路由。
Pavel: 你刚说到了关于 MirageOS 内存消耗的问题。 它到底有多少内存? 下限或上限是多少? 我听说 MirageOS 不能配置大于 1 GB 的内存。 真有这种限制吗?
Hannes: 嗯,目前是这样。 OCaml 运行时和 MirageOS unikernel 所需的最小内存量为 10 MB,目前的上限为 1 GB 内存。 但如果你需要更多内存,基本上可以很容易调整。 比如我的 DNS 服务,需要大约 14-24 MB 的内存。 这并不是数百万条记录,而是数百条记录而已。 而我运行的 Web 服务通常有 32-128 MB 的内存。 这足以存储数据。
Pavel: 你用过 Irmin 数据存储吗? 据我所知,它有点像 Git,而且它是用 OCaml 编写的用于 MirageOS 的唯一数据存储。
Hannes: 是的。 Irmin 是一个可分支、不可变的存储。 我通常不直接使用 Irmin,而是通过 Git 实现来使用,也就是在后台使用 Irmin。 例如,我的 DNS 服务器会将它的区域文件存储在远程 Git 存储库中,它会获取存储库,将其克隆到内存中,然后从那里提供数据。 2019 年,Irmin 发布了一个重要版本,Irmin 2.0。
Pavel: 好的,那我们稍微换个话题,聊聊这次聚会的形式。 你能跟我们讲讲关于 MirageOS 静修旅行的情况吗? 你是怎么想到这个主意的?
Hannes: 我从不同的会议和 OpenBSD 黑客马拉松中获得了很多启发。 基本思想就是将一群很棒的人聚集起来。 大家相聚在一个很棒的地方,天气、食物、阳光都很好,你可以享受当下的环境。 对我来说,最重要的就是大家整天都待在一起,互相交流。 没有严格的时间表。 大家每天都更新一轮信息,了解谁做了什么,谁对什么感兴趣,以及谁在什么特定的点上卡住了。 其他人也可以加入进来,为他们提供解决方案。 大家可以随机聚在一起讨论问题和解决方案,其他人也可以只忙着写一些代码。
一方面,我想让那些社区里的名人来到这里,谈一谈他们对于不同的库和生态系统的经验和想法,讨论生态系统发生的根本变化。 另一方面,我也很希望能有一些新朋友加入进来,以便了解一些新的想法,还可以让他们真正融入这个团队,让他们为 OCaml 和 MirageOS 项目编写一些程序,从而壮大这个社区。 这个活动不仅仅是针对那些已经知道 MirageOS 或者有好几年 OCaml 使用经验的人们,所有愿意去马拉喀什旅行的人都可以参加这个活动。
Pavel: 太棒了! 你认为函数式编程会影响程序员的思维方式吗? 我刚开始编写 OCaml 代码时,才明白有些类型是可以转换的。 这让我开始思考我所处理的数据的类型和含义。 我知道在欧洲,函数式编程是基础阶段编程研究的一部分。 据我所知,在俄罗斯,大多数学生学习编程都是从命令式技术开始的,他们几乎从未脱离过这个范畴。
Hannes: 是的。 在编写实际代码之前,我考虑了许多关于类型的问题,也应用了不少类型驱动的开发。 所以,当我用函数式语言进行编程时,我首先会考虑类型应该是什么样子。 在获得了正确形状的类型后,所有实现都会变得更加容易。 对我来说,这还涉及到函数式编程中的代码维护和本地化的程序理解。 而且我认为,如果使用函数式语言编写代码,不滥用语法糖和功能,那么在五年后,这种代码与使用命令式语言开发,且一个函数动辄数百行的代码相比,更容易让人理解。 我尽量让函数简洁易懂。 是的,函数式编程会让你的大脑来思考程序。
Pavel: 我看到单子正在被不同的语言所采用。 它们存在于 Ruby 和 C++ 中。 它仅仅是将一些学术知识应用于日常编程的一种方式吗?
Hannes:我认为它是一种切实可行的手段,但如果你自己从没接触过单子的话,你是很难理解这个概念的。 要向一个新手命令式程序员解释单子是非常困难的。 我们仍然在 MirageOS 和 OCaml 中使用单子,但希望随着多核分支在今年某个时候成为 OCaml 运行时的一部分,我们会克服这个问题。
Pavel: 现在我们来谈谈开源。 我们所谈到的一切都是开源的。 有一种观点认为,只有投入足够的资金,技术才能成功。 虽然开源耗费了我们的精力和时间,但它并没有真正带来收入。 当你在一个开放的社区传播一些新的技术时,你迟早会达成开源合作的想法。 在你看来,开源有多重要?
Hannes: 我认为开源是一个至关重要的因素。 实际上,我们所做的大部分工作都是在开发 OCaml 库,然后将其用于 MirageOS unikernel。 每个人都应该能够自由地对它们进行混合和匹配。 我在编写 TLS 堆栈或 DNS 实现时,我强烈希望开源所有内容,因为这样其他人就可以重新利用它们。 我很喜欢编写软件,如果有人使用我的软件来盈利,无论是个人还是公司,我都很高兴。 我对此没有意见。
在 MirageOS 中,大多数软件都已获得 BSD 许可,因此每个人都可以随心所欲地使用。 我认为获得许可是非常重要的。 每个人都能理解 GPL(通用公共许可证),但它有大量的文本页面,而 BSD 只有两到三个段落,通常 25 行文本就能写完。 而且如果你想说服某个行业使用你的某些软件,你最好使用授权许可证。 这样你会更容易说服他们,因为,如果你使用 GPL 许可证,要说服律师同意你的观点可能会很难。 例如,在 MirageOS 中,我们有 IBM Research 贡献的代码,我们设法说服他们使用授权许可证,这并非易事,因为律师通常希望坚持使用商标。
Pavel: 我听说你在一家从事 unikernel 开发的公司工作。 开发一项不挣钱的技术是一种什么体验,比如成熟的、知名的命令式编程?
Hannes: 我在一家名为 Robur 的非营利性公司工作。 我们通过资助、捐赠和商业合同来增强 MirageOS 生态系统并开发 unikernel。
去年,我们从公众那里筹到了一些资金。 我们从德国和欧盟获得了一些拨款,来开发某些应用,例如 OpenVPN Gateway,目前,我们正利用从欧盟获得的资金来开发 DNSmasq,这是每个人所使用的网络中的一个关键组成部分。 这种感觉是相当棒的。
Pavel: 随着时间的推移,MirageOS 的开发速度如何? 它是否处于快速开发中,而且加入了新的功能?
Hannes: 开发过程总是缓慢的,但我们也做了很多工作。 我们努力摆脱技术债,并适应现代的构建系统,这有时会比其他项目更费时间。 在功能方面,主要是正在开发的新库。 我们简要谈了一下 Irmin DataStore,它的 2.0 版本是一个重要的里程碑,直到去年才实现。 还有即将推出的 TLS 1.3 堆栈。 至于 MirageOS,我们现在正在向 4.0 版本迈进,它肯定会从根本上改善开发体验,摆脱旧的“OCamlbuild”,取而代之的是一个名为“Dune”的新构建系统,其特点是增量构建。
Pavel: 好的,我们的采访就要结束了,请你鼓励一下那些可能会学习 MirageOS、使用 OCaml 的开发人员,让他们不要再害怕在理论上烧脑的函数式编程。 你会如何鼓励大家?
Hannes: 函数式编程的优点是你可以控制相当复杂的代码。 在函数式编程中,如果你发现了一个高级错误,你可以在一个周末内将其调试到最低级别并进行修复,而在普通操作系统上,由于代码库和涉及的库的大小,这样做是不可能的。
你可以控制整个堆栈。 它是全栈开发,从网卡级别到业务逻辑再到实际应用运行。
Evrone 一直积极关注着新技术的发展,并采用具有创新性的新工具和方法。 因此我们能够利用最佳资源为客户提供最佳解决方案,满足他们的独特需求。 我们使用多种编程语言和工具,同时强烈鼓励我们的团队成员参加 MirageOS 静修旅行等技术会议和活动,并做出自己的贡献。 如果您有一个想要开发的项目构想,请告诉我们您的联系方式,我们会尽快与您取得联系,就您的项目以及我们能够提供的帮助进行讨论。