k8s集群中golang线程数过多的问题

问题背景是这样的:

发现K8S线上集群,单个node上线程数超过5000个,这样的数量肯定是不合理的。

机型是32核,限制POD的cpu个数是1个,然而发现POD内golang线程数量超过30+。

过大的线程数量+繁忙的业务量,将会导致严重的上下文切换,所以该问题需要引起重视。

我怀疑Golang Runtime在容器中是不是错误识别了宿主机的CPU数量,因为Golang的runtime线程数量默认与CPU个数相关,可以通过GOMAXPROCS方法来影响线程数量(实际golang运行线程会比指定的要稍微多一些)。

因为每个应用POD分配的CPU个数不一定相同,所以通过人工指定GOMAXPROCS线程数来匹配CPU个数的方法比较繁琐。

好在uber公司也遇到了这个问题,他们提供了一个包自动的根据cgroup值识别容器的CPU quota,并自动设置GOMAXPROCS线程数量,这样就可以实现go程序自适应容器了。

关于该问题的详细分析可见链接:http://www.dockone.io/article/9387

使用Uber的automaxprocs库,可以轻松解决该问题:https://github.com/uber-go/automaxprocs,使用方法见该项目README.md即可。

关于go线程数的另外一个坑

GOMAXPROCS只是常态下的线程数量,一个特例是当所有线程执行syscall处于长时间block的时候,runtime会新建线程,这个数量上限默认是1万个线程。

容易阻塞的syscall常见的就是读写磁盘,如果iowait高必然导致golang的线程打高,所以高级语言虽然带来了很大的开发效率,但和C/C++这种底层语言比会带来很多意想不到的问题,而且也难以解决。

错误识别容器cpu数量的问题也发生在jdk<8的java中,在java8以上版本已经内置自适应容器。

nodejs的pm2也存在该问题,目前没有看到有人提出自适应的解决方案,可以参考uber读取cgroup的方式计算得到实际核心数并控制pm2启动参数来实现自适应。

如果文章帮助您解决了工作难题,您可以帮我点击屏幕上的任意广告,或者赞助少量费用来支持我的持续创作,谢谢~