Nocalhost:云原生开发新体验
本文为 CSDN 博主「祈晴小义」(黄鑫鑫:腾讯云 CODING DevOps 研发工程师、Nocalhost 项目的核心开发者)的原创文章,并根据作者在 CSDN 云原生 Meetup 深圳站的演讲内容进行了整理,主要分享 Nocalhost 在解决云原生开发问题上的思路和探索,并展示 Nocalhost 为云原生开发带来的全新体验。
云原生场景下的开发痛点
当我们的应用架构从传统应用过渡到云原生应用的时候,会发现应用架构的复杂性大大提升了,原来的传统应用组件少,部署简单,我们往往可以在本地开发完一个传统应用后,把它丢到服务器上就能跑起来。而对于云原生应用来说,应用被拆分成一个一个粒度更小的微服务,各个服务之间有着错综复杂的关系,从而让开发环境的搭建和服务的调试变得异常困难。
本地部署 VS 集群部署
当我们要开发云原生微服务应用时,如何将我们的开发环境搭建起来呢?常见的有两种方式:本地部署和集群部署。
本地部署是将一整套微服务应用部署到本地的开发机器上,如下图所示:
这种方式会带来以下几个问题:
1. 影响开发机器的性能。微服务应用往往规模比较大,动辄几十上百个服务,都泡在自己的开发机上可能会让电脑变得很卡,影响工作效率。
2. 环境无法共享,资源浪费严重。当我们需要在本地部署起整套规模比较大的微服务应用的时候,就需要使用配置较高的开发机器,并且每台开发机器的开发环境只有一个开发人员能使用,即便该开发人员只需要开发其中一个或某几个服务,也无法将其它使用不到的服务共享给其它开发人员使用。
3. 对于一些规模很大的微服务应用,本地机器可能还没有办法跑起来。
另外一种方式将微服务应用部署到云上的 K8s 集群里,如下图所示:
这种部署方式可以较好地提高资源利用率,但是它会让开发和调试应用时的反馈链路被大大拉长。
我们开发传统应用时的工作流是:在本地编写好代码 -> 把代码进行编译 -> 运行程序查看结果,如下图所示:
这个过程往往很快,所以我们可以在做完一次代码的小改动以后,就把它运行起来查看结果。
但是在开发 K8s 集群上的应用时,工作流变成了:修改代码 -> 编译程序 -> 将程序打包到 Docker 镜像 -> 将 Docker 镜像推送到镜像仓库 -> 修改集群中容器的镜像版本,等待 K8s 将新版本的镜像部署上去 -> 查看结果,如下图所示:
这个流程可能需要耗时几分钟,当这个循环反馈被大大拉长了以后,无疑会让开发的效率大大降低。
目前主流的云原生开发方式
手动打包推送镜像
这种方式是最原始的方式,工作流大体如下:
编写完代码以后,在本地编译生成二进制文件或者 jar 包之类,然后通过 Dockerfile 构建出 Docker 镜像,再将镜像推送到 Docker 仓库,再通过修改工作负载的 yaml 定义中镜像版本,部署新版本容器的任务则交给 K8s 去做,只不过部署调度的过程可能有点漫长,需要等待 K8s 将新版本的 Pod 调度运行起来之后,我们才能看到代码修改的效果。如果代码改动得频繁,这个流程显然是非常繁琐的。
CI/CD 流水线
这种方式和第一种方式的流程大体上是一样的,只不过是通过 CI/CD 的能力,把手动的操作改成了自动化的流程:
这种方式的工作流是:在本地修改完代码,把代码推送到代码仓库,从而触发代码仓库配置好的 CI 流程,把代码编译构建成应用程序(如二进制或 Jar 包),并打包成镜像,之后会触发所谓的持续交付(Continuous Delivery)机制,将镜像推送到制品仓库里,最后再触发持续部署(Continuous Delivery)流程,将新版本的容器调度部署到集群中。虽然使用 CI/CD 以后,可以减少大部分手工操作环节,但整个流程花费的时间仍然很多,事实上,CI/CD 更适合在发布应用环节使用,而不是在开发应用环节。开发环节更注重的是能够快速得到反馈从而验证自己的想法,当我们的修改需要提交到代码仓,在 CI/CD 流水线跑完了以后才能看到效果,会限制我们使用简单的尝试,来从几种方案中找出最优的一种,或者定位 bug 的原因。
流量转发
流量转发的思路是:将集群里访问开发中服务的流量转发到本地。如下图所示:
当需要开发 D 服务时,将集群中访问 D 服务的流量转发到本地开发机器上的某个端口上,在本地写完代码以后,直接将应用程序在本地跑起来即可。实现这种方式的相关产品有:kt-connect 和 telepresence。
在本地直接运行应用程序固然可以缩短循环反馈,提高开发效率,但这种方式也有一个很大的问题:许多运行在 K8s 集群上的服务会依赖其它 K8s 资源,例如依赖 ServiceAccount、ConfigMap、Secret、PVC 等等,这样的服务要在本地跑起来并不太容易。
在容器里进行开发
这种方式的思路是:当我们要对某个服务进入开发时,先让要开发的服务进入开发模式,然后将代码同步到容器中,直接在容器中把开发中的代码运行起来:
这种方式同时解决了开发循环反馈过慢和服务依赖集群问题,是目前云原生开发中较好的实践,也是 Nocalhost 支持的主要开发方式之一。
Nocalhost 初体验
第三部分主要是以 Demo 演示的方式来带大家体验 Nocalhost 的特性,感兴趣的同学可以前往深圳站 Meetup 视频回放,从 01:26:15 处开始观看。
https://live.csdn.net/room/csdnnews/yCHrYqnM
Nocalhost 核心机制
Nocalhost 是如何实现在容器中进行应用程序的开发的呢?在一个服务进入开发模式时,Nocalhost 所做的核心工作有以下 4 个步骤。
缩减副本数
开发应用程序时,我们只需要在一个容器里运行正在开发中的应用程序,如果存在多个副本,我们通过 Service 访问该服务时,就无法控制流量只访问到我们正在开发中的应用程序所运行的那个副本,所以 Nocalhost 需要先将工作负载的副本数缩减为 1。
替换开发容器镜像
生产环境运行的容器往往会使用很轻量级的镜像,镜像里仅包含运行业务程序所必须的组件,而缺少编译构建业务程序所需的相关工具(如 JDK)。在对某个工作负载进行开发的时候,Nocalhost 会将容器镜像替换成包含完整开发工具的开发镜像。
增加 SideCar 容器
为了将本地的源代码改动同步到容器中,我们需要在容器里运行一个文件同步服务器。为了使文件同步服务器进程和业务进程解耦,Nocalhost 将文件同步服务器运行在一个独立的 sidecar 容器中,该容器与业务容器挂载相同的同步目录,因此,同步到 sidecar 容器中的源代码在业务容器中也可以访问。
启动文件同步客户端
由于文件同步服务器监听在容器里的某个端口上,我们在本地无法直接访问,所以 Nocalhost 会把一个本地随机端口转发到容器里文件同步服务器监听的端口,打通文件同步服务器和客户端的网络,然后再启动本地的文件同步客户端。文件同步客户端启动后会通过刚刚转发的本地随机端口和文件同步服务器建立通信,之后便会开始进行文件的同步。
在以上步骤完成后,Nocalhost 会自动打开一个进入到远程容器的终端,通过该终端,我们就可以把实时同步到容器里到源代码直接运行起来。
Nocalhost 高级特性
Duplicate 开发模式
Nocalhost 默认的开发模式是将集群中原本正常运行着的服务给替换成开发容器,如下图:
这种方式存在以下问题:
1. 开发时会影响环境的使用。当对某个服务进行开发时,该服务在开发过程中可能会由于代码修改有问题导致异常甚至奔溃,集群里又有其他服务依赖该服务,从而影响到整个环境的使用。
2. 无法支持多人开发同一个服务。
为此,我们可以使用 Duplicate 开发模式,这种模式下,Nocalhost 不会对原有的服务做任何修改,而是复制出和一个原有服务一样的副本来进行开发,如下图所示:
这种模式下,多人可以对同一个服务进行开发,每个人都拥有自己的开发副本,集群原有的环境也不会受到任何影响。
Nocalhost Server
Nocalhost 除了通过 IDE 提供方便开发 K8s 应用的插件外,还提供了一个适合企业级开发环境管理的 Nocalhost Server,以下是 Nocalhost Server 的管理界面:
Nocalhost Server 可以提供集群、应用、人员权限上的管理,关于 Nocalhost Server 的详细介绍,可以参考官方文档上的介绍。
Mesh 模式
前面我们提到,如果要实现多人开发同一个服务,可以使用 Duplicate 开发模式,但这种方式也有一个局限性,就是只能在本地通过 API 接口请求去访问开发中的副本,没办法通过应用的入口地址来访问。对于需要通过统一的应用入口来访问开发中服务的场景,我们可以使用 Nocalhost 的 Mesh 模式。
Mesh 模式会为每个人分配一个 MeshSpace,不同的 MeshSpace 通过在流量中带入不同的 Header 来控制从应用入口进来的流量的访问链路。
使用 Mesh 模式要求开发环境通过 Nocalhost Server 管理,并且应用需要有 Header 透传和使用 Istio 进行流量转发的能力,关于 Mesh 模式的使用可以参考官网文档 (文档目前还不是很完善,更详细的信息可以直接通过 GitHub 联系 Nocalhost 的开发团队)。