DEV Community

Nick Parsons
Nick Parsons

Posted on

Containerizing a Node.js API & Using Docker with Kubernetes and Minikube

As a Developer Evangelist at Stream, I have the opportunity to learn about tons of awesome, new technologies that our engineering team and customers are working with on a daily basis. The amount of knowledge out there to soak in is absolutely astonishing, and I like to take every opportunity to learn about all of the up-and-coming tools and toys.

One of the most talked about combos as of late is Docker and Kubernetes. Docker and Kubernetes are a powerhouse that makes it infinitely easier to develop fast, immutable applications capable of running on multiple operating systems, without all the hassle of handling all the requirements of package management. Docker packages all of the requirements for any given operating system in your Docker container and, with a few Kubernetes commands, your application can be served to users, AND with immutable pods that can be killed and brought up at any time with a single Kubernetes command.

In this post, I’ll walk you through how I containerized an application with Docker and served it locally using Kubernetes and Minikube. In the end, you’ll walk away with enough knowledge to do the same and, hopefully, take it to the next level by launching your own app in the cloud with Kubernetes.

Requirements

As with any tutorial, there are a few requirements we toss out to set you up for success; we want to ensure that you, the reader, can follow along without getting lost 😉.

The most important piece to note about this post is that it’s intended for users on macOS. You can still follow along if you’re on Windows or Linux; however, my commands will be slightly different than the ones you’ll want to use.

Step 1: Homebrew

If you don’t have homebrew installed on your computer, you’ll want to get that installed using the install command shown on the https://brew.sh/ website.

Step 2: Docker

To install Docker, head over to the Getting Started page and click “Download for Mac”. You will be redirected to another page where you may have to create an account first.

Step 3: Minikube

This tutorial uses Minikube to create a local cluster. With that said, let’s install Minikube:

brew cask install minikube

Step 4: Hyperkit

Install the HyperKit driver, as described by the Minikube driver installation guide. Or, run this command (if you’re on macOS) to install the Hyperkit driver:

curl -Lo docker-machine-driver-hyperkit https://storage.googleapis.com/minikube/releases/latest/docker-machine-driver-hyperkit \
&& chmod +x docker-machine-driver-hyperkit \
&& sudo cp docker-machine-driver-hyperkit /usr/local/bin/ \
&& rm docker-machine-driver-hyperkit \
&& sudo chown root:wheel /usr/local/bin/docker-machine-driver-hyperkit \
&& sudo chmod u+s /usr/local/bin/docker-machine-driver-hyperkit

Step 5: Kubernetes CLI

Use Homebrew to download the kubectl command-line tool, which you can use to interact with Kubernetes clusters:

brew install kubernetes-cli

Cloning the Boilerplate Express API 💾

Now that you’ve completed the steps to get Docker and Kubernetes up and running, let’s go ahead and download a boilerplate Express API that I’ve put together. You can use your own, however, I would suggest trying this, first, until you get the hang of the commands that we’ll be using.

Head over to a directory of your choice where you want to store the code. Then go ahead and run the following command to clone the repo to your directory:

git clone git@github.com:nparsons08/boilerplate-express-api.git api

Note: If you want to have a look at the repo first, click here.

Next, move into the directory, build the API and launch it to test:

cd api && yarn build && yarn start

A window should automatically open with the API and you should see something identical to this:

Containerizing the API with Docker 📦

I’ll admit it, when I first looked at Docker, I was a bit scared. The concept didn’t quite make sense to me, and the commands looked completely foreign. After a bit of reading and playing around in the docs, however, I started to understand the fundamentals of working with Docker — and you will too. For now, we’re going to keep things simple; in future posts, we’ll go ahead and take things to the next level 😀.

Note: All commands should be run in the terminal.

Step 1: Understanding the Dockerfile (a template already exists within the directory):

# use latest version of node
FROM mhart/alpine-node:latest

# set working directory
WORKDIR /dist

# bundle source code
COPY . .

# expose port 3000
EXPOSE 3000

# start app with yarn
CMD ["yarn", "start"]

Step 2: Building a Docker Image:

docker build -t boilerplate-api/api .

Step 3: List all Docker Images (to verify yours exists):

# list all docker images
docker images

Step 4: Run the Docker Image:

# run the docker image
docker run -p 8080:3000 -d boilerplate-api/api

Step 5: View Running API:

# open in browser
open http://localhost:8080

Boom 💥! You just launched the API using Docker! Now let’s tear it down, as we’ll be using Kubernetes and Minikube to launch the Docker container here in a couple of minutes.

Step 6: Tear It Down:

# stop all containers
docker stop $(docker ps -a -q)

# destroy all containers
docker rm $(docker ps -a -q)

# destroy all images
docker rmi $(docker images -q)

Stepping Into the Minikube & Kubernetes Land 👣

Minikube is a tool that makes it easy to run Kubernetes locally. Minikube runs a single-node Kubernetes cluster inside a VM on your laptop.

Setting Up Minikube

Determine whether you can access sites like https://cloud.google.com/container-registry/ directly, without a proxy, by opening a new terminal and using the following command:

# check that you have access to google's container registry
curl --proxy "" https://cloud.google.com/container-registry/

Note: If this cURL command does not work, please stop and try to fix the issue. Minikube won’t be able to start properly without access to the external internet (for this tutorial).

The cURL command should kick back a bunch of HTML that looks something like this:

Note: If you cannot access the Google Container Registry, there is an issue with your connection. You’ll need to debug this before you can move on.

Next, make sure that the Docker daemon is started. You can determine if docker is running by using a command such as:

# quick check if docker is running
docker images

Note: If the command was successful, you should see a list of Docker images in your terminal. If you haven’t you will need to debug why Docker is not running on your machine.

Now that you’ve verified that Docker is running we can kickoff a Minikube process by using the following command:

# start minikube with kyperkit specified
minikube start --vm-driver=hyperkit

Note: The --vm-driver=hyperkit flag specifies that you are using Docker for macOS. The default VM driver is actually VirtualBox, but HyperKit is preferred on macOS.

If successful, your terminal will look exactly like this:

Now set the Minikube context. The context is what determines which cluster kubectlis interacting with. We’ll use the following command to do exactly that:

# specify context
kubectl config use-context minikube

Verify that kubectl is configured to communicate with your cluster:

# get cluster info
kubectl cluster-info

Start the Minikube Dashboard

Note: Minikube comes bundled with a dashboard that you so you can visualize everything that is going on!

Now, let’s go ahead and start the dashboard!

# start minikube dashboard
minikube dashboard

Alright, you’ve made it this far. Let’s continue on!

Create a Docker Image

To keep things simple, let’s go ahead and use the Boilerplate API that we used previously in this article.

Because this tutorial uses Minikube, instead of pushing your Docker image to a registry, you can simply build the image using the same Docker host as the Minikube VM, so that the images are automatically present. To do so, make sure you are using the Minikube Docker daemon:

# set the docker daemon to minikube
eval $(minikube docker-env)

Now that our daemon is set for Docker, we can continue with creating a Docker Image. Head over to the /api directory that we created earlier and run the following command:

# build docker image
docker build -t api:v1 .

Note: Now the Minikube VM can run the image you build.

The output of the command should look like this:

Create a Kubernetes Deployment 💻

Minikube is running, our Docker image is created, things are going well. Let’s quickly discuss the anatomy of a Kubernetes deployment.

A Kubernetes Pod is a group of one or more Containers, tied together for the purposes of administration and networking. The Pod in this tutorial has only one Container. A Kubernetes Deployment checks on the health of your Pod and restarts the Pod’s Container if it terminates. Deployments are the recommended way to manage the creation and scaling of Pods.

Use the kubectl run command to create a Deployment that manages a Pod. The Pod runs a Container based on your api:v1 Docker image. Set the --image-pull-policy flag to Never to always use the local image, rather than pulling it from your Docker registry (since you haven’t pushed it there):

# create a kubernetes deployment
kubectl run api --image=api:v1 --port=8080 --image-pull-policy=Never

Now we can view the deployment using the following command:

# get kubernetes deployments
kubectl get deployments

And, if you visit your dashboard (run the command minikube dashboard in your terminal), you’ll see green!

You can even visit API at http://localhost:8080!

Note: When you no longer wish to use the Minikube host, you can undo this change by running eval $(minikube docker-env -u).

Congratulations! 🎉

Congratulations! You just containerized an API with Docker, spun up Minikube, and deployed the Docker image to Kubernetes on your local machine.

Job well done! In future posts, we’ll go into detail on how to containerize and run an application that is stateless but requires access to external services such as Stream, MongoDB, Redis, etc.

Until then, I recommend the following articles to improve your skills:

Happy Coding! 👏

Top comments (0)