Slide 1

Slide 1 text

Alice and the lost pod: practical guide to Kubernetes in Scala

Slide 2

Slide 2 text

Roksolana Diachuk • Big Data Developer at Captify • Diversity & Inclusion ambassador for Captify Kyiv o ff ice • Women Who Code Kyiv Data Engineering Lead and Mentor • Speaker and traveller

Slide 3

Slide 3 text

DISCLAIMER

Slide 4

Slide 4 text

In previous episode talk…

Slide 5

Slide 5 text

Functional forest

Slide 6

Slide 6 text

“Why these houses look like constructor details?” - asks Alice. “This is one of our interesting features. All of our objects constitute type system and they are immutable”

Slide 7

Slide 7 text

TYPE SYSTEM IMMUTABLE DECLARATIVE DSL

Slide 8

Slide 8 text

magic-db- cluster-0

Slide 9

Slide 9 text

magic-db- cluster-0

Slide 10

Slide 10 text

2 years later

Slide 11

Slide 11 text

Alice is a successful Scala developer working in a big city. Getting tired of work s h e d e c i d e d t o spend some time in the countryside in her uncle’s house.

Slide 12

Slide 12 text

Reading time

Slide 13

Slide 13 text

On one of the days Alice was walking in the forest around the house. It kept a lot of memories of childhood she held dear.

Slide 14

Slide 14 text

No content

Slide 15

Slide 15 text

No content

Slide 16

Slide 16 text

cave@k8s % _

Slide 17

Slide 17 text

cave@k8s % kubectl get al l

Slide 18

Slide 18 text

cave@k8s % kubectl get al l NAME DESIRED CURRENT AGE pv/magic-db 1 1 2y120h

Slide 19

Slide 19 text

cave@k8s % kubectl describe pv magic-db- p v

Slide 20

Slide 20 text

cave@k8s % kubectl describe pv magic-db- p v Name: magic-db-pv Namespace: default StorageClass: hostpath Status: Running Volume: Labels:

Slide 21

Slide 21 text

magic-db- cluster-0 Long time no see! !

Slide 22

Slide 22 text

magic-db- cluster-0

Slide 23

Slide 23 text

magic-db- cluster-0 “I’m so happy to see you!”

Slide 24

Slide 24 text

No content

Slide 25

Slide 25 text

magic-db- cluster-0

Slide 26

Slide 26 text

magic-db- cluster-0

Slide 27

Slide 27 text

magic-db- cluster-0

Slide 28

Slide 28 text

“But how can I help you?” magic-db- cluster-0 You can use the power of your Scala knowledge to help me.

Slide 29

Slide 29 text

“Ok, I can do this”

Slide 30

Slide 30 text

java-k8s-client fabric8io skuber

Slide 31

Slide 31 text

skuber

Slide 32

Slide 32 text

Data model Object kinds List kinds Pods, ReplicationController ObjectResource Pod.Spec, ReplicationController.Status ListResource[Pod], ListResource[Node]

Slide 33

Slide 33 text

val k8s = client.init( client.defaultK8sCon fi g.currentContext)

Slide 34

Slide 34 text

val container = Container( name = “magic-link”, image = “magic-link:1.7.9”) val labels = “app" -> "magic-link" val template = Pod.Spec() .addContainer(container).addLabel(labels) k8s.create(Pod(“magic-link-pod”, template))

Slide 35

Slide 35 text

apiVersion: v1 kind: Pod metadata: name: magic-link-pod labels: app: magic-link spec: containers: - name: magic-link image: magic-link:1.7.9

Slide 36

Slide 36 text

kubectl get all

Slide 37

Slide 37 text

NAME READY STATUS AGE …. magic-db- cluster-0

Slide 38

Slide 38 text

kubectl get all

Slide 39

Slide 39 text

YA NAME READY STATUS AGE pod/magic-link-pod-djf8 0/1 ContainerCreating 5s pod/magic-link-pod-djf8 1/1 Running 37s magic-db- cluster-0 magic-svc -djf8 YAY!

Slide 40

Slide 40 text

k8s.delete[Pod](“magic-link-pod”)

Slide 41

Slide 41 text

Magic-link -djf8

Slide 42

Slide 42 text

magic-link -djf8

Slide 43

Slide 43 text

“We need more than that. But what exactly?”

Slide 44

Slide 44 text

“We need more pods!” magic-db- cluster-0 What? !

Slide 45

Slide 45 text

val container = Container( name = “magic-link”, image = “magic-link:1.7.9”) val labels = “app" -> "magic-link" val template = Pod.Template.Spec .addContainer(container) .addLabel(labels)

Slide 46

Slide 46 text

container.exposePort(80) val deployment = Deployment(“magic-link“) .withReplicas(3) .withTemplate(template) val created = k8s.create(deployment)

Slide 47

Slide 47 text

NAME READY STATUS AGE deployment/magic-link 1/1 Running 32s NAME DESIRED CURRENT AGE replicaset/magic-link 1 1 27s

Slide 48

Slide 48 text

val container = Container( name = “magic-link”, image = “magic-link:1.8”) container.exposePort(80) val deployment = Deployment(“magic-link“) .withReplicas(3) .withTemplate(template) val created = k8s.create(deployment)

Slide 49

Slide 49 text

created.recoverWith { case ex: K8SException if (ex.status.code.contains(409)) => { println("Deployment object already exists”) (k8s get[Deployment] deployment.name) fl atMap { currentDep => println("retrieved latest deployment, now updating”)
 val updated = deployment.withResourceVersion( currentDep.metadata.resourceVersion) k8s update updated> }

Slide 50

Slide 50 text

NAME READY STATUS AGE pod/magic-link-wjnr 1/1 Running 15s pod/magic-link-eh8f 1/1 Running 11s pod/magic-link-eu2r 1/1 Running 7s magic-link- eu2r magic-link- eh8f magic-link- wjnr

Slide 51

Slide 51 text

val scale = k8s.getScale[Deployment] (“magic-link”) val upscale = scale.withSpecReplicas(6) k8s.updateScale[Deployment] (“magic-link”, upscale)

Slide 52

Slide 52 text

NAME READY STATUS AGE pod/magic-link-7wjr 1/1 Running 32s pod/magic-link-eh8f 1/1 Running 28s pod/magic-link-eu2r 1/1 Running 23s pod/magic-link-uew 1/1 Running 12s pod/magic-link-omr8 1/1 Running 8s pod/magic-link-yt8p 1/1 Running 5s

Slide 53

Slide 53 text

private def listPods(pods: List[Pod]) = { logger.info(“POD NAMESPACE STATUS”) … logger.info(s"$name $ns $phase”) }

Slide 54

Slide 54 text

… pods.map { pod: Pod => val name = pod.name val ns = pod.namespace val phaseOpt = for { status <- pod.status phase <- status.phase } yield phase } …

Slide 55

Slide 55 text

val podsList: Future[PodList] = k8s.listInNamespace[PodList](“default”) val printPods = podsList.map( pods => listPods(pods.items))

Slide 56

Slide 56 text

NAME NAMESPACE STATUS pod/magic-link-7wjr default Running pod/magic-link-eh8f default Running pod/magic-link-eu2r default Running pod/magic-link-uew default Running pod/magic-link-omr8 default Running pod/magic-link-yt8p default Running

Slide 57

Slide 57 text

val service = Service(“magic-svc”, labels, 80) .exposeOnNodePort(30001 -> 80) k8s.create(service)

Slide 58

Slide 58 text

NAME TYPE PORT(S) AGE svc/magic-svc NodePort 30001 27s

Slide 59

Slide 59 text

magic-svc: df-np: Is that really you? We thought we’ve lost you! magic-svc: It’s magic-db-cluster-0 df-np: Who’s that? Hello!

Slide 60

Slide 60 text

magic-db- cluster-0 Oh my god! I can talk to someone from home

Slide 61

Slide 61 text

“It’s good but there should be another way” magic-db- cluster-0 What do you mean?!

Slide 62

Slide 62 text

“But I really don’t know what else I can build help you” magic-db- cluster-0

Slide 63

Slide 63 text

But Alice, you can build anything you want magic-db- cluster-0 “But I really don’t know what else I can build help you”

Slide 64

Slide 64 text

“You’re a genius! And I know exactly what to build for you.” Something that will let me fly back home magic-db- cluster-0

Slide 65

Slide 65 text

Custom object Service Namespace Volume Deployment StatefulSet ReplicaSet DaemonSet Pod

Slide 66

Slide 66 text

No content

Slide 67

Slide 67 text

case class CustomResource[Sp, St] ( override val kind: String, override val apiVersion: String, override val metadata: String,
 spec: Sp,
 status: Option[St] extends ObjectResource { …

Slide 68

Slide 68 text

… def withMetadata(metadata: ObjectMetadata) def withName(name: String) def withGenerateName(generateName: String) def withNamespace(namespace: String) def withLabels(labels: Tuple2[String, String]*) def withAnnotations(generateName: Tuple2[String, String]*) def withFinalizers(namespace: String*) def withStatus(status: St) }

Slide 69

Slide 69 text

object CustomResource { def statusMethodEnabler[C <: CustomResource[_,_]] (implicit rd: ResourceDe fi nition[C]: HasStatusSubresource[C]) = { if (!rd.spec.subresources.map(_.status).isDe fi ned) throw new K8sException(Status(message = Some(“Status sub resource must be de fi ned”)))
 new HasStatusSubresource[C] {} } …

Slide 70

Slide 70 text

No content

Slide 71

Slide 71 text

val crd = CustomResourceDe fi nition( name = “launcher.io”, kind = “launcher”, scope = Scope.Cluster) implicit val system = ActorSystem() implicit val dispatcher = system.dispatcher val saved = saveCrd(crd)

Slide 72

Slide 72 text

def saveCrd(crd: CustomResourceDe fi nition) = { k8s.create(crd).recoverWith { case notFound: K8sException if (notFound.status.code.contains(404)) => { println(“Unable to create CRD”) throw notFound } …

Slide 73

Slide 73 text

case alreadyExists: K8sException if (alreadyExists.status.code.contains(404)) => { (k8s get[CustomResourceDe fi nition] crd.name) fl atMap { existing => val currentVersion = existing.metadata.resourceVersion val newMeta = crd.metadata.copy( resourceVersion = currentVersion) val updatedObj = crd.copy(metadata = newMeta) k8s update updatedObj> }

Slide 74

Slide 74 text

NAME READY STATUS AGE launcher-crd 1/1 Running 33s magic-db- cluster-0

Slide 75

Slide 75 text

“Yay! You’re fl ying home!” magic-db- cluster-0

Slide 76

Slide 76 text

Thank you! Will see you again someday! magic-db- cluster-0

Slide 77

Slide 77 text

No content

Slide 78

Slide 78 text

Now Alice knew that her knowledge of Scala could help her to build a link between Scala and Kubernetes. And she could build anything she wanted!

Slide 79

Slide 79 text

No content

Slide 80

Slide 80 text

My contact info dead_flowers22 roksolana-d roksolanadiachuk roksolanad