大背景
滾滾長江東逝水,浪花淘盡英雄。
作为一个一直在底层苦苦挣扎多年程序员,保持一分学习的好奇心,对技术时势的感知,由为重要。因为这最终决定了技术方向。如果你是个在组织中有话语权的人,那么这影响到你组织的技术方向。而在技术驱动型的公司中,这个直接影响到公司的前途。
开发环境作为一个开发周期的基础架构,本应走在技术的前面去演化。程序员每天为业务,为老板服务的同时,更应该考虑提高开发效率,让更多时间可以在诗和远方上,而非等待打包、等待环境。
下面我尝试分阶段分析一下现状,最后,写写我的想法。
我的黑胶时代
对于开发期的流程,在微服务化前的,过去的 20 年里,程序员已经习惯于:
开发流程:
- 本地开发、连接开发用的数据库、调试
- 提交代码、跑 CI 测试
CD/DVD 光碟 时代
基于直线的改进思维,微服务化后,程序员自然地探索出:
开发流程:
- 本地开发、本地Mock依赖服务、调试
- 提交代码、跑 CI 测试
- 打包开发期服务 Docker Image
测试环境流程:
- 测试人员拉起测试环境、运行测试
- 测试中发现问题,通知开发
- 开发通过日志、高级一点就动态注入探针、猜 来定位问题
- 开发走上面的“开发流程”尝试修正。
- 测试安装新服务 Docker Image,重跑测试
- 如果还是有问题,跳回步骤 3
显然,上面有很大的工作效率问题,和不确定性。
网盘时代
为了减少上面的 猜 的成分,加快开发到部署的效率,同时继续把这条思维线拉直。程序员不懈努力。于是新的云开发环境思路出来了,主要线路有两点:
- 打通本地开发到测试环境的部署,实现一键完成
- 打通本地开发到测试环境的网络,实现测试环境流量引流和互通,有两个目标场景
- Debug 本地运行的应用,但连接到 K8s 网络
- Debug K8s Pod 中运行的应用,本地 IDE 远程 debug。
这类型的方法有两个代表项目:
- telepresence
- nocalhost
telepresence
其中 telepresence
主要解决网络问题,即打通本机到 k8s 的网络。然后可以在本地运行各种应用来接收和发送流量。当然也可以本地 debug 服务了。
nocalhost
而 nocalhost
的野心更大,具有开发环境的管理体系。深度整合 vscode
/ IntelliJ IDEA
实现了一键部署上云,一键远程 debug 等式开发的贴心功能。其设计的核心就是自动同步:
- 自动同步和热加载代码
- 文件
- 终端
图片来源:https://nocalhost.dev/docs/introduction
重新思考
dev-to-cloud
的定义
用架构设计、API设计的思维思考一下。以上方法主要是在为云环境与开发者本机环境间设计一个接口。核心的代码、IDE、开发工具均安置或运行于本机。即,核心开发环境
还是以前的样子。以上方法主要带给我们好处是:
- 一键操作糖、自动同步糖
- 不可否认,糖很 Sweet 。代价是复杂的工具环境、学习曲线、工具本身的各种环境兼容性问题
- 网络的连通性
- 而这个网络连通性是个临时的 vpn tunnel,其稳定性和带宽均难保证,当然,还得面对工具本身的各种环境兼容性问题。
如果我们坐下来想想,我们用云技术把生产环境和集成测试环境上云了,实现了灵活性和伸缩性的需求。而程序员自己的核心开发环境
,还一直留在“本机”:
从 High Level 看,这样的结构有其高复杂性、兼容性的问题:
- 需要保持两端的同步,对 POD 的注入显然有 POD 的兼容性要求,也对“本地”机器的环境有要求。
- Network Tunnel 的复杂性也产生了对“本地”和云端的环境要求。如果云环境
- 做得再好的 Brigde 毕竟是 Brigde,很多本地原生开发环境高效完成的事情,变得困难或不可能。
- jstat, jcmd…
从 Low Level 看:
- “本地” 到 “云端”的网络延迟、带宽、可靠性一般不理想。卡壳是常见问题, Troubleshooting 同时,还得 troubleshooting 工具本身,是件痛苦的事情。
- 现有的 debug 网络协议如:Java Debug Wire Protocol (JDWP) ,均是基于低网络延迟的本地网设计的,
IntelliJ IDEA
下默认的远程 debug 慢的感人。 - 试想要调查一个 heap dump,文件要传多久,本地的机器可能因内存太少根本打不开 heap dump。
- 现有的 debug 网络协议如:Java Debug Wire Protocol (JDWP) ,均是基于低网络延迟的本地网设计的,
我们把这种开发环境的设计方案定义为:dev-to-cloud
。
dev@cloud
的定义
Remote VSCode
在重新思考时, Remote VSCode 的设计给予了我一点启示:
图片来源:https://code.visualstudio.com/docs/remote/ssh
和之前的重要分别是,本地运行的只是个 UI 壳。文件存储与读写,编译、打包、运行、debug 均发生在远端。每个 UI 中的按键触发一个远端事件。
这个设计当然好,但有两个问题:
- VSCode 虽好,但我们还有其它更适合具体语言的 IDE 和工具集。
- 要求对端安装 sshd 服务。这有时会是个问题。
kubectl debug
相信大家有听过或用过 kubectl debug
。其核心就是在被 debug 的 POD 中拉起一个新的 debugger 容器。并提供一个终端用户界面给用户去控制容器。
The Idea
如何让工具更贴近应用运行,让更多本地开发时用到的功能在云上同时可用?如何提高工具的运行效率,减少远程来回传输?如何提供新手友好、IDE 友好的 GUI 环境?如何方便开发者的接入,只要一个浏览器即可?如果:
- 把工具容器(Debugger Container)放在被 debug 的 container 中运行
- 两个 container 共享 pid & network namespace
- Debugger Container 提供 X Server/ HTTP VNC 服务
- 需要更深度整合的话,Debugger Container与被debug的 container 共享目录
从使用者角度看,说得直白点,就是远程桌面到 POD。而且,连接这个远程桌面只需要一个浏览器。
我们把这种开发环境的设计方案定义为:dev@cloud
💡 对于不了解什么是 vnc 的读者,可以看:https://en.wikipedia.org/wiki/Virtual_Network_Computing , 简单来说,就是个远程桌面服务端。
💡 对于不了解什么是 novnc 的读者,可以看:https://novnc.com/info.html , 简单来说,就是个让用户可以通过浏览器,连接到 vnc 服务的界面。
我们可以预期开发端看到的样子大概是:
从我实测看,novnc 用 intellij IDEA 的卡壳比真正由开发机 remote debug 到云端的卡壳少很多。网络临时断开等问题可以轻松自动恢复。想想,下班前未 Troubleshooting 完的问题 ,明天上班简单浏览器一连上就可以继续了。如果自己 Troubleshooting 解决不了,把 novnc 的 URL 一发给同事,对方就可以开工了,不会问你一堆的环境如何连接等问题。
优点:
- 功能性
- 靠近应用运行,原有的本地 troubleshooting 手段大部分可以用上:jcmd、jstat…
- 用你喜欢的 GUI 工具。想想 wireshark、jconsole、eclipse memory analyzer、IDEs
- 便利性
- 本地只需要一个浏览器
- 会话共享给他人协同解决问题方便,只需要一个 URL
- 会话持久可用,重连方便,不存在数据同步问题
- 可靠性
- 保持了应用容器的独立性,资源限制。你大概不敢在一个有 memory limit 的容器中使用
kubectl exec $POD -- wireshark
。 - 不存在本机和云间数据不一致问题,不存在来回手工传输大文件等问题。
- 保持了应用容器的独立性,资源限制。你大概不敢在一个有 memory limit 的容器中使用
- 安全/隐私性
- 大量数据只保存在云,不存在本机和云间手工传输大文件等问题。只有部分在 GUI 中展示。
to be fair,说说缺点:
- 安全性与连通性
- 为桌面设计的 GUI 应用在云端运行,可能有一些互联网访问等问题需要考虑
- GUI 运行于云端,当然需要云资源。如果公有云的资源昂贵,这是个问题,这个方案可能只适合私有云
- 由于不同类型应用需要共享的目录不同,
mount --bind share directory
可能需要定制
实现 dev@cloud
到现在为止,我是手工实现自己的 dev@cloud
环境的,还未自动化和工具化。现实方法还未有可移植性等考虑。现在的手工步骤是:
- docker build 自己的 image。其中包括 xvnc 和 novnc 和一些工具
- 在
被 debug container
的 pid & network namespace 下启动一个 debugger 容器。- 这里的方法很多,可以是在 node 中打命令启动,也可以是
kubectl debug
- 这里的方法很多,可以是在 node 中打命令启动,也可以是
mount --bind share directory
是个可选的步骤。如果打算用 jstat/jcmd 等,共享/tmp
几乎是必须的。这步暂时无优雅的实现。只能在 node 用mount --bind
- 在
debugger 容器
中启动 xvnc 和 novnc - 通过 k8s nodeport service 等方式,让开发者本机可以访问 novnc 的端口
这里有很多具体细节,我只能在下一编中细说。
总结
写本文的目的不是踩什么和捧什么。方案没有好坏,只有哪个适合你的需求。本文是想开阔一下思路,换个角度,实现真正的 dev@cloud
。谢谢阅读。
是非成敗轉頭空。
青山依舊在,幾度夕陽紅。
白髮漁樵江渚上,慣看秋月春風。
一壺濁酒喜相逢。
古今多少事,都付笑談中。
—— 明代:楊慎