k8sのserviceにtype: LoadBalancer指定すると複数プロトコルが指定できない

Pocket

例えば

 ゲーム用のDedicated Serverによくありがちな、ConnectionにUDPをQueryにTCPを使用するような、1プロセスで複数のプロトコルを併用するようなプロセスをKubernetes環境に載せようとする。

kind: Service
apiVersion: v1
metadata:
  name:  acserver4-service
  namespace: acserver
  labels:
    app: acserver1
spec:
  type: LoadBalancer
  ports:
  - name: query1
    port: 9610
    targetPort: 9610
    protocol: TCP
  - name: query2
    port: 8085
    targetPort: 8085
    protocol: TCP
  - name: connection
    port: 9610
    targetPort: 9610
    protocol: UDP
  selector:
    app: acserver1
  loadBalancerIP: 192.168.1.200

 このような、UDPとTCPが混在しているServiceをデプロイしようとする。type: NodePortを使用している場合は(恐らくは)問題ないけれど、type: LoadBalancerを使用する環境だと下記の様なエラーが返る。

$ kubectl apply -f kubernetes/game_servers/acserver/acservertest.yaml
The Service "acserver4-service" is invalid: spec.ports: Invalid value: []core.ServicePort{core.ServicePort{Name:"query1", Protocol:"TCP", Port:9610, TargetPort:intstr.IntOrString{Type:0, IntVal:9610, StrVal:""}, NodePort:0}, core.ServicePort{Name:"query2", Protocol:"TCP", Port:8085, TargetPort:intstr.IntOrString{Type:0, IntVal:8085, StrVal:""}, NodePort:0}, core.ServicePort{Name:"connection", Protocol:"UDP", Port:9610, TargetPort:intstr.IntOrString{Type:0, IntVal:9610, StrVal:""}, NodePort:0}}: cannot create an external load balancer with mix protocols

 このKubernetes環境ではL4LBとしてMetalLBで、L7LBとしてIngressでそれぞれLBが収容しているので、Bare-Metalなk8s環境でLBを用意していないが、type: LoadBalancerを使用することができる。

原因と対策

cannot create an external load balancer with mix protocols

 単純に、type: LoadBalancerはmixed protocolsをサポートしない。今後のk8sも恐らくサポートされない。詳細はこのPull Reqestが詳しいと思う。
Allow for mixed UDP/TCP ports on LoadBalancer Services by Miouge1 · Pull Request #64471 · kubernetes/kubernetes · GitHub
 そもそもMetalLBの様なUDPをサポートするLoadBalancerの方が例外的で、通常はTCPの負荷分散に使用される。なのでMixedでLoadBalancer指定はどちらかと言えば例外的(本来は必要ない)と思う。

対策

 単純に「別々のserviceを作成する」のが正しい。

---
kind: Service
apiVersion: v1
metadata:
  name:  acserver1-service-tcp
  namespace: acserver
  labels:
    app: acserver1
  annotations:
    metallb.universe.tf/allow-shared-ip: "acserver-service"
spec:
  type: LoadBalancer
  ports:
  - name: query1
    port: 9610
    targetPort: 9610
    protocol: TCP
  - name: query2
    port: 8085
    targetPort: 8085
    protocol: TCP
  selector:
    app: acserver1
  loadBalancerIP: 192.168.1.144
---
kind: Service
apiVersion: v1
metadata:
  name:  acserver1-service-udp
  namespace: acserver
  labels:
    app: acserver1
  annotations:
    metallb.universe.tf/allow-shared-ip: "acserver-service"
spec:
  type: LoadBalancer
  ports:
  - name: connection
    port: 9610
    targetPort: 9610
    protocol: UDP
  selector:
    app: acserver1
  loadBalancerIP: 192.168.1.144
---

 ただし、そのままではプロトコル別にIPアドレスを分けないとMetalLBのRouterからアドレスが割り当てられないので(恐らくPendingの状態で止まると思う)、annotationを付けて調整する必要がある。

annotations:
    metallb.universe.tf/allow-shared-ip: "acserver-service"

 annotationのvalueが、そのまま関連付けるserviceのsharing key(関連付けの為のキー)になるので、適当な一意の名前付けをする。
 詳細はMetalLBのドキュメントが詳しい。
MetalLB, bare metal load-balancer for Kubernetes

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です

このサイトはスパムを低減するために Akismet を使っています。コメントデータの処理方法の詳細はこちらをご覧ください