headless serviceを使ってVIPを使わずに各podのIPに直接アクセスする

kubernetesはserviceの仕組みの中でVIPを使って背後の複数podへのルーティングを管理していて、多くのケースではよしなにやってくれるので便利なのだが、以前Nettyを使ってservice間のコネクションを管理していて、背後にある複数podへの接続が偏る問題に悩んでいた。やりたいこととしては、背後のpod達に完全に均等に接続を振りたいのに、serviceを使うとランダム要素を排除することができず、一定の確率でコネクションがかたよってしまう。

そのときは接続数を増やす(たとえば3バックエンドに対して3コネクションだとどこかに偏ってしまう可能性大だが、3バックエンドに対して300コネクションはれば平均に回帰するのでだいたい均等になる)&一定時間ごとに接続を貼り直すという対策をとったが、headless serviceを使う手もあるというのを最近知った。

きっかけとしてはこの記事で、gRPCも似たような問題に遭遇することがある。

Kubernetes上でgRPCサービスを動かす | SOTA

  • 公式ドキュメント

kubernetes.io

  • headless serviceについて(日本語)

qiita.com

上記の記事を読むと、serviceのclusterIPをNoneで起動すればheadless serviceになるみたいなので、試してみた。

まずは普通にサービスを起動する場合。

ローカルのminikubeを立ち上げておいて、

minikube start
helm init

ingress controller + バックエンドnginxの組み合わせの公式のhelm chartで試してみる

helm install stable/nginx-ingress --set defaultBackend.replicaCount=3

ingress-controllerのpodが1つ、backendのpodが1つできている。

ᐅ kubectl get pods                                                                                                                                                        
NAME                                                              READY     STATUS    RESTARTS   AGE
opinionated-saola-nginx-ingress-controller-7b64bf5b74-gk6pw       0/1       Running   0          9s
opinionated-saola-nginx-ingress-default-backend-759d876cd-pkgsr   1/1       Running   0          9s
opinionated-saola-nginx-ingress-default-backend-759d876cd-sbdnr   1/1       Running   0          9s
opinionated-saola-nginx-ingress-default-backend-759d876cd-zr8ng   1/1       Running   0          9s
ᐅ kubectl get services                                                                                                                                                     
NAME                                              TYPE           CLUSTER-IP       EXTERNAL-IP   PORT(S)                      AGE
kubernetes                                        ClusterIP      10.96.0.1        <none>        443/TCP                      15m
opinionated-saola-nginx-ingress-controller        LoadBalancer   10.100.231.194   <pending>     80:32561/TCP,443:32723/TCP   24s
opinionated-saola-nginx-ingress-default-backend   ClusterIP      10.97.231.34     <none>        80/TCP                       24s

今回はClusterIPが貼られているのでheadlessではない。この場合はnslookupで引いてもClusterIPが返ってくる。

# nslookup opinionated-saola-nginx-ingress-default-backend
Server:     10.96.0.10
Address:    10.96.0.10#53

Non-authoritative answer:
Name:   opinionated-saola-nginx-ingress-default-backend.default.svc.cluster.local
Address: 10.97.231.34

次にheadless serviceで起動した場合。

helm install stable/nginx-ingress --set defaultBackend.replicaCount=3 --set defaultBackend.service.clusterIP=None

backendのClusterIPをNoneにした。

ᐅ kubectl get service                                                                                                                                                      
NAME                                         TYPE           CLUSTER-IP      EXTERNAL-IP   PORT(S)                      AGE
kubernetes                                   ClusterIP      10.96.0.1       <none>        443/TCP                      19m
original-pig-nginx-ingress-controller        LoadBalancer   10.108.233.23   <pending>     80:30045/TCP,443:30788/TCP   6s
original-pig-nginx-ingress-default-backend   ClusterIP      None            <none>        80/TCP                       6s
# nslookup original-pig-nginx-ingress-default-backend
Server:     10.96.0.10
Address:    10.96.0.10#53

Name:   original-pig-nginx-ingress-default-backend.default.svc.cluster.local
Address: 172.17.0.5
Name:   original-pig-nginx-ingress-default-backend.default.svc.cluster.local
Address: 172.17.0.6
Name:   original-pig-nginx-ingress-default-backend.default.svc.cluster.local
Address: 172.17.0.8

この場合には、3つのアドレスが返ってくる。

headless serviceを使えば各podのIPに直接アクセスできるので、接続の偏りの問題があるときに、クライアント側での対処方法として使えそう。