Kubernetes

Workshop

This workshop is based on the course "Learn DevOps: The Complete Kubernetes Course" on Udemy

Workshop

Setup & Running First App

Setup on Azure

Please refer to this guide.

Running First App

Create a Pod

Create a file pod-helloworld.yml with the pod definition:

apiVersion: v1
kind: Pod
metadata:
  name: nodehelloworld.example.com
  labels:
    app: helloworld
spec:
  containers:
    - name: k8s-demo
      image: wardviaene/k8s-demo
      ports:
        - containerPort: 3000

Use kubectl to create the pod on the kubernetes cluster:

$ kubectl create -f pod-helloworld.yml
pod "nodehelloworld.example.com" created

See pod status

$ kubectl get pod
NAME                         READY     STATUS    RESTARTS   AGE
nodehelloworld.example.com   1/1       Running   0          2m

See pod config

$ kubectl describe pod nodehelloworld.example.com
Name:		nodehelloworld.example.com
Namespace:	default
Node:		k8s-agent-7d111633-0/10.240.0.4
Start Time:	Sun, 29 Apr 2018 04:36:37 +0000
Labels:		app=helloworld
Annotations:	<none>
Status:		Running
IP:		10.244.1.7
Containers:
  k8s-demo:
    Container ID:	docker://dddf1e925faf9c3fee56b373244bb7227d7f4c139c76816dbafb8237d6d00515
    Image:		wardviaene/k8s-demo
    Image ID:		docker-pullable://wardviaene/k8s-demo@sha256:2c050f462f5d0b3a6430e7869bcdfe6ac48a447a89da79a56d0ef61460c7ab9e
    Port:		3000/TCP
    State:		Running
      Started:		Sun, 29 Apr 2018 04:37:46 +0000
    Ready:		True
    Restart Count:	0
    Environment:	<none>
    Mounts:
      /var/run/secrets/kubernetes.io/serviceaccount from default-token-lr576 (ro)
Conditions:
  Type		Status
  Initialized 	True
  Ready 	True
  PodScheduled 	True
Volumes:
  default-token-lr576:
    Type:	Secret (a volume populated by a Secret)
    SecretName:	default-token-lr576
    Optional:	false
QoS Class:	BestEffort
Node-Selectors:	<none>
Tolerations:	<none>
Events:
  FirstSeen	LastSeen	Count	From				SubObjectPath			Type		Reason		      	Message
  ---------	--------	-----	----				-------------			--------	------		      	-------
  4m		4m		1	default-scheduler						Normal		Scheduled	      	Successfully assigned nodehelloworld.example.com to k8s-agent-7d111633-0
  4m		4m		1	kubelet, k8s-agent-7d111633-0					Normal		SuccessfulMountVolume  	MountVolume.SetUp succeeded for volume "default-token-lr576"
  4m		4m		1	kubelet, k8s-agent-7d111633-0	spec.containers{k8s-demo}	Normal		Pulling		      	pulling image "wardviaene/k8s-demo"
  2m		2m		1	kubelet, k8s-agent-7d111633-0	spec.containers{k8s-demo}	Normal		Pulled		      	Successfully pulled image "wardviaene/k8s-demo"
  2m		2m		1	kubelet, k8s-agent-7d111633-0	spec.containers{k8s-demo}	Normal		Created		      	Created container
  2m		2m		1	kubelet, k8s-agent-7d111633-0	spec.containers{k8s-demo}	Normal		Started		      	Started container

Forward port

$ kubectl port-forward nodehelloworld.example.com 8081:3000
Forwarding from 127.0.0.1:8081 -> 3000
Forwarding from [::1]:8081 -> 3000

Create Service

Expose service

$ kubectl expose pod nodehelloworld.example.com --type=NodePort --name nodehelloworld-service
service "nodehelloworld-service" exposed

See list of service

$ kubectl get service
NAME                     CLUSTER-IP    EXTERNAL-IP   PORT(S)          AGE
kubernetes               10.0.0.1      <none>        443/TCP          7d
nodehelloworld-service   10.0.241.45   <nodes>       3000:30706/TCP   25s

Get service url

$ kubectl describe service nodehelloworld-service
Name:				nodehelloworld-service
Namespace:			default
Labels:				app=helloworld
Annotations:		<none>
Selector:			app=helloworld
Type:				NodePort
IP:			    	10.0.241.45
Port:				<unset>	3000/TCP
NodePort:			<unset>	30706/TCP
Endpoints:			10.244.1.7:3000
Session Affinity:	None
Events:				<none>

Try accessing app locally

$ curl 10.244.1.7:3000
Hello World!

Access From the Internet

Edit service with VI

$ kubectl edit service/nodehelloworld-service
service "nodehelloworld-service" edited

Change externalTrafficPolicy to Local and change type to LoadBalancer.

# Please edit the object below. Lines beginning with a '#' will be ignored,
# and an empty file will abort the edit. If an error occurs while saving this file will be
# reopened with the relevant failures.
#
apiVersion: v1
kind: Service
metadata:
  creationTimestamp: 2018-04-29T04:44:16Z
  labels:
    app: helloworld
  name: nodehelloworld-service
  namespace: default
  resourceVersion: "109236"
  selfLink: /api/v1/namespaces/default/services/nodehelloworld-service
  uid: f8e985ab-4b67-11e8-9b4e-000d3aa08982
spec:
  clusterIP: 10.0.241.45
  externalTrafficPolicy: Local
  healthCheckNodePort: 31970
  ports:
  - nodePort: 30706
    port: 3000
    protocol: TCP
    targetPort: 3000
  selector:
    app: helloworld
  sessionAffinity: None
  type: LoadBalancer
status:
  loadBalancer:
    ingress:
    - ip: 52.163.120.220

VI Usage: Press i to enter edit mode. Once done, press ESC and type :wq to save and quite.

Check service using kubectl get service

$ kubectl get service
NAME                     CLUSTER-IP    EXTERNAL-IP   PORT(S)          AGE
kubernetes               10.0.0.1      <none>        443/TCP          7d
nodehelloworld-service   10.0.241.45   <pending>     3000:30706/TCP   56m

Wait for a few minutes until <pending> changing to an IP address

$ kubectl get service
NAME                     CLUSTER-IP    EXTERNAL-IP      PORT(S)          AGE
kubernetes               10.0.0.1      <none>           443/TCP          7d
nodehelloworld-service   10.0.241.45   52.163.120.220   3000:30706/TCP   59m

Try to access the app at the displayed IP:port e.g. http://52.163.120.220:3000

References

Useful Commands

Command

Description

kubectl get pod

Get information about all running pods

kubectl describe pod <pod>

Describe one pod

kubectl expose pod <pod> --port=444 --name=frontend

Expose the pod of a pod (creates a new service)

kubectl port-forward <pod> 8080

Pod forward the exposed pod port to your local machine

kubectl attach <pod> -i

Attach to the pod

kubectl exec <pod> -- <command>

Execute a command on the pod

kubectl label pod <pod> mylabel=awesome

Add a new label to a pod

kubectl run -i --tty busybox --image=busybox --restart=never -- sh

Run a shell in a pod, very useful for debugging

Workshop

Replication and Deployment

$ kubectl scale --replicas=3 -f helloworld-replica.yml
replicationcontroller "helloworld-controller" scaled

$ kubectl get pod
NAME                          READY     STATUS    RESTARTS   AGE
helloworld-controller-2v2x1   1/1       Running   0          13m
helloworld-controller-hgn9s   1/1       Running   0          3m
helloworld-controller-r2vkh   1/1       Running   0          5s

Replication Controller

Scaling

Our First App

To replicate our example app 2 times:

apiVersion: v1
kind: ReplicationController
metadata:
  name: helloworld-controller
spec:
  replicas: 2
  selector:
    app: helloworld
  template:
    metadata:
      labels:
        app: helloworld
    spec:
      containers:
      - name: k8s-demo
        image: wardviaene/k8s-demo
        ports:
        - name: nodejs-port
          containerPort: 3000

Create pods

$ kubectl create -f helloworld-replica.yml
replicationcontroller "helloworld-controller" created

$ kubectl get pod
NAME                          READY     STATUS    RESTARTS   AGE
helloworld-controller-2v2x1   1/1       Running   0          9s
helloworld-controller-8bdf3   1/1       Running   0          9s

If one pod is failed, replication controller will automatically restore.

$ kubectl delete pod helloworld-controller-8bdf3
pod "helloworld-controller-8bdf3" deleted

$ kubectl get pod
NAME                          READY     STATUS              RESTARTS   AGE
helloworld-controller-2v2x1   1/1       Running             0          10m
helloworld-controller-8bdf3   1/1       Terminating         0          10m
helloworld-controller-hgn9s   0/1       ContainerCreating   0          3s

You can also specify number of replicas.

$ kubectl scale --replicas=3 -f helloworld-replica.yml
replicationcontroller "helloworld-controller" scaled

$ kubectl get pod
NAME                          READY     STATUS    RESTARTS   AGE
helloworld-controller-2v2x1   1/1       Running   0          13m
helloworld-controller-hgn9s   1/1       Running   0          3m
helloworld-controller-r2vkh   1/1       Running   0          5s

You can see status of the Replication Controller using kubectl get rc

$ kubectl get rc
NAME                    DESIRED   CURRENT   READY     AGE
helloworld-controller   3         3         3         14m

Finally, delete the app.

$ kubectl delete rc/helloworld-controller
replicationcontroller "helloworld-controller" delete

Deployments

Replication Set

Deployment Object

Example Deployment

apiVersion: extensions/v1beta1
kind: Deployment
metadata:
  name: helloworld-deployment
spec:
  replicas: 3
  template:
    metadata:
      labels:
        app: helloworld
    spec:
      containers:
      - name: k8s-demo
        image: wardviaene/k8s-demo
        ports:
        - name: nodejs-port
          containerPort: 3000

Useful Commands

Command Description
kubectl get deployments Get information on current deployments
kubectl get rs Get information about the replica sets
kubectl get pods --show-labels Get pods, and also show labels attached to those pods
kubectl rollout status deployment/helloworld-deployment Get deployment status
kubectl set image deployment/helloworld-deployment k8s-demo=k8s-demo:2 Run k8s-demo with the image label version 2
kubectl rollout status deployment/helloworld-deployment Get the status of the rollout
kubectl rollout history deployment/helloworld-deployment Get the rollout history
kubectl rollout undo deployment/helloworld-deployment Rollback to previous version
kubectl rollout undo deployment/helloworld-deployment --to-revision=n Rollback to any version

Demo

Deploy Version 1

$ kubectl create -f helloworld-deploy.yml
deployment "helloworld-deployment" created

$ kubectl get deployment
NAME                    DESIRED   CURRENT   UP-TO-DATE   AVAILABLE   AGE
helloworld-deployment   3         3         3            3           2m

$ kubectl get rs
NAME                               DESIRED   CURRENT   READY     AGE
helloworld-deployment-4153696333   3         3         3         2m

$ kubectl get pod --show-labels
NAME                                     READY     STATUS    RESTARTS   AGE       LABELS
helloworld-deployment-4153696333-17vqp   1/1       Running   0          3m        app=helloworld,pod-template-hash=4153696333
helloworld-deployment-4153696333-ltxvt   1/1       Running   0          3m        app=helloworld,pod-template-hash=4153696333
helloworld-deployment-4153696333-zbtn7   1/1       Running   0          3m        app=helloworld,pod-template-hash=4153696333

$ kubectl rollout status deployment/helloworld-deployment
deployment "helloworld-deployment" successfully rolled out

Expose and Test Version 1

$ kubectl expose deployment helloworld-deployment --type=NodePort
service "helloworld-deployment" exposed

$ kubectl get service
NAME                    CLUSTER-IP     EXTERNAL-IP   PORT(S)          AGE
helloworld-deployment   10.0.214.139   <nodes>       3000:31616/TCP   37s
kubernetes              10.0.0.1       <none>        443/TCP          9d

$ kubectl describe service helloworld-deployment
Name:				helloworld-deployment
Namespace:			default
Labels:				app=helloworld
Annotations:		<none>
Selector:			app=helloworld
Type:				NodePort
IP:					10.0.214.139
Port:				<unset>	3000/TCP
NodePort:			<unset>	31616/TCP
Endpoints:			10.244.1.11:3000,10.244.1.12:3000,10.244.1.13:3000
Session Affinity:	None
Events:				<none>

$ curl http://10.0.214.139:3000
Hello World!

Upgrade to Version 2

$ kubectl set image deployment/helloworld-deployment k8s-demo=wardviaene/k8s-demo:2
deployment "helloworld-deployment" image updated

$ kubectl rollout status deployment/helloworld-deployment
deployment "helloworld-deployment" successfully rolled out

$ curl http://10.0.214.139:3000
Hello World v2!

$ kubectl get pod
NAME                                     READY     STATUS        RESTARTS   AGE
helloworld-deployment-4153696333-zbtn7   0/1       Terminating   0          15m
helloworld-deployment-521624165-3jftr    1/1       Running       0          36s
helloworld-deployment-521624165-469mg    1/1       Running       0          46s
helloworld-deployment-521624165-pt5f7    1/1       Running       0          46s

$ kubectl rollout history deployment/helloworld-deployment
deployments "helloworld-deployment"
REVISION	CHANGE-CAUSE
1		<none>
2		<none>

Rollback to Version 1

$ kubectl rollout undo deployment/helloworld-deployment
deployment "helloworld-deployment" rolled back

$ kubectl rollout status deployment/helloworld-deployment
Waiting for rollout to finish: 2 out of 3 new replicas have been updated...
Waiting for rollout to finish: 2 out of 3 new replicas have been updated...
Waiting for rollout to finish: 2 out of 3 new replicas have been updated...
Waiting for rollout to finish: 1 old replicas are pending termination...
Waiting for rollout to finish: 1 old replicas are pending termination...
Waiting for rollout to finish: 1 old replicas are pending termination...
Waiting for rollout to finish: 2 of 3 updated replicas are available...
deployment "helloworld-deployment" successfully rolled out

$ kubectl get pod
NAME                                     READY     STATUS        RESTARTS   AGE
helloworld-deployment-4153696333-d0jv7   1/1       Running       0          10s
helloworld-deployment-4153696333-w5dhs   1/1       Running       0          15s
helloworld-deployment-4153696333-x62bk   1/1       Running       0          15s
helloworld-deployment-521624165-3jftr    1/1       Terminating   0          1m
helloworld-deployment-521624165-469mg    1/1       Terminating   0          1m
helloworld-deployment-521624165-pt5f7    1/1       Terminating   0          1m

$ kubectl rollout history deployment/helloworld-deployment
deployments "helloworld-deployment"
REVISION	CHANGE-CAUSE
2		<none>
3		<none>
Workshop

Services

Services

Example Service

apiVersion: v1
kind: Service
metadata:
  name: helloworld-service
spec:
  ports:
  - port: 31001
    nodePort: 31001
    targetPort: nodejs-port
    protocol: TCP
  selector:
    app: helloworld
  type: NodePort

Demo

Create Pod

$ kubectl create -f pod-helloworld.yml
pod "nodehelloworld.example.com" created

$ kubectl get pod
NAME                         READY     STATUS    RESTARTS   AGE
nodehelloworld.example.com   1/1       Running   0          9s

$ kubectl describe pod nodehelloworld.example.com
Name:		nodehelloworld.example.com
Namespace:	default
Node:		k8s-agent-7d111633-0/10.240.0.4
Start Time:	Mon, 07 May 2018 13:55:55 +0000
Labels:		app=helloworld
Annotations:	<none>
Status:		Running
IP:		10.244.1.10
Containers:
  k8s-demo:
    Container ID:	docker://88b44009636f515c1af43c940fa7b351ba7e772af7d8fe71e572a8cdba0d6505
    Image:		wardviaene/k8s-demo
    Image ID:		docker-pullable://wardviaene/k8s-demo@sha256:2c050f462f5d0b3a6430e7869bcdfe6ac48a447a89da79a56d0ef61460c7ab9e
    Port:		3000/TCP
    State:		Running
      Started:		Mon, 07 May 2018 13:55:59 +0000
    Ready:		True
    Restart Count:	0
    Environment:	<none>
    Mounts:
      /var/run/secrets/kubernetes.io/serviceaccount from default-token-lr576 (ro)
Conditions:
  Type		Status
  Initialized 	True
  Ready 	True
  PodScheduled 	True
Volumes:
  default-token-lr576:
    Type:	Secret (a volume populated by a Secret)
    SecretName:	default-token-lr576
    Optional:	false
QoS Class:	BestEffort
Node-Selectors:	<none>
Tolerations:	<none>
Events:
  FirstSeen	LastSeen	Count	From				SubObjectPath			Type		Reason			Message
  ---------	--------	-----	----				-------------			--------	------			-------
  37s		37s		1	default-scheduler						Normal		Scheduled		Successfully assigned nodehelloworld.example.com to k8s-agent-7d111633-0
  37s		37s		1	kubelet, k8s-agent-7d111633-0					Normal		SuccessfulMountVolume	MountVolume.SetUp succeeded for volume "default-token-lr576"
  36s		36s		1	kubelet, k8s-agent-7d111633-0	spec.containers{k8s-demo}	Normal		Pulling			pulling image "wardviaene/k8s-demo"
  33s		33s		1	kubelet, k8s-agent-7d111633-0	spec.containers{k8s-demo}	Normal		Pulled			Successfully pulled image "wardviaene/k8s-demo"
  33s		33s		1	kubelet, k8s-agent-7d111633-0	spec.containers{k8s-demo}	Normal		Created			Created container
  32s		32s		1	kubelet, k8s-agent-7d111633-0	spec.containers{k8s-demo}	Normal		Started			Started container

Create Service

$ cat helloworld-service.yml
apiVersion: v1
kind: Service
metadata:
  name: helloworld-service
spec:
  ports:
  - port: 31001
    nodePort: 31001
    targetPort: nodejs-port
    protocol: TCP
  selector:
    app: helloworld
  type: NodePort

$ kubectl create -f helloworld-service.yml
service "helloworld-service" created

$ kubectl get svc
NAME                 CLUSTER-IP     EXTERNAL-IP   PORT(S)           AGE
helloworld-service   10.0.240.190   <nodes>       31001:31001/TCP   12s
kubernetes           10.0.0.1       <none>        443/TCP           15d

$ kubectl describe svc helloworld-service
Name:			helloworld-service
Namespace:		default
Labels:			<none>
Annotations:		<none>
Selector:		app=helloworld
Type:			NodePort
IP:			10.0.240.190
Port:			<unset>	31001/TCP
NodePort:		<unset>	31001/TCP
Endpoints:		<none>
Session Affinity:	None

Re-create Service

$ kubectl delete svc helloworld-service
service "helloworld-service" deleted

$ kubectl create -f helloworld-service.yml
service "helloworld-service" created

$ kubectl describe svc helloworld-service
Name:			helloworld-service
Namespace:		default
Labels:			<none>
Annotations:		<none>
Selector:		app=helloworld
Type:			NodePort
IP:			10.0.166.250
Port:			<unset>	31001/TCP
NodePort:		<unset>	31001/TCP
Endpoints:		<none>
Session Affinity:	None

Please note that the virtual IP address is changed.

Workshop (3DS)

My notes from attended training course "Advanced Docker with Kubernetes on 8 and 15 July 2018" @ 3DS INTERACTIVE CO.,LTD.

Workshop (3DS)

Day 1 - First Half

Setup

Repositories

Course Materials: https://github.com/praparn/kubernetes_20180701 
Docker Hub: https://hub.docker.com/u/labdocker/ 

Workshop 1: Install minikube

Install Oracle VirtualBox or DockerToolbox first.

$ brew cask install minikube
$ kubectl get-k8s-versions
$ minikube config set kubernetes-version v1.9.0
$ minikube start --vm-driver=virtualbox profile=minikubelab1
$ kubectl config get-contexts
$ kubectl config use-context minikube

Kubernetes tends to be stable at every other minor versions i,e. v1.7.0, 1.9.0, 1.11.0, ...

If minikube stucks at 'Starting cluster components...', check out this solution.

$ minikube status
$ minikube ip
$ minikube ssh
  $ docker version
$ kubectl get nodes
$ kubectl get cs

Kubectl command syntax are kubectl Verb Object vs. docker Object Verb 

$ kubectl run webtest --image=labdocker/nginx:latest --port=80
$ kubectl expose deployment webtest --target-port=80 --type=NodePort
$ kubectl get pods
$ kubectl get deployment
$ kubectl get svc
$ kubectl describe svc
$ kubectl get svc webtest
$ kubectl describe svc webtest
$ curl http://192.168.99.100:30930

To enable autocomplete for kubectl, check out this guide.

Cloud Native Landscape

CNCF Cloud Native Interactive Landscape

Kubernetes is now a project of CNCF, after Google invented it.

Cloud Native Landscape Diagram (Full Resolution)

What are the best Docker orchestration tools?

In Thailand, the green bank is going for OpenShift while purple bank is going for Kubernetes.

Alternative Playground

Introduction

What is Orchestrator

Why is Orchestrator

Kubernetes Introduction

Kubernetes Features

Kubernetes Architecture

https://thenewstack.io/kubernetes-an-overview/