1. Overview

In Kubernetes, the LoadBalancer service is the standard way of exposing network applications to the external world. The LoadBalancer service is preferred when assigning a dedicated IP address to each service.

In the public cloud platforms, the LoadBalancer service deploys the network load balancer in the cloud. However, minikube simulates it using the tunnel protocol, and by default, it shows the service’s external IP address as pending.

In this short tutorial, we’ll discuss how to address Minikube’s issue when the Kubernetes service shows its external IP as pending.

2. Setting up an Example

It’s a good practice to use Kubernetes namespaces to isolate the resources. So, let’s create the namespace with the name service-demo:

$ kubectl create ns service-demo
namespace/service-demo created

Next, let’s use the create command to deploy the Redis pod in the newly created namespace:

$ kubectl create deploy redis --image=redis:alpine -n service-demo
deployment.apps/redis created

Finally, let’s use the get command to verify that the redis pod is running:

$ kubectl get pods -n service-demo
NAME                     READY   STATUS    RESTARTS   AGE
redis-8648874d67-d66wz   1/1     Running   0          35s

In this section, we created Kubernetes objects to use as an example. In the upcoming sections, we’ll discuss exposing the Redis server outside the cluster.

3. Assigning the External IP Using Minikube Tunnel

The LoadBalancer service is beneficial only if the Kubernetes cluster supports the deployment of external load balancers. For example, managed Kubernetes service in all major public cloud platforms supports the load balancer deployment.

By default, Kubernetes doesn’t provide an implementation of network load balancers for bare-metal clusters. However, we can simulate it by setting up the network routes. So, let’s see how to assign an external IP address to the LoadBalancer service in minikube using the tunnel protocol.

3.1. Creating a LoadBalancer Service

First, let’s use the expose command to create a Service with the type LoadBalancer:

$ kubectl expose deploy redis --port 6379 --type LoadBalancer -n service-demo
service/redis exposed

The expose command uses the selectors of the Deployment object and creates a Service using the same selectors. In this case, the Service uses the selectors of the Redis deployment.

Next, let’s find out the external IP address of the LoadBalancer service:

$ kubectl get service -n service-demo
NAME    TYPE           CLUSTER-IP      EXTERNAL-IP   PORT(S)          AGE
redis   LoadBalancer   10.103.33.106   <pending>     6379:31794/TCP   12s

In the output, the column with the header EXTERNAL-IP shows the load balancer’s IP address.

However, in this case, it’s showing as . In the next section, we’ll discuss how to fix this issue.

3.2. Using the Minikube Tunnel

In the previous section, we saw that the Redis service shows the external IP address as pending. Let’s fix this using the minikube tunnel command.

First, let’s execute the tunnel command in the terminal:

$ minikube tunnel       
Status:    
    machine: minikube
    pid: 9325
    route: 10.96.0.0/12 -> 192.168.49.2
    minikube: Running
    services: [redis]
    errors: 
        minikube: no errors
        router: no errors
        loadbalancer emulator: no errors

Here, we can see that the command sets up the network routes using the load balancer emulator.

Next, let’s open another terminal and check the IP address of the LoadBalancer service:

$ kubectl get service -n service-demo
NAME    TYPE           CLUSTER-IP      EXTERNAL-IP     PORT(S)          AGE
redis   LoadBalancer   10.103.33.106   10.103.33.106   6379:31794/TCP   53s

In the output, we can see the newly allocated external IP address. Now, let’s use the same IP address from outside to access the Redis server:

$ redis-cli -h 10.103.33.106 PING
PONG

Here, we can see that the Redis server responds with the PONG message.

3.3. Cleaning Up

Now, let’s clean up from the service-demo namespace before moving to the next section.

First, switch back to the first terminal and press the Ctrl + C key combination. This operation terminates the tunnel process and cleans up the network routes.

Next, let’s use the delete command to remove the Redis service:

$ kubectl delete svc redis -n service-demo
service "redis" deleted

4. Assigning the External IP Using MetalLB

Just like the tunnel protocol, we can also use the MetalLB load balancer to assign the external IP address to the LoadBalancer service.

In the case of Minikube, we can either install and configure the MetalLB from scratch or configure it via the add-ons. Minikube add-ons are extensions of minikube used for added functionality for Kubernetes.

In this section, we’ll discuss how to configure and use the MetalLB addon.

4.1. Enabling the metallb Addon

First, let’s enable the metallb addon using the addons enable command:

$ minikube addons enable metallb

Next, let’s use the addons list command to check the status of all addons:

$ minikube addons list

In the output, we can see that the metallb addon has been enabled. Now, let’s see how to configure it.

4.2. Configuring the metallb Addon

To configure the metallb addon, we need to specify the range of IP addresses the load balancer can use. So, let’s find the IP address of the minikube node using the ip command:

$ minikube ip                      
192.168.49.2

Next, let’s use the addons configure command to specify the IP address range for the load balancer:

$ minikube addons configure metallb
-- Enter Load Balancer Start IP: 192.168.49.10
-- Enter Load Balancer End IP: 192.168.49.20
    ▪ Using image quay.io/metallb/speaker:v0.9.6
    ▪ Using image quay.io/metallb/controller:v0.9.6
✅  metallb was successfully configured

In this example, we have specified 192.168.48.10 as the start IP address and 192.168.48.20 as the end IP address.

It’s important to note that we need to use the IP address range that matches the network range of the minikube node. In this case, the network range starts with the 192.168.48.xx

4.3. Creating a LoadBalancer Service

Now, let’s create the LoadBalancer service using the expose command:

$ kubectl expose deploy redis --port 6379 --type LoadBalancer -n service-demo
service/redis exposed

Next, let’s check the external IP address of the Redis service:

$ kubectl get service -n service-demo
NAME    TYPE           CLUSTER-IP      EXTERNAL-IP     PORT(S)          AGE
redis   LoadBalancer   10.102.24.187   192.168.49.10   6379:31569/TCP   10s

Here, we can see that the EXTERNAL-IP column shows the IP address of the LoadBalaner service, and it matches the IP address range we configured in the previous section.

Now, let’s access the Redis server from outside using the service’s external IP address:

$ redis-cli -h 192.168.49.10 PING
PONG

Here, we can see that the Redis server sends a PONG reply.

5. Cleaning Up

While setting up an example, we created a service-demo namespace and deployed a few Kubernetes objects into it. Now, let’s do the clean-up of those objects:

$ kubectl delete ns service-demo
namespace "service-demo" deleted

In this example, we deleted the namespace directly instead of deleting individual objects. This is acceptable only in the test environment, and one should never execute the same command in the production environment.

6. Conclusion

In this article, we discussed two methods to address the issue in minikube when the service shows its external IP as pending.

First, we used the tunnel command to assign an external IP address to the LoadBalancer service. Then, we used the metallb addon to achieve the same.