Ein Schritt-für-Schritt Praxis-Tutorial

Erste Schritte für das Erstellen von Serverless-Anwendungen mit Knative

Kamesh Sampath

© Shutterstock / Artur Balytskyi

In diesem Artikel werden die ersten Schritte für das Erstellen von Serverless-Anwendungen mit Knative vorgestellt. Das Tutorial reicht vom Aufsetzen eines Minikube über das Erstellen einer Kubernetes-Ressourcendatei bis hin zum Deployment der Serverless-Anwendung.

Im ersten Teil des Artikels werden wir uns mit dem Aufsetzen einer Entwicklungsumgebung beschäftigen, die für Knative in Version 0.6.0 geeignet ist. Im zweiten Teil geht es dann um das Deployment des ersten eigenen Serverless Microservice. Grundvoraussetzung für die Nutzung von Knative für das Erstellen von Serverless-Anwendungen ist eine solide Kenntnis von Kubernetes. Wer darin noch unerfahren ist, sollte das offizielle Basis-tutorial von Kubernetes [1] absolvieren. Bevor es ans Eingemachte geht, müssen noch ein paar Tools und Utilities installiert werden:

  • Minikube [2]

  • kubectl [3]

  • kubens [4]

Für Windows-Nutzer hat sich WSL [5] als ziemlich nützlich erwiesen, ich empfehle, das auch noch zu installieren.

Aufsetzen von Minikube

Minikube ist ein Kubernetes-Cluster mit einem einzigen Knoten, der sich hervorragend für die alltägliche Entwicklung mit Kubernetes eignet. Nach der Installation müssen die folgenden Schritte durchgeführt werden, um Minikube fit für ein Deployment mit Knative Serving zu machen. Wie das im Code aussieht, zeigt Listing 1.

minikube profile knative 

minikube start -p knative --memory=8192 --cpus=6 \
  --kubernetes-version=v1.12.0 \
  --disk-size=50g \
  --extra-config=apiserver.enable-admission-plugins="LimitRanger,NamespaceExists,NamespaceLifecycle,ResourceQuota,ServiceAccount,DefaultStorageClass,MutatingAdmissionWebhook" 

Zunächst muss ein Minikube-Profil angelegt werden, wofür die erste Zeile steht. Anschließend wird mit dem zweiten Befehl eine Minikube-Instanz aufgesetzt, die 8 GB RAM, 6 CPUs und 50 GB Festplattenspeicher erhält. Der Startbefehl beinhaltet zudem ein paar zusätzliche Konfigurationen für den Kubernetes-Cluster, die nötig sind, um Knative zum Laufen zu bekommen. Wichtig ist auch, dass die verwendete Kubernetes-Version nicht älter als Version 1.12.0 ist, sonst funktioniert Knative nicht. Startet Minikube nicht sofort, ist das völlig normal: Es kann ein paar Minuten dauern, bis der initiale Start abgeschlossen ist, ein wenig Geduld sollte man beim Aufsetzen also mitbringen.

Aufsetzen eines Istio Ingress Gateways

Knative benötigt ein Ingress Gateway, um Requests an Knative Services zu routen. Neben Istio [6] wird auch Gloo [7] als Ingress Gateway unterstützt. Für unser Beispiel werden wir allerdings Istio nutzen. Die folgenden Schritte zeigen, wie man eine möglichst leichtgewichtige Installation von Istio durchführt, die ledgiglich das Ingress Gateway enthält:

curl -L  https://raw.githubusercontent.com/knative/serving/release-0.6/third_party/istio-1.1.3/istio-lean.yaml \
  | sed 's/LoadBalancer/NodePort/' \
  | kubectl apply --filename -

Wie bereits das Aufsetzen von Minikube, dauert auch das Deployment des Istio Pods ein paar Minuten. Mit dem Befehl kubectl –namespace istio-system get pods –watch kann man sich den Status ansehen, beendet wird die Übersicht mit STRG + C. Ob das Deployment erfolgreich war, lässt sich leicht mit dem Kommando kubectl –namespace istio-system get pods feststellen. Ist alles gut gelaufen, sollte die Ausgabe es wie in Listing 2 aussehen.

NAME                                     READY    STATUS    RESTARTS   AGE
cluster-local-gateway-7989595989-9ng8l   1/1      Running   0          2m14s
istio-ingressgateway-6877d77579-fw97q    2/2      Running   0          2m14s
istio-pilot-5499866859-vtkb8             1/1      Running   0          2m14s

Installation von Knative Serving

Die Installation von Knative Serving [8] erlaubt es uns, Serverless Workloads auf Kubernetes laufen zu lassen. Darüber hinaus stellt es automatische Skalierung und automatisches Tracking von Revisionen bereit. Mit den folgenden Befehlen lässt sich Knative Serving installieren:

kubectl apply --selector knative.dev/crd-install=true \
--filename https://github.com/knative/serving/releases/download/v0.6.0/serving.yaml
 
kubectl apply --filename https://github.com/knative/serving/releases/download/v0.6.0/serving.yaml --selector networking.knative.dev/certificate-provider!=cert-manager

Auch hier wird es vermutich einige Minuten dauern, bis die Knative Pods deployt werden. Mit dem Kommando kubectl –namespace knative-serving get pods –watch kann man den Status überprüfen, und wie gehabt wird die Überprüfung mit STRG + C abgebrochen. Mit dem Kommando kubectl –namespace knative-serving get pods lässt sich auch in diesem Fall kontrollieren, ob alles läuft. Ist das der Fall, sollte eine Ausgabe wie in Listing 3 angezeigt werden.

NAME                               READY    STATUS     RESTARTS    AGE
activator-54f7c49d5f-trr82         1/1      Running    0           27m
autoscaler-5bcd65c848-2cpv8        1/1      Running    0           27m
controller-c795f6fb-r7bmz          1/1      Running    0           27m
networking-istio-888848b88-bkxqr   1/1      Running    0           27m
webhook-796c5dd94f-phkxw           1/1      Running    0           27m

Deployment einer Anwendung

Die Anwendung, die wir zur Demonstration erstellen wollen, ist eine einfache Begrüßungsautomatik, die „Hi“ ausgibt. Hierfür nutzen wir ein bestehendes Linux-Container-Image, das unter [9] zu finden ist.

Der erste Schritt ist es, ein traditionelles Kubernetes Deployment zu erstellen, das anschließend für die Nutzung von Serverless-Funktionalitäten modifiziert werden kann. Dadurch wird deutlich werden, worin die eigentlichen Unterschiede liegen und wie man existierende Deployments unter Zuhilfenahme von Knative serverless macht.

Erstellen einer Kubernetes-Ressourcendatei

Die folgenden Schritte zeigen, wie wir eine Kubernetes-Ressourcendatei erstellen können. Dazu muss zunächst eine neue Datei mit dem Namen app.yaml erstellt werden, in die der in Listing 4 aufgeführte Code kopiert werden muss.

---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: greeter
spec:
  selector:
    matchLabels:
      app: greeter
  template:
    metadata:
      labels:
        app: greeter
    spec:
      containers:
      - name: greeter
        image: quay.io/rhdevelopers/knative-tutorial-greeter:quarkus
        resources:
          limits:
            memory: "32Mi"
            cpu: "100m"
        ports:
        - containerPort: 8080
        livenessProbe:
          httpGet:
            path: /healthz
            port: 8080
        readinessProbe:
          httpGet:
            path: /healthz
            port: 8080
---
apiVersion: v1
kind: Service
metadata:
  name: greeter-svc
spec:
  selector:
    app: greeter
  type: NodePort
  ports:
  - port: 8080
    targetPort: 8080

Erstellen des Deployments und des Service

Indem wir die zuvor erstellte YAML-Datei anwenden, können wir das Deployment und den Service erstellen. Dazu wird der Befehl kubectl apply –filename app.yaml verwendet. Auch an dieser Stelle kann das Kommando kubectl get pods –watch genutzt werden, um sich über den Status der Anwendung zu informieren, während STRG + C das Ganze beendet. Ist alles gut gegangen, steht uns ein Deployment mit dem Namen greeter und ein Service namens greeter-svc zur Verfügung (Listing 5).

$ kubectl get deployments
NAME      DESIRED   CURRENT   UP-TO-DATE   AVAILABLE   AGE
greeter   1         1         1            1           16s

$ kubectl get svc
NAME           TYPE        CLUSTER-IP        EXTERNAL-IP     PORT(S)           AGE
greeter-svc    NodePort    10.110.164.179    <none>          8080:31633/TCP    50s

Um einen Service zu aktivieren, kann man hier auch einen Minikube Shortcut wie minikube service greeter-svc nutzen, der den Service URL im Browser öffnet. Wer lieber curl nutzen möchte, um den gleichen URL aufzurufen, muss das Kommando curl $(minikube service greeter-svc –url) verwenden. Nun sollte ein Text angezeigt werden, der in etwa so aussieht: Hi greeter => ‚9861675f8845‘ : 1

Migration des traditionellen Kubernetes Deployments zu Serverless mit Knative

Die Migration startet ganz einfach damit, dass wir die Datei app.yaml kopieren, sie serverless-app-yaml nennen und sie um die in Listing 6 gezeigten Zeilen aktualisieren.

---
apiVersion: serving.knative.dev/v1alpha1
kind: Service
metadata:
  name: greeter
spec:
  template:
    metadata:
      labels:
        app: greeter
    spec:
      containers:
      

      - image: quay.io/rhdevelopers/knative-tutorial-greeter:quarkus
        resources:
          limits:
            memory: "32Mi"
            cpu: "100m"
        ports:
        - containerPort: 8080
        livenessProbe:
          httpGet:
            path: /healthz
        readinessProbe:
          httpGet:
            path: /healthz

Vergleicht man die traditionelle Kubernetes-Anwendung (app.yaml) mit der Serverless-Anwendung (serverless-app.yaml), stellt man drei Dinge fest. Zum einen wird kein zusätzlicher Service benötigt, da Knative den Service automatisch erstellen und routen wird. Da die Definition des Service manuell erfolgt, braucht es keine Selektoren mehr, folgende Codezeilen fallen also weg:

selector:
  matchLabels:
    app: greeter

Unter template.spec.containers fällt name: weg, da der Name von Knative automatisch generiert wird. Außerdem müssen für die Probes liveness und readiness keine Ports definiert werden.

Deployment der Serverless-Anwendung

Das Deployment läuft nach dem gleichen Muster ab wie bisher, dazu wird der Befehl kubectl apply –filename serverless-app.yaml angewandt. Die folgenden Objekte sollten erstellt worden sein, wenn das Deployment der Serverless-Anwendung erfolgreich abgeschlossen wurde: Zunächst sollte das Deployment jetzt hinzugefügt worden sein (Listing 7). Auch ein paar neue Services sollten verfügbar sein (Listing 8), unter anderem der Service ExternalName, der auf istio-ingressgateway.istio-system.svc.cluster.local zeigt. Es sollte zudem ein Knative Service mit einem URL verfügbar sein, an den Requests gesendet werden können (Listing 9).

$ kubectl get deployments
NAME                        DESIRED    CURRENT    UP-TO-DATE    AVAILABLE    AGE
greeter                     1          1          1             1            30m
greeter-bn8cm-deployment    1          1          1             1            59s
$ kubectl get services
NAME                     TYPE             CLUSTER-IP        EXTERNAL-IP                                            PORT(S)      AGE
greeter                  ExternalName                 istio-ingressgateway.istio-system.svc.cluster.local           114s
greeter-bn8cm            ClusterIP        10.110.208.72                                                      80/TCP       2m21s
greeter-bn8cm-metrics    ClusterIP        10.100.237.125                                                     9090/TCP     2m21s
greeter-bn8cm-priv       ClusterIP        10.107.104.53                                                      80/TCP       2m21s
kubectl get services.serving.knative.dev
NAME     URL                                  LATESTCREATED    LATESTREADY      READY   REASON
greeter  http://greeter.default.example.com   greeter-bn8cm    greeter-bn8cm    True

Wichtig

In einem Minikube Deployment werden wir weder LoadBalancer noch DNS haben, um etwas nach *.example.com oder einem Service-URL wie http://greeter.default.example.com aufzulösen. Um einen Service aufzurufen, muss der Header vom Host mit http/curl genutzt werden.

Um nun einen Service aufrufen zu können, muss der Request durch den Ingress bzw. das Gateway (in unserem Fall Istio) gehen. Um die Adresse des Istio Gateways herauszufinden, die wir im http/curl Call nutzen müssen, kann das folgende Kommando genutzt werden:

IP_ADDRESS="$(minikube ip):$(kubectl get svc istio-ingressgateway --namespace istio-system --output 'jsonpath={.spec.ports[?(@.port==80)].nodePort}')"

Der Befehl erhält den NodePort des Service istio-ingressgateway im Namespace istio-system. Wenn wir den NodePort des istio-ingressgateway haben, können wir den Service greeter via $IP_ADDRESS aufrufen, indem wir den Hostheader mit http/curl Calls passieren.

curl -H "Host:greeter.default.example.com" $IP_ADDRESS

Nun sollte man die gleiche Antwort wie beim traditionellen Kubernetes Deployment erhalten (Hi greeter => ‚9861675f8845‘ : 1). Erlaubt man dem Deployment, sich für etwa 90 Sekunden im Idle-Modus zu befinden, wird das Deployment terminiert. Beim nächsten Call wird das terminierte Deployment dann wieder aktiviert und den Request beantworten.

Herzlichen Glückwunsch, ihr habt eure erste Serverless-Anwendung erfolgreich deployt und aufgerufen!

Links & Literatur

Geschrieben von
Kamesh Sampath
Kamesh Sampath
Kamesh Sampath ist Principal Software Engineer und Director of Developer Experience bei Red Hat. Zu seinen Tätigkeiten gehört unter anderem die aktive Schulung von Entwicklern in den Bereichen Kubernetes/OpenShift, Service Mesh und Serverless-Technologien. In seiner beinahe zwanzigjährigen Karriere hat er zahlreichen Unternehmen dabei geholfen, Java-basierte Lösungen zu erstellen. Seit über zehn Jahren engagiert sich Kamesh im Open-Source-Bereich und hat an etlichen Projekten aktiv mitgewirkt, darunter Knative, Minishift, Eclipse Che, fabric8 usw. Er glaubt fest an das Motto „Learn more, do more and share more!“
Kommentare

Hinterlasse einen Kommentar

avatar
4000
  Subscribe  
Benachrichtige mich zu: