Teil 1: Knative-Installation & App-Deployment

Knative Tutorial: Das Deployment von 12 Factor Apps auf Knative

Dr. Nic Williams

© 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.

Geschrieben von
Dr. Nic Williams
Dr. Nic Williams
CEO of Stark & Wayne. User and evangelist of Cloud Foundry, Kubernetes, Concourse CI, and BOSH. Author of books Concourse Tutorial and Ultimate Guide to BOSH. Awarded Cloud Foundry Champion 2018. Twitter: @drnic
Kommentare

Hinterlasse einen Kommentar

Hinterlasse den ersten Kommentar!

avatar
4000
  Subscribe  
Benachrichtige mich zu: