作者 | Justin Cormack
译者 | 核子可乐
审校 | 褚杏娟
Docker 公司 CTO Justin Cormack 在 InfoQ 的会议上与 Docker 公司创始人、Docker 之父 Solomon Hykes、Vitess 联合创始人 Sougoumarane 、Krustlet 主要作者 Matt Butcher 等人一起探讨了等各大云原生项目在最开始为何选择了 Go 和 Rust 语言,并对云原生项目如何选择编程语言提出了几点建议。我们将本次大咖们的对话进行了编译,希望对大家有所启发。
Cormack:大家好,我是 Justin Cormack,Docker 公司的 CTO。另外,我也是云原生计算基金会技术监督委员会的一员。
我一直在关注编程语言的发展,以及这些语言如何改变我们的工作方式。在云原生计算基金会中,我们总共拥有 42 个已毕业和正在孵化的项目,其中 26 个项目主要由 Go 编写而成。因此,我想聊聊 Go 为什么能够占据主导地位、云原生领域还有哪些新语言正在涌现,以及云原生这一路的发展历程。
这些项目中,最具历史影响力的当然是 Docker,而在我于 2015 年加入 Docker 时,Go 已经成为公司内广泛采用的一种成熟语言。下面要请出 Docker 公司创始人 Solomon Hykes,由他本人介绍当初为什么要选择 Go,以及他们为何看好当时仍显青涩的 Go、而非 Python。
Go 语言的使用情况
Docker 为什么选择 Go?
Hykes:我们需要面向的并不是 Java 或 Python 平台,而是 Linux 平台,这是一方面。另一方面,老实说,选择 Go 也有个人直觉的因素在里面。我们之前都是用 Python 和 C 编写分布式系统的开发人员,对 Python 在实际生产中的应用已经非常熟悉了,所以大家都很讨厌 Python 的类型问题。最终,这些本该被早点发现的问题就暴露在运行时中,再也没法更改。
此外,我们还想重新创建一套轻量化线程系统。这项工作已经推进一段时间了,当时我们会经常用到 gevent 和 Greenlets 之类的库和框架,而 Go 内置有 goroutines,虽属于同类工具但表现更好,具备 C 语言的类型化优势。所以,在我们这些开发分布式系统的 C 和 Python 程序员来看,Go 真的是一个完美选项。
Cormack:那你当时为什么不选择 C 语言呢?
Hykes:我从来就没考虑过用 C 语言。
Python 属于默认选项,毕竟我们已经在用了,而在我们关注的每项指标上,Go 都表现得更好。其一,它能编译为独立的二进制文件;其二,它正好适配我们的编程模型;其三,我们特别想培养起一个庞大的开源贡献者社区。我们设想中的 Docker 不仅会是款成功的工具,还会是个成功的开源项目,因此语言的选择对于后期建立社区非常重要。比方说,我们得保证选择的语言有足够高的人气,保证语言本身不会成为理解源代码、贡献新代码的障碍。Go 的好处就是它的语法比较平易近人,会写 C 或者 Python 的人肯定能很快上手 Go。
总之,Go 不像 Haskell 或者 Lisp 那么激进,它会继续遵循主流编程语言中的种种普遍性约定。这当然是个巨大的优势,能让新人们更容易地为项目做出贡献。
Vitess 又为何选择 Go?
Cormack:Hykes 曾在采访中提到过,当时他很认真地研究了 Go 的现有生态系统,正是 Vitess 项目(现为云原生计算基金会项目)让他下定了决心。Vitess 是当时 YouTube 上的一个项目,用于支撑 YouTube 快速增长的视频业务。为此,我还专门跟 Vitess 创始人之一的 Sougoumarane 聊过他选择 Go 语言的原因。
Sougoumarane:其实,当时的 Go 可以算是意外入选。
2010 年刚开始准备启动这个项目时,我们的主要选项是 Python、Java 和 C ,当时能想到的也就这三个选项。考虑 Python 是因为 YouTube 本身就是用 Python 编写的,但它的问题也让我们记忆犹新——它不是那种系统编程语言,执行效率不够理想,所以没法满足我们构建高效代理的需求。
那么,接下来就只剩下 Java 和 C 了。我对 Java 不怎么熟悉,而且跟 Java 有关的回忆一点也不美好。我也说不上来为什么,可能跟我之前遇到的人有关吧,反正我也不太认可 Java。另一位创始人 Mike 则明确反对 C ,他说自己用 C 写不出高水平的软件。
我们选择 Go 语言有几个原因,还有个有趣的解释是大家一时兴起,如果我们使用 Go 并且项目失败了,我们可以将其归咎于此。实际上,那时候最打动我们的是 Go 语言的作者,也就是 Rob、Russ、Ian 和 Robert Griesemer。
那时候 Go 才诞生不久,所以我们必须得看看它的作者是什么水平。在深入研究了这几位之后,我们意识到他们的价值观、思想甚至是哲学理念都非常成熟,而且跟我们处理问题的思路颇为相似——既不太理论化,也不太过守旧,他们是在以非常务实且平衡的态度解决问题。当时谷歌内部甚至出现了那种不太好的风气,就是工程师们开始对复杂性过分迷恋,总觉得复杂的才是好的。但 Go 语言的创始团队不一样,他们坚持认为简单的才是好的。我很喜欢这样,我喜欢他们的想法。
后来,我把我们的难题告诉了 Dmitry Vyukov。我们一共有八个 CPU,但当时的 Go 运行时只能用上其中六个。我跟他讲,如果他能把运行时优化一下,把剩下两个也用上,那我们就选 Go。
他自己鼓捣了大概两个月,然后拿出了设计方案和原型实现。我们试了一下,确实能用上全部八个 CPU。问题得到解决,他挽救了我们这个项目。如果他没能成功,我们可能早就放弃 Go 了。
这其实跟 Go 自身的设计无关,只是我们当时确实面对现实压力,必须找到靠谱的解决方案。Vyukov 的解决方案恢复了我们对 Go 的信心,之后我们就坚定地在 Go 这条路上走了下去,再没有丝毫犹豫。
Cormack:Hykes 和 Sougoumarane 都在为自己的新项目寻找合适的语言,一种面向云原生的系统语言。他们俩也都意识到了社区的重要意义。对 Sougoumarane 来说,他构想的社区就是 Go 创造者和希望让这种语言愈发强大的群体。而对 Hykes 来说,他构想的就是 Docker 社区,吸引众多成员在这里用 Go 语言实现容器化。
2012 年末,负责 NATS 项目的 Derek Collison 在 Twitter 上提到,Go 将在未来两年内成为系统和云原生领域的主导语言。当时人们对此普遍抱有怀疑,但事实却证实了他的猜测。那个时候,Docker 和 Kubernetes 已经先后亮相,采用量也迎来了爆炸式增长。我问过他当时为什么会做出这样的断言。
Go 为什么能成为云原生主导语言?
Collison:最初 NATS 项目是用 Ruby 语言编写的,跟 Cloud Foundry 一样。其实从开发的角度来看,我一直倾向于在系统建立完成后只使用一种语言进行后续开发。时至今日,我仍然觉得 Ruby 是种很棒的语言。我们尝试使用 Ruby VM 和各种依赖项部署生产系统,而且依靠事件机器高效执行异步操作,但最终没能成功。
2012 年,我们创办了 Apcera,而且需要将 NATS 作为 Apcera 平台的控制平面寻址发现与遥测系统,这就是 Continuum。我不想再用 Ruby 了,所以想试试当时的新语言 Go,好像刚推出 0.52 版本吧。当然,我们也考虑过相对年轻的 Node.js,但它还是比 Go 老了点。所以,我们最终决定给“年轻人”个机会。
后来我们慢慢熟悉了 Go 生态系统,并发现了 Go 很多当初决策时可能并没注意到的独特优势。刚开始,大家只是希望用 Go 回避掉在 Ruby 生态中部署生产系统的痛苦经历。而 Node,虽然它有 npm,但在本质上仍然是个虚拟机,拥有相应的包管理系统,所以只能围绕它执行所有包。Go 则不同,它有能够提供准静态可执行文件。当时我们已经有了完整的静态可执行文件,只需要稍加调整就能使用。这很重要,意味着我们的部署可以作为 SCP 方法。另外,goroutines 和并发模型对我们也很有吸引力。
对我来说,另一个重要的决定因素是,我们已经在 TIBCO 上花了很长时间并设计出了一套具有类似功能的系统,所以我们在 TIBCO 项目中继续使用 C 语言。我个人也一直非常喜欢 C,它用起来虽然困难重重,但离机器更近,或者说离“解空间”更近其实挺有乐趣的。我也学过 Rust。今年假期,我打算抽时间学学 Zig。以后我可能要彻底告别 C 语言编程了,但我还是很喜欢 C。
言归正传,当时我们需要把 80%~90% 原本放置在栈上的用例,透明转移到堆中。这件事在 C 里很难实现,需要耗费大量时间和精力,但在 Go 里却几乎没有难度。这事对我无异于当头棒喝,原来自己在 C 中费尽心力才搞成的效果,在 Go 中根本就不是问题。
当然,那时候的 Go 只是 0.52 版本,还有很多自己的问题:垃圾收集器非常原始,标记和清除功能也很粗糙等等。但我觉得这些都无所谓,因为可以在架构设计上把大部分东西都放在栈上。这样在运行经过栈时,代码就会在 Go 中自动升级,不用再像 TIBCO 的 C 代码库那样强加转变。可以说,静态可执行文件和真栈让我下定决心选择了 Go。
当然,Go 的并发性也很出色。现在回顾整个生态,go-funk 的影响其实要比人们想象中大得多。如今所有开发者都在以同样的方式处理问题,使用现成的 Go Vet、pprof 和其他测试工具。这样哪怕有一天我暂时离开项目,那后面再回来的时候也能很快搞清楚这是在干什么。即使是其他人编写的 Go 代码,搞清其中的逻辑和意图也要比 Haskell 或者 Caml 简单得多。哪怕是在 Ruby 中,如果隔几个月再看之前编写的代码,也往往需要一个小时左右才能找回当初的思路。总之,Go 就是这样一种对新人特别友好、适合快速上手的语言,我觉得这一点非常、非常重要。
Rust 的使用情况
Cormack:我们已经聊了很多关于 Go 当初如何在云原生生态系统中迈出了第一步。最近,我们看到 Rust 项目开始快速腾飞,其他一些语言也风头正劲。我跟 Matt Butcher 聊了聊他为什么要选择 Rust。他最早其实是 Go 程序员,还构建了 Helm 等项目。但最近,他开始在新项目里用 Rust 了。
Butcher:Ryan Levick 是 Rust 语言的核心维护者之一。我们开始选择语言时,他正好也在微软工作。那会儿他刚加入我们 Slack 频道,并提到“听说你正打算开发一款 Clippy 风格的 Rust 程序。”基本上,哪里有想要学习 Rust 的人,哪里就有 Ryan 的身影和热情的指导。他不光分享基础知识,也会提供学习资源,并简要回答一些常见问题。很快,部门中就有七、八个人决定试试 Rust,形势就这么转变了。
我们之所以决定用 Rust 来编写 Krustlet,是因为当时我们想构建一款 Kubernetes 控制器。其实最初我们并没有刻意选择 Rust,只是随手一用,之后的新项目就逐渐默认用 Rust、而非 Go 了。
Krustlet 为什么选择 Rust?
Cormack:你为什么要用 Rust 编写 Krustlet?
Butcher:主要是我们当时想搞一个 WebAssembly 运行时,当时最好的 WebAssembly 运行时要么是面向 JavaScript 生态用 C 或 C 编写的,要么就是用 Rust 编写的。我们当时打算用 Wasmtime,也就是 WASI 规范的参考实现,它是用 Rust 编写的。我们研究了一下,发现可以把它编译成库,然后用链接到 Go。
从这里开始,大家逐渐开始研究 Rust。有人喜欢它的泛型,有人喜欢它的 kube.rs crate 库。不久之后,人人都迷上了 Rust。木已成舟,整个 Krustlet 也就只能都用 Rust 编写了。其实 Rust 最初只有一个用途,就是用于开发 WebAssemblyl 运行时,但它跟我们的需求太贴合了,所以最后成了正确的语言选项。顺着这种习惯,我们后面开始用 Rust 编写其他项目,而且越用越觉得顺手、舒心。
其他语言情况如何?
WebAssembly 与 Zig
Cormack:Collison 在面向边缘位置开发轻量级语言时,也经历了类似的过程。我们讨论过要不要使用 WebAssembly,也考虑过 Zig。
Collison:大多数新型生态系统都采取类似的设计方法,即标准库不但要能扩展,还得足够完善。就连相对年轻的低级语言 Zig 也在标准库身上下了不少功夫,让它尽可能充实。
Cormack:甚至连 C 都打算引入 HTTP 和 TLS 了,但估计还得十年才能实现。
Collison:我不知道自己的职业生涯还能持续多久,但我可以自信地说,我再也不会用 C 或者 C 编程了。没错,现在更好的语言选项太多了。
另外我觉得,未来的边缘计算没准会让云计算也相形见绌。云计算很快就会沦为现在的大型机——仍然存在,但已经没人在乎了。没人会跟大型机直接交互,它们单纯成了生活中的背景。未来,谁在执行同等任务时耗费的能量和资源更少,谁就是老大。
我觉得 .NET 和 Java 业务还会继续存在,特别是在数据中心或者云环境下,但真正有活力的一代,应该会是 C、Rust、Zig,以及速度极快的 Wasm 或者其他 JavaScript 引擎,它们可能更松散,也可能会出现 MicroPython、CircuitPython 之类更轻量化的语言。没准到时候 TinyGo 会大放异彩呢,至少我觉得有希望。
Q 编程语言
Cormack:Hykes 现在仍然是 Go 的铁粉和忠诚用户,但他也希望 Go 语言能再多一点变化。
Hykes:我还在用 Go。其实我不是那种热衷尝试最新语言的人,我更习惯于长期使用相同的语言工具。没错,我们当初的选择可能确实推动了 Go 以及后来 YAML 的普及,但这并不能说明谁就一定比谁更好。
以 YAML 为例,我不是说它不好,而是它有点被过度使用了。比如我们目前正在开发的新项目 Dagger 就是用 Go 编写的,而且具备可配置和可定制功能。之所以不选择 YAML 或者 JSON,单纯是因为它们不支持我们想要表达的功能。这时候我们建立了 Q 语言。
其实最早我们在初代原型中使用的是 HCL,毕竟 Terraform 和其他 HashiCorp 工具用的都是 HCL。我觉得 Q 语言只是个内部项目,是从库衍生出来的,可供大家在自己的工具中使用。Q 语言有着非常严重的局限性,甚至可以说它的作用与特定工具紧密关联,在最初设计上根本就不属于能广泛适应多种工具的独立语言。但另一方面,Q 语言确实是一种语言,能够解决实际问题。正如 Go 当初能解决特定问题一样,Q 也完美匹配我们的现实需求。
Q 更像是 YAML 的一种替代品,我对它的未来非常看好,Q 应该能在不少云原生配置场景中取代 YAML。
云原生项目如何选择编程语言
Cormack:首先就是,一定要认清社区的重要意义。当我们考虑使用一种新的语言,并规划它的功能特性和构建方式时,种种判断都应以社区为基准。
第二,一定要给项目以社区,想清楚自己希望整个社区以怎样的方式使用正在开发的语言和工具。这就是典型的问题导向了,在云原生领域,很多人会提出明确的要求,例如静态二进制文件,这些要求能帮助他们轻松分发代码、或者让代码在生产环境中轻松运行。总之,一定要把这些需求跟当前设计匹配起来,这种立足新领域进行思考的机会非常宝贵,能帮我们判断自己正在使用的工具或者语言到底合不合适,并决定是否有必要做出更改。
性能对云原生用例同样非常重要。有趣的是,语言的性能水平其实是有弹性的。Sougoumarane 在开发 YouTube 的时候也有这样一段真实经历:随着他们对性能的要求越来越严苛,Go 也一直在更新并努力满足这些需求,所以直到最后 Go 语言也没有拖过性能的后腿。所以请牢记一点:语言会随着用户的改变而发展,会随着采用率的提升而完善,由此发展出庞大的生态系统。这些非常重要,足以决定一种语言的命运。
还有最后一点,就是如今人们学习新语言的方式越来越多样。有些人会拉长思考周期,在真正下定决心前用好几年时间来慢慢实验。也有些人会主动试水,通过实践尝试新语言的工作效果和其中的机会空间。总之,新语言的学习过程对程序员们来说越来越重要。我们都在不断学习新的编程语言,借此探索并更新自己看待和解决问题的思路。
所以,当我们进入一个新领域或者打算尝试一个新想法时,首先想到的就是哪种编程语言最切合需求、自己想要建立什么样的社区?这个问题的答案,往往会决定项目的未来。
原文链接:
https://www.infoq.com/presentations/languages-cloud-native/
今日好文推荐
备受乔布斯推崇的 PWA,为什么还没有杀死原生应用?
那位用 Rust 重写数据库的创始人来复盘了:删除 27 万行 C 代码,值吗?
谷歌翻译中国站点疑似关闭;字节跳动升级员工关怀计划:新增每年 10 天家庭关爱假;Istio 正式成为 CNCF 孵化项目|Q 资讯
“产品杀手”谷歌关闭 Stadia,网友:负责人是把 Stadia 当职业跳板了吗?