Implementing Hot Reloading in Kubernetes

Kasper Siig
Minute Read

Engineers have always tried to make developing applications as easy as possible. Over time, many different languages and frameworks have implemented hot reloading functionality, such as nodemon in Node.js. Simply put, hot reloading is the process of automatically rebuilding or recompiling your application when you save any changes to the application files.

The principle of hot reloading has been common in the developer community for many years, but using it when working with Kubernetes is less common. In this article, you'll be given a high-level overview of what it means to hot reload inside Kubernetes, and how it can be efficiently done using DevSpace.

Why Do You Need Hot Reloading?

To understand why hot reloading is useful, it's important to understand what the alternative is. Looking at the specific case of Kubernetes, there are quite a few steps needed if you want to directly test your application inside a cluster while in development.

First, you'll need to deploy the application. Assuming that you're doing this in the most efficient way possible, you'll likely be using Helm to do so. This way, you only need to run helm upgrade ... in order to deploy your application. With the application deployed, you can start to make changes to the application files. To test those changes, you need to rebuild the Docker image (assuming you're using Docker) and run the helm upgrade ... command again. On top of that, you have to move into a terminal window to run these commands.

When developing Kubernetes applications with a tool like DevSpace, the entire previous paragraph is automated. DevSpace takes care of rebuilding and redeploying your application as soon as you save the application file.

Hot Reloading With DevSpace

It's certainly possible to implement your own hot reloading solution, but the most effective solution is to use an existing tool like DevSpace that implements all the functionality in a simple-to-use CLI-based tool. All you have to do is execute a few commands in a terminal.

Now you'll look at an example of how hot reloading is done using DevSpace, after which you'll be given an explanation of how it works under the hood. You can find the code for this article in this GitHub Repo.

Using Hot Reloading

To follow along with this tutorial, you need to have npm and DevSpace installed.

To test this functionality, you need to have an example application. DevSpace supports many different programming languages, and this example will be based on Node.js. Start by creating a file called index.js with the following contents:

const express = require("express");
const app = express();
const port = 3000;

app.get("/", (req, res) => {
  res.send("Hello World!");
});

app.listen(port, () => {
  console.log(`Example app listening on port ${port}`);
});

There's no need to worry if you're not familiar with Node.js. To show the functionality of hot reloading, an example application is needed, but nothing much will be changed in the application. The code above is a simple "Hello World" example. To make sure that the application works, run npm install express.

Now it's time to add DevSpace to the project. This is done simply by running devspace init, which will give you the following output:

$ devspace init

     ____              ____
    |  _ \  _____   __/ ___| _ __   __ _  ___ ___
    | | | |/ _ \ \ / /\___ \| '_ \ / _` |/ __/ _ \
    | |_| |  __/\ V /  ___) | |_) | (_| | (_|  __/
    |____/ \___| \_/  |____/| .__/ \__,_|\___\___|
                            |_|


? How do you want to deploy this project?  [Use arrows to move, type to filter]
> helm: Use Component Helm Chart [QUICKSTART] (https://devspace.sh/component-chart/docs)
  helm: Use my own Helm chart (e.g. local via ./chart/ or any remote chart)
  kubectl: Use existing Kubernetes manifests (e.g. ./kube/deployment.yaml)
  kustomize: Use an existing Kustomization (e.g. ./kube/kustomization/)

For your own application you are free to choose any of these options, but for this tutorial, you should choose the default "Use Component Helm Chart" option. This will give you a Helm chart that's defined by DevSpace, and optimized for developing/deploying applications with DevSpace. The next option will be what Dockerfile you want to choose, and here you should choose "Create a new Dockerfile for this project".

? How should DevSpace build the container image for this project?  [Use arrows to move, type to filter]
> Create a new Dockerfile for this project
  Based on an existing Dockerfile within in this project (e.g. ./backend/Dockerfile)
  Using a custom build process (e.g. jib, bazel)

DevSpace should detect that it's a JavaScript project, and you should confirm this.

? Select the programming language of this project  [Use arrows to move, type to filter]
  dotnet
  go
  java
> javascript
  none
  php
  python

Moving on, you should choose what Docker registry you want to use. You can use any you like, but in this tutorial, Docker Hub is being used.

? Which registry would you want to use to push images to?  [Use arrows to move, type to filter]
> Use hub.docker.com => you are logged in as ksiig
  Use GitHub image registry
  Use other registry
  Skip Registry

Finally, you need to confirm with DevSpace that your application is listening on port 3000.

? Which port is your application listening on? 3000

With DevSpace initialized, you can start using it. Start by choosing the namespace for your application:

$ devspace use namespace hot-reload

Now you're ready to start using DevSpace to develop your application. Run devspace dev, and you'll see in your terminal that DevSpace is building your application and deploying it to Kubernetes. Once the application is built and deployed, DevSpace will open up a terminal session inside the container that's just been deployed to Kubernetes.

If you now open localhost:3000 in your browser, you'll notice that the application isn't running. To start it, you'll need to run npm run dev in the container terminal. Now you can make changes to the application files and see that DevSpace is taking care of syncing your local files into the container in Kubernetes, successfully enabling hot reloading for you.

How it Works

In your project you'll find a file called devspace.yaml. This file contains all of the configuration options that are relevant to DevSpace. While they are too extensive to go through in this article, if you're curious about the different parts of the file, you can read through the documentation.

The relevant part for this article starts on line 60 with replacePods. The first line is the imageSelector, which specifies that DevSpace will look for a container that has the image specified in this field to replace. Looking at the next line, you can see that the container in the pod will be replaced by a container that is based on the image loftsh/javascript:latest, which is a development optimized image provided by Loft. So far, this is fairly straightforward. However, the next part needs an explanation.

The patches section replaced two key values; command and args. As the file is written, the container will set PID 1 to be sleep 9999999, thereby replacing any command that would otherwise start the application. This allows you to control the behavior of the application through a terminal, as you saw earlier, and means that you can run tools like nodemon, which is the software taking care of watching the application files and rebuilding the application. DevSpace helps in this case by syncing your local files into the container, where nodemon can then detect file changes.

Container Restart Helper

When discussing hot reloading, it's worth mentioning another capability of DevSpace, which is the injectRestartHelper option. This is useful in the cases where you need to manually restart a container, but don't want to terminate and recreate the entire container.

The devspace.yaml file allows you to specify an images section, where you can select the images that need to be built when running devspace dev or devspace build. In this article, that would mean such a section could look like:

images:
  hot-reload:
    image: ${IMAGE}

Using the injectRestartHelper option is as easy as adding it to this section:

images:
  hot-reload:
    image: ${IMAGE}
    injectRestartHelper: true

Now you can restart the pod when it's running in Kubernetes by running devspace restart.

Conclusion

By now, you've learned a bit more about what hot reloading is, and how it can also be useful in Kubernetes. You also know how to implement this functionality by yourself, and how it can be more efficiently implemented by using a tool like DevSpace.

If this seems like it might be a useful functionality for you, take a look at what else DevSpace can do for your workflow.

Photo by Bernd Dittrich on Unsplash

Sign up for our newsletter

Be the first to know about new features, announcements and industry insights.