如果你是一名 Kubernetes Operator 的开发者,你曾经是否面临过这样一个棘手的问题:如何在本地环境中高效地调试 Webhook,尤其是在涉及有效证书回调的情况下。这篇文章旨在提供一种清晰的指南,帮助你克服这一挑战,优化本地开发和测试流程。
当我们初步涉足 Kubernetes Webhook 时,面对的首个挑战通常是 Validation Webhook。对于这种验证型 Webhook 来说,我们可以通过编写自动化测试来验证其功能。
这不仅确保了我的 Webhook 按预期工作,还允许我在日常开发中临时禁用它,从而加快了整个开发过程。这种方法让我能够巧妙地避免复杂的调试问题,而不对整体功能造成任何影响。
if os.Getenv("ENABLE_WEBHOOKS") != "false" { if err = (&webappv1.Guestbook{}).SetupWebhookWithManager(mgr); err != nil { setupLog.Error(err, "unable to create webhook", "webhook", "Guestbook") os.Exit(1) }}
然而,对于 Mutating Webhook 来说,情况就变得有点复杂了。这类 Webhook 通常负责埋点的行为甚至更深层次的集群操作,比如注入 sidecar,这时候单靠自动化测试显然是不够的。我们需要一个更加高效的本地测试和调试方法。
在我们团队中,有同事采用 Kind 来部署和测试服务,这种方法非常值得称赞。它完全符合 K8s 的操作模式,为我们提供了一个接近生产环境的本地测试平台。但是,大家可能也注意到了,这种方式存在一个效率瓶颈:每次进行代码更改后,都需要重新构建 Docker 镜像并部署到集群中,这一过程既耗时又影响开发流程的连贯性。
作为开发工程师,我们渴望的是一个极速的内部开发循环,一个不再需要频繁的 docker build、docker push 或繁琐的部署流程,即使这些已经完全自动化。
我们希望能够使用本地熟悉的开发工具,如 VS Code 或者 IntelliJ IDEA 进行本地调试,而不是先部署到集群环境,再通过日志来分析错误这种远程调试模式。
在不禁用 Webhook 的情况下,我们在本地启动 controller 后会有如下错误。这个比较好处理,我们只要使用自签证书,注入到 WebhookServer 即可,在前面的文章中我介绍过很多次,这里不再赘述。
2023-11-26T12:55:17+08:00 INFO Stopping and waiting for webhooks...2023-11-26T12:55:17+08:00 INFO Wait completed, proceeding to shutdown the manager2023-11-26T12:55:17+08:00 ERROR setup problem running manager {"error": "open /var/folders/hn/v2s5bx...00000gn/T/k8s-webhook-server/serving-certs/tls.crt: no such file or directory"}...
我们来运行一个示例,想必下面这个错误大家都非常熟悉吧,这个是因为 Webhook 注册的地址'不对',它是集群内的地址。
➜ kubectl apply -f ./config/samplesError from server (InternalError): error when creating "config/samples/webapp_v1_guestbook.yaml": Internal error occurred: failed calling webhook "vguestbook.kb.io": failed to call webhook: Post "https://testing-webhooks-webhook-service.testing-webhooks-system.svc:443/validate-webapp-foobar-ai-v1-guestbook?timeout=10s": no endpoints available for service "testing-webhooks-webhook-service"
我们再来看下 ValidatingWebhookConfiguration 的配置。
apiVersion: admissionregistration.k8s.io/v1kind: ValidatingWebhookConfigurationmetadata: name: testing-webhooks-validating-webhook-configurationwebhooks:- admissionReviewVersions: - v1 clientConfig: service: name: testing-webhooks-webhook-service namespace: testing-webhooks-system path: /validate-webapp-foobar-ai-v1-guestbook port: 443 failurePolicy: Fail matchPolicy: Equivalent name: vguestbook.kb.io rules: - ... ...
在这个配置中,webhooks 字段定义了一个或多个要注册的 Webhook。每个 Webhook 通过 clientConfig 配置与 Kubernetes API 服务器的连接方式。
正如大家所看到的 https://testing-webhooks-webhook-service.testing-webhooks-system.svc:443/validate-webapp-foobar-ai-v1-guestbook,这个默认地址其实就是 K8s 集群内部的地址。这恰是 K8s 中处理 Webhook 的常规方法,其中 service 字段指向集群内运行的特定服务。
然而,在本地开发环境中,我们只在本地运行了我们的 Operator,直接使用内部服务是不大可能的,因为它要求 Webhook 服务必须部署在 K8s 集群中。
当我们在 K8s 中配置 Webhook 时,了解其配置细节是非常重要的。kubectl explain 是一个非常实用的小工具,它可以帮助我们深入理解 K8s 资源的各个属性。
以 ValidatingWebhookConfiguration.webhooks.clientConfig 为例:
➜ kubectl explain ValidatingWebhookConfiguration.webhooks.clientConfigGROUP: admissionregistration.k8s.ioKIND: ValidatingWebhookConfigurationVERSION: v1FIELD: clientConfig <WebhookClientConfig>:DESCRIPTION:FIELDS: caBundle <string> `caBundle` is a PEM encoded CA bundle which will be used to validate the webhook's server certificate. If unspecified, system trust roots on the apiserver are used. service <ServiceReference> `service` is a reference to the service for this webhook. Either `service` or `url` must be specified. If the webhook is running within the cluster, then you should use `service`. url <string> `url` gives the location of the webhook, in standard URL form (`scheme://host:port/path`). Exactly one of `url` or `service` must be specified.
通过以上提供的详细信息,不难发现 clientConfig 它除了通过定义 Service 让 API 服务器连接到 WebhookServer 外,还有另外一种方式,那就是直接通过 URL 连接。
为了解决这个问题,我们可以将 Webhook 的配置从服务转变为直接使用 URL。
通过将 clientConfig 中的 service 字段替换为 url 字段,我们可以指定 Webhook 服务的外部 URL。这样一来,开发者可以在本地运行 Webhook 服务,并通过公开的 URL 使其可被 Kubernetes API 服务器访问。
例如:
webhooks:- admissionReviewVersions: - v1 clientConfig: url: https://testing-webhooks.loca.lt/validate-webapp-foobar-ai-v1-guestbook
这种方法使得在本地开发环境中调试 Webhook 变得更加灵活和便捷。开发者可以使用本地服务器或通过隧道(如 ngrok[1] 或 localtunnel[2])暴露的服务,从而实现在本地环境中的有效调试。
事实上,我最初的首选是 ngrok,因为这玩意确实好用,它还有个 localhost:4040 非常的实用,但遗憾的是,它的 tls 能力是付费的。幸好,有很多平替工具可以选择,比如 localtunnel,用起来也非常的方便。
步骤 1: 在我们的 main.go 需要接收一个证书路径
...var certDir stringflag.StringVar(&certDir, "webhook-cert-dir", "/tmp/k8s-webhook-server/serving-certs", "Admission webhook cert/key dir.")...mgr, err := ctrl.NewManager(ctrl.GetConfigOrDie(), ctrl.Options{ Scheme: scheme, Metrics: metricsserver.Options{BindAddress: metricsAddr}, HealthProbeBindAddress: probeAddr, WebhookServer: webhook.NewServer(webhook.Options{ CertDir: certDir, Port: 9443, }), LeaderElection: enableLeaderElection, LeaderElectionID: "dcc993a0.foobar.ai",})...
步骤 2:调整 Makefile,并启动我们的程序
修改 Makefile 文件,让其可以接收证书目录:
.PHONY: runrun: manifests generate fmt vet ## Run a controller from your host. go run ./cmd/main.go --webhook-cert-dir ./config/certs
启动程序:
➜ make run...go run ./cmd/main.go --webhook-cert-dir ./config/certs2023-11-26T11:18:42+08:00 INFO controller-runtime.builder Registering a mutating webhook {"GVK": "webapp.foobar.ai/v1, Kind=Guestbook", "path": "/mutate-webapp-foobar-ai-v1-guestbook"}2023-11-26T11:18:42+08:00 INFO controller-runtime.webhook Registering webhook {"path": "/mutate-webapp-foobar-ai-v1-guestbook"}2023-11-26T11:18:42+08:00 INFO controller-runtime.builder Registering a validating webhook {"GVK": "webapp.foobar.ai/v1, Kind=Guestbook", "path": "/validate-webapp-foobar-ai-v1-guestbook"}2023-11-26T11:18:42+08:00 INFO controller-runtime.webhook Registering webhook {"path": "/validate-webapp-foobar-ai-v1-guestbook"}...2023-11-26T11:18:42+08:00 INFO controller-runtime.webhook Starting webhook server...2023-11-26T11:18:42+08:00 INFO controller-runtime.webhook Serving webhook server {"host": "", "port": 9443}...
步骤 3:将本地主机服务器通过隧道公开
# 安装npm install -g localtunnel# 使用 lt 命令启动隧道lt --port 9443 / --local-https / --local-ca $(pwd)/certs/ca.crt / --local-cert $(pwd)/certs/tls.crt / --local-key $(pwd)/certs/tls.key / --subdomain testing-webhooksyour url is: https://testing-webhooks.loca.lt
步骤 4:修改 ValidatingWebhookConfiguration 配置
我们将默认的 service 服务。
webhooks:- admissionReviewVersions: - v1 clientConfig: service: name: testing-webhooks-webhook-service namespace: testing-webhooks-system path: /validate-webapp-foobar-ai-v1-guestbook port: 443...
替换换成 url 直连模式
webhooks:- admissionReviewVersions: - v1 clientConfig: url: https://testing-webhooks.loca.lt/validate-webapp-foobar-ai-v1-guestbook...
以上仅以 ValidatingWebhookConfiguration 为例,如果你的 controller 同时使用了 MutatingWebhookConfiguration,别忘了,处理方式是一样的。
步骤 5:看看实际效果如何
最后,我们再执行同样一个用例,就可以被当前的 ValidatingWebhook 拦截到了。
➜ kubectl apply -f ./config/samples/webapp_v1_guestbook.yamlThe Guestbook "guestbook-sample" is invalid: metadata.name: Invalid value: "guestbook-sample": Guestbook name must be no more than 5 characters for test purposes
本文链接:http://www.28at.com/showinfo-26-34633-0.html云原生小技巧 : 如何在本地调试 Kubernetes Webhook?
声明:本网页内容旨在传播知识,若有侵权等问题请及时与本网联系,我们将在第一时间删除处理。邮件:2376512515@qq.com
上一篇: 四种消息队列,如何选型?