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.

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

Here are the steps:

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.