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))
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
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
{
…
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