Last updated on August 9, 2023
What is a Pod?
A Pod is a Kubernetes Object that has one or more containers running inside it. A Pod will ensure that the containers inside it share the same resources and local network. Each Pod gets its own IP address.
If you use a Deployment to run your app, that Deployment can create and destroy Pods dynamically. Pods are created and destroyed to match the desired state of your cluster.
Pods are ephemeral resources, meaning that they can be deleted and recreated as needed by Kubernetes. As such, you shouldn’t expect an individual Pod to be reliable and durable. This is by design.
What are Kubernetes Services?
In Kubernetes, a Service is a method for exposing a network application that is running as one or more Pods in your Cluster.
Since Pods are ephemeral, we need to have a mechanism for Pods inside the Cluster to find other Pods. This is when we use Kubernetes Services.
The Service API is an abstraction to help you expose a group of Pods over a network. Each Service object defines a logical set of endpoints along with a policy to make these Pods available.
The Pods that a Service will use can be retrieved with a selector.
Kubernetes Service Types
In Kubernetes, you might need to expose a service via an external IP address. This is usually done for frontend and API endpoints.
These are the four types of Services:
- ClusterIP
- NodePort
- LoadBalancer
- ExternalName
ClusterIP
By default, this is the Service Type used by Kubernetes. ClusterIP will allow the Service to be reachable from within the Cluster. To expose this Service to outside the Cluster (for example, by allowing the public Internet to access the Service), an Ingress or Gateway is used.
ClusterIP works by assigning an IP address from the Cluster’s reserved pool of IP addresses.
NodePort
This exposes the Service on each Node’s IP at a static port.
NodePort works by having the Kubernetes Control Plane allocate a port from a predefined range (default: 30000 – 32767). The Node in which the Service resides in will proxy a port from that range, allowing access to the Service.
LoadBalancer
This exposes the Service using an external load balancer. Kubernetes doesn’t come with a built-in load balancer but can integrate with most load balancers (like Nginx) and any supported Cloud provider.
ExternalName
This maps the Service to the value of the “externalName” field. The mapping uses the cluster’s DNS server to return a CNAME record with the value.
Defining a Service
ClusterIP
By default, ClusterIP is used for a Service. Other Service Types are based on this configuration. Here is an example of a Service manifest:
apiVersion: v1 kind: Service metadata: name: my-service spec: selector: app.kubernetes.io/name: MyApp ports: – protocol: TCP port: 80 targetPort: 9376 |
After saving this yaml to disk, you can apply the yaml with the following command:
$ kubectl apply -f [filename.yaml]
This manifest creates a Service named “my-service”. By default, this is a ClusterIP service type. The Service targets TCP port 9376 on any pod with the app.kubernetes.io/name: MyApp label.
After creation, Kubernetes will assign the cluster’s IP address to this Service via Kubernetes virtual IPs and Service Proxies.
You can define ports with names instead of hardcoding them in the Service. Here is an example:
apiVersion: v1 kind: Pod metadata: name: nginx labels: app.kubernetes.io/name: proxy spec: containers: – name: nginx image: nginx:stable ports: – containerPort: 80 name: http-web-svc |
— apiVersion: v1 kind: Service metadata: name: nginx-service spec: selector: app.kubernetes.io/name: proxy ports: – name: name-of-service-port protocol: TCP port: 80 targetPort: http-web-svc |
You can also define multiple ports for a Service. Here is an example:
apiVersion: v1 kind: Service metadata: name: my-service spec: selector: app.kubernetes.io/name: MyApp ports: – name: http protocol: TCP port: 80 targetPort: 9376 – name: https protocol: TCP port: 443 targetPort: 9377 |
NodePort
This is an example of a NodePort:
apiVersion: v1 kind: Service metadata: name: my-service spec: type: NodePort selector: app.kubernetes.io/name: MyApp ports: – protocol: TCP port: 80 targetPort: 9376 # optional. without this line, Kubernetes will use the default port range nodePort: 30007 |
Note that it is the same as a ClusterIP except we explicitly declare .spec.type as NodePort and added an optional .spec.ports.nodePort value.
LoadBalancer
This is an example of a LoadBalancer:
apiVersion: v1 kind: Service metadata: name: my-service spec: selector: app.kubernetes.io/name: MyApp ports: – protocol: TCP port: 80 targetPort: 9376 clusterIP: 10.0.171.201 type: LoadBalancer status: loadBalancer: ingress: – ip: 192.0.2.127 |
Traffic from the load balancer will be directed to the Pod with the app.kubernetes.io/name: MyApp label.
If a cloud-controller-manager component is used in the Cluster, it will configure the external load balancer to forward traffic to the assigned node port. This can be omitted if the Cloud provider supports this.
ExternalName
This is an example of a ExternalName Service:
apiVersion: v1 kind: Service metadata: name: my-service namespace: prod spec: type: ExternalName externalName: my.database.example.com |
When a request for my-service.prod.svc.cluster.local is sent to the cluster DNS Service, this configuration will returns a CNAME record with the externalName value of “my.database.example.com”.
Headless Service
If you plan to use a different or customized service discovery mechanism other than Kubernetes’ default implementation, you can run a Headless Service.
A Headless Service doesn’t use load-balancing or a single Service IP. A cluster IP is not allocated, kube-proxy will not handle these services, and Kubernetes will not provide a proxy.
A Headless Service is created when the value for .spec.clusterIP is set to “None”.
The redirection happens at the DNS level instead of forwarding or proxying.