记一次容器环境下出现 Address not available

news2024/10/3 8:17:26

作者:郑明泉、余凯

困惑的源地址

pod 创建后一段时间一直是正常运行,突然有一天发现没有新的连接创建了,业务上是通过 pod A 访问 svc B 的 svc name 的方式,进入 pod 手动去 wget 一下,发现报错了 Address not available,为何会报错这个呢?

大概示例图如下:

在这里插入图片描述

为什么会出现 Address not available,是什么地址不可用,查了很多资料,根据 POSIX(Portable Operating System Interface for UNIX)标准的错误定义中找到了相关的定义,同样说的还不是很清楚。

错误代码参考连接:[errno.3 [ 1] ]

EADDRNOTAVAIL               
    Address not available (POSIX.1-2001).

容易被忽视的内核参数

通过 netstat -an 查看到连接 svc 的地址,其中 estab 状态的连接数,已经到达了可用的随机端口数量阈值,无法在新建连接了。

在这里插入图片描述

最后通过修改了内核参数随机端口 net.ipv4.ip_local_port_range 端口范围才得以解决的。

我们可以知道 Linux 的内核定义的随机端口 32768 ~ 60999,可能在业务设计场景中,比较容易被忽略的,我们都知道,每一个 TCP 连接都是由四元组(源 IP,源端口,目的 IP,目的端口)构成的,只要四元组中其中一个元组发生了变化,就可以创建一个 TCP 连接的。当一个 POD 要访问一个固定的目的 IP + 目的端口的时候,那么每一个 TCP 连接的变量就只剩下源端口是随机的了,所以如果在需求就是需要创建大量长连接的话,要么就调大内核随机端口,要么就调整业务。

相关内核参考连接:[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.

同样的问题还可能出现什么类型的报错呢?

手动调小了 net.ipv4.ip_local_port_range,之后进行复现。

同样的问题,分别尝试了 curl,nc,wget 命令,报错都不一样,这就犯难了。

难道就不能统一一下吗?

  • 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

在这里插入图片描述

那么就通过 strace 命令进程分析一下看看,跟踪指定系统调用名称 它们都会创建 socket(), 然后发现 wget/curl 命令是通过 connect() 函数,而 nc 命令先是是通过 bind() 函数调用, 如果报错就不会继续调用 connect() 函数了。

在这里插入图片描述

如图,通过对 B/S 架构的分析如下,connect() 是在客户端创建 socket 后建立的。

在这里插入图片描述

引发思考

为什么 wget/curl 同样调用的是 connect() 函数报错的,为何报错还是不一样的?

  • 每一个客户端程序都会有自定义的 errorcode,在同样的 connect() 函数报错后 ,wget 是直接输出了 POSIX 标准的错误定义 Address not available,而 curl 会输出自己的定义错误码和对应的提示信息 curl: (7) Couldn’t connect to server,错误代码是 7,curl 的报错定义在 lib/strerror.c。

在这里插入图片描述

在这里插入图片描述

为什么 connect() 函数和 bind() 函数报错不一样?

  • 函数不同,错误的定义也就不同,从 POSIX 标准的错误定义都能找到。
EADDRINUSE               
    Address already in use (POSIX.1-2001).        
EADDRNOTAVAIL               
    Address not available (POSIX.1-2001).

是不是所有情况下都是这样输出呢?

那么直接找了一台 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

在这里插入图片描述

#define  EADDRNOTAVAIL  99  /* Cannot assign requested address */

容器环境下,端口配置最佳实践

可修改范围

理论上来是 0~65535 都能使用, 但是 0~1023 是特权端口,已经预留给一下标准服务,如 HTTP:80,SSH:22 等,只能特权用户使用,同时也避免未授权的用户通过流量特征攻击等所以建议端口调大的话可以将随机端口范围限制在 1024-65535 之间。

如何正确配置 Pod 源端口

普通 Pod 源端口修改方法

从 kubernetes 社区得知可以通过安全上下文修改 securityContext [ 4] ,还有可以通过 initContainers 容器给特权模式 mount -o remount rw /proc/sys 的方式修改,此修改方式只会在 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 修改注意事项

1.22+ 集群以上就不建议修改 net.ipv4.ip_local_port_range,因为这会和 ServiceNodePortRange 产生冲突。

Kubernetes 的 ServiceNodePortRange 默认是 30000~32767,Kubernetes 1.22 及以后的版本,去除了 kube-proxy 监听 NodePort 的逻辑,如果有监听的话,应用程序在选用随机端口的时候,会避开这些监听中的端口。如果 net.ipv4.ip_local_port_range 的范围和 ServiceNodePortRange 存在重叠,由于去掉了监听 NodePort 的逻辑,应用程序在选用随机端口的时候就可能选中重叠部分,比如 30000~32767,在当 NodePort 与内核 net.ipv4.ip_local_port_range 范围有冲突的情况下,可能会导致偶发的 TCP 无法连接的情况,可能导致健康检查失败、业务访问异常等问题。更多信息,请参见 Kubernetes 社区 PR [ 5]

大量创建 svc 的时候减少创建监听的步骤只是提交 ipvs/iptables 规则,这样可以优化连接性能 。另一个就解决某些场景下出现大量的 CLOSE_WAIT 占用 TCP 连接等问题。在 1.22 版本之后就去掉了 PortOpener 逻辑。

kubernetes/pkg/proxy/iptables/proxier.go

Line 1304 in f98f27b [ 6]

1304        proxier.openPort(lp, replacementPortsMap)

在这里插入图片描述

具体是如何冲突的呢?测试环境是 k8s 1.22.10,kube-proxy 网络模式 ipvs。以 kubelet 健康检查为例,调整了节点的内核参数 net.ipv4.ip_local_port_range 为1 024~65535。

在这里插入图片描述

部署 tcpdump 抓包,抓到有健康检查失败的事件后,停止抓包。

看到 kubelet 是用节点 IP(192.168.66.27)+随机端口 32582 向 pod 发起了 TCP 握手 podIP(192.168.66.65)+80,但是 pod 在 TCP 握手时回 SYN ACK 给 kubelet 的时候,目标端口是 32582,却一直在重传。因为这个随机端口刚好是某一个服务的nodeport,所以优先被 IPVS 拦截给规则后端的服务,但这个后端服务 (192.168.66.9) 并没有发起和 podIP(192.168.66.65)TCP 建连,所以后端服务 (192.168.66.9) 直接是丢弃的。那么 kubelet 就不会收到 SYN ACK 回应,TCP 无法建联,所以导致健康检查失败。

这个报文看 kubelet 发起 TCP 握手,pod 回 syn ack 的时候一直重传。

在这里插入图片描述

实际是发送到了 32582 这个 svc 的后端 pod 了,直接是丢弃。

在这里插入图片描述
在这里插入图片描述

增加前置判断

所以 hostnework 可以加上一个判断,通过 initContainers 容器修改的时候,如果 podIP 和 hostIP 不相等才修改 net.ipv4.ip_local_port_range 参数,避免误操作导致修改节点的内核参数。

      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 的方式直接将 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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/787713.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Roslyn 动态编译

NET 编译器平台(.NET Compiler Platform) 也称为Roslyn,是Microsoft的一组用于C#和Visual Basic (VB.NET) 语言的开源 编译器和代码分析 API。 该项目特别包括C# 和 VB.NET 编译器的自托管版本——用语言本身编写的编译器。编译器可通过传统…

vue2路由(个人学习笔记四)

目录 友情提醒第一章、路由简介1.1)什么是路由1.2)安装路由插件 第二章、自定义路由器2.1)创建路由器文件index.js文件2.2)index.js文件中配置路由信息 第三章、使用路由器3.1)在main.js文件中将路由器绑定到Vue对象3.…

查看maven发布时间的方法

查看maven发布时间的方法如下【 打开maven官网 选中Release Notes 即可查看对应版本的发布时间 】

【Linux命令200例】cmp文件比较工具

🏆作者简介,黑夜开发者,全栈领域新星创作者✌,2023年6月csdn上海赛道top4。 🏆本文已收录于专栏:Linux命令大全。 🏆本专栏我们会通过具体的系统的命令讲解加上鲜活的实操案例对各个命令进行深入…

微信小程序开发知识点总结(不断补充)

一、怎样实现小程序与数据库的交互原理 微信小程序开发里面是不能直接写数据库连接文件,如果要实现与数据库的交互,就需要用到中间的接口。(这个接口可以理解成springboot项目中controller层的RequestMapping的URL)具体用法如下&…

Thunderbird 115 带来了Supernova UI 和先进功能

导读Thunderbird 115 带来了Supernova UI 和先进功能,将电子邮件带入了新的高度。 流行的开源电子邮件客户端 Mozilla Thunderbird 推出了最新版本Thunderbird 115,它带来了一系列令人兴奋的功能和改进。更新带来了令人耳目一新的超新星用户界面(UI)&…

STM32MP157驱动开发——按键驱动(线程化处理)

文章目录 “线程化处理”机制:内核函数线程化处理方式的按键驱动程序(stm32mp157)编程思路button_test.cgpio_key_drv.cMakefile修改设备树文件编译测试 “线程化处理”机制: 工作队列是在内核的线程的上下文中执行的 工作队列中有多个 work&#xff0…

matlab安装激活后报错找不到icuuc54.dll

matlab激活后,有报错找不到icuuc54.dll 解决办法: 这是因为破解用的版本不一样,我下载的是matlab2016b,但是破解时,在网上下载的matlab2016a的破解包,所以我重新下载2016b的crack包后,然后再在…

使用Vs Studio和Cmake生成C++库

使用Vs Studio和Cmake生成C库 在windows系统下,由于没有C的编译器,想通过源码安装、加载C的库存在不方便的地方。本文将介绍通过使用Vs Studio运用Cmake的方式,编译一个简单的自定义库,并进行加载、调用。 工程源代码 前提条件 …

Python增删改查小练习

目录 1. List操作-增加 2. List操作-查询 3. List操作-修改 4. List操作-删除 资料获取方法 1. List操作-增加 List Append(“xx”) 插入到列表尾部 Insert(x,xx) 在指定的位置插入 Extend 将列表的元素分开,插入到之前列表的尾部 小练习: 把一个字符串”abcdefg…

小白必看系列之c语言中常见操作符示例和用法总结

文章目录 前言算术操作符(Arithmetic Operators)代码示例代码解析 关系操作符(Relational Operators)代码示例代码解析 逻辑操作符(Logical Operators)代码示例代码解析 位操作符(Bitwise Opera…

矩阵svd分解和矩阵的伪逆

真该好好学习一下Latex数学公式的语法和规则了,否则,连写个博客都没法写,这叫什么事! https://blog.csdn.net/ViatorSun/article/details/82826664 直接上数学博士写的ppt图(肯定比我在这里胡说八道强的多&#xff0…

【设计模式——学习笔记】23种设计模式——原型模式Prototype(原理讲解+应用场景介绍+案例介绍+Java代码实现)

原型模式 介绍 原型模式指用通过拷贝原型实例创建新的实例,新实例和原型实例的属性完全一致原型模式是一种创建型设计模式工作原理是通过调用原型实例的 clone()方法来完成克隆,原型实例需要实现Cloneable接口,并重写clone()方法需要为每个…

平台化的测试工具推荐|一站式测试平台RunnerGo

互联网行业的发展到今天越来越多的公司更加注重工作效率和团队协作,越来越多的产品也趋于平台化,平台化也更有利于提高团队效率,代码管理、持续构建、持续部署这些工具的发展都是非常超前的,它们对于团队协作的支持和工作效率的提…

河南移动联合中兴打造SPNPTN网络融合示范样板

最近河南移动和中兴在濮阳进行SPN和PTN两网融合规模部署,可以充分展示出PTN网络价值,并优化网络结构、降低网络运营成本,实现SPN和PTN网络的融合融通,在增强SPN网络综合业务承载能力之余也能提升了网络性能。 河南移动现在是SDH、…

CF1837 A-D

A题 题目链接:https://codeforces.com/problemset/problem/1837/A 基本思路: 要求计算蚂蚱到达位置 x最少需要多少次跳跃,并输出蚂蚱的跳跃方案。因为每次可以向左或向右跳跃一定距离(距离必须为整数),但是…

微服务探索之路06篇k8s配置文件Yaml部署Redis使用Helm部署MongoDB和kafka

1 安装Redis 1.1创建配置文件redis.conf 切换到自己的目录下如本文是放在/home/ubuntu下 cd /home/ubuntuvim redis.conf bind 0.0.0.0 protected-mode yes port 6379 requirepass qwe123456 tcp-backlog 511 timeout 0 tcp-keepalive 300 daemonize no pidfile /var/run/r…

STM32MP157驱动开发——按键驱动(工作队列)

文章目录 “工作队列”机制:内核函数work_struct 结构体定义 work使用 work :schedule_workworkqueue 其他函数 工作队列方式的按键驱动程序(stm32mp157)编程思路button_test.cgpio_key_drv.cMakefile修改设备树文件编译测试 “工作队列”机制&#xff1…

UDS之11服务

11服务: 功能:控制MCU进行重启,重启分为硬重启和软重启,11服务一般代表软重启,虽然它里面有个子服务是硬件重启,这里需要注意下;硬重启在日常工作中一般代表B重启。命令格式(请求&am…

外贸找客户工具之邮件群发:MaxBulk Bulk Mailer Pro 9.5

MaxBulk Bulk Mailer Pro 是一款快速的批量邮件软件,旨在帮助在一次操作中向大量电子邮件 ID 发送批量电子邮件。直接将电子邮件发送到收件箱而不是垃圾邮件。该工具的目的是使批量电子邮件处理过程快速而精确,并且它配备了很多高级功能来实现此目的。用…