Understanding services
In Kubernetes, a service is an abstraction that defines a logical set of pods, as well as a policy for accessing them. An example service definition is shown in the following code block, which includes a collection of pods that each listen on TCP port 9876
with the app=exampleApp
label:
apiVersion: v1 kind: Service metadata: name: example-service spec: selector: app: exampleApp ports: - protocol: TCP port: 80 targetPort: 9876
In the preceding example, a new Service
object named example-service
was created that routes TCP port 9876
to any pod with the app=exampleApp
label. This service is given an IP address by Kubernetes, which is utilized by the service proxies. A Kubernetes service, in simple terms, connects a group of pods to an abstracted service name and IP address. Discovery and routing between pods are provided by services. Services, for example, connect an application's frontend to its backend, which are both deployed in different cluster deployments. Labels and selectors are used by services to match pods with other applications.
The core attributes of a Kubernetes service are as follows:
- A label selector that locates pods
- The cluster IP address and the assigned port number
- Port definitions
- (Optional) Mapping for incoming ports to a targetPort
Kubernetes will automatically assign a cluster IP address, which will be used to route traffic by service proxies. The selector's controller will check for pods that match the defined label. Some applications will require multiple ports to be exposed via the service. Kubernetes facilitates this by using multi-port services, where a user can define multiple ports in a single service object.
In the following example, we have exposed ports 80
and 443
to target ports 8080
and 8090
to route HTTP and HTTPS traffic to any underlying pods using the app=webserver-nginx-multiport-example
selector:
apiVersion: v1 kind: Service metadata: name: nginx-service spec: selector: app: webserver-nginx-multiport-example ports: - name: http protocol: TCP port: 80 targetPort: 8080 - name: https protocol: TCP port: 443 targetPort: 8090
A service can also be defined without the use of a selector; however, you must explicitly connect the service (IP address, port, and so on) using an endpoints
object. This is because, unlike with a selector, Kubernetes does not know which pods the service should be connected to, so endpoint
objects are not built automatically.
Some use cases for services without selectors are as follows:
- Connecting to another service in a different namespace or cluster
- Communicating with external services, data migration, testing services, deployments, and so on
Let's create a deployment with three replicas of an Apache web server:
apiVersion: apps/v1 kind: Deployment metadata: name: apache-deployment labels: app: webserver spec: replicas: 3 selector: matchLabels: app: webserver template: metadata: labels: app: webserver spec: containers: - name: apache image: httpd:latest ports: - containerPort: 80
Create the deployment using the following command:
kubectl apply -f apache-deployment.yaml
The following are the most common types of services:
- ClusterIP: This is the default type and exposes the service via the cluster's internal IP address. These services are only accessible within the cluster. So, users need to implement port forwarding or a proxy to expose a ClusterIP to a wider ingress of traffic.
- NodePort: A static port on each node's IP is used to expose a service. To route traffic to the NordPort service, a ClusterIP service is automatically created. Requesting
NodeIP:NodePort>
from the outside allows users to communicate with the service. - LoadBalancer: This is the preferred solution for exposing the cluster to the wider internet. The LoadBalancer type of service will create a load balancer (the load balancer's type depends on the cloud provider) and expose the service externally. It will also automatically create ClusterIP and NodePort services and route traffic accordingly.
- ExternalName: Maps a service to a predefined
externalName ex.sampleapp.test.com
field by returning a value for theCNAME
record.