痛点

在我们团队将产品的部署形态迁移到 kubernetes 上之后,在研发过程中,开发和联调代码的过程非常的痛苦,决定寻找在 k8s 环境下的云原生时代的打通本地和开发环境的解决方案。

回顾一下,过去的开发过程,我们的进程一共有 2 种模式

分析

k8s 无关进程

由于项目依赖了 oracle 连接库,安装 oracle 驱动,在团队中既有 window,unbuntu,和 mac 的情况下,团队成员的本地开发环境的搭建非常复杂进程依赖的环境变量非常多,如果开发环境的 ip 换掉,配置环境变量非常麻烦进程内依赖了一些二进制 bin 文件,格式是 ELF 的,这些文件在 windows 和 mac 的环境上是无法执行的

依赖了 k8s 的进程

需要复制开发环境上 kubeconfig 文件到本地,一个人如果同时开发多个环境,切换 kubeconfig 文件非常繁琐本地开发无法将开发环境上的流量导到本地,需要打包替换环境上镜像来完成联调

现状

因此日常的开发流程便是:

显然,为了保证在本地开发的代码,在环境上真实运行的效果完全一致,带来了很大的工作效率问题,本地编码 2 分钟,上环境自测 5 分钟

需求

因此我们急需一种便捷的,学习成本低的,基于配置的,一劳永逸的,团队统一的解决方案来打通本地和开发环境,节省本地开发环境的搭建和配置的时间,节省团队成员联调的时间,提高团队效率

方案选型telepresence参考

https://docs.microsoft.com/zh-cn/visualstudio/bridge/overview-bridge-to-kubernetes?view=vs-2022

介绍

Telepresence 是一款为 Kubernetes 微服务框架提供快速本地化开发功能的开源软件。Telepresence 在 Kubernetes 集群中运行的 Pod 中部署双向网络代理,该 Pod 将 Kubernetes 环境(如 TCP 连接,环境变量,卷)中的数据代理到本地进程。本地进程透明地覆盖其网络,以便 DNS 调用和 TCP 连接通过代理路由到远程 Kubernetes 集群,能够获取

特性缺陷Bridge to kubernetes参考

https://docs.microsoft.com/zh-cn/visualstudio/bridge/overview-bridge-to-kubernetes?view=vs-2022

介绍

Bridge to Kubernetes 可重定向已连接的 Kubernetes 群集与开发计算机之间的流量。 Kubernetes 群集中的本地代码和服务可以像在同一 Kubernetes 群集中一样进行通信。

Bridge to Kubernetes 插件只可用于 Visual Studio 和 VS Code,支持其他 ide 的计划还在官方的 RoadMap 上没有开始

私有链开发(Nocalhost-让云原生时代的开发更高效)(1)

工作原理特性

Bridge to Kubernetes 具有以下限制:

  1. 要使 Bridge to Kubernetes 成功连接,一个 pod 只能有一个容器在该 pod 中运行,但是现实中有很多多容器的 pod。
  2. 目前,Bridge to Kubernetes pod 必须是 Linux 容器。 Windows 容器。
  3. 如果 k8s 环境是集群环境,若要更新 /etc/hosts 文件 ,Bridge to Kubernetes 计算机上需要管理员访问权限
Nocalhost

Nocalhost 是一款开源的基于 IDE 的云原生应用开发工具

参考

https://nocalhost.dev/zh-CN/docs/introduction

介绍

Nocalhost 是一款开源的基于 IDE 的云原生应用开发工具:

nocalhost 的野心相比 Bridge to Kubernetes 和 Telepresence 更大,具有开发环境的管理体系。深度整合 vscode / IntelliJ IDEA 实现了一键部署上云,一键远程 debug 等式开发的贴心功能。其设计的核心就是自动同步:

Nocalhost 目前是完全开源的,并已进入 CNCF Landscape:

并且支持 goland, idea,pycharm, vscode

工作原理

Nocalhost 由单个二进制 CLI 和 IDE 插件组成。 理想情况下,您可以直接将它与您熟悉的 IDE 一起使用。

Nocalhost 不需要服务器端组件,因为它通过 KubeConfig 直接与您的 Kubernetes 集群通信,就像 kubectl 一样。

特性实时线上编码

Nocalhost 预配置为与你喜欢的 IDE 配合使用,你可以一键连接到任何 Kubernetes 集群,开始享受在集群内进行编码,摆脱烦人的本地环境配置。

实时同步

Nocalhost 可以在你每次进行修改时自动将代码同步到 K8s 容器中。 这样能免去了镜像提交、构建和推送等步骤,极大提速了开发的反馈循环。 因此代码修改可以实现秒级生效。

私有链开发(Nocalhost-让云原生时代的开发更高效)(2)

易于协作

Nocalhost 可帮助你的团队实现标准化开发工作流程,而无需让团队中的每个人都成为 Kubernetes 专家。

使用方式

Nocalhost 由单个二进制 CLI 和 IDE 插件组成。 理想情况下,您可以直接将它与您熟悉的 IDE 一起使用。

为什么使用 Nocalhost?

构建 Kubernetes 应用并不简单,尤其对于大型开发团队来说更具挑战。 Nocalhost 提供了最高效的方式来构建云原生应用程序。

使用 Nocalhost 直接在 Kubernetes 中进行开发的优势有:

在下述环境里,在 Kubernetes 集群中进行开发很有用:

对比/PK

私有链开发(Nocalhost-让云原生时代的开发更高效)(3)

三者比较来看,nocalhost 是特性最多,支持的场景最多,并且支持 IDE 的插件最多,用户体验最好,完胜其他两位,是其他两个工具支持的特性的超集,因此我们选择了 nocalhost

开始使用安装vs code
  1. 打开 VScode 编辑器,然后单击左侧栏中的方块图标
  2. 在搜索输入框中键入 nocalhost
  3. 选择 Nocalhost 插件,然后单击安装按钮进行安装。

私有链开发(Nocalhost-让云原生时代的开发更高效)(4)

jetbrains

Nocalhost 支持 JetBrains 全系列 IDE, 请参阅 安装 JetBrains 插件。

当安装 Nocalhost 插件时,会自动帮你安装 nhctl

Windows

`文件 > 设置 > 插件 > 浏览插件仓库... > 搜索 "Nocalhost" > 安装插件"

MacOS

`首选项> 设置 > 插件 > 浏览插件仓库. 。> 搜索"Nocalhost" > 安装插件

私有链开发(Nocalhost-让云原生时代的开发更高效)(5)

编写配置

需要在项目根目录下创建.nocalhost 文件夹,并在其中创建 config.yaml 文件。这个文件编写好后可以传到版本控制系统中去,整个团队共享,易于协作, 一个项目只需要编写一次,有一个人负责维护,其他人都可以无须知道该技术的细节,从而将精力和时间聚焦在核心业务之上。

目录结构如下:

私有链开发(Nocalhost-让云原生时代的开发更高效)(6)

python 项目设置

注意: 使用 pycharm 需要使用商业版,社区版没有 remote debug server 的插件,无法使用 remote debug 的功能

config.yaml 设置如下

configProperties: version: v2 application: name: cloud-api-server services: # name 与 serviceType 表明这是属于 dock 这个 deployment 的配置 # 配置的内容集中在 containers 中 - name: cloud-api-server serviceType: deployment dependLabelSelector: pods: - "app.kubernetes.io/name=cloud-api-server" # 可以为单个工作负载下的多个容器分别定制不同的配置 containers: # 必须设置对应容器的名字,以区分不同的容器 # 如果有多个容器,但是只需要调试一个容器的话,只设置一个container即可 - name: cloud-api-server dev: # 开发容器配置 # 开发镜像 # must install pydevd in dev image image: registry.***.com/***/cloud-api-server:v18.0.0 # 开发容器默认 Shell shell: "bash" # 文件同步的远程目录 workDir: /app/ # 开发容器资源的 request 和 limit resources: limits: cpu: "2" memory: 2Gi requests: cpu: "2" memory: 2Gi # 热加载 hotReload: true command: # 一键运行 run: - /venv/bin/python - run.py - --port=11101 - --debug=true # 一键调试, must install pydevd in dev image debug: - /venv/bin/python - -m - pydevd - --client - 127.0.0.1 - --port - 8008 - --file - run.py # debug 协议的端口 debug: remoteDebugPort: 8008 # Ports to be forwarded to local when enter devMode # 进入 Devmode 后是否自动开启端口转发功能 portForward: - 11101:11101 useDevContainer: true # https://nocalhost.dev/zh-CN/docs/config/config-pattern sync: type: "send" # 一般设置send就行 mode: "pattern" # 需要同步的文件的模式 filePattern: - "." ignoreFilePattern: - ".DS_Store" - ".idea" - "logs/**" - ".git" - ".github" - ".vscode" - "bin" - "docs" - "examples" - "tests" - "venv"

通过编写这份配置文件,来描述本地环境和远程 k8s 环境的 流量,文件同步,远程 debug 的通信端口的关系。nocalhost 插件会通过加载该配置文件来完成所有工作

golang 项目设置

configProperties:version: v2 application:name: dock services: # name 与 serviceType 表明这是属于 dock 这个 deployment 的配置 # 配置的内容集中在 containers 中 - name: dock serviceType: deployment dependLabelSelector: pods: - "app.kubernetes.io/name=dock" # 可以为单个工作负载下的多个容器分别定制不同的配置 containers: # 必须设置对应容器的名字,以区分不同的容器 # 如果有多个容器,但是只需要调试一个容器的话,只设置一个container即可 - name: dock dev: # 开发容器配置 # 开发镜像 image: golang:1.17 # 开发容器默认 Shell shell: "bash" # 文件同步的远程目录 workDir: /opt/dock-template/ # storageClass 持久化需要 storageClass 的能力来提供支持 storageClass: "csi-localpv" persistentVolumeDirs: - path: "./local-pv" # Dir to be persisted in DevContainer capacity: 1Gi # Capability of the dir # 开发容器资源的 request 和 limit resources: limits: cpu: "2" memory: 2Gi requests: cpu: "2" memory: 2Gi # 热加载 hotReload: true command: # 一键运行 run: - go - run - dock-server.go # 一键调试 debug: - go get -d github.com/go-delve/delve/cmd/dlv && - dlv - --headless - --log - --listen - :9009 - --api-version 2 - --accept-multiclient - debug - dock-server.go # debug 协议的端口 debug: remoteDebugPort: 9009 # Ports to be forwarded to local when enter devMode # 进入 Devmode 后是否自动开启端口转发功能 portForward: - 8080:8080 useDevContainer: false # https://nocalhost.dev/zh-CN/docs/config/config-pattern sync: type: "send" mode: "pattern" filePattern: - "./" ignoreFilePattern: - ".DS_Store" - ".git" - ".idea" - "logs" - "dock.tar.gz" # Specify dev mode environment parameters env: - name: GOPROXY value: https://goproxy.cn # 当存在 go.mod 文件时或处于 GOPATH 外, 其行为均会等同于 GO111MODULE=on。相当于 Go 1.13 下你可以将所有的代码仓库均不存储在 GOPATH 下 - name: GO111MODULE value: on

关联配置

当编辑好配置后,开始进行开发之前,需要进行一次将本地代码和远程环境上的工作负载关联的动作,因为 nocalhost 插件是在 ide 中跨项目共享的。意思是,你只需要配置一次远程服务,在使用 pycharm/goland 打开多个 repo 的时候,为了避免混乱,你需要将本地的某一个确定目录和远程的某一个 pod 中的某一个确定容器进行精确关联。从而开始打通流量和开始同步文件。

私有链开发(Nocalhost-让云原生时代的开发更高效)(7)

开发模式

开发模式的本质是使用 nhctl 工具,在本地计算机和远程环境之间建立 VPN 隧道和使用 syncing 文件同步工具建立实时同步机制。

nocalhost 会将原本的 pod 中将要开发的容器替换为两个容器,一个是 nocalhost_dev 开发容器,一个是 nocalhost_sidecar 容器。

nocalhost_dev 容器负责来运行我们的代码,nocalhost_sidecar 来利用 pod 内共享存储的机制来处理文件同步。

私有链开发(Nocalhost-让云原生时代的开发更高效)(8)

私有链开发(Nocalhost-让云原生时代的开发更高效)(9)

vpn 模式

在没有开启开发模式的情况下,我们开发远程程序还有另外一种方式,如果我们的应用本身非常简单,没有太多的依赖,也不依赖于 k8s 环境,不依赖于特定的存储,只是在网络层面上依赖于环境中的其他服务。

这种情况下,完全可以在本地开发,无须在本地和远程之间建立文件同步,将程序跑在远端的 pod 内。

通过运行 ProxyMode 模式,nocalhost 可以只帮我们在本地和远端 pod 容器之间建立 vpn 隧道。我们直接在本地 run 程序或者 debug 程序,在远端 pod 内能访问的服务,在本地都可以访问到。不需要写/etc/hosts 文件等操作。也无须配置环境变量。

私有链开发(Nocalhost-让云原生时代的开发更高效)(10)

私有链开发(Nocalhost-让云原生时代的开发更高效)(11)

远程 run

如果你的程序依赖非常复杂,依赖了:

或者本地环境难以搭建:

这些问题,当然都可以解决掉,可以在本地模拟出来这些依赖,也可以想办法安装上该装的依赖,但是需要付出的成本很大。

nocalhost 提供的方案则非常简单,通过实时同步本地文件直接将本地程序 run 在远端环境中。并且可以直接通过插件执行,和 ide 原生的 run 体验类似。如果是 Telepresence 则需要编写非常长的命令来执行。

私有链开发(Nocalhost-让云原生时代的开发更高效)(12)

远程 debug

注意: 使用 pycharm 需要使用商业版,社区版没有 remote debug server 的插件,无法使用 remote debug 的功能

原理

私有链开发(Nocalhost-让云原生时代的开发更高效)(13)

python 通过 pydevd 在 pod 中启动应用进程,除了应用本身会 listen 多个 port。

同时会在 pod 的 nocalhost_dev 容器上的 127.0.0.1 上 bind 一个 remote debug 的端口,该端口是 config.yaml 中定义的 remoteDebugPort。

由于本地和远程 pod 之间已经建立了 vpn 隧道通信,此时 remoteDebugPort 端口上的流量会被 route 到本地计算机的 remoteDebugPort 端口之上,至此本地和远端进程的 remote debug 通信建立,我们可以在就可以在本地 ide 中 debug 运行在 pod 中的进程了,

和在本地开发没有任何区别,并且此时并不是我们在本地模拟了应用运行所需要的资源,而是该进程本就运行在真实的环境中,没有任何区别。

python

官方的示例配置是:

私有链开发(Nocalhost-让云原生时代的开发更高效)(14)

但这个例子使用了 Flask 框架来做演示,并且命令是错误的。对于 python 程序,常见的 IDE,比如 Jetbrains pycharm,vs code,pydev 等,都是使用 pydevd 的库来做 debug 的,而且 pydevd 支持 remote debug。

因此我们需要一个和框架无关,可以 remote debug 所有 python 程序的命令。因此更通用的 debug 配置应该是:

pydevd --client 127.0.0.1 --port 8008 --file run.py

注意:

config.yaml 中配置的开发镜像中应提前安装 pydevd 依赖,才可以在 debug 参数中直接配置 pydevd 的命令,否则就需要将 debug 的值配置为:

pip install pydevd && pydevd --client 127.0.0.1 --port 8008 --file run.pygolang

golang 使用了 dlv 来 debug 进程,goland 本身也是使用该工具。因此通过预先在开发容器中安装 dlv 工具或者在 debug 时候立即安装,都可以来使用该工具。

golang 项目的 remote debug 的指令是:

dlv --headless --log --listen :9009 --api-version 2 --accept-multiclient debug app.goFAQ

如何设置开发镜像?

首先解释下开发镜像,开发镜像是 nocalhost 在替换原始容器时候的我们指定的容器,nocalhost 官方本身已经提供了很多开发容器,这些容器中都内置了很多调试工具和参数,比如 pydevd 和 dlv。

nocalhost-docker.pkg.coding.net/nocalhost/dev-images/node:latest nocalhost-docker.pkg.coding.net/nocalhost/dev-images/node:14 nocalhost-docker.pkg.coding.net/nocalhost/dev-images/golang:latest nocalhost-docker.pkg.coding.net/nocalhost/dev-images/golang:1.16 nocalhost-docker.pkg.coding.net/nocalhost/dev-images/python:latest nocalhost-docker.pkg.coding.net/nocalhost/dev-images/python:3.9 nocalhost-docker.pkg.coding.net/nocalhost/dev-images/java:latest nocalhost-docker.pkg.coding.net/nocalhost/dev-images/java:11 nocalhost-docker.pkg.coding.net/nocalhost/dev-images/ruby:latest nocalhost-docker.pkg.coding.net/nocalhost/dev-images/ruby:3.0

复制代码

针对 node,python,java,ruby 这些语言的项目,一般以来包非常的大,特别是 node 的依赖,体积和数量较为夸张,建议在开发镜像中预置依赖,减少初次初始化开发环境同步文件的时间。一般可以使用当前代码仓库分支的最新/较新镜像作为开发镜像,一般来讲依赖比较并不是很频繁。

针对 golang 的项目,依赖一般量比较小,或者在线安装也比较快,开发镜像可以使用官方原始镜像,通过在 run/debug 指令中实时安装的方式来安装依赖。可以避免频繁改动开发镜像

热加载的作用是?如何开启?

热加载:在 IDE 中对源代码文件的修改将实时同步到远端容器中,同时你配置的 Run/Debug 命令将会被重新执行。

开启方法:

私有链开发(Nocalhost-让云原生时代的开发更高效)(15)

参考

https://nocalhost.dev/zh-CN/docs/reference/nh-config

,