背景

虽然goland、vscode都支持debug甚至远程debug,但是在调试过程中,难免修改配置、添加调试信息、修改代码并验证,这样就需要提交代码,重新编译、部署以验证功能是否符合预期,这样就拉长时间线,拉低了开发效率。一个系统可能还好点,如何项目A调用B、B又调用C,想要调试C,本地调试的话就需要配置3套环境,分别运行3个项目,流程线可想而知。

k8s容器化

使用k8s容器化,可以极大地提高开发效率,将一般项目改造成k8s部署的方式,只需将配置信息放入.yaml文件并设置相关资源、镜像等信息。

Nocalhost

Nocalhost 是一款开源的基于 IDE 的云原生应用开发工具,其具有如下特点:

  • 直接在 Kubernetes 集群中构建、测试和调试应用程序。
  • 提供易于使用的 IDE 插件(支持 VS Code 和 JetBrains),即使在 Kubernetes 集群中进行开发和调试,Nocalhost 也能保持和本地开发一样的开发体验。
  • 使用即时文件同步进行开发: 即时将您的代码更改同步到远端容器,而无需重建映像或重新启动容器。

使用Nocalhost直接在k8s中进行开发,有如下几点优势:

  • 生产环境相似 - 开发环境与你的生产环境非常相似,让你更有信心在发布新功能时一切都像在生产环境中一样工作。
  • 更改即时生效 - 通过文件同步,对代码的所有更改都可以在容器中立即生效,而无需重建镜像或重新部署容器,从而提升开发效率。
  • 灵活的扩展性 - 开发人员无需再担心本地资源不足。
  • 降低成本 - 更有效地使用资源并降低 IT 设施成本。
  • 方便调试 - Nocalhost支持远程debug,可以方便地在本地计算机上复现线上的问题。
  • 减少本地配置 - 由于线上、测试环境已经部署过,Nocalhost使用这些现成的资源,不需再在本地安装mysql、redis等进行配置,也不必担心本地资源的限制,从而可以更愉快地本地开发,远程k8s部署并调试。

Nocalhost工作原理

Nocalhost 由单个二进制 CLI 和 IDE 插件组成,可以直接与IDE 一起配合调试程序。 Nocalhost 不需要服务器端组件,因为它通过 KubeConfig 直接与 Kubernetes 集群通信,就像 kubectl 一样。

使用Nocalhost进行远程调试

Nocalhost远程调试过程,主要分为Start DevMode启动调试模式、Associate Local DIR关联本地目录、设置断点、Remote Debug远程调试、本地请求接口、查看断点处信息等过程。

安装Nocalhost

goland在File → Settings → Plugins中的搜索框中,搜索nocalhost点install,并Apply、Ok,即可进行快速安装Nocalhost插件。默认安装在C:\Users\xxx.nh\目录下,生成的kubeConfigs也会在这个目录下。

Nocalhost config

进行nocalhost remote debug之前,还需要设置nocalhost的config配置信息。

注意:在配置nocalhost的config.yaml文件时,对debug命令的设置,要么将debug命令放在单独的debug.sh文件中,要么将debug命令中的每个参数进行list化处理,否则(将debug命令在一行在.yaml配置文件中)无法解析。

如下,是将debug命令list化处理的方式。

如下,是将debug命令单独放在一个sh脚本文件中。

debug.sh文件内容:
dlv --headless --log --listen :9009 --api-version 2 --accept-multiclient debug main.go

Start DevMode

在goland中右侧,点击Nocalhost,会以目录树的形式展示所有的k8s集群,找到相关k8s的namespace,点对应的项目,然后点Workloads工作负载 → Deployments部署,看到对应的pod。鼠标右键点Start DevMode,等待启动调试模式。

启动调试模式过程中的日志信息如下:

[cmd] C:\Users\xxx\.nh\bin\nhctl.exe dev start xxx --deployment xxx --local-sync D:\Go1.16\gopath\src\xxx.com\xxx\xxx --container xxx --controller-type Deployment --without-terminal --kubeconfig C:\Users\xxx\.nh\intellij-plugin\kubeConfigs\92c19749-c2e4-45ac-b732-35bd2959f8e0_config --namespace vc
Starting DevMode...
[name: xxx serviceType: deployment]                    Success load svc config from local file [D:\Go1.16\gopath\src\xxx.com\xxx\xxx\.nocalhost\config.yaml]
No previous syncthing process (25336) found
Deployment xxx replicas is already 1, no need to scale
Mount workDir to emptyDir
Updating development container...
Previous replicaSet xxx-f67c799f5 has not been terminated, waiting revision 2 to be ready
This may take several minutes, depending on the load of your k8s cluster
Waiting pod to start...
Forwarding 30000:8032
Port-forward 30000:8032 has been started+  Dev container has been updatedNo previous syncthing pid found(ignore)
Syncthing port-forward pod xxx-85659d48bd-p6p5j, namespace vc
Port-forward 60979:60979 has been started
ignoredPattern: syncedPattern: 
!**+  File sync started

看到上面Port-forward 30000:8032 has been started的信息,说明新的pod已经起来,容器端口为8032,映射到对外端口30000。No previous syncthing pid found(ignore)这条日志说明,前一个pod已经暂时Removed,找不到其pid,忽略这条信息即可。

Associate Local DIR

由于打开的目录并不一定是当前要执行命令的目录,所以可以通过Associate Local DIR指定要进行关联后续操作的目录。可以选择New创建一个Window窗口,进行后续debug操作。

点击Associate Local DIR,选择关联的目录后,使用New Window的方式打开新的窗口,在新的窗口中进行后续操作,之前的窗口就可以关闭。

出现如下所示,Success load svc config from local file表示成功关联当前目录,并加载nocalhost中的config配置信息。

[cmd] C:\Users\xxx\.nh\bin\nhctl.exe dev associate xxx --associate D:\Go1.16\gopath\src\xxx.com\xxx\xxx --controller-type Deployment --deployment xxx --container nocalhost-dev --kubeconfig C:\Users\xxx\.nh\intellij-plugin\kubeConfigs\92c19749-c2e4-45ac-b732-35bd2959f8e0_config --namespace vc
[name: xxx serviceType: deployment]                    Success load svc config from local file [D:\Go1.16\gopath\src\xxx.com\xxx\xxx\.nocalhost\config.yaml]

Remote Debug

右键nocalhost项目,选择Remote Debug,以启动远程调试。

如果有不同的sidecar-image镜像版本,在启动remote debug模式的时候,由于在 .nocalhost/config.yaml配置文件containers里面配置了image: hub.kce.ksyun.com/ksc-vc/basic/golang:1.16.5-0.0.12和sidecar_image: hub.kce.ksyun.com/ksc-vc-public/nocalhost-sidecar:sshversion,会出现如下界面,需要 需要对应的sidecar和prebuild镜像版本,这里分别选择sidecar和golang版本即可。

出现downloading xxxgolang包所示的信息,说明进入remote debug模式。

如下,是启动远程调试过程中,打印的相关日志信息。首先,下载相关依赖包;然后,运行到断点处。

C:\Users\xxx\.nh\bin\nhctl.exe exec xxx --deployment xxx --controller-type deployment --command bash --command -c --command ./debug.sh --kubeconfig C:\Users\xxx\.nh\nhctl\kubeconfig\3ba9842b0e19ed1f7fdac39ae9792d88c07e7c1f --namespace vc
go: downloading github.com/astaxie/beego v1.12.2
go: downloading github.com/garyburd/redigo v1.6.0
go: downloading github.com/go-sql-driver/mysql v1.5.0
go: downloading go.uber.org/multierr v1.5.0
go: downloading go.uber.org/zap v1.16.0
go: downloading gopkg.in/natefinch/lumberjack.v2 v2.0.0
go: downloading github.com/prometheus/client_golang v1.7.0
go: downloading golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550
go: downloading github.com/shiena/ansicolor v0.0.0-20151119151921-a422bbe96644
go: downloading gopkg.in/yaml.v2 v2.2.8
go: downloading go.uber.org/atomic v1.6.0
go: downloading github.com/prometheus/common v0.10.0
go: downloading github.com/prometheus/client_model v0.2.0
go: downloading github.com/cespare/xxhash v1.1.0
go: downloading github.com/beorn7/perks v1.0.1
go: downloading github.com/golang/protobuf v1.4.2
go: downloading github.com/prometheus/procfs v0.1.3
go: downloading github.com/cespare/xxhash/v2 v2.1.1
go: downloading github.com/matttproud/golang_protobuf_extensions v1.0.1
go: downloading google.golang.org/protobuf v1.23.0
go: downloading golang.org/x/sys v0.0.0-20200918174421-af09f7315aff
go: downloading golang.org/x/net v0.0.0-20200421231249-e086a090c8fd
go: downloading golang.org/x/text v0.3.2
API server listening at: [::]:9009
2021-09-29T15:25:30+08:00 info layer=debugger launching process with args: [/work/conf/__debug_bin]
2021-09-29T15:25:31+08:00 info layer=debugger created breakpoint: &api.Breakpoint{ID:1, Name:"", Addr:0xcbba95, Addrs:[]uint64{0xcbba95}, File:"/work/conf/models/notify_model.go", Line:435, FunctionName:"xxx/models.(*NotifyInput).UpdateInputParams", Cond:"", HitCond:"", Tracepoint:false, TraceReturn:false, Goroutine:false, Stacktrace:0, Variables:[]string(nil), LoadArgs:(*api.LoadConfig)(nil), LoadLocals:(*api.LoadConfig)(nil), WatchExpr:"", WatchType:0x0, VerboseDescr:[]string(nil), HitCount:map[string]uint64{}, TotalHitCount:0x0, Disabled:false}
2021-09-29T15:25:31+08:00 info layer=debugger created breakpoint: &api.Breakpoint{ID:2, Name:"", Addr:0xcbc03a, Addrs:[]uint64{0xcbc03a}, File:"/work/conf/models/notify_model.go", Line:510, FunctionName:"xxx/models.(*NotifyInput).UpdateInputParams", Cond:"", HitCond:"", Tracepoint:false, TraceReturn:false, Goroutine:false, Stacktrace:0, Variables:[]string(nil), LoadArgs:(*api.LoadConfig)(nil), LoadLocals:(*api.LoadConfig)(nil), WatchExpr:"", WatchType:0x0, VerboseDescr:[]string(nil), HitCount:map[string]uint64{}, TotalHitCount:0x0, Disabled:false}
2021-09-29T15:25:31+08:00 info layer=debugger created breakpoint: &api.Breakpoint{ID:3, Name:"", Addr:0xcbbb76, Addrs:[]uint64{0xcbbb76}, File:"/work/conf/models/notify_model.go", Line:455, FunctionName:"xxx/models.(*NotifyInput).UpdateInputParams", Cond:"", HitCond:"", Tracepoint:false, TraceReturn:false, Goroutine:false, Stacktrace:0, Variables:[]string(nil), LoadArgs:(*api.LoadConfig)(nil), LoadLocals:(*api.LoadConfig)(nil), WatchExpr:"", WatchType:0x0, VerboseDescr:[]string(nil), HitCount:map[string]uint64{}, TotalHitCount:0x0, Disabled:false}
2021-09-29T15:25:31+08:00 info layer=debugger created breakpoint: &api.Breakpoint{ID:4, Name:"", Addr:0xcc5215, Addrs:[]uint64{0xcc5215}, File:"/work/conf/controllers/notify_controller.go", Line:261, FunctionName:"xxx/controllers.(*NotifyController).NotifyFromPost", Cond:"", HitCond:"", Tracepoint:false, TraceReturn:false, Goroutine:false, Stacktrace:0, Variables:[]string(nil), LoadArgs:(*api.LoadConfig)(nil), LoadLocals:(*api.LoadConfig)(nil), WatchExpr:"", WatchType:0x0, VerboseDescr:[]string(nil), HitCount:map[string]uint64{}, TotalHitCount:0x0, Disabled:false}
2021-09-29T15:25:31+08:00 debug layer=debugger continuing
2021/09/29 15:25:31 [cron.go:17] [I] run cron...
2021/09/29 15:25:31.781 [I] [asm_amd64.s:1371]  http server Running on http://0.0.0.0:8032
2021/09/29 15:25:31.781 [I] [asm_amd64.s:1371]  Admin server Running on 0.0.0.0:8031
2021-09-29T15:25:34+08:00 error layer=rpc rpc:invalid character 'p' looking for beginning of value
2021-09-29T15:26:04+08:00 error layer=rpc rpc:invalid character 'p' looking for beginning of value
2021-09-29T15:26:34+08:00 error layer=rpc rpc:invalid character 'p' looking for beginning of value
2021-09-29T15:27:04+08:00 error layer=rpc rpc:invalid character 'p' looking for beginning of value
2021-09-29T15:27:34+08:00 error layer=rpc rpc:invalid character 'p' looking for beginning of value
2021-09-29T15:28:04+08:00 error layer=rpc rpc:invalid character 'p' looking for beginning of value

看到layer=debugger created breakpoint: &api.Breakpoint{ID:1, Name:"", Addr:0xcbba95, Addrs:[]uint64{0xcbba95},说明进入当前设置的断点位置。

启动debug模式后,terminal变成进入到pod里面,可以通过ps -ef查看是否有相关进程正常启动。

Reset Pod

调试结束,或者出现问题,不是自己想要的结果,可以Reset Pod,修改配置后,保存配置,自动将修改推送到远程,并启动pod。之后,再Start DevMode进行Remote Debug即可。

[cmd] C:\Users\xxx\.nh\bin\nhctl.exe dev reset xxx --deployment xxx --controller-type Deployment --kubeconfig C:\Users\xxx\.nh\intellij-plugin\kubeConfigs\92c19749-c2e4-45ac-b732-35bd2959f8e0_config --namespace vc
Stopping port forward
Annotation nocalhost.origin.spec.json found, use itDeleting current revision...Recreating original revision...
Service xxx has been reset.
采坑记录

查看使用的nocahost CLI的版本,进入nocalhost安装目录,Windows默认在C:\Users\xxx.nh\bin目录(其他系统在~/.nh/bin目录下),执行nhctl version命令。

该版本的nocalhost对Windows系统支持不好,会出现以下问题,下面记录以下踩坑过程及解决方式。

无法识别CRLF问题

使用Windows系统,在Remote Debug过程中,会出现无法解决CRLF换行的问题,在命令行中对git进行如下配置:

git config --global core.autocrlf input

无法转义路径\问题

启动Remote Debug模式过程中,发现对C:\Users\xxx.nh\bin\路径中的\无法正确转义,解析为C:Usersxxx.nhbin形式,而使用Copy Terminal Exec Command方式拿到的命令,对其进行转义(将\替换为\)可以正确进行debug。

C:\Users\xxx\.nh\bin\nhctl.exe exec xxx --deployment xxx --controller-type deployment --command bash --command -c --command ./debug.sh --kubeconfig C:\Users\xxx\.nh\nhctl\kubeconfig\3ba9842b0e19ed1f7fdac39ae9792d88c07e7c1f --namespace vcC:\\Users\\xxx\\.nh\\bin\\nhctl.exe k exec xxx-f67c799f5-vjv49 --stdin --tty --container xxx --kubeconfig C:\\Users\\xxx\\.nh\\intellij-plugin\\kubeConfigs\\71c60295-9716-4aa5-aa3f-a74268b332bc_config --namespace vc -- sh -c "clear; (zsh || bash || ash || sh)"

最后,定位到时配置了goland中的terminal为git的bash.exe,将其改为Windows自带的powershell即可。

虽然,Windows自带的命令行是cmd,但是不知道为什么配置git的bash会无法解析路径,联系nocalhost那边开发,说十一后的下一个版本修复。

configure the run/debug command first

在使用vscode进行Remote Debug时,出现如下问题。


点devstart进入开发模式,然后在goland的Run → Edit Configurations添加Nocalhost Go,在右侧将其Name命令为Debug。

然后点Apply,OK即可。

Exception in plugin Nocalhost

这是 nhctl 在windows下的一个问题,由于日志归档导致的 stdout 多了一些日志,导致插件这边解析出问题。

把这些.log.gz日志文件或者~/.nh/nhctl/logs/ 这个目录删除即可。

Failed to replace dev container

在进入开发模式Start DevMode过程中,如果出现如下问题,可能是对已经进入开发模式的容器再次进开发模式。可以右键工作负载,选 Reset Pod ,然后再进开发模式就能解决。

[cmd] C:\Users\xxx\.nh\bin\nhctl.exe dev associate xxx --associate D:\Go1.16\gopath\src\xxx.com\xxx\xxx --controller-type Deployment --deployment xxx --container nocalhost-dev --kubeconfig C:\Users\xxx\.nh\intellij-plugin\kubeConfigs\92c19749-c2e4-45ac-b732-35bd2959f8e0_config --namespace vc
[name: xxx serviceType: deployment]                    Success load svc config from local file [D:\Go1.16\gopath\src\xxx.com\xxx\xxx\.nocalhost\config.yaml]
[cmd] C:\Users\xxx\.nh\bin\nhctl.exe sync-status xxx --deployment xxx --controller-type Deployment --wait --timeout 600 --kubeconfig C:\Users\xxx\.nh\intellij-plugin\kubeConfigs\92c19749-c2e4-45ac-b732-35bd2959f8e0_config --namespace vc
{"status":"idle","msg":"sync finished"}
[cmd] C:\Users\xxx\.nh\bin\nhctl.exe dev start xxx --deployment xxx --local-sync D:\Go1.16\gopath\src\xxx.com\xxx\xxx --container xxx --controller-type Deployment --without-terminal --kubeconfig C:\Users\xxx\.nh\intellij-plugin\kubeConfigs\92c19749-c2e4-45ac-b732-35bd2959f8e0_config --namespace vc
Starting DevMode...
[name: xxx serviceType: deployment]                    Success load svc config from local file [D:\Go1.16\gopath\src\xxx.com\xxx\xxx\.nocalhost\config.yaml]
No previous syncthing pid found(ignore)
Deployment xxx replicas is already 1, no need to scale
Mount workDir to emptyDir
Updating development container...
Failed to replace dev container: : Deployment.apps "xxx" is invalid: [spec.template.spec.volumes[4].name: Duplicate value: "nocalhost-syncthing", spec.template.spec.volumes[5].name: Duplicate value: "nocalhost-syncthing-secret", spec.template.spec.volumes[6].name: Duplicate value: "nocalhost-shared-volume", spec.template.spec.containers[1].name: Duplicate value: "nocalhost-dev", spec.template.spec.containers[3].name: Duplicate value: "nocalhost-sidecar"]
Resetting workload...
Failed to get syncthing pid: open C:\Users\xxx\.nh\nhctl\ns\vc\76707b84\xxx\syncthing\xxx\syncthing.pid: The system cannot find the file specified.
Stopping port forward
Annotation nocalhost.origin.spec.json found, use itDeleting current revision...Recreating original revision...
: : Deployment.apps "xxx" is invalid: [spec.template.spec.volumes[4].name: Duplicate value: "nocalhost-syncthing", spec.template.spec.volumes[5].name: Duplicate value: "nocalhost-syncthing-secret", spec.template.spec.volumes[6].name: Duplicate value: "nocalhost-shared-volume", spec.template.spec.containers[1].name: Duplicate value: "nocalhost-dev", spec.template.spec.containers[3].name: Duplicate value: "nocalhost-sidecar"]

Loading clusters error加载k8s集群错误

使用nocalhost的v0.5.9-212的Windows版本,会出现点击nocalhost后不展示远程k8s集群树 的问题。


点击+添加 本地kubeconfig,并点击右上角同步刷新按钮,还是不显示集群列表。

是因为该版本,对日志归档解析有问题,导致加载集群错误。

进入 C:\Users\xxx.nh\bin目录,执行nhctl.exe list查看

C:\Users\xxx\.nh\bin>nhctl.exe2021-09-30 14:07:23.9134658 +0800 CST m=+0.119345801 write error: can't rename log file: rename C:\Users\xxx\.nh\nhctl\logs\nhctl.log C:\Users\xxx\.nh\nhctl\logs\nhctl-2021-09-30T06-07-23.913.log: The process cannot access the file because it is being used by another process.

发现在C:\Users\xxx.nh\nhctl\logs目录下多了一个nhctl-2021-09-30T06-07-23.913.log日志文件,而打开该目录发现只有一个nhctl.log日志文件。

于是把nhctl.log日志文件删除,注意:需要在Windows底栏右键选择【任务管理器】,找到正在运行的nhctl.exe进程,先结束该进程,才能删掉或替换nhtcl目录中的文件。

不显示断点处变量的值

使用goland启动dev mode后,设置断点、启动remote debug、然后请求接口后,在设置的断点处不停留、悬停鼠标不显示变量的值。

参考 https://youtrack.jetbrains.com/issue/GO-11011#focus=Comments-27-4864841.0-0 设置如下dlv路径:

dlv.path=D:\Go1.16\gopath\bin\dlv.exe

参考

nocalhost官方文档

git提示CRLF will be replaced by LF问题及解决

M1 Macbook Goland无法debug的问题解决