掘金 后端 ( ) • 2024-05-10 10:00

问题

flask+uwsgi 服务在k8s中部署时,出现无法启动的现象,通过查看日志发现:

detected max file descriptor number: 65535
lock engine: pthread robust mutexes
thunder lock: enabled
Listen queue size is greater than the system max net.core.somaxconn (128).

发现这个现象的原因是:uwsgi.ini 中设置了listen = 1024配置,而在k8s中启动时发现 net.core.somaxconn的值为128,远远小于设置的1024。

分析

提问:net.core.somaxconn内核配置是做什么的?
在tcp三次握手过程中,linux内核会维护两个队列:半连接队列和全连接队列。这两个队列的目的也是做缓存效果。
半连接队列:在sync阶段,且server端已接收到相关报文,将连接放入。
命令:netstat -natp|grep SYNC_RECV | wc -l
全连接队列:三次握手ACK阶段后,建立ESTABLISHED后,放入全连接队列中。
命令:ss -lnt 即可查看
具体相关可参考:4.4 TCP 半连接队列和全连接队列
如何设置?
一般情况下,在主机环境下,可以通过以下两种方式设置即可:
永久生效:

echo "net.core.somaxconn=65535" >> /etc/sysctl.conf
sysctl -p 

立即生效:

sysctl -w net.core.somaxconn=65535

关键问题出现

  1. 在进行私有化部署过程中,线上集群没有出现这个问题报错,而在客户机器上出现这个问题。是否为主机存在问题?
  2. 在k8s或者docker环境下,对宿主机完成设置后,在容器下还是不生效。(这里不管我们怎么去修改,都不会生效)为什么?

这里可以思考下,docker的核心原理->namespace,众所周知,在容器内部环境与外界隔离,就是通过namespace完成,而遇到的问题是不是有可能为 net namespace创建过程的问题?
而net namespace创建过程直接相关的环境问题,是否为linux kernel版本不一致。
通过以上分析,确实发现生产环境内核版本为5.13,而客户提供机器内核为4.x。继续分析:
遇到一个问题, 我当前并没有看到linux的源码,那么该如何进行下去?答案是:源码关键字搜索+谷歌。

关键字搜索
在问题中,已经完成知道了需要查看内核的字段:net.core.somaxconn,那么就可以在源码中执行搜索关键字操作:

~/linux-source-5.4.0/net# grep -rn "somaxconn" *
core/request_sock.c:31: * Note : Dont forget somaxconn that may limit backlog too.
------>core/net_namespace.c:380:       net->core.sysctl_somaxconn = SOMAXCONN;  <-------
core/sysctl_net_core.c:588:             .procname       = "somaxconn",
core/sysctl_net_core.c:589:             .data           = &init_net.core.sysctl_somaxconn,
core/sysctl_net_core.c:608:             tbl[0].data = &net->core.sysctl_somaxconn;
socket.c:1679:  int somaxconn;
socket.c:1683:          somaxconn = READ_ONCE(sock_net(sock->sk)->core.sysctl_somaxconn);
socket.c:1684:          if ((unsigned int)backlog > somaxconn)
socket.c:1685:                  backlog = somaxconn;

这里可以看到关键字:net_namespace.c,这样根据调用链可以分析:

static int __net_init net_defaults_init_net(struct net *net)
{
        net->core.sysctl_somaxconn = SOMAXCONN;
        return 0;
}

而这个值在5.13下 SOMAXCONN为 4096, 4.x下为128。到目前为止已经破案了。
通过谷歌查看了 namespace创建过程,也证实了这个问题个根因。

解决方式

  1. k8s可以通过initContainer开启特权模式进行内核参数的修改。
  2. 直接升级内核。

小结

经验点:遇到问题不要急,通过表征一步步从原理进行入手,及时源码不熟悉的情况下,可以活用谷歌来解决。毕竟国内的环境 最喜欢读源码。(卷)

喜欢可以关注: 小唐云原生