kubectl exec 在kubelet中的处理流程 (4)

其实整个流程也比较简单,就是各个runtime shim实现一个kubelet定义好的iterface streaming.Runtime, 然后就可以利用kubelet提供的一个统一的工具package简单实现一个streaming server。该server负责两件事情,1. getExec: 首先根据用户请求的命令返回一个带有token的url,重定向用户请求到该url。2. serveExec: 随后真正提供exec的服务,该exec调用各个runtime shim的具体实现.。

关于 RedirectContainerStreaming

在前面提到,如果kubelet开启了RedirectContainerStreaming,则kubelet会将streaming URL返回给apiserver, 随后apiserver会重定向到该streaming URL。这样设计是为了避免所有的流式请求都经过kubelet对kubelet造成压力, 但是从另外一方面考虑,这样做的缺点是无法使用kubelet的认证功能。

那接下来我们仔细探究一下该参数所起到的真正作用,kubelet中默认runtime是docker,所以这里的研究对象就是dockershim:

一: 如果将RedirectContainerStreaming参数设置为true 则返回的URL类似于/cri/exec/aRbQe4pn,可以看到这里的域名默认是没有hostname的,则重定向时默认重定向到原来的hostname,即kubelet监听的hostname。 所以其实dockershim作为默认的runtime时,设置RedirectContainerStreaming为true并不会有什么本质的区别,对kubelet的性能影响并没有减少,因为所有的流处理还是经过了kubelet。

上述/cri/exec/aRbQe4pn这个路径在kubelet server中的对应handler为criHandler。 kubelet启动的时候会对该criHandler进行赋值,将dockerService赋值给criHandler (此处docker service其实就是docker shim)

if crOptions.RedirectContainerStreaming { klet.criHandler = ds }

dockerService中的实现为:

func (ds *dockerService) ServeHTTP(w http.ResponseWriter, r *http.Request) { if ds.streamingServer != nil { ds.streamingServer.ServeHTTP(w, r) } else { http.NotFound(w, r) } }

简单调用了streamingSever的ServeHTTP方法:

func (s *server) ServeHTTP(w http.ResponseWriter, r *http.Request) { s.handler.ServeHTTP(w, r) }

在streamingServer会根据注册的handler进行处理请, 这就又回到了我们上面提到的第二步:

整个workflow如下:

kubectl exec 在kubelet中的处理流程

二: 如果设置RedirectContainerStreaming为false 则此时第一步获取到的streaming URL形如::36699/exec/8rYzmQK9。 可以看到这里是带有hostname的。 因为此时kubelet并不会将该URL返回给apiserver,会直接请求该URL进行代理,如此一来就可以通过127.0.0.1进行直接通信,这个localhost的端口必然是由docker shim来进行监听:

// Start initializes and starts components in dockerService. func (ds *dockerService) Start() error { ds.initCleanup() // Initialize the legacy cleanup flag. if ds.startLocalStreamingServer { go func() { if err := ds.streamingServer.Start(true); err != nil { klog.Fatalf("Streaming server stopped unexpectedly: %v", err) } }() } return ds.containerManager.Start() }

在dockerShim中调用了streamingServer的Start方法启动监听端口:

func (s *server) Start(stayUp bool) error { if !stayUp { // TODO(tallclair): Implement this. return errors.New("stayUp=false is not yet implemented") } listener, err := net.Listen("tcp", s.config.Addr) if err != nil { return err } // Use the actual address as baseURL host. This handles the "0" port case. s.config.BaseURL.Host = listener.Addr().String() if s.config.TLSConfig != nil { return s.server.ServeTLS(listener, "", "") // Use certs from TLSConfig. } return s.server.Serve(listener) }

通过启动一个监听在localhost的sever, 这就又回到了我们上面提到的第二步:

整个workflow如下:

kubectl exec 在kubelet中的处理流程

仔细观察这两幅workflow图片的差别就会发现, RedirectContainerStreaming 在默认的dockershim中并没有实质的作用,此时还是建议将该值设置为false来提供流处理请求的认证功能。

Reference

Kubernetes 容器运行时演进
How does 'kubectl exec' work?

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/zygdgz.html