Mit Spring, Docker und Kubernetes nach Produktion schippern

Mit Spring, Docker und Kubernetes nach Produktion schippern

Beispiele, um eine Spring-Boot-Anwendung in einem Docker-Container zu deployen, gibt es wie Sand am Meer. Aber reicht das auch für einen entspannten Produktionsbetrieb in Kubernetes? Lassen sich Docker und Java so ohne Weiteres kombinieren? Welche Fallen lauern hier? Wie bauen wir verschiedene Umgebungen mit Kubernetes auf? Wie konfigurieren wir unsere Anwendung am besten für diese? Kubernetes macht erst mit Continuous Delivery richtig Spaß, aber wie setzt man das konkret um?

In meinem Vortrag möchte ich all diese und weitere Fragen beantworten. Ich werde erklären, welche JVM Flags für einen sauberen Betrieb mit Docker sinnvoll oder notwendig sind und was das "PID 1 Problem" ist und wie es umgangen werden kann.

Kubernetes braucht für den Betrieb noch einige Zustandsinformationen von unserer Anwendung. Ich zeige, wie wir diese mit Hilfe von Spring Boot bereitstellen können und wie wir unsere Anwendung so konfigurieren, dass wir auch unter "Volllast" ein unterbrechungsfreies Deployment durchführen können. Danach stelle ich eine einfache Lösung für eine Deployment Pipeline via Jenkins und kubectl vor. Als Erweiterung dazu gibt es einen kurzen Ausblick auf das Thema "Git Ops".

Nach dem Vortrag kennt das Publikum die Besonderheiten beim Betrieb von Spring-Boot-Anwendungen in Kubernetes, weiß, welche Fallen hier lauern und wie man diese geschickt umschiffen kann.

E75e669eb121804cbb20052574861288?s=128

Timm Hirsens

March 19, 2019
Tweet

Transcript

  1. Mit Spring, Docker & Kubernetes nach Produktion schippern Timm Hirsens

    Photo by Tyler Lastovich on Unsplash
  2. Über mich Timm Hirsens 29 Jahre Softwareentwickler (Java, JavaScript, …)

    @fr1zle hi@timmhirsens.de
  3. Warum Ich? 2 Jahre Erfahrung ~8 Services in Produktion Viel

    gelernt ... … in einige Fallen getappt
  4. None
  5. Disclaimer Kubernetes ist nicht einfach zu betreiben Es gibt reichlich

    Alternativen Wenn’s geht: Ab in die Cloud statt selber hosten Amazon AWS, Google Cloud, Microsoft Azure, DigitalOcean
  6. Der Beginn...

  7. None
  8. Das richtige Docker Image Alpine Linux 1. ist klein (8mb)

    2. Keine Debug-Tools 3. musl libc statt glibc (Kompabilität, JNI) 4. Optimal wenn die Größe des Images entscheidend ist
  9. Das richtige Docker Image Eigenes (Basis)Image bauen (Centos - 7)

    1. ist relativ klein (70mb) 2. bash, glibc Immer ein versioniertes Image als Basis nehmen, niemals “latest”! Auch nicht für Build-Nodes! https://www.elastic.co/blog/docker-base-centos7
  10. Das richtige Docker Image

  11. Das richtige Docker Image Besser:

  12. Docker overlayfs Linux Java Anwendung, Spring + Libs (app.jar) Linux

    Java Spring + Libs Anwendung VS.
  13. Das richtige Docker Image Am besten™: FROM mycompany.com/java-11-base:11.0.1 ENV TINI_VERSION

    v0.18.0 ADD https://github.com/krallin/tini/releases/download/${TINI_VERSION}/tini /tini RUN chmod +x /tini ENTRYPOINT ["/tini", "--"] VOLUME /tmp ARG DEPENDENCY=target/dependency COPY ${DEPENDENCY}/BOOT-INF/lib /app/lib COPY ${DEPENDENCY}/META-INF /app/META-INF COPY ${DEPENDENCY}/BOOT-INF/classes /app
  14. PID 1 Problem https://github.com/krallin/tini/issues/8

  15. PID 1 Problem Kubernetes hat schon eine Lösung eingebaut In

    jedem POD wird zunächst ein Pause Container gestartet Auf Nummer sicher gehen Docker Image funktioniert auch außerhalb von k8s https://www.ianlewis.org/en/almighty-pause-container
  16. Java Flags Konfiguration in Kubernetes Deployment (bspw.: JAVA_TOOL_OPTIONS) -XX:MaxRAMPercentage=50.0 <

    Java 11 && < Java 8u191: -XX:+UnlockExperimentalVMOptions -XX:+UseCGroupMemoryLimitForHeap -XX:MaxRAMFraction=2 -XX:ParallelGCThreads=1 => wenn irgendwie möglich: Java 11(+) oder Java 8u191(+) verwenden! https://blogs.oracle.com/java-platform-group/java-se-support-for-docker-cpu-and-memory-limits
  17. service Pod 1 Pod 2 Pod 3 Pod 4 deployment

    HTTP ingress production.k8s.company.com
  18. Wir brauchen also… Deployment Service Ingress / LoadBalancer

  19. Deployment apiVersion: apps/v1 kind: Deployment metadata: name: spring-auf-kubernetes labels: app:

    spring-auf-kubernetes spec: replicas: 4 selector: matchLabels: app: spring-auf-kubernetes template: metadata: labels: app: spring-auf-kubernetes spec: containers: - name: spring-auf-kubernetes image: fr1zle/spring-auf-kubernetes:1 ports: - containerPort: 8080
  20. Deployment apiVersion: apps/v1 kind: Deployment metadata: name: spring-auf-kubernetes labels: app:

    spring-auf-kubernetes spec: replicas: 4 selector: matchLabels: app: spring-auf-kubernetes template: metadata: labels: app: spring-auf-kubernetes spec: containers: - name: spring-auf-kubernetes image: fr1zle/spring-auf-kubernetes:1 ports: - containerPort: 8080 apiVersion: apps/v1 kind: Deployment metadata: name: spring-auf-kubernetes labels: app: spring-auf-kubernetes spec: replicas: 4 selector: matchLabels: app: spring-auf-kubernetes
  21. Deployment template: metadata: labels: app: spring-auf-kubernetes spec: containers: - name:

    spring-auf-kubernetes image: fr1zle/spspring-auf-kubernetes:1 ports: - containerPort: 8080 apiVersion: apps/v1 kind: Deployment metadata: name: spring-auf-kubernetes labels: app: spring-auf-kubernetes spec: replicas: 4 selector: matchLabels: app: spring-auf-kubernetes template: metadata: labels: app: spring-auf-kubernetes spec: containers: - name: spring-auf-kubernetes image: fr1zle/spring-auf-kubernetes:1 ports: - containerPort: 8080
  22. Service kind: Service apiVersion: v1 metadata: name: spring-auf-kubernetes spec: selector:

    app: spring-auf-kubernetes ports: - protocol: TCP port: 8080
  23. Ingress apiVersion: extensions/v1beta1 kind: Ingress metadata: labels: app: spring-auf-kubernetes name:

    spring-auf-kubernetes spec: rules: - host: spring-auf-kubernetes.timmhirsens.de http: paths: - backend: serviceName: spring-auf-kubernetes servicePort: 8080 path: /
  24. Kubernetes Probes Liveness und Readiness Probe Beide sollten für den

    produktiven Betrieb aktiv sein Einfaches HTTP Request auf definierten Endpunkt Liveness: Anwendung “lebt”, wenn nicht => Pod wird abgebaut und neu gestartet Readiness: Anwendung ist vollständig gestartet, alle wichtigen (auch externen) Ressourcen stehen zur Verfügung. Wenn nicht => Pod wird aus Load Balancing genommen
  25. service Pod 1 Pod 2 Pod 3 Pod 4 deployment

    HTTP ingress production.k8s.company.com Readiness Pod 1 = Status 500
  26. service Pod 1 Pod 2 Pod 3 deployment HTTP ingress

    production.k8s.company.com Liveness Pod 4 = Status 500 Pod 5 Pod 4 Pod 4
  27. Kubernetes Probes & Spring Boot Actuator verwenden! Liveness: /actuator/info Readiness:

    /actuator/health livenessProbe: httpGet: path: /actuator/info port: 8080 initialDelaySeconds: 30 periodSeconds: 3 readinessProbe: httpGet: path: /actuator/health port: 8080 initialDelaySeconds: 30 periodSeconds: 3
  28. Resource Requests & Limits Requests: Benötigt der Pod mindestens um

    lauffähig zu sein, wird vom Cluster garantiert Limits: Darf der Pod maximal verbrauchen, bei Memory: OOMKill, bei CPU: Drosselung Memory Limits werden von der JVM zur Berechnung des Heaps verwendet CPU Requests zur Berechnung der Threads / verfügbaren CPUs Quotas machen Sinn zum Management des Clusters
  29. Resource Requests & Limits resources: requests: memory: 1Gi cpu: 100m

    limits: memory: 1Gi cpu: 2
  30. Aufbau Kubernetes Cluster Verschiedene Umgebungen (dev, qa & prod) Entweder

    Namespaces oder eigener Cluster
  31. Umgebungen via Namespaces + Nur einmal anmelden + Ein zentraler

    Platz für alles - Keine Chance Updates von Kubernetes zu testen - Fehler in dev können prod stören - Eventuell komplexere Rollenstruktur
  32. Umgebungen via Cluster Ein Cluster pro Umgebung: dev.k8s.company.com qa.k8s.company.com prod.k8s.company.com

    + Kubernetes Updates können getestet werden + Bessere Isolierung der Umgebungen + Möglichkeit Komponententausch zu testen - Keine 100% einheitlichen Bedingungen - Pflegeaufwand in kubectl config
  33. Staging in Kubernetes 12 Factor App:

  34. Staging in Kubernetes In Spring / Kubernetes mehrere Möglichkeiten: application-{env}.properties

    -> SPRING_PROFILES_ACTIVE spring.config.location -> Kubernetes ConfigMap Alles in Umgebungsvariablen im Deployment Spring Cloud Config Server
  35. Rolling Updates Für ein ordentliches Rolling Update muss unsere Anwendung

    sauber heruntergefahren werden Laufende Requests müssen noch abgearbeitet werden Zumindest solche von einer “durchschnittlichen” Dauer => Lösung: Graceful Shutdown
  36. Graceful Shutdown Vorgehen: 1. Wir bekommen ein SIGTERM Signal 2.

    Wir setzen unsere Readiness Probe auf Fehler => kein neuer Trafic mehr 3. Wir warten 30 Sekunden (nach Anwendungsfall, richtiges Percentile wählen) 4. Wir stoppen unsere Anwendung / Beenden den JVM Prozess 5. Liveness Probe läuft auf Fehler => Der Pod wird abgeräumt Schweizerische Bundesbahnen haben Lösung https://github.com/SchweizerischeBundesbahnen/springboot-graceful-shutdown
  37. Deployment Pipeline Branch develop Build JAR Build Image Deploy Dev-Cluster

    Run Integration-Tests
  38. Deployment Pipeline Branch master (after merge) Build RELEASE-JAR Build Image

    Deploy QA-Cluster Run Integration-Tests Manual QA-Tests Approve Promote Deploy to Production
  39. Jenkinsfile “Single Source of Truth” Build Pipeline wird im Repository

    hinterlegt Bei vielen Services kann Teil der Build/Deploy Logik in Shared Libraries ausgelagert werden Manuelle Schritte möglich (Freigabe für Deployment nach Prod.) Leider etwas frickelig, wie so oft bei Jenkins...
  40. Alternativen Concourse CI https://concourse-ci.org/ Gitlab (EE) https://gitlab.com/ Azure brigade https://brigade.sh/

    CircleCI https://circleci.com/ Jenkins X https://jenkins.io/projects/jenkins-x/ Knative Build https://github.com/knative/build
  41. GitOps https://twitter.com/williamdenniss/status/938822673655885824

  42. GitOps Zustand des Clusters / Namespaces liegt im Git Änderungen

    werden durch Build Pipeline angestoßen Deployments erfolgen via Pull / Merge Request
  43. Sonstiges Metriken: Prometheus, Grafana https://micrometer.io Logging: Elasticsearch, Fluentd, Kibana JMX

    & Remote Debugging: kubectl port-forward Service Mesh: https://cloud.google.com/istio/
  44. DANKE https://github.com/fr1zle/spring-auf-kubernetes