kubernetes(k8s)容器内无法连接同所绑定的Service ClusterIP问题记录
1. k8s环境
k8s使用kubernetes-server-linux-amd64_1.19.10.tar.gz 二进制bin 的方式手动部署
k8s 版本:
[root@master ~]# kubectl version
Client Version: version.Info{Major:"1", Minor:"19", GitVersion:"v1.19.10", GitCommit:"98d5dc5d36d34a7ee13368a7893dcb400ec4e566", GitTreeState:"clean", BuildDate:"2021-04-15T03:28:42Z", GoVersion:"go1.15.10", Compiler:"gc", Platform:"linux/amd64"}
Server Version: version.Info{Major:"1", Minor:"19", GitVersion:"v1.19.10", GitCommit:"98d5dc5d36d34a7ee13368a7893dcb400ec4e566", GitTreeState:"clean", BuildDate:"2021-04-15T03:20:25Z", GoVersion:"go1.15.10", Compiler:"gc", Platform:"linux/amd64"}
2. 测试nginx-busybox应用
2.1 构建nginx测试镜像Dockerfile
# 基于官方 Nginx 镜像
FROM nginx
# 安装 Busybox
RUN apt-get update && apt-get install -y busybox
# 复制 Nginx 配置文件
# COPY nginx.conf /etc/nginx/nginx.conf
# 复制自定义 HTML 文件(可选)
COPY index.html /usr/share/nginx/html/
# 在容器启动时执行的命令
CMD ["nginx", "-g", "daemon off;"]
echo "hello world" > index.html
docker build . -t nginx-busybox:v1.0
2.2 创建k8s 测试资源nbusybox_deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: nbusybox-deployment
spec:
replicas: 1
selector:
matchLabels:
app: nbusybox
template:
metadata:
labels:
app: nbusybox
spec:
containers:
- name: nbusybox
image: nginx-busybox:v1.0
ports:
- containerPort: 80
---
apiVersion: v1
kind: Service
metadata:
annotations: {}
labels:
k8s.kuboard.cn/name: nbusybox-service-v1
name: nbusybox-service
spec:
clusterIP: 10.233.0.45
ports:
- name: mrmsqs
nodePort: 30233
port: 80
protocol: TCP
targetPort: 80
selector:
app: nbusybox
sessionAffinity: None
type: NodePort
kubectl create -f nbusybox_deployment.yaml
2.3 ping service 测试
[root@master ~]# docker ps | grep nbusybox
975b550531df 3740b49d5078 "/docker-entrypoint.…" 31 minutes ago Up 31 minutes k8s_nbusybox_nbusybox-deployment-6b48bb8f65-mpnhg_default_1a60a955-c5fa-4e08-b8f3-7461edd9ea48_0
6d4a77f60cee k8s.gcr.io/pause:3.2 "/pause" 31 minutes ago Up 31 minutes k8s_POD_nbusybox-deployment-6b48bb8f65-mpnhg_default_1a60a955-c5fa-4e08-b8f3-7461edd9ea48_0
进入容器:
docker exec -it 975b550531df /bin/bash
http get 所在的service clusterIP 发现不通:
root@nbusybox-deployment-6b48bb8f65-mpnhg:/# busybox wget nbusybox-service.default.svc.cluster.local
Connecting to nbusybox-service.default.svc.cluster.local (10.233.0.45:80)
而在http get 其他的service clusterIP 发现可通:
root@nbusybox-deployment-6b48bb8f65-mpnhg:/# busybox wget nginxbusybox-service.default.svc.cluster.local
Connecting to nginxbusybox-service.default.svc.cluster.local (10.233.0.62:80)
saving to 'index.html'
index.html 100% |*******************************************************************************************************************************| 11 0:00:00 ETA
'index.html' saved
以为是kube-proxy 在iptables 的转发模式所致, 尝试更改 kube-proxy 的代理模式为ipvs
./kube-proxy --logtostderr=true --v=4 --hostname-override=192.168.0.251 --master=http://192.168.0.250:8080 --proxy-mode=ipvs
发现问题仍然存在。
kube-proxy 在iptables 和 ipvs 模式下,不会处理ip 包的转发,而是交由linux 网络协议栈处理。猜测是linux 网络协议栈的处理问题。尝试在宿主机对docker0 网卡抓包:
发现 nbusybox-service.default.svc.cluster.local 这个service 可以正常连接了。
当一但关掉 wireshark, nbusybox-service.default.svc.cluster.local 这个service 就连接不了。
在wireshark 的抓包记录中,发现一条 从 10.0.9.2(容器ip) 发往 10.0.9.2 的http 响应,似乎构成了回环。
询问chatgpt:
linux 网络协议栈,对于源IP, 源MAC 跟目标IP, 目标MAC一样的IP包,能正常发送吗?
在 Linux 网络协议栈中,对于源 IP 地址(Source IP)和源 MAC 地址(Source MAC)与目标 IP 地址(Destination IP)和目标 MAC 地址(Destination MAC)完全相同的 IP 包,通常是无法正常发送的。
这是因为在以太网(Ethernet)协议中,源 MAC 和目标 MAC 是用于网络中的数据链路层寻址和交换的重要字段。当一个主机发送一个 IP 包时,它会填充源 IP 和源 MAC 字段,同时指定目标 IP 和目标 MAC 字段。当数据包通过网络进行传输时,数据链路层设备(如交换机)会根据目标 MAC 地址将数据包转发到正确的目的地。
如果源 IP 和源 MAC 与目标 IP 和目标 MAC 完全相同,这意味着数据包将被送回到自身,形成了一个循环。为了避免这种无限循环的情况发生,Linux 网络协议栈通常会阻止发送这样的数据包。
当尝试发送源 IP 和源 MAC 与目标 IP 和目标 MAC 完全相同的数据包时,Linux 网络协议栈通常会丢弃这些数据包或者不进行发送。这是为了确保网络的正常运行和防止数据包的无限循环。
需要注意的是,这种行为可能会因操作系统和网络配置而有所不同,但在大多数常见的 Linux 发行版和网络环境中,源 IP 和源 MAC 与目标 IP 和目标 MAC 完全相同的 IP 包通常无法正常发送。
关掉 wireshark 的混杂模式
再次开启wireshark 对docker0 抓包, 容器内对 nbusybox-service.default.svc.cluster.local service 就连接不了。
问题已初步定位,当启用了 docker0 的混杂模式,容器内对 nbusybox-service.default.svc.cluster.local service 就可以连接。
再次验证, 手动对docker0 启用混杂模式
ifconfig docker0 promisc
测试:
发现 容器内对 nbusybox-service.default.svc.cluster.local service 就可以连接。
3. 结论
当在容器内(10.0.9.2)访问 service, 而这个service 经负载均衡后,找回这个容器自身(10.0.9.2),这时tcp 连接就无法建立,这时开启docker0网卡的混杂模式,tcp连接就可以建立。 但笔者在使用v1.23.17 的k8s 版本在某云厂商的云服务器上部署,是可以在容器内访问自身的Service 的,因此该结论可能还跟操作系统或k8s的版本配置有关系。