KIA CH02 First Steps with Docker and Kubernetes
Hi there.
Today, let us read the Chapter 02: First Steps with Docker and Kubernetes (Part I: Overview) of Kubernetes in Action
- Creating, running, and sharing a container image with Docker
- Running a single-node Kubernetes cluster locally, with Minikube
- Setting up and using the kubectl command-line client
- Deploying an app on Kubernetes and scaling it horizontally
2.1 Creating, running, and sharing a container image
2.1.1 Running a Hello World container
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.
1# docker run <image>:<tag>
2docker run busybox echo "Hello world"
- Docker checks the presence of image
busybox:latest
in local machine. (image = busybox, tag islatest
by default)- If yes, continue to step 2
- If no, Docker pulls it from Docker Hub registry (locally stored layer will NOT be pulled)
- Docker creates a container from that image
- 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.
2.1.2-7 Running a Node.js container
-
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 code200 OK
:1// app.js - a demo Node.js app 2const http = require('http'); 3const os = require('os'); 4 5console.log("kubia server starting..."); 6 7var handler = function(request, response) { 8console.log("Received request from " + request.connection.remoteAddress); 9response.writeHead(200); 10response.end("You've hit " + os.hostname() + "\n"); 11}; 12 13var www = http.createServer(handler); 14www.listen(8080);
-
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:
1FROM node:7 2ADD app.js /app.js 3ENTRYPOINT ["node", "app.js"]
- the
FROM
to define the base image(image as a starting point) - the
ADD
to add yourapp.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 commandnode 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.
- the
-
build the container image
use "
docker build
" command to build the container image out of Dockerfile by pulling/creating layers:1docker build -t kubia .
- image name:
kubia
- where is the Dockerfile located: '
.
' (the current working directory)
@startuml actor developer usecase "Docker client" as client usecase "Docker daemon" as daemon database "Docker Hub" as hub node "output image" as output developer-->client: 1. runs `docker build kubia .` client-->daemon: 2. uploads 'Dockerfile' and 'app.js' daemon<--hub: 3. pulls layers of base image if not stored locally daemon-->output: 4. builds new image @enduml
Use "
docker images
" to list the newly built Docker image:REPOSITORY TAG IMAGE ID CREATED VIRTUAL SIZE kubia latest d30ecc7419e7 1 minute ago 637.1 MB - image name:
-
run the container
Use "
docker run
" to run the image1docker run --name kubia-container -p 80:8080 -d kubia
- name of container is
kubia-container
, started from image namedkubia
-d
: container is detached from console-p 80:8080
: the container's internal port8080
is mapped to port80
on the host- the 'kubia' app can thus be accessed by
http://localhost:80
Use "
docker ps
" to list running containersCONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 44d76963e8e1 kubia:latest "/bin/sh -c 'node app.js' 6 minutes ago Up 6 minutes 0.0.0.0:80->8080/tcp kubia-container Use "
docker inspect
" to see detailed info about the docker container in JSONUse "
docker exec
" to run the shell inside the container1docker 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 runningps 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
- name of container is
-
stops and removes a container Use "
docker stop
" to stop a docker1docker stop kubia-container
Use "
docker rm
" to remove the container that still exists in "docker ps -a
" command1docker rm kubia-container
2.1.8 Pushing the image to an image registry
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.
- private Docker Registry
- Docker Hub Registry
- Google Artifact Registry
- Red Hat Quay.io
There are 3 steps to push image to registry
-
Tag an image
Use "
docker tag
" to create an additional tag for the same image1docker tag kubia ${yourDockerRegistryID}/kubia
-
Log into Docker Registry
Use "
docker login
" to log into the Docker Hub Registry, or other Registries by:1docker login -u username ${DockerRegistryURL}
-
Push the image Use "
docker push
" to push image to Registry1docker push ${yourDockerRegistryID}/kubia
2.2 Setting up a Kubernetes cluster
2.2.1 Running a local single-node Kubernetes cluster with Minikube
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:
1curl -LO https://storage.googleapis.com/minikube/releases/latest/minikube_latest_amd64.deb
2sudo dpkg -i minikube_latest_amd64.deb
-
start a Kubernetes cluster with Minikube
1minikube start
-
display cluster info
1minikube kubectl -- cluster-info
to explore inside cluster, use
minikube ssh
to log into the Minikube VM with SSH
2.2.2 Using a hosted Kubernetes cluster of Cloud Provider
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.
@startuml
caption "Figure 2.1 The relationship among containers, pods, and physical worker nodes"
node "Worker node 1" {
rectangle "Pod 1 (IP: 10.1.0.1)" {
rectangle "Container" as c11
}
rectangle "Pod 2 (IP: 10.1.0.2)" {
rectangle "Container 1" as c21
rectangle "Container 2" as c22
}
rectangle "Pod 3 (IP: 10.1.0.3)" {
rectangle "Container" as c31
}
}
node "Worker node 2" {
rectangle "Pod 4 (IP: 10.1.1.1)" {
rectangle "Container 1" as c41
rectangle "Container 2" as c42
}
rectangle "Pod 5 (IP: 10.1.1.2)" {
rectangle "Container" as c51
}
}
@enduml
Use "kubectl get pods
" to show basic info about pods, and use "kubectl describe pod
".
2.3 Running your first app on Kubernetes
This section require experiments on cloud platform, and the minikube may not fit this section, because the minikube does not support "LoadBalancer" service.
2.3.1 Deploying your Node.js app
Use "kubectl create deployment
" to create a deployment
1kubectl create deployment kubia --image=${YourDockerRegistryId}/kubia --port=8080
2# (deprecated): kubectl run kubia --image=${YourDockerRegistryId}/kubia --port=8080 --generator=run/v1
The diagram shown below is about how a container runs in Kubernetes:
@startuml
caption "Figure 2.2 Running container in Kubernetes"
actor "Developer" as dev
node "Local dev machine" as local {
rectangle "Docker" as localDocker
rectangle "kubectl" as localKubectl #BAC3E2
}
rectangle "Kubernetes Cluster" as cluster {
node "Control Plane(Master) Node" as master {
rectangle "Scheduler" as masterScheduler
rectangle "RESTful API Server" as apiServer #BAC3E2
}
rectangle "Worker Nodes" as workers {
node "worker-1" {
rectangle "Docker" as Docker1 #BAC3E2
rectangle {
rectangle "Kubelet" as Kubelet1 #BAC3E2
rectangle "Kube-proxy" as proxy1
}
}
node "worker-2" {
rectangle " " as Docker2 #BAC3E2
rectangle {
rectangle " " as Kubelet2 #BAC3E2
rectangle " " as proxy2
}
}
node "worker-3" {
rectangle " " as Docker3 #BAC3E2
rectangle {
rectangle " " as Kubelet3 #BAC3E2
rectangle " " as proxy3
}
}
}
}
database "Docker Hub" as hub
dev-->localDocker: 1. <u>docker push</u>
localDocker-->hub: 2. <u>push image</u>
dev-->localKubectl: 3. <u>kubectl run</u>
localKubectl-->apiServer: 4. <u>issues REST call</u>
note top of master: 5. <u>creates Pod and schedules to Worker node</u>
master-->Kubelet1: 6. <u>notifies Kubelet</u>
Kubelet1-->Docker1: 7. <u>instructs Docker to run image</u>
Docker1<--hub: 8. <u>pulls and runs image</u>
@enduml
- what happens behind the scene:
- kubectl will create a new ReplicationController object in the cluster, by sending a REST HTTP request to the Kubernetes API server
- ReplicationController then creates a new pod
- Scheduler schedules the new pod to one of the worker nodes
- kubelet of the scheduled worker node instructs Docker to pull image from registry if the specified image is not available locally
- Docker creates and runs the container
2.3.2 Accessing your web application
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:
- Use "
kubectl create
" to create a LoadBalancer service
1kubectl expose deployment kubia --type=LoadBalancer --name kubia-http
2# (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 bepending
. "Replication Controller"
has been deprecated in newer version of kubectl
-
Use "
kubectl get services
" command to see the newly created Service object- cluster IP
- external IP
- port
- age
-
Access the service via external IP
2.3.3 The logical parts of your system
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
- expose multiple pods at a single constant
- Pods
@startuml
left to right direction
caption "Figure 2.3 System consists of 1 ReplicationController, 1 Pod, and 1 Service"
actor User
interface Service #BAC3E2
note top of Service
Service: kubia-http-service
Internal IP: 10.3.246.185
External IP: 104.155.74.57
end note
component "Pod" as Pod{
rectangle Container
}
note top of Pod
name: kubia-pod
IP: 10.1.0.1
end note
rectangle ReplicationController
note top of ReplicationController
name: kubia
Replicas: 1
end note
User-->Service: requests
Service-->Container: Port 8080
Pod<--ReplicationController
@enduml
2.3.4 Horizontally scaling the application
To scale up the number of pods:
-
use "
kubectl get rc
" or "kubectl get replicationcontrollers
" to get the number of replicasNAME DESIRED CURRENT AGE kubia 1 1 17m DESIRED
: the number of pod replicas you want the ReplicationController to keepCURRENT
: the actual number of pods currently running
-
use "
kubectl scale rc kubia --replicas=3
" command to increase theDESIRED
replica count ofkubia
to 3- inspect with "
kubectl get rc
"NAME DESIRED CURRENT READY AGE kubia 1 1 2 17m - inspect with "
kubectl get pods
"NAME READY STATUS RESTARTS AGE kubia-hczji 1/1 Running 0 7s kubia-iq9y6 0/1 Pending 0 7s kubia-4jfyf 1/1 Running 0 18m
- inspect with "
@startuml
left to right direction
caption "Figure 2.4 System consists of 1 ReplicationController, 3 Pods, and 1 Service"
actor User
interface Service #BAC3E2
note top of Service
Service: kubia-http-service
Internal IP: 10.3.246.185
External IP: 104.155.74.57
end note
component "Pod" as Pod1
component "Pod" as Pod2
component "Pod" as Pod3
note top of Pod1
name: kubia-hczji
IP: 10.1.0.1
end note
note top of Pod2
name: kubia-iq9y6
IP: 10.1.0.2
end note
note bottom of Pod3
name: kubia-4jfyf
IP: 10.1.0.3
end note
rectangle ReplicationController
note top of ReplicationController
name: kubia
Replicas: 3
end note
User-->Service: requests
Service-->Pod1: Port 8080
Service-->Pod2: Port 8080
Service-->Pod3: Port 8080
Pod1<--ReplicationController
Pod2<--ReplicationController
Pod3<--ReplicationController
@enduml
2.3.5 Examining what nodes your app is running on
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:
-
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
-
use "
kubectl describe pod kubia-hczji
" command to check which worker node the podkubia-hczji
is scheduled to.
2.3.6 Kubernetes dashboard
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.
1# 1) start Minikube Dashboard
2minikube dashboard
3
4# 2) add proxy to allow external access (outside VM where Minikube is installed)
5kubectl proxy --port=8001 --address='0.0.0.0' --accept-hosts='^.*' &
Then dashboard can be accessed by:
1http://${YourVmAddress}:8001/api/v1/namespaces/kubernetes-dashboard/services/http:kubernetes-dashboard:/proxy/
2.4 Summary
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
2.5 Appendix: best practice
Original | Abbreviation |
---|---|
pods | po |
replication controller(deprecated) | rc |
services | svc |
best practice about alias in Linux terminal:
1# use `k` instead of `kubectl`
2alias k=kubectl
Note: if you use k
instead of kubectl
, you should execute the following commands to enable command autocompletion:
1# 1) allow command completion for `kubectl`
2source <(kubectl completion bash)
3
4# 2) allow command completion for `k`
5source <(kubectl completion bash | sed s/kubectl/k/g)