记一次容器环境下出现Address not available
困惑的源地址
Cloud Native
pod 创建后一段时间一直是正常运行,突然有一天发现没有新的连接创建了,业务上是通过 pod A 访问 svc B 的 svc name 的方式,进入 pod 手动去 wget 一下,发现报错了 Address not available,为何会报错这个呢?
错误代码参考连接:[errno.3[1]]
EADDRNOTAVAIL
Address not available (POSIX.1-2001).
容易被忽视的内核参数
Cloud Native
通过 netstat -an 查看到连接 svc 的地址,其中 estab 状态的连接数,已经到达了可用的随机端口数量阈值,无法在新建连接了。
相关内核参考连接:[ip-sysctl.txt[2]]
ip_local_port_range - 2 INTEGERS
Defines the local port range that is used by TCP and UDP to
choose the local port. The first number is the first, the
second the last local port number.
If possible, it is better these numbers have different parity
(one even and one odd value).
Must be greater than or equal to ip_unprivileged_port_start.
The default values are 32768 and 60999 respectively.
同样的问题还可能出现什么类型的报错呢?
Cloud Native
手动调小了 net.ipv4.ip_local_port_range,之后进行复现。
curl: (7) Couldn't connect to server nc: bind: Address in use wget: can't connect to remote host (1.1.1.1): Address not available
引发思考
Cloud Native
为什么 wget/curl 同样调用的是 connect() 函数报错的,为何报错还是不一样的?
每一个客户端程序都会有自定义的 errorcode,在同样的 connect() 函数报错后 ,wget 是直接输出了 POSIX 标准的错误定义 Address not available,而 curl 会输出自己的定义错误码和对应的提示信息 curl: (7) Couldn't connect to server,错误代码是 7,curl 的报错定义在 lib/strerror.c。
函数不同,错误的定义也就不同,从 POSIX 标准的错误定义都能找到。
EADDRINUSE
Address already in use (POSIX.1-2001).
EADDRNOTAVAIL
Address not available (POSIX.1-2001).
是不是所有情况下都是这样输出呢?
Cloud Native
那么直接找了一台 Centos7.9 的系统,安装 curl 、wget、 nc 等工具,同样改小端口范围的情况下会出现如下报错 Cannot assign requested address,从这里可以得知某些镜像(alpine、busybox) 里,使用相同的命令工具对相同的情况下报错会不同。因为这些镜像里可能为了缩小整个镜像大小,对于一些基础命令都会选择 busybox 工具箱(上面的 wget 和 nc 就来自于 busybox 工具箱里的,参考 busybox 文档:Busybox Command Help[3])来使用,所以就造成在问题定位方面困扰了。
Linux 系统中用于包含与错误码相关的定义:/usr/include/asm-generic/errno.h
容器环境下,端口配置最佳实践
Cloud Native
可修改范围
理论上来是 0~65535 都能使用, 但是 0~1023 是特权端口,已经预留给一下标准服务,如 HTTP:80,SSH:22 等,只能特权用户使用,同时也避免未授权的用户通过流量特征攻击等所以建议端口调大的话可以将随机端口范围限制在 1024-65535 之间。
如何正确配置 Pod 源端口
普通 Pod 源端口修改方法
securityContext
...
securityContext:
sysctls:
name: net.ipv4.ip_local_port_range
value: 1024 65535
initContainers
initContainers:
command:
/bin/sh
'-c'
|
sysctl -w net.core.somaxconn=65535
sysctl -w net.ipv4.ip_local_port_range="1024 65535"
securityContext:
privileged: true
...
hostnetwork 模式 pod 修改注意事项
大量创建 svc 的时候减少创建监听的步骤只是提交 ipvs/iptables 规则,这样可以优化连接性能 。另一个就解决某些场景下出现大量的 CLOSE_WAIT 占用 TCP 连接等问题。在 1.22 版本之后就去掉了 PortOpener 逻辑。
1304 proxier.openPort(lp, replacementPortsMap)
部署 tcpdump 抓包,抓到有健康检查失败的事件后,停止抓包。
增加前置判断
initContainers:
command:
/bin/sh
'-c'
|
if [ "$POD_IP" != "$HOST_IP" ]; then
mount -o remount rw /proc/sys
sysctl -w net.ipv4.ip_local_port_range="1024 65535"
fi
env:
name: POD_IP
valueFrom:
fieldRef:
apiVersion: v1
fieldPath: status.podIP
name: HOST_IP
valueFrom:
fieldRef:
apiVersion: v1
fieldPath: status.hostIP
securityContext:
privileged: true
...
如何正确配置 NodePort 范围
在 Kubernetes中,APIServer 提供了 ServiceNodePortRange 参数(命令行参数 --service-node-port-range),该参数是用于限制 NodePort 或 LoadBalancer 类型的 Service 在节点上所监听的 NodePort 端口范围,该参数默认值为 30000~32767。在 ACK Pro 集群中,您可以通过自定义 Pro 集群的管控面参数修改该端口范围。具体操作,请参见自定义 ACK Pro 集群的管控面参数[7]。
在修改 NodePort 端口范围时必须十分谨慎。务必保证 NodePort 端口范围与集群节点上 Linux 内核提供的 net.ipv4.ip_local_port_range 参数中的端口范围不冲突。该内核参数 ip_local_port_range 控制了 Linux 系统上任意应用程序可以使用的本地端口号范围。ip_local_port_range 的默认值为 32768~60999,Nodeport 默认值为 30000~32767。
ACK 集群在默认配置情况下,ServiceNodePortRange 参数和 ip_local_port_range 参数不会产生冲突。如果您此前为了提升端口数量限制调整了这两个参数中任意一个,导致两者范围出现重合,则可能会产生节点上的偶发网络异常,严重时会导致业务健康检查失败、集群节点离线等。建议您恢复默认值或同时调整两个端口范围到完全不重合。
调整端口范围后,集群中可能存在部分 NodePort 或 LoadBalancer 类型的 Service 仍在使用 ip_local_port_range 参数端口范围内的端口作为 NodePort。此时您需要对这部分 Service 进行重新配置以避免冲突,可通过 kubectl edit <service-name> 的方式直接将 spec.ports.nodePort 字段的值更改为未被占用的 NodePort。
[1] errno.3
https://man7.org/linux/man-pages/man3/errno.3.html
[2] ip-sysctl.txt
https://www.kernel.org/doc/Documentation/networking/ip-sysctl.txt
[3] Busybox Command Help
https://www.busybox.net/downloads/BusyBox.html
[4] securityContext
https://kubernetes.io/docs/tasks/administer-cluster/sysctl-cluster/
[5] Kubernetes社区PR
https://github.com/kubernetes/kubernetes/pull/108888
[6] f98f27b
https://github.com/kubernetes/kubernetes/blob/f98f27bc2f318add77118906f7595abab7ab5200/pkg/proxy/iptables/proxier.go#L1304
[7] 自定义ACK Pro集群的管控面参数
https://help.aliyun.com/zh/ack/ack-managed-and-ack-dedicated/user-guide/customize-ack-pro-control-plane-component-parameters
END
点这里 ↓↓↓ 记得 关注✔ 标星⭐ 哦
微信扫码关注该文公众号作者