Loadbalancing/Redirecting from Kubernetes NodePort Services

6/8/2020

i got a bare metal cluster with a few nodeport deployments of my services (http and https). I would like to access them from a single url like myservices.local with (sub)paths.

config could be sth like the following (pseudo code):

/app1
http://10.100.22.55:30322
http://10.100.22.56:30322
# browser access: myservices.local/app1
/app2
https://10.100.22.55:31432
https://10.100.22.56:31432
# browser access: myservices.local/app2
/...

I tried a few things with haproxy and nginx but nothing really worked (for inexperienced in this webserver/lb things kinda confusing syntax/ config style in my opinion).

what is the easiest solution for a case like this?

-- Patrick Hermann
haproxy
kubernetes
load-balancing
nginx
webserver

1 Answer

6/9/2020

The easiest and most used way is to use a NGINX Ingress. The NGINX Ingress is built around the Kubernetes Ingress resource, using a ConfigMap to store the NGINX configuration.

In the documentation we can read:

Ingress exposes HTTP and HTTPS routes from outside the cluster to services within the cluster. Traffic routing is controlled by rules defined on the Ingress resource.

    internet
        |    
   [ Ingress ]    
   --|-----|--    
   [ Services ]

An Ingress may be configured to give Services externally-reachable URLs, load balance traffic, terminate SSL / TLS, and offer name based virtual hosting. An Ingress controller is responsible for fulfilling the Ingress, usually with a load balancer, though it may also configure your edge router or additional frontends to help handle the traffic.

An Ingress does not expose arbitrary ports or protocols. Exposing services other than HTTP and HTTPS to the internet typically uses a service of type Service.Type=NodePort or Service.Type=LoadBalancer.

This is exactly what you want to achieve.

The first thing you need to do is to install the NGINX Ingress Controller in your cluster. You can follow the official Installation Guide.

A ingress is always going to point to a Service. So you need to have a Deployment, a Service and a NGINX Ingress.

Here is an example of an application similar to your example.

apiVersion: apps/v1
kind: Deployment
metadata:
  labels:
      app: app1
  name: app1
spec:
  replicas: 1
  selector:
    matchLabels:
      app: app1
  strategy:
    type: Recreate
  template:
    metadata:
      labels:
        app: app1
    spec:
      containers:
      - name: app1
        image: nginx
        imagePullPolicy: Always
        ports:
        - containerPort: 5000
---
apiVersion: apps/v1
kind: Deployment
metadata:
  labels:
      app: app2
  name: app2
spec:
  replicas: 1
  selector:
    matchLabels:
      app: app2
  strategy:
    type: Recreate
  template:
    metadata:
      labels:
        app: app2
    spec:
      containers:
      - name: app2
        image: nginx
        imagePullPolicy: Always
        ports:
        - containerPort: 5001
---          
apiVersion: v1
kind: Service
metadata:
  name: app1
  labels:
    app: app1
spec:
  type: ClusterIP
  ports:
  - port: 5000
    protocol: TCP
    targetPort: 5000
  selector:
    app: app1
---
apiVersion: v1
kind: Service
metadata:
  name: app2
  labels:
    app: app2
spec:
  type: ClusterIP
  ports:
  - port: 5001
    protocol: TCP
    targetPort: 5001
  selector:
    app: app2    
---
apiVersion: networking.k8s.io/v1beta1
kind: Ingress #ingress resource
metadata:
  name: myservices
  labels:
    app: myservices
spec:
  rules:
  - host: myservices.local #only match connections to myservices.local. 
    http:
      paths:
      - path: /app1
        backend:
          serviceName: app1
          servicePort: 5000
      - path: /app2
        backend:
          serviceName: app2
          servicePort: 5001          
-- Mark Watney
Source: StackOverflow