我正在尝试使用 kubernetes 实现零停机部署,但在测试期间,服务的负载平衡效果不佳。
我的 kubernetes 清单是:
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
name: myapp-deployment
spec:
replicas: 3
strategy:
type: RollingUpdate
rollingUpdate:
maxUnavailable: 0
maxSurge: 1
template:
metadata:
labels:
app: myapp
version: "0.2"
spec:
containers:
- name: myapp-container
image: gcr.io/google-samples/hello-app:1.0
imagePullPolicy: Always
ports:
- containerPort: 8080
protocol: TCP
readinessProbe:
httpGet:
path: /
port: 8080
initialDelaySeconds: 5
periodSeconds: 5
successThreshold: 1
---
apiVersion: v1
kind: Service
metadata:
name: myapp-lb
labels:
app: myapp
spec:
type: LoadBalancer
externalTrafficPolicy: Local
ports:
- port: 80
targetPort: 8080
selector:
app: myapp
如果我使用外部 IP 循环该服务,假设:
$ kubectl get services
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes ClusterIP 10.35.240.1 <none> 443/TCP 1h
myapp-lb LoadBalancer 10.35.252.91 35.205.100.174 80:30549/TCP 22m
使用 bash 脚本:
while True
do
curl 35.205.100.174
sleep 0.2s
done
connection refused
我在部署过程中收到了一些:
curl:(7)无法连接到 35.205.100.174 端口 80:连接被拒绝
该应用程序是默认的你好由 Google Cloud Platform 提供并在 8080 上运行。
集群信息:
- Kubernetes 版本:1.8.8
- 谷歌云平台
- 机器类型:g1-small
答案1
我遇到了同样的问题,并尝试在 GKE 网络设置中深入研究这种负载平衡。
我怀疑运行容器的节点上的 iptables 规则更新得太早了。我在您的示例中稍微增加了超时时间,以便更好地找到请求超时的阶段。
我对您的部署所做的更改:
spec:
...
replicas: 1 # easier to track the state of the system
minReadySeconds: 30 # give the load-balancer time to pick up the new node
...
template:
spec:
containers:
command: ["sh", "-c", "./hello-app"] # ignore SIGTERM and keep serving requests for 30s
一切运行良好,直到旧 pod 从状态切换Running
到Terminating
。我kubectl port-forward
在终止 pod 上进行了测试,我的请求没有超时就得到了处理。
Running
从 到的转变过程中会发生以下事情 Terminating
:
- Pod-IP 已从服务中删除
- 节点健康检查返回 503
"localEndpoints": 0
- iptables 规则已更改,并且该服务的流量被丢弃(
--comment "default/myapp-lb: has no local endpoints" -j KUBE-MARK-DROP
负载均衡器的默认设置是每 2 秒检查一次,需要 5 次故障才能删除节点。这意味着数据包至少会丢失 10 秒。在我将间隔更改为 1 并仅在 1 次故障后切换后,丢失的数据包数量减少了。
如果您对客户端的源 IP 不感兴趣,您可以删除该行:
externalTrafficPolicy: Local
在您的服务定义和部署中没有连接超时。
在具有 4 个节点和版本的 GKE 集群上进行测试v1.9.7-gke.1
。
答案2
查看您在评论中分享的屏幕截图,您遇到的问题不是您的 k8s 集群无法接受并正确回复 HTTP GET / 请求,而是 siege 及其工作方式的问题。我自己也遇到过几次这种情况。
请参阅此 github 问题以供参考:https://github.com/JoeDog/siege/issues/127
问题是默认情况下 siege 会关闭每个连接,使端口处于 TIME_WAIT 状态,这意味着一段时间内无法重新使用。您的服务器只是用完了可用端口。
基本上,当您使用了所有可用的临时端口时。您可以使用以下命令检查可用的端口范围:
sysctl net.ipv4.ip_local_port_range
以及它们从 TIME_WAIT 变为 CLOSE 需要多长时间:
sysctl net.ipv4.tcp_fin_timeout
在我目前正在使用的 Linux 桌面上,这些是值:
sysctl net.ipv4.ip_local_port_range
net.ipv4.ip_local_port_range = 32768 60999
sysctl net.ipv4.tcp_fin_timeout
net.ipv4.tcp_fin_timeout = 60
这意味着它在 60 秒内不能使用超过 28231 个套接字(可用范围在 32768 到 60999 之间)。60 秒后,系统将从 TCP 连接终止的那一刻起等待已达到该时间量的套接字,以便实际释放套接字,以便将其用于新连接:
tcp_fin_timeout
在套接字始终关闭之前,接收最终 FIN 所需的时间长度(以秒为单位)。这严格违反了 TCP 规范,但是为了防止拒绝服务攻击而必需的。 http://www.tldp.org/LDP/Linux-Filesystem-Hierarchy/html/proc.html
这就是为什么您会看到间歇性错误,而不是仅仅到达围攻停止形成连接的位置。
如果您感兴趣的是对您的部署进行更严格的压力测试,并且考虑到您正在从不会在生产中使用的测试实例启动测试,您可以简单地将该值暂时降低到更低的值:
sysctl net.ipv4.tcp_fin_timeout=30
并且最大化短暂利润范围:
sudo sysctl -w net.ipv4.ip_local_port_range="1024 65535"
这将改变这些值直到您的会话结束,并且一旦您重新启动服务将恢复为默认值。
如果要使更改永久生效,可以覆盖 /proc/ 中的相应值:
echo "your new port range" > /proc/sys/net/ipv4/ip_local_port_range
echo "your new timeout" > /proc/sys/net/ipv4/tcp_fin_timeout
事实上,这一切还有更多的复杂性,但至少这足以让你的测试持续更长时间。
此外,如果你想在某些发行版上检查套接字统计信息和状态,经典版netstat
将不再存在。在这种情况下,你可以使用党卫军像这样检查 TIME-WAIT 上的套接字:
ss state time-wait