Improve kubernetes external name service support for UDP

This commit is contained in:
Kevin Pollet 2021-01-19 09:30:05 +01:00 committed by GitHub
parent bbee63fcf3
commit 41d22ef17e
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
9 changed files with 309 additions and 58 deletions

View file

@ -1322,7 +1322,7 @@ Register the `IngressRouteUDP` [kind](../../reference/dynamic-configuration/kube
|------|--------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| [1] | `entryPoints` | List of [entrypoints](../routers/index.md#entrypoints_1) names |
| [2] | `routes` | List of routes |
| [3] | `routes[n].services` | List of [Kubernetes service](https://kubernetes.io/docs/concepts/services-networking/service/) definitions |
| [3] | `routes[n].services` | List of [Kubernetes service](https://kubernetes.io/docs/concepts/services-networking/service/) definitions (See below for `ExternalName Service` setup) |
| [4] | `services[n].name` | Defines the name of a [Kubernetes service](https://kubernetes.io/docs/concepts/services-networking/service/) |
| [6] | `services[n].port` | Defines the port of a [Kubernetes service](https://kubernetes.io/docs/concepts/services-networking/service/). This can be a reference to a named port. |
| [7] | `services[n].weight` | Defines the weight to apply to the server load balancing |
@ -1348,6 +1348,105 @@ Register the `IngressRouteUDP` [kind](../../reference/dynamic-configuration/kube
weight: 10
```
!!! important "Using Kubernetes ExternalName Service"
Traefik backends creation needs a port to be set, however Kubernetes [ExternalName Service](https://kubernetes.io/fr/docs/concepts/services-networking/service/#externalname) could be defined without any port.
Accordingly, Traefik supports defining a port in two ways:
- only on `IngressRouteUDP` service
- on both sides, you'll be warned if the ports don't match, and the `IngressRouteUDP` service port is used
Thus, in case of two sides port definition, Traefik expects a match between ports.
??? example "Examples"
```yaml tab="IngressRouteUDP"
---
apiVersion: traefik.containo.us/v1alpha1
kind: IngressRouteUDP
metadata:
name: test.route
namespace: default
spec:
entryPoints:
- foo
routes:
- services:
- name: external-svc
port: 80
---
apiVersion: v1
kind: Service
metadata:
name: external-svc
namespace: default
spec:
externalName: external.domain
type: ExternalName
```
```yaml tab="ExternalName Service"
---
apiVersion: traefik.containo.us/v1alpha1
kind: IngressRouteUDP
metadata:
name: test.route
namespace: default
spec:
entryPoints:
- foo
routes:
- services:
- name: external-svc
---
apiVersion: v1
kind: Service
metadata:
name: external-svc
namespace: default
spec:
externalName: external.domain
type: ExternalName
ports:
- port: 80
```
```yaml tab="Both sides"
---
apiVersion: traefik.containo.us/v1alpha1
kind: IngressRouteUDP
metadata:
name: test.route
namespace: default
spec:
entryPoints:
- foo
routes:
- services:
- name: external-svc
port: 80
---
apiVersion: v1
kind: Service
metadata:
name: external-svc
namespace: default
spec:
externalName: external.domain
type: ExternalName
ports:
- port: 80
```
### Kind: `TLSOption`
`TLSOption` is the CRD implementation of a [Traefik "TLS Option"](../../https/tls.md#tls-options).

View file

@ -12,3 +12,5 @@ spec:
- name: whoamiudp
namespace: default
port: 8090
- name: externalname-svc
port: 9090

View file

@ -317,7 +317,17 @@
}
},
"udpServices": {
"default-test3.route-0@kubernetescrd": {
"default-test3.route-0-externalname-svc-9090@kubernetescrd": {
"loadBalancer": {
"servers": [
{
"address": "domain.com:9090"
}
]
},
"status": "enabled"
},
"default-test3.route-0-whoamiudp-8090@kubernetescrd": {
"loadBalancer": {
"servers": [
{
@ -328,6 +338,21 @@
}
]
},
"status": "enabled"
},
"default-test3.route-0@kubernetescrd": {
"weighted": {
"services": [
{
"name": "default-test3.route-0-whoamiudp-8090",
"weight": 1
},
{
"name": "default-test3.route-0-externalname-svc-9090",
"weight": 1
}
]
},
"status": "enabled",
"usedBy": [
"default-test3.route-0@kubernetescrd"

View file

@ -146,6 +146,43 @@ spec:
app: traefiklabs
task: whoamiudp
---
apiVersion: v1
kind: Service
metadata:
name: external-svc
namespace: default
spec:
externalName: external.domain
type: ExternalName
---
apiVersion: v1
kind: Service
metadata:
name: external.service.with.port
namespace: default
spec:
externalName: external.domain
type: ExternalName
ports:
- name: http
protocol: TCP
port: 80
---
apiVersion: v1
kind: Service
metadata:
name: external.service.without.port
namespace: default
spec:
externalName: external.domain
type: ExternalName
ports:
- name: http
protocol: TCP
---
kind: Endpoints
apiVersion: v1

View file

@ -0,0 +1,14 @@
apiVersion: traefik.containo.us/v1alpha1
kind: IngressRouteUDP
metadata:
name: test.route
namespace: default
spec:
entryPoints:
- foo
routes:
- services:
- name: external-svc
port: 8000

View file

@ -0,0 +1,14 @@
apiVersion: traefik.containo.us/v1alpha1
kind: IngressRouteUDP
metadata:
name: test.route
namespace: default
spec:
entryPoints:
- foo
routes:
- services:
- name: external.service.with.port
port: 80

View file

@ -0,0 +1,13 @@
apiVersion: traefik.containo.us/v1alpha1
kind: IngressRouteUDP
metadata:
name: test.route
namespace: default
spec:
entryPoints:
- foo
routes:
- services:
- name: external-svc

View file

@ -82,11 +82,9 @@ func TestLoadIngressRouteTCPs(t *testing.T) {
Servers: []dynamic.TCPServer{
{
Address: "10.10.0.1:8000",
Port: "",
},
{
Address: "10.10.0.2:8000",
Port: "",
},
},
},
@ -123,11 +121,9 @@ func TestLoadIngressRouteTCPs(t *testing.T) {
Servers: []dynamic.TCPServer{
{
Address: "10.10.0.1:8000",
Port: "",
},
{
Address: "10.10.0.2:8000",
Port: "",
},
},
},
@ -137,11 +133,9 @@ func TestLoadIngressRouteTCPs(t *testing.T) {
Servers: []dynamic.TCPServer{
{
Address: "10.10.0.1:8000",
Port: "",
},
{
Address: "10.10.0.2:8000",
Port: "",
},
},
},
@ -179,11 +173,9 @@ func TestLoadIngressRouteTCPs(t *testing.T) {
Servers: []dynamic.TCPServer{
{
Address: "10.10.0.1:8000",
Port: "",
},
{
Address: "10.10.0.2:8000",
Port: "",
},
},
},
@ -423,11 +415,9 @@ func TestLoadIngressRouteTCPs(t *testing.T) {
Servers: []dynamic.TCPServer{
{
Address: "10.10.0.1:8000",
Port: "",
},
{
Address: "10.10.0.2:8000",
Port: "",
},
},
},
@ -467,11 +457,9 @@ func TestLoadIngressRouteTCPs(t *testing.T) {
Servers: []dynamic.TCPServer{
{
Address: "10.10.0.1:8000",
Port: "",
},
{
Address: "10.10.0.2:8000",
Port: "",
},
},
},
@ -532,11 +520,9 @@ func TestLoadIngressRouteTCPs(t *testing.T) {
Servers: []dynamic.TCPServer{
{
Address: "10.10.0.1:8000",
Port: "",
},
{
Address: "10.10.0.2:8000",
Port: "",
},
},
},
@ -595,11 +581,9 @@ func TestLoadIngressRouteTCPs(t *testing.T) {
Servers: []dynamic.TCPServer{
{
Address: "10.10.0.1:8000",
Port: "",
},
{
Address: "10.10.0.2:8000",
Port: "",
},
},
},
@ -657,11 +641,9 @@ func TestLoadIngressRouteTCPs(t *testing.T) {
Servers: []dynamic.TCPServer{
{
Address: "10.10.0.1:8000",
Port: "",
},
{
Address: "10.10.0.2:8000",
Port: "",
},
},
},
@ -708,11 +690,9 @@ func TestLoadIngressRouteTCPs(t *testing.T) {
Servers: []dynamic.TCPServer{
{
Address: "10.10.0.1:8000",
Port: "",
},
{
Address: "10.10.0.2:8000",
Port: "",
},
},
},
@ -759,11 +739,9 @@ func TestLoadIngressRouteTCPs(t *testing.T) {
Servers: []dynamic.TCPServer{
{
Address: "10.10.0.1:8000",
Port: "",
},
{
Address: "10.10.0.2:8000",
Port: "",
},
},
},
@ -801,11 +779,9 @@ func TestLoadIngressRouteTCPs(t *testing.T) {
Servers: []dynamic.TCPServer{
{
Address: "10.10.0.1:8000",
Port: "",
},
{
Address: "10.10.0.2:8000",
Port: "",
},
},
},
@ -844,11 +820,9 @@ func TestLoadIngressRouteTCPs(t *testing.T) {
Servers: []dynamic.TCPServer{
{
Address: "10.10.0.1:8000",
Port: "",
},
{
Address: "10.10.0.2:8000",
Port: "",
},
},
TerminationDelay: Int(500),
@ -893,11 +867,9 @@ func TestLoadIngressRouteTCPs(t *testing.T) {
Servers: []dynamic.TCPServer{
{
Address: "10.10.0.1:8000",
Port: "",
},
{
Address: "10.10.0.2:8000",
Port: "",
},
},
},
@ -938,7 +910,6 @@ func TestLoadIngressRouteTCPs(t *testing.T) {
Servers: []dynamic.TCPServer{
{
Address: "external.domain:8000",
Port: "",
},
},
},
@ -976,7 +947,6 @@ func TestLoadIngressRouteTCPs(t *testing.T) {
Servers: []dynamic.TCPServer{
{
Address: "external.domain:80",
Port: "",
},
},
},
@ -3036,11 +3006,9 @@ func TestLoadIngressRoutes(t *testing.T) {
Servers: []dynamic.TCPServer{
{
Address: "10.10.0.1:8000",
Port: "",
},
{
Address: "10.10.0.2:8000",
Port: "",
},
},
ProxyProtocol: &dynamic.ProxyProtocol{Version: 2},
@ -3393,11 +3361,9 @@ func TestLoadIngressRouteUDPs(t *testing.T) {
Servers: []dynamic.UDPServer{
{
Address: "10.10.0.1:8000",
Port: "",
},
{
Address: "10.10.0.2:8000",
Port: "",
},
},
},
@ -3438,11 +3404,9 @@ func TestLoadIngressRouteUDPs(t *testing.T) {
Servers: []dynamic.UDPServer{
{
Address: "10.10.0.1:8000",
Port: "",
},
{
Address: "10.10.0.2:8000",
Port: "",
},
},
},
@ -3452,11 +3416,9 @@ func TestLoadIngressRouteUDPs(t *testing.T) {
Servers: []dynamic.UDPServer{
{
Address: "10.10.0.3:8080",
Port: "",
},
{
Address: "10.10.0.4:8080",
Port: "",
},
},
},
@ -3622,6 +3584,104 @@ func TestLoadIngressRouteUDPs(t *testing.T) {
TLS: &dynamic.TLSConfiguration{},
},
},
{
desc: "Simple Ingress Route, with externalName service",
paths: []string{"udp/services.yml", "udp/with_externalname.yml"},
expected: &dynamic.Configuration{
UDP: &dynamic.UDPConfiguration{
Routers: map[string]*dynamic.UDPRouter{
"default-test.route-0": {
EntryPoints: []string{"foo"},
Service: "default-test.route-0",
},
},
Services: map[string]*dynamic.UDPService{
"default-test.route-0": {
LoadBalancer: &dynamic.UDPServersLoadBalancer{
Servers: []dynamic.UDPServer{
{
Address: "external.domain:8000",
},
},
},
},
},
},
TCP: &dynamic.TCPConfiguration{
Routers: map[string]*dynamic.TCPRouter{},
Services: map[string]*dynamic.TCPService{},
},
HTTP: &dynamic.HTTPConfiguration{
ServersTransports: map[string]*dynamic.ServersTransport{},
Routers: map[string]*dynamic.Router{},
Middlewares: map[string]*dynamic.Middleware{},
Services: map[string]*dynamic.Service{},
},
TLS: &dynamic.TLSConfiguration{},
},
},
{
desc: "Ingress Route, externalName service with port",
paths: []string{"udp/services.yml", "udp/with_externalname_with_port.yml"},
expected: &dynamic.Configuration{
UDP: &dynamic.UDPConfiguration{
Routers: map[string]*dynamic.UDPRouter{
"default-test.route-0": {
EntryPoints: []string{"foo"},
Service: "default-test.route-0",
},
},
Services: map[string]*dynamic.UDPService{
"default-test.route-0": {
LoadBalancer: &dynamic.UDPServersLoadBalancer{
Servers: []dynamic.UDPServer{
{
Address: "external.domain:80",
},
},
},
},
},
},
TCP: &dynamic.TCPConfiguration{
Routers: map[string]*dynamic.TCPRouter{},
Services: map[string]*dynamic.TCPService{},
},
HTTP: &dynamic.HTTPConfiguration{
ServersTransports: map[string]*dynamic.ServersTransport{},
Routers: map[string]*dynamic.Router{},
Middlewares: map[string]*dynamic.Middleware{},
Services: map[string]*dynamic.Service{},
},
TLS: &dynamic.TLSConfiguration{},
},
},
{
desc: "Ingress Route, externalName service without port",
paths: []string{"udp/services.yml", "udp/with_externalname_without_ports.yml"},
expected: &dynamic.Configuration{
UDP: &dynamic.UDPConfiguration{
Routers: map[string]*dynamic.UDPRouter{
"default-test.route-0": {
EntryPoints: []string{"foo"},
Service: "default-test.route-0",
},
},
Services: map[string]*dynamic.UDPService{},
},
TCP: &dynamic.TCPConfiguration{
Routers: map[string]*dynamic.TCPRouter{},
Services: map[string]*dynamic.TCPService{},
},
HTTP: &dynamic.HTTPConfiguration{
ServersTransports: map[string]*dynamic.ServersTransport{},
Routers: map[string]*dynamic.Router{},
Middlewares: map[string]*dynamic.Middleware{},
Services: map[string]*dynamic.Service{},
},
TLS: &dynamic.TLSConfiguration{},
},
},
{
desc: "Ingress class does not match",
paths: []string{"udp/services.yml", "udp/simple.yml"},
@ -4359,11 +4419,9 @@ func TestCrossNamespace(t *testing.T) {
Servers: []dynamic.TCPServer{
{
Address: "10.10.0.1:8000",
Port: "",
},
{
Address: "10.10.0.2:8000",
Port: "",
},
},
},
@ -4419,11 +4477,9 @@ func TestCrossNamespace(t *testing.T) {
Servers: []dynamic.UDPServer{
{
Address: "10.10.0.1:8000",
Port: "",
},
{
Address: "10.10.0.2:8000",
Port: "",
},
},
},

View file

@ -11,7 +11,6 @@ import (
"github.com/traefik/traefik/v2/pkg/log"
"github.com/traefik/traefik/v2/pkg/provider/kubernetes/crd/traefik/v1alpha1"
corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/util/intstr"
)
func (p *Provider) loadIngressRouteUDPConfiguration(ctx context.Context, client Client) *dynamic.UDPConfiguration {
@ -112,23 +111,15 @@ func loadUDPServers(client Client, namespace string, svc v1alpha1.ServiceUDP) ([
return nil, errors.New("service not found")
}
var portSpec *corev1.ServicePort
for _, p := range service.Spec.Ports {
p := p
if (svc.Port.Type == intstr.Int && svc.Port.IntVal == p.Port) || (svc.Port.Type == intstr.String && svc.Port.StrVal == p.Name) {
portSpec = &p
break
}
}
if portSpec == nil {
return nil, errors.New("service port not found")
svcPort, err := getServicePort(service, svc.Port)
if err != nil {
return nil, err
}
var servers []dynamic.UDPServer
if service.Spec.Type == corev1.ServiceTypeExternalName {
servers = append(servers, dynamic.UDPServer{
Address: net.JoinHostPort(service.Spec.ExternalName, strconv.Itoa(int(portSpec.Port))),
Address: net.JoinHostPort(service.Spec.ExternalName, strconv.Itoa(int(svcPort.Port))),
})
} else {
endpoints, endpointsExists, endpointsErr := client.GetEndpoints(namespace, svc.Name)
@ -147,7 +138,7 @@ func loadUDPServers(client Client, namespace string, svc v1alpha1.ServiceUDP) ([
var port int32
for _, subset := range endpoints.Subsets {
for _, p := range subset.Ports {
if portSpec.Name == p.Name {
if svcPort.Name == p.Name {
port = p.Port
break
}