Kubernetes Development Blog
  • Docs
  • GitHub
  • Blog
  • Install

›Recent Posts

Recent Posts

  • DevOps and the Cloud: Tools and Technologies for Forward Looking DevOps Teams
  • The Missing Kubernetes Platform for Developers
  • How to Give Developers Access to Kubernetes During Development
  • How to deal with computing resource cost for Kubernetes-based development
  • Why Cloud Development Could (Finally) Become the New Standard

Deploy an Express.js Server to Kubernetes

October 15, 2019

Jérémy Lamy

Jérémy Lamy

The easiest way to containerize and deploy an Express.js server to Kubernetes using DevSpace.

Introduction

In this tutorial, we are going to see how to easily deploy an Express.js server to Kubernetes using DevSpace. Here are the steps to do this:

  1. Create a simple Express.js app (take your own if you already have one)
  2. Add nodemon for hot reloading
  3. Containerize the server (Dockerfile & Helm Chart)
  4. Deploy our application to Kubernetes
  5. Stream the logs of your deployment

Prerequisites

Make sure that you have the following installed:

  • Node
  • npm

We will use DevSpace which is a development tool for Kubernetes. Run this command to install DevSpace:

npm install -g devspace

1. Create an Express.js Server

You can skip this step if you want to deploy your own Express.js server.

In case you want to start from scratch, let's create a basic Express.js server. We start by creating a minimal project structure and then installing Express:

mkdir express-server 
cd express-server

# Create a package.json, -y flag to answer yes to all questions    
npm init -y

# Install and save express as dependency
npm install --save express

Let's finish the creation of our basic Express.js server by creating a file called index.js in the express-server directory:

const express = require('express');
const app = express();

app.get('/', function (req, res) {
  res.send('Hello World!');
})

app.listen(3000, function () {
  console.log('App listening on port 3000!');
})

As you can see, this simple Express.js server will be listening on port 3000. Now, we just need to tell npm how it should start this express application when running npm start. That's why we add start to the scripts section of our package.json file which should look like this afterward:

{
  "name": "express-server",
  "version": "1.0.0",
  "main": "index.js",
  "scripts": {
    "start": "node index.js",
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "description": "",
  "dependencies": {
    "express": "^4.17.1"
  }
}

2. Containerize the Express.js Server

Our application requires a Dockerfile and some Kubernetes manifests in order to run on Kubernetes. This is when DevSpace comes into play: it will autogenerate these files for us, thanks to a simple command:

devspace init

Create a Dockerfile

DevSpace automatically detects that there is no Dockerfile in our project root directory and shows several options to continue with. Select the first option Create a Dockerfile for me by hitting Enter and DevSpace will create a Dockerfile in your project directory.

? This project does not have a Dockerfile. What do you want to do?
  [Use arrows to move, space to select, type to filter]
> Create a Dockerfile for me                            
  Enter path to your Dockerfile
  Enter path to your Kubernetes manifests
  Enter path to your Helm chart
  Use existing image (e.g. from Docker Hub)

If there already is a Dockerfile in the project folder, DevSpace will detect it and suggest to use it instead of creating a new one.

Select The Programming Language

Our programming language will also be detected by DevSpace, so we can confirm that we are using javascript by hitting Enter.

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

Choose an Image Registry

DevSpace is going to build a Docker image from our newly created Dockerfile. This image needs to be pushed somewhere. We get the following options:

? Which registry do you want to use for storing your Docker images?
  [Use arrows to move, space to select, type to filter]
  Use hub.docker.com
> Use dscr.io (free, private Docker registry)
  Use other registry

If you have no clue which registry to use, you can just use dscr.io. It is free and allows you to push the images to a private registry. DevSpace will then open a login page and allow you to sign up for dscr.io.

Define The Application Port

Enter the port of the Express.js server (remember, we specified it via app.listen() in index.js). So in our case, we need to enter 3000:

? Which port is your application listening on? (Enter to skip) 3000

Project Successfully Initialized

Once the init command has terminated, you will find the following new files in your project folder:

express-server/     # Your project directory
|
|--devspace.yaml    # DevSpace config file (defines which images to build and how to deploy your project)
|--Dockerfile       # Defines how to build the Docker image
|--.dockerignore    # Defines paths to be ignored when building the Docker image
|--.gitignore       # Defines paths to be ignored when commiting your code via git

Besides the configuration for DevSpace in devspace.yaml, there is a Dockerfile for building a Docker image for our Express.js server before deploying the app to Kubernetes.

Dockerfile

The autogenerated Dockerfile will look like this:

FROM node:8.11.4

# Create project directory (workdir)
RUN mkdir /app
WORKDIR /app

# Add package.json to WORKDIR and install dependencies
COPY package.json .
RUN npm install

# Add the remaining source code files to WORKDIR
COPY . .

# Start the application
CMD ["npm", "start"]

This Dockerfile:

  • defines node as base image
  • creates the working directory /app
  • copies the package.json into the image and installs the dependencies
  • copies the rest of our application into the working directory
  • defines npm start as the start command for the container

Adding the package.json separately and installing dependencies before adding the rest of the application allows Docker to use layer-caching and saves a lot of time when building the image because the dependencies will only have to be re-installed when the package.json has changed and not every time we change any other source code file. Not having to install the dependencies every time we re-build and re-deploy our application saves a lot of time.

Note that this Dockerfile starts our Express.js server in development mode. For production, it is recommended to also set the environment variable NODE_ENV=production which would be possible using a separate Dockerfile just for production.

Component Helm Chart

By default, DevSpace deploys your application as a so-called component that uses a highly configurable Helm chart. To customize the deployment of your app, you can edit the values section of the deployments section within the devspace.yaml configuration file:

...
deployments:
- name: express-server
  helm:
    chart:
      name: component-chart
      version: v0.0.6
      repo: https://charts.devspace.cloud
    values:
      containers:
      - image: dscr.io/${DEVSPACE_USERNAME}/devspace
      service:
        ports:
        - port: 3000
...

For more information on how to customize this component, take a look at customizing DevSpace components.


3. Deploy The Express.js Server

Now that our Express server is containerized, we are ready to deploy it to Kubernetes. DevSpace works with every Kubernetes cluster, no matter if it is running locally, in a public cloud or a private cloud. You can also use virtual Kubernetes clusters that are provided by loft.

The loft-devspace-plugin even allows you to create virtual Clusters with DevSpace. For this, you or your admin need to connect a cluster to loft first and then you install the loft-devspace-plugin with the command devspace add plugin https://github.com/loft-sh/loft-devspace-plugin. Afterwards, you and your teammates can create virtual clusters and isolated namespaces on a shared physical cluster.

When you have a Kubernetes cluster set up and the kube-context configured on your local machine, you can simply run this command:

devspace use namespace my-express-server

DevSpace will automatically create this namespace during the deployment process.

Build & Deploy Your App

DevSpace automates the entire deployment process (image building, pushing images to the registry, creating resources in Kubernetes etc.), so you can just run one single command to deploy the Express.js server:

devspace deploy

This command takes a little while when you run it for the very first time. If you run it later again, it will be much quicker.

Now, it's time to take a look at the deployment in the browser:

devspace open

When DevSpace asks you how to open your application, choose the first option: via localhost

? How do you want to open your application?
  [Use arrows to move, space to select, type to filter]
> via localhost (provides private access only on your computer via port-forwarding) # <<<<<<<< CHOOSE THIS ONE!
  via domain (makes your application publicly available via ingress)
Don't miss any new blog posts!
Subscriptions

4. Start Development

If we want to develop this application instead of just running it, we can add nodemon as hot reloading tool and use the file-synchronization of DevSpace to update the deployment within seconds.

Add nodemon for Hot Reloading

Our server is running nicely; nevertheless, if you tried to mess around and modify anything to the index.js, you might have noticed that the changes are not applied, unless we run devspace deploy again. This is a very tedious task that has a negative impact on our development workflow and efficiency, hence it needs to be fixed.

Luckily, there is a nice package that was created to overcome this problem: nodemon. Once installed, it will automatically restart our Node.js application when file changes are detected in the directory. So, let's install it:

npm install --save-dev nodemon

Instead of running node index.js, we can now run:

nodemon index.js

Change The Start Command

Let's assume that you are in a small team, not everyone is supposed to know the specific command to use in order to start the app correctly. The best practice in this case is to add our nodemon index.js command in the scripts of the package.json:

"scripts": {
  "start": "nodemon index.js"
},

This is only suitable for development. For production, you should add another script, e.g. "start-prod": "node index.js".

Start Development Mode

You can now just run this command to develop directly inside the Kubernetes cluster:

devspace dev -b

In addition to deploying our Express.js server to Kubernetes, the devspace dev command also starts a powerful development UI which runs on localhost and is available as long as the devspace dev command is running.

You should see a log statement similar to this one in the output of devspace dev:

#########################################################
[info]   DevSpace UI available at: http://localhost:8090
#########################################################

So go ahead and check out the DevSpace UI in your browser! You can use it to see the container and pod status of your deployments as well as to stream logs and open interactive terminal sessions for every container. It should look like in this screencast:

DevSpace: Kubernetes Development UI - Screencast

Now, let's change something in our app to check if the hot reloading is working. For instance, replace Hello World! with Bye World! and then save the file. If it's working, a message similar to the one below should be displayed in the console:

[nodemon] restarting due to changes...
[nodemon] starting `node index.js`
App listening on port 3000!

Refresh your web page at http://localhost:3000/ and you should see your changes without having to restart the app manually!

Happy Coding!


I hope you liked this tutorial and were able to deploy your first Express.js app to Kubernetes. Leave a comment, clap for this post on Medium or share the link with your colleagues, if this article was helpful for you.

Tweet
Recent Posts
  • Introduction
  • Prerequisites
  • 1. Create an Express.js Server
  • 2. Containerize the Express.js Server
    • Create a Dockerfile
    • Select The Programming Language
    • Choose an Image Registry
    • Define The Application Port
    • Project Successfully Initialized
  • 3. Deploy The Express.js Server
    • Build & Deploy Your App
  • 4. Start Development
    • Add nodemon for Hot Reloading
    • Change The Start Command
    • Start Development Mode
Star
Don't miss new blog posts and subscribe here:
Subscriptions