脱离背景谈历史就是耍流氓,没想到在纯技术层面也能应验
文章会涉及到的技术领域如下:
事情是这样的...
周一我们突然发现集成k8s的测试环境不能正常使用了,现象如下:
GET
方法的静态地址规律性404
504
追踪nginx
和业务程序日志后,我们基本能定位到是 redis
服务不可用和后端的一台nginx
容器无法连接导致问题。我们的简易架构图如下:
archer
定位到问题后,我们认为问题应该很好解决了。我们在 proxy
中去掉有问题的服务器。前端 404
的问题解决了。但在问题2和问题3上,我们折腾了2天依然才解决了问题却依然无法修复根源问题。
先介绍一个大背景。历史原因,我们有一套 V1.5版本的k8s集群,大家要知道,现在k8s最新版已经是 1.18了,这样的大背景,对我们排查问题造成了很大的困扰。具体的困扰如下:
问题1:最大的问题是我们要尽快学会1.5版本的k8s,比如1.5版本不使用 ~/.kube/conf
来管控k8s,这个在问题排查初期我们可不是这么认为的,因为我们在执行kubectl
命令时,总会报错,
[root@k8s:~]# kubectl get pods
The connection to the server localhost:8080 was refused - did you specify the right host or port?
直接搜索报错关键字会发现,所有的解决方案都指向 ~/.kube/conf
不存在。大家要留意,这是google
的搜索结果哦,说服力很强的哦。
google-resule
经检查确实没有发现该文件,
[root@K8S:~]# ls ~/.kube/
cache schema
所以在问题第一阶段,我们理所当然的认为是哪位误删除了k8s的集群配置文件。
我们起初认为是哪位只是把 ~/.kube/conf
删除了,所以想把 /etc/kubernetes/admin.conf
复制到 .kube/conf
即可,但可悲的是这个文件也没有。
我们当时很生气但更害怕,因为我们再次觉得,是哪位入侵了我们的服务器「Linus会不会哭晕在厕所,他的Linux是如此的不靠谱,只要出问题,就怀疑是服务器被入侵」,因为这根本不像是误删除,根本就像是有意而为。
虽然被产品、开发、测试催成狗,我还是简单过滤了系统安全日志和 command history
,初步确认没有入侵痕迹后,心里略松了一口气「linus想必也已经退了要我面基的飞机票」。
ok,现在问题的重心就是生成 .kube/conf
配置文件了,因为是二进制安装的,所以
kubeadmin config print-default
直接生成配置的办法也泡汤了,在连蒙带骗
的各种尝试下,我们拼出了 k8s 1.5版本生成 conf的方式。
kubectl config set-cluster kubernetes \
--certificate-authority=/etc/kubernetes/openssl/ca.crt \
--embed-certs=true \
--server=https://ipadress:443 \
--kubeconfig=kubectl.kubeconfig
总算是生成 ~/.kube/conf
了,但接下来蒙逼的事情发生了。
~/.kube/conf
有了,在和1.16 版本的配置对比后,也确认配置是生效的,但问题没有消除,依旧报错
[root@k8s:~]# kubectl get pods
The connection to the server localhost:8080 was refused - did you specify the right host or port?
这时我们怀疑集群真的是坏了.... 但到目前为止不知道怎么去修.... 所以我们只能告知产品、开产、测试、PM尝试在其它测试环境重构测试数据...
当时我有怀疑是服务器重启,k8s的进程不在了,所以特意看了下系统时间
[root@K8S:~]# uptime
13:19:20 up 82 days, 18:52, 1 user, load average: 0.18, 0.21, 0.24
看到这里,我没怀疑是进程挂了因为没理由,后面的事实证明,是我幼稚了。但现在想解决这个问题,只能找到二进制安装k8s的文档,重头再来遍试试了。
网上1.5的版本太少了。抱着试试的态度,我在wiki上找了找,竟然找到部署文档。
神奇又恶心的事情来了,两个
master
机的kube-apiserver
竟然没有了解 k8s 架构的朋友知道,google
在k8s
的架构中,是专门把apiserver
进程独立出来,是Master/Node
及所有api
接口的总闸门。
.kube/conf
没用为了验证 .kube/conf
在k8s 1.5版本中确实无用,所以我们特意mv conf{,.bak}
,发现1.5版本确实不需要conf。
Too young, Too Simple
到这里,你以为问题就搞定了吗?...
Too young, Too Simple +2
我们以为大功告成,让大家用起来。这时才发现 问题2和问题3问题依旧。即验证码和登录依然 5xx。。
进一步排查问题后,我们发现,业务日志里报错 redis
无法连接.
这个问题,我们起初认为是小问题,因为通常问题无非两个:
简单画下k8s svc 模式下数据流走向。
redis-k8s
redis
时,先经过svc, svc 在etcd中查询到对应主机,对应端口后;kube-proxy
把请求转发至对应主机的kube-proxy
进程kube-proxy
接受并处理请求,最终的数据按原路返回给请求方一个完整的请求流结束。
所以,我们
telnet pod1 30007
如果通的话,即可以证明 redis 就是通的了?对吧?「嘿嘿,记住你心里的答案哦」
telnet
是时代的产物,发展至今就只剩下测试对端程序是否监听了TCP
指定端口。如果能出现如下类似提示,即可以说明对端已经监听了指定的端口了,这是毫无疑问且无争议的。
telnet
敲黑板!
敲黑板!
敲黑板!
重要的事情说三遍!通常情况下,确实如此。但结合k8s后,这里有一个巨大的陷阱!!
下面会通过两个试验给大家说明,请耐心看下去:
kubectl apply -f tmp-svc.yml
# cat tmp-svc.yml
apiVersion: v1
kind: Service
metadata:
name: tmp-port
spec:
type: NodePort
ports:
- port: 6379
targetPort: 6379
protocol: TCP
name: httprequest
nodePort: 30090
selector:
t1: tmp-port
大家会发现,端口全部都能 telnet 通的哦!
但需要注意的是!
注意!
注意!!
我们集群中并没有label
是 t1=tmp-port
的pod哦
telnet
redis-cli测试
但redis-cli
是可以正常连通的哦!
这点很重要,这里要埋个伏笔。
# cat im-redis-service.yml
apiVersion: v1
kind: Service
metadata:
name: t1-im-redis
spec:
type: NodePort
ports:
- port: 6379
targetPort: 6379
protocol: TCP
name: httprequest
nodePort: 30031
selector:
t1: im-redis
pod yml
太长,这里不详述。
telnet测试
大家也能看到,redis-cli 是能正常连接的哦
redis-cli连接测试
那么问题来了?!!!大家还记得前面问大家的问题吗?
telnet
通了就代表服务正常启动了?!
大家有没有发现,在结合k8s
后,连telnet
都不能相信了。我们必须用最贴近结果的方式来验证结果,才有可能得到我们最终期望的结果!
啊!生活好难!
请不要小看这个问题,为什么我敢这么讲呢?你想像这么一个场景:
你的k8s集群中,每个节点上运行有诸多POD
容器,而这些节点有一个节点不明原因坏了,容器都可以正常部署且状态正常,telnet
也可以通,监控也正常,但就是外部的其它节点无法连通他。
你想想该如何发现并定位到问题吧。。。
下次抽时间和大家聊聊 k8s 的网络架构,但k8s的网络架构是出了名的复杂!在这之前,要请大家需要做好一些基础网络知识技能储备。