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.
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.
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.
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.
This tutorial uses Minikube to create a local cluster. With that said, let’s install Minikube:
brew cask install minikube
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
Use Homebrew to download the kubectl command-line tool, which you can use to interact with Kubernetes clusters:
brew install kubernetes-cli
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 firstname.lastname@example.org:nparsons08/boilerplate-express-api.git api
Note: If you want to have a look at the repo first, click here.
cd api && yarn build && yarn start
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.
# 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"]
docker build -t boilerplate-api/api .
# list all docker images docker images
# run the docker image docker run -p 8080:3000 -d boilerplate-api/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.
# 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)
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.
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
--vm-driver=hyperkitflag 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
kubectl is configured to communicate with your cluster:
# get cluster info kubectl cluster-info
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!
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:
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! 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! 👏