Cloud Native DevOps mit Kubernetes

Book description

Cloud-Experten John Arundel und Justin Domingus zeigen Ihnen in diesem pragmatischen Buch, was Kubernetes kann, welche Tools und Frameworks Ihnen zur Verfügung stehen und wie Sie in der Cloud eine Anwendung mit Kubernetes entwickeln und deployen.

Erfahren Sie alles über das Ökosystem von Kubernetes und lernen Sie erprobte Lösungen für die tagtäglichen Probleme kennen. Bauen Sie Schritt für Schritt eine Cloud-native Beispielanwendung und die zugehörige Infrastruktur auf, zusammen mit einer Entwicklungsumgebung und Continuous-Development-Pipeline, die Sie für Ihre eigenen Anwendungen nutzen können.

Table of contents

  1. Cover
  2. Titel
  3. Impressum
  4. Inhaltsverzeichnis
  5. Vorwort
  6. Einleitung
  7. Danksagung
  8. 1 Revolution in der Cloud
    1. 1.1 Die Entstehung der Cloud
    2. 1.1.1 Zeit einkaufen
    3. 1.1.2 Infrastructure as a Service
    4. 1.2 Der Aufstieg von DevOps
    5. 1.2.1 Keiner versteht DevOps
    6. 1.2.2 Der Geschäftsvorteil
    7. 1.2.3 Infrastruktur als Code
    8. 1.2.4 Gemeinsames Lernen
    9. 1.3 Das Aufkommen von Containern
    10. 1.3.1 State of the Art
    11. 1.3.2 Innerhalb der Box denken
    12. 1.3.3 Software in Containern verpacken
    13. 1.3.4 Plug-and-Play-Anwendungen
    14. 1.4 Das Container-Orchester dirigieren
    15. 1.5 Kubernetes
    16. 1.5.1 Von Borg zu Kubernetes
    17. 1.5.2 Was macht Kubernetes so wertvoll?
    18. 1.5.3 Wird Kubernetes wieder verschwinden?
    19. 1.5.4 Kubernetes kann nicht alles
    20. 1.6 Cloud Native
    21. 1.7 Die Zukunft von Operations
    22. 1.7.1 Verteiltes DevOps
    23. 1.7.2 Manches wird zentralisiert bleiben
    24. 1.7.3 Developer Productivity Engineering
    25. 1.7.4 Sie sind die Zukunft
    26. 1.8 Zusammenfassung
  9. 2 Erste Schritte mit Kubernetes
    1. 2.1 Starten Sie Ihren ersten Container
    2. 2.1.1 Docker Desktop installieren
    3. 2.1.2 Was ist Docker?
    4. 2.1.3 Ein Container-Image starten
    5. 2.2 Die Demo-Anwendung
    6. 2.2.1 Den Quellcode anschauen
    7. 2.2.2 Go?
    8. 2.2.3 Wie die Demo-Anwendung funktioniert
    9. 2.3 Einen Container bauen
    10. 2.3.1 Dockerfiles verstehen
    11. 2.3.2 Minimale Container-Images
    12. 2.3.3 docker image build ausführen
    13. 2.3.4 Ihrem Image einen Namen geben
    14. 2.3.5 Port Forwarding
    15. 2.4 Container-Registries
    16. 2.4.1 Sich an der Registry authentifizieren
    17. 2.4.2 Ihr Image benennen und pushen
    18. 2.4.3 Ihr Image ausführen
    19. 2.5 Hallo Kubernetes
    20. 2.5.1 Die Demo-App starten
    21. 2.5.2 Wenn der Container nicht startet
    22. 2.6 Minikube
    23. 2.7 Zusammenfassung
  10. 3 Kubernetes-Installationen
    1. 3.1 Cluster-Architektur
    2. 3.1.1 Die Steuerungsebene
    3. 3.1.2 Komponenten auf den Knoten
    4. 3.1.3 Hochverfügbarkeit
    5. 3.2 Die Kosten eines selbst gehosteten Kubernetes
    6. 3.2.1 Es ist mehr, als Sie denken
    7. 3.2.2 Es geht nicht nur um das initiale Setup
    8. 3.2.3 Tools nehmen Ihnen nicht die ganze Arbeit ab
    9. 3.2.4 Kubernetes ist schwer
    10. 3.2.5 Administrativer Overhead
    11. 3.2.6 Beginnen Sie mit Managed Services
    12. 3.3 Managed Kubernetes Services
    13. 3.3.1 Google Kubernetes Engine (GKE)
    14. 3.3.2 Cluster Autoscaling
    15. 3.3.3 Amazon Elastic Container Service for Kubernetes (EKS)
    16. 3.3.4 Azure Kubernetes Service (AKS)
    17. 3.3.5 OpenShift
    18. 3.3.6 IBM Cloud Kubernetes Service
    19. 3.3.7 Heptio Kubernetes Subscription (HKS)
    20. 3.4 Turnkey-Kubernetes-Lösungen
    21. 3.4.1 Stackpoint
    22. 3.4.2 Containership Kubernetes Engine (CKE)
    23. 3.5 Kubernetes-Installer
    24. 3.5.1 kops
    25. 3.5.2 Kubespray
    26. 3.5.3 TK8
    27. 3.5.4 Kubernetes The Hard Way
    28. 3.5.5 kubeadm
    29. 3.5.6 Tarmak
    30. 3.5.7 Rancher Kubernetes Engine (RKE)
    31. 3.5.8 Puppet Kubernetes Module
    32. 3.5.9 Kubeformation
    33. 3.6 Kaufen oder bauen: Unsere Empfehlungen
    34. 3.6.1 Run Less Software
    35. 3.6.2 Nutzen Sie Managed Kubernetes, wenn Sie können
    36. 3.6.3 Aber was ist mit dem Vendor Lock-In?
    37. 3.6.4 Nutzen Sie Self-Hosting-Standard-Tools für Kubernetes, wenn Sie müssen
    38. 3.6.5 Wenn Ihre Auswahl begrenzt ist
    39. 3.6.6 Bare-Metal und On-Prem
    40. 3.7 Clusterless Container Services
    41. 3.7.1 Amazon Fargate
    42. 3.7.2 Azure Container Instances (ACI)
    43. 3.8 Zusammenfassung
  11. 4 Mit Kubernetes-Objekten arbeiten
    1. 4.1 Deployments
    2. 4.1.1 Supervising und Scheduling
    3. 4.1.2 Container neu starten
    4. 4.1.3 Deployments abfragen
    5. 4.2 Pods
    6. 4.3 ReplicaSets
    7. 4.4 Den gewünschten Status warten
    8. 4.5 Der Kubernetes-Scheduler
    9. 4.6 Ressourcen-Manifeste im YAML-Format
    10. 4.6.1 Ressourcen sind Daten
    11. 4.6.2 Deployment-Manifeste
    12. 4.6.3 kubectl apply verwenden
    13. 4.6.4 Service-Ressourcen
    14. 4.6.5 Das Cluster mit kubectl abfragen
    15. 4.6.6 Ressourcen noch leistungsfähiger machen
    16. 4.7 Helm: Ein Kubernetes-Paketmanager
    17. 4.7.1 Helm installieren
    18. 4.7.2 Einen Helm-Chart installieren
    19. 4.7.3 Charts, Repositories und Releases
    20. 4.7.4 Helm-Releases anzeigen
    21. 4.8 Zusammenfassung
  12. 5 Ressourcen managen
    1. 5.1 Ressourcen verstehen
    2. 5.1.1 Ressourcen-Einheiten
    3. 5.1.2 Ressourcen-Anforderungen
    4. 5.1.3 Ressourcen-Grenzen
    5. 5.1.4 Halten Sie Ihre Container klein
    6. 5.2 Den Lebenszyklus des Containers managen
    7. 5.2.1 Liveness-Probe
    8. 5.2.2 Verzögerung und Häufigkeit der Probe
    9. 5.2.3 Andere Arten von Proben
    10. 5.2.4 gRPC-Proben
    11. 5.2.5 Readiness-Proben
    12. 5.2.6 Dateibasierte Readiness-Proben
    13. 5.2.7 minReadySeconds
    14. 5.2.8 Pod Disruption Budgets
    15. 5.3 Namensräume verwenden
    16. 5.3.1 Mit Namensräumen arbeiten
    17. 5.3.2 Welchen Namensraum sollte ich verwenden?
    18. 5.3.3 Service-Adressen
    19. 5.3.4 Resource Quotas
    20. 5.3.5 Standards für Ressourcen-Anforderungen und -Grenzen
    21. 5.4 Die Kosten von Clustern optimieren
    22. 5.4.1 Deployments optimieren
    23. 5.4.2 Pods optimieren
    24. 5.4.3 Vertical Pod Autoscaler
    25. 5.4.4 Knoten optimieren
    26. 5.4.5 Storage optimieren
    27. 5.4.6 Ungenutzte Ressourcen aufräumen
    28. 5.4.7 Freie Kapazitäten prüfen
    29. 5.4.8 Reservierte Instanzen nutzen
    30. 5.4.9 Präemptive (Spot)-Instanzen verwenden
    31. 5.4.10 Sorgen Sie für eine ausgeglichene Verteilung Ihrer Workloads
    32. 5.5 Zusammenfassung
  13. 6 Cluster betreiben
    1. 6.1 Sizing und Skalieren des Clusters
    2. 6.1.1 Kapazitätsplanung
    3. 6.1.2 Knoten und Instanzen
    4. 6.1.3 Das Cluster skalieren
    5. 6.2 Konformitäts-Prüfungen
    6. 6.2.1 CNCF Certification
    7. 6.2.2 Konformitäts-Tests mit Sonobuoy
    8. 6.3 Validierung und Auditing
    9. 6.3.1 K8Guard
    10. 6.3.2 Copper
    11. 6.3.3 kube-bench
    12. 6.3.4 Kubernetes Audit Logging
    13. 6.4 Chaos Testing
    14. 6.4.1 Nur Produktion ist Produktion
    15. 6.4.2 chaoskube
    16. 6.4.3 kube-monkey
    17. 6.4.4 PowerfulSeal
    18. 6.5 Zusammenfassung
  14. 7 Power-Tools für Kubernetes
    1. 7.1 Die Arbeit mit kubectl
    2. 7.1.1 Shell-Aliasse
    3. 7.1.2 Kurze Flags verwenden
    4. 7.1.3 Ressourcen-Typen abkürzen
    5. 7.1.4 Autovervollständigen von kubectl-Befehlen
    6. 7.1.5 Hilfe erhalten
    7. 7.1.6 Hilfe zu Kubernetes-Ressourcen erhalten
    8. 7.1.7 Detaillertere Ausgaben nutzen
    9. 7.1.8 Mit JSON-Daten und jq arbeiten
    10. 7.1.9 Objekte beobachten
    11. 7.1.10 Objekte beschreiben
    12. 7.2 Mit Ressourcen arbeiten
    13. 7.2.1 Imperative kubectl-Befehle
    14. 7.2.2 Wann Sie keine imperativen Befehle verwenden sollten
    15. 7.2.3 Ressourcen-Manifeste generieren
    16. 7.2.4 Ressourcen exportieren
    17. 7.2.5 Ressourcen diffen
    18. 7.3 Mit Containern arbeiten
    19. 7.3.1 Die Logs eines Containers anzeigen
    20. 7.3.2 Sich mit einem Container verbinden
    21. 7.3.3 Kubernetes-Ressourcen mit kubespy beobachten
    22. 7.3.4 Einen Container-Port weiterleiten
    23. 7.3.5 Befehle in Containern ausführen
    24. 7.3.6 Container zur Fehlersuche ausführen
    25. 7.3.7 BusyBox-Befehle verwenden
    26. 7.3.8 BusyBox zu Ihren Containern hinzufügen
    27. 7.3.9 Programme auf einem Container installieren
    28. 7.3.10 Live Debugging mit kubesquash
    29. 7.4 Kontexte und Namensräume
    30. 7.4.1 kubectx und kubens
    31. 7.4.2 kube-ps1
    32. 7.5 Shells und Tools für Kubernetes
    33. 7.5.1 kube-shell
    34. 7.5.2 Click
    35. 7.5.3 kubed-sh
    36. 7.5.4 Stern
    37. 7.6 Bauen Sie Ihre eigenen Kubernetes-Tools
    38. 7.7 Zusammenfassung
  15. 8 Container ausführen
    1. 8.1 Container und Pods
    2. 8.1.1 Was ist ein Container?
    3. 8.1.2 Was gehört in einen Container?
    4. 8.1.3 Was gehört in einen Pod?
    5. 8.2 Container-Manifeste
    6. 8.2.1 Image-Bezeichner
    7. 8.2.2 Das Tag »latest«
    8. 8.2.3 Container-Digests
    9. 8.2.4 Basis-Image-Tags
    10. 8.2.5 Ports
    11. 8.2.6 Ressourcen-Anforderungen und -Grenzen
    12. 8.2.7 Image-Pull-Richtlinie
    13. 8.2.8 Umgebungsvariablen
    14. 8.3 Sicherheit von Containern
    15. 8.3.1 Container als Nicht-Root-Benutzer ausführen
    16. 8.3.2 Root-Container blockieren
    17. 8.3.3 Ein Read-Only-Dateisystem nutzen
    18. 8.3.4 Privilege Escalation deaktivieren
    19. 8.3.5 Capabilities
    20. 8.3.6 Pod Security Contexts
    21. 8.3.7 Pod Security Policies
    22. 8.3.8 Pod Service-Accounts
    23. 8.4 Volumes
    24. 8.4.1 emptyDir-Volumes
    25. 8.4.2 Persistente Volumes
    26. 8.5 Neustart-Richtlinien
    27. 8.6 Image Pull Secrets
    28. 8.7 Zusammenfassung
  16. 9 Pods managen
    1. 9.1 Labels
    2. 9.1.1 Was sind Labels?
    3. 9.1.2 Selektoren
    4. 9.1.3 Komplexere Selektoren
    5. 9.1.4 Andere Einsatzzwecke für Labels
    6. 9.1.5 Labels und Anmerkungen
    7. 9.2 Node Affinities
    8. 9.2.1 Hard Affinities
    9. 9.2.2 Soft Affinities
    10. 9.3 Pod Affinities und Anti-Affinities
    11. 9.3.1 Pods zusammenhalten
    12. 9.3.2 Pods auseinanderhalten
    13. 9.3.3 Soft Anti-Affinities
    14. 9.3.4 Wann Sie Pod Affinities verwenden
    15. 9.4 Taints und Tolerations
    16. 9.5 Pod-Controller
    17. 9.5.1 DaemonSets
    18. 9.5.2 StatefulSets
    19. 9.5.3 Jobs
    20. 9.5.4 Cronjobs
    21. 9.5.5 Horizontal Pod Autoscaler
    22. 9.5.6 PodPresets
    23. 9.5.7 Operatoren und Custom Resource Definitions (CRDs)
    24. 9.6 Ingress-Ressourcen
    25. 9.6.1 Ingress-Regeln
    26. 9.6.2 TLS Termination mit Ingress
    27. 9.6.3 Ingress-Controller
    28. 9.7 Istio
    29. 9.8 Envoy
    30. 9.9 Zusammenfassung
  17. 10 Konfiguration und Secrets
    1. 10.1 ConfigMaps
    2. 10.1.1 ConfigMaps erstellen
    3. 10.1.2 Umgebungsvariablen aus ConfigMaps setzen
    4. 10.1.3 Die gesamte Umgebung aus einer ConfigMap erstellen
    5. 10.1.4 Umgebungsvariablen in Argumenten für Befehle einsetzen
    6. 10.1.5 Konfigurationsdateien aus ConfigMaps erstellen
    7. 10.1.6 Pods bei einer Konfigurationsänderung aktualisieren
    8. 10.2 Kubernetes-Secrets
    9. 10.2.1 Secrets als Umgebungsvariablen verwenden
    10. 10.2.2 Secrets in Dateien schreiben
    11. 10.2.3 Secrets lesen
    12. 10.2.4 Zugriff auf Secrets
    13. 10.2.5 Encryption at Rest
    14. 10.2.6 Secrets behalten
    15. 10.3 Strategien zum Verwalten von Secrets
    16. 10.3.1 Secrets in der Versionsverwaltung verschlüsseln
    17. 10.3.2 Secrets remote ablegen
    18. 10.3.3 Ein dediziertes Secrets-Management-Tool einsetzen
    19. 10.3.4 Empfehlungen
    20. 10.4 Secrets mit Sops verschlüsseln
    21. 10.4.1 Einführung in Sops
    22. 10.4.2 Eine Datei mit Sops verschlüsseln
    23. 10.4.3 Ein KMS-Backend verwenden
    24. 10.5 Zusammenfassung
  18. 11 Sicherheit und Backups
    1. 11.1 Zugriffskontrolle und Berechtigungen
    2. 11.1.1 Den Zugriff vom Cluster abhängig machen
    3. 11.1.2 Role-Based Access Control (RBAC)
    4. 11.1.3 Rollen
    5. 11.1.4 Rollen mit Benutzern verbinden
    6. 11.1.5 Welche Rolle benötige ich?
    7. 11.1.6 Den Zugriff auf cluster-admin beschränken
    8. 11.1.7 Anwendungen und Deployment
    9. 11.1.8 Fehlersuche mit RBAC
    10. 11.2 Security Scanning
    11. 11.2.1 Clair
    12. 11.2.2 Aqua
    13. 11.2.3 Anchore Engine
    14. 11.3 Backups
    15. 11.3.1 Muss ich ein Backup von Kubernetes machen?
    16. 11.3.2 Backup von etcd
    17. 11.3.3 Backup des Ressourcen-Status
    18. 11.3.4 Backup des Cluster-Status
    19. 11.3.5 Große und kleine Katastrophen
    20. 11.3.6 Velero
    21. 11.4 Den Cluster-Status monitoren
    22. 11.4.1 kubectl
    23. 11.4.2 CPU- und Speicherauslastung
    24. 11.4.3 Konsole des Cloud-Providers
    25. 11.4.4 Kubernetes Dashboard
    26. 11.4.5 Weave Scope
    27. 11.4.6 kube-ops-view
    28. 11.4.7 node-problem-detector
    29. 11.5 Weitere Informationen
    30. 11.6 Zusammenfassung
  19. 12 Kubernetes-Anwendungen deployen
    1. 12.1 Manifeste mit Helm bauen
    2. 12.1.1 Was befindet sich in einem Helm-Chart?
    3. 12.1.2 Helm-Templates
    4. 12.1.3 Variablen interpolieren
    5. 12.1.4 Template-Werte mit Anführungszeichen versehen
    6. 12.1.5 Abhängigkeiten festlegen
    7. 12.2 Helm-Charts deployen
    8. 12.2.1 Variablen setzen
    9. 12.2.2 Werte in einem Helm-Release angeben
    10. 12.2.3 Eine Anwendung mit Helm aktualisieren
    11. 12.2.4 Zu früheren Versionen zurückrollen
    12. 12.2.5 Ein Helm-Chart-Repo erstellen
    13. 12.2.6 Helm-Chart-Secrets mit Sops managen
    14. 12.3 Mehrere Charts mit Helmfile managen
    15. 12.3.1 Was befindet sich in einem Helmfile?
    16. 12.3.2 Chart-Metadaten
    17. 12.3.3 Das Helmfile anwenden
    18. 12.4 Fortgeschrittene Tools zur Manifest-Verwaltung
    19. 12.4.1 ksonnet
    20. 12.4.2 Kapitan
    21. 12.4.3 kustomize
    22. 12.4.4 kompose
    23. 12.4.5 Ansible
    24. 12.4.6 kubeval
    25. 12.5 Zusammenfassung
  20. 13 Entwicklungs-Workflow
    1. 13.1 Entwicklungs-Tools
    2. 13.1.1 Skaffold
    3. 13.1.2 Draft
    4. 13.1.3 Telepresence
    5. 13.1.4 Knative
    6. 13.2 Deployment-Strategien
    7. 13.2.1 Rollierende Updates
    8. 13.2.2 Recreate
    9. 13.2.3 maxSurge und maxUnavailable
    10. 13.2.4 Blue/Green-Deployments
    11. 13.2.5 Rainbow-Deployments
    12. 13.2.6 Canary-Deployments
    13. 13.3 Migration mit Helm
    14. 13.3.1 Helm Hooks
    15. 13.3.2 Umgang mit fehlgeschlagenen Hooks
    16. 13.3.3 Andere Hooks
    17. 13.3.4 Hooks verketten
    18. 13.4 Zusammenfassung
  21. 14 Continuous Deployment in Kubernetes
    1. 14.1 Was ist Continuous Deployment?
    2. 14.2 Welches CD-Tool soll ich verwenden?
    3. 14.2.1 Jenkins
    4. 14.2.2 Drone
    5. 14.2.3 Google Cloud Build
    6. 14.2.4 Concourse
    7. 14.2.5 Spinnaker
    8. 14.2.6 GitLab CI
    9. 14.2.7 Codefresh
    10. 14.2.8 Azure Pipelines
    11. 14.3 CD-Komponenten
    12. 14.3.1 Docker Hub
    13. 14.3.2 Gitkube
    14. 14.3.3 Flux
    15. 14.3.4 Keel
    16. 14.4 Eine CD-Pipeline mit Cloud Build
    17. 14.4.1 Google Cloud und GKE einrichten
    18. 14.4.2 Das Demo-Repository forken
    19. 14.4.3 Erste Schritte in Cloud Build
    20. 14.4.4 Den Test-Container bauen
    21. 14.4.5 Die Tests ausführen
    22. 14.4.6 Den Anwendungs-Container bauen
    23. 14.4.7 Die Kubernetes-Manifeste überprüfen
    24. 14.4.8 Das Image veröffentlichen
    25. 14.4.9 Git-SHA-Tags
    26. 14.4.10 Den ersten Build-Trigger erstellen
    27. 14.4.11 Den Trigger testen
    28. 14.4.12 Von einer CD-Pipeline deployen
    29. 14.4.13 Einen Deploy-Trigger erstellen
    30. 14.4.14 Die Build-Pipeline optimieren
    31. 14.4.15 Die Beispiel-Pipeline anpassen
    32. 14.5 Zusammenfassung
  22. 15 Observability und Monitoring
    1. 15.1 Was ist Observability?
    2. 15.1.1 Was ist Monitoring?
    3. 15.1.2 Black-Box Monitoring
    4. 15.1.3 Was heißt »Up«?
    5. 15.1.4 Logging
    6. 15.1.5 Metriken
    7. 15.1.6 Tracing
    8. 15.1.7 Observability
    9. 15.2 Die Observability-Pipeline
    10. 15.3 Monitoring in Kubernetes
    11. 15.3.1 Externe Black-Box Checks
    12. 15.3.2 Interne Health-Checks
    13. 15.4 Zusammenfassung
  23. 16 Metriken in Kubernetes
    1. 16.1 Was sind Metriken wirklich?
    2. 16.1.1 Zeitreihen-Daten
    3. 16.1.2 Zähler und Maße
    4. 16.1.3 Was können uns Metriken sagen?
    5. 16.2 Die Wahl guter Metriken
    6. 16.2.1 Services: Das RED-Muster
    7. 16.2.2 Ressourcen: Das USE-Muster
    8. 16.2.3 Business-Metriken
    9. 16.2.4 Kubernetes-Metriken
    10. 16.3 Metriken analysieren
    11. 16.3.1 Was ist an einem einfachen Durchschnitt falsch?
    12. 16.3.2 Mittelwert, Median und Ausreißer
    13. 16.3.3 Perzentile
    14. 16.3.4 Perzentile auf Metrikdaten anwenden
    15. 16.3.5 Meist wollen wir das Schlimmste wissen
    16. 16.3.6 Mehr als Perzentile
    17. 16.4 Metriken in Dashboards visualisieren
    18. 16.4.1 Ein Standard-Layout für alle Services verwenden
    19. 16.4.2 Einen Information Radiator mit Master Dashboards bauen
    20. 16.4.3 Dashboards mit Fehlerursachen
    21. 16.5 Alarme durch Metriken
    22. 16.5.1 Was ist falsch an Alarmen?
    23. 16.5.2 Bereitschaftsdienst sollte nicht die Hölle sein
    24. 16.5.3 Dringende, wichtige und umsetzbare Alarme
    25. 16.5.4 Dokumentieren Sie Warnungen und Alarme außerhalb der Bürozeiten und in der Nacht
    26. 16.6 Tools und Services für Metriken
    27. 16.6.1 Prometheus
    28. 16.6.2 Google Stackdriver
    29. 16.6.3 AWS Cloudwatch
    30. 16.6.4 Azure Monitor
    31. 16.6.5 Datadog
    32. 16.6.6 New Relic
    33. 16.7 Zusammenfassung
  24. 17 Nachwort
    1. 17.1 Wohin als Nächstes?
    2. 17.2 Willkommen an Bord
  25. Fußnoten
  26. Index

Product information

  • Title: Cloud Native DevOps mit Kubernetes
  • Author(s): John Arundel, Justin Domingus
  • Release date: September 2019
  • Publisher(s): dpunkt
  • ISBN: 9783864906985