To create a cluster, by default, Minikube pulls images directly from the Docker registry. When creating a cluster, you must first upload the image to Docker Hub and then download the same image to run that cluster. While working with Kubernetes locally, Minikube doesn’t allow you to use the locally built image out of the box. Minikube leverages a local Docker registry that is isolated from your computer. We can use this strategy to create Kubernetes deployments without pushing the images to Docker Hub global registry. In this guide, we will learn how to use local Docker images in Kubernetes deployments using Minikube.
Before proceeding with this guide, it will be essential to have the following tools ready and working:
- Docker Engine should be up and running
- Ensure Minikube is installed and running with
minikube start
. - To run the Kubernetes command, you will require Kubectl installed and finally check it is correctly working using
kubectl cluster-info
. - You will need Node.js runtime to create a basic Node.js app.
If you get stuck configuring these tools, check this guide for more insights.
Along this guide, you will learn three different ways of how to use local Docker images in Kubernetes deployments using Minikube.
Running a Simple App With Docker
To get started, it will be essential to have a basic running application. This guide will use a simple Node.js server as follows:
- Create a directory to host your project.
- Run
npm init-y
to set up a Node.js app. - Install express using the
npm install express
command - Create an
index.js
and add the following code:
//Import the express package module here const express = require('express'); //Create an instance to initialize express const app = express(); //Define the port number const port = 3000; //Define a GET request handler app.get('/', (req, res) => { // Create and send the response to the GET endpoint res.send('Hello Kubernetes! Please Run Me Locally'); }); //Run the server on the port defined above app.listen(port, () => { //Add a log when the server starts console.log(`Server started on port ${port}`); });
Next, create a Dockerfile
on the project directory to dockerize the application as follows:
# Add the base image for the Node.js app FROM node:18-alpine3.15 # Create a working folder for running the application inside the Docker container WORKDIR /src/app # Copy dependencies to the above working folder COPY package.json ./ # Copy dependencies loom file to the working folder COPY package-lock.json . # Install the dependencies based on the above json files RUN npm install # Copy every code to the Docker working directory COPY ./ ./ # Tell Docker the container will listen on port 3000 EXPOSE 3000 # Run the the Node.js application CMD ["node", "index.js"] Let's now test if the above instructions are working. Go ahead and execute the following command to create the application image: docker build -t test-app .
To test it, run the create app image on a Docker Container:
docker run -it -p 3000:3000 test-app
Opening http://localhost:3000/
should serve the application as follows:
Running the Application With Kubernetes
You will need to create a k8s.yaml file as follows:
# First, add the API apiVersion: apps/v1 # This will be the deployment setup kind: Deployment metadata: # Name your Deployment here name: example-app-dep labels: # label your deployment app: example-test-app spec: # The number of pods/replicas to run replicas: 1 selector: matchLabels: # selector to match the pod app: example-test-app template: metadata: labels: # label your pod app: example-test-app spec: containers: # Add the container name for Kubernetes - name: test-app # Add the local image name image: test-app # never pull the image policy imagePullPolicy: Never ports: # port for running the container - containerPort: 3000 --- # First, add the Service API apiVersion: v1 # This will be the Service setup kind: Service metadata: # Your service name name: example-app-src spec: selector: # selector that matches the pod app: example-test-app # type of service type: LoadBalancer ports: - protocol: TCP # port for exposing the service port: 5000 # portfor exposing the pod targetPort: 3000 # port for exposing the node nodePort: 31110
The key difference is that,
imagePullPolicy
is set to Never. This will ensure Kubernetes uses locally built images instead of trying to pull them remotely from the Docker Hub registry.- The
image
is equivalent totest-app
, the name of the image you are running locally.
Deploy the application to a Kubernetes cluster by executing the appropriate command as follows:
kubectl create -f k8s.yaml
Upon execution, the command will give you results similar to:
deployment.apps/example-app-dep created service/example-app-src created
Test the functionality of the pod by executing the following command. Execute this the command to confirm if the pod is working properly:
kubectl get pods
This, however, will result in the following ErrImageNeverPull
error:
NAME READY STATUS RESTARTS AGE example-app-dep-6c75dfc85-vngwn 0/1 ErrImageNeverPull 0 77s
This is a result of the fact that Minikube cannot read locally built-images within Docker. Minikube uses its own local Docker registry that cannot pull an image directly. Instead, you need to reuse the Docker daemon from Minikube so that Minikube can pull images from the local machine. Below are three ways how to resolve this error.
Solution One: Setting up Minikube Environment Variables
This approach allows you to configure the Docker and make it available on the host machine’s terminal. You will be required to configure your shell as follows:
eval $(minikube docker-env) #for Unix shell minikube docker-env | Invoke-Expression # for windows PowerShell
This sets the environment variables for the Docker daemon to run inside the Minikube cluster. It allows you to use the Docker daemon inside the Minikube cluster from your host.
Note: Using this approach, all the subsequent commands should be executed with the shell you used to create the above environment variables.
Once set, re-run the build command as follows:
This will import the Docker image into Minikube.
This time the image will be created on the Docker daemon of Minikube. Run the following to verify this step:
minikube image ls
This should list the available images in the Docker daemon of Minikube as follow:
At this point, restart your pod using the following command.
kubectl rollout restart deployment example-app-dep
This will start the deployment:
deployment.apps/example-app-dep restarted
Check the pod again:
kubectl get pods
The ErrImageNeverPull
should be gone, as shown below:
NAME READY STATUS RESTARTS AGE example-app-dep-66d66c7b8c-lvbgv 1/1 Running 0 21s
To confirm the successful deployment of the service, you can run the following command to verify the deployment service:
minikube service example-app-src
The application is now correctly on Kubernetes using a locally built image:
To proceed to the next steps:
- Delete deployments
kubectl delete -n default deployment example-app-dep
- Delete services
kubectl delete -n default service example-app-src
- Delete the image built:
minikube image rm test-app
You can now also exit to the current shell using the following command:
eval $(minikube docker-env -u) #for Unix shell minikube docker-env -u | Invoke-Expression # for windows PowerShell
Solution Two: Using Minikube Load Command
This command enables the transfer of the image created on the local system to the registry within Minikube. This will step construct the Docker image as as usual:
docker build -t test-app .
Once done, copy the image to Minikube using the following command:
minikube image load test-app
This may take a few seconds to finish. Once done, run the ls command to verify if the image was indeed copied:
minikube image ls
You now run your deployment or restart your pod to test this out.
Solution Three: Build the Image Directly Using Minikube
Of all the above approaches, this seems to be the most straightforward. Instead of building the image on Docker, you can do the same for Minikube. Using the image build command of Minikube, you can build the image directly inside the Minikube container. This is the command you need to get the task done:
minikube image build -t test-app .
Conclusion
When working with Kubernetes, you can use comfortably locally built images without deploying them to Docker Hub. This guide has taught you how to use local Docker images in Kubernetes deployments using Minikube, and I hope you found it helpful!