Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Kubernetes Distilled

Sponsored · SiteGround - Reliable hosting with speed, security, and support you can count on.

Kubernetes Distilled

Avatar for Alberto C. Ríos

Alberto C. Ríos

November 04, 2019

More Decks by Alberto C. Ríos

Other Decks in Programming

Transcript

  1. @Albertoimpl @Olliehughes82 #Devoxx #KubernetesDistilled Kubernetes Distilled An in-depth guide for

    the busy Java developer Ollie Hughes @olliehughes82 Alberto C. Ríos @albertoimpl Pivotal
  2. @Albertoimpl @Olliehughes82 #Devoxx #KubernetesDistilled What this talk is not about

    How to operate or deploy a k8s cluster Live Demo (we’re not brave enough!)
  3. @Albertoimpl @Olliehughes82 #Devoxx #KubernetesDistilled Common Biases about Kubernetes • Kubernetes

    is so complicated ‍ • Only Google & Netflix have the scale to need it • You need a dedicated team to manage it ‍‍‍‍ • I’m happy with [chef/ansible/terraform] ‍♂
  4. @Albertoimpl @Olliehughes82 #Devoxx #KubernetesDistilled “we don’t crash, ever” - Mark

    Zuckerburg, 2010 “we don’t crash, ever” - Your CTO, 2019
  5. @Albertoimpl @Olliehughes82 #Devoxx #KubernetesDistilled Kubernetes in an elevator ride Orchestration

    of software workloads ♻ • Lifecycle • Stateful, Stateless, Batch • Provision of storage • Distribution across computing nodes • Networking & access from the outside world • Security, Policies & Access Control • Declaration of how to run workloads in the cloud
  6. @Albertoimpl @Olliehughes82 #Devoxx #KubernetesDistilled Architecture from 10,00 ft By Marvin

    The Paranoid - Own work, CC BY-SA 4.0, https://commons.wikimedia.org/w/index.php?curid=75140812
  7. @Albertoimpl @Olliehughes82 #Devoxx #KubernetesDistilled Architecture from 10,00 ft By Marvin

    The Paranoid - Own work, CC BY-SA 4.0, https://commons.wikimedia.org/w/index.php?curid=75140812
  8. @Albertoimpl @Olliehughes82 #Devoxx #KubernetesDistilled Architecture from 10,00 ft By Marvin

    The Paranoid - Own work, CC BY-SA 4.0, https://commons.wikimedia.org/w/index.php?curid=75140812 Control Plane
  9. @Albertoimpl @Olliehughes82 #Devoxx #KubernetesDistilled Architecture from 10,00 ft By Marvin

    The Paranoid - Own work, CC BY-SA 4.0, https://commons.wikimedia.org/w/index.php?curid=75140812
  10. @Albertoimpl @Olliehughes82 #Devoxx #KubernetesDistilled Architecture from 10,00 ft By Marvin

    The Paranoid - Own work, CC BY-SA 4.0, https://commons.wikimedia.org/w/index.php?curid=75140812
  11. @Albertoimpl @Olliehughes82 #Devoxx #KubernetesDistilled Architecture from 10,00 ft By Marvin

    The Paranoid - Own work, CC BY-SA 4.0, https://commons.wikimedia.org/w/index.php?curid=75140812
  12. @Albertoimpl @Olliehughes82 #Devoxx #KubernetesDistilled Architecture from 10,00 ft By Marvin

    The Paranoid - Own work, CC BY-SA 4.0, https://commons.wikimedia.org/w/index.php?curid=75140812
  13. @Albertoimpl @Olliehughes82 #Devoxx #KubernetesDistilled Kubernetes Objects • Kubernetes Objects describe

    the desired state of the system. ◦ Analogous to an instance of a resource in the system ◦ They must have a Kind, Group and Version ◦ They are persisted in etcd. apiVersion: networking.k8s.io/v1beta1 kind: Ingress Group Version
  14. @Albertoimpl @Olliehughes82 #Devoxx #KubernetesDistilled Kubernetes Resources • Resources are a

    collection of types in the system eg `deploymets` • Resources belong to a group and have a version.
  15. @Albertoimpl @Olliehughes82 #Devoxx #KubernetesDistilled Building an image from source https://start.spring.io

    @SpringBootApplication @RestController public class ContainersApplication { @RequestMapping ("/") public String home() { return "Hello, world" ; } public static void main(String[] args) { SpringApplication .run(ContainersApplication .class, args); } }
  16. @Albertoimpl @Olliehughes82 #Devoxx #KubernetesDistilled Building an image from source FROM

    openjdk:8 COPY app/build/libs/myapp-0.0.1-SNAPSHOT.jar /opt/myapp/ WORKDIR /opt/myapp/ ENTRYPOINT ["java", "-jar", "/opt/myapp/myapp-0.0.1-SNAPSHOT.jar"] EXPOSE 8080
  17. @Albertoimpl @Olliehughes82 #Devoxx #KubernetesDistilled Building an image from source %

    docker build -t albertoimpl/myapp . -f Dockerfile1 % docker run -p 8080:8080 albertoimpl/myapp . ____ _ __ _ _ /\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \ ( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \ \\/ ___)| |_)| | | | | || (_| | ) ) ) ) ' |____| .__|_| |_|_| |_\__, | / / / / =========|_|==============|___/=/_/_/_/ :: Spring Boot :: (v2.1.8.RELEASE) 2019-09-20 15:22:39.915 INFO 1 --- [ main] c.a.d.containers.ContainersApplication : Starting ContainersApplication on 6f66a251ae87 with PID 1 (/opt/myapp/myapp-0.0.1-SNAPSHOT.jar started by root in /opt/myapp) 2019-09-20 15:22:39.922 INFO 1 --- [ main] c.a.d.containers.ContainersApplication : No active profile set, falling back to default profiles: default 2019-09-20 15:22:42.022 INFO 1 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat initialized with port(s): 8080 (http) 2019-09-20 15:22:42.090 INFO 1 --- [ main] o.apache.catalina.core.StandardService : Starting service [Tomcat] 2019-09-20 15:22:42.090 INFO 1 --- [ main] org.apache.catalina.core.StandardEngine : Starting Servlet engine: [Apache Tomcat/9.0.24] 2019-09-20 15:22:42.284 INFO 1 --- [ main] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring embedded WebApplicationContext 2019-09-20 15:22:42.284 INFO 1 --- [ main] o.s.web.context.ContextLoader : Root WebApplicationContext: initialization completed in 2255 ms 2019-09-20 15:22:42.716 INFO 1 --- [ main] o.s.s.concurrent.ThreadPoolTaskExecutor : Initializing ExecutorService 'applicationTaskExecutor' 2019-09-20 15:22:43.212 INFO 1 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat started on port(s): 8080 (http) with context path '' 2019-09-20 15:22:43.219 INFO 1 --- [ main] c.a.d.containers.ContainersApplication : Started ContainersApplication in 3.965 seconds (JVM running for 4.717)
  18. @Albertoimpl @Olliehughes82 #Devoxx #KubernetesDistilled Building an image from source %

    docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 8c315a54ed41 albertoimpl/myapp "java -jar /opt/myap…" 12 seconds ago Up 11 seconds 0.0.0.0:8080->8080/tcp cranky_khayyam
  19. @Albertoimpl @Olliehughes82 #Devoxx #KubernetesDistilled Almost Random func GetRandomName(retry int) string

    { begin: name := fmt.Sprintf("%s_%s", left[rand. Intn(len(left))], right[rand. Intn(len(right))]) if name == "boring_wozniak" /* Steve Wozniak is not boring */ { goto begin } if retry > 0 { name = fmt.Sprintf("%s%d", name, rand.Intn(10)) } return name } https://github.com/moby/moby/blob/master/pkg/namesgenerator/names-generator.go#L844
  20. @Albertoimpl @Olliehughes82 #Devoxx #KubernetesDistilled Building an image from source %

    docker images REPOSITORY TAG IMAGE ID CREATED SIZE albertoimpl/myapp latest 3401ce1ae307 4 seconds ago 505MB openjdk 8 e8d00769c8a8 6 days ago 488MB
  21. @Albertoimpl @Olliehughes82 #Devoxx #KubernetesDistilled Layers %jar -xf ../app/build/libs/myapp-0.0.1-SNAPSHOT.jar %tree -L

    2 ├── BOOT-INF │ ├── classes │ └── lib ├── META-INF │ └── MANIFEST.MF ├── myapp-0.0.1-SNAPSHOT.jar └── org └── springframework
  22. @Albertoimpl @Olliehughes82 #Devoxx #KubernetesDistilled Layers FROM openjdk:8 COPY BOOT-INF/lib /myapp/lib

    COPY META-INF /myapp/META-INF COPY BOOT-INF/classes /myapp ENTRYPOINT ["java","-cp","myapp:myapp/lib/*","com/albertoimpl/devoxxbe/containers/Cont ainersApplication"] EXPOSE 8080
  23. @Albertoimpl @Olliehughes82 #Devoxx #KubernetesDistilled Layers % docker history albertoimpl/myapp IMAGE

    CREATED CREATED BY SIZE b0663ff6b45d 31 seconds ago /bin/sh -c #(nop) EXPOSE 8080 0B 3786b90f8280 31 seconds ago /bin/sh -c #(nop) ENTRYPOINT ["java" "-cp" … 0B 3e6da2768e29 31 seconds ago /bin/sh -c #(nop) COPY dir:38efc374c2fceb72b… 1.05kB 562b2fee53d2 32 seconds ago /bin/sh -c #(nop) COPY dir:e3df4502282198578… 262B a5513f0b84bb 32 seconds ago /bin/sh -c #(nop) COPY dir:c0c3b7e91e9ce0b4e… 16.7MB 57c2c2d2643d 2 weeks ago /bin/sh -c set -eux; dpkgArch="$(dpkg --pr… 205MB ...
  24. @Albertoimpl @Olliehughes82 #Devoxx #KubernetesDistilled The importance of the base image

    FROM openjdk:8 adoptopenjdk/openjdk8:alpine-slim gcr.io/distroless/java:8
  25. @Albertoimpl @Olliehughes82 #Devoxx #KubernetesDistilled Distroless FROM gcr.io/distroless/java:8 The image Google

    uses to deploy software in production. Reduces the attack surface, you can't even SSH in!
  26. @Albertoimpl @Olliehughes82 #Devoxx #KubernetesDistilled Distroless FROM gcr.io/distroless/java:8 % docker images

    REPOSITORY TAG IMAGE ID CREATED SIZE albertoimpl/myapp latest 6d69e5b15226 4 seconds ago 142MB openjdk 8 e8d00769c8a8 6 days ago 488MB gcr.io/distroless/java 8 2ee039e7a421 49 years ago 125MB
  27. @Albertoimpl @Olliehughes82 #Devoxx #KubernetesDistilled Alpine FROM adoptopenjdk/openjdk8:alpine-slim % docker images

    REPOSITORY TAG IMAGE ID CREATED SIZE albertoimpl/myapp latest 8314b5c99ec7 4 minutes ago 107MB openjdk 8 e8d00769c8a8 6 days ago 488MB adoptopenjdk/openjdk8 alpine-slim a3562aa0b991 4 months ago 90.2MB
  28. @Albertoimpl @Olliehughes82 #Devoxx #KubernetesDistilled Jib plugins { id 'com.google.cloud.tools.jib' version

    '1.6.1' } Reproducible by all the projects All the good practices are already built in Easy to set up Don't need Docker daemon. Perfect for CI
  29. @Albertoimpl @Olliehughes82 #Devoxx #KubernetesDistilled Jib % ./gradlew jibDockerBuild % docker

    images REPOSITORY TAG IMAGE ID CREATED SIZE gcr.io/distroless/java 8 2ee039e7a421 49 years ago 125MB myapp-jib 0.0.1-SNAPSHOT 272b59084a4c 49 years ago 142MB
  30. @Albertoimpl @Olliehughes82 #Devoxx #KubernetesDistilled Tagging Never, ever use LATEST in

    production You would never go to production with SNAPSHOTs, so never go with LATEST
  31. @Albertoimpl @Olliehughes82 #Devoxx #KubernetesDistilled Tagging Jib by default uses the

    artifact version jib.to.image = 'grc.io/albertoimpl/myapp:' + System.nanoTime() ./gradlew jib --image=grc.io/albertoimpl/myapp:{{github.sha}} Alternatively - {{major.minor.patch-commit-jvm-distro}} 2.5.4-6bd32a-jdk11-alpine
  32. @Albertoimpl @Olliehughes82 #Devoxx #KubernetesDistilled Image Registries • DockerHub Is the

    original and most used registry, free for public, paid for private. • GRC/ACR/ECR Are the biggest cloud providers registries, if you are using their hosted kubernetes container services, you should use their registries. • Harbor Open Source registry part of the CNCF. On premise registry. Vulnerability Scanner.
  33. @Albertoimpl @Olliehughes82 #Devoxx #KubernetesDistilled Which Kubernetes Cluster to use? •

    Use a managed service from your cloud provider of choice • Otherwise use an on-prem distribution (OpenShift, PKS, Rancher etc) • For the brave and curious - kubeadm • If you want to really understand how it fits together - Kubernetes the hard way by Kelsey Hightower
  34. @Albertoimpl @Olliehughes82 #Devoxx #KubernetesDistilled KIND Kubernetes IN Docker Local Kubernetes

    clusters using Docker Takes about 30 seconds to spin a new cluster % time kind create cluster Creating cluster "kind" ... ✓ Ensuring node image (kindest/node:v1.15.3) ✓ Preparing nodes ✓ Creating kubeadm config ✓ Starting control-plane ✓ Installing CNI ✓ Installing StorageClass Cluster creation complete. real 0m49.919s
  35. @Albertoimpl @Olliehughes82 #Devoxx #KubernetesDistilled KIND Kubernetes IN Docker % k

    cluster-info Kubernetes master is running at https://127.0.0.1:60694 KubeDNS is running at https://127.0.0.1:60694/api/v1/namespaces/kube-system/services/kube-dns:dns/proxy To further debug and diagnose cluster problems, use 'kubectl cluster-info dump'.
  36. @Albertoimpl @Olliehughes82 #Devoxx #KubernetesDistilled Namespace % k get namespaces NAME

    STATUS AGE default Active 56s kube-node-lease Active 60s kube-public Active 60s kube-system Active 60s Namespaces provide a scope for names and it's a way to divide cluster resources.
  37. @Albertoimpl @Olliehughes82 #Devoxx #KubernetesDistilled Pod apiVersion: v1 kind: Pod metadata:

    name: myapp labels: app: myapp spec: containers: - image: albertoimpl/myapp-jib:latest name: myapp-jib resources: {}
  38. @Albertoimpl @Olliehughes82 #Devoxx #KubernetesDistilled Pod validation % k apply -f

    pod.yaml --dry-run pod/myapp created (dry run) Beta in K8s 1.16 % k apply -f pod.yaml --server-dry-run
  39. @Albertoimpl @Olliehughes82 #Devoxx #KubernetesDistilled Pod % k apply -f pod.yaml

    pod/myapp created % k get pods NAME READY STATUS RESTARTS AGE myapp 0/1 ImagePullBackOff 0 1m % k get pods NAME READY STATUS RESTARTS AGE myapp 1/1 Running 0 1m
  40. @Albertoimpl @Olliehughes82 #Devoxx #KubernetesDistilled Pod % k describe pod myapp

    Name: myapp Namespace: default Priority: 0 PriorityClassName: <none> Node: kind-control-plane/172.17.0.2 Start Time: Sat, 12 Oct 2019 14:59:44 -0500 Labels: app=myapp Annotations: kubectl.kubernetes.io/last-applied-configuration: {"apiVersion":"v1","kind":"Pod","metadata":{"annotations":{},"labels":{"app":"myapp"},"name":"myapp","namespac e":"default"},"spec":{"conta... Status: Running IP: 10.244.0.4
  41. @Albertoimpl @Olliehughes82 #Devoxx #KubernetesDistilled A service defines a set of

    Pods and a policy to access them. The main types are: • ClusterIP: Exposes only inside the cluster. • NodePort: Exposes a port through the node to the world. • LoadBalancer: Exposes the Service externally using a cloud provider’s load balancer. Service
  42. @Albertoimpl @Olliehughes82 #Devoxx #KubernetesDistilled Service apiVersion: v1 kind: Service metadata:

    name: service-myapp-jib spec: selector: app: myapp ports: - protocol: TCP port: 8080 targetPort: 8080 type: NodePort
  43. @Albertoimpl @Olliehughes82 #Devoxx #KubernetesDistilled apiVersion: v1 kind: Service metadata: name:

    service-myapp-jib spec: selector: app: myapp Selectors and labels apiVersion: v1 kind: Pod metadata: name: myapp labels: app: myapp spec: containers:
  44. @Albertoimpl @Olliehughes82 #Devoxx #KubernetesDistilled Service % k apply -f service.yml

    service/service-myapp-jib created % k get services NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 92m service-myapp-jib NodePort 10.111.36.24 <none> 8080:31106/TCP 6s % k port-forward service/service-myapp-jib 8080:8080 Forwarding from 127.0.0.1:8080 -> 8080 Forwarding from [::1]:8080 -> 8080 % curl localhost:8080 Hello, All
  45. @Albertoimpl @Olliehughes82 #Devoxx #KubernetesDistilled ReplicaSet % k apply -f replicaset.yaml

    replicaset.apps/myapp created % k get po,rs NAME READY STATUS RESTARTS AGE pod/myapp 1/1 Running 0 101m pod/myapp-lbb82 1/1 Running 0 43s pod/myapp-m82tv 1/1 Running 0 43s NAME DESIRED CURRENT READY AGE replicaset.extensions/myapp 3 3 3 43s
  46. @Albertoimpl @Olliehughes82 #Devoxx #KubernetesDistilled ReplicaSet % k delete pod/myapp pod

    "myapp" deleted % k get po,svc,rs NAME READY STATUS RESTARTS AGE pod/myapp-l5tfq 1/1 Running 0 18s pod/myapp-lbb82 1/1 Running 0 4m35s pod/myapp-m82tv 1/1 Running 0 4m35s NAME DESIRED CURRENT READY AGE replicaset.extensions/myapp 3 3 3 4m35s
  47. @Albertoimpl @Olliehughes82 #Devoxx #KubernetesDistilled Deployment % k get po,rs,deployments NAME

    READY STATUS RESTARTS AGE pod/myapp-l5tfq 1/1 Running 0 4m11s pod/myapp-lbb82 1/1 Running 0 8m28s pod/myapp-m82tv 1/1 Running 0 8m28s NAME DESIRED CURRENT READY AGE replicaset.extensions/myapp 3 3 3 8m28s NAME READY UP-TO-DATE AVAILABLE AGE deployment.extensions/myapp 3/3 3 3 2s
  48. @Albertoimpl @Olliehughes82 #Devoxx #KubernetesDistilled Deployment with --dry-run k create deployment

    myapp --image=dockerhub.com/albertoimpl/myapp-jib --dry-run -oyaml k expose deployment myapp --target-port=8080 --port=8080 --dry-run -oyaml
  49. @Albertoimpl @Olliehughes82 #Devoxx #KubernetesDistilled Deployment with Dekorate compile 'io.dekorate:kubernetes-spring-starter' annotationProcessor

    'io.dekorate:kubernetes-annotations' dekorate: kubernetes: labels: - app: myapp ports: - protocol: TCP port: 8080 targetPort: 8080 serviceType: NodePort group: albertoimpl
  50. @Albertoimpl @Olliehughes82 #Devoxx #KubernetesDistilled Deployment trade-offs • Dekorate: No YAML!

    YAML or Annotations to generate more YAML • --dry-run: Low level You can version control it Easy to template and integrate with other tools
  51. @Albertoimpl @Olliehughes82 #Devoxx #KubernetesDistilled Probes problems • No probes •

    livenessProbe == readinessProbe • livenessProbe == actuator/health
  52. @Albertoimpl @Olliehughes82 #Devoxx #KubernetesDistilled Probes • livenessProbe to actuator/info •

    readinessProbe to actuator/health There are other values that you should set, but that will depend on your application.
  53. @Albertoimpl @Olliehughes82 #Devoxx #KubernetesDistilled Probes containers: - image: albertoimpl/myapp-jib:latest name:

    myapp-provider livenessProbe: httpGet: path: "/actuator/info" port: 8080 scheme: "HTTP" failureThreshold: 3 initialDelaySeconds: 5 periodSeconds: 30 successThreshold: 1 timeoutSeconds: 10 readinessProbe: httpGet: path: "/actuator/health" port: 8080 scheme: "HTTP" failureThreshold: 3 initialDelaySeconds: 15 periodSeconds: 30 successThreshold: 1 timeoutSeconds: 10
  54. @Albertoimpl @Olliehughes82 #Devoxx #KubernetesDistilled Probes with Dekorate @KubernetesApplication ( livenessProbe

    = @Probe(httpActionPath = "/actuator/info"), readinessProbe = @Probe(httpActionPath = "/actuator/health") ) dekorate: kubernetes: labels: - app: myapp livenessProbe: httpActionPath: "/actuator/info" readinessProbe: httpActionPath: "/actuator/health"
  55. @Albertoimpl @Olliehughes82 #Devoxx #KubernetesDistilled Startup Probe Alpha from k8s 1.16

    startupProbe: httpGet: path: /healthz port: liveness-port failureThreshold: 30 periodSeconds: 10
  56. @Albertoimpl @Olliehughes82 #Devoxx #KubernetesDistilled Skaffold Port forwarding service/service-myapp-jib in namespace

    default, remote port 8080 -> local port 8080 Watching for changes... Port forwarding pod/myapp-7cd498bf7f-8jhxp in namespace default, remote port 5005 -> local port 5005 [myapp-7cd498bf7f-8jhxp myapp-jib] Picked up JAVA_TOOL_OPTIONS: -agentlib:jdwp=transport=dt_socket,server=y,address=5005,suspend=n,quiet=y [myapp-7cd498bf7f-8jhxp myapp-jib] [myapp-7cd498bf7f-8jhxp myapp-jib] . ____ _ __ _ _ [myapp-7cd498bf7f-8jhxp myapp-jib] /\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \ [myapp-7cd498bf7f-8jhxp myapp-jib] ( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \ [myapp-7cd498bf7f-8jhxp myapp-jib] \\/ ___)| |_)| | | | | || (_| | ) ) ) ) [myapp-7cd498bf7f-8jhxp myapp-jib] ' |____| .__|_| |_|_| |_\__, | / / / / [myapp-7cd498bf7f-8jhxp myapp-jib] =========|_|==============|___/=/_/_/_/ [myapp-7cd498bf7f-8jhxp myapp-jib] :: Spring Boot :: (v2.1.8.RELEASE) [myapp-7cd498bf7f-8jhxp myapp-jib] [myapp-7cd498bf7f-8jhxp myapp-jib] 2019-10-25 08:32:28.464 INFO 1 --- [ main] c.a.d.containers.ContainersApplication : Starting
  57. @Albertoimpl @Olliehughes82 #Devoxx #KubernetesDistilled Okteto compile 'org.springframework.boot:spring-boot-devtools' % okteto init

    ✓ Okteto manifest (okteto.yml) created % okteto up Deployment app-okteto doesn't exist in namespace default. Do you want to create a new one? [y/n]: y ✓ Development environment activated ✓ Files synchronized Namespace: default Name: app-okteto Forward: 8080 -> 8080 8088 -> 8088
  58. @Albertoimpl @Olliehughes82 #Devoxx #KubernetesDistilled Okteto root@app-okteto-56b998d9f6-m76p5:/okteto# gradle bootRun Listening for

    transport dt_socket at address: 8088 > Task :bootRun > :compileJava > Resolve dependencies of :compileClasspath > Resolve files of :detachedConfiguration6 . ____ _ __ _ _ /\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \ ( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \ \\/ ___)| |_)| | | | | || (_| | ) ) ) ) ' |____| .__|_| |_|_| |_\__, | / / / / =========|_|==============|___/=/_/_/_/ :: 2019-10-29 09:07:54.142 INFO 257 --- [ restartedMain] o.s.b.d.a.OptionalLiveReloadServer : LiveReload server is running on port 35729 2019-10-29 09:07:54.161 INFO 257 --- [ restartedMain] o.s.b.a.e.web.EndpointLinksResolver : Exposing 2 endpoint(s) beneath base path '/actuator' 2019-10-29 09:07:54.359 INFO 257 --- [ restartedMain] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat started on port(s): 8080 (http) with context path '' 2019-10-29 09:07:54.373 INFO 257 --- [ restartedMain] c.a.d.containers.ContainersApplication : Started ContainersApplication in 7.742 seconds (JVM running for 8.586)
  59. @Albertoimpl @Olliehughes82 #Devoxx #KubernetesDistilled Okteto % curl localhost:8080/hello Hello, world

    @RequestMapping("/hello") public String home() { return "Hello, live!"; } % curl localhost:8080/hello Hello, live!
  60. @Albertoimpl @Olliehughes82 #Devoxx #KubernetesDistilled How do we deploy all of

    this? git push ./gradlew jib --image=albertoimpl/myapp-jib ./gradlew check Deploy to development?? Deploy to production??
  61. @Albertoimpl @Olliehughes82 #Devoxx #KubernetesDistilled Kustomize Lets you customize raw, template-free

    YAML files for multiple purposes, leaving the original YAML untouched and usable as is.
  62. @Albertoimpl @Olliehughes82 #Devoxx #KubernetesDistilled Kustomize % touch kustomization.yaml % kustomize

    edit add resource service.yaml % kustomize edit add resource deployment.yaml % cat kustomization.yaml apiVersion: kustomize.config.k8s.io/v1beta1 kind: Kustomization resources: - k8s/deployment.yaml - k8s/service.yaml % k apply -k ~/app-kustomize
  63. @Albertoimpl @Olliehughes82 #Devoxx #KubernetesDistilled Kustomize overlays ~/app-kustomize ├── base │

    ├── deployment.yaml │ ├── service.yaml │ └── kustomization.yaml └── overlays ├── development │ ├── kustomization.yaml │ ├── profiles.yaml │ └── replicas.yaml └── production ├── kustomization.yaml ├── profiles.yaml └── replicas.yaml
  64. @Albertoimpl @Olliehughes82 #Devoxx #KubernetesDistilled Kustomize overlays % cat development/replicas.yaml apiVersion:

    apps/v1 kind: Deployment metadata: namespace: default name: myapp spec: replicas: 1 % cat production/replicas.yaml apiVersion: apps/v1 kind: Deployment metadata: namespace: default name: myapp spec: replicas: 5
  65. @Albertoimpl @Olliehughes82 #Devoxx #KubernetesDistilled Kustomize profiles % cat development/profiles.yaml apiVersion:

    apps/v1 kind: Deployment …. containers: - name: myapp env: - name: spring.profiles.active value: development - name: my.templated.key value: ${TEMPLATED_VALUE} % cat production/profiles.yaml apiVersion: apps/v1 kind: Deployment .... containers: - name: myapp env: - name: spring.profiles.active value: production - name: my.templated.key value: ${TEMPLATED_VALUE}
  66. @Albertoimpl @Olliehughes82 #Devoxx #KubernetesDistilled Kustomize overlays Will output the deployment

    with the changes applied to it: kustomize build overlays/development kustomize build overlays/production k apply -k overlays/production
  67. @Albertoimpl @Olliehughes82 #Devoxx #KubernetesDistilled Networking @RequestMapping("/customers") public List<String> home() {

    return Arrays.asList("Laura", "Bella", "Olga"); } % curl localhost:8081/customers ["Laura","Bella","Olga"]
  68. @Albertoimpl @Olliehughes82 #Devoxx #KubernetesDistilled LoadBalancer Services ✅ Simple setup ✅

    Robust and secure - uses cloud load balancer service ⚠ Will incur more costs from cloud providers ⚠ Vendor specific configuration ⚠ Less configurable than Ingress ⚠ LoadBalancer not available in on-prem k8s (needs metal-lb, a 3rd party LoadBalancer implementation)
  69. @Albertoimpl @Olliehughes82 #Devoxx #KubernetesDistilled Ingress Pod Applicatio n Container Image

    Registry Applicatio n Container ReplicaSet Deployment Service Ingress Pod Application Container Image Registry Application Container ReplicaSet Deployment Service Ingress
  70. @Albertoimpl @Olliehughes82 #Devoxx #KubernetesDistilled Ingress apiVersion: networking.k8s.io/v1beta1 kind: Ingress metadata:

    name: provider-ingress annotations: ingress.kubernetes.io/rewrite-target: / spec: rules: - host: provider.test.app.com - http: paths: - path: / backend: serviceName: service-myapp-provider servicePort: 8080
  71. @Albertoimpl @Olliehughes82 #Devoxx #KubernetesDistilled Ingress % k apply -f networking/ingress.yaml

    ingress.networking.k8s.io/provider-ingress created % k get svc,ingress NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE service/kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 3d1h service/service-myapp-provider NodePort 10.107.90.53 <none> 8080:31230/TCP 2d21h NAME HOSTS ADDRESS PORTS AGE ingress.extensions/provider-ingress provider.test.app.com 107.178.254.228 80 75s % curl 107.178.254.228/customers ["Laura","Bella","Olga"]
  72. @Albertoimpl @Olliehughes82 #Devoxx #KubernetesDistilled Service Discovery % k get services

    -n kube-system NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE kube-dns ClusterIP 10.96.0.10 <none> 53/UDP,53/TCP,9153/TCP 166m $ cat /etc/resolv.conf search default.svc.cluster.local svc.cluster.local cluster.local nameserver 10.96.0.10 options ndots:5
  73. @Albertoimpl @Olliehughes82 #Devoxx #KubernetesDistilled Service Discovery @Value("${provider.url}") private String providerUrl;

    @RequestMapping("/hello") public String home() { List<String> customers = new RestTemplate().getForObject(providerUrl, List .class); String message = customers.stream().collect(Collectors.joining(",")); return "Welcome: " + message; }
  74. @Albertoimpl @Olliehughes82 #Devoxx #KubernetesDistilled Service Discovery Same namespace: provider.url=http://service-myapp-provider:8080/customers Different

    namespace with Fully Qualified Domain Name: provider.url=http://service-myapp-provider.other-namespace. svc:8080/customers % curl localhost:8080/hello Welcome: Laura,Bella,Olga
  75. @Albertoimpl @Olliehughes82 #Devoxx #KubernetesDistilled Pod Application Container Image Registry Application

    Container ReplicaSet Deployment Service Ingress Persistent Volume Persistent Volumes
  76. @Albertoimpl @Olliehughes82 #Devoxx #KubernetesDistilled Persistent Volumes volumeMounts: - name: mongo-persistent-volume

    - mountPath: /data/db volumes: - name: mongo-persistent-volume hostPath: path: /mount/mongodbdata type: DirectoryOrCreate
  77. @Albertoimpl @Olliehughes82 #Devoxx #KubernetesDistilled Persistent Volumes volumeMounts: - name: mongo-persistent-volume

    - mountPath: /data/db volumes: - name: mongo-persistent-volume gcePersistentDisk: pdName: test-data-disk fsType: ext4
  78. @Albertoimpl @Olliehughes82 #Devoxx #KubernetesDistilled Pod Application Container Image Registry Application

    Container ReplicaSet Deployment Service Ingress Persistent Volume Claim Persistent Volume Claim Persistent Volume
  79. @Albertoimpl @Olliehughes82 #Devoxx #KubernetesDistilled Persistent Volumes Claim volumeMounts: - name:

    mongo-persistent-volume - mountPath: /data/db volumes: - name: mongo-persistent-volume persistentVolumeClaim: claimName: myclaim
  80. @Albertoimpl @Olliehughes82 #Devoxx #KubernetesDistilled Externalised Configuration Pod Application Container Image

    Registry Application Container ReplicaSet Deployment Service Ingress Persistent Volume Claim Persistent Volume Volume Mount Config Map
  81. @Albertoimpl @Olliehughes82 #Devoxx #KubernetesDistilled Config Maps Config Maps are volumes

    we mount into our containers. k create configmap database-configuration --from-literal key=value k create configmap database-configuration --from-file=myconfig.properties apiVersion: v1 kind: ConfigMap metadata: name: database-configuration namespace: default data: MYSQL_DATABASE: "customers" MYSQL_HOSTNAME: "database.service" MYSQL_PASSWORD: "secretpassword" MYSQL_PORT: "3701" MYSQL_USER: "customers"
  82. @Albertoimpl @Olliehughes82 #Devoxx #KubernetesDistilled Config Maps as environment % diff

    --git a/apps/app-networking-provider/k8s/deployment.yaml containers: - image: albertoimpl/myapp-jib:latest name: myapp-provider + envFrom: + - configMapRef: + name: database-configuration livenessProbe: httpGet: path: "/actuator/info"
  83. @Albertoimpl @Olliehughes82 #Devoxx #KubernetesDistilled Config Maps as environment % k

    exec myapp-provider-fcbf996dc-mtrvb -it sh # echo $MYSQL_DATABASE customers # echo $MYSQL_PASSWORD secretpassword
  84. @Albertoimpl @Olliehughes82 #Devoxx #KubernetesDistilled Config Maps as mount apiVersion: v1

    kind: ConfigMap metadata: name: volume-configuration namespace: default data: application.properties: "greeter.message=Hello!!"
  85. @Albertoimpl @Olliehughes82 #Devoxx #KubernetesDistilled Config Maps as mount 19a20,22 +

    volumeMounts: + - name: volume-configuration + mountPath: /config 39a43,49 + volumes: + - name: volume-configuration + configMap: + name: volume-configuration + items: + - key: application.properties + path: application.properties
  86. @Albertoimpl @Olliehughes82 #Devoxx #KubernetesDistilled Config Maps as mount @Value("${greeter.message}") private

    String value; @RequestMapping("/message") public String message() { return value; } % curl localhost:8080/message Hello!! % k exec myapp-provider-67876959-7srbm -it sh # cat config/application.properties greeter.message=Hello!!
  87. @Albertoimpl @Olliehughes82 #Devoxx #KubernetesDistilled Secrets Pod Application Container Image Registry

    Application Container ReplicaSet Deployment Service Ingress Persistent Volume Claim Persistent Volume Volume Mount Config Map Secrets
  88. @Albertoimpl @Olliehughes82 #Devoxx #KubernetesDistilled Secrets % echo -n 'supersecret' |

    base64 c3VwZXJzZWNyZXQ= apiVersion: v1 kind: Secret metadata: name: database-secrets type: Opaque data: DB_PASSWORD: "c3VwZXJzZWNyZXQ=" volumes: - name: database-secrets secret: secretName: database-secrets
  89. @Albertoimpl @Olliehughes82 #Devoxx #KubernetesDistilled Secrets % k exec myapp-provider-8468d89dfd-89pt6 -it

    sh # cat config/DB_PASSWORD supersecret • Encrypting the data on rest: https://kubernetes.io/docs/tasks/administer-cluster/encrypt-data/ • Using Sealed Secrets that allow us to encrypt everything on git: https://github.com/bitnami-labs/sealed-secrets • Using Vault to store them: https://github.com/coreos/vault-operator
  90. @Albertoimpl @Olliehughes82 #Devoxx #KubernetesDistilled Securing your Application • Kubernetes doesn’t

    really help us here • Ingress can provide course-grained auth for a service • Implement an OAuth2 or Openid Connect flow ◦ On Prem: Hydra, Dex, UAA ◦ Cloud: Google, Github etc • Istio can secure communication between pods using mTLS & policy rules
  91. @Albertoimpl @Olliehughes82 #Devoxx #KubernetesDistilled Cluster Security • Users accounts identify

    humans • ServiceAccounts identify a process • Roles describe an action against a resource eg create Pod • RoleBinding describes a set of Users or ServiceAccounts that a Role applies to • Namespaces are a logical grouping of resources User: Jon Can create & read Resource: Pod Namespace: prod Resource: Pod / BookingApp ServiceAccount: DevPods Can read Resource: Secret / DevSecret
  92. @Albertoimpl @Olliehughes82 #Devoxx #KubernetesDistilled Logs from one Pod % k

    logs pod/myapp-55bbf4947c-4l6lq . ____ _ __ _ _ /\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \ ( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \ \\/ ___)| |_)| | | | | || (_| | ) ) ) ) ' |____| .__|_| |_|_| |_\__, | / / / / =========|_|==============|___/=/_/_/_/ :: Spring Boot :: (v2.1.8.RELEASE) 2019-10-18 08:06:19.095 INFO 1 --- [ main] c.a.d.containers.ContainersApplication : Starting ContainersApplication on myapp-55bbf4947c-4l6lq with PID 1 (/app/classes started by root in /) 2019-10-18 08:06:19.107 INFO 1 --- [ main] c.a.d.containers.ContainersApplication : No active profile set, falling back to default profiles: default
  93. @Albertoimpl @Olliehughes82 #Devoxx #KubernetesDistilled Logs from all the Pods with

    stern % stern myapp + myapp-55bbf4947c-57dcn › myapp-jib + myapp-55bbf4947c-4l6lq › myapp-jib + myapp-55bbf4947c-8v8c8 › myapp-jib myapp-55bbf4947c-8v8c8 myapp-jib myapp-55bbf4947c-8v8c8 myapp-jib . ____ _ __ _ _ myapp-55bbf4947c-8v8c8 myapp-jib /\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \ myapp-55bbf4947c-8v8c8 myapp-jib ( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \ myapp-55bbf4947c-8v8c8 myapp-jib \\/ ___)| |_)| | | | | || (_| | ) ) ) ) myapp-55bbf4947c-8v8c8 myapp-jib ' |____| .__|_| |_|_| |_\__, | / / / / myapp-55bbf4947c-8v8c8 myapp-jib =========|_|==============|___/=/_/_/_/ myapp-55bbf4947c-57dcn myapp-jib myapp-55bbf4947c-57dcn myapp-jib . ____ _ __ _ _ myapp-55bbf4947c-57dcn myapp-jib /\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \ myapp-55bbf4947c-57dcn myapp-jib ( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
  94. @Albertoimpl @Olliehughes82 #Devoxx #KubernetesDistilled Logs with the EFK stack https://github.com/kubernetes/kubernetes/tree/master/cluster/ad

    dons/fluentd-elasticsearch % k apply -f . service/elasticsearch-logging created serviceaccount/elasticsearch-logging created clusterrole.rbac.authorization.k8s.io/elasticsearch-logging created clusterrolebinding.rbac.authorization.k8s.io/elasticsearch-logging created statefulset.apps/elasticsearch-logging created configmap/fluentd-es-config-v0.2.0 created serviceaccount/fluentd-es created clusterrole.rbac.authorization.k8s.io/fluentd-es created clusterrolebinding.rbac.authorization.k8s.io/fluentd-es created daemonset.apps/fluentd-es-v2.7.0 created deployment.apps/kibana-logging created service/kibana-logging created
  95. @Albertoimpl @Olliehughes82 #Devoxx #KubernetesDistilled Metrics Logs are a recording from

    events. Metrics represent data combined from measuring events.
  96. @Albertoimpl @Olliehughes82 #Devoxx #KubernetesDistilled Prometheus and Spring Boot implementation 'io.micrometer:micrometer-registry-prometheus'

    management: endpoints: web: exposure: include: "prometheus,info,health" % curl localhost:8001/actuator/prometheus # HELP jvm_memory_committed_bytes The amount of memory in bytes that is committed for the Java virtual machine to use # TYPE jvm_memory_committed_bytes gauge jvm_memory_committed_bytes{area="heap",id="PS Survivor Space",} 1.2582912E7
  97. @Albertoimpl @Olliehughes82 #Devoxx #KubernetesDistilled Prometheus and Spring Boot Pod annotation:

    annotations: prometheus.io/path: '/actuator/prometheus' prometheus.io/scrape: 'true' prometheus.io/port: '8080' Scraping rule in prometheus: - job_name: 'spring-actuator' metrics_path: '/actuator/prometheus' scrape_interval: 5s static_configs: - targets: ['myapp:8080']
  98. @Albertoimpl @Olliehughes82 #Devoxx #KubernetesDistilled Declarative APIs instead of Imperative APIs

    1. See https://thenewstack.io/kubernetes-design-and-development-explained/ Describe Desired State Operator Control Plane Persist Desired State Controller Watch for changes of interest Ensure cluster is in desired state
  99. @Albertoimpl @Olliehughes82 #Devoxx #KubernetesDistilled Meet the user where they are

    My Application Secrets Persistence Configuration Service Discovery
  100. @Albertoimpl @Olliehughes82 #Devoxx #KubernetesDistilled What the future may look like

    Cloud Native Application Bundle Knative Cloud Native Buildpacks Project Riff