Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
99 changes: 99 additions & 0 deletions examples/dns-dual-protocol.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
# Deploys a DNS server container and creates a loadbalancer service for it with both UDP and TCP:
#
# export KUBECONFIG=path/to/kubeconfig
# kubectl apply -f dns-dual-protocol.yaml
#
# Wait for `kubectl describe service dns-server` to show "Loadbalancer Ensured",
# then use the IP address found under "LoadBalancer Ingress" to connect to the
# service.
#
# You can test the DNS service with dig:
#
# # Test UDP (default)
# dig @$(kubectl get service dns-server -o jsonpath='{.status.loadBalancer.ingress[0].ip}') example.com
#
# # Test TCP explicitly
# dig +tcp @$(kubectl get service dns-server -o jsonpath='{.status.loadBalancer.ingress[0].ip}') example.com
#
# To view the logs:
#
# kubectl logs -l "app=dns-server"
#
---
apiVersion: v1
kind: ConfigMap
metadata:
name: coredns-config
data:
Corefile: |
.:53 {
log
errors
health
ready
whoami
forward . 8.8.8.8 9.9.9.9
}
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: dns-server
spec:
replicas: 2
selector:
matchLabels:
app: dns-server
template:
metadata:
labels:
app: dns-server
spec:
containers:
- name: coredns
image: coredns/coredns:1.11.1
args:
- -conf
- /etc/coredns/Corefile
volumeMounts:
- name: config
mountPath: /etc/coredns
ports:
- containerPort: 53
protocol: UDP
name: dns-udp
- containerPort: 53
protocol: TCP
name: dns-tcp
livenessProbe:
httpGet:
path: /health
port: 8080
readinessProbe:
httpGet:
path: /ready
port: 8181
volumes:
- name: config
configMap:
name: coredns-config
---
apiVersion: v1
kind: Service
metadata:
labels:
app: dns-server
name: dns-server
spec:
ports:
- port: 53
protocol: UDP
targetPort: 53
name: dns-udp
- port: 53
protocol: TCP
targetPort: 53
name: dns-tcp
selector:
app: dns-server
type: LoadBalancer
63 changes: 63 additions & 0 deletions examples/udp.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
# Deploys a UDP echo server container and creates a loadbalancer service for it:
#
# export KUBECONFIG=path/to/kubeconfig
# kubectl apply -f udp-echo.yml
#
# Wait for `kubectl describe service udp-echo` to show "Loadbalancer Ensured",
# then use the IP address found under "LoadBalancer Ingress" to connect to the
# service.
#
# You can test the UDP service with netcat:
#
# echo "Tell me a joke" | nc -u -w 1 $(kubectl get service udp-echo -o jsonpath='{.status.loadBalancer.ingress[0].ip}') 5000
#
# To view the logs:
#
# kubectl logs -l "app=udp-echo"
#
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: udp-echo
spec:
replicas: 2
selector:
matchLabels:
app: udp-echo
template:
metadata:
labels:
app: udp-echo
spec:
containers:
- name: udp-echo
image: docker.io/alpine/socat
command:
- socat
- "-v"
- "UDP4-RECVFROM:5353,fork"
- "SYSTEM:echo 'I could tell you a UDP joke, but you might not get it...',pipes"
ports:
- containerPort: 5353
protocol: UDP
---
apiVersion: v1
kind: Service
metadata:
annotations:
k8s.cloudscale.ch/loadbalancer-health-monitor-type: udp-connect
k8s.cloudscale.ch/loadbalancer-health-monitor-delay-s: "3"
k8s.cloudscale.ch/loadbalancer-health-monitor-timeout-s: "2"
labels:
app: udp-echo
name: udp-echo
spec:
ports:
- port: 5000
protocol: UDP
targetPort: 5353
name: udp
selector:
app: udp-echo
type: LoadBalancer
15 changes: 13 additions & 2 deletions pkg/cloudscale_ccm/loadbalancer.go
Original file line number Diff line number Diff line change
Expand Up @@ -115,8 +115,14 @@ const (
// as all pools have to be recreated.
LoadBalancerPoolAlgorithm = "k8s.cloudscale.ch/loadbalancer-pool-algorithm"

// LoadBalancerPoolProtocol defines the protocol for all the pools of the
// service. We are technically able to have different protocols for
// LoadBalancerPoolProtocol defines the protocol the pools of a port
// with protocol `TCP` use. Set it to `proxy` and `proxyv2` if TCP
// traffic should be forwarded using these protocols.
//
// When setting the protocol of a port to `UDP`, traffic is always forwarded
// using UDP.
//
// We are technically able to have different protocols for
// different ports in a service, but as our options apart from `tcp` are
// currently `proxy` and `proxyv2`, we go with Kubernetes's recommendation
// to apply these protocols to all incoming connections the same way:
Expand Down Expand Up @@ -216,6 +222,8 @@ const (
// LoadBalancerHealthMonitorType defines the approach the monitor takes.
// (ping, tcp, http, https, tls-hello).
//
// Note that the same type is used for all ports in your service.
//
// See https://www.cloudscale.ch/en/api/v1#health-monitor-types
//
// Changing this annotation on an active service may lead to new
Expand All @@ -233,6 +241,9 @@ const (
// LoadBalancerListenerProtocol defines the protocol used by the listening
// port on the loadbalancer. Currently, only tcp is supported.
//
// This property is ignored for ports with the protocol `UDP`, where
// we always create udp listeners.
//
// See https://www.cloudscale.ch/en/api/v1#listener-protocols
//
// Changing this annotation on an established service may cause downtime
Expand Down
25 changes: 18 additions & 7 deletions pkg/cloudscale_ccm/reconcile.go
Original file line number Diff line number Diff line change
Expand Up @@ -122,9 +122,10 @@ func desiredLbState(

for _, port := range serviceInfo.Service.Spec.Ports {

if port.Protocol != "TCP" {
if port.Protocol != v1.ProtocolTCP && port.Protocol != v1.ProtocolUDP {
return nil, fmt.Errorf(
"service %s: cannot use %s for %d, only TCP is supported",
"service %s: cannot use %s for %d"+
", only TCP and UDP are supported",
serviceInfo.Service.Name,
port.Protocol,
port.Port)
Expand All @@ -145,10 +146,15 @@ func desiredLbState(
}
}

poolProtocol := protocol
if port.Protocol != v1.ProtocolTCP {
poolProtocol = "udp"
}

pool := cloudscale.LoadBalancerPool{
Name: poolName(port.Protocol, port.Name),
Algorithm: algorithm,
Protocol: protocol,
Protocol: poolProtocol,
}
s.pools = append(s.pools, &pool)

Expand Down Expand Up @@ -213,7 +219,7 @@ func desiredLbState(
s.monitors[&pool] = append(s.monitors[&pool], *monitor)

// Add a listener for each pool
listener, err := listenerForPort(serviceInfo, int(port.Port))
listener, err := listenerForPort(serviceInfo, port)
if err != nil {
return nil, err
}
Expand Down Expand Up @@ -813,16 +819,21 @@ func runActions(
// annotations into consideration.
func listenerForPort(
serviceInfo *serviceInfo,
port int,
port v1.ServicePort,
) (*cloudscale.LoadBalancerListener, error) {

var (
listener = cloudscale.LoadBalancerListener{}
err error
)

listener.Protocol = serviceInfo.annotation(LoadBalancerListenerProtocol)
listener.ProtocolPort = port
listenerProtocol := serviceInfo.annotation(LoadBalancerListenerProtocol)
if port.Protocol != v1.ProtocolTCP {
listenerProtocol = "udp"
}

listener.Protocol = listenerProtocol
listener.ProtocolPort = int(port.Port)
listener.Name = listenerName(listener.Protocol, listener.ProtocolPort)

listener.TimeoutClientDataMS, err = serviceInfo.annotationInt(
Expand Down
Loading
Loading