In this post I will show how to expose multiple microservices from a Kubernetes cluster. This specific example uses Asp.Net Core microservices, but the approach will work for any microservice technology.

Load Balancer

In a previous article I showed how to deploy multiple internal microservices behind a single public service using a Kubernetes LoadBalancer service. This setup works well for many scenarios, but using LoadBalancers is less optimal in cases where you need to expose multiple external endpoints. Technical you could expose your services through multiple LoadBalancer services, but the downside is that each service gets a separate IP address. This is because LoadBalancers are limited to a single IP address in front of a single service.

Ingress

Instead of multiple LoadBalancer services it’s recommended to create a Kubernetes Ingress. The benefit of an Ingress is that you can expose a single entry point and route to pre-defined routes via an Ingress controller. This means we can expose a single IP address and just route traffic to specific microservices based on url patterns. There are many different Ingress controllers to chose from, but in my example I am using the nginx controller.

The nginx controller doesn’t ship with Kubernetes by default, but it’s easy to install it separately as a helm chart.

Here are the steps:
helm init helm install stable/nginx-ingress --name nginx-ingress --namespace default --set controller.service.loadBalancerIP=some-public-ip --set controller.scope.enabled=true --set controller.scope.namespace="default"

Defining Services

In the code listings below I have defines yaml for two microservices

Friends Services
apiVersion: apps/v1 kind: Deployment metadata:   name: friends spec:   selector:     matchLabels:       app: friends   replicas: 1   template:     metadata:       labels:         app: friends     spec:       containers:         - name: friends           image: "friend-service:v6"           ports:             - name: http               containerPort: 80 --- apiVersion: v1 kind: Service metadata:   name: friends-service spec:   selector:     app: friends   ports:   - protocol: TCP     port: 80     name: http     targetPort: 80   type: ClusterIP
Greetings Service
apiVersion: apps/v1 kind: Deployment metadata:   name: greetings spec:   selector:     matchLabels:       app: greetings   replicas: 1   template:     metadata:       labels:         app: greetings     spec:       containers:         - name: greetings           image: "greeting-service:v6"           ports:             - name: http               containerPort: 80 --- apiVersion: v1 kind: Service metadata:   name: greetings-service spec:   selector:     app: greetings   ports:   - protocol: TCP     name: http     targetPort: 80     port: 80   type: ClusterIP

Ingress

As you can see from the previous yml, these services are deployed with ClusterIPs, which means the services aren’t reachable from outside the cluster.

To expose the services we have to add the Ingress yml with routing definitions as seen below:

apiVersion: extensions/v1beta1 kind: Ingress metadata:   name: api-ingress   annotations:     nginx.ingress.kubernetes.io/ssl-redirect: "false"     kubernetes.io/ingress.class: nginx spec:   rules:   - http:       paths:         - path: /api/Greetings           backend:             serviceName: greetings-service             servicePort: 80         - path: /api/Friends           backend:             serviceName: friends-service             servicePort: 80

Now, if we navigate to /api/Greetings or /api/Friends we will be routed to the underlying service based on the above route definitions. Notice how the service names in the Ingress yml map to the metadata names in the corresponding service yml.

Future Enhancements

A nice additions to this would be to add support for https.

Demo

The demo code can be downloaded from my Github project.