只有 1 个 pod 处理 Kubernetes 集群中的所有请求
Only 1 pod handles all requests in Kubernetes cluster
这是 minikube Kubernetes 的清单文件,用于部署和服务:
apiVersion: apps/v1
kind: Deployment
metadata:
name: hello-deployment
spec:
selector:
matchLabels:
app: hello
replicas: 3
template:
metadata:
labels:
app: hello
spec:
containers:
- name: hello
image: hello_hello
imagePullPolicy: Never
ports:
- containerPort: 4001
protocol: TCP
---
apiVersion: v1
kind: Service
metadata:
name: hello
spec:
selector:
app: hello
ports:
- port: 4001
nodePort: 30036
protocol: TCP
type: NodePort
以及一个用 Golang 编写的简单 HTTP 服务器
package main
import (
http "net/http"
"github.com/gin-gonic/gin"
)
func main() {
r := gin.Default()
r.GET("/ping", func(c *gin.Context) {
c.JSON(200, gin.H{
"message": "pong",
})
})
server := &http.Server{
Addr: ":4001",
Handler: r,
}
server.ListenAndServe()
}
当我向 IP:30036/ping 发出多个请求,然后打开 pod 的日志时,我可以看到只有 3 个中的 1 个 pods 处理所有请求。如何对请求做出其他 pods 响应?
在 Kubernetes 集群中,发送到 k8s 服务的请求由 kube-proxy 路由。
自 Kubernetes v1.2 以来,默认 kube-proxy
模式是 Iptalbles
,它允许服务和后端之间更快的数据包解析 Pods。后端 Pods 之间的负载平衡直接通过 iptables rules
.
完成
也许您没有生成一个 pod 无法处理的足够负载,这就是您从 kube-proxy
路由到同一个 pod 的原因。
您还可以查看此问题的答案以实现自定义 iptalbes-rule
:
- Implementing iptables rules on Kubernetes nodes
您正在使用 NodePort 公开服务,因此没有适当的反向代理,但您直接连接到您的 Pod。这是一个不错的选择。 (稍后您可能想使用 Ingress)
您看到的是只有一个 Pod 处理您的请求。您希望每个请求都负载均衡到不同的 pod。你的假设是正确的,但是负载均衡不是发生在 HTTP 请求层,而是发生在 TCP 层。
因此,当您拥有持久的 TCP 连接并重新使用它时,您将不会体验到您期望的负载平衡。由于建立 TCP 连接在延迟方面相当昂贵,因此通常会进行优化以避免重复打开新的 TCP 连接:HTTP keep-alive。
Keep alive 在大多数框架和客户端中默认启用,Go 也是如此。
试试 s.SetKeepAlivesEnabled(false)
,看看是否能解决您的问题。 (推荐仅用于测试!)
您还可以使用多个不同的客户端,f.e。从命令行使用 curl 或在 Postman 中禁用 keep-alive。
感谢@Thomas 的深刻见解!我尝试处理请求 header,它解决了所有请求只命中一个副本的负载平衡问题,而对于演示或测试,能够将请求分发给所有副本很有用
来自文档:
https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Connection
连接:keep-alive
连接:关闭
这个请求总是命中同一个 pod
curl -H "Connection: keep-alive" http://your-service:port/path
但是,使用 close
,请求平衡到所有 pods
curl -H "Connection: close" http://your-service:port/path
这是 minikube Kubernetes 的清单文件,用于部署和服务:
apiVersion: apps/v1
kind: Deployment
metadata:
name: hello-deployment
spec:
selector:
matchLabels:
app: hello
replicas: 3
template:
metadata:
labels:
app: hello
spec:
containers:
- name: hello
image: hello_hello
imagePullPolicy: Never
ports:
- containerPort: 4001
protocol: TCP
---
apiVersion: v1
kind: Service
metadata:
name: hello
spec:
selector:
app: hello
ports:
- port: 4001
nodePort: 30036
protocol: TCP
type: NodePort
以及一个用 Golang 编写的简单 HTTP 服务器
package main
import (
http "net/http"
"github.com/gin-gonic/gin"
)
func main() {
r := gin.Default()
r.GET("/ping", func(c *gin.Context) {
c.JSON(200, gin.H{
"message": "pong",
})
})
server := &http.Server{
Addr: ":4001",
Handler: r,
}
server.ListenAndServe()
}
当我向 IP:30036/ping 发出多个请求,然后打开 pod 的日志时,我可以看到只有 3 个中的 1 个 pods 处理所有请求。如何对请求做出其他 pods 响应?
在 Kubernetes 集群中,发送到 k8s 服务的请求由 kube-proxy 路由。
自 Kubernetes v1.2 以来,默认 kube-proxy
模式是 Iptalbles
,它允许服务和后端之间更快的数据包解析 Pods。后端 Pods 之间的负载平衡直接通过 iptables rules
.
也许您没有生成一个 pod 无法处理的足够负载,这就是您从 kube-proxy
路由到同一个 pod 的原因。
您还可以查看此问题的答案以实现自定义 iptalbes-rule
:
- Implementing iptables rules on Kubernetes nodes
您正在使用 NodePort 公开服务,因此没有适当的反向代理,但您直接连接到您的 Pod。这是一个不错的选择。 (稍后您可能想使用 Ingress)
您看到的是只有一个 Pod 处理您的请求。您希望每个请求都负载均衡到不同的 pod。你的假设是正确的,但是负载均衡不是发生在 HTTP 请求层,而是发生在 TCP 层。
因此,当您拥有持久的 TCP 连接并重新使用它时,您将不会体验到您期望的负载平衡。由于建立 TCP 连接在延迟方面相当昂贵,因此通常会进行优化以避免重复打开新的 TCP 连接:HTTP keep-alive。
Keep alive 在大多数框架和客户端中默认启用,Go 也是如此。
试试 s.SetKeepAlivesEnabled(false)
,看看是否能解决您的问题。 (推荐仅用于测试!)
您还可以使用多个不同的客户端,f.e。从命令行使用 curl 或在 Postman 中禁用 keep-alive。
感谢@Thomas 的深刻见解!我尝试处理请求 header,它解决了所有请求只命中一个副本的负载平衡问题,而对于演示或测试,能够将请求分发给所有副本很有用
来自文档:
https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Connection 连接:keep-alive 连接:关闭
这个请求总是命中同一个 pod
curl -H "Connection: keep-alive" http://your-service:port/path
但是,使用 close
,请求平衡到所有 pods
curl -H "Connection: close" http://your-service:port/path