grpc中的dns负载均衡
(金庆的专栏 2018.8)
grpc-go 中如下连接服务器,请求将在多个IP之间轮转。
conn, err := grpc.Dial(
"dns:///rng-headless:8081",
grpc.WithBalancerName(roundrobin.Name),
grpc.WithInsecure())
标准的目标名应该是这样的:`"dns://authority/endpoint_name"`,
此处 authority 为空,详见:https://github.com/grpc/grpc/blob/master/doc/naming.md
服务器开3个实例,所有请求在3个实例上轮转:
[jinqing@host-10-2-3-4 RoundRobin]$ kubectl run -it --rm jinqing-roundrobin --image=jinq0123/roundrobin:4
If you don't see a command prompt, try pressing enter.
2018/08/28 10:18:01 request 7754383576636566559
2018/08/28 10:18:02 request 2543876599219675746
2018/08/28 10:18:03 request 927204261937181213
2018/08/28 10:18:04 request 7754383576636566559
2018/08/28 10:18:05 request 2543876599219675746
2018/08/28 10:18:06 request 927204261937181213
...
服务器返回一个随机数,不同实例的随机数不同。代码是从
https://github.com/kcollasarundell/balancing-on-k8s 修改的。
...
const (
port = ":8081"
)
type server struct{}
var r int64
func init(){
rand.Seed(time.Now().UnixNano())
r = rand.Int63()
}
func (s *server) Rng(context.Context, *rng.Source) (*rng.RN, error) {
return &rng.RN{RN: r}, nil
}
func main() {
lis, err := net.Listen("tcp", port)
if err != nil {
log.Fatalf("failed to listen: %v", err)
}
s := grpc.NewServer()
rng.RegisterRngServer(s, &server{})
// Register reflection service on gRPC server.
reflection.Register(s)
if err := s.Serve(lis); err != nil {
log.Fatalf("failed to serve: %v", err)
}
}
先编译,打包成镜像,然后用 `balancing-on-k8s\backend\kube.yaml` 运行:
kubectl apply -f kube.yaml
`backend\kube.yaml` 创建了一个 ClusterIP 服务和一个 Headless 服务,部署了 3 个服务器实例。
[jinqing@host-10-2-3-4 RoundRobin]$ kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 93d
rng-cluster ClusterIP 10.111.30.205 <none> 8081/TCP 4h
rng-headless ClusterIP None <none> 8081/TCP,8080/TCP 4h
客户端是一个简单的grpc, 定时发送请求,打印返回的随机数。
`balancing-on-k8s\clientSideBalancer\RoundRobin\main.go`中的地址需要添加端口,
不然grpc会去连接 443 端口而失败。
扩容后,测到大概3分钟后才看到负载转移。缩容后会立即生效。
kubectl scale --replicas=5 deployment/rng
如果是 ClusterIP 服务, 则服务名对应一个ClusterIP;
如果是 Headless 服务,则服务名对应各个Pod的IP:
/ # nslookup rng-headless
Server: 10.96.0.10
Address: 10.96.0.10#53
Name: rng-headless.default.svc.cluster.local
Address: 10.244.3.27
Name: rng-headless.default.svc.cluster.local
Address: 10.244.0.108
Name: rng-headless.default.svc.cluster.local
Address: 10.244.2.66
/ # nslookup rng-cluster
Server: 10.96.0.10
Address: 10.96.0.10#53
Name: rng-cluster.default.svc.cluster.local
Address: 10.111.30.205
/ #
如果去除 "dns:///", 仅仅是域名加端口:
conn, err := grpc.Dial(
"rng-headless:8081",
grpc.WithBalancerName(roundrobin.Name),
...
则只会请求同一个实例。只有当该实例pod被删除后才会切换到另一个实例。
使用缩容时发现会优先删除没有客户端连接的实例。
用2个客户端连接到不同服务器实例,然后缩容为1实例,就可以看到请求切换。
如果客户端和服务器数量很大,这个dns负载均衡就不合适了,因为客户端会连接每个服务器实例。
参考:
Exploring Kubernetes Service Discovery and loadbalancing ( https://kca.id.au/post/k8s_service/ )