Skip to content
Mighten's Blog

KIA CH02 First Steps with Docker and Kubernetes

Ch 2 notes for Kubernetes in Action. Key topics include: creating and sharing Docker container images, running a local single-node cluster via Minikube, configuring the kubectl CLI, and deploying/horizontally scaling a Kubernetes application.

k8s-in-action 8 min read

Hi there.

Today, let us read the Chapter 02: First Steps with Docker and Kubernetes (Part I: Overview) of Kubernetes in Action

  1. Creating, running, and sharing a container image with Docker
  2. Running a single-node Kubernetes cluster locally, with Minikube
  3. Setting up and using the kubectl command-line client
  4. Deploying an app on Kubernetes and scaling it horizontally

busybox is a single executable that combines many of the standard UNIX command-line tools, such as echo, ls, gzip, etc.

docker run command will download and run the container.

Terminal window
# docker run <image>:<tag>
docker run busybox echo "Hello world"
  1. Docker checks the presence of image busybox:latest in local machine. (image = busybox, tag is latest by default)
    • If yes, continue to step 2
    • If no, Docker pulls it from Docker Hub registry (locally stored layer will NOT be pulled)
  2. Docker creates a container from that image
  3. Docker runs the echo "Hello world" command inside container

Then the app was executed inside a container, completely isolated from all the other processes running on your machine.

  1. write the app Use the following code to start an HTTP server on port 8080, and then to reply with "You've hit " + ${actual hostname of a server} and status code 200 OK:

    // app.js - a demo Node.js app
    const http = require('http');
    const os = require('os');
    console.log('kubia server starting...');
    var handler = function (request, response) {
    console.log('Received request from ' + request.connection.remoteAddress);
    response.writeHead(200);
    response.end("You've hit " + os.hostname() + '\n');
    };
    var www = http.createServer(handler);
    www.listen(8080);
  2. write Dockerfile

    A Dockerfile is a text file that contains all the commands to build a given image. And in the Dockerfile, each individual command creates a new layer. The topmost layer a Dockerfile created will be tagged as latest.

    Use the following Dockerfile to package the app into an image:

    FROM node:7
    ADD app.js /app.js
    ENTRYPOINT ["node", "app.js"]
    • the FROM to define the base image(image as a starting point)
    • the ADD to add your app.js file from your local directory into the root directory in the image, creating a new layer on top of the base image
    • the ENTRYPOINT command denotes that command node app.js will be executed upon starting the image, thereby creating an additional layer on top of the newly created layer, which will then be tagged as latest.
  3. build the container image

    use “docker build” command to build the container image out of Dockerfile by pulling/creating layers:

    Terminal window
    docker build -t kubia .
    • image name: kubia
    • where is the Dockerfile located: ‘.’ (the current working directory)

    Docker Build Workflow

    Use “docker images” to list the newly built Docker image:

    REPOSITORYTAGIMAGE IDCREATEDVIRTUAL SIZE
    kubialatestd30ecc7419e71 minute ago637.1 MB
  4. run the container

    Use “docker run” to run the image

    Terminal window
    docker run --name kubia-container -p 80:8080 -d kubia
    • name of container is kubia-container, started from image named kubia
    • -d: container is detached from console
    • -p 80:8080: the container’s internal port 8080 is mapped to port 80 on the host
    • the ‘kubia’ app can thus be accessed by http://localhost:80

    Use “docker ps” to list running containers

    CONTAINER IDIMAGECOMMANDCREATEDSTATUSPORTSNAMES
    44d76963e8e1kubia:latest”/bin/sh -c ‘node app.js’6 minutes agoUp 6 minutes0.0.0.0:80->8080/tcpkubia-container

    Use “docker inspect” to see detailed info about the docker container in JSON

    Use “docker exec” to run the shell inside the container

    Terminal window
    docker exec -it kubia-container /bin/bash
    • -i: makes sure STDIN is kept open
    • -t: allocates a pseudo terminal (TTY)

    Now that the app is running inside the container 44d76963e8e1, we can find it by running ps aux.

    • isolated process tree
      • PID Linux namespace
      • processes running in the container are running in the host OS, but with different process ID (PID)
    • isolated filesystem
  5. stops and removes a container Use “docker stop” to stop a docker

    Terminal window
    docker stop kubia-container

    Use “docker rm” to remove the container that still exists in “docker ps -a” command

    Terminal window
    docker rm kubia-container

To make the local image (e.g., the image built in section 2.1.4) available to others, we need to push the image to an external image registry.

There are 3 steps to push image to registry

  1. Tag an image

    Use “docker tag” to create an additional tag for the same image

    Terminal window
    docker tag kubia ${yourDockerRegistryID}/kubia
  2. Log into Docker Registry

    Use “docker login” to log into the Docker Hub Registry, or other Registries by:

    Terminal window
    docker login -u username ${DockerRegistryURL}
  3. Push the image Use “docker push” to push image to Registry

    Terminal window
    docker push ${yourDockerRegistryID}/kubia

Minikube is a tool that sets up a single-node cluster for both testing Kubernetes and developing apps locally.

The minikube documentation provides detailed installation guide. For example, I will use Debian package in x64-based Ubuntu Linux:

Terminal window
curl -LO https://storage.googleapis.com/minikube/releases/latest/minikube_latest_amd64.deb
sudo dpkg -i minikube_latest_amd64.deb
  1. start a Kubernetes cluster with Minikube

    Terminal window
    minikube start
  2. display cluster info

    Terminal window
    minikube kubectl -- cluster-info

    to explore inside cluster, use minikube ssh to log into the Minikube VM with SSH

kubectl is a command line tool for communicating with control plane of a Kubernetes cluster, using the Kubernetes API.

Use kubectl to list all nodes in the cluster

  • kubectl cluster-info”: to show basic information about a Kubernetes cluster
  • kubectl get nodes”: to show the most basic information for the listed objects
  • kubectl describe node ${nameOfNode}”: to show the status of a specific node, including CPU and memory data, system information, containers running on the node, etc.

Kubernetes uses Pod to deal with a group of containers (rather than dealing with individual containers directly)

  • same pod: containers running on the same logical machine
  • different pod: containers running on the different logical machines, even these containers are running on the same worker node

Each pod behaves like a separate independent machine with its own IP address and hostname.

Relationship among Containers, Pods, and Worker Nodes

Use “kubectl get pods” to show basic info about pods, and use “kubectl describe pod”.

This section require experiments on cloud platform, and the minikube may not fit this section, because the minikube does not support “LoadBalancer” service.

Use “kubectl create deployment” to create a deployment

Terminal window
kubectl create deployment kubia --image=${YourDockerRegistryId}/kubia --port=8080
# (deprecated): kubectl run kubia --image=${YourDockerRegistryId}/kubia --port=8080 --generator=run/v1

The diagram shown below is about how a container runs in Kubernetes:

Container Runs in Kubernetes

  • what happens behind the scene:
    1. kubectl will create a new ReplicationController object in the cluster, by sending a REST HTTP request to the Kubernetes API server
    2. ReplicationController then creates a new pod
    3. Scheduler schedules the new pod to one of the worker nodes
    4. kubelet of the scheduled worker node instructs Docker to pull image from registry if the specified image is not available locally
    5. Docker creates and runs the container

There are 4 types of services:

  • ClusterIP: A regular service, just like the pod, is only accessible from inside the cluster.
  • NodePort
  • LoadBalancer: creating an external load balancer, and allowing external access to the pod via the public IP
    • Note: Minikube does NOT support LoadBalance service
  • ExternalName

And this section mainly covers how to use LoadBalancer service to allow external access of the Node.js app:

  1. Use “kubectl create” to create a LoadBalancer service
Terminal window
kubectl expose deployment kubia --type=LoadBalancer --name kubia-http
# (deprecated): kubectl expose rc kubia --type=LoadBalancer --name kubia-http
  • minikube does NOT support LoadBalance service, so the “EXTERNAL-IP” of command “kubectl get services” will always be pending.
  • "Replication Controller" has been deprecated in newer version of kubectl
  1. Use “kubectl get services” command to see the newly created Service object

    • cluster IP
    • external IP
    • port
    • age
  2. Access the service via external IP

There are 2 views of the system:

  • Physical view
    • Worker nodes (or nodes)
    • Control Plane (Master) node(s)
  • Logical View
    • Pods
      • each pod has its unique private IP address
    • ReplicationControllers
      • replicate pods to the exactly number of replicas(by default: 1 replica).
      • monitor and keep pods running
        • creating a new pod to replace the missing one
    • Services
      • expose multiple pods at a single constant <IP, port> pair
      • offer a static IP to forward user requests to pods, regardless of IP addresses of pods

A System of 1 Replication Controller 1 Pod and 1 Service

To scale up the number of pods:

  1. use “kubectl get rc” or “kubectl get replicationcontrollers” to get the number of replicas

    NAMEDESIREDCURRENTAGE
    kubia1117m
    • DESIRED: the number of pod replicas you want the ReplicationController to keep
    • CURRENT: the actual number of pods currently running
  2. use “kubectl scale rc kubia --replicas=3” command to increase the DESIRED replica count of kubia to 3

    • inspect with “kubectl get rc
      NAMEDESIREDCURRENTREADYAGE
      kubia11217m
    • inspect with “kubectl get pods
      NAMEREADYSTATUSRESTARTSAGE
      kubia-hczji1/1Running07s
      kubia-iq9y60/1Pending07s
      kubia-4jfyf1/1Running018m

A System of 1 RC 3 Pods and 1 Service

In general, there is no need to care about which worker node a pod is scheduled to, yet there is still a way to find out:

  1. use “kubectl get pods -o wide” command to show list pods with IP and worker node

    • -o wide option is used to display additional columns based on “kubectl get pods” command
  2. use “kubectl describe pod kubia-hczji” command to check which worker node the pod kubia-hczji is scheduled to.

Kubernetes dashboard allows you to list all the Pods, ReplicationControllers, Services, and other objects deployed in your cluster, as well as to create, modify, and delete them.

Use “minikube dashboard” to open Kubernetes Dashboard in browser.

Terminal window
# 1) start Minikube Dashboard
minikube dashboard
# 2) add proxy to allow external access (outside VM where Minikube is installed)
kubectl proxy --port=8001 --address='0.0.0.0' --accept-hosts='^.*' &

Then dashboard can be accessed by:

http://${YourVmAddress}:8001/api/v1/namespaces/kubernetes-dashboard/services/http:kubernetes-dashboard:/proxy/

In this chapter, there are mainly two topics:

  • how to run a app in Docker, and then package it into a Docker Image
  • how to use Kubernetes with Minikube and kubectl
    • single node Kubernetes cluster
    • scaling the Kubernetes cluster
      • one control plane node
      • three worker nodes

OriginalAbbreviation
podspo
replication controller(deprecated)rc
servicessvc

best practice about alias in Linux terminal:

Terminal window
# use `k` instead of `kubectl`
alias k=kubectl

Note: if you use k instead of kubectl, you should execute the following commands to enable command autocompletion:

Terminal window
# 1) allow command completion for `kubectl`
source <(kubectl completion bash)
# 2) allow command completion for `k`
source <(kubectl completion bash | sed s/kubectl/k/g)