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

Kubernetes Distilled

Kubernetes Distilled

An in-depth guide for the busy Java developer: https://www.youtube.com/watch?v=l7lt6yYLvRo&t=3845s

Alberto C. Ríos

November 04, 2019
Tweet

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