Kubernetes 05 - Exposing Kubernetes Ports

In Kubernetes, exposing ports is a fundamental aspect of managing and accessing applications running within your cluster. Whether you’re aiming to connect internally within the cluster or expose your services to the outside world, Kubernetes offers a variety of methods to do so.

Exposing Containers with kubectl expose

To start with, kubectl expose is a command that creates a service for existing pods. But what exactly does this mean?

A service in Kubernetes is essentially a stable endpoint that provides access to a set of pods. These pods are dynamic and can change over time, but the service provides a consistent way to reach them. This is crucial because, in a Kubernetes environment, pods may come and go, scale up or down, and the service ensures that your applications can reliably communicate with these pods.

Kubernetes uses CoreDNS to resolve these services by name, allowing applications to connect to services seamlessly.

There are several types of services in Kubernetes.

Basic Service Types

ClusterIP (default)

  • Description: Allocates a single internal virtual IP address that is only reachable from within the cluster.
  • Use Case: Suitable for internal communication between applications running within the cluster.

NodePort

  • Description: Designed for external access to the cluster. It allocates a high port on each node, making it accessible on every node’s IP address.
  • Use Case: Allows external clients to connect to a specific port on the nodes to access the service.

LoadBalancer

  • Description: Creates an external load balancer that distributes traffic across multiple nodes and pods. It relies on an infrastructure provider that supports load balancers, such as AWS ELB (Elastic Load Balancer) or Azure Load Balancer.
  • Use Case: Used for distributing external traffic across multiple pods and nodes, ensuring high availability.

ExternalName

  • Description: Maps a service to a DNS name. It adds a CNAME record to CoreDNS, pointing to an external DNS name.
  • Use Case: Used when you want to give pods a DNS name to use for an external service that resides outside the Kubernetes cluster.

Creating a ClusterIP Service

Let’s walk through the steps to create a ClusterIP service.

Step 1: Monitor the Pods

Open a terminal and run the following command to monitor the pods.

kubectl get pods -w

Step 2: Create a Deployment

In another terminal, create a deployment with the following command.

First run kubectl get pods -w on one terminal.

kubectl create deployment httpenv --image=bretfisher/httpenv

Step 3: Scale the Deployment

Scale the deployment to five replicas.

kubectl scale deployment/httpenv --replicas=5

Step 4: Create a ClusterIP Service

Create a ClusterIP service to expose the deployment.

kubectl expose deployment/httpenv --port=8888

Let’s break down this command:

  • kubectl: This is the command-line tool for interacting with your Kubernetes cluster.
  • expose: This subcommand creates a new Kubernetes service based on a specified resource.
  • deployment/httpenv: This specifies the resource that you want to expose. In this case, it is a deployment named httpenv.
  • --port=8888: This flag specifies the port on which the service will be exposed. It tells Kubernetes to create a service that listens on port 8888 and routes traffic to the corresponding port on the pods managed by the httpenv deployment.

When you run this command, Kubernetes performs the following actions:

  • Service Creation: Kubernetes creates a new service resource. This service has a stable IP address within the cluster (known as ClusterIP) that can be used by other pods to communicate with the httpenv pods.
  • Port Mapping: The service listens on port 8888 and forwards traffic to port 8888 on the pods selected by the httpenv deployment.

Listing the ClusterIP Services

Verify that the ClusterIP service was created by running:

kubectl get service

The output should be similar to:

NAME         TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)    AGE
httpenv      ClusterIP   10.111.103.37   <none>        8888/TCP   92s
kubernetes   ClusterIP   10.96.0.1       <none>        443/TCP    2d1h

Remember, the ClusterIP is internal only. To test the service from within the cluster, follow these steps:

Step 1: Create a Temporary Shell

To interact with your Kubernetes services and test their connectivity, you often need a pod with a variety of networking tools. The bretfisher/netshoot image has pre-installed with network tools.

kubectl run tmp-shell --rm -it --image bretfisher/netshoot -- bash

Let’s break down this command:

  • kubectl run: This command creates a new pod or deployment.
  • tmp-shell: The name of the pod. In this case, it’s named tmp-shell to indicate that it’s a temporary shell.
  • --rm: Automatically removes the pod once it exits. This is useful for temporary pods that you don’t want lingering around.
  • -it: Stands for interactive terminal. It allows you to interact with the pod in real-time.
  • --image bretfisher/netshoot: Specifies the container image to use for the pod.
  • -- bash: Specifies the command to run in the container. In this case, it starts a bash shell, giving you an interactive terminal session inside the pod.

When you run this command, Kubernetes starts a new pod with the bretfisher/netshoot image and provides you with a bash shell. This environment is ideal for testing network connectivity and troubleshooting within the cluster.

Step 2: Test the Service

Once inside the temporary shell pod, you can test the connectivity to your Kubernetes service using the curl command:

curl httpenv:8888

When you run this command, curl sends an HTTP request to the httpenv service on port 8888. If the service is correctly set up and the pods are running as expected, you should receive a response. This confirms that the service is reachable and functioning properly.

Creating a NodePort Service

Now that we’ve covered how to create a ClusterIP service, let’s move on to creating a NodePort service. This service type is designed for external access, allowing clients outside the cluster to communicate with the pods.

Step 1: Exposing the Deployment with a NodePort

Let’s check the current services:

kubectl get service

This command lists all the services in the cluster. You should see the httpenv service with the ClusterIP type.

NAME                           READY   STATUS    RESTARTS   AGE
pod/httpenv-6cc7f7986f-28zvb   1/1     Running   0          5m34s
pod/httpenv-6cc7f7986f-6xf7m   1/1     Running   0          5m34s
pod/httpenv-6cc7f7986f-czxjv   1/1     Running   0          5m34s
pod/httpenv-6cc7f7986f-n74lb   1/1     Running   0          5m40s
pod/httpenv-6cc7f7986f-vgbbn   1/1     Running   0          5m34s

NAME                 TYPE        CLUSTER-IP     EXTERNAL-IP   PORT(S)    AGE
service/httpenv      ClusterIP   10.105.5.251   <none>        8888/TCP   5m29s
service/kubernetes   ClusterIP   10.96.0.1      <none>        443/TCP    3d10h

NAME                      READY   UP-TO-DATE   AVAILABLE   AGE
deployment.apps/httpenv   5/5     5            5           5m40s

NAME                                 DESIRED   CURRENT   READY   AGE
replicaset.apps/httpenv-6cc7f7986f   5         5         5       5m40s

To expose your deployment using a NodePort service, use the following command:

kubectl expose deployment/httpenv --port 8888 --name httpenv-np --type NodePort

Let’s break down this command:

  • kubectl expose: This command exposes a resource as a new Kubernetes service.
  • deployment/httpenv: This specifies the deployment that you want to expose.
  • --port 8888: This flag specifies the port on which the service will be exposed.
  • --name httpenv-np: Names the service httpenv-np for easier identification.
  • --type NodePort: Specifies that this service should be a NodePort service.

When you run this command, Kubernetes creates a service that is accessible externally on a high port (in the range 30000-32767) on each node in the cluster.

Step 2: Verify the NodePort Service

Check the services in your cluster to see the details of the newly created NodePort service:

kubectl get service

The output should show something similar to this:

NAME         TYPE        CLUSTER-IP     EXTERNAL-IP   PORT(S)          AGE
httpenv      ClusterIP   10.105.5.251   <none>        8888/TCP         8m41s
httpenv-np   NodePort    10.106.203.2   <none>        8888:32725/TCP   6s
kubernetes   ClusterIP   10.96.0.1      <none>        443/TCP          3d10h

For the service httpenv-np, the PORT(S) column shows 8888:32725/TCP.

  • 8888: This is the internal port on which the service is accessible within the cluster. Traffic directed to this service will be forwarded to port 8888 on the pods selected by the service.
  • 32725: This is the external port assigned by Kubernetes for the NodePort service. This port can be used to access the service from outside the cluster. Any traffic sent to any node in the cluster on port 32725 will be routed to the internal port 8888 of the service.

Step 3: Access the NodePort Service

To access the service from outside the cluster, use the external IP address of any node in your cluster and the NodePort assigned to the service. For example, if you are running Kubernetes on your local machine or using a cloud provider, you can access the service using localhost and the NodePort.

curl localhost:32725

This command sends an HTTP request to localhost on port 32725, which is then routed to the httpenv deployment pods on port 8888.

Note that a NodePort service also creates a ClusterIP service, making the service accessible within the cluster.

Adding a LoadBalancer Service

If you need to expose your service to the outside world and distribute traffic across multiple nodes and pods, you can use a LoadBalancer service. This service type relies on an infrastructure provider that supports load balancers, such as AWS ELB (Elastic Load Balancer) or Azure Load Balancer.

Step 1: Expose the Deployment with a LoadBalancer

If you are on Docker Desktop, it provides a built-in LoadBalancer service that publishes the --port on local host.

kubectl expose deployment/httpenv --port=8888 --name=httpenv-lb --type=LoadBalancer

Step 2: Verify the LoadBalancer Service

Check the services in your cluster to see the details of the newly created LoadBalancer service:

kubectl get service

The output should look like this:

NAME         TYPE           CLUSTER-IP     EXTERNAL-IP   PORT(S)          AGE
httpenv      ClusterIP      10.105.5.251   <none>        8888/TCP         41m
httpenv-lb   LoadBalancer   10.97.126.49   localhost     8888:32066/TCP   2s
httpenv-np   NodePort       10.106.203.2   <none>        8888:32725/TCP   32m
kubernetes   ClusterIP      10.96.0.1      <none>        443/TCP          3d11h

In this example, the httpenv-lb service is of type LoadBalancer.

If you are on kubeadm, minikube, or microk8s, there is no built-in LoadBalancer. You can still run the command, but it will just stay in pending state.

Step 3: Access the LoadBalancer Service

To access the service, use the external IP address of the LoadBalancer and the specified port:

curl localhost:8888

The LoadBalancer service distributes the incoming requests to the NodePort service, which then routes the traffic to the appropriate pods.

The load balancer port 32066 is the built in NodePort that it is creating. In the background, the load balancer receives packets on port 8888 and forwards them to the NodePort service on port 32066. Then the NodePort is passing it to the ClusterIP, which is forwarding it to the pods. Note that there is always going to be that NodePort shown next to the colon in the PORT(S) column for the LoadBalancer service even though the port is not really the load balancer is using on the host.

Cleaning up

kubectl delete service/httpenv service/httpenv-np service/httpenv-lb deployment/httpenv



    Enjoy Reading This Article?

    Here are some more articles you might like to read next:

  • Dependency Injection
  • CPU Cache
  • Understanding Linear Blended Skinning in 3D Animation
  • Starvation in Operating Systems
  • Virtual Memory