Table of Contents
Kubernetes has revolutionized the way applications are deployed and scaled, and Kubernetes services are a fundamental component of its networking model. Kubernetes services provide a stable endpoint for communication by abstracting away the details of pod IP addresses. Every service gets assigned an IP address and DNS name, which remain consistent regardless of the lifecycle changes of the underlying pods. This ensures uninterrupted access to the service. Kubernetes offers different service types, each tailored to specific needs, such as facilitating internal communication, exposing an application to external users, or linking to an external service. Selecting the appropriate service type is pivotal for application performance and resilience, as it can influence factors like latency, availability, and even cost.
In this guide, you'll learn about the mechanics, types, and best practices of Kubernetes services. You'll explore the different service types available in Kubernetes, including ClusterIP, NodePort, LoadBalancer, and ExternalName, and learn the scenarios for which each type is best suited. You'll also explore the criteria for selecting the appropriate service type, taking into account factors like traffic source and infrastructure. By the end of this guide, you'll have a solid understanding of Kubernetes services and be equipped to make informed decisions about which service type to use for your applications.
The Mechanics of Kubernetes Services
The need for Kubernetes services arises from the dynamic nature of pods and the instability that pods introduce to the network. Pods are ephemeral, meaning they can be created, destroyed, or replaced at any time. This can lead to frequent changes in IP addresses and DNS names, making it difficult to establish stable communication channels. Without a mechanism to abstract away these details, developers would have to manually manage the IP addresses of each pod and perform complex client-side load balancing operations. Kubernetes services act as a load balancer over a set of pods that work together, such as one tier of a multitiered application, and can be accessed as a single unit. They provide a consistent IP address and DNS name, allowing for load balancing and service discovery among a set of pods.
To illustrate the need for services, create a simple deployment of two Nginx pods using the following command:
kubectl create deployment nginx --image=nginx:stable-alpine-slim --replicas=2
Now retrieve the IP addresses of the pods using the following command:
kubectl get pods -l=app=nginx -o wide
Execute the following command to create a client application pod:
kubectl run myclient --image=curlimages/curl -i --tty -- sh
Each pod is given a DNS A record in the format of <pod IP address>.<namespace>.pod.cluster.local
. To access one of the Nginx pods from the pod shell, run the following command, replacing the <pod IP address>
placeholder with the IP address you recorded from the previous command and replacing the periods (.
) in the IP address with hyphens (-
):
curl <pod IP address>.default.pod.cluster.local
The screenshot below displays the result of executing the command:
Interacting with the pods requires the client to maintain the pod IP addresses. To illustrate how this can be a challenge, delete the pods of the deployment using the following command:
kubectl delete pods -l app=nginx
After some time, Kubernetes will recreate the pods with new IP addresses, as illustrated in the following screenshot:
In Kubernetes, a service is a construct designed to create a consistent endpoint for communication between clients and the underlying pods. This is achieved using appropriate IPTABLES
to direct traffic from the client to the pod. Essentially, a service acts as a grouping object that defines a logical set of pods and a policy for accessing those pods. This means that pods can be logically grouped as a cohesive unit, ensuring that any pod connected to a service will respond to requests in a similar manner. The diagram below illustrates how services aggregate pods in a Kubernetes cluster:
For a non-headless service, Kubernetes creates a DNS A record in the format <name of service>.<namespace>.svc.cluster.local
, which the client can use to access one of the pods exposed by the service. If the service is headless, resolving the DNS record returns the IP addresses of all the pods exposed by the service, which the client can use to load balance the traffic. Discussing headless services is beyond the scope of this guide. For more information, refer to the official documentation.
The final example in this section demonstrates how to create a service for the Nginx deployment, which you can use to access the Nginx pods through the URL of the service rather than the IP address of the pod. Execute the following command to create a service:
kubectl apply -f - <<EOF
apiVersion: v1
kind: Service
metadata:
name: nginx-svc
spec:
selector:
app: nginx
ports:
- protocol: TCP
port: 80
EOF
After deployment, the service automatically acts as a proxy for traffic directed towards the pods. To confirm this, simply run the following command from your client pod:
kubectl exec -i --tty myclient -- sh
curl nginx-svc.default.svc.cluster.local
The following screenshot illustrates the output of the command:
Service Types in Kubernetes
When working with Kubernetes, it's essential to understand that not all services are the same. Kubernetes provides various types of services that cater to specific needs. These services are designed to facilitate internal communication, make an application accessible to external users, or even connect to an external service. It's essential to choose the right Kubernetes service for your use case.
This section examines the different service types by implementing them on the same deployment of two Nginx pods. You'll also explore the scenarios that each type is best suited for.
ClusterIP
The foundational service type, ClusterIP, is the default choice for intra-cluster communication. It exposes the service on an internal IP set cluster-wide, ensuring it's reachable only within the cluster. If more than one pod is behind the service, the ClusterIP service will provide a load balancing mechanism using the round-robin algorithm.
The ClusterIP service uses a router component known as an ingress for external communication. An ingress is an intelligent router that operates within the cluster and can receive external traffic. In Kubernetes, an ingress directs traffic from specific web addresses, like https://a.hostname.com
, to the right service, such as sending it to service A or directing traffic from https://hostname.com/b
to service B. As a result, a single ingress can expose multiple services on the same IP address. The implementation of ingress is accomplished through the ingress controller in Kubernetes.
Use the following specification to create a ClusterIP service for the Nginx deployment:
apiVersion: v1
kind: Service
metadata:
name: clusterip-svc
spec:
selector:
app: nginx
ports:
- protocol: TCP
port: 8081
targetPort: 80
You can access the service through the client pod using the following command:
curl clusterip-svc.default.svc.cluster.local:8081
The following screenshot illustrates the output of the command:
NodePort
NodePort is designed for scenarios where external access to the service is required. It exposes the service on a static port on each node. After provisioning the service, you can reach the pods it exposes by using the worker node's IP address and the port of the NodePort service in the following format:
<WORKER_NODE_IP_ADDRESS>:<PORT_DECLARED_IN_NODE_PORT_SVC>
Use the following specification to create a NodePort service for the Nginx deployment:
apiVersion: v1
kind: Service
metadata:
name: nodeport-svc
spec:
type: NodePort
selector:
app: nginx
ports:
- protocol: TCP
nodePort: 31234
port: 80
targetPort: 80
NodePort services have three ports to be specified in YAML: nodePort
, port
, and targetPort
. The first one is the port on the worker node to access the service. The second one is the service's port itself, and the third is the targeted pods' port. The NodePort setting is limited to a specific port range, which, on a default Kubernetes installation, can be a port from the 30000–32767 range.
To access the service, you'll first get the IP address of one of the worker nodes using the following command:
kubectl get nodes -o wide
The following screenshot illustrates the output of the command:
You can now access the service through the client pod using the following command:
curl <WORKER_NODE_IP_ADDRESS>:31234
The following screenshot illustrates the output of the command:
LoadBalancer
LoadBalancer is the go-to approach for applications that demand high availability. It provisions an external load balancer, directing external traffic to the service. The LoadBalancer service type is only available when Kubernetes is deployed on a supported cloud provider such as AWS, GCP, Azure, or OpenStack. It is not available for on-premise deployments.
Use the following specification to create a LoadBalancer service for the Nginx deployment:
apiVersion: v1
kind: Service
metadata:
name: loadbalancer-svc
spec:
type: LoadBalancer
selector:
app: nginx
ports:
- protocol: TCP
port: 8080
targetPort: 80
In the specification, the port
field is the port on which the service is exposed, and the targetPort
field is the port of the pods that the service is exposing.
ExternalName
Being a unique service type, ExternalName doesn't route traffic internally. Instead, it returns a CNAME record pointing to a specified external service.
You can use the service to link to an external service that is not part of the Kubernetes cluster. For example, you can use it to link to a web service hosted on a cloud provider.
Use the following specification to create an ExternalName service using the Postman Echo service, which is a free service that allows you to test your HTTP client against a variety of responses:
apiVersion: v1
kind: Service
metadata:
name: externalname-svc
spec:
type: ExternalName
externalName: postman-echo.com
Deploying the service will direct traffic sent to the service to the Postman Echo service. You can access the service through the client pod using the following command:
curl https://externalname-svc.default.svc.cluster.local/get?greeting=hello-world -k
The command will send an HTTP request to the Postman Echo service and ignore the SSL certificate verification. The following screenshot illustrates the output of the command:
Criteria for Kubernetes Service Selection
In the Kubernetes ecosystem, selecting the appropriate service type is more than just a technical decision; it's pivotal for application performance and resilience. The choice can influence factors like latency, availability, and even cost. Here's an overview of the criteria that should guide this decision:
- Traffic source: Is the traffic primarily internal or external? ClusterIP suffices for the former, while NodePort or LoadBalancer cater to the latter.
- Infrastructure: If you're on the cloud, LoadBalancer becomes an attractive option due to its seamless integration with cloud providers.
- Availability and resilience: High-traffic applications demanding fault tolerance benefit from LoadBalancer, which distributes incoming traffic across pods.
Apart from general considerations, each service type has its own characteristics that make it suitable for specific scenarios:
- LoadBalancer service: Each service that you expose through the load balancer gets its own address, so this option can get quite expensive if you have a high number of services running in your cluster.
- NodePort service: A NodePort service is generally used with an external LoadBalancer so that the client doesn't need to know about individual node IPs. However, since a limited number of ports are available on nodes, this option is not suitable for high-traffic applications.
- ClusterIP service: You can use an ingress to expose ClusterIP services to external clients. An ingress provides additional features, such as TLS termination and Server Name Identification (SNI), so that they need not be implemented by individual services. Because a single IP address is used by the ingress, you pay only for a single IP address when integrating an ingress with LoadBalancer on the cloud, making it the most cost-effective option for internet-facing services.
Best Practices for Kubernetes Services
While Kubernetes services are powerful, they require careful management to ensure they deliver optimal performance. From security to monitoring, there are several facets to consider. Here's a compilation of best practices, distilled from real-world deployments and expert insights:
- Security: Minimize exposure. Default to ClusterIP and expose services externally only when imperative. Implement network policies to fine-tune traffic between pods.
- Labeling and selectors: Consistency in labeling is not just good practice; it's essential. Ensure that services use precise selectors, optimizing traffic routing.
- Configuration: Tailor configurations to application needs. This includes setting session affinity and optimizing service annotations for cloud-specific features.
- Monitoring: In Kubernetes, monitoring is key to ensuring optimal performance. Use tools like Prometheus for monitoring and Fluentd for logging to ensure optimal performance and quick troubleshooting.
You can find detailed guides pertaining to Kubernetes cluster administration in the Kubernetes documentation.
Conclusion
Kubernetes services are a fundamental component of the Kubernetes networking model. They provide a stable endpoint for communication by abstracting away the details of pod IP addresses. Kubernetes offers different service types, each tailored to specific needs, such as facilitating internal communication, exposing an application to external users, or linking to an external service. Selecting the appropriate service type is pivotal for application performance and resilience, as it can influence factors like latency, availability, and even cost. For any Kubernetes expert, a deep-rooted understanding of its services is indispensable for successful deployments.