Knative Tutorial: Das Deployment von 12 Factor Apps auf Knative

© Shutterstock / Avesun (modifiziert)
Mit Knative können Serverless-Plattformen erstellt werden, die auf Kubernetes basieren. Doch wie genau funktioniert das eigentlich alles? Wie skaliert meine Anwendung auf 0? Wie läuft das Deployment ab? Diese und weitere Fragen beantwortet Dr. Nic Williams, CEO von Stark & Wayne, in diesem umfangreichen Tutorial zu Knative.
Der vorliegende Artikel ist Teil des Knative Tutorials von Dr. Nic Williams, CEO von Stark & Wayne, und ist im englischsprachigen Original auf dem Blog des Unternehmens erschienen.
Auf der Google Next18 kündigte Google an, dass das „Serverless Add-on“ für die Google Kubernetes Engine (GKE) unter dem Namen „Knative“ Open Source gestellt werden würde. Da Knative als Sammlung von Bausteinen für das Erstellen von Serverless-Plattformen definiert ist, kann man schon erahnen, dass es nicht „einfach läuft“ und man auf Google, Pivotal oder Red Hat angewiesen ist, um es zu nutzen. Vielleicht jedenfalls. Dies sind wahrlich verwirrende Zeiten in der Open-Source-Welt.
Mein erster Eindruck ist: Knative läuft. Fügt man dann noch Buildpacks für Heroku/Cloud Foundry hinzu, fühlt sich das Ganze immer mehr nach dem Heroku/Cloud Foundry an, die wir kennen und lieben, als nach purem Kubernetes.
In diesem ersten Teil des Tutorials werden wir gemeinsam Knative in unser eigenes Kubernetes installieren (knctl install
) und dann eine Menge Spaß mit Knative haben (knctl deploy
).
Knative hat für Ops, die ihre zustandslosen Apps auf Kubernetes laufen lassen, eine ganze Meine in petto. Für mich ist das interessanteste Feature das automatisch Skalieren: Man kann hochskalieren, wenn viele Workloads auftauchen, und bis auf 0 hinuntergehen, wenn keiner mehr etwas für die eigene Anwendung übrig hat.
Installation von Knative
Zunächst laden wir das praktische Knative CLI (knctl
) herunter, mit dem Knative in Kubernetes installiert und Anwendungen später deployt werden können. MacOS-Nutzer können dies recht einfach mit unserem Homebrew Tap erledigen:
brew install starkandwayne/kubernetes/knctl
Für dieses Tutorial gehen wir davon aus, dass Minikube genutzt wird, bei dem die Konfiguration über Node-Ports und nicht über einen Load Balancer vollführt wird. (Wer ein anderes Kubernetes nutzen möchte, findet Tipps und Tricks für die Installation von Knative in der Dokumentation.)
minikube start --memory=8192 --cpus=3 \ --kubernetes-version=v1.11.3 \ --vm-driver=hyperkit \ --bootstrapper=kubeadm knctl install --node-ports --exclude-monitoring
Die Ausführung des Kommandos knctl install
wird ein paar Minuten dauern. Vielleicht sogar zehn Minuten und mehr. Der Grund hierfür ist, dass mehr als ein Dutzend Container Images heruntergeladen werden. Da weder die Internetgeschwindigkeit noch die Größe der Container Images sich einfach ändern lassen, heißt es abwarten und Tee trinken. Oder man macht draußen einen kurzen Spaziergang.
Schlägt knctl install
fehl, liegt das vermutlich daran, dass die Internetgeschwindigkeit zu gering ist. Daraus resultiert, dass die Docker Images nicht fertig heruntergeladen werden, bevor ein Timeout für das Kommando eintritt. Jetzt hilft nur, kubectl get pods --all-namespaces
auszuführen, bis man alle laufenden Pods sieht, und dann das Kommando knctl install
erneut auszuführen, um die Installation fortzusetzen.
Ist das geschafft, ist der Kubernetes Cluster nun „Serverless“. Sehr gut!
Mit dem Befehl kubectl get pods --all-namespaces
kann man alle Pods sehen, aus denen Knative grundlegend besteht.
$ kubectl get pods --all-namespaces NAMESPACE NAME READY STATUS RESTARTS AGE istio-system istio-citadel-7d8f9748c5-zgm9x 1/1 Running 0 21m istio-system istio-cleanup-secrets-j4pkx 0/1 Completed 0 21m istio-system istio-egressgateway-676c8546c5-dnwsd 1/1 Running 0 21m istio-system istio-galley-5669f7c9b-g774b 1/1 Running 0 21m istio-system istio-ingressgateway-5475685bbb-q5f2x 1/1 Running 0 21m istio-system istio-pilot-5795d6d695-9klrz 2/2 Running 0 21m istio-system istio-policy-7f945bf487-2wh88 2/2 Running 0 21m istio-system istio-sidecar-injector-d96cd9459-7knkm 1/1 Running 0 21m istio-system istio-statsd-prom-bridge-549d687fd9-lcmb7 1/1 Running 0 21m istio-system istio-telemetry-6c587bdbc4-t4jql 2/2 Running 0 21m istio-system knative-ingressgateway-7f4477dd99-n9wz2 1/1 Running 0 4m knative-build build-controller-7dcc4b7544-rkgwb 1/1 Running 0 4m knative-build build-webhook-fb6484576-sr4fk 1/1 Running 0 4m knative-serving activator-77d46b585d-b6g8n 2/2 Running 0 4m knative-serving controller-85768cfd45-t8ktc 1/1 Running 0 4m knative-serving webhook-56dd548f8-hjw44 1/1 Running 0 4m ...
Deployment einer vorhandenen Anwendung
Wir wollen nun ein existierendes öffentliches Docker Image als automatisch skalierende und zustandslose Anwendung im aktuellen Kubernetes Namespace auf Knative deployen:
kubectl create ns helloworld knctl deploy \ --namespace helloworld \ --service hello \ --image gcr.io/knative-samples/helloworld-go \ --env TARGET=Rev1
Die Ausgabe zeigt an, dass ein neuer Service namens hello
erstellt wurde. Gleichzeitig wurde die erste Revision erstellt (hello-00001
) und als latest
und previous
(da es die erste Revision ist) getaggt.
Name hello Waiting for new revision to be created... Tagging new revision 'hello-00001' as 'latest' Tagging new revision 'hello-00001' as 'previous' Succeeded
Nun können wir etwa einen curl
-Request zu unserem hello
-Service in die Routing-Ebene von Knative einbauen:
$ knctl curl --namespace helloworld --service hello Running: curl '-H' 'Host: hello.helloworld.example.com' 'http://192.168.64.8:32380' Hello World: Rev1! Succeeded
Gibt dies nicht umgehend Hello World: Rev1!
aus, könnte das daran liegen, dass das System noch immer dabei ist, das Image gcr.io/knative-samples/helloworld-go
herunterzuladen. Ich empfehle, einfach kurz abzuwarten und es dann erneut zu probieren.
Ich selbst nutze Minikube in Verbindung mit dem Zugang über Node Ports, weshalb ich kein hübsches DNS-Routing aufsetzen kann. In einem späteren Teil des Artikels werde ich auf die Nutzung von Load Balancern, DNS, Knative Routing und kwt etwas näher eingehen.
Nach der Ausführung von knctl deploy
läuft Kubernetes auf einem einzelnen Pod unseres Services:
$ kubectl get pods --namespace helloworld NAME READY STATUS RESTARTS AGE hello-00001-deployment-5864685cbc-v8r7n 3/3 Running 0 15s
Die Möglichkeit, Revisionen unseres Services zu erstellen, ist eines der wichtigsten Features von Knative. Im folgenden Code-Beispiel sehen wir, dass die initiale Revision unseres Services 100 % des Traffics bekommt:
$ knctl revisions list --namespace helloworld --service hello Revisions for service 'hello' Name Tags Allocated Traffic % Annotations Age hello-00001 previous 100% - 1m latest 1 revisions
Sobald wir knctl
erneut ausführen, wird eine neue Revision erstellt und der gesamte Traffic auf diese Revision umgeleitet:
$ knctl deploy \ --namespace helloworld \ --service hello \ --image gcr.io/knative-samples/helloworld-go \ --env TARGET=Rev2 Name hello Waiting for new revision (after revision 'hello-00001') to be created... Tagging new revision 'hello-00002' as 'latest' Tagging older revision 'hello-00001' as 'previous' Succeeded
Auch die Requests werden nun an die neue Revision gestellt:
$ knctl revisions list --namespace helloworld --service hello Revisions for service 'hello' Name Tags Annotations Conditions Age Traffic hello-00002 latest - 4 OK / 4 20s 100% -> hello.helloworld.example.com hello-00001 previous - 4 OK / 4 1m - $ knctl curl --namespace helloworld --service hello Running: curl '-H' 'Host: hello.helloworld.example.com' 'http://192.168.64.8:32380' Hello World: Rev2!
Obwohl wir eine zweite Revision deployt haben, laufen die Pods beider Revisionen weiter:
$ kubectl get pods --namespace helloworld NAME READY STATUS RESTARTS AGE hello-00001-deployment-5864685cbc-v8r7n 3/3 Running 0 1m hello-00002-deployment-7874bf89b8-4b4k5 3/3 Running 0 29s
Später werden wir sehen, dass Knative Pods automatisch herunterfährt, auf die nicht zugegriffen wird oder von denen aus keine Routen zu einer Revision führen.
Routing
Die Dokumentation zum Knative-Serving-Modul enthält das folgende Diagramm, das eine Übersicht der Zusammenhänge zwischen Routen, Revisionen und dem Service darstellt:

Quelle: Google
Knative Serving API
Revisionen sind eine unveränderliche Momentaufnahme (Snapshot) vom Code, Abhängigkeiten und der Konfiguration. Revisionen, die nicht via Routen referenziert sind, werden abgeschaltet und ihre Kubernetes-Ressourcen gelöscht. Das aktuelle Routing der Domänen können wir uns jederzeit ansehen:
$ knctl routes list --namespace helloworld Routes in namespace 'helloworld' Name Traffic All Traffic Assigned Ready Domain Age hello 100% -> hello: true true hello.helloworld.example.com 2h
Skalieren auf 0
Verschwindet man einmal kurz für etwa fünf Minuten und kommt wieder, wird man feststellen, dass keine hello-00001
-Pods ausgeführt werden. Sie wurden möglicherweise beendet:
$ kubectl get pods --namespace helloworld NAME READY STATUS RESTARTS AGE hello-00001-deployment-5c79d4f99b-h622d 2/3 Terminating 0 6m hello-00002-deployment-7ff7bf89d9-f72mn 3/3 Running 0 5m
Und später wird auch hello-00002
beendet:
$ kubectl get pods --namespace helloworld NAME READY STATUS RESTARTS AGE hello-00001-deployment-5c79d4f99b-h622d 0/3 Terminating 0 6m hello-00002-deployment-7ff7bf89d9-f72mn 3/3 Terminating 0 5m
Und schließlich sehen wir Folgendes:
$ kubectl get pods --namespace helloworld No resources found.
Sobald man nun wieder knctl curl
für den Service ausführt (via $ knctl curl --namespace helloworld --service hello
, fährt Knative dynamisch einen einzelnen Pod hoch, um den Request zu bedienen. Der erste Request wird ein kleines Lag (mehrere Sekunden) haben, da Knative die Revision erst wieder hochfahren muss.
$ kubectl get pods --namespace helloworld NAME READY STATUS RESTARTS AGE hello-00002-deployment-7ff7bf89d9-f8755 3/3 Running 0 56s
Warum allerdings der Pod hello-00001-deployment-...
nicht herunterskaliert und beendet wird, ist mir noch etwas schleierhaft.
Im nächsten Teil geht es um das Erstellen und Deployen von Knative-Anwendungen mit Knative Build.
Hinterlasse einen Kommentar