Upgrade to PRO for Only $50/Year—Limited-Time Offer! 🔥

Mit Spring, Docker und Kubernetes nach Produkti...

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.

Timm Hirsens

March 19, 2019
Tweet

More Decks by Timm Hirsens

Other Decks in Programming

Transcript

  1. Warum Ich? 2 Jahre Erfahrung ~8 Services in Produktion Viel

    gelernt ... … in einige Fallen getappt
  2. 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
  3. 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
  4. 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
  5. 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
  6. 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
  7. 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
  8. service Pod 1 Pod 2 Pod 3 Pod 4 deployment

    HTTP ingress production.k8s.company.com
  9. 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
  10. 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
  11. 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
  12. Service kind: Service apiVersion: v1 metadata: name: spring-auf-kubernetes spec: selector:

    app: spring-auf-kubernetes ports: - protocol: TCP port: 8080
  13. 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: /
  14. 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
  15. service Pod 1 Pod 2 Pod 3 Pod 4 deployment

    HTTP ingress production.k8s.company.com Readiness Pod 1 = Status 500
  16. 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
  17. 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
  18. 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
  19. 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
  20. 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
  21. 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
  22. 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
  23. 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
  24. 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
  25. 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...
  26. 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
  27. GitOps Zustand des Clusters / Namespaces liegt im Git Änderungen

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

    & Remote Debugging: kubectl port-forward Service Mesh: https://cloud.google.com/istio/