Exposing Applications Using Kubernetes Services

Updated: January 30, 2024 By: Guest Contributor Post a comment

Introduction

Kubernetes is an open-source platform for automating deployment, scaling, and operations of application containers across clusters of hosts. It’s a powerful tool for microservices architecture and has become a cornerstone of container orchestillation. One of the main features of Kubernetes is the Service object, which provides an abstract way to expose an application running on a set of Pods as a network service. In this tutorial, we will cover how to expose applications using Kubernetes Services thereby making them accessible to external traffic and other services inside or outside of a cluster.

Understanding Kubernetes Services

In Kubernetes, a Service is an abstraction which defines a logical set of Pods and a policy by which to access them. This abstraction enables loose coupling between dependent Pods. The set of Pods targeted by a Service is usually determined by a selector. Types of Services include:

  • ClusterIP: Exposes the Service on an internal IP in the cluster. This type of Service is reachable only within the cluster.
  • NodePort: Exposes the Service on the same port of each selected Node in the cluster using NAT. It makes a cluster service reachable externally, although the IP is not stable.
  • LoadBalancer: Exposes the Service externally using a cloud provider’s load balancer. NodePort and ClusterIP Services, to which the external load balancer routes, are automatically created.
  • ExternalName: Maps the Service to the contents of the externalName field by returning a CNAME record with its value. No proxying of any kind is set up.

Creating a Basic Service

The busiest element of a Kubernetes service is the Pod, which encapsulates application containers. Here’s how to create a simple Service that exposes a Pod.

apiVersion: v1
kind: Service
metadata:
  name: my-service
spec:
  selector:
    app: MyApp
  ports:
    - protocol: TCP
      port: 80
      targetPort: 9376

After creating the YAML file, apply it using the kubectl command:

$ kubectl apply -f my-service.yaml

Output:

service/my-service created

Exposing a Service via NodePort

Let’s move towards making our Service accessible externally through a NodePort.

apiVersion: v1
kind: Service
metadata:
  name: my-nodeport-service
spec:
  type: NodePort
  selector:
    app: MyApp
  ports:
    - protocol: TCP
      port: 80
      nodePort: 30007
      targetPort: 9376

After updating the Service definition, apply it:

$ kubectl apply -f my-nodeport-service.yaml

Output:

service/my-nodeport-service created

You can then access your application externally using any of your nodes’ IP addresses combined with the NodePort. Assuming your Node IP is 192.168.1.2, access it via:

http://192.168.1.2:30007

Using a LoadBalancer Service

In a cloud environment, you likely want to use a LoadBalancer service. Here’s a simple example for that.

apiVersion: v1
kind: Service
metadata:
  name: my-loadbalancer-service
spec:
  type: LoadBalancer
  selector:
    app: MyApp
  ports:
    - protocol: TCP
      port: 80
      targetPort: 9376

Deploy it as follows:

$ kubectl apply -f my-loadbalancer-service.yaml

Output:

service/my-loadbalancer-service created

The LoadBalancer service is provisioned with an external IP address by your cloud provider. You can list the details of the Service to get the IP:

$ kubectl get services my-loadbalancer-service

Output:

NAME                     TYPE          CLUSTER-IP    EXTERNAL-IP     PORT(S)        AGE
my-loadbalancer-service LoadBalancer  10.0.171.239  203.0.113.1     80:31377/TCP   78s

This external IP (203.0.113.1) is what you’d use to access your service from outside the Kubernetes cluster.

Defining a Service Without Selectors

There may be situations where you want to define Services without selectors such as when you want to manage the Pods’ lifecycle manually. For that, you create endpoints manually.

apiVersion: v1
kind: Service
metadata:
  name: my-manual-service
spec:
  ports:
    - protocol: TCP
      port: 80

Apply this definition and create an endpoint:

$ kubectl apply -f my-manual-service.yaml

apiVersion: v1
kind: Endpoints
metadata:
  name: my-manual-service
subsets:
  - addresses:
      - ip: 192.168.1.2
    ports:
      - port: 9376

After creating the endpoints, your Service should route to the specified Pod IP address.

Debugging Kubernetes Services

Sometimes, exposed services may not work as expected. Common ways to debug services include checking Logs, the Events tab in Kubernetes Dashboard, or running descriptions for detailed information.

$ kubectl describe service my-service
$ kubectl logs [pod-name]

Accessing Services from Other Applications

Within the cluster, other applications may need to communicate with the service. Kubernetes supports service discovery and request routing by assigning a stable IP address and DNS name for the service, which routes to any of the service’s Pods.

apiVersion: v1
kind: Pod
metadata:
  name: my-pod
spec:
  containers:
  - name: my-container
    image: nginx
    env:
      - name: SERVICE_NAME
        value: my-service

In this Pod, applications could connect to the ‘my-service’ through environment variables or DNS.

Conclusion

Services in Kubernetes offer a powerful abstraction for exposing applications running in Pods to be accessible as a network service. Using the correct type of Service depending on your environment and requirements is crucial. With this understanding, scaling and operating applications become much more manageable.