前言
这个周一fetch到一个任务,前端需要使用webshell的方式,链接到正在使用中的kubernetes pods,实现这样的效果
kubectl exec -it podName -c containerName -n namespaceName [bash,sh]
分析
ok,任务来了,怎么实现尼?然后就在Google了一下websocket kubernetes pods关键字,原来kubernetes早就想到可能会有这样的场景,因此已经提供了一个接口,用于远程链接并执行命令,格式大概为:
ip:port/namespace/xxxx/pods/xxxx/container/xxxx/exec/xxxx
并且已经封装在了kubernetes client中的remotecommand包中,我们只需要new一个NewSPDYExecutor出来,然后自定义写入写出操作,然后启动一个web容器,将http请求升级为websocket,跑起来就行了。
实践
config, _ := rest.InClusterConfig()
var clientSet, _ = kubernetes.NewForConfig(config)
request := clientSet.
CoreV1().
RESTClient().
Post().
Resource("pods").
Name(podInfo.Pod).
Namespace(podInfo.Namespace).
SubResource("exec")
request.VersionedParams(
&v1.PodExecOptions{
Container: podInfo.Container,
Command: []string{},
Stdin: true,
Stdout: true,
Stderr: true,
TTY: true,
},
scheme.ParameterCodec,
)
// 最重要就是这里了,NewSPDYExecutor,这个执行器定义了和k8s的操作,接口的具体封装
executor, _ := remotecommand.NewSPDYExecutor(
config, http.MethodPost, request.URL(),
)
// 然后给这个执行器赋予自定义的写入写出操作
executor.Stream(remotecommand.StreamOptions{
Stdin: wsRead, // 这里接收一个写入操作,也就是要执行的命令,丢进去
Stdout: wsWrite,// 这里是个写出操作,将结果丢回来
Stderr: wsWrite,// 错误结果的写出
Tty: true,
TerminalSizeQueue: wsChan,//调整窗口大小的,接受一个对象,一般会放进channel中。阻塞读
})
注意
这里所说的写入操作,写出操作
type Reader interface {
**// 其实k8s client只关心这个p数组,也就是你可以从自己和前段建立的websocket中读到要执行的命令,然后赋值给p,返回值n不为0。
// k8s client就会拿到这个p,去执行
**
Read(p []byte) (n int, err error)
}
type Writer interface {
**// 这里也是一样,k8s client会把上一步执行命令返回的结果赋值给p,然后我们将这个p,写会到自己的流中即可。
**
Write(p []byte) (n int, err error)
}
总结
整体来说还是挺好玩的,尤其是这一套模版,值得学习,这个和java中的模版方法很像,也就是主流程已经定义好,你只需要实现预留的抽象方法。
后记
好像docker也提供了类似的接口,可以直接执行命令的,所以没准儿k8s是在docker开放的接口上包装了一层,提供的接口,给我们用的。。。