专业的编程技术博客社区

网站首页 > 博客文章 正文

k8s高级面试题:kubectl port-forward 原理

baijin 2024-10-12 02:20:16 博客文章 13 ℃ 0 评论

这道面试题虽然简单,但却很少有人能回答出来,很多人认为kubectl 直接把流量转发到pod上面了,但其实完全不是这回事。所以我只在面试k8s 高级工程师的时候才会去问这个问题。关于port-forward 使用参见之前发的 k8s 实战里面的文章,下面直接进入正题。

当面执行kubectl port-forward,首先kubectl 会先启动一个本地监听,然后发送请求port-forward发到apiserver,这个其实很容易理解,kubectl 所有的请求都走apiserver 。

	req := o.RESTClient.Post().
		Resource("pods").
		Namespace(o.Namespace).
		Name(pod.Name).
		SubResource("portforward")

apiserver 接收到请求后会将请求转发到 kubelet ,kubelet 启动的时候就注册了port-forward接口,这里多说一句,kubelet 除了注册port-forward 同时还注册了 exec 和 attach,他们原理都是类似的。

	s.addMetricsBucketMatcher("portForward")
	ws = new(restful.WebService)
	ws.
		Path("/portForward")
	ws.Route(ws.GET("/{podNamespace}/{podID}").
		To(s.getPortForward).
		Operation("getPortForward"))
   . . .
	s.restfulCont.Add(ws)

他们后端都是调用了 getPortForward接口,这个方法里面判断是否走redirect ,如果是redirect,那么则返回一个url 给 apiserver,让apiserver重新和CRI 建立连接,如下所示 exec 请求链路,它和port-forward 完全一样,如果不是redirect 则直接返回数据流。

真正实现 port-forward 能力的是在CRI server里面 cri/pkg/server/sandbox_portforward_unix.go里面。

err = netNSDo(func(_ ns.NetNS) error {
	 // 代码中删除部分无关逻辑
		conn, err := net.Dial("tcp4", fmt.Sprintf("localhost:%d", port))
		
		go func() {
			_, err := io.Copy(stream, conn)
			errCh <- err
		}()

		go func() {
	
			_, err := io.Copy(conn, stream)
			errCh <- err
		}()

通过直接在容器的namespace 里面直接dail 端口,然后建立一个双向管道,来回拷贝数据。

总结一下整个 port-forward 的整个链路: kubectl --> apiserver --> kubelet --> CRI server。

本文暂时没有评论,来添加一个吧(●'◡'●)

欢迎 发表评论:

最近发表
标签列表