我在kubernetes集群中遇到了一些神秘的网络错误。虽然我最初在使用ingress时遇到了这些错误,但当我绕过负载均衡器、绕过kube代理和绕过nginx ingress时,会出现更多错误。当直接转到服务和直接转到pod IP时,出现的错误最多。我相信这是因为负载均衡器和nginx比原始iptable路由有更好的错误处理。
为了测试错误,我使用来自同一子网上VM的apache基准测试,任何并发级别,没有保持活动,连接到pod IP,并使用足够高的请求号,以便有时间扩大或缩小部署。奇怪的是,我修改哪个部署根本无关紧要,因为它总是会导致相同的错误集,即使它与我正在修改的pod无关。任何pod的添加或删除都会触发apache基准测试错误。手动删除、放大/缩小、自动缩放所有触发错误。如果在ab测试运行时没有pod更改,则不会报告任何错误。注意,如果不能消除错误的话,keep-alive似乎可以大大减少错误,但我只测试了几次,从未发现错误。
除了一些奇怪的iptable冲突之外,我真的不明白删除pod A会如何影响pod B的网络连接。由于错误很短,几秒钟内就会消失,这看起来更像是短暂的网络中断。
样本ab测试:ab -n 5000 -c 2 https://10.112.0.24/
使用HTTPS时出错:
SSL handshake failed (5).
SSL read failed (5) - closing connection
使用HTTP:时出错
apr_socket_recv: Connection reset by peer (104)
apr_socket_recv: Connection refused (111)
ab输出示例。遇到第一个错误后的I ctl-C:
$ ab -n 5000 -c 2 https://10.112.0.24/
This is ApacheBench, Version 2.3 <$Revision: 1826891 $>
Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/
Licensed to The Apache Software Foundation, http://www.apache.org/
Benchmarking 10.112.0.24 (be patient)
Completed 500 requests
Completed 1000 requests
SSL read failed (5) - closing connection
Completed 1500 requests
^C
Server Software: nginx
Server Hostname: 10.112.0.24
Server Port: 443
SSL/TLS Protocol: TLSv1.2,ECDHE-RSA-AES256-GCM-SHA384,2048,256
Document Path: /
Document Length: 2575 bytes
Concurrency Level: 2
Time taken for tests: 21.670 seconds
Complete requests: 1824
Failed requests: 2
(Connect: 0, Receive: 0, Length: 1, Exceptions: 1)
Total transferred: 5142683 bytes
HTML transferred: 4694225 bytes
Requests per second: 84.17 [#/sec] (mean)
Time per request: 23.761 [ms] (mean)
Time per request: 11.881 [ms] (mean, across all concurrent requests)
Transfer rate: 231.75 [Kbytes/sec] received
Connection Times (ms)
min mean[+/-sd] median max
Connect: 5 15 9.8 12 82
Processing: 1 9 9.0 6 130
Waiting: 0 8 8.9 6 129
Total: 7 23 14.4 19 142
Percentage of the requests served within a certain time (ms)
50% 19
66% 24
75% 28
80% 30
90% 40
95% 54
98% 66
99% 79
100% 142 (longest request)
可能相关的当前sysctl设置:
net.netfilter.nf_conntrack_tcp_be_liberal = 1
net.nf_conntrack_max = 131072
net.netfilter.nf_conntrack_buckets = 65536
net.netfilter.nf_conntrack_count = 1280
net.ipv4.ip_local_port_range = 27050 65500
我没有看到任何连接"完全"错误。我能说的最好的是没有数据包丢失。我们最近从1.14升级,没有注意到这个问题,但我不能肯定它不存在。我相信我们很快就会被迫离开罗马,因为它似乎不再得到维护,而且当我们升级到kube 1.16.x时,我们在启动时遇到了问题。
我今天在网上搜索了一整天,寻找类似的问题,与我们的问题最相似的是https://tech.xing.com/a-reason-for-unexplained-connection-timeouts-on-kubernetes-docker-abd041cf7e02但我不知道如何实现iptable伪装——假设我们使用的是romana,我读过(https://github.com/kubernetes/kubernetes/pull/78547#issuecomment-527578153),随机完全是我们正在使用的linux内核5的默认值。有什么想法吗?
- kubernetes 1.15.5
- 罗马2.0.2
- centos7
- Linux kube-master01 5.0.7-1.el7.elrepo.x86_64#1 SMP美国东部时间2019年4月5日星期五18:07:52 x86_64 x86_66 GNU/Linux
======2019年11月5日更新======
有人建议测试替代CNI。我选择了印花布,因为我们在一个旧的基于Debian的kube集群中使用了印花布。我用我们最基本的Centos 7模板(vSphere)重建了一个虚拟机,所以我们的定制带来了一些负担。我不能列出我们在模板中定制的所有内容,但最显著的变化是内核5升级yum --enablerepo=elrepo-kernel -y install kernel-ml
。
启动虚拟机后,安装kubernetes并运行测试的最简单步骤如下:
yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo
yum -y install docker-ce-3:18.09.6-3.el7.x86_64
systemctl start docker
cat <<EOF > /etc/yum.repos.d/kubernetes.repo
[kubernetes]
name=Kubernetes
baseurl=https://packages.cloud.google.com/yum/repos/kubernetes-el7-x86_64
enabled=1
gpgcheck=1
repo_gpgcheck=1
gpgkey=https://packages.cloud.google.com/yum/doc/yum-key.gpg https://packages.cloud.google.com/yum/doc/rpm-package-key.gpg
EOF
# Set SELinux in permissive mode (effectively disabling it)
setenforce 0
sed -i 's/^SELINUX=enforcing$/SELINUX=permissive/' /etc/selinux/config
echo '1' > /proc/sys/net/bridge/bridge-nf-call-iptables
yum install -y kubeadm-1.15.5-0 kubelet-1.15.5-0 kubectl-1.15.5-0
systemctl enable --now kubelet
kubeadm init --pod-network-cidr=192.168.0.0/16
mkdir -p $HOME/.kube
sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
kubectl taint nodes --all node-role.kubernetes.io/master-
kubectl apply -f https://docs.projectcalico.org/v3.8/manifests/calico.yaml
cat <<EOF > /tmp/test-deploy.yml
apiVersion: apps/v1 # for versions before 1.9.0 use apps/v1beta2
kind: Deployment
metadata:
name: test
spec:
selector:
matchLabels:
app: test
replicas: 1
template:
metadata:
labels:
app: test
spec:
containers:
- name: nginx
image: nginxdemos/hello
ports:
- containerPort: 80
EOF
# wait for control plane to become healthy
kubectl apply -f /tmp/test-deploy.yml
现在设置已经准备好了,这就是ab测试:
$ docker run --rm jordi/ab -n 100 -c 1 http://192.168.4.4/
This is ApacheBench, Version 2.3 <$Revision: 1826891 $>
Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/
Licensed to The Apache Software Foundation, http://www.apache.org/
Benchmarking 192.168.4.4 (be patient)...apr_pollset_poll: The timeout specified has expired (70007)
Total of 11 requests completed
ab测试在此错误后放弃。如果我减少请求数量以避免超时,这就是你会看到的:
$ docker run --rm jordi/ab -n 10 -c 1 http://192.168.4.4/
This is ApacheBench, Version 2.3 <$Revision: 1826891 $>
Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/
Licensed to The Apache Software Foundation, http://www.apache.org/
Benchmarking 192.168.4.4 (be patient).....done
Server Software: nginx/1.13.8
Server Hostname: 192.168.4.4
Server Port: 80
Document Path: /
Document Length: 7227 bytes
Concurrency Level: 1
Time taken for tests: 0.029 seconds
Complete requests: 10
Failed requests: 0
Total transferred: 74140 bytes
HTML transferred: 72270 bytes
Requests per second: 342.18 [#/sec] (mean)
Time per request: 2.922 [ms] (mean)
Time per request: 2.922 [ms] (mean, across all concurrent requests)
Transfer rate: 2477.50 [Kbytes/sec] received
Connection Times (ms)
min mean[+/-sd] median max
Connect: 0 1 0.8 1 3
Processing: 1 2 1.2 1 4
Waiting: 0 1 1.3 0 4
Total: 1 3 1.4 3 5
Percentage of the requests served within a certain time (ms)
50% 3
66% 3
75% 4
80% 5
90% 5
95% 5
98% 5
99% 5
100% 5 (longest request)
这个问题在技术上与我报道的原始问题不同,但这是一个不同的CNI,仍然存在网络问题。当我在kube/romana集群中运行相同的测试时,它确实有常见的超时错误:在与pod相同的节点上运行ab测试。两者都遇到了相同的超时错误,但在罗马,我可以在达到超时之前收到几千个请求。Calico在收到十几个请求之前遇到超时错误。
其他变体或注释:-netfilter.nf_conntrack_tcp_be_liberal=0/1似乎没有什么区别-较高的CCD_ 3值有时起作用,但它在很大程度上是随机的。-连续几次在低-n
值下运行"ab"测试有时会触发超时
在这一点上,我很确定我们的centos安装有问题,但我甚至猜不出可能是什么。有没有其他限制,sysctl或其他配置可能导致这种情况?
======2019年11月6日更新======
我注意到我们安装了一个旧的内核,所以我用同样的新内核5.3.8-1.el7.elrepo.x86_64升级了我的kube/calico测试虚拟机。更新和几次重新启动后,我再也无法再现"apr_pollset_poll:指定的超时已过期(70007)"timout错误。
现在超时错误消失了,我可以重复原始测试,在vSphere虚拟机上加载测试吊舱A并终止吊舱B。在romana环境中,问题仍然存在,但仅当负载测试在与pod a所在的主机不同的主机上时。如果我在同一台主机上运行测试,则不会出现任何错误。使用Calico而不是romana,两台主机都没有负载测试错误,所以问题消失了。可能还有一些设置需要调整,可以帮助romana,但我认为这是romana的"罢工3",所以我将开始将整个环境过渡到Calico,并在那里进行一些验收测试,以确保没有隐藏的问题。
您提到,如果在ab测试运行时没有pod更改,则不会报告任何错误。因此,这意味着在添加或删除pod时会出现错误。
当pod被删除时,这是正常的行为;iptable规则更改的传播需要时间。可能发生的情况是,容器被删除了,但iptable规则尚未更改,但结束数据包正在转发到不存在的容器,这会导致错误(这有点像竞争条件)。
您可以做的第一件事始终是创建readiness probe
,因为它将确保在容器准备好处理请求之前,流量不会转发到容器。
要做的第二件事是正确地处理删除容器的问题。这是一项有点困难的任务,因为它可能在许多级别上处理,但你能做的最简单的事情是向你的容器添加PreStop钩子,如下所示:
lifecycle:
preStop:
exec:
command:
- sh
- -c
- "sleep 5"
PreStop hook
在pod删除请求的时刻被执行。从这一刻起,k8s开始更改iptable规则,它应该停止将新流量转发到即将被删除的容器。当sleeping时,您给k8s一些时间在集群中传播iptable更改,同时不中断现有连接。PreStop handle
退出后,容器将收到SIGTERM信号。
我的建议是将这两种机制结合起来,并检查它是否有帮助。
您还提到绕过入口会导致更多错误。我认为这是由于ingress已经实现了重试机制。如果它无法打开与容器的连接,它会尝试几次,希望能找到一个可以处理其请求的容器。