例えば
ゲーム用の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