Previously, Ive talked about how to get a NodeJS app running in a container, and today we're going to deploy that app to Kubernetes.
What is Kubernetes?
For those that haven't ventured into container orchestration, you're probably wondering what Kubernetes is.
Kubernetes is an open-source system for automating deployment, scaling, and management of containerized applications.
Kubernetes ("k8s" for short), was a project originally started at, and designed by Google, and is heavily influenced by Google's large scale cluster management system, Borg. More simply, k8s gives you a platform for managing and running your applications at scale across multiple physicaly (or virtual) machines.
Installing minikube and kubectl
To make things easy, we're going to use minikube on our local machine to run a single-node kubernetes cluster. Minikube is a handy tool that starts a virtual machine and bootstraps the cluster for you.
Firstly, if you dont have VirtualBox, go download and install it. While minikube works with other virtualization platforms, Ive found VirtualBox to be the most reliable.
Next, we need to install not only minikube, but also kubectl which will be used to interact with our k8s cluster. To do so, run the script below:
#!/bin/bash
ARCH=$(uname | awk '{print tolower($0)}')
TARGET_VERSION="v0.15.0"
MINIKUBE_URL="https://storage.googleapis.com/minikube/releases/${TARGET_VERSION}/minikube-${ARCH}-amd64"
KUBECTL_VER="v1.5.1"
KUBECTL_URL="http://storage.googleapis.com/kubernetes-release/release/${KUBECTL_VER}/bin/${ARCH}/amd64/kubectl"echo "installing latest kubectl..."
curl -Lo kubectl $KUBECTL_URL && chmod +x kubectl && sudo mv kubectl /usr/local/bin/
echo "installing latest minikube..."
curl -Lo minikube $MINIKUBE_URL && chmod +x minikube && sudo mv minikube /usr/local/bin/
ISO_URL="https://storage.googleapis.com/minikube/iso/minikube-v1.0.1.iso"
minikube start \
--vm-driver=virtualbox \
--iso-url=$ISO_URLecho "starting minikube dashboard..."
minikube dashboard
If everything has worked correctly, the kubernetes dashboard should open in your browser.
Using kubectl
When minikube starts, it will automatically set the context for kubectl. If you run kubectl get nodes
you should see something like this:
kubectl get nodesNAME STATUS AGEminikube Ready 2m
Same with if you run kubectl get pods --all-namespaces
:
kubectl get pods --all-namespaces
NAMESPACE NAME READY STATUS RESTARTS AGE
kube-system kube-addon-manager-minikube 1/1 Running 0 3m
kube-system kube-dns-v20-qkzgg 3/3 Running 0 3m
kube-system kubernetes-dashboard-1hs02 1/1 Running 0 3m
While the dashboard is useful for visualizing pods and deployments, we'll primarily be using kubectl to interact with our cluster.
The demo NodeJS app
Back in the article I wrote on deploying Docker containers with CoreOS Fleet, we wrote a little NodeJS server called "stupid-server" (for being stupidly simple). Stupid-server can be found over at github.com/seanmcgary/stupid-server and we'll be using it for this example as well. In the repository, you should find a server that looks something like this:
var http = require('http');
var server = http.createServer(function(req, res){
res.end(new Date().toISOString());
});
server.listen(8000);
And a Dockerfile that looks like this:
FROM quay.io/seanmcgary/nodejs-raw-base
MAINTAINER Sean McGary <sean@seanmcgary.com>
EXPOSE 8000ADD start.sh start.shRUN chmod +x start.shCMD ./start.sh
By default, the Dockerfile will on runtime, clone the repo and run the server. Feel free to edit the Dockerfile to add the repo you've already cloned to the container rather than pulling every time.
Build the container
To build the container, run:
CONTAINER_NAME="<container name>"
docker build -t $CONTAINER_NAME:latest .
docker push $CONTAINER_NAME:latest
Note - since k8s is running in it's own virtual machine, it doesn't have access to Docker images that you build. In order to proceed with this tutorial, you'll need to push your image to some place accessible by k8s. Dockerhub is available and free, but I would highly suggest Google's Container Registry which is extremely low cost and supports private images. You can find the gcr getting started guide over here.
Creating a deployment
To deploy our app, we're going to use the "Deployment" pod type. A deployment wraps the functionality of Pods and ReplicaSets to allow you to declaratively update your application. This is the magic that allows you to leverage zero-downtime deploys via Kubernetes' RollingUpdate functionality.
deployment.yaml
apiVersion: extensions/v1beta1kind: Deploymentmetadata:name: stupid-server-deploymentspec:replicas: 1template:metadata:labels:app: stupid-serverspec:containers:- name: stupid-serverimage: <container image>imagePullPolicy: Alwaysports:- containerPort: 8000# vim: set ts=2 expandtab!:
To deploy your deployment, run:
kubectl create -f deployment.yaml
To get your deployment with kubectl, run:
kubectl get deployments
NAME DESIRED CURRENT UP-TO-DATE AVAILABLE AGE
stupid-server-deployment 1 1 1 1 7m
This metadata will update as your deployment is created and pulls down containers.
Creating a service
Now that our application is deployed, we need a way to expose it to traffic from outside the cluster. To to this, we're going to create a Service. Since we're not covering IngressControllers and advanced load balancing in this tutorial, we're going to open up a NodePort directly to our application on port 30061.
service.yaml
apiVersion: v1kind: Servicemetadata:name: stupid-serverlabels:app: stupid-serverspec:selector:app: stupid-serverports:- port: 8000protocol: TCPnodePort: 30061type: LoadBalancer
Now we can create the service within Kubernetes:
kubectl create -f service.yaml
And we can get the details by running:
kubectl get services
NAME CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes 10.0.0.1 <none> 443/TCP 1h
stupid-server 10.0.0.121 <pending> 8000:30061/TCP 12m
Now, if we look at the ReplicaSet for our deployment, we should see something like this:
Accessing the stupid-server
In the Service we defined a NodePort; this exposes a port directly to the IP address that minikube is running on so that your app is accessible outside of the cluster.
By default, minikube binds to port 192.168.99.100
. To double check this, you can run minikube ip
which will return the current IP address.
To access your service, simply curl the IP on port 30061:
curl http://192.168.99.100:300612017-01-17T16:10:55.153Z
If everything is successful, you'll see a timestamp returned from your application.
Wrap up
This tutorial was meant as a very quick overview of how to get a NodeJS application up and running on Kubernetes with the least amount of configuration possible. Kubernetes is an incredibly powerful platform that has many more features than we used today. Stay tuned for more tutorials and articles on how to work with Kubernetes!