Kubernetes Service: A Deeper Look
Introduction
A Service in Kubernetes is an abstraction that provides a network interface to access a set of Pods. These Pods can either be a component of an application or the whole application itself.
The Service can expose the set of Pods:
- Internally within the cluster to enable inter-communication between nodes and Pods.
- Externally to accept incoming traffic from outside the cluster.
Different components of an application may need to communicate with each other. These components live on Pods that have an internal IP address assigned to them. In Kubernetes, Pods are not a permanent entity. Pods may fail and get replaced by new ones with different IP addresses. To keep the application functioning, the components of an application that rely on these Pods have to be updated with their new IP addresses. Manually updating the IP address in these components is not practical. Instead, you can use a network entity with a fixed IP address, such as a Service that acts as a proxy for non-permanent Pods. Furthermore, the application that serves requests to external clients, like an Nginx web server, has to be exposed as a single endpoint, even if the cluster has multiple instances of that application.
Prerequisites
To test out the Kubernetes manifest files of this article, you will need:
- A Kubernetes cluster. You can use Vultr Kubernetes Engine to deploy a Kubernetes cluster.
- A kubectl client on your local workstation that is configured to work with your Kubernetes cluster.
Understanding a Kubernetes Service
The following is the manifest file that creates a Kubernetes Service Object:
apiVersion: v1
kind: Pod
metadata:
name: nginx
labels:
app: web-server
spec:
containers:
- name: nginx
image: nginx:stable
ports:
- containerPort: 80
name: pod-port
---
apiVersion: v1
kind: Service
metadata:
name: first-service
spec:
selector:
app: web-server
ports:
- name: service-port
protocol: TCP
port: 80
targetPort: pod-port
This manifest file creates two resources:
- A Pod named
nginx
. - A Service named
first-service
.
The .spec.selector
field specifies a list of labels
to look for in a Pod. Pods that have all the labels of the Service become its endpoint
and are added to the Endpoints
object of the service. The Endpoints
object represents the set of Pods that a Service is exposing. The Service forwards the incoming request to any one of its endpoints. Kubernetes automatically adds any new pod as an endpoint that has all the matching labels with the service.
The .spec.ports
field defines the network port of the Service, Service protocol, and other necessary network-related information. It contains the following sub-fields:
name
: This defines the name of the port through which the Service accepts the incoming network requests.protocol
: This defines the network protocol of the Service. The default protocol isTCP
. The other two supported protocols areSCTP
andUDP
.port
: This specifies the port number through which the Service accepts the incoming network requests.targetPort
: This specifies the port of the endpoints to which the Service forwards the incoming network traffic. You can specify the port number of the endpoint or its port name. In the example above, the value of this field matches the Pod's.spec.ports.name
field. By default, thetargetPort
and theport
field have the same value.
Note: The
---
inside the above manifest files between the definition of the Pod and the Service allows you to group many definitions in a single file.
To POST this manifest file to the Kubernetes API, use:
$ kubectl apply -f first-service.yaml
Expected output:
pod/nginx created
service/first-service created
Once a Service is created, it is assigned an IP address. To check the IP address of a service, use:
$ kubectl get services
Expected output:
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
first-service ClusterIP 10.107.13.79 <none> 80/TCP 52m
kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 101d
This lists all the Services in your cluster. Under the CLUSTER-IP
field, you can see the assigned IP address of a Service.
Types of Kubernetes Services
A Service can expose its endpoints to other internal components of the cluster, or it can expose them to traffic from outside the cluster. This section discusses how you can do that and more using the different Service types.
The Service manifest has a .spec.type
field. The value of this field determines how the Service works.
The type
property can have one of the following values:
- ClusterIP
- NodePort
- LoadBalancer
- ExternalName
ClusterIP
This is the default value of the .spec.type
field. The Service is assigned an internal IP address of the cluster. The Service is only reachable from within the cluster. The two example Service definitions that are mentioned above in this article are of type ClusterIP
by default.
NodePort
A NodePort
type Service is an extension of ClusterIP
type, meaning that the Service is still assigned an internal IP address that exposes the endpoints internally within the cluster. The additional feature of NodePort
type Service is that the nodes that host the endpoint Pods are assigned a random port from --service-node-port-range
. The default range is from 30000 through 32767.
Note: You can specify a custom port number in the
.spec.ports.nodePort
field to theNodePort
Service from this range. However, it is recommended to let Kubernetes assign thenodePort
value. This can prevent a possible collision with Services that pre-exist.
These nodes are directly accessible through their public IP address.
Working of NodePort Service:
- The assigned port number is opened on all the nodes.
- This port number is then proxied to the Service.
- Any request made to the IP address of the node and its port is then proxied by the Service to the
targetPort
of the node's Pod. - For example, a node hosts an endpoint Pod of a Service. Its IP address is
192.168.1.101
and theNodePort
Service assigned port30200
to this Node. ThetargetPort
of the endpoint Pod of this Service is80
. Then, any external request that is made at192.168.1.101:30200
is directed to the Pod's port80
which resided on the node with the same IP address.
Example:
apiVersion: v1
kind: Service
metadata:
name: nodeport-service
spec:
type: NodePort
selector:
app: webapp
ports:
- port: 8080
targetPort: 80
nodePort: 30007
This Service accepts internal traffic at port 8080 and forwards the traffic to the endpoints at port 80. The optional .spec.ports.nodePort
field specifies the port number of the Node at which it accepts the external traffic.
Note: NodePort Service is not very secure as it exposes the Nodes directly through their IP addresses.
LoadBalancer
Using type LoadBalancer
creates a load balancer in the infrastructure that hosts the cluster. For example, deploying a LoadBalancer
type Service in Vultr creates a Vultr Load Balancer that distributes the traffic to different endpoints. The distribution mechanism depends on the cloud service provider.
The control plane of the Kubernetes cluster has a component called cloud-controller-manager
. This component connects the cluster to the cloud service provider's API and implements its specific mechanism of creating and handling objects. It includes a service controller
that is responsible to implement components like a load balancer.
Working of LoadBalancer
Service:
- Kubernetes creates a Service that is similar to a
NodePort
type Service, meaning that the Service has an internal cluster IP address that is accessible locally and each node in the cluster is assigned anodePort
value. - The
service controller
component configures the load balancer to send traffic to individual nodes on their assignednodePort
. - A
LoadBalancer
type Service can be accessed externally using the Kubernetes Ingress object. You can assign an external IP address to the Service in its.status.loadBalancer.ingress
field.
Example:
apiVersion: v1
kind: Service
metadata:
name: load-balancer-service
spec:
selector:
app: web-app
ports:
- protocol: TCP
port: 80
targetPort: 9350
type: LoadBalancer
status:
loadBalancer:
ingress:
- ip: 192.0.2.127
Refer to the Vultr Load Balancer with VKE article for more information.
ExternalName
This type of Service uses a DNS name to redirect the incoming traffic. The traditional .spec.selector
field is not used to select the endpoints. Instead, the Service uses an external endpoint that is specified using a DNS name in the .spec.externalName
field. This Service type is generally used to abstract a component that is not present inside the cluster.
Example:
apiVersion: v1
kind: Service
metadata:
name: external-service
spec:
type: ExternalName
externalName: endpoint.database.site.com
Basic Operations
The operations described in this section are performed on a Service named test-service
for demonstration purposes. You can replace it with any Service object's name.
List all Service objects
$ kubectl get services
Expected output:
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE external-service ExternalName <none> endpoint.database.site.com <none> 26h kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 103d load-balancer-service LoadBalancer 10.106.55.89 <pending> 80:32075/TCP 6h21m nodeport-service NodePort 10.97.164.108 <none> 8080:30007/TCP 23h test-service LoadBalancer 10.105.111.220 <pending> 80:31297/TCP 2m29s
Describe a Service object
$ kubectl describe service test-service
Expected output:
Name: test-service Namespace: default Labels: <none> Annotations: <none> Selector: app=web-app Type: LoadBalancer IP Family Policy: SingleStack IP Families: IPv4 IP: 10.105.111.220 IPs: 10.105.111.220 Port: <unset> 80/TCP TargetPort: 9350/TCP NodePort: <unset> 31297/TCP Endpoints: <none> Session Affinity: None External Traffic Policy: Cluster Events: <none>
Delete a Service object
$ kubectl delete service test-service
Expected output:
service "test-service" deleted
Expose Multiple Ports
A Service can accept traffic on multiple ports. You can define multiple port definitions under the .spec.ports
field.
apiVersion: v1
kind: Service
metadata:
name: multi-port-service
spec:
selector:
app: nginx
ports:
- name: http-port
protocol: TCP
port: 80
targetPort: 9376
- name: https-port
protocol: TCP
port: 443
targetPort: 9377
Note: You have to define the
name
property for every port if your Service has multiple port definitions.
Set a Custom IP Address
Kubernetes allows you to set a custom IP Address for your Service. You can specify the IP address in the .spec.clusterIP
field.
The value should:
- Lie within the
service-cluster-ip-range
- Be a valid IPv4 or IPv6 address.
Not following these rules will cause the API server to throw an error.
To check the default service-cluster-ip-range
, use:
$ kubectl cluster-info dump | grep -m 1 service-cluster-ip-range | sed -e 's/^[ \t]*//'
Sample output:
"--service-cluster-ip-range=10.96.0.0/12",
You may want to use a custom IP address in certain situations like:
- You have a preconfigured client that relies on a specific IP address and reconfiguring it may turn out to be a tedious task.
- You have the IP address set as a DNS entry somewhere and you wish to reuse it.
Service Discovery
It is the process of discovering the network location of a Service. It can be done using the environment variables and cluster DNS.
Environment Variables
Kubernetes adds a set of environment variables to the Pod. These variables contain information about each active Service which helps the Pod to communicate with the Service. This way, you do not have to hard-code the IP address of the Service in each Pod. The disadvantage of using this method is that the Pod will not be updated with the data about Services that are created after the Pod. Using DNS for Service discovery eliminates this issue.
DNS
In Kubernetes, Service and Pod objects get DNS records. A DNS server like CoreDNS creates a set of DNS records for a new Service and Pod object. To use DNS service in a cluster, you have to set it up. To set up a DNS service, check out the Kubernetes documentation. Pods can use DNS records to Discover Services.
Session Stickiness
In Kubernetes, you can route the network traffic from a specific client to a fixed Pod during the entirety of its session. To set this up, you have to set the .spec.sessionAffinity
field of the Service. Refer to the Session Stickiness section for more information.
Conclusion
Kubernetes Service is a vital component of Kubernetes clusters, enabling efficient communication and load balancing between pods. With different types of services and service discovery options, it allows access to services within and outside the cluster, making it a powerful tool for building scalable and reliable applications.