Kubernetes 101 – Services
This is the third in a series of blog posts that will explain the different components of Kubernetes. Primarily because if I can explain it here, I’ll have learned it quite well myself.
What is a Service?
So you’ve deployed a pod, now how do you access it? How do other pods access it? What about if you’ve got multiple pods running the same application, how do you access all these pods? Remembering of course that pods are ephemeral and get a new IP address every time they start, how can you connect to an address that might have changed since last time you connected to it?
This is where services come in.
A Kubernetes service resource creates a static entrypoint that is backed by the appropriate pods. Additionally a record will be created in the cluster DNS, enabling it to be found by name.
Types of Service
There are different types of services, which you’d use depends on the application requirements and the infrastructure configuration.
- Cluster IP
The differences between these are described below.
This is the default if no type is specified when creating the service. It enables a DNS discoverable, static, endpoint for clients internal to the cluster to use. If you’ve got pods that need to talk to other pods, this is an option.
A NodePort gives you external access to your service. It does this by mapping a port (typically a value in the 30000s) on the Kubernetes node to the Service resource in addition to the internal cluster IP. The allocated port number is used across all nodes, so it doesn’t matter which node you hit, you’ll get the same service.
The next level up from a NodePort, and only available if you’ve got a configured cloud provider within your cluster. In this case, when you create the service resource a load balancer on your cloud provider is also spun up with the assoicated External IP. This will then forward the traffic onto the NodePort.
If a DNS lookup is performed against a service’s hostname, it will return it’s configured Cluster IP. Sometimes you may have a scenario where all the pods running that service need to be discoverable. If you set
ClusterIP: None within the service configuration it will have no ClusterIP. Then when a DNS lookup is performed it will return a record for each pod listed as an endpoint for that service. Enabling clients to retrieve the IP details for all the pods and connect to them all (if desired).
These all sound great, but what if you were limited by the number of external IP addresses you have available?
To resolve this you can install an Ingress controller into your Kubernetes cluster. Once that’s installed, you can create Ingress resources which will forward traffic to the appropriate upstream service based on the headers of the HTTP request. Typically this is the web address the request has come in on, such as www.bar.com or www.foo.com. This could also be the path of the address, such as www.bar.com/foo or www.bar.com/ting.
How does a Kubernetes understand where to send the traffic once it has hit the service resource?
There is another construct called an endpoint. An endpoint is a just that, the endpoint backing the service. This is typically an IP address & a port number. There is one of these for each pod associated with the service.
I’m sure you can imagine creating all these endpoints and trying to keep up the pods scaling up and down isn’t the best idea (although sometimes it might be).
Luckily, Kubernetes will create these endpoints by virtue of a label selector. When creating the service, you can specify the label selector. Kubernetes wlll then create an endpoint for each pod that has that label.
Doesn’t that makes things easier?
As soon as a pod has started, a service will start forwarding traffic to it. While this is ok some of the time, there are times when it’s not. When the application takes a few seconds to warm up for example.
In this instance you can add a readiness probe to a service. This will run a command to determine if the pod is ready to receive connections. This can be one of three types of probe:
- An Exec probe, where a process is executed within the pod.
- An HTTP GET probe, where an HTTP GET request is sent to the container.
- A TCP Socker probe, where a connection is opened on a specified TCP port.
Each of these has it’s own use case which will be specific to each application that it is using.