在进一步解释这个问题之前,我们需要区分一下Terway和Terwayd。从本质上来说,Terway和Terwayd是客户端和服务器的关系,这跟Flannel和Flanneld之间的关系是一样的。Terway按照Kubelet的定义,实现了CNI接口的插件,如图14-5所示。

图14-5 Terway架构图
而在上一节最后我们看到的问题是,Kubelet调用CNI Terway去配置Pod网络的时候,Terway长时间无响应。正常情况下这个操作应该是秒级的,非常快速。而出问题的时候,Terway没有正常完成任务,因而我们在集群节点上看到大量Terway进程堆积,如图14-6所示。

图14-6 Terway进程堆积状态
同样,我们可以发送SIGABRT给这些Terway插件进程,来打印出进程的调用栈。下面是其中一个Terway的调用栈。这个线程在执行cmdDel函数,其作用是删除一个Pod网络相关配置。

以上线程通过rpc调用Terwayd来真正移除Pod网络,所以我们需要进一步排查Terwayd的调用栈,以进一步定位此问题。
Terwayd作为Terway的服务器端,接受Terway的远程调用,并替Terway完成其cmdAdd或者cmdDel来创建或者移除Pod网络配置。我们在上面的截图里可以看到,集群节点上有上千个Terway进程,它们都在等待Terwayd,所以实际上Terwayd里也有上千个线程在处理Terway的请求。使用下面的命令,可以在不重启Terwayd的情况下输出调用栈。

因为Terwayd的调用栈非常复杂,而且几乎所有的线程都在等锁,直接去分析锁的等待持有关系比较复杂。这个时候我们可以使用“时间大法”,即假设最早进入等待状态的线程,有很大概率是持有锁的线程。经过调用栈和代码分析,我们发现下面这个是等待时间最长(1595分钟),并且拿了锁的线程,而这个锁会阻碍所有创建或移除Pod网络的线程。

