Quick intro to K8s

Primitives that just make sense

Created by Laurence J MacGuire a.k.a. Laz a.k.a. 刘建明

ThoughtWorks Bangkok, 2017/02/05

Creative Commons License

Hi to Phisit!

About Me

  • Started running Linux in ~2000.
  • Had a server room in my closet. Started programming.
  • Haven’t touched Windows since 2008.
  • So-so programmer. Ops that doesn’t keep a pager.
  • ThoughtWorks since 2013.

What I do

  • Helping people. ~100 devs in my office.
  • Very AWS heavy.
  • Slowly Incrementally moving into Docker.

Recap.

Docker. Docker. Docker

(It’s the future)

Images

  • Passive
  • A snapshot of a file-system
  • Kind of like
    • a hard-drive image
    • an AWS AMI
  • Dockerfiles “somewhat declarative”

Container

  • Active
  • Runs (hopefully only) one process
  • “Kind of like a VM”

Innovation?

Not all that much. It all existed before.

  • Dockerfile syntax is nice
  • Packaging and retrieval of images
  • UI is better

K8s

What is that thing?

  • Inspired by Google’s “Borg”
  • Schedules processes against resources
    • node CPU, RAM & other constraints
  • Provides and builds upon simple primitives

  • Runs on bare-metal or VMs

  • Encourages 12 factor applications

Architecture

Architecture

Architecture - API Server

It’s a swagger API!

  • Only accessible through the API.
  • CRUD on K8s primitives / resources
  • A few non REST-y actions

Architecture - Etcd

Reliable key-value store for the most critical data of a distributed system

  • K-V store that choses C (and A) over p.
  • Uses the RAFT concensus to determine leadership
  • Widely used by other “Cloud Native” apps

Architecture - Scheduler

  • Looks at resource requests and resource availability
  • Decides where to place a workload
    • Which node can this process run on?

Architecture - Controller Manager

  • Orchestrates resource requests and assignments
  • Breaks down more complex objects into simpler primitives

Architecture - TL;DR

  • The whole thing is declarative
  • Tell it what you want
    • Give it YAML
    • It tries it best to make it the truth
      • and keep it the truth
  • Uses labels everywhere to query itself

K8s

Composing coherent primitives.

  • Pods
  • Job / ScheduledJob
  • Sets
    • ReplicaSet
    • DaemonSet
    • StatefulSet
  • Deployment
  • ConfigMaps & Secrets
  • Service, Ingress

All these things live in a namespace

Demo Code Here

https://github.com/leprechaun/intro-to-k8s-code/

Pods

One logical process.

  • Accomplish one task / function
  • CPU & RAM allocation
  • Get an IP & potentially storage

Pods

./manifests/v1/pod.yml

Pods

$ kubectl create -f ./manifests/v1/pod.yml
pod "nginx-static-app-on-k8s" created
$ kubectl get pods
NAME                                  READY     STATUS    RESTARTS   AGE
nginx-static-app-on-k8s               1/1       Running   1          1d

$ kubectl logs nginx-static-app-on-k8s
172.17.0.1 - - [04/Feb/2017:09:47:36 +0000] "GET / HTTP/1.1" 200 287 "-" "Go-http-client/1.1" "-"
172.17.0.1 - - [04/Feb/2017:09:47:38 +0000] "GET / HTTP/1.1" 200 287 "-" "Go-http-client/1.1" "-"
172.17.0.1 - - [04/Feb/2017:09:47:41 +0000] "GET / HTTP/1.1" 200 287 "-" "Go-http-client/1.1" "-"

Jobs

A Pod/Process that is meant to finish

./manifests/job/job.yml

Jobs

$ kubectl create -f manifests/job/job.yml 
job "who-am-i" created

Jobs

kubectl get jobs
NAME       DESIRED   SUCCESSFUL   AGE
who-am-i   10        10           2m

ScheduledJobs

A Job that is meant to finish and restart periodically.

ScheduledJobs

apiVersion: batch/v2alpha1
kind: ScheduledJob
metadata:
  name: example-cron-job
spec:
  schedule: "*/1 * * * *"
  jobTemplate:
	spec:
	  template:
		spec:
		  containers:
		  - name: my-cron-job
			image: busybox:1.25.0
			args:
			- whoami
		  restartPolicy: OnFailure

ScheduledJobs

$ kubectl create -f ./cronjob.yaml
cronjob "example-cron-job" created

$ kubectl get scheduledjob
NAME               SCHEDULE      SUSPEND   ACTIVE    LAST-SCHEDULE
example-cron-job   */1 * * * *   False     0         Sun, 05 Feb 2017 11:52:00 +0700

$ kubectl get jobs
NAME                          DESIRED   SUCCESSFUL   AGE
example-cron-job-1706329924   1         1            2m
example-cron-job-1782286151   1         1            51s
example-cron-job-1858111306   1         1            1m
example-cron-job-2010351440   1         1            3m
example-job                   1         1            11m

Sets

Define groups of pods w/ count, affinity & lifecycle

ReplicaSet

A Pod scheduled to run with N replicas

Hint! Hint! This smells like horizontal scaling!

./manifests/v2/replicaset.yml

DaemonSet

A Pod scheduled to run on ALL nodes of a cluster.

DaemonSet

apiVersion: extensions/v1beta1
kind: DaemonSet
metadata:
  name: kube2iam
  labels:
	app: kube2iam
spec:
  template:
	metadata:
	  labels:
		name: kube2iam
	spec:
	  hostNetwork: true
	  containers:
		- image: jtblin/kube2iam
		  name: kube2iam

StatefulSet

A ReplicaSet, but …

  • Pods get stable names and network identity
  • Persistent storage requests
  • Storage re-assigned to the “same” pods

StatefulSet

apiVersion: apps/v1beta1
kind: StatefulSet
metadata:
  name: web
spec:
  serviceName: "nginx"
  replicas: 2
  template:
	metadata:
	  labels:
		app: nginx
	spec:
	  containers:
	  - name: nginx
		image: gcr.io/google_containers/nginx-slim:0.8
		ports:
		- containerPort: 80
		  name: web
		volumeMounts:
		- name: www
		  mountPath: /usr/share/nginx/html
  volumeClaimTemplates:
  - metadata:
	  name: www
	  annotations:
		volume.alpha.kubernetes.io/storage-class: anything
	spec:
	  accessModes: [ "ReadWriteOnce" ]
	  resources:
		requests:
		  storage: 1Gi

Deployments

Wraps a ReplicaSet and exposes less RESTy API actions.

Eg, “rollout”

./manifests/v3/deployment-v1.yml

So far, somewhat useless

No one can access our app. Not really anyway.

We’ve got a bunch of IP:Port combinations our app responds on. What do we need now?

Services

  • LoadBalancing
  • Network names (dns)

AKA, “a service”

Service

./manifests/v4/service.yml

Service

$SERVICE_NAME.$NAMESPACE.svc.cluster.local

nginx-static-app-on-k8s.default.svc.cluster.local

We get a stable name, and a stable ip. YAY!

Ingress

Public HTTP entrypoint to a service

./manifests/v5/ingress.yml

Ingress

  • Custom controller backed
  • Implementation different by installation

Config Injection

12 Factor App: Store config in the environment

ConfigMaps & Secrets

ConfigMaps

apiVersion: v1
kind: ConfigMap
metadata:
  name: game-config
data:
  config.yaml: |
    ---
    config:
      db:
        host: db.blah.com

Can then be injected in a Pods ENV vars, or as a file in a volume.

Secrets

The exact same thing.

But with upcoming additional contraints.

Other cool things

  • Custom Resource Definitions
    • (define your own objects on the K8S api)
  • Operators
    • can watch and react on CRDs
    • control the lifecycle of pods etc

Where it [creates vacuum]

  • Many layers of abstractions
  • It’s still not easy to deploy properly
  • Per installation/cluster specifications
  • The ecosystem moves very fast
  • Tool vendors/operators do CI/CD on K8s itself

Try It Out

Demos On

My Cluster

Openshift

  • A (collaborative) fork of Kubernetes
  • Trails 1-2 minor versions behind
  • Implements corporate friendly features
  • Commercial support from RedHat

Openshift

Notable Differences

  • DeploymentConfig vs Deployment
  • Build/BuildConfig vs nothing
  • OAuth out of the box
  • A fancy read/write UI