Beginners Guide to Kubernetes Services

Anaïs Urlichs avatar
By Anaïs Urlichs


Learn about the different types of Kubernetes services and when and how to use them to deploy and access your application in a cluster.


Introduction to Kubernetes Services

Hello, there, and welcome to an introduction to Kubernetes services. In this introduction series, we will look at what Kubernetes services are, why we want to use them, when we use them, how we use them, and the different types of Kubernetes services that we have within Kubernetes.

What are Kubernetes Services?

Now, let's assume we have our Kubernetes application, and we want to deploy this application to our Kubernetes cluster. Now, this is our K3s Kubernetes cluster that's running on Siebel. So now, how do we get this application into our cluster? We will have several different nodes within the cluster depending on how many we specify upon spinning up the cluster, right? So we have those different nodes, and we can spin up pods on those different nodes.

Now, a pod will contain the running container of our application in most cases. So we can pack up this application into a docker container or any other container. It doesn't have to be docker, and then we can deploy this container onto our Kubernetes cluster. And it's running as a pod, which is one of the smallest resources within Kubernetes inside our Kubernetes cluster. So now, this part will, for example, live on node one.

Now, we have two different other nodes, such as node two and node three. And in a lot of cases, this part might die eventually. It might die at some point, and then Kubernetes will have to spin up a new pod to replace the dead pod here.

To make it even more resilient to breaking down, we want to have pods running on all of our nodes, at least multiple pods. So now, we could spin up those pods to spin up the application of our container image and run it in our Kubernetes cluster. We can use pure Kubernetes manifests. We could use templating solutions such as customize, and we can use home charts.

Now, there are other tools such as developer platforms that you could use instead that will take several different opinionated decisions to deploy your application and make it run into a Kubernetes cluster. But since we want to learn Kubernetes and how to do it ourselves, we're going to use Kubernetes manifests and pure Kubernetes manifests for the sake of this video.

Using Kubernetes services for deploying applications

When we go ahead and deploy our application, we want to make sure that it's accessible from the outside world. So we want to make sure that somebody, a user, can send a request to the running application within our cluster. And this is where Kubernetes services come in. So let's spin up our pod. So I have my example application. Let's open it up. As you can see, I have different folders here. I have a manifest folder with deployment and service that we will look at in a second, and then I have a pod.yaml file.

Now, I'm currently connected to my K3s Kubernetes cluster. So if I use the command "kubectl get nodes", I will see that I have three nodes, three K3s nodes running within my Kubernetes cluster. So within that cluster, I currently don't have anything running within my default namespace. So we'll go ahead and create a namespace, create a namespace example. Once the namespace is created, we can now deploy this pod resources.

Currently, this pod, referred to as my container image, holds a simple node application specified within this git repository and spins that up.

We can use the command "kubectl apply -f hack/pod.yaml -n example". So when I now go ahead and say I want to get everything within my namespace example, I can see that I have my one pod running here, and it's called node example pod, as specified here. The thing is, if I go ahead and delete this pod, right, delete pot, node example pod, and namespace example, and I carry again everything that's within my example namespace, the pod is gone. I've deleted it. I've created this one resource, and I've deleted it.

In Kubernetes, we can use something that's called reconciling or reconciliation. So we reconcile our resources. Does this work? So let's assume we spin up our pod. We want to have a new pod spun up within our Kubernetes cluster whenever this pod dies. So whenever this pod dies, we want to have a new pod spin up. Now, in this case, when I deleted the pod, it did not happen because the pod in itself is a finite resource. So it does not do this reconciliation.

So what we do in status is we use deployments, and deployments allow us to specify how many pods we want to run within our cluster. And then, once we provide Kubernetes with our deployment resource that specifies how many pods we want to run and what kind of application we run within our cluster, it will take care of the reconciliation. So when we delete any of the pods, or any of the pods dies, it will automatically spin up a new pod to replace the existing pod, and we can go ahead and have a look at such a deployment resource.

Now, there is a deployment resource. As we can see here, we have our API version with a kind equivalent to deployment. Then within the specification section, we specify our replica set. So we are saying we want to have a replica set of two pods running within our Kubernetes cluster. And this will make sure that at any point in time. Two pods are running. If one of the pods dies, a new one is spun up. The other pod is still existing there. So that means at any point in time, we should be able to access our application, at least one pod of our application.

Now, I've specified a selector, and the selector will become important in a little bit of time. And then, we have here our template and specification within the template that specifies our container image. We will get to the container pod in a second and the other specifications within.

What's important is that there's a label called node application or run node application, to be precise. So it's a key one of value to that label. So we want to deploy this specific deployment to our Kubernetes cluster since, right now, nothing is running. So we're going to go ahead, and use the command kubectl apply -f manifests/deploy.yaml -n example. We always want to have everything within a namespace so we can easily clean up our cluster afterwards. So we've created that.

Now, we want to go ahead and check out what's going to be spun up within our example namespace. So use the command kubectl get all -n example. As you can see, we have here the deployment resource that we just specified within the YAML. And then we have here our replica set. The replica set specifies the number of desired pods.

As you can see, we have two pods running right now within our cluster. And as you can see right now, they don't have a nice, readable name as we had before. So instead, the nice, readable name is within our deployment. So when I delete this pod now using kubectl delete pod/[POD_NAME] -n example, we can see that a different pod has been spun up. We can verify that by using kubectl get all -n example. So while the second pod is still the same, you can see that the first one had died and the new one had been spun up.

So, right now, we could port-forward to one of these pods to access our application. So I could say, "Okay, I want to access this pod right now." So I can use the command kubectl port-forward pod/[POD_NAME] -n example 8080:8080. So now I will be able to access my application through localhost 8080, saying "hello world" in my face. So the thing is now, right now, I'm accessing my application through localhost through the specific pod. Now, what happens if the pod dies? If the pod dies, I won't automatically reconnect to another pod.

Kubernetes service and port-forwarding

So let's say there are two pods. Right now, I'm accessing through port-forwarding a specific pod. If this pod dies, the link here will be broken, and I won't be able to access this pod anymore, which is where Kubernetes services come in. So instead of port-forwarding, we will deploy a Kubernetes service, which is all within our deployment. So there is our deployment, and a Kubernetes service will specify how we can connect to our deployments. And in that case, if one of these pods dies and a new pod has spun up, I will be automatically routed and forwarded to the other pod. If that pod dies, and then, at this point, the other one has hopefully spun up again, it will route me back to the other pod that's alive.

If both pods are alive, I will be routed to any pods or the pod with the least traffic, but that's an additional configuration that we don't have in place right now. So a Kubernetes service allows us to connect dynamically to the different pods. Now, let's assume we have our front end, and the front end has three different pods. Then we have our backends. The backend might also run several different pods, as in this case. So all the same pods, and then we have our database.

Now, these are all different pods with different endpoints. So once we spin up a new pod, it's assigned an endpoint, a pod endpoint, like an IP address of 0.1, and then the next one will have 0.2. So they all have different endpoints.

Now, if the pod dies, a new pod with a new endpoint is spun up, and this endpoint will be different. So we cannot assign the backend to talk to this endpoint directly because it will always change. That wouldn't work. It would break our flow. It would be tightly coupled between those different pods and endpoints, and we do not want that.

So what we do instead is spin up services between those different sections of our application, let's say. So we have here one service, and then we have here service for our front end, we have a service for our database. In this case, the service will be assigned a specific endpoint or a label to it. So we can tell the back end that this is the service that always has to communicate with the front end. And this is the service that the backend always has to communicate with the database. So we don't care anymore about the specific pods that are running. But instead, we care about the different services attached to the deployment.

So, there are separate deployments for the front-end, backend, and database in this case. So usually, for each deployment, you will have a specific service. This service allows you to communicate with your deployment, different pods within your deployment that are independent of the endpoints attached to your specific pods or independent of which node they're running in, and so on.

Types of Kubernetes service

Now, we have three different types of Kubernetes services, and then there's a fourth type that's highly useful. So we will take a look at it as well. The fourth type is ClusterIP. Then we have NodePort, we have LoadBalancer, and we have something called a Headless Service, and we are going to take a look at that in a second.

So as you can see, we can spin up a service that dynamically routes the traffic to our deployments. So going back to our running application, if the port-forwarded pod dies, we will lose access to it. So let's spin up our service and connect directly to the service and show you how I can delete the pods by still being connected to the service.

Hence, we will use the kubectl apply -f manifests/service.yaml -n example command. Let's apply our service in our namespace example. Then we can use kubectl get all -n example. So as you can see now, we have a new resource known as the service and a node application. Now, in this case, I specified it to be NodePort. The default will be ClusterIP. In this case, it doesn't matter. We will take a look at that in a second. Now, as you can see, it's accessible through port 3000.

So I can use the command kubectl port-forward service/node-application -n example 3000:3000. So I can specify port 3000, and this should forward my service. In this case, my application is running at host 8080, but I'm accessing it through localhost 3000 as specified within the service. So now, we can go ahead. If I open port 8080 right now, it will not open; however, if I open port 3000, if I go ahead and I will see hello world on display again.

So now that this is running, I can open up a new terminal, and I'm still connected to the same cluster. And then, I can go ahead and use the command kubectl get all -n example to see all the pods, services, and deployments, and I can delete a pod. So what we will see is that a new pod is going to be spun up, and we still have access at any point in time through the service through our application. So to do that, we will use the command kubectl delete pod/[POD_NAME] -n example.

Now, that pod is deleting right now. We can have another look at how it's deleting, but as you can see, it already spun up a new one as quickly as that is. So at any point in time, we could access our application through localhost 3000. It should be. Let's reconnect and see if we can access it and use our application again.


Now, using port-forward is always a little bit clunky. So, later on, we will see how we can use Ingress or service mesh to establish communication between our different services. So as we've seen here, as our application grows, it will get more complex, meaning we will have difficulty routing the traffic from the front end to the database and ensuring that the connection between all the different pods is insured.

So Ingress can allow us to access our application securely from the outside world because this is all still within our Kubernetes cluster. It is all packed up within our K3s cluster, and we will want to access it from the outside.

At the same time, a service mesh will then ensure that we will be able to access the difference and route the traffic between those different endpoints within our cluster. In the following video, we will take a look at clusterIP, NodePort, LoadBalancer, and Headless Services.

Don't stop now, check out your next lesson