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

2023-11-10

困惑的源地址

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 <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

作者:郑明泉、余凯

点击立即免费试用云产品 开启云上实践之旅!

原文链接

本文为阿里云原创内容,未经允许不得转载。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

记一次容器环境下出现 Address not available 的相关文章

  • 分布式定时任务

    本文引用了谷粒商城的课程 定时任务 定时任务是我们系统里面经常要用到的一些功能 如每天的支付订单要与支付宝进行对账操作 每个月定期进行财务汇总 在服务空闲时定时统计当天所有信息数据等 定时任务有个非常流行的框架Quartz和Java原生AP
  • Latex技巧(持续更新)(\newcommand{}{})

    自定义一个新命令 newcommand tcr textcolor red 为了方便设置文本颜色为红色而定义的简化命令 code documentclass article usepackage xcolor newcommand tcr
  • Protobuf之proto文件编写规则

    一 简单介绍 protobuf文件 就是定义你要的消息 类似java中的类 和消息中的各个字段及其数据类型 类似java类中的成员变量和他的数据类型 二 Protobuf消息定义 1 消息由至少一个字段组合而成 类似于C语言中的结构 每个字
  • 【Docker】docker 执行 apt-get E: Could not open lock file /var/lib/dpkg/lock - open

    1 背景 mac下安装了docker 然后用docker 安装了grafana软件 然后进入grafana base lcc lcc prometheus docker exec it 4b5f517f4340 bash grafana 4
  • Python运维开发(CMDB管理系统)——常用函数

    内置函数 len 返回容器中item的数量 sum 计算可迭代对象的和 字符串不可以和数字相加 str 把其他类型转换成字符串 int 把其他类型转换成int max min 求列表中的最大值或者最小值 range 返回一个可迭代对象 ra
  • Python 删除列表中的'\n'和空格

    要爬取的span标签下的价格730 用的是xpath获取内容 但爬取的结果为 把 i 标签以及后面的div也爬取下来了 导致这部分为 n 和空格 很显然 这不是我要的结果 想过先用xpath 再用正则表达式匹配数字 但一直提示类型不一致 因

随机推荐

  • 苏小红版 c语言程序设计(第三版)系列实验题:学生成绩管理系统V6.0

    github https github com Jackie0Feng SAMS 系统需求描述 某班有最多不超过30人 具体人数由键盘输入 参加期末考试 考试科目最多不超过6门 具体门数由键盘输入 学生成绩管理系统是一个非常实用的程序 如果
  • 深入探索C++中模板参数的自动推导

    深入探索C 中模板参数的自动推导 利用模板参数推导时需要注意以下几点 编译器只根据函数调用时给出的实参列表来推导模板参数值 与函数参数类型无关的模板参数其值无法推导 与函数返回值相关的模板参数其值也无法推导 所有可推导模板参数必须是连续位于
  • 函数的调用过程及其运行时堆栈解析

    为了较为深入的了解一下函数的调用过程 所以我在学习的过程中用一段简单的代码对其进行了研究 代码和调用堆栈如下 下图为上述函数的反汇编及调用过程
  • pyecharts 安装报错 ModuleNotFoundError: No module named 'pyecharts_snapshot'

    安装方法 pip install pyecharts 正常导入 import pyecharts 报错信息 ERROR lml utils failed to import pyecharts snapshot Traceback most
  • PostgreSQL常用命令使用

    1 连接数据库 psql U postgres psql U username d databse name h host W U 指定用户 d 指定数据库 h 要链接的主机 W 提示输入密码 2 切换数据库 c dbname 3 列举数据
  • 《单片机原理及应用》——概述

    系列文章目录 文章目录 系列文章目录 前言 一 计算机的位数是什么 二 51单片机 前言 学习视频链接是 https www bilibili com video BV1sE411L7j5 spm id from 333 337 searc
  • C# 引用类型作为函数参数时

    C 引用类型作为函数参数时 在探讨本文的主题之前 先来介绍下C 中的值类型和引用类型 众所周知C 中有值类型和引用类型 值类型有基础数据类型 诸如int double bool等 结构体 枚举 引用类型有接口 类 委托 值类型全部在操作系统
  • sqli-labs--Less7

    目录 一 Less 7 1 按照之前文中的思路 输入 id 1 发现提示 2 提示我们使用outfile函数 接下来需要做的就是研究outfile 的用法 以及寻找注入点 3 执行写入的文件 发现执行成功 一 Less 7 1 按照之前文中
  • 3. Python3 运算符

    Hi 大家好 我是茶桁 前两节我们学习了基本的Python特性和语法 并且认识了一些基本的Python脚本 今天 我们来学习一下Python的运算符 而我们选择的版本为Python3 什么是运算符 为了能让我们的学习顺利进行下去 首先我们需
  • 【编程之路】常见的排序算法(一)

    常见的排序算法 一 本文将介绍五种基础的排序算法 分别是 冒泡 选择 插入 快速 归并 1 冒泡排序 冒泡排序应该是入门级的排序算法了 class solution def sort arr self arr n len arr for i
  • 聊聊最近几年的路径追踪技术的进展(一)

    路径追踪技术 Path tracing PT 已经是当下工业中离线渲染使用的主流技术 不管是商业渲染器如皮克斯的RenderMan Solid Angle的Arnold等 还是迪士尼的in house渲染器Hyperion以及Weta Di
  • 与finally单独使用的是try

    try一般和catch搭配使用 捕获时可以没有catch块 但是此时必须有finally块 换言之try可以和catch finally两个中的一个搭配使用 但是catch和finally不能单独搭配使用
  • 如何正视自己的劣势?面试!

    面试真的是最直接面对自己劣势的方式 平时身边人看不清或者不愿意指出你的劣势 自己如果又不肯正视 那就会逐渐的积累成大问题 印象最深刻的是某小公司的技术终面 做的跟我完全同领域 面试官与我对切模型然后我败下阵来 临走的时候他说 我们公司还是想
  • 域名备案后修改服务器,域名备案后修改服务器

    域名备案后修改服务器 内容精选 换一换 PHPWind 简称 PW 是一个基于PHP和MySQL的开源社区程序 是国内较受欢迎的论坛之一 轻架构 高效易开发 使用户可快速搭建并轻松管理 本文档指导用户使用华为云市场镜像 PHPWind 论坛
  • 推荐系统(一)

    协同过滤 Collaborative Filtering A基于邻域的算法 B隐语义模型 C基于图的随机游走算法 A 基于邻域的算法 一 基于用户的协同过滤算法 UserCF 给用户推荐与其兴趣相似的其他用户喜欢的物品 1 首先找到与目标用
  • 几种常见的神经网络了解

    神经网络技术起源 感知机 神经网络技术起源于上世纪五 六十年代 当时叫感知机 perceptron 拥有输入层 输出层和一个隐含层 输入的特征向量通过隐含层变换达到输出层 在输出层得到分类结果 早期感知机的推动者是Rosenblatt 当时
  • 【单片机毕业设计】【mcuclub-309】衣柜除湿消毒

    设计简介 项目名 基于单片机的智能衣柜除湿消毒控制系统设计 标准版 基于单片机的衣柜环境监测 控制系统设计 标准版 基于单片机的多功能衣柜控制系统设计 标准版 单片机 STC89C52 功能简介 1 通过DHT11检测衣柜内温湿度 当湿度大
  • 常用正则表达式以及校验

    1 邮箱验证 判断邮箱格式是否正确 String ruleEmail w w w A Za z0 9 A Za z0 9 A Za z0 9 正则表达式的模式 编译正则表达式 Pattern p Pattern compile ruleEm
  • nRF52832学习记录(九、SAADC)

    nRF52xx 处理器中的ADC为一个逐次逼近的模拟数字转换器 所有nRF52xx 系列处理器的内部 ADC 称为 SAADC 目录 nRF52xx SAADC基础介绍 SAADC采样示例 SAADC EasyDMA 缓冲采样示例 SAAD
  • 记一次容器环境下出现 Address not available

    困惑的源地址 pod 创建后一段时间一直是正常运行 突然有一天发现没有新的连接创建了 业务上是通过 pod A 访问 svc B 的 svc name 的方式 进入 pod 手动去 wget 一下 发现报错了 Address not ava