Lokales K8s-Development & -Debugging mit Telepresence (Hands-On)

10.11.2021 Tom Trapp
Mobile Tech handson

Telepresence haben wir schon im TechUp zu Ambassador angeschaut und wollen dies nun vertiefen und Hands-On ausprobieren.

Hier nochmal als kleine Erinnerung:

Was ist Telepresence?

Telepresence erlaubt es, Teile einer komplexen Kubernetes-Applikation lokal laufen zu lassen.

Cloud Native Applikationen können schnell zu gross und zu komplex werden, um sie mit allen beweglichen Teilen und Abhängigkeiten lokal zu starten. Mit Telepresence wird ein smarter Proxy genutzt, um den Traffic eines bestimmten Services (Microservice) an die lokale Entwicklungsumgebung weiterzuleiten. So wird quasi der eigene Rechner oder Laptop in das Kubernetes-Cluster eingebunden.

Aufgerufen wird die gesamte Applikation dann über die normale URL oder eine gewisse Preview-URL, welche das Routing des Zielservices an die lokale Maschine aktiviert. Technisch gesehen wird beim Aufruf dieser Preview-URL ein Header an den Request gehängt. Anhand dieses Headers entscheidet dann der Telepresence-Proxy, ob der Traffic zum echten Service oder zum lokal laufenden Service gerouted werden soll.

Telepresence wurde vor Kurzem in der Version 2.0 veröffentlicht, welche nun nicht mehr in Python, sondern in Golang geschrieben ist. Ausserdem wurde die Version 2 speziell auf den Einsatz in Corporate Netzwerken, beispielsweise in Kombination mit VPNs entwickelt.

Konkret gibt es zwei Modi welche entscheiden, wie der Traffic weitergeleitet wird. Im Default Modus wird sämtlicher Traffic direkt an die lokale Maschine geleitet, die laufende Applikation wird somit vollständig beeinflusst. Im Collaboration-Modus ist es möglich nur bestimmte Benutzer, über eine preview URL, auf die lokale laufende Instanz eines Microservices zu leiten. So bleibt die Applikation an sich unberührt und kann weiterhin normal genutzt werden.

Der grosse Vorteil dieses Konzeptes ist die Zeitersparnis. Bei kurzen Tests im Cluster muss nicht immer der komplette CI/CD-Prozess durchlaufen werden, sondern es ist möglich nur bestimmte User auf die lokale Instanz zu lotsen. So erhält man ein wesentlich schnelleres Feedback und kann sich beim Pair-Programming viel einfacher austauschen. In einem heterogenen System ist es oft umständlich ein Bug lokal nachzustellen. Auch hier schafft Telepresence Abhilfe, indem man die lokale Instanz einfach debuggen und anschliessend den erforderlichen Fix direkt im Cluster vornehmen kann.

In folgendem Bild ist die Architektur von Telepresence beschrieben. Über einen sogenannten Traffic Manager wird gesteuert, wohin bestimmter Traffic geleitet wird. Der Traffic-Agent sorgt als Sidecar-Proxy Container dafür, dass alle Container angesprochen werden können.

Quelle 31.08.2021: https://www.getambassador.io/docs/telepresence/latest/reference/architecture/

Telepresence muss lokal und im Cluster installiert und konfiguriert werden. Das Forwarding kann dann vom lokalen Rechner, mit gültiger kubeconfig, gestartet werden.

Hands On

Um Telepresence schnell und einfach testen zu können nutzen wir das demo Kubernetes Cluster von Ambassador, genaueres ist hier beschrieben. Selbstverständlich lässt sich Telepresence auch mit einem normalen oder gar self-hosted K8s-Cluster nutzen.

Wichtig ist hier, dass kubectl entsprechend konfiguriert wird um die neue kubeconfig.yaml zu verwenden. Dies kann beispielsweise über eine Umgebungsvariable gemacht werden:

export KUBECONFIG=$(pwd)/kubeconfig.yaml

Nun wollen wir Telepresence auf unserem MacBook installieren, wir nutzen hierfür den Quickstart Guide als Referenz. Nachdem wir z. B. mittels brew Telepresence installiert haben können wir mit telepresence status unsere Installation verifizieren. Dieses Command gibt uns nützliche Informationen über den aktuellen Stand der Daemons.

Anschliessend wollen wir unseren Telepresence-Client mit dem Traffic-Manager von Telepresence im Cluster verbinden. Einfach gesagt hängen wir mit diesem Command unseren Client in das Kubernetes-Cluster, als wären diese im selben Cluster und Netzwerk.

telepresence connect

Um verifizieren zu können, dass wir effektiv im Cluster sind, können wir beispielsweise den Kubernetes API Server anfragen. Hier erwarten wir ein '401 Unauthorized'-Error.

curl -ik https://kubernetes.default

HTTP/1.1 401 Unauthorized
Cache-Control: no-cache, private

Hier sehen wir nun, dass sämtlicher Netzwerktraffic inklusive DNS Resolving usw. ins Cluster geleitet wird und wir somit eine Verbindung zum API-Server in unserem Kubernetes-Cluster haben.

Dieses Setup erlaubt es uns unter anderem:

  • auf alle Cluster Services zuzugreifen und diese zu nutzen
  • nur einen dedizierten Microservice lokal zu entwickeln und alle echten beweglichen Teile im Cluster zu nutzen
  • auftretende Fehler direkt debuggen, analysieren und lösen zu können ohne mühsames, lokales nachstellen

Dies bedeutet auch, dass wir nicht über externe URLs o. ä. beim Entwickeln auf Ressourcen im Cluster zugreifen müssen. Wir können, gleiche wie die Applikation oder die Microservices auch, die internen DNS- oder Servicenamen nutzen. Cool, oder? 🚀

Als Nächstes wollen wir ein einfaches Demo-Projekt (wir nutzen hier das Demo-Projekt von Edgey Corp) mit folgendem Kommando deployen:

kubectl apply -f https://raw.githubusercontent.com/datawire/edgey-corp-nodejs/main/k8s-config/edgey-corp-web-app-no-mapping.yaml

Im Detail besteht dieses Demo-Projekt aus drei unterschiedlichen Services, der VeryLargeJavaService fragt den DataProcessingServicean und rendert die Page. Vom DataProcessingService bekommt der VeryLargeJavaService die Farbe, welche beim Rendering dann für das Bild sowie die Überschrift genutzt wird. Der DataProcessingService besteht aus unterschiedlichen Endpunkten, ein Endpunkt frägt im Hintergrund eine Datenbank, den VeryLargeDataStore ab.

Nach kurzer Zeit sehen wir via kubectl get pods, dass alle Pods laufen und die Demo einsatzbereit ist. Via (http://verylargejavaservice.default:8080)[http://verylargejavaservice.default:8080] können wir das Demo-Projekt aufrufen.

Dieser Aufruf würde ohne vorheriges telepresence connect nicht funktionieren, da der DNS Name sonst unbekannt ist. Da unsere lokale Maschine Teil des Clusters ist, können wir diese Url direkt aufrufen.

In der Demo App ist nun ein grüner Title und ein grüner Pod zu sehen.

Der erste Change

Nun ist es so weit, wir wollen einen Change an dem Demo-Projekt machen und die lokale laufende Installation dann ins Cluster einhängen. Wir wollen die Farbe der Überschrift, welche über den DataProcessingService abgefragt wird, verändern.

Hierfür checken wir uns zuerst das Repository aus, installieren und starten den DataProcessingService.

git clone https://github.com/datawire/edgey-corp-nodejs.git
cd edgey-corp-nodejs/DataProcessingService/
npm install && npm start

Sobald alles gestartet ist, können wir die lokale Version des DataProcessingService prüfen, indem wir localhost:3000/color aufrufen. Hier sehen wir, dass die Farbe 'blue' zurückkommt.

Nun wollen wir unsere lokal laufenden Applikation ins Cluster hängen, dies können wir mit folgenden Command machen. Wichtig ist hier, dass es sich um den Namen des Kubernetes Services handelt und der korrekte Port, welcher intercepted werden soll, angegeben wird. In unserem Beispiel wäre dies:

telepresence intercept dataprocessingservice --port 3000

Dieses Command funktioniert allerdings nur, wenn das telepresence connect vorher korrekt ausgeführt wurde und noch aktiv ist.

Laden wir nun unsere Page erneut sehen wir, dass die Überschrift blau ist, der neue Wert von unserem lokalen Microservice.

Technisch gesehen leitet dieses Command sämtlichen Datenverkehr, welcher im Cluster auf dataprocessingservice:3000 kommt weiter an localhost:3000 auf unserem lokalen Client. Läuft unsere Applikation lokal nicht oder auf einem anderen Port kann keine Verbindung aufgebaut werden, dies erkennen wir an den null Werten im Beispiel und generell der Farbe ' grau' im Bild.

Nun können wir auch hingehen und die Farbe beispielsweise auf 'purple' ändern. Hierfür passen wir die Konstante DEFAULT_COLOR in der Datei edgey-corp-nodejs/DataProcessingService/app.js an. Der NodeJS-Server erkennt automatisch die Änderung und compiled und restarted sich. Der Change ist dann nach kurzer Wartezeit direkt über die normale Cluster Url sichtbar.

Zu guter Letzt können wir mit telepresence leave dataprocessingservice die aktuelle Interception wieder beenden. Hierbei gibt man wieder den Namen vom Kubernetes-Service an.

Selbstverständlich lassen sich mehrere Interceptions gleichzeitig aufbauen, diese kann man sich mit telepresence list auslisten lassen.

Eigene Preview Url

Im vorherigen Beispiel haben wir nun den kompletten Netzwerktraffic intercepted, der laufende Service im Cluster ist somit komplett umgegangen worden. Oft hat man aber den UseCase, Changes erstmal nur einer dedizierten Person oder Gruppe zu zeigen, hierfür bietet Telepresence eine sogenannte preview url. Hierfür muss man bei Ambassador angemeldet sein, dies geschieht mit dem Command telepresence login.

Anschliessend starten wir einen neuen Intercept und werden nach weiteren Konfigurationen gefragt. Wir nutzen verylargejavaservice.default, 8080 und n als Parameter. Den vierten Parameter können wir auf dem Standard-Wert belassen.

In der Ausgabe des Commands bekommen wir dann eine Preview-URL. Die komplette Ein- und Ausgabe sollte dann ungefähr wie folgt aussehen:

telepresence intercept dataprocessingservice --port 3000

To create a preview URL, telepresence needs to know how cluster
ingress works for this service.  Please Confirm the ingress to use.

1/4: What's your ingress' layer 3 (IP) address?
     You may use an IP address or a DNS name (this is usually a
     "service.namespace" DNS name).

       [default: ambassador.ambassador]: verylargejavaservice.default                            

2/4: What's your ingress' layer 4 address (TCP port number)?

       [default: 443]: 8080

3/4: Does that TCP port on your ingress use TLS (as opposed to cleartext)?

       [default: y]: n

4/4: If required by your ingress, specify a different layer 5 hostname
     (TLS-SNI, HTTP "Host" header) to access this service.

       [default: verylargejavaservice.default]: 

Using Deployment dataprocessingservice
intercepted
    Intercept name    : dataprocessingservice
    State             : ACTIVE
    Workload kind     : Deployment
    Destination       : 127.0.0.1:3000
    Volume Mount Error: sshfs is not installed on your local machine
    Intercepting      : HTTP requests that match all headers:
      'x-telepresence-intercept-id: 61cda5a8-0dcd-4eb0-bad8-ebe198a77828:dataprocessingservice'
    Preview URL       : https://magical-mcnulty-6488.preview.edgestack.me
    Layer 5 Hostname  : verylargejavaservice.default

Hier können wir nun über unsere Preview URL die lokal laufende Version ansehen und dies ohne, dass die Cluster-URL http://verylargejavaservice.default:8080/ beeinflusst wird. Technisch gesehen fungiert das über einen Header, welcher beim Aufruf der Preview Url gesetzt wird. Der Ingress- & Traffic-Controller erkennt anhand dieses Header, wohin der Request geleitet werden soll.

Natürlich können wir den Header auch manuell an den Request appenden und müssen so die URL nicht umstellen.

Fehlerhandling

Bei uns kam es beim Ausprobieren immer wieder zu Abbrüchen bzw. Performanzproblemen. Augenscheinlich kommt es nach längerer Wartezeit oder Inaktivität zu Timeouts bzw. Verbindungsabbrüchen. Also mögliche Lösung kann man mit telepresence quit die aktuelle Sitzung komplett beenden und anschliessend wieder mit telepresence connect erneut starten.

Fazit

Telepresence ist eine klasse Idee, um immer wieder auftretende Probleme oder Use-Cases schnell und einfach zu lösen. Leider kam es beim Evaluieren immer wieder zu Performance oder Stabilitätsproblemen, hier ist sicher noch Luft nach oben.

Nichtsdestotrotz ist der Einsatz von Telepresence zu empfehlen, sobald langwierige oder komplexe CI/CD-Prozesse Teil des Projektes sind. Grundsätzlich muss man sich aber die Frage stellen, ob man mit den echten Ressourcen im Cluster entwickeln will. Mit einem grossen Entwicklerteam kann es schnell zu Konflikten oder kaputten, falschen States kommen, hier könne man über den Einsatz von lokalen Mocks-Servern o. ä. nachdenken.

Stay tuned! 🚀

Tom Trapp – Problemlöser, Innovator, Sportler. Am liebsten feilt Tom den ganzen Tag an der moderner Software und legt viel Wert auf objektiv sauberen, leanen Code.