The goal of this tutorial is for you to turn a simple Hello World Node.js app into an application running on Kubernetes. The tutorial shows you how to take code that you have developed on your machine, turn it into a Docker container image and then run that image on Minikube. Minikube provides a simple way of running Kubernetes on your local machine for free.
Note: If you see the following Homebrew error when you run
brew updateafter you update your computer to macOS 10.13:
Error: /usr/local is not writable. You should change the ownership and permissions of /usr/local back to your user account: sudo chown -R $(whoami) /usr/local
You can resolve the issue by reinstalling Homebrew:
/usr/bin/ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"
NodeJS is required to run the sample application.
Install Docker. On macOS, we recommend Docker for Mac.
This tutorial uses Minikube to create a local cluster. This tutorial also assumes you are using Docker for Mac on macOS. If you are on a different platform like Linux, or using VirtualBox instead of Docker for Mac, the instructions to install Minikube may be slightly different. For general Minikube installation instructions, see the Minikube installation guide.
Use Homebrew to install the latest Minikube release:
brew cask install minikube
Install the HyperKit driver, as described by the Minikube driver installation guide.
Use Homebrew to download the
kubectl command-line tool, which you can
use to interact with Kubernetes clusters:
brew install kubernetes-cli
Determine whether you can access sites like https://cloud.google.com/container-registry/ directly without a proxy, by opening a new terminal and using
curl --proxy "" https://cloud.google.com/container-registry/
Make sure that the Docker daemon is started. You can determine if docker is running by using a command such as:
If NO proxy is required, start the Minikube cluster:
minikube start --vm-driver=hyperkit
If a proxy server is required, use the following method to start Minikube cluster with proxy setting:
minikube start --vm-driver=hyperkit --docker-env HTTP_PROXY=http://your-http-proxy-host:your-http-proxy-port --docker-env HTTPS_PROXY=http(s)://your-https-proxy-host:your-https-proxy-port
--vm-driver=hyperkit flag specifies that you are using Docker for Mac. The
default VM driver is VirtualBox.
Now set the Minikube context. The context is what determines which cluster
kubectl is interacting with. You can see all your available contexts in the
kubectl config use-context minikube
kubectl is configured to communicate with your cluster:
Open the Kubernetes dashboard in a browser:
The next step is to write the application. Save this code in a folder named
with the filename
Run your application:
You should be able to see your “Hello World!” message at http://localhost:8080/.
Stop the running Node.js server by pressing Ctrl-C.
The next step is to package your application in a Docker container.
Create a file, also in the
hellonode folder, named
Dockerfile. A Dockerfile describes
the image that you want to build. You can build a Docker container image by extending an
existing image. The image in this tutorial extends an existing Node.js image.
This recipe for the Docker image starts from the official Node.js LTS image
found in the Docker registry, exposes port 8080, copies your
to the image and starts the Node.js server.
By default, Docker will create images and store them in your local machine’s Docker registry.
In this tutorial, we will not use your local machine’s Docker registry; we will use the Docker registry of the Docker daemon running inside Minikube’s vm instance. To point the ‘docker’ command to your Minikube’s Docker daemon, type (unix shells):
eval $(minikube docker-env)
or in powershell:
minikube docker-env | Invoke-Expression
Note: Later, when you no longer wish to use the Minikube host, you can undo this change by running
eval $(minikube docker-env -u).
Build your Docker image, using the Minikube Docker daemon (mind the trailing dot):
docker build -t hello-node:v1 .
check that the image is in Minikube’s Docker registry:
minikube ssh docker images
REPOSITORY TAG IMAGE ID CREATED SIZE hello-node v1 f82485ca953c 3 minutes ago 655MB ... node 6.9.2 faaadb4aaf9b 20 months ago 655MB
Now the Minikube VM can run the image you built.
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.
kubectl run command to create a Deployment that manages a Pod. The
Pod runs a Container based on your
hello-node: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):
kubectl run hello-node --image=hello-node:v1 --port=8080 --image-pull-policy=Never
View the Deployment:
kubectl get deployments
NAME DESIRED CURRENT UP-TO-DATE AVAILABLE AGE hello-node 1 1 1 1 3m
View the Pod:
kubectl get pods
NAME READY STATUS RESTARTS AGE hello-node-714049816-ztzrb 1/1 Running 0 6m
View cluster events:
kubectl get events
kubectl config view
For more information about
kubectlcommands, see the
By default, the Pod is only accessible by its internal IP address within the
Kubernetes cluster. To make the
hello-node Container accessible from outside the
Kubernetes virtual network, you have to expose the Pod as a
From your development machine, you can expose the Pod to the public internet
kubectl expose command:
kubectl expose deployment hello-node --type=LoadBalancer
View the Service you just created:
kubectl get services
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE hello-node ClusterIP 10.0.0.71 <pending> 8080/TCP 6m kubernetes ClusterIP 10.0.0.1 <none> 443/TCP 14d
--type=LoadBalancer flag indicates that you want to expose your Service
outside of the cluster. On cloud providers that support load balancers,
an external IP address would be provisioned to access the Service. On Minikube,
LoadBalancer type makes the Service accessible through the
minikube service hello-node
This automatically opens up a browser window using a local IP address that serves your app and shows the “Hello World” message.
Assuming you’ve sent requests to your new web service using the browser or curl, you should now be able to see some logs:
kubectl logs <POD-NAME>
server.js file to return a new message:
response.end('Hello World Again!');
Build a new version of your image (mind the trailing dot):
docker build -t hello-node:v2 .
Update the image of your Deployment:
kubectl set image deployment/hello-node hello-node=hello-node:v2
Run your app again to view the new message:
minikube service hello-node
Minikube has a set of built-in addons that can be enabled, disabled and opened in the local Kubernetes environment.
First list the currently supported addons:
minikube addons list
- storage-provisioner: enabled - kube-dns: enabled - registry: disabled - registry-creds: disabled - addon-manager: enabled - dashboard: disabled - default-storageclass: enabled - coredns: disabled - heapster: disabled - efk: disabled - ingress: disabled
Minikube must be running for these commands to take effect. To enable
heapster addon, for example:
minikube addons enable heapster
heapster was successfully enabled
View the Pod and Service you just created:
kubectl get po,svc -n kube-system
NAME READY STATUS RESTARTS AGE pod/heapster-zbwzv 1/1 Running 0 2m pod/influxdb-grafana-gtht9 2/2 Running 0 2m NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE service/heapster NodePort 10.0.0.52 <none> 80:31655/TCP 2m service/monitoring-grafana NodePort 10.0.0.33 <none> 80:30002/TCP 2m service/monitoring-influxdb ClusterIP 10.0.0.43 <none> 8083/TCP,8086/TCP 2m
Open the endpoint to interacting with heapster in a browser:
minikube addons open heapster
Opening kubernetes service kube-system/monitoring-grafana in default browser...
Now you can clean up the resources you created in your cluster:
kubectl delete service hello-node kubectl delete deployment hello-node
Optionally, force removal of the Docker images created:
docker rmi hello-node:v1 hello-node:v2 -f
Optionally, stop the Minikube VM:
minikube stop eval $(minikube docker-env -u)
Optionally, delete the Minikube VM: