Securing DevOps Pipelines with Secure Source Code Repositories

How to Use Local Docker Images in Kubernetes

16080 VIEWS

· ·

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 to test-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!


Joseph is fluent in Fullstack web development and has a lot of passion for DevOps engineering. He loves to write code and document them in blogs to share ideas.


Discussion

Leave a Comment

Your email address will not be published. Required fields are marked *

Menu
Skip to toolbar