Kubernetes

Workshop

Workshop

Setup & Running First App

Setup on Azure

Please refer to this guide.

Running First App

  • Let's run an application on the new Kubernetes cluster
  • Before we can launch a container based on the image, we need to create a pod definition
    • A pod describes an application running on Kubernetes
    • A pod can contain one or more tightly coupled containers, that make up the app
      • Those apps can easily communicate with each other using their local
  • Our app has only one container

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

  • Each node contain Docker engine (can be also another container engine)
  • Each pod contain one or more Docker containers
  • iptables is like a firewall
  • The pod is built based on the YAML spec kind Pod
  • Containers are built based on the the containers section

Replication Controller

Scaling

  • If your application is stateless, you can horizontally scale it
    • Stateless = your application doesn't have a state, it doesn't write any local files / keeps local sessions
    • All traditional databases (MySQL, Postgres) are stateful, they have database files that can't split over multiple instances
  • Most web applications can be made stateless:
    • Session management needs to be done outside the container
    • Any files that need to be saved can't be save locally on the container
  • For more information about best practices, have a look at 12factor.net
  • We can use volumes to still run stateful apps
    • Those stateful apps can't horizontally scale, but you can run them in a single container and vertically scale (allocate more CPU / memory / disk)
  • Scaling in Kubernetes can be done using the Replication Controller
  • The replication controller will ensure a specified number of pod replicas will run at all time
  • A pod created with the replica controller will automatically be replaced if they fail, get deleted, or are terminated
  • Using the replication controller is also recommended if you just want to make sure 1 pod is always running, even after reboots 
    • You can then run a replication controller with just 1 replica
    • This makes sure that the pod is always running

Our First App

To replicate our example app 2 times:

  • Set kind: ReplicationController
  • Add template
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

  • Replica Set is the next-generation Replication Controller
  • It supports a new selector that can do selection based on filtering according a set of values
    • e.g. "environment" either "dev" or "qa"
    • not only based on equality, like the Replication Controller
      • e.g. "environment" == "dev"
  • This Replica Set, rather than the Replication Controller, is used by the Deployment object

Deployment Object

  • A deployment declaration in Kubernetes allows you to do app deployments and updates
  • When using the deployment object, you define the state of your application
    • Kubernetes will then make sure the clusters matches your desired state
  • Just using the replication controller or replication set might be cumbersome to deploy apps
    • The Deployment Object is easier to use and gives you more possibilities
  • With a deployment object, you can:
    • Create a deployment (e.g. deploying an app)
    • Update a deployment (e.g. deploying a new version)
    • Do rolling updates (zero downtime deployments)
    • Roll back to a previous version
    • Pause / resume a deployment (e.g. to roll-out to only a certain percentage) 

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

  • Pods are very dynamic, they come and go on Kubernetes cluster
    • When using a Replication Controller, pods are terminated  and created during scaling operations
    • When using Deployments, when updating the image version, pods are terminated and new pods take the place of older pods
  • That's why Pods should never be accessed directly, but always through a Service
  • A service is the logical bridge between the "mortal" pods and other services or end-users
  • When using the kubectl expose command earlier, you created a new Service for your pod, so it could be accessed externally
  • Create a service will create an endpoint for your pods(s):
    • a ClusterIP: a virtual IP address only reachable from within the cluster (this is the default)
    • a NodePort: a port that is the same on each node that is also reachable externally
    • a LoadBalancer: a LoadBalancer created by the cloud provider that will route external traffic to every node on the NodePort (e.g. ELB on AWS)
  • The options just shown only allow you to create virtual IPs or ports
  • There is also a possibility to use DNS names
    • ExternalName can provide a DNS name for the service
    • e.g. for service discovery using DNS
    • This only works when the DNS add-on is enabled

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
  • Note: By default, service can only run between port 30000-32767. But you could change this behavior by adding --service-node-port-range= argument to the kube-apiserver (in the init scripts)

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)

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

  • Align business request with Application/Data/Infrastructure
  • Centralized management for:
    • Resource Pool
    • Automated Workflow
    • Provisioning
    • Scale Up/Down
    • Monitoring
    • etc.

Why is Orchestrator

  • Production must be cluster
  • Microservices architecture
  • Stateful applications will run on stateless architecture
  • Scale up/down

Kubernetes Introduction

Kubernetes Features

  • Automatic Binpacking
    • If a pod reaches memory limit, it will be terminated and will be restarted on another pod if restart flag was set.
  • Horizontal Pods Autoscaling (HPA)
    • Docker Swarm on Desktop does not have HPA. But Swarm on all cloud providers have HPA.
  • Automated Rollout and Rollbacks
    • Ensue no downtime when upgrading /downgrading
  • Storage Orchestrator
    • Local/Network/Cloud
    • Support dynamic provisioning of storage class
  • Self-healing
    • By Replication Controller
  • Service Discovery and Load Balancing
    • Load balance by kube-proxy
  • Secret and Configuration Management
    • ConfigMap allows environment configurable

Kubernetes Architecture

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