某客户k3s网络故障案例

麦浪1年前技术文章872

1、出现问题

    在我们吃饭的过程中,小丫告诉我客户的系统出现问题了,我们赶快吃完饭回去帮忙排查。当我们回去的时候,被告知问题已经被修复了,但是问题根源没有找到。故障原因给出的是:  服务重启后对应服务配置文件丢失。但是我们觉得问题没那么简单,首先是什么配置文件在什么情况下导致的丢失?

2、排查原因

接下来我们带着疑问展开排查。通过沟通我们知道配置丢失的服务为:eopx-gateway。我们先看看他们现在是什么样子的配置,如图1所示:

1.png

图1

通过这里我们看到此服务里面配置了环境变量NACOS_HOST和NACOS_PORT,这里配置的是一个固定的IP,这个IP是什么的值?通过命令进行查看,我们发现这个值是eopx-register的pod IP,结果如图2所示:

2.png

图2

根据环境变量的名称,我们可以知道这个服务其实就是NACOS服务,NACOS服务就是配置中心,服务在启动的过程中,会去到NACOS服务中获取配置项,pod IP是一个易变的值,很容易就发生变化了。他们这里出现问题,是不是因为pod IP改变,导致服务在启动的过程中找不到配置中心,导致出现的问题呢?我们对比了eopx-gateway的k8s rs的变更前后的版本,发现之前根本就没有NACOS配置相关环境变量,这个是他们后面手动加上去的。结果如图3所示:

3.png

图3

后面根据沟通,才知道他们指的配置丢失,就是他们找不到是在哪里配置去连接NACOS的地址和端口,所以他们才在deployment中手动进行了配置。沟通结果如图4:

4.png

图4

这个时候我们怀疑是通过Dockerfile的ENV指令进行的配置,但是开发贴出了Dockerfile的相关配置,如图5所示:

5.png

图5

这里的Dockerfile很干净利落,确实是没有我们要的NACOS相关的ENV配置,这个时候我怀疑是不是配置在xxxx/java:1.8-full这个基础镜像里面呢?因为这个基础镜像看起来不像是官方的JDK镜像。我们要求开发提供了此镜像,在我们的服务器上面通过导入镜像,并查看环境变量,也没有发现相关环境变量。

# docker run --rm -it xxxx/java:1.8-full ash 
/usr/lib/java # env
HOSTNAME=cf3ae10caa94
SHLVL=1
HOME=/root
TERM=xterm
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/lib/java/jdk1.8.0_202/bin
LANG=C.UTF-8
TIME_ZONE=Asia/Shanghai
CLASSPATH=.:/usr/lib/java/jdk1.8.0_202/lib/dt.jar:/usr/lib/java/jdk1.8.0_202/lib/tools.jar
JAVA_HOME=/usr/lib/java/jdk1.8.0_202
PWD=/usr/lib/java

这就很奇怪了,肯定有一个地方要配置NACOS的地址,根据开发的说法是:不在代码里面,那就应该是环境变量或者configmap、secret之类的里面,不然应用不可能知道NACOS在哪里。我们已经看过了,eopx-gateway的deployment里面是没有配置configmap或secret的,那就应该是环境变量里面。结果环境变量里面也没有。那真相只有一个:NACOS的地址就是配置在代码里面的

2、查看代码配置

代码是使用Java写的,这里简单说一下:Java的jar包其实就是zip包,我们可以把jar包拿下来通过unzip解压缩,然后使用grep进行搜索,看看代码里面是否有NACOS相关配置项。搜索结果如下:

# egrep -r -i 'nacos' * 
BOOT-INF/classes/logback-spring.xml: <!--nacos 心跳 INFO 屏蔽-->
BOOT-INF/classes/logback-spring.xml: <logger name="com.alibaba.nacos" level="OFF">
BOOT-INF/classes/bootstrap.yml:    nacos:
BOOT-INF/classes/bootstrap.yml:        server-addr: ${NACOS_HOST:eopx-register}:${NACOS_PORT:8848}

我们可以看到代码里面:BOOT-INF/classes/bootstrap.yml 这个文件里面配置的有NACOS相关路径。我们可以看一下这个文件里面都有什么:

server:
 port: 9999

spring:
 application:
   name: eopx-gateway
 cloud:
   nacos:
     discovery:
       server-addr: ${NACOS_HOST:eopx-register}:${NACOS_PORT:8848}
     config:
       server-addr: ${spring.cloud.nacos.discovery.server-addr}
       file-extension: yml
       shared-configs:
         - application-${spring.profiles.active}.${spring.cloud.nacos.config.file-extension}
 profiles:
   active: dev

这里我们简单的说一下:${NACOS_HOST:eopx-register} 这个配置语法是:如果存在环境变量NACOS_HOST,那么这里的值就是环境变量:NACOS_HOST的值,如果环境变量NACOS_HOST不存在,这把值配置为:eopx-register,端口也是一样的,就是如果没有环境变量就去默认的8848,从这里我们可以得出:代码里面已经配置了NACOS的地址和端口,并不是开发所说的没有配置,不配置环境变量其实就是取默认值。我们通过kubectl get svc 输出如图6所示:

6.png图6

这里的默认值就是对应k3s里面的service name和service port。那说明配置是没问题的,但是为什么会连不上nacos呢?

3、排查eopx-register service连通性

我们刚开始怀疑的是coredns可能存在解析异常?导致service name没有正常的解析出对应的ip地址。结果如图7所示:

7.png

图7

从这里我们可以看到:coredns的解析是没问题的,解析出来的就是eopx-register的service ip。那么应该就是网络上面连不通了。而且我们通过ping service ip虽然有解析出来,但是没有ping通,说明这里使用的是iptables模式,不是IPVS模式,我们直接进行telnet

# 刚开始的时候,容器内是没有telnet命令的,所以我们需要安装telnet命令,这里安装也很简单,
# 我们要先确定容器是什么Linux distribution

$ cat /etc/os-release
NAME="Alpine Linux"
ID=alpine
VERSION_ID=3.12.0
PRETTY_NAME="Alpine Linux v3.12"
HOME_URL="https://alpinelinux.org/"
BUG_REPORT_URL="https://bugs.alpinelinux.org/"
# 从输出结果来看是Alpine linux,此发行版通过apk 进行包管理的,我们通过apk进行telnet安装
$ apk add busybox-extras
# 安装之后进行telnet
$ busybox-extras telnet eopx-register 8848
telnet: can't connect to remote host (10.43.176.183):
Host is unreachable

通过上面的操作,可以很清楚的看到确实是网络上面不通,导致无法访问eopx-register这个nacos服务。那为什么会网络不通呢?从上面我们已经看到是iptables模式了,我们需要去查看对应的iptables规则。

4、查看iptables规则

这里我们可以顺藤摸瓜,一步步去查找对应的iptables规则,先查看master2 节点的NAT表的KUBE-SERVICES段:

$ iptables -t nat -S KUBE-SERVICES | grep -i 'eopx-register' | grep -v nodeport
-A KUBE-SERVICES ! -s 10.42.0.0/16 -d 10.43.176.183/32 
-p tcp -m comment --comment "default/eopx-register:8848tcp1-eopx-register cluster IP" 
-m tcp --dport 8848 -j KUBE-MARK-MASQ

-A KUBE-SERVICES -d 10.43.176.183/32 -p tcp -m comment --comment 
"default/eopx-register:8848tcp1-eopx-register cluster IP" 
-m tcp --dport 8848 -j KUBE-SVC-C4WILTTNV4XPAEEQ

这里看到eopx-register这个SVC对应的转发规则是到: KUBE-SVC-C4WILTTNV4XPAEEQ,然后我们再看这个规则:

$ iptables -t nat -S KUBE-SVC-C4WILTTNV4XPAEEQ 
-N KUBE-SVC-C4WILTTNV4XPAEEQ
-A KUBE-SVC-C4WILTTNV4XPAEEQ -m comment --comment "default/eopx-register:8848tcp1-eopx-register"
-j KUBE-SEP-ZBUGZHNZKU6FMQKM

顺着KUBE-SEP-ZBUGZHNZKU6FMQKM这个规则继续往下查看:

$ iptables -t nat -S KUBE-SEP-ZBUGZHNZKU6FMQKM
-N KUBE-SEP-ZBUGZHNZKU6FMQKM
-A KUBE-SEP-ZBUGZHNZKU6FMQKM -s 10.42.1.94/32 -m comment
--comment "default/eopx-register:8848tcp1-eopx-register"
-j KUBE-MARK-MASQ

-A KUBE-SEP-ZBUGZHNZKU6FMQKM -p tcp -m comment --comment
"default/eopx-register:8848tcp1-eopx-register"
-m tcp -j DNAT --to-destination 10.42.1.94:8848

这里我们看到是转发到:10.42.1.94 的 8848端口,这里刚刚好是对应eopx-register的pod ip和端口,那说明iptables的转发规则是没问题的?但是,为什么我们在pod内部测试的不通呢?因为我们kubectl 命令和证书都是在master2节点,所以我们就在这个节点上面查看的iptables 规则,如果eopx-gateway 这个pod被调度到其他节点了呢?如图8所示:

8.png图8

果然如我们所猜测的那样,eopx-gateway这个pod被调度到了prod-agent1节点了,我们再登录prod-agent1节点查看iptables规则,结果竟然查看不了,报错如图9所示:

9.png图9

我们使用iptables-save > iptables.log 命令把所有的iptables规则都保存到iptables.log文件中去,然后再查看规则中到底有什么问题。如图10所示:

10.png

图10

从图10中我们发现iptables竟然有18万条规则?至此问题已经比较明显了,iptables规则数量明显存在问题,我们查看了其他几个节点规则数量都在200-300,这个节点的规则存在某种泄露。

5、问题根源

既然已经查到问题了,但是这个问题我们应该怎么解决呢?我们查了很多的资料,最终在k3s的官方文档发现了以下说明,如图11所示:

11.png图11

官方issue地址:https://github.com/k3s-io/k3s/issues/3117

原来这是一个k3s与iptables版本兼容性的一个官方已知的bug,我们查看了我们服务器的iptables版本,如图12所示:

12.png

图12

发现确实是iptables版本太低导致的。建议升级iptables的版本或者升级k3s版本到 1.23.15使用k3s自带的iptables。

6、总结

这次排查我们绕了好几次,其中有被开发误导的,不过我们还是凭借着我们的运维能力和经验给纠正回来了,最终定位到故障的根源。究其根本这个故障在使用k3s的时候就已经注定了,大家可以看到这个集群已经运行了2年多了,因为这个bug是一个缓慢形成的。所以在我们产品选型的过程中,对于这些基础组件一定要慎重考虑,尽量还是选择官方的k8s版本或者各种公共云的商业版本。简单点说就是随大流,这样出现bug的概率会相对小,就算出现了bug也会很快就解决了。



相关文章

Kudu节点数规划

一、概述由于Kudu是Hadoop生态的一部分(虽然它不依赖于Hadoop生态系统),因此大多数实际应用场景需要的不仅仅是Kudu;为了输入数据,可能需要Kafka、StreamSets或Spark...

Python 识别 MySQL 中的冗余索引

前言最近在搞标准化巡检平台,通过 MySQL 的元数据分析一些潜在的问题。冗余索引也是一个非常重要的巡检目,表中索引过多,会导致表空间占用较大,索引的数量与表的写入速度与索引数成线性关系(微秒级),如...

podman相关使用

Podman介绍Podman 是一个开源的容器运行时项目,可在大多数 Linux 平台上使用。Podman 提供与 Docker 非常相似的功能。正如前面提到的那样,它不需要在你的系统上运行任何守护进...

zabbix监控华为存储设备

zabbix监控华为存储设备

确认监控方式开始监控之前首先思考确认好要监控的方式。提出疑问:zabbix 监控华为存储设备推荐使用snmptrap还是snmptt呢?回答:在 Zabbix 监控华为存储设备时,您可以选择使用 SN...

shell脚本-expect

shell脚本-expect

一、概述       Expect是建立在tcl基础上的一个工具,Expect 是用来进行自动化控制和测试的工具。主要解决shell脚本中不可交互的问题。       在一些需要交互输入指令的场景下,...

使用Sqoop将数据从MySQL导入HBase (二)

使用Sqoop将数据从MySQL导入HBase (二)

创建hbase表create_namespace 'data';create 'data:data', {NAME => 'cf1'}, {NAME => 'cf2'}, {NAME =&...

发表评论    

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。