1. Overview
Kubernetes is currently the most widely used container orchestration tool. It has become the de facto standard for the deployment of microservices. Often, each microservice is deployed as a Kubernetes Pod, and we need a stable network endpoint to access that microservice. In Kubernetes, we can achieve this using the Service object.
In this short tutorial, we’ll discuss how to expose the network application running inside a minikube cluster.
2. Setting up an Example
In this section, we’ll create a few Kubernetes objects to use for our examples. Let’s start with creating a new Namespace.
2.1. Creating a Kubernetes Namespace
Minikube comes with a few namespaces out of the box, and one such namespace is default. We can deploy applications inside this namespace. However, it’s a best practice to create a new namespace for the application deployment.
So, let’s create a new namespace with the name service-demo:
$ kubectl create ns service-demo
namespace/service-demo created
2.2. Creating a Kubernetes Deployment
Next, let’s deploy the NGINX pod inside the service-demo namespace:
$ kubectl create deploy nginx --image=nginx:alpine -n service-demo
deployment.apps/nginx created
Finally, let’s verify that the NGINX pod is running:
$ kubectl get pods -n service-demo
NAME READY STATUS RESTARTS AGE
nginx-b4ccb96c6-qzvmm 1/1 Running 0 2s
Now, the required setup is ready. In the forthcoming sections, we’ll discuss how to expose the nginx deployment using the various service types.
3. Exposing a Port Using port-forward
In the previous section, we deployed an NGINX pod. Kubernetes assigns an IP address to each pod that is routable within the cluster. However, using the pod’s IP address isn’t the most reliable method because it might change when the pod restarts or gets scheduled on another node. We can address this issue using the Kubernetes Service.
3.1. Creating a ClusterIP Service
First, let’s use the expose command to create a ClusterIP service and verify that the service has been created:
$ kubectl expose deploy nginx --type ClusterIP --port=80 -n service-demo
service/nginx exposed
$ kubectl get svc -n service-demo
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
nginx ClusterIP 10.96.252.114 <none> 80/TCP 2s
It’s important to note that the expose command creates a service using the selectors of the Deployment object. In our case, it’s using the selectors of the nginx deployment.
3.2. Exposing the ClusterIP Service Using port-forward
In the previous section, we created a ClusterIP service with the name nginx, and that is listening on the TCP port 80.
Next, let’s use the port-forward command to forward the traffic from the local machine to the nginx pod:
$ kubectl port-forward svc/nginx 8080:80 -n service-demo
Forwarding from 127.0.0.1:8080 -> 80
Forwarding from [::1]:8080 -> 80
Handling connection for 8080
In this example, we’ve used the colon (:) character to separate the local and pod ports. Port 8080 is the local port, whereas 80 is the pod port.
Now, let’s open another terminal and execute the curl command using 127.0.0.1:8080 as the web server’s URL:
$ curl http://127.0.0.1:8080
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
html { color-scheme: light dark; }
body { width: 35em; margin: 0 auto;
font-family: Tahoma, Verdana, Arial, sans-serif; }
</style>
</head>
<body>
<h1>Welcome to nginx!</h1>
<p>If you see this page, the nginx web server is successfully installed and
working. Further configuration is required.</p>
<p>For online documentation and support please refer to
<a href="http://nginx.org/">nginx.org</a>.<br/>
Commercial support is available at
<a href="http://nginx.com/">nginx.com</a>.</p>
<p><em>Thank you for using nginx.</em></p>
</body>
</html>
Here, we can see that the NGINX server responds with the welcome page.
3.3. Cleaning Up
In the next section, we’ll expose the nginx deployment using the NodePort service. But before that, let’s do the cleanup of the ClusterIP service.
port-forward is a blocking command. So, first, let’s use the Ctrl + c key combination to stop the port forwarding.
Now, let’s use the delete command to remove the nginx service:
$ kubectl delete svc nginx -n service-demo
service "nginx" deleted
4. Exposing the Port Using the Nodeport Service
A NodePort service is the most common way of exposing a service outside the cluster. The NodePort service opens a specified port on all cluster nodes and forwards the traffic from this port to the service.
Let’s understand this with a simple example.
4.1. Creating a NodePort Service
First, let’s create a NodePort service using the expose command:
$ kubectl expose deploy nginx --port 80 --type NodePort -n service-demo
service/nginx exposed
In this example, we’ve used the –type option to specify the service type.
Next, let’s find out the allocated node port:
$ kubectl get svc -n service-demo
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
nginx NodePort 10.108.241.105 <none> 80:30136/TCP 2s
In the output, the PORT(S) column shows that the service port 80 is mapped to the node port 30136.
Now, we can use the IP address of the Kubernetes node and port 30136 from outside to access the NGINX server. Let’s see this in action in the next section.
4.2. Using the NodePort Service
First, let’s find out the node on which the nginx pod is running:
$ kubectl get pods -o wide -n service-demo
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
nginx-b4ccb96c6-qzvmm 1/1 Running 0 2m36s 10.244.1.2 minikube-m02 <none> <none>
Here, we’ve used the -o option with the get command to show a few additional fields. In the output, the NODE column shows that the pod is running on a minikube-m02 node.
Next, let’s find the IP address of the minikube-m02 node:
$ kubectl get nodes minikube-m02 -o wide
NAME STATUS ROLES AGE VERSION INTERNAL-IP EXTERNAL-IP OS-IMAGE KERNEL-VERSION CONTAINER-RUNTIME
minikube-m02 Ready <none> 13m v1.28.3 192.168.58.3 <none> Ubuntu 22.04.3 LTS 5.15.0-41-generic docker://24.0.7
In the above output, the column INTERNAL-IP shows the IP address of the Kubernetes node.
Now, from the external machine, let’s connect to the NGINX server using 192.168.58.3:30136 as the URL:
$ curl http://192.168.58.3:30136
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
html { color-scheme: light dark; }
body { width: 35em; margin: 0 auto;
font-family: Tahoma, Verdana, Arial, sans-serif; }
</style>
</head>
<body>
<h1>Welcome to nginx!</h1>
<p>If you see this page, the nginx web server is successfully installed and
working. Further configuration is required.</p>
<p>For online documentation and support please refer to
<a href="http://nginx.org/">nginx.org</a>.<br/>
Commercial support is available at
<a href="http://nginx.com/">nginx.com</a>.</p>
<p><em>Thank you for using nginx.</em></p>
</body>
</html>
Here, we can see that the NGINX server is accessible using the IP address and port of the Kubernetes node.
4.3. Cleaning Up
Now, let’s do the cleanup of the NodePort service before going to the next section:
$ kubectl delete svc nginx -n service-demo
service "nginx" deleted
5. Exposing the Port Using the LoadBalancer Service
In a production environment, service availability is crucial. We need a reliable load balancer that can handle heavy traffic effortlessly to meet high request demands. In such cases, we can expose the application using the LoadBalancer service.
Kubernetes’ LoadBalancer service provides an option to create a cloud load balancer. It provides an externally accessible IP address that routes traffic to the application.
It’s important to note that minikube doesn’t create a load balancer in the cloud. Instead, it’s simulated using the tunnel. So, let’s see how to create a LoadBalancer service in a minikube.
5.1. Creating a LoadBalancer Service
First, let’s create a service with the type LoadBalancer:
$ kubectl expose deploy nginx --port 80 --type LoadBalancer -n service-demo
service/nginx exposed
Next, let’s find out the IP address of the load balancer:
$ kubectl get svc -n service-demo
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
nginx LoadBalancer 10.105.32.228 <pending> 80:30814/TCP 2s
In the above output, the column EXTERNAL-IP shows the IP address of the load balancer. However, in our case, it’s showing as
It’s important to note that this is the default behavior of the minikube. To avoid this, we need to execute the minikube tunnel command:
$ minikube tunnel
Status:
machine: minikube
pid: 34540
route: 10.96.0.0/12 -> 192.168.58.2
minikube: Running
services: [nginx]
errors:
minikube: no errors
router: no errors
loadbalancer emulator: no errors
Now, we can see that the load balancer is simulated using minikube’s tunnel.
5.2. Using the LoadBalancer Service
In the previous section, we used the minikube tunnel command to allocate the external IP. However, that is a blocking command.
So, let’s open another terminal and check the external IP address of the LoadBalancer service:
$ kubectl get svc -n service-demo
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
nginx LoadBalancer 10.105.32.228 10.105.32.228 80:30814/TCP 34s
In this output, we can see that the minikube has allocated an external IP to the LoadBalancer service.
Now, let’s access the NGINX web server using 10.105.32.228 as its URL:
$ curl http://10.105.32.228
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
html { color-scheme: light dark; }
body { width: 35em; margin: 0 auto;
font-family: Tahoma, Verdana, Arial, sans-serif; }
</style>
</head>
<body>
<h1>Welcome to nginx!</h1>
<p>If you see this page, the nginx web server is successfully installed and
working. Further configuration is required.</p>
<p>For online documentation and support please refer to
<a href="http://nginx.org/">nginx.org</a>.<br/>
Commercial support is available at
<a href="http://nginx.com/">nginx.com</a>.</p>
<p><em>Thank you for using nginx.</em></p>
</body>
</html>
Here, we can see that the NGINX server renders the welcome page.
6. Cleaning Up
It’s a good practice to clean up the temporarily created resources. So, let’s do the cleanup in our cluster.
First, let’s use the Ctrl + c key combination to terminate the minikube tunnel.
Next, let’s delete the service-demo namespace:
$ kubectl delete ns service-demo
namespace "service-demo" deleted
While setting up an example, we deployed all resources in the service-demo namespace, and we wanted to delete all of them as a part of the cleanup activity. Hence, we deleted the namespace directly.
However, we should be careful while performing the delete operation in the production environment, and deleting the namespace directly is not recommended.
7. Conclusion
In this article, we discussed how to expose a service in minikube.
First, we discussed exposing the ClusterIP service using the port-forward command. Next, we discussed exposing a service using the Kubernetes node’s IP address and port.
Finally, we discussed exposing a service using the external load balancer.