Quickie: Erhebungstechniken für Anforderungen
27. Juli 2022Schätzungen im Projektalltag
28. September 2022Remote Debugging von Java Apps in Kubernetes
“It works on my machine ¯\_(ツ)_/¯” – ein Satz den die ältere Generation an Entwicklern oft im Alltag benutzt hat. Heute gibt es mit Docker und Kubernetes Plattformen, die genau solche Probleme lösen.
Aber zu früh gefreut: Manchmal macht die Applikation auf dem Kubernetes Cluster einfach nicht das was lokal implementiert und getestet wurde. Ein routinierter Entwickler würde in so einem Fall den Debugger einschalten, aber geht das so einfach auf einem Kubernetes Cluster? Ja und wir zeigen Euch in diesem Blog Artikel wie das genau geht!
Der Einfachheit halber beschränken wir uns in diesem Artikel auf die IntelliJ Entwicklungsumgebung, aber das sollte keine Einschränkung darstellen – andere Entwicklungsumgebungen bieten ähnliche Features an.
Konzept
Die zugrundeliegende Idee sieht vor, in dem Kubernetes Pod (in welchem die Java App läuft) das Debugging auf einem Port zu aktivieren. Dieser Port muss vom Pod freigegeben werden.
Anschließend muss mit einem Port Forwarding ein lokaler Port auf den geöffneten Debugging Port des Pods weitergeleitet werden. Zu guter Letzt muss in der Entwicklungsumgebung das JVM Remote Debugging mit dem entsprechenden Port gestartet werden. Das folgende Schaubild zeigt schematisch den Aufbau des Remote Debuggings im Kubernetes Cluster:
Schritt 0: Einrichten des Remote Debuggings im IntelliJ
Im ersten Schritt soll in IntelliJ das Remote Debugging eingerichtet werden. Dazu muss das Bearbeitungsfenster für die Run Configurations
geöffnet werden:
- Erstelle eine neue
Run Configuration
, indem Du oben links auf das Plus-Symbol klickst und den EintragRemote JVM Debug
aus der Liste auswählst. - Anschließend kannst Du der Konfiguration einen beliebigen Namen geben.
- Die Standardkonfiguration muss üblicherweise nicht angepasst werden. Wenn der Standardport
5005
in deiner Applikation anderweitig benutzt wird, kannst Du ihn in der Konfiguration ändern. - Nun kopierst Du die Kommandozeilenargumente, die werden im nächsten Schritt gebraucht.
- Abschließend speicherst Du die Konfiguration ab, denn die wird später gebraucht.
Schritt 1: Debugging in der Remote JVM aktivieren
Üblicherweise verpacken wir unsere Applikationen in Docker Images, welche wir mit solch einem Dockerfile
bauen:
FROM eclipse-temurin:17-alpine
ARG ARTIFACT
COPY $ARTIFACT app.jar
ENTRYPOINT ["sh", "-c", "java $JAVA_OPTS -jar /app.jar"]
Wichtig ist hierbei die Variable $JAVA_OPTS
im ENTRYPOINT
. Diese benutzen wir um die vorher kopierten Kommandozeilenargumente einzusetzen. Das machen wir indem die Deployment Ressource im Kubernetes angepasst wird. Mit kubectl get deployments
kannst Du dir eine Liste deiner Deployments ausgeben und mit kubectl edit deployment <DEPLOYMENT_NAME>
kannst Du das Deployment wie folgt anpassen:
spec:
containers:
- ...
env:
- name: JAVA_OPTS
value: <COPIED_JAVA_OPTS>
ports:
- name: remotedebugging
containerPort: <PORT>
protocol: TCP
...
- Unter
<COPIED_JAVA_OPTS>
fügst Du die kopierten Kommandozeilenargumente aus IntelliJ ein. - Unter
<PORT>
fügst Du den Port, welchen Du zum debuggen benutzt, in unserem Fall ist das5005
.
Wenn Du das erledigt hast, kannst Du den Editor schließen und das Kubernetes Deployment sollte automatisch neu starten. Überprüfen kannst Du dies mit kubectl get pods
. Ein neuer Pod müsste erstellt worden sein. Wichtig für den nächsten Schritt: Kopiere dir den Namen des Pods!
Schritt 2: Port Forwarding
Nachdem der Pod neugestartet wurde, muss der Debugging-Port des Pods auf den lokalen Rechner weitergeleitet werden. Das geht mit folgendem Kommando:
kubectl port-forward <POD_NAME> <LOCAL_PORT>:<POD_PORT>
- Der
<POD_NAME>
ist der Name des Pods, welchen Du dir im vorigen Schritt kopiert haben solltest. - Der
<LOCAL_PORT>
ist der Port, welcher lokal für das Forwarding benutzt wird und auf welchen sich die Entwicklungsumgebung zum Debugging verbindet. - Der
<POD_PORT>
ist der geöffnete Port aus der Deployment Konfiguration vom vorigen Schritt.
Da der Port 5005 in diesem Artikel durchgängig benutzt wurde, sieht das Kommando so aus:
kubectl port-forward <POD_NAME> 5005:5005
Schritt 3: Happy Debugging!
Abschließend muss die in Schritt 0 erstellte Debugging Konfiguration gestartet werden und es sollte folgende Meldung erscheinen: Listening for transport dt_socket at address: 5005
Nun könnt Ihr Breakpoints setzen und den Fehlern auf den Grund gehen!