This Kubernetes 101 workshop teaches you some basic kubernetes concepts like pods, deployments, and services. (It is partly stolen from https://github.com/aws-samples/aws-workshop-for-kubernetes/tree/master/01-path-basics/103-kubernetes-concepts ;-) and adapted for local usage)
We will use a local running kubernetes cluster in this workshop. An easy way to get a kubernetes cluster up and running is via Docker for Mac. We also need a command line tool to interact with kubernetes, kubectl.
- Install (via brew: https://brew.sh/index_de)
- Docker (
brew cask install docker
) - Kubectl (
brew install kubernetes-cli
)
- Docker (
- Start Docker
- Start Kubernetes (via Docker: Click Docker icon -> Kubernetes -> Enable local cluster)
Now that we have a cluster up and running we can start exploring the Kubernetes CLI via the kubectl
(pronounced "cube control") command.
kubectl
interacts with the Kubernetes API Server, which runs on the master nodes in the cluster.
Kubernetes as a platform has a number of abstractions that map to API objects. These Kubernetes API Objects can be used to describe your cluster's desired state - including information such as applications and workloads running, container images, networking resources, and more. This section explains the most-used Kubernetes API concepts and how to interact with them via kubectl
.
This command will show all the nodes available in your kubernetes cluster:
$ kubectl get nodes
It will show an output similar to:
NAME STATUS ROLES AGE VERSION
docker-desktop Ready master 27h v1.14.8
Normally a Kubernetes cluster has master and worker nodes. The Kubernetes setup provided by Docker for Mac only has a master node, which also does a worker's job.
Each resource in Kubernetes can be defined using a configuration file. For example, an NGINX pod can be defined with configuration file shown in below:
$ cat pod.yaml
apiVersion: v1
kind: Pod
metadata:
name: nginx-pod
labels:
name: nginx-pod
spec:
containers:
- name: nginx
image: nginx:latest
ports:
- containerPort: 80
Create the pod as shown below:
$ kubectl create -f pod.yml
pod/nginx-pod created
Get the list of pods:
$ kubectl get pod
NAME READY STATUS RESTARTS AGE
nginx-pod 1/1 Running 0 8s
You can't access the pod from the outside of the cluster at the moment. To do so, you have to forward the pod's port.
$ kubectl port-forward nginx-pod 8080:80
You can access the nginx page now with your browser on http://localhost:8080
Take a look at the nginx container log. You can see the access log there:
$ kubectl logs nginx-pod
127.0.0.1 - - [15/Jan/2020:18:42:37 +0000] "GET / HTTP/1.1" 200 612 "-" "curl/7.54.0" "-"
127.0.0.1 - - [15/Jan/2020:18:43:26 +0000] "GET / HTTP/1.1" 200 612 "-" "curl/7.54.0" "-"
127.0.0.1 - - [15/Jan/2020:18:43:45 +0000] "GET / HTTP/1.1" 304 0 "-" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.117 Safari/537.36" "-"
This command will open a TTY to a shell in your pod. It is like SSH for containers.
$ kubectl exec nginx-pod -it bash
This opens a bash shell and allows you to look around the filesystem of the container. You can for example navigate to the config file of nginx and take a look at it.
$ cd /etc/nginx
$ cat nginx.conf
You can change the image tag e.g. or other parameters and the container will be recreated (kubectl edit pod nginx-pod
). But this would cause a downtime. It is kind of simply running docker run
on a server.
The fun using kubernetes comes with deployments
!
This command instantiates an nginx container in your cluster, inside a pod, which is managed by a deployment:
$ kubectl create -f deployment.yml
deployment.apps/nginx-deployment created
Get the list of deployments:
$ kubectl get deployments
NAME READY UP-TO-DATE AVAILABLE AGE
nginx-deployment 3/3 3 3 11s
Get the list of running pods:
$ kubectl get pods
NAME READY STATUS RESTARTS AGE
nginx-deployment-f76fdbc94-6wplq 1/1 Running 0 65m
nginx-deployment-f76fdbc94-7gj8h 1/1 Running 0 65m
nginx-deployment-f76fdbc94-wspj5 1/1 Running 0 65m
Get additional details for the pod by using the from the above output:
$ kubectl describe pod nginx-deployment-f76fdbc94-wspj5
By default, pods are created in a default namespace. In addition, a kube-system namespace is also reserved for Kubernetes system pods. A list of all the pods in kube-system namespace can be displayed as shown:
$ kubectl get pods --namespace=kube-system
NAME READY STATUS RESTARTS AGE
coredns-6dcc67dcbc-7xxcs 1/1 Running 1 2d23h
coredns-6dcc67dcbc-b5tkj 1/1 Running 1 2d23h
etcd-docker-desktop 1/1 Running 1 2d23h
kube-apiserver-docker-desktop 1/1 Running 1 2d23h
kube-controller-manager-docker-desktop 1/1 Running 1 2d23h
kube-proxy-h4d54 1/1 Running 1 2d23h
kube-scheduler-docker-desktop 1/1 Running 1 2d23h
A pod is ephemeral. Each pod is assigned a unique IP address. If a pod that belongs to a replication controller dies, then it is recreated and may be given a different IP address. Further, additional pods may be created using Deployment or Replica Set. This makes it difficult for an application server, such as WildFly, to access a database, such as MySQL, using its IP address.
A Service is an abstraction that defines a logical set of pods and a policy by which to access them. The IP address assigned to a service does not change over time, and thus can be relied upon by other pods. Typically, the pods belonging to a service are defined by a label selector. This is similar mechanism to how pods belong to a replica set.
This abstraction of selecting pods using labels enables a loose coupling. The number of pods in the deployment may scale up or down but the application server can continue to access the database using the service.
A Kubernetes service defines a logical set of pods and enables them to be accessed through microservices.
You can create a service with a manifest file or let kubectl
do the work:
$ kubectl expose deployment nginx-deployment --type=NodePort
service/nginx-deployment exposed
$ kubectl get service
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 3d
nginx-deployment NodePort 10.107.148.24 <none> 80:30849/TCP 5m44s
Kubernetes provided by Docker for Mac lets you access NodePort services via localhost
. The service in the example can be accessed by localhost:30849
. Give it a try.
You can also take a look at the created manifest file.
$ kubectl get service nginx-deployment -o yaml
Now play around with the deployment. Do it in the yaml file and use kubectl apply ...
or use kubectl --help
to find ways to change a deployment without a yaml file.
- Scale the deployment from 3 to 5 replicas
- Change the Deployment Strategy to
Recreate
and watch how the deployment behavior changes when you set another image. (watch -n1 kubectl get pods
, don't have thewatch
command:brew install watch
)
Now that you have been guided so much. Here comes a challenge for you. Create a broken deployment:
$ kubectl create -f broken-deployment.yml
You should see something like this:
$ kubectl get pods
NAME READY STATUS RESTARTS AGE
broken-deployment-55db7fb997-8ztjr 0/1 Pending 0 50s
broken-deployment-55db7fb997-pw98w 0/1 Pending 0 50s
broken-deployment-55db7fb997-tx7wz 1/1 Running 0 50s
broken-deployment-55db7fb997-vlckl 0/1 Pending 0 50s
broken-deployment-55db7fb997-vlld8 0/1 Pending 0 50s
When a Pod is in Pending
state, there is something wrong and the pod cannot be scheduled to a worker. You can figure out the "why" by using kubectl describe
. Then fix the deployment (all Pods in state Running
) and feel like a real hero!
Bonus points: Find a well suited solution - not only fix the problem, but do it in an informed
You are already done? Nice! Then try out some stuff on your own ... OR ... start a pod based on this image pengbai/docker-supermario
and have some fun with super mario running on Kubernetes ;-)
Delete all the Kubernetes resources created so far:
$ kubectl get all
$ kubectl delete deployment my-deployment
$ kubectl delete service nginx-deployment
$ ...