This blog post is a follow up on my previous post where we talked about deploying a Python app with nginx reverse proxy using docker.

So lets line up all the things that we're going to touch in this post:

Now that we have set the tone, lets go in brief about what the above this are about that what role they play.

We will then actually go over each and code them and implement the deployment in kubernetes.

What is Ingress?

From the kubernetes documentation Ingress is defined as:

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.

Also this diagram seems to provide a bit more details so I'll just leave it here:



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

All though IMO the above diagram is a bit more simplified. Hence I came up with this arch. diagram explaining the above arrangement in a bit more detail.

Another way to look at Ingress is that its a Layer 7 load balancer. As layer 7 LB is application aware, it can determine where to send the traffic depending on application state.



In contrast, a simple load balancer like Google cloud load balancer or AWS' ELB are Layer 4 LB. You would use it to expose single app or service to the outside world. It would balance the load based

on destination IP address, protocol and port.

What are services?

Without going into much detail about what services are, k8s docs describe services as:

An abstract way to expose an application running on a set of Pods as a network service.

And Deployments?

Deployment are merely high level abstractions over Pods (which in themselves are abstractions over your containers). Well that's only half correct.

In fact ReplicaSets are a direct abstractions on your Pods and Deployments are an abstraction over ReplicaSets.

Again from the docs:

You describe a desired state in a Deployment, and the Deployment Controller changes the actual state to the desired state at a controlled rate. You can define Deployments to create new ReplicaSets, or to remove existing Deployments and adopt all their resources with new Deployments.

So lets dive right in

Let's first see how our Go code is going to look like:



package main import ( "fmt" "net/http" "github.com/gin-gonic/gin" ) func greet ( c * gin . Context ) { name := c . Param ( "name" ) c . String ( http . StatusOK , fmt . Sprintf ( "Hello %s!" , name )) } func stranger ( c * gin . Context ) { c . String ( http . StatusOK , "Hello stranger!" ) } func main () { router := gin . Default () router . GET ( "/hello/:name" , greet ) router . GET ( "/hello/" , stranger ) router . GET ( "/" , func ( c * gin . Context ) { c . String ( http . StatusOK , "" ) }) router . Run ( ":8000" ) }

This is a very trivial api, we add three endpoints. The root url is basically also a health check and hence must return a 200 OK http status.

Once we have this go code built into a container – which BTW is not the subject of this post and I'm skipping it out, we can start concentrating on more about how to deploy

this service on kubernetes.

I'm going to deploy this to a k8s cluster on Google Cloud, but the code should work seamlessly on any other k8s deployments as well.

Also, I've created a container with the above code and made it available on docker hub publicly. The container can be found here – https://hub.docker.com/repository/docker/ishankhare07/ingress-test

You can pull down the image using docker pull ishankhare07/ingress-test:0.0.3

The cluster

So lets get down to business, I'm assuming you have your cluster running and kubectl command configured to connect to the cluster. We'll be setting up an

ingress resource

ingress controller

While there are many plug-and-play options for ingress available today written over the ingress api , the simplest and most straight forward one is nginx ingress.

I've given the link to official docs which details the exact installation steps for setting it up. But these steps vary slightly depending on the cloud provider, k8s version etc.

Hence the best and easiest way to actually setup nginx ingress is through helm

WTF is helm?

Helm is a package manager for k8s. Consider it like apt-get or yum but for kubernetes. Just tell helm the package – which are called charts in helm terminology to be installed, and helm will do it for you. The good this about helm is – its sort of an automation tool over kubernetes. So if you have something that requires multiple steps to setup, helm will do it for you – like run a few deployments, expose it at a port through a service, mount a volume from a persistent disk etc. More on helm some other day. Right now go ahead and install helm on your cluster.

And yes you read that right, "install on cluster". You see helm is a 2 part thing

The helm cli client that is installed on your dev machine. The tiller controllers that are spun as pod controllers inside your cluster

The way it works is you tell helm what to install using the helm cli. The cli then communicates this to the tiller controllers running on your k8s cluster.

Those controllers fetch and spin the required pods etc. in the cluster for you. In a way we are using kubernetes provided primitives to automate and extend kubernetes itself.

Once you've helm installed and setup, we can now proceed with deploying our application.

Deploying our go backend

We want out go backend to be available on port 8000 because that is what we exposed in our code.

So lets write a greeter.yaml file with the following content



kind : Deployment apiVersion : apps/v1 metadata : name : go-greeter labels : app : greeter spec : replicas : 3 selector : matchLabels : app : greeter template : metadata : labels : app : greeter spec : containers : - name : greeter image : ishankhare07/ingress-test:0.0.3 command : [ " /go/bin/ingress-test" ] ports : - containerPort : 8000 --- kind : Service apiVersion : v1 metadata : name : greeter-service spec : selector : app : greeter type : NodePort ports : - port : 8000

We define a Deployment – with a replicaset of 3 pods. Each pod's port 8000 is open only accessible from inside the cluster. Next we expose a service that funnels all the incoming data to the 3 replicas of our containers in a round robin fashion.

Lets deploy this to our cluster first:

kubectl apply -f greeter.yaml

Lets see the deployment:



$ kubectl get deploy NAME READY UP-TO-DATE AVAILABLE AGE go-greeter 3/3 3 3 13d

Also we deployed a service, lets check that too:



$ kubectl get svc NAME TYPE CLUSTER-IP EXTERNAL-IP PORT ( S ) AGE greeter-service NodePort 10.36.7.174 <none> 8000:31269/TCP 13d

So our Deployment is up and running and is exposed inside the cluster through a service. Next we need to connect it to the outside world through Ingress.

Installing nginx ingress through helm

Assuming helm is installed and setup with RBAC roles applied, we can install nginx-ingress chart with the following command:

helm install --name nginx-ingress stable/nginx-ingress --set rbac.create=true --set controller.publishService.enabled=true

If all goes well, this should give us the following output:



NAME TYPE CLUSTER-IP EXTERNAL-IP PORT ( S ) AGE nginx-ingress-controller LoadBalancer 10.7.248.226 pending 80:30890/TCP,443:30258/TCP 1s nginx-ingress-default-backend ClusterIP 10.7.245.75 none 80/TCP 1s

So we have our ingress controller and the default backend setup and running. Now all that's left is telling this nginx-ingress-controller how and where to redirect incoming traffic.

Let's write a new file ingress.yaml for it:



apiVersion : extensions/v1beta1 kind : Ingress metadata : name : my-ingress annotations : kubernetes.io/ingress.class : " nginx" ingress.kubernetes.io/rewrite-target : / spec : rules : - http : paths : - path : /hello/ backend : serviceName : greeter-service servicePort : 8000

Let's deploy this now:

kubectl apply -f ingress.yaml

This will create and ingress and a loadbalancer service and attach a public IP address, which can be seen by:



$ kubectl get ingress NAME HOSTS ADDRESS PORTS AGE my-ingress * XXX.XXX.XXX.XXX 80 11d

Now, if we visit the above IP Address we'll hit the default backend for 404 – since / url is not defined in our ingress.



Now on hitting the first path /hello , this is what we get:



Also we have another internal API endpoint in our greeter service, lets hit that too – /hello/ishan , what we get:



Now that we are able to direct traffic to different deployments using the ingress, let's deploy a new pod and try to redirect some traffic there. We'll simply use hashicorp/http-echo

to use as a second service. Let's write a manifest for this:

apple.yaml



kind : Pod apiVersion : v1 metadata : name : apple-app labels : app : apple spec : containers : - name : apple-app image : hashicorp/http-echo args : - " -text=apple" --- kind : Service apiVersion : v1 metadata : name : apple-service spec : selector : app : apple type : NodePort ports : - port : 5678

Let's deploy this:

kubectl apply -f apple.yaml

Now that we have this pod running and exposed through the service, we would want to update the ingress file with these changes.



apiVersion : extensions/v1beta1 kind : Ingress metadata : name : local-ingress annotations : kubernetes.io/ingress.class : " nginx" ingress.kubernetes.io/rewrite-target : / spec : rules : - http : paths : - path : /apple backend : serviceName : apple-service servicePort : 5678 - path : /hello/ backend : serviceName : greeter-service servicePort : 8000

Re-configure the ingress by running:



$ kubectl apply -f ingress.yaml ingress.extensions/my-ingress configured

We can test it by going to the path /apple



And that's it! I'm sure we can all appreciate the loosely coupled nature of kubernetes and the scalability it provides. In the next blog post we'll look into deploying a gRPC endpoint written in Go deployed on this same k8s cluster behind the ingress. We'll also try to connect it to via a python client.

This post was originally published on my blog at ishankhare.com