部署期间 kubernetes 连接被拒绝

部署期间 kubernetes 连接被拒绝

我正在尝试使用 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 从状态切换RunningTerminating。我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

相关内容