Oracle Cloud Infrastructure Functions in Go und Verwendung des OCI Go-SDK für den Zugriff auf OCI-Services von Go aus

Dies ist die dritte Rate in einer fünfteiligen Serie zu Go und Oracle Cloud Infrastructure. In dieser Serie wird erläutert, wie Go-Anwendungen auf Oracle Cloud Infrastructure (OCI) in Compute-Instanzen (VMs), in Containern auf Kubernetes oder als serverlose Funktionen erstellt und ausgeführt werden können. Die Artikel zeigen, wie Sie die Erstellung und das Deployment dieser Go-Anwendungen mit OCI DevOps automatisieren. Ein wichtiges Thema ist die Verwendung von OCI-Services von Go-Anwendungen, die sowohl auf OCI ausgeführt werden, als auch von Go-Code, der an anderer Stelle ausgeführt wird. Einige der besprochenen OCI-Services sind Object Storage, Streaming, Key Vault und Autonomous Database.

Um diesen Artikeln zu folgen, sollten die Leser zumindest grundlegende Kenntnisse darüber haben, wie man Go-Anwendungen erstellt. Es wird davon ausgegangen, dass Leser Zugriff auf ihre eigene Go-Entwicklungsumgebung haben. Einige der Beispiele und Screenshots werden VS Code als Entwicklungstool speziell erwähnen. Es können jedoch auch andere Editoren und IDEs verwendet werden. Der in diesen Artikeln vorgestellte Go-Code zeigt eine Reihe von Mechanismen in ihrer einfachsten Form für maximale Klarheit und mit den geringsten Abhängigkeiten. Leser sollten keine sinnvolle Funktionalität oder keinen produktionsbereiten Code erwarten.

In den Artikeln wird beschrieben, wie Sie Going auf OCI erhalten. Um die Beispiele auszuprobieren, benötigen die Leser Zugriff auf einen OCI-Mandanten mit Berechtigungen zum Erstellen der in diesen Artikeln besprochenen OCI-Ressourcen. Die meisten der verwendeten Ressourcen sind in der Aways Free Tier (Compute-Instanz, VCN, Autonomous Database, Object Storage, Logging, Resource Manager) verfügbar oder verfügen über eine kostenlose Zuteilungsebene für eine begrenzte monatliche Nutzung (Funktionen, API-Gateway, Streaming, Vault, DevOps).

Im ersten Teil dieser Serie wird das Provisioning einer Compute-Instanz basierend auf dem Oracle Linux Cloud Developer-Image beschrieben. Es wird für eingehende und ausgehende Netzwerkaktivitäten geöffnet, eine Go-Anwendung erstellt und ausgeführt, die HTTP-Anforderungen verarbeitet, und das von der Anwendung erstellte Logging mit OCI Logging verbunden. Der zweite Teil behandelt das Software-Engineering, die Automatisierung des Builds und das Deployment der Anwendung mit dem OCI-Service DevOps. Dieser Service wird zum Speichern des Go-Quellcodes, zum Erstellen der ausführbaren Anwendung, zum Speichern als bereitstellbares Artefakt und zum Bereitstellen dieses Artefakts in einer Compute-Instanz verwendet. Der zweite Artikel zeigt auch, wie Sie einen HTTP-Endpunkt für die Anwendung über ein OCI-API-Gateway bereitstellen.

Dieser dritte Teil zeigt, wie Sie serverlose Funktionen in Go erstellen und auf OCI bereitstellen. Das Go SDK für OCI wird eingeführt – zunächst für lokale, eigenständige Go-Anwendungen und anschließend für die Verwendung aus Funktionen – und nutzt dabei die Resource-Principal-Authentifizierung. Dieses SDK wird für die Interaktion mit dem OCI Object Storage-Service zum Erstellen von Buckets und zum Schreiben und Lesen von Dateien verwendet.

Zunächst wird die Funktion manuell erstellt und bereitgestellt. Dem Deployment in API Gateway wird eine Route hinzugefügt, um die Funktion von einem Client außerhalb von OCI aufzurufen. Anschließend wird eine OCI DevOps-Deployment-Pipeline zum Deployment der Funktion aus einem Image in der Containerimage-Registry erstellt. Schließlich wird eine Build-Pipeline eingerichtet, um Quellen im Code-Repository zu beziehen, ein Containerimage zu erstellen und zu veröffentlichen und dann die Deployment-Pipeline für End-to-End-Build und -Deployment auszulösen.

OCI Functions unterwegs

Serverlose Funktionen in OCI basieren auf der Technologie des Open-Source-Projekts Fn. Die Geschäftslogik der Funktion ist in Ihrer bevorzugten Sprache – in diesem Fall Go – geschrieben und in das Fn-Framework eingebettet, das den Lebenszyklus der Funktion und die Interaktionen mit der Funktion verarbeitet. Das Fn-Framework kann überall ausgeführt werden: auf Ihrem lokalen Rechner, in einer VM in einer beliebigen Cloud oder On Premise. Oracle Cloud Infrastructure bietet einen vollständig verwalteten OCI Functions-Service PaaS für serverlose Funktionen, die auf derselben Technologie basieren.

Eine Funktion ist in ein Containerimage integriert. Dieses Image wird an eine Containerimage-Registry übertragen. Um die Funktion zu veröffentlichen, wird dieses Bild an einen Fn-Server übertragen. Wenn die Funktion aufgerufen wird, wird ein Container vom Image gestartet, und die Anforderung wird verarbeitet. Container werden nach der Verarbeitung eines Aufrufs einige Zeit in einem Hot-Status ausgeführt, der bereit ist, zusätzliche Anforderungen zu verarbeiten. Wenn die Anzahl der Hintergrundprozesse steigt, werden vom Fn-Server zusätzliche Container für dieselbe Funktion gestartet, um sicherzustellen, dass alle Anforderungen verarbeitet werden können.

Die Attraktivität von Funktionen für Entwickler und Anwendungsbetreiber ist die Tatsache, dass keine Energie in das Entwerfen, Erstellen und Verwalten der Plattform, auf der die Geschäftslogik ausgeführt wird, investiert werden muss. Jeder Fokus kann darauf liegen, diese Logik zu schreiben.

Wir werden nun die Funktion in Go erstellen, in einem Containerimage erstellen, bereitstellen und lokal ausführen. Anschließend übernehmen wir diese Funktion für den OCI Functions-Service und machen ihn cloudbasiert.

Die Fn Entwicklungsumgebung

Um Funktionen zu entwickeln, benötigen Sie eine Umgebung, die Project Fn unterstützt. Fn ist eine schlanke Docker-basierte serverlose Funktionsplattform, die Sie auf Ihrem Laptop, Server oder Ihrer Cloud ausführen können. Sie können Fn einfach unter Linux oder MacOS installieren, indem Sie die Anweisungen unter Fn-Projekt-Tutorials – Fn installieren befolgen.

Sie können wählen, ob Sie mit der go-app-vm-Compute-Instanz arbeiten möchten, die wir in der ersten erstellt und auch in der zweiten Rate dieser Serie verwendet haben. Diese Oracle Linux-Umgebung ist nicht mit Fn eingerichtet, aber die Installation ist relativ einfach.

Alternativ können Sie in OCI Cloud Shell arbeiten. Diese browserbasierte Umgebung ist mit Fn eingerichtet. Informationen zum Arbeiten mit Fn in OCI Cloud Shell finden Sie unter OCI-Dokumentationsfunktionen: Erste Schritte mit Cloud Shell.

Fn-Funktion entwickeln

Navigieren Sie in der Entwicklungsumgebung mit installierter Fn-CLI zu einem Verzeichnis, in dem Sie das Unterverzeichnis der Funktion erstellen möchten. Geben Sie diesen Befehl in der Befehlszeile ein, und führen Sie ihn aus:

 fn init --runtime go --trigger http greeter 

Es wird ein Unterverzeichnis namens Greeter erstellt. Navigieren Sie hinein und prüfen Sie den Inhalt:

 cd greeter ls -l 

Die Datei func.yaml enthält die Metadaten zur Funktion, die beim Erstellen vom Fn-Framework interpretiert werden sollen, und später beim Ausführen der Funktion. Die Datei go.mod enthält die Abhängigkeit der Funktion vom fdk-go-Package. Die eigentliche Funktion befindet sich in func.go. Die Struktur der Funktion und ihre Interaktion mit der Fn-Laufzeit sind hier zu sehen: Funktionshauptverzeichnis registriert die Funktion myHandler mit der Fn-Laufzeit, die anweist und es der Laufzeit ermöglicht, diese Funktion für jede empfangene HTTP-Anforderung aufzurufen. Die Funktion empfängt den Text der Anforderung in einem io.Reader-Parameter. Er empfängt auch die Ausgabe von io.Writer, in die der Antwortbody geschrieben werden kann. Der Parameter context.Context ctx enthält Metadaten für die ursprüngliche Anforderung, einschließlich HTTP-Headern, der vollständigen URL, der Anforderungsmethode und der Funktion selbst, einschließlich aller dafür definierten Konfigurationsvariablen.

Derzeit decodiert die Funktion myHandler den Anforderungstext und erwartet, dass er eine JSON-Payload mit einem Feld namens Name enthält. Es erstellt eine Person, deren Name auf den Wert dieses Feldes gesetzt ist oder deren Abwesenheit standardmäßig auf "Welt" gesetzt ist. Anschließend wird die erwartete Antwort erstellt: ein JSON-Objekt mit einem einzelnen Feld namens message, das eine Zeichenfolge aus Hello und dem Namenswert enthält.

Obwohl es nichts wirklich spektakuläres tut, ist die Funktion solide und vollständig und wir können sie lokal bereitstellen und aufrufen. Dazu benötigen wir einen lokalen Kontext und den lokalen Fn-Server. Prüfen Sie die Kontexte mit:

 fn list contexts 

Dies zeigt eine Liste von mindestens einem Kontext - möglicherweise mehr als einem. Um mit dem lokalen Fn-Server zu arbeiten, stellen Sie sicher, dass der Standardkontext der aktive ist. Wenn Sie den aktuellen Kontext auf den Standardwert setzen möchten, verwenden Sie:

 fn use context default 

Erstellen Sie nun eine Anwendung als Host für die Funktion:

 fn create app go-oci-app fn list apps 

Wenn die erste dieser Anweisungen fehlschlägt und die Verbindung abgelehnt wurde, wird der Server wahrscheinlich noch nicht ausgeführt. Verwenden Sie den nächsten Befehl zum Starten des Servers, und versuchen Sie dann erneut, die Anwendung zu erstellen.

 fn start 

Wenn die Anwendung erfolgreich erstellt wurde, kann die Funktion jetzt in sie bereitgestellt werden. Der nächste Befehl kümmert sich um dieses Deployment. Vor ihm steht der Prozess zum Erstellen von Containerimages.

 fn --verbose deploy --app go-oci-app --local 

Wenn Sie --local angeben, wird das Deployment auf dem lokalen Server ausgeführt, aber das Funktionsimage wird nicht an eine Docker-Registry übertragen. Dies wäre erforderlich, wenn wir es auf einem Remote-Fn-Server bereitstellen würden.

Da es eine beeindruckende Anzahl von Logmeldungen freigibt, die produziert werden sollen, ist das --verbose-Flag nicht das, was Sie die ganze Zeit verwenden werden. Es gibt Ihnen jedoch einen ziemlich guten Einblick in das, was vor sich geht. Mehrere Container-Images werden abgerufen, dann wird ein zweistufiger Container-Build-Prozess zum Erstellen eines Container-Images für die Greeter-Funktion ausgeführt. Vordefinierte Fn-Projektbilder werden für die Build-Phase (fnproject/go:1.15-dev zum Zeitpunkt des Schreibens) und als Grundlage für das Laufzeitbild (fnproject/go:1.15) verwendet.

Die endgültige Ausgabe sieht folgendermaßen aus:

 Updating function greeter using image greeter:0.0.2... Successfully created function: greeter with greeter:0.0.2 Successfully created trigger: greeter Trigger Endpoint: http://localhost:8080/t/go-oci-app/greeter 

Das Funktionsbild heißt greeter:0.0.2. Sie finden dieses Image in der lokalen Containerregistrierung mit:

 docker images | grep greeter 

Die Funktion kann über die Fn-CLI mit ihrem Namen und ihrer Anwendung wie folgt aufgerufen werden:

 fn invoke go-oci-app greeter 

Die Funktion erwartet eine JSON-Payload, die ein Namensfeld enthält. Geben Sie daher genau Folgendes an:

 echo -n '{"name":"Clark Kent"}' | fn invoke go-oci-app greeter --content-type application/json 

Die Ausgabe aus dem Funktions-Deployment gab uns auch den Trigger-Endpunkt für die Funktion. Dies ist ein HTTP-Endpunkt, an den wir eine HTTP-Anforderung senden und die Funktion auslösen können. Wir haben keine (sichtbare) Interaktion mit Fn, obwohl der Endpunkt, den wir aufrufen, wirklich der Fn-Server-Endpunkt ist. Der URL-Pfad weist Fn die auszulösende Anwendung und spezifische Funktion an.

 curl -X "POST" -H "Content-Type: application/json" -d '{"name":"Mickey Mouse"}' http://localhost:8080/t/go-oci-app/greeter 

OCI-Funktion erstellen

Erstellen wir nun diese Funktion auf OCI anstatt nur in unserer Entwicklungsumgebung. Die Schritte sind denen sehr ähnlich, die wir zum Erstellen der Funktion vor Ort verwendet haben; wir müssen nur einen anderen Kontext verwenden. Nicht der lokale Kontext, sondern einer für OCI.

Anwendung erstellen

Erstellen Sie zunächst die Anwendung über die OCI-Konsole. Geben Sie die App in das Suchfeld ein und klicken Sie im Servicebereich auf Anwendungsfunktionen.

Klicken Sie auf die Schaltfläche Anwendung erstellen. Geben Sie den Namen für die Anwendung ein: go-on-oci-app. Wählen Sie das VCN aus, das in einem Teil der Artikelserie und dem zugehörigen öffentlichen Subnetz erstellt wurde. Klicken Sie anschließend auf Erstellen, um die Anwendung zu erstellen.

Way-to-Go-on-oci-Artikel

Lokale Umgebung für OCI-Interaktions- und Funktionsimagepush vorbereiten

Nachdem die Anwendung erstellt wurde, werden die allgemeinen Informationen für die Anwendung angezeigt. Die Seite enthält auch Anweisungen zum Erstellen Ihrer ersten Funktion, entweder in der OCI Cloud Shell oder in einem lokalen Setup (was natürlich auch die Go-App-vm-Compute-Instanz sein kann).

Way-to-Go-on-oci-Artikel

Wenn Sie die OCI Cloud Shell verwenden, unterscheiden sich die Schritte zum Erstellen dieses Kontextes etwas (und sind einfacher) als bei der Arbeit in einer regulären Entwicklungsumgebung. Fühlen Sie sich frei, das OCI Shell-Setup zu befolgen. In diesem Artikel gehen wir den anderen Weg, der für jede lokale Entwicklungsumgebung verwendet wird.

In einer lokalen Umgebung (in der zuvor Fn-CLI installiert wurde) müssen mehrere Schritte ausgeführt werden:

  1. Richten Sie einen API-Signaturschlüssel ein, und speichern Sie den Private Key in einer PEM-Datei im lokalen HOME/.OCI-Verzeichnis. Laden Sie den Public Key in die OCI-Konsole hoch. Anweisungen hierzu finden Sie in der OCI-Dokumentation - Erforderliche Schlüssel.
  2. Erstellen Sie die Dateikonfiguration im Verzeichnis .oci in der lokalen Umgebung. Kopieren Sie das Snippet zur Konfigurationsdatei-Vorschau in die Konfigurationsdatei. Aktualisieren Sie den Eintrag key_file in der Datei: Verweisen Sie darauf auf die PEM-Datei des Private Keys. OCI-Dokumentation - SDK- und CLI-Konfigurationsdatei.
  3. Um Containerimages an die OCI Container Image Registry zu übertragen, benötigen Sie ein Authentifizierungstoken. In einem Teil dieser Artikelserie haben Sie ein Token für die Anmeldung beim DevOps Code Repository vom Git-Client erstellt. Sie können dieses Token wiederverwenden, um den Docker-Client bei der Containerimage-Registry zu protokollieren, oder Sie können ein neues Authentifizierungstoken erstellen. Im letzteren Fall finden Sie weitere Informationen unter OCI-Dokumentation - Authentifizierungstoken abrufen.
  4. Sie benötigen die OCI-CLI. Anweisungen zur Installation dieses Tools finden Sie in der OCI-Dokumentation: Mit der OCI-Befehlszeilenschnittstelle arbeiten - Schnellstart. Die OCI-CLI verwendet die Datei HOME/.oci/config und den referenzierten Private Key, um sichere Verbindungen zu den OCI-APIs herzustellen.

Nach diesen Schritten können Sie mit diesem Befehl den Erfolg der Schritte 1, 2 und 4 ausprobieren, der eine Liste der Compartments in Ihrem Mandanten zurückgeben sollte:

 oci iam compartment list 

Optional: Containerimage-Registry-Repository erstellen

Wenn der für das Deployment der Funktion verwendete Benutzeraccount über die erforderlichen IAM-Berechtigungen verfügt, erstellt das Deployment das Repository für die Funktionsimages in der Containerimage-Registry. Falls diese Berechtigungen nicht verfügbar sind oder Sie das Repository vorbereiten möchten, können Sie dies wie folgt tun.

  1. Geben Sie regi in die Suchleiste ein. Klicken Sie auf den Link Container Registry > Container & Artefakte.
  2. Klicken Sie auf "Repository erstellen". Geben Sie den Namen des Repositorys ein: go-on-oci/greeter. Diese besteht aus dem Repository-Präfix und dem Namen der Funktion, in der das Repository die Images enthält. Setzen Sie den Zugriff auf "Öffentlich".
  3. Way-to-Go-on-oci-Artikel
  4. Klicken Sie auf die Schaltfläche Repository erstellen. Nach einigen Sekunden wird ein neues und leeres Containerimage-Repository erstellt, das bereit ist, die Funktions-(Container-)Images zu empfangen, die wir mit der Fn-CLI übertragen.

Kontext für OCI in Fn-CLI erstellen

Wenn Sie zur Befehlszeile der lokalen Umgebung zurückkehren, müssen Sie einen Fn-Kontext für das aktuelle Compartment auf OCI erstellen und ihn anschließend zur Verwendung in Fn-Vorgängen auswählen. Führen Sie diese Befehle aus (die Sie auf der Go-on-oci-app-Seite auf der Registerkarte "Erste Schritte" kopieren können):

 fn create context go-on-oci --provider oracle fn use context go-on-oci 
Way-to-Go-on-oci-Artikel

Kopieren Sie die Befehle unter Schritt 4, um den Kontext mit der Compartment-OCID und der Oracle Functions-API-URL zu aktualisieren. In meinem Fall:

 fn update context oracle.compartment-id ocid1.compartment.oc1..aaaaaaaaqb4vxvxuho5h7eewd3fl6dmlh4xg5qaqmtlcmzjtpxszfc7nzbyq fn update context api-url https://functions.us-ashburn-1.oraclecloud.com 

Der Befehl ist ähnlich, aber anders für Sie.

Geben Sie das eindeutige Präfix für den Repository-Namen an. Verwenden Sie go-on-oci, und geben Sie das Compartment an, das das Image-Registry-Repository enthält, in dem das Funktionsimage veröffentlicht werden muss:

 fn update context registry iad.ocir.io/idtwlqf2hanz/go-on-oci fn update context oracle.image-compartment-id  

Melden Sie sich bei der Registrierung mit dem Authentifizierungstoken als Kennwort an:

 docker login iad.ocir.io 

In meinem Fall ist die Region, in der ich arbeite, Ashburn, identifiziert mit dem Regionsschlüssel iad.ocir.io. Ich werde zur Eingabe des Benutzernamens aufgefordert. Dies ist eine Zeichenfolge, die das Namespace-Präfix enthält, das im Namen der Containerimage-Registry und jedes Repository enthalten ist. Anschließend wird das Passwort angefordert. Hier stellen Sie ein für den Benutzer eingerichtetes Authentifizierungstoken bereit, das wir zuvor im vorherigen Artikel verwendet haben, als die Anmeldung im Code-Repository durchgeführt wurde.

Der nächste Befehl zeigt eine Liste der Anwendungen im aktuellen Fn-Kontext:

 fn list apps 

Die Liste enthält eine Anwendung namens go-on-oci-app.

Der zuvor erstellte, lokal bereitgestellte und aufgerufene Funktions-Greeter kann jetzt auch in der OCI-Anwendung bereitgestellt werden, um eine cloudnative, serverlose Funktion zu werden. Der für das Deployment verwendete Befehl ist derselbe wie zuvor. Seine Wirkung ist aufgrund des veränderten Kontextes dramatisch unterschiedlich. Anstelle eines lokalen Kontextes gibt es jetzt einen Kontext, der auf einem OCI-Provider basiert und mit einem OCI-Mandanten und -Compartment verknüpft ist. Das Containerimage wird in die OCI Container Image Registry übertragen, und die Funktion wird im OCI Function-Service erstellt.

 fn -v deploy --app go-on-oci-app 

Die Ausgabe ähnelt der zuvor generierten Ausgabe, aber der Erstellungsprozess ist genau derselbe. Sobald das Image des Funktionscontainers fertig ist, beginnen die Dinge zu weichen. Das Image wird in die OCI Container Image Registry übertragen, und die Funktion wird in der Cloud bereitgestellt. Die zugehörigen Zeilen in der Ausgabe:

 => exporting to image 0.0s => => exporting layers 0.0s => => writing image sha256:008dc3b990f1e69d67a7dd8649fbd63649d72f0bf1a161b2c2e073064f16c918 0.0s => => naming to iad.ocir.io/idtwlqf2hanz/go-on-oci/greeter:0.0.3 0.0s Parts: [iad.ocir.io idtwlqf2hanz go-on-oci greeter:0.0.3] Using Container engine docker to push Pushing iad.ocir.io/idtwlqf2hanz/go-on-oci/greeter:0.0.3 to docker registry...The push refers to repository [iad.ocir.io/idtwlqf2hanz/go-on-oci/greeter] ... e57f007acf74: Pushed 0.0.3: digest: sha256:bb4f2abde44d97517520571a21c407e349ddfc6572583a8ba53717436fd0b7f5 size: 1155 Updating function greeter using image iad.ocir.io/idtwlqf2hanz/go-on-oci/greeter:0.0.3... Successfully created function: greeter with iad.ocir.io/idtwlqf2hanz/go-on-oci/greeter:0.0.3 Fn: HTTP Triggers are not supported on Oracle Functions 

Zu diesem Zeitpunkt befindet sich die Funktion in der Cloud und kann aufgerufen werden (noch mit der Fn-CLI):

 fn invoke go-on-oci-app greeter 

Der erste Aufruf dauert einige Zeit, da die Funktion kalt beginnt und das zugrunde liegende Containerimage in einem laufenden Container instanziiert werden muss. Jeder nachfolgende Aufruf der Funktion wird viel schneller sein. Wenn Sie zehn Minuten warten und die Funktion kalt wird, wird der Container gestoppt.

Dieses Bild beschreibt die Situation, in der wir angekommen sind:

Way-to-Go-on-oci-Artikel

Sie können in der OCI-Konsole nachweisen, was gerade passiert ist. Geben Sie "greeter" in das Suchfeld in der Konsole ein. Unter Ressourcen wird ein Eintrag >greeter > Functions> angezeigt. Klicken Sie auf den Link, um zur Seite zu gelangen, auf der Details für die Funktion angezeigt werden. Sie finden Referenzen auf das Funktionsbild, die Speichereinstellung und den Endpunkt für den Aufruf der Funktion. Unter Metriken sollten Sie Beweise für den Aufruf der Funktion finden, die mit der Fn-CLI vorgenommen wurde.

In den Suchergebnissen für Greeter finden Sie auch das Container Repository go-on-oci/greeter. Wenn Sie zum Repository navigieren, finden Sie Details zu den darin veröffentlichten Images.

API-Gateway-Route für Funktion erstellen

OCI Functions kann nicht nur aufgerufen werden. Obwohl sie einen HTTP-Endpunkt haben, der vorschlägt, dass Sie sie einfach über Ihren Browser oder die Befehlszeile aufrufen können, ist es eigentlich nicht ganz so einfach. HTTP-Aufrufe an Funktionen müssen signiert werden, und dieser Signaturprozess ist nicht einfach und unkompliziert.

Ein besserer Weg, damit Verbraucher Funktionen aufrufen können, ist ein API-Gateway. Im vorherigen Artikel haben wir ein API-Gateway verwendet, um eine öffentliche Route zur myserver-Anwendung zu öffnen, die auf einer (potenziell) privaten Compute-Instanz ausgeführt wird. Jetzt werden wir dasselbe für die Greeter-Funktion tun, indem wir eine zusätzliche Route im API Gateway the-API-gateway und dem Deployment myserver-API verwenden, das im vorherigen Artikel erstellt wurde.

Way-to-Go-on-oci-Artikel

IAM-Zugriff für das API-Gateway einrichten

Das API-Gateway muss berechtigt sein, die Funktion mit einer Policy aufzurufen, die dem API-Gateway die Berechtigung zum Aufrufen von Funktionen erteilt.

Erstellen Sie die Policy für API Gateway, um Funktionen aufzurufen. So erstellen Sie eine Policy in der Konsole: Geben Sie in der Suchleiste "poli" ein, und klicken Sie im Suchergebnis-Popup im Bereich "Services" auf "Policys > Identity". Dadurch gelangen Sie zur Überblickseite "Policys" für das aktuelle Compartment.

Die Policy definiert die Berechtigung für die API-Gateways, auf Ressourcen im Compartment zuzugreifen. Erstellen Sie eine neue Policy, geben Sie einen Namen (invoke-function-for-api-gateway), eine Beschreibung und die folgende Anweisung ein:

ALLOW any-user to use functions-family in compartment  where ALL {request.principal.type= 'ApiGateway', request.resource.compartment.id = ''}

Ersetzen Sie durch den Namen des Compartments, das wahrscheinlich Go-on-oci ist. Ersetzen Sie durch die ID des Compartments, in dem Sie arbeiten.

Route für die Funktion im Deployment im API-Gateway definieren

Mit den erforderlichen Berechtigungen können wir jetzt die Route im API-Gateway definieren. Geben Sie gat in die Suchleiste in der Konsole ein. Klicken Sie auf Gateways > API-Management. Klicken Sie auf den Link für *the-api-gateway. Klicken Sie auf "Deployments". Klicken Sie in der Liste der Deployments (die ein einzelnes Deployment enthält) auf den Link myserver-api.

Klicken Sie auf die Schaltfläche Bearbeiten, um die Deployment-Spezifikation zu öffnen. Klicken Sie auf den Link für den zweiten Schritt: Routen. Scrollen Sie nach unten und klicken Sie auf die Schaltfläche + Weitere Route.

Geben Sie /Gruß als Pfad für diese neue Route ein. Wählen Sie GET als Methode und Oracle Functions als Typ (Backend). Wählen Sie die Anwendung go-on-oci-app und dann den Funktionsnamen auf greeter.

Way-to-Go-on-oci-Artikel

Klicken Sie auf "Weiter". Klicken Sie dann auf Änderungen speichern, um die Änderungen anzuwenden und die neue Route zu realisieren.

Funktion über das API-Gateway aufrufen

Nachdem die neue Route eingerichtet und das Deployment im API-Gateway aktualisiert wurde, können wir jetzt eine einfache, einfache HTTP-Anforderung an den öffentlichen Endpunkt des API-Gateways erstellen, wodurch die Funktion indirekt Greeter ausgelöst und ihre Antwort empfangen wird.

Mit dieser URL in jedem Browser können Sie die Antwort der Funktion abrufen:

https:///my-api/greeting

Die Antwort ist ein wenig unerträglich, aber das wird mit einer so vereinfachten Funktion erwartet.

Mit curl können Sie eine JSON-Payload an die Funktion senden und eine etwas interessantere Antwort erhalten.

curl -X "GET" -H "Content-Type: application/json" -d '{"name":"Mickey Mouse"}' https:///my-api/greeting

Die Antwort lautet {"message":"Hallo Mickey Mouse"}.

Jetzt haben wir also den End-to-End-Fluss vom API Gateway zur serverlosen Funktion eingerichtet. Und wir haben die Möglichkeit, die Funktion basierend auf den Quellen in unserer lokalen Entwicklungsumgebung manuell bereitzustellen. Um unsere Arbeit zu nutzen, können Sie einige Änderungen am Quellcode in func.go vornehmen und dann die Funktion erneut bereitstellen - einen einzigen Befehl mit der Fn-CLI - und die Begrüßungsroute im API-Gateway aufrufen, um zu sehen, dass unsere Änderung live ist.

Beispiel: Ändern Sie die Zeile, die den Wert für "Nachricht" festlegt auf

Msg: fmt.Sprintf("Warmest greetings from your function dear %s", p.Name)

Speichern Sie die aktualisierte func.go-Quelle. Führen Sie dann die folgenden Befehle aus, um die aktualisierte Funktion bereitzustellen, und rufen Sie sie anschließend auf:



fn -v deploy --app go-on-oci-app
curl -X "GET" -H "Content-Type: application/json" -d '{"name":"Mickey Mouse"}' https:///my-api/greeting
	  

Dies sollte zu einer verbesserten Reaktion führen. Der Build- und Deployment-Prozess wird in einer vorbereiteten Umgebung zu einem einzelnen manuellen Befehl zusammengefasst. Als Nächstes betrachten wir einen automatisierten Deployment-Prozess für Funktionen mit OCI DevOps, gefolgt vom vorherigen automatisierten Build-Prozess basierend auf der Quelle in einem Code-Repository. Dann werden wir auf Funktionen gehen, die etwas mehr tun, als einfache Grüße zurückzugeben.

Automatisiertes Deployment von Funktionen

Im vorherigen Teil dieser Serie haben wir die Verwendung von OCI DevOps Deployment Pipelines für das Deployment einer Anwendung in einer Compute-Instanz gesehen. Jetzt verwenden wir eine Pipeline für die automatisierte Bereitstellung einer Funktion. Der Gesamtansatz und die Zutaten sind ähnlich. Wir benötigen ein Artefakt, eine (Ziel-)Umgebung und die Deployment-Pipeline mit einer Funktions-Deployment-Phase sowie IAM-Berechtigungen, damit die Pipeline das Artefakt lesen und die Funktion bereitstellen kann.

Way-to-Go-on-oci-Artikel

Diese Zutaten im Detail:

  1. Ein Artefakt: Die Referenz auf in diesem Fall ein bestimmtes Containerimage in der OCI Container Image Registry, das den vollqualifizierten Pfad zum Repository sowie das spezifische Image und die Version verwendet.
  2. Eine Umgebung: Die Referenz auf die Funktion, die ich bereitstellen möchte (erneut). Die Umgebung beim Funktions-Deployment ist nicht das Compartment oder eine Anwendung in einem Compartment (wie man vermuten könnte), sondern die Funktion selbst, die daher bereits vorhanden sein muss, bevor sie über eine OCI-Deployment-Pipeline DevOps bereitgestellt werden kann. (Beachten Sie, dass die Funktion nicht nützlich sein muss – sie kann auf dem Scratch-Container-Image basieren.)
  3. Eine Deployment-Pipeline mit einer Deployment-Pipelinephase des Typs "Funktions-Deployment", die das Artefakt und die Umgebung verbindet.
  4. Eine dynamische Gruppe, die die Deployment-Pipeline enthält, und IAM-Policys, mit denen die dynamische Gruppe Artefakte (wie Funktionscontainerimages) lesen und Funktionen bereitstellen kann (im Großen und Ganzen OCI-Ressourcen verwalten).

DevOps-Artefakt für das Funktionscontainerimage erstellen

Navigieren Sie in der OCI-Konsole zur Homepage für die Go-on-OCI des Projekts DevOps. Öffnen Sie die Registerkarte "Artefakte". Klicken Sie auf die Schaltfläche "Artefakt hinzufügen". Beachten Sie, dass wir hier einen Link oder Proxy aus dem Projekt DevOps zu einem tatsächlichen Artefakt definieren, nicht das Artefakt selbst.

Geben Sie die Greeter-Funktion als Namen des DevOps-Artefakts ein. Der Typ muss auf "Containerimage-Repository" gesetzt werden. Der vollqualifizierte Pfad zum Image besteht aus dem Regionsschlüssel, dem Repository-Namespace und dem Präfix, dem Funktionsnamen und dem Funktionsversionstag. Verwenden Sie in diesem Fall einen Platzhalter für das Versionstag. Der Pfad ist jetzt wie folgt definiert:

///greeter:${imageVersion}

Setzen Sie das Dropdown-Feld "Parameter, die in diesem Artefakt verwendet werden, ersetzen" auf "Ja".

Way-to-Go-on-oci-Artikel

Klicken Sie auf die Schaltfläche "Hinzufügen", um die Definition des Artefakts abzuschließen und zu speichern.

Definieren Sie die DevOps-Umgebung für die Funktion.

Öffnen Sie die Registerkarte "Umgebungen" im Projekt DevOps. Es enthält die go-on-oci-vm-Umgebung, die für das Deployment von myserver für die Compute-Instanz erstellt wurde (im vorherigen Artikel). Klicken Sie auf die Schaltfläche Umgebung erstellen.

Im ersten Schritt, Basisinformationen, klicken Sie auf die Kachel Funktionen - Erstellen einer Umgebung für eine Funktion. Geben Sie den Namen der Umgebung "greeter-function-in-app-go-on-oci-app" ein. Klicken Sie auf "Weiter", um mit den Umgebungsdetails zum zweiten Schritt zu gelangen. Bestätigen Sie Region, Compartment, Anwendung und Funktion. Sie müssen wahrscheinlich keine dieser Einstellungen ändern. Stellen Sie in diesem Fall sicher, dass die Funktion Greeter in der Anwendung go-on-oci-app ausgewählt ist.

Klicken Sie auf "Umgebung erstellen", um die Definition zu speichern.

Way-to-Go-on-oci-Artikel

Deployment-Pipeline für das Deployment der Funktion erstellen

Klicken Sie auf der Überblickseite des Projekts DevOps auf "Pipeline erstellen". Das Formular "Pipeline erstellen" wird angezeigt. Geben Sie einen Namen (deploy-greeter-function-to-go-on-oci-app) und optional eine Beschreibung ein. Klicken Sie anschließend auf Pipeline erstellen. Die Deployment-Pipeline wird erstellt, obwohl sie ziemlich leer ist: keine Umgebung, in der sie bereitgestellt werden soll, keine Artefakte, die bereitgestellt werden sollen, und keine Konfigurationsdatei zur Definition der auszuführenden Schritte.

Klicken Sie im angezeigten Pipelineeditor auf die Kachel "Phase hinzufügen" (oder auf das Pluszeichen). Auf der nächsten Seite wird eine Liste der Phasentypen angezeigt. Klicken Sie auf die Kachel mit der Bezeichnung Verwendet die integrierte Functions-Aktualisierungsstrategie.

Klicken Sie auf "Weiter".

Geben Sie den Namen der Stufe ein, z.B. update-function-greeter. Wählen Sie die Umgebung aus, die zuvor für die Funktion definiert wurde: greeter-function-in-app-go-on-oci-app.

Klicken Sie unter der Überschrift "Artefakt" auf "Artefakt auswählen". Eine Liste aller Artefakte im Projekt DevOps des Typs "Docker-Image" wird angezeigt. Wählen Sie den einzigen Eintrag aus, den Sie zuvor für das Funktionscontainerimage erstellt haben.

Beachten Sie, dass die Schaltfläche "Artefakt auswählen" nicht mehr aktiviert ist: Dieser Phase kann nur ein einzelnes Containerimage zugeordnet werden.

Way-to-Go-on-oci-Artikel

Klicken Sie auf Hinzufügen. Die Pipelinephase wird in der Pipeline erstellt. Und die Pipeline ist nun bereit, ausgeführt zu werden – ihre Definition ist abgeschlossen. Oder doch? Das Artefakt, das diese Pipeline verwendet, ist nicht eindeutig definiert: Das Versionslabel im Pfad für das Containerimage enthält den Platzhalter ${imageVersion}. Um sicherzustellen, dass die richtige Version für das Deployment verwendet wird, muss dieser Platzhalter durch den richtigen Wert ersetzt werden. Dazu wird ein Parameter in der Pipeline mit dem Namen imageVersion definiert, der auf ein vorhandenes Versionslabel gesetzt ist.

Klicken Sie auf die Registerkarte "Parameter" für die Pipeline. Definieren Sie einen neuen Parameter namens imageVersion. Der Standardwert kann beliebig sein, aber er kann auch einem vorhandenen Versionslabel für das Greeter-Funktionscontainerimage entsprechen. Speichern Sie die Parameterdefinition.

Es scheint, dass die Pipeline bereit ist, ausgeführt zu werden, aber wir müssen immer noch sicherstellen, dass sie ihre Arbeit erledigen darf. Bevor Sie etwas Ausschlag versuchen, lesen Sie den nächsten Abschnitt.

Dynamische Gruppen und Policys

Im vorherigen Artikel wurde eine dynamische Gruppe für alle Deployment-Pipelines im Compartment definiert. Die neue Pipeline ist automatisch ein Mitglied dieser Gruppe. Außerdem haben wir eine Policy definiert, die der dynamischen Gruppe Berechtigungen zum Lesen aller Artefakte erteilt hat. Dazu gehören (Funktions-)Containerimages in den Containerimage-Registry-Repositorys des Compartments. Eine andere Policy, die bereits erstellt wurde, erteilt der dynamischen Gruppe die sehr breite Berechtigung zur Verwaltung aller Ressourcen im Compartment. Wir können von dem breiten Anwendungsbereich dieser Politik profitieren, da sie auch die Erstellung und Aktualisierung von Funktionen umfasst.

Deployment-Pipeline ausführen

Führen Sie die Deployment-Pipeline aus, indem Sie auf "Pipeline ausführen" klicken.

Sobald das Deployment abgeschlossen ist, werden die grünen Marker angezeigt, die den Erfolg verkünden. Ein weiterer offensichtlicher Hinweis auf diesen Erfolg gibt es jedoch nicht, da das Endergebnis genau die Situation ist, die wir mit der manuellen Bereitstellung der Funktion über die Fn CLI-Befehlszeile erreicht haben.

Way-to-Go-on-oci-Artikel

Um die Dinge etwas interessanter zu machen, werden wir den Code der Funktion ändern. Erstellen Sie dann das Containerimage für die Funktion (lokal), und übertragen Sie das neue Funktionsimage in die Containerimage-Registry. Dann starten wir die Deployment-Pipeline erneut. Wenn dies erfolgreich ist, wird eine neue Situation geschaffen, die wir erleben können, indem wir die my-API/greeting-Route auf dem API-Gateway aufrufen.

Funktionsimplementierung ändern

Nehmen Sie eine kleine, aber sichtbare Änderung an func.go in Ihrer lokalen Umgebung vor: Stellen Sie sicher, dass die Antwort der neuen Version der Funktion deutlich von der aktuellen Version abweicht. Speichern Sie die Änderung.

In den nächsten Abschnitten wird eine neue Version des Funktionscontainerimages aus der geänderten Quelle erstellt und schließlich auf OCI Functions ausgeführt.

Neues Funktionscontainerimage erstellen (lokal)

Diese nächsten Befehle ändern zuerst das Versionslabel, mit dem die Funktion mit einer Erhöhung der dritten Ziffer gekennzeichnet wird (bm steht für Bump). Als Nächstes wird das Funktionscontainerimage mit den geänderten Quellen erstellt. Der dritte Befehl listet die lokalen Containerimages auf und filtert Bilder mit einem Grüner in ihrem Namen. Führen Sie nun die Befehle aus.




fn bm
fn build 
docker images | grep greeter
	  

Sie sollten in der Lage sein, das neu erstellte Image mit seinem vollqualifizierten Namen zu finden, einschließlich des OCI-Regionsschlüssels, des Namespace, des Repository-Präfixes und des Funktionsnamens greeter, wobei das Versionslabel angehängt ist.

Containerimage mit neuem Versionslabel taggen und an Registry übertragen

Definieren wir eine neue ID für das Image mit diesem Befehl, der die Versionsbezeichnung auf 0.1.0 setzt:




docker tag :  :0.1.0

	  

Übertragen Sie dann das neue Funktionscontainerimage mit folgendem Befehl in das OCI Container Image Registry-Repository:




docker push :0.1.0

	  

Beachten Sie, dass wir die Funktion zu diesem Zeitpunkt nicht basierend auf dieser neuen Version des Containerimages erneut bereitgestellt haben. Wir haben nur das Image erstellt und an die Registry auf OCI übertragen. Beim Aufrufen der OCI-Funktion wird kein Unterschied angezeigt.

Deployment-Pipeline ausführen (für das neue Funktionsimage)

Führen Sie die Deployment-Pipeline erneut aus. Setzen Sie den Wert des Parameters imageVersion auf 0.1.0.

Wenn die Pipeline erfolgreich abgeschlossen ist, ist die neue Version der Funktion mit allen spannenden Änderungen, die Sie darauf angewendet haben, live.

Neu bereitgestellte Funktion aufrufen

Sie können die neue Funktionsversion in Aktion sehen, indem Sie sie in der Befehlszeile mit der Fn-CLI aufrufen:




fn invoke go-on-oci-app greeter

	  

(Da der Kontext der Fn-CLI weiterhin vom Oracle-Provider übernommen wird und für das Go-on-OCI-Compartment konfiguriert ist, das die Greeter-Funktion enthält, wird dieser Aufruf an die OCI-Funktion weitergeleitet, die zu diesem Zeitpunkt auf der neuen Version des Containerimages basiert.)

Alternativ können Sie die Route im API-Gateway, das die Funktion aufruft, curlieren:




curl -X "GET" -H "Content-Type: application/json" -d '{"name":"Mickey Mouse"}' https:///my-api/greeting

	  

Automatisierter Funktionsaufbau

Bisher haben wir das Funktionscontainerimage per Hand in der lokalen Entwicklungsumgebung mit der Fn CLI erstellt. Wie im vorherigen Artikel für die Go-Anwendung, die von einer Build-Pipeline als ausführbare Datei erstellt wurde, werden wir nun den Aufbau der Funktion in einen automatisierten Prozess verwandeln. Eine OCI DevOps-Build-Pipeline wird erstellt, um Quellen aus dem Code-Repository zu beziehen, eine verwaltete Build-Phase auszuführen, die ein lokales Containerimage erstellt, und dieses Image dann als Artefakt im Containerimage-Registry-Repository zu veröffentlichen. Im letzten Schritt löst die Build-Pipeline die Deployment-Pipeline aus, um die neueste Definition der Funktion in der OCI Functions-Laufzeitumgebung zu veröffentlichen.

Wenn alle Elemente vorhanden sind, sieht das gesamte miteinander verbundene Set von OCI-Komponenten wie in der nächsten Abbildung visualisiert aus.

Way-to-Go-on-oci-Artikel

Das Artefakt und die in dieser Abbildung gezeigte Deployment-Pipeline sind bereits im Projekt DevOps definiert, ebenso wie das Repository "Anwendung", "Funktion" und "Containerimage-Registry" für die Images für die Funktion. Wir verwenden das Code-Repository, das im vorherigen Artikel eingerichtet wurde. Alles, was wir erstellen müssen, ist die Build-Greeter-Funktion der Build-Pipeline mit ihren drei Phasen.

Build-Pipeline erstellen

Klicken Sie auf der Überblickseite für DevOps Project go-on-oci auf die Schaltfläche Build-Pipeline erstellen. Es wird eine Seite zur Angabe des Namens – z.B. build-greeter-function – und einer Beschreibung angezeigt. Klicken Sie auf "Erstellen", um die Build-Pipeline zum Projekt DevOps hinzuzufügen.

Klicken Sie auf den Link Build-Greeter-Funktion in der Liste, um zur Detailseite zu navigieren.

Erste Phase – Verwalteter Build

Die erste Phase in einer Build-Pipeline ist eine Phase "Verwalteter Build". Diese Phase enthält Anweisungen, mit denen die Pipeline einen Build-Server abrufen, bestimmte Quellen aus Code-Repositorys auf den Server kopieren und eine Reihe von Aktionen auf diesem Server ausführen kann. Zum Zeitpunkt des Schreibens können wir ein einzelnes Image für den Build-Server verwenden. Es handelt sich um ein Oracle Linux-Image (8 GB Arbeitsspeicher, 1 OCPU), das eine Reihe vorinstallierter Tools und Sprachlaufzeiten enthält. Für die Erstellung des Funktionscontainerimages ist es relevant, dass der Build-Server sowohl mit Docker als auch mit Fn-CLI ausgestattet ist.

Klicken Sie entweder auf das Plus-Symbol oder auf die Karte "Stufe hinzufügen". Der aus zwei Schritten bestehende Assistent "Phase hinzufügen" wird angezeigt. Stellen Sie im Assistenten im ersten Schritt sicher, dass die Karte "Verwalteter Build" für den Phasentyp ausgewählt ist. Klicken Sie auf "Weiter".

Die zweite Seite wird angezeigt. Definieren Sie einen Namen für die Build-Phase: build-go-source-to-function-container-image. Fügen Sie optional eine Beschreibung hinzu.

Gegenwärtig können wir kein anderes Build-Image auswählen, so dass wir uns mit dem verfügbaren zufrieden geben, was für unsere Zwecke in Ordnung ist.

Setzen Sie den Dateipfad der Build-Spezifikation auf /functions/greeter/go-function-build-spec.yaml. Diese Datei enthält die Anweisungen zum Erstellen der Go-Quellen in der Greeter-Funktion (oder einer anderen Go-Funktion) und schließlich zum Erstellen des Funktionscontainer-Images.

Klicken Sie unter Primärcode-Repository auf die Schaltfläche Auswählen. Jetzt können Sie angeben, aus welchem Code-Repository der Build seine Quellen erhält. Wählen Sie als Quellverbindungstyp "OCI-Code-Repository". Wählen Sie dann das Repository "go-on-oci-repo". Wir arbeiten mit Quellen in der Hauptverzweigung, also ändern Sie diesen Standardwert nicht. Geben Sie go-on-oci-sources als Wert für den Build-Quellnamen ein. Eine verwaltete Build-Phase kann Quellen aus mehreren Repositorys verwenden. In der Build-Spezifikation können wir auf die Root-Speicherorte jeder dieser Quellen mit dem Label verweisen, das als Build-Quellname definiert ist. Klicken Sie auf "Speichern".

Way-to-Go-on-oci-Artikel

Klicken Sie auf die Schaltfläche "Hinzufügen". Damit ist die Definition der verwalteten Build-Phase abgeschlossen. Dies ist alles, was benötigt wird, um Quellen zu nehmen und sie in Artefakte zu verarbeiten. Die detaillierten Anweisungen, die von dieser verwalteten Build-Phase und auf dem Build-Server ausgeführt werden, sind in der Datei go-function-build-spec.yaml definiert. Diese Datei enthält die Anweisungen für die tatsächlichen detaillierten Schritte, die auf dem Build-Server ausgeführt werden.




version: 0.1
component: build
timeoutInSeconds: 6000
runAs: root
shell: bash
env:
# these are local variables to the build config
variables:
SOURCE_DIRECTORY: "go-on-oci-sources/functions/greeter"
FUNCTION_NAME: "greeter"

# # the value of a vaultVariable is the secret-id (in OCI ID format) stored in the OCI Vault service
# you can then access the value of that secret in your build_spec.yaml commands
vaultVariables:

# exportedVariables are made available to use in sucessor stages in this Build Pipeline
# For this Build to run, the Build Pipeline needs to have a BUILDRUN_HASH parameter set
exportedVariables:
- BUILDRUN_HASH


steps:
- type: Command
name: "Export variables"
timeoutInSeconds: 40
command: |
export BUILDRUN_HASH=`echo ${OCI_BUILD_RUN_ID} | rev | cut -c 1-7`
echo "BUILDRUN_HASH: " $BUILDRUN_HASH
echo "fully qual sources" ${OCI_WORKSPACE_DIR}/${SOURCE_DIRECTORY}
echo "container image version from build pipeline parameter" ${imageVersion}      
go version

- type: Command
timeoutInSeconds: 600
name: "Install golangci-lint"
command: |
curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s -- -b $(go env GOPATH)/bin v1.37.1

- type: Command
timeoutInSeconds: 600
name: "Verify golangci-lint version"
command: |
/root/go/bin/golangci-lint version

- type: Command
timeoutInSeconds: 600
name: "Run go mod tidy for Go Application"
command: |
cd ${OCI_WORKSPACE_DIR}/${SOURCE_DIRECTORY}
go mod tidy

- type: Command
timeoutInSeconds: 600
name: "Run go vet for Go Application"
command: |
cd ${OCI_WORKSPACE_DIR}/${SOURCE_DIRECTORY}
go vet .

- type: Command
timeoutInSeconds: 600
name: "Run gofmt for Go Application"
command: |
gofmt -w ${OCI_WORKSPACE_DIR}/${SOURCE_DIRECTORY}

- type: Command
timeoutInSeconds: 600
name: "Run Lint for Go Application"
command: |
cd ${OCI_WORKSPACE_DIR}/${SOURCE_DIRECTORY}
/root/go/bin/golangci-lint run .

- type: Command
timeoutInSeconds: 600
name: "Run Unit Tests for Go Application (with verbose output)"
command: |
cd ${OCI_WORKSPACE_DIR}/${SOURCE_DIRECTORY}
go test -v 

- type: Command
timeoutInSeconds: 600
name: "Build Go Function into Function Container Image"
command: |
cd ${OCI_WORKSPACE_DIR}/${SOURCE_DIRECTORY}
pwd
fn build --verbose
image=$(docker images | grep $FUNCTION_NAME  | awk -F ' ' '{print $3}') ; docker tag $image go-function-container-image    


outputArtifacts:
- name: go-function-container-image
type: DOCKER_IMAGE
# this location tag doesn't effect the tag used to deliver the container image
# to the Container Registry
location: go-function-container-image:latest

	  

Die Build-Spezifikation besteht aus drei Teilen:

  1. Richten Sie ein, wer das Skript ausführen soll, welche Shell verwendet werden soll und welche Variablen verwendet werden sollen
  2. Build-Schritte: Shell-Befehle, die auf dem Build-Server ausgeführt werden sollen
  3. Ausgabeartefakte geben an, welche Dateien am Ende aller Build-Schritte aussagekräftig sind und für andere Schritte in der Pipeline verfügbar gemacht werden sollen (z.B. als Artefakt veröffentlichen)

Die Erstellungsschritte können wie folgt zusammengefasst werden:

  1. Umgebungsvariablen drucken und aktuell installierte Go-Version (auf dem Vanilla-Build-Server)
  2. golangci-lint installieren
  3. Überprüfen Sie den Erfolg und die Version der golangci-lint-Installation
  4. Führen Sie go mod tidy aus, um die Datei go.mod mit Abhängigkeiten zu organisieren
  5. Go-Vet ausführen, um eine erste Prüfung in den Go-Quellen auszuführen
  6. Führen Sie go fmt aus, um die Quellen gemäß generischen Formatierungsregeln zu formatieren
  7. Führen Sie golangci-lint aus, um die Quellen anhand verschiedener Linting-Regeln zu überprüfen
  8. Unit-Tests ausführen
  9. Funktionsquellen mit der Fn-CLI in einem Funktionscontainerimage erstellen (in lokaler Image-Registry speichern)
  10. Taggen Sie das Funktionscontainerimage mit dem Namen go-function-container-image. Dies ist der Name, der in der nächsten Phase zum Suchen des zu veröffentlichenden Images verwendet wird

Diese Schritte entsprechen weitgehend dem verwalteten Build, der im vorherigen Artikel für die Go-Anwendung definiert wurde und schließlich in eine binäre ausführbare Datei umgewandelt wurde, die auf einer VM bereitgestellt wurde. Die Schritte 9 und 10 unterscheiden sich – diese verwandeln die Go-Anwendung in ein Function Container Image, das das Endprodukt des Builds ist.

Zweite Stufe – Artefakt veröffentlichen

Klicken Sie auf der Überblickseite für die Build-Pipeline auf das Pluszeichen unten in der aktuellen verwalteten Build-Phase. Klicken Sie im Kontextmenü auf Stufe hinzufügen. Der Staging Wizard wird angezeigt.

Klicken Sie auf Artefakte übermitteln. Klicken Sie anschließend auf "Weiter".

Geben Sie den Namen für diese Phase ein: publish-greeter-function-container-image. Wir müssen das Artefakt im Projekt DevOps auswählen, das wir veröffentlichen möchten. Dieses Artefakt ist das Containerimage go-on-oci/greeter:${imageVersion}. Klicken Sie auf Artefakt(e) auswählen, und wählen Sie das Containerimage aus.

Im Bereich Artefakte mit Build-Ergebnis verknüpfen müssen wir für jedes der ausgewählten Artefakte angeben, welches der Ergebnisse einer verwalteten Build-Phase die Quelle für die Veröffentlichung des Artefakts ist. Die Build-Spezifikation definiert eine Ausgabe mit der Bezeichnung go-function-container-image. Diese Ausgabe bezieht sich auf das Containerimage, das vom Funktionserstellungsprozess auf dem Build-Server erstellt wird. Geben Sie das Label go-function-container-image in das Feld "Artefaktname für Build-Konfiguration/Ergebnis" ein. Klicken Sie auf die Schaltfläche "Hinzufügen", um die Phase "Artefakte übermitteln" zu erstellen.

Dritte Phase – Deployment-Pipeline auslösen

Klicken Sie auf der Überblickseite für die Build-Pipeline auf das Plussymbol unten in der Phase "Artefakte übermitteln". Klicken Sie im Kontextmenü auf Stufe hinzufügen. Der Staging Wizard wird angezeigt.

Klicken Sie auf "Deployment auslösen". Klicken Sie dann auf "Weiter".

Geben Sie einen Namen für die Phase ein: trigger-deployment-of-greeter-function-to-go-on-oci-app und optional eine Beschreibung. Klicken Sie auf die Schaltfläche "Deployment-Pipeline auswählen". Wählen Sie die Pipeline Deploy-greeter-function-to-go-on-oci-app aus. Details der Pipeline werden angezeigt, einschließlich Parametern (imageVersion) und des vom Deployment verwendeten Artefakts.

Klicken Sie auf "Hinzufügen", um die Phasendefinition abzuschließen und der Build-Pipeline hinzuzufügen.

Damit ist die Build-Pipeline abgeschlossen: Sie ruft Quellen ab, verarbeitet sie in einem bereitstellbaren Artefakt, veröffentlicht das Artefakt im Image-Repository und löst die Deployment-Pipeline aus, um es von dort zu übernehmen.

Way-to-Go-on-oci-Artikel

Build-Pipeline ausführen und Deployment-Pipeline auslösen

Klicken Sie auf Manuelle Ausführung starten. Definieren Sie einen Wert für den Parameter imageVersion, z.B. 0.2.0. Klicken Sie auf die Schaltfläche, um die Build-Pipeline zu starten.

Es dauert jetzt einige Minuten, die Build-Pipeline abzuschließen und das anschließende Deployment des neu erstellten Funktionsimages auszulösen.

Way-to-Go-on-oci-Artikel

Wenn alles erledigt ist und der Erfolg gemeldet wird, können Sie die Route im API-Gateway aufrufen, die zur Greeter-Funktion führt, um zu prüfen, ob die Antwort tatsächlich die erwartete neue ist.




curl -X "GET" -H "Content-Type: application/json" -d '{"name":"Mickey Mouse"}' https:///my-api/greeting
{"message":"Extremely hot greetings from your automatically built and deployed function dear  Mickey Mouse"}

	  

Dies ist ein Moment für ein kleines Fest. Wir haben einen automatisierten End-to-End-Prozess erreicht, der Go-Anwendungsquellen aus dem Code-Repository übernimmt und nach dem Linting, der Überprüfung, dem Testen und dem Erstellen eine live ausgeführte neue Version einer serverlosen OCI-Funktion bereitstellt. Um weitere Updates für die Funktion zu erstellen, müssen Sie nur die Änderung festschreiben und die Build-Pipeline auslösen. Im nächsten Schritt können wir sogar die Build-Pipeline automatisch auslösen, wenn ein (spezifischer) Commit im Code-Repository stattfindet.

Im zweiten Teil dieses Artikels werden wir die Verwendung von OCI-Services von Go-Anwendungen im Allgemeinen und insbesondere von Go-Funktionen besprechen. Die Verwendung des Go SDK für OCI wird eingeführt und Interaktionen mit dem OCI Object Storage Service demonstriert.

Go SDK for OCI – Interaktion mit OCI Object Storage von Go-Anwendungen

Oracle Cloud Infrastructure ist die Plattform, mit der in Go entwickelte Anwendungen erstellt und ausgeführt werden können. Entweder in Compute-Instanzen oder als serverlose Funktionen und auch in einem verwalteten Kubernetes-Cluster containerisiert (wie wir in Teil fünf dieser Serie diskutieren werden). Es ist gut zu erkennen, dass OCI viel mehr für Go-Entwicklungsteams ist als eine Laufzeitplattform. OCI bietet viele Services, die von Anwendungen genutzt werden können. Dienste zum Speichern von Dateien, relationalen oder "NoSQL"-Daten zur Verarbeitung der Veröffentlichung und Nutzung von Nachrichten. Services für die Übertragung und Analyse von Daten. Und Services, die Vorgänge an Anwendungen wie Monitoring unterstützen.

Die Interaktion mit OCI-Services kann über die REST-APIs erfolgen, die für jeden Aspekt aller Services verfügbar sind. Der Aufruf von REST-Services mit JSON-Payloads über HTTP ist von einer Go-Anwendung aus einfach genug. Es gibt einen komplizierten Faktor: Diese API-Aufrufe müssen signiert werden. Die Oracle Cloud Infrastructure-Signatur verwendet das Authentifizierungsschema "Signatur" (mit einem Autorisierungsheader), und der Signaturprozess ist nicht gerade trivial. Details zu diesem Signaturprozess finden Sie unter OCI-Dokumentation - OCI-REST-API-Anforderungssignaturen.

Glücklicherweise können wir für die Entwicklung von Go-Anwendungen, die OCI-Services aufrufen, das Go-SDK für OCI nutzen. Dieses Open-Source-Entwicklungskit erleichtert das Signieren der OCI-API-Anforderungen. Mit dem SDK werden die Aufrufe an OCI-Services als lokale Aufrufe mit stark typisierten vordefinierten Strukturen statt mit JSON-Anforderungs- und -Antwortbodys ausgeführt. Das Go-SDK für OCI verwendet dieselbe Konfigurationsdatei, die wir zuvor für die Fn-CLI und die OCI-CLI verwendet haben. Der Standardspeicherort für diese Datei ist $HOME/.oci. Diese Datei leitet das Go-SDK an einen bestimmten Mandanten und eine bestimmte Region für einen Benutzeraccount weiter. Dabei wird die Hälfte des für den Benutzer eingerichteten Private-Key-Paars verwendet. Go-Anwendungen, die das Go-SDK für OCI verwenden, können einfach auf dieser Konfiguration aufbauen, ohne sich mit den Details befassen zu müssen.

Die Dokumentation für das Go SDK für OCI finden Sie in der OCI-Dokumentation - SDK für Go.

In diesem Abschnitt wird eine einfache Go-Anwendung entwickelt, die den OCI Object Storage Service zum Erstellen und Lesen von Dateien verwendet. Zunächst handelt es sich um eine Standalone-Anwendung, die überall kompiliert und ausgeführt werden kann (solange die OCI-Konfigurationsdatei verfügbar ist). Anschließend wird die Go-Anwendung in OCI auf einer VM oder als Funktion ausgeführt. Dieser besondere Schwerpunkt ist relevant, da das Go-SDK bei Verwendung in OCI die Identität und Berechtigungen der Komponente nutzen kann, auf der die Anwendung ausgeführt wird. Das bedeutet, dass Code, der auf OCI ausgeführt wird, keine eigene OCI-Konfigurationsdatei verwenden muss und daher noch einfacher ist.

Beliebige Go-Anwendung im Gespräch mit OCI Object Storage Service

Zuerst erstellen wir eine sehr einfache Go-Anwendung, die über das SDK eine Verbindung zu OCI herstellen kann. Als Nächstes verwenden wir diese Grundlage, um die Interaktion mit Object Storage Service hinzuzufügen.

Der einfachste Go-Client von OCI

Die einfachste Go-Anwendung, die mit OCI über das Go SDK für OCI spricht, wird wie folgt erstellt:

  1. Erstellen Sie ein Verzeichnis für die Anwendung
  2. go.mod-Datei mit Abhängigkeiten vom Go SDK für OCI-Package erstellen
  3. Führen Sie go mod tidy aus, um die Datei go.sum zu erstellen und die erforderlichen Packages herunterzuladen
  4. Datei OCI-client.go mit dem Code für die Kommunikation mit OCI erstellen

Es wird davon ausgegangen, dass sich die OCI-Konfigurationsdatei, die zuvor in diesem Artikel besprochen wurde, im Standardverzeichnis mit dem Standardnamen $HOME/.oci/config befindet. Wenn sich die Datei an einem anderen Speicherort befindet, können Sie die Funktion ConfigurationProviderFromFile im Package github.com/oracle/oci-go-sdk/v65/common verwenden, das den benutzerdefinierten Speicherort der Konfigurationsdatei akzeptiert.

Die Datei go.mod enthält den folgenden Inhalt:






module oci-client

go 1.16

require github.com/oracle/oci-go-sdk/v65 v65.2.0



	  

Das github.com/oracle/oci-go-sdk/v65-Bit referenziert die neueste (zum Zeitpunkt des Schreibens) Version des Go-SDK. Die Quellen werden basierend auf dieser Referenz heruntergeladen. In der Go-Anwendung bedeutet dies, dass wir die Packages im SDK nutzen können, um auf verschiedene Services im OCI-Portfolio zuzugreifen.

Die Datei oci-client.go enthält diesen Code, der die allgemeinen und Identitätspackages verwendet:




package main

import (
"context"
"fmt"

"github.com/oracle/oci-go-sdk/v65/common"
"github.com/oracle/oci-go-sdk/v65/identity"
)

func main() {    
c, _ := identity.NewIdentityClientWithConfigurationProvider(common.DefaultConfigProvider())
tenancyID, _ := common.DefaultConfigProvider().TenancyOCID()
request := identity.GetCompartmentRequest{
    CompartmentId: &tenancyID,
}
response, _ := c.GetCompartment(context.Background(), request)
fmt.Printf("Name of Root Compartment: %v", *response.Compartment.Name)
}


	  

Die erste Zeile in der Funktion ruft den Client ab, der anschließend für die meisten Interaktionen mit OCI verwendet werden kann, z.B. den Aufruf GetCompartment, der die Details für das Root Compartment zurückgibt.

Die Anwendung kann mit go run oci-client.go ausgeführt werden und erzeugt eine sehr einfache Ausgabe:




Name of Root Compartment: 

	  

Obwohl das Ergebnis nicht besonders bemerkenswert ist, ist die Tatsache, dass es eine Ausgabe gibt, ein Beweis dafür, dass das Go SDK für OCI funktioniert und bereit ist, für höherwertige Aktivitäten genutzt zu werden.

Beachten Sie, dass der Code die Fehler, die auftreten können, vollständig ignoriert. Wenn Fehler auftreten, ersetzen Sie die Unterstriche durch eine Variable, um diesen Fehler zu erfassen und zu behandeln.

Go Application - Objekte in und aus OCI Object Storage Service erstellen und abrufen

OCI Object Storage Service bietet kostengünstigen und persistenten Speicher für verschiedene Datentypen. Große und kleine Objekte können auf diesem Service programmgesteuert gespeichert werden, um kurz oder lang aufbewahrt zu werden und programmgesteuert oder über direkte URLs aufgerufen zu werden. Object Storage bietet Versionierung, verschiedene Aufbewahrungsregeln, Verschlüsselung gespeicherter Daten, Objektlebenszyklusmanagement, automatisiertes zeitbasiertes Entfernen und verschiedene Speicherebenen.

Objekte in Object Storage Service sind in Buckets organisiert. Ein Bucket ist ein logischer Container von Objekten, die sich in einem bestimmten Compartment befinden. Einige Vorgänge können für alle Objekte in einem Bucket ausgeführt werden.

Das Go SDK für OCI bietet Funktionen und Typen, mit denen Sie einfach und unkompliziert mit den Object Storage Service from Go-Anwendungen arbeiten können. Vorgänge zum Bearbeiten von Buckets und zum Erstellen, Löschen und Abrufen von Objekten sind problemlos verfügbar.

Einzelheiten zum Object Storage-Package im Go-SDK für OCI finden Sie in dieser Referenz: Dokumentation für Object Storage-Package im Go-SDK für OCI.

Das Quell-Repository für diesen Artikel enthält die Ordneranwendungen/store-n-retrieve, die eine einfache Go-Anwendung enthält, die eine Verbindung zu Ihrem OCI-Mandanten herstellt und einen Bucket erstellt, gefolgt von der Erstellung eines Objekts und dem Abruf desselben Objekts. Die Anwendung verwendet denselben DefaultConfigProvider, der im vorherigen Abschnitt verwendet wurde, um Anforderungen mit der Datei $HOME/.oci/config bei OCI-APIs zu signieren.

Die Abhängigkeit dieser Anwendung vom Go SDK für OCI ist in go.mod definiert.

Way-to-Go-on-oci-Artikel

Der erste Teil des Codes erstellt eine ObjectStorageClient-Instanz. Viele Funktionen werden auf der zugrunde liegenden Schnittstelle definiert – alle unterstützen eine Art der Interaktion mit Object Storage Service. Die ObjectStorageClient wird mit common.DefaultConfigProvider() (wie zuvor) erstellt. Dabei wird die OCI-Standardkonfigurationsdatei mit einer Referenz auf eine Datei verwendet, die den Private Key enthält.

Die Funktion getNamespace wird aufgerufen, um den Namespace für den aktuellen Mandanten abzurufen. Dann wird ensureBucketExists mit dem Namen des Buckets aufgerufen, um den Bucket zu erstellen, falls er noch nicht vorhanden ist.






package main

import (
"bytes"
"context"
"fmt"
"io"
"io/ioutil"

"github.com/oracle/oci-go-sdk/v65/common"
"github.com/oracle/oci-go-sdk/v65/objectstorage"
)

const (
bucketName      = "go-bucket" // feel free to use a different name for the bucket
compartmentOCID = "" // replace with the OCID of the go-on-oci compartment in your tenancy
objectName      = "welcome.txt" // feel free to use a different name for the object
)

func main() {
objectStorageClient, cerr := objectstorage.NewObjectStorageClientWithConfigurationProvider(common.DefaultConfigProvider())
if cerr != nil {
fmt.Printf("failed to create ObjectStorageClient : %s", cerr)
}
ctx := context.Background()
namespace, cerr := getNamespace(ctx, objectStorageClient)
if cerr != nil {
fmt.Printf("failed to get namespace : %s", cerr)
} else {
fmt.Printf("Namespace : %s", namespace)
}

err := ensureBucketExists(ctx, objectStorageClient, namespace, bucketName, compartmentOCID)
if err != nil {
fmt.Printf("failed to read or create bucket : %s", err)
}
.........................



	  

Die Funktionen, die in diesem Snippet aufgerufen werden, um den Namespace abzurufen und das Vorhandensein des Buckets zu prüfen (und ihn zu erstellen, wenn er nicht vorhanden ist), sind recht einfach. Sie sind wie folgt definiert:





func getNamespace(ctx context.Context, client objectstorage.ObjectStorageClient) (string, error) {
request := objectstorage.GetNamespaceRequest{}
response, err := client.GetNamespace(ctx, request)
if err != nil {
    return *response.Value, fmt.Errorf("failed to retrieve tenancy namespace : %w", err)
}
return *response.Value, nil
}

func ensureBucketExists(ctx context.Context, client objectstorage.ObjectStorageClient, namespace string, name string, compartmentOCID string) error {
req := objectstorage.GetBucketRequest{
    NamespaceName: &namespace,
    BucketName:    &name,
}
// verify if bucket exists.
response, err := client.GetBucket(ctx, req)
if err != nil {
    if response.RawResponse.StatusCode == 404 {
        err = createBucket(ctx, client, namespace, name, compartmentOCID)
        return err
    }
    return err
}
fmt.Printf("bucket %s already exists", bucketName)
return nil
}

// bucketname needs to be unique within compartment. there is no concept of "child" buckets. using "/" separator characters in the name, the suggestion of nested bucket can be created
func createBucket(ctx context.Context, client objectstorage.ObjectStorageClient, namespace string, name string, compartmentOCID string) error {
request := objectstorage.CreateBucketRequest{
    NamespaceName: &namespace,
}
request.CompartmentId = &compartmentOCID
request.Name = &name
request.Metadata = make(map[string]string)
request.PublicAccessType = objectstorage.CreateBucketDetailsPublicAccessTypeNopublicaccess
_, err := client.CreateBucket(ctx, request)
if err != nil {
    return fmt.Errorf("failed to create bucket on OCI : %w", err)
} else {
    fmt.Printf("created bucket : %s", bucketName)
}
return nil
}




	  

Der zweite Teil dieser Anwendung erstellt ein Objekt im Bucket und ruft anschließend dieses Objekt ab. Wenn die Anwendung ausgeführt wird, hat dies eine persistente Auswirkung: Der Bucket wurde erstellt (sofern noch nicht vorhanden) und ein Objekt wurde erstellt oder aktualisiert (sofern die Anwendung zuvor ausgeführt wurde). Sie können auf der Detailseite für den Bucket-Go-Bucket in der OCI-Konsole einchecken, um sowohl den Bucket als auch das erstellte Objekt anzuzeigen.





..................

contentToWrite := []byte("We would like to welcome you in our humble dwellings. /n We consider it a great honor. Bla, bla.")
objectLength := int64(len(contentToWrite))
err = putObject(ctx, objectStorageClient, namespace, bucketName, objectName, objectLength, ioutil.NopCloser(bytes.NewReader(contentToWrite)))
if err != nil {
    fmt.Printf("failed to write object to OCI Object storage : %s", err)
}

var contentRead []byte
contentRead, err = getObject(ctx, objectStorageClient, namespace, bucketName, objectName)
if err != nil {
    fmt.Printf("failed to get object %s from OCI Object storage : %s", objectName, err)
}
fmt.Printf("Object read from OCI Object Storage contains this content: %s", contentRead)
}

func putObject(ctx context.Context, client objectstorage.ObjectStorageClient, namespace string, bucketName string, objectname string, contentLen int64, content io.ReadCloser) error {
request := objectstorage.PutObjectRequest{
    NamespaceName: &namespace,
    BucketName:    &bucketName,
    ObjectName:    &objectname,
    ContentLength: &contentLen,
    PutObjectBody: content,
}
_, err := client.PutObject(ctx, request)
fmt.Printf("Put object %s in bucket %s", objectname, bucketName)
if err != nil {
    return fmt.Errorf("failed to put object on OCI : %w", err)
}
return nil
}

func getObject(ctx context.Context, client objectstorage.ObjectStorageClient, namespace string, bucketName string, objectname string) (content []byte, err error) {
request := objectstorage.GetObjectRequest{
    NamespaceName: &namespace,
    BucketName:    &bucketName,
    ObjectName:    &objectname,
}
response, err := client.GetObject(ctx, request)
if err != nil {
    return nil, fmt.Errorf("failed to retrieve object : %w", err)
}
buf := new(bytes.Buffer)
_, err = buf.ReadFrom(response.Content)
if err != nil {
    return nil, fmt.Errorf("failed to read content from object on OCI : %w", err)
}
return buf.Bytes(), nil
}


	  

Wenn Sie diese Anwendung ausführen, führen Sie object-organizer.go aus. Das Ergebnis ist die folgende Ausgabe:



Namespace : idtwlqf2hanz
created bucket : go-bucket
Put object welcome.txt in bucket go-bucket
Object read from OCI Object Storage contains this content: We would like to welcome you in our humble dwellings. /n We consider it a great honor. Bla, bla.


	  

Go-basierte OCI Functions im Gespräch mit OCI Object Storage Service

Im vorherigen Abschnitt wurde jede Go-Anwendung erläutert, die über das SDK mit OCI-Services interagiert. Was gibt es mehr über OCI Functions in Go zu sagen? Lesen Sie weiter, denn es stellt sich heraus, dass wir das Leben einfacher gestalten können, wenn der Go-Code, den Sie entwickeln und von dem aus Sie mit dem Go-SDK arbeiten möchten, als OCI-Funktion bereitgestellt oder auf einer Compute-Instanz in OCI ausgeführt wird. In diesen Fällen können wir die Resource-Principal-Authentifizierung (für Funktionen) und die Instanz-Principal-Authentifizierung (für Code, der auf einer Compute-Instanz ausgeführt wird) nutzen. Das bedeutet, dass wir die OCI-Konfigurationsdatei nicht einschließen müssen. Unser Code, der mit dem SDK kommuniziert, kann etwas einfacher sein.

Weitere Informationen zur Resource Principal-Authentifizierung finden Sie in der OCI-Dokumentation zur Resource Principal-Authentifizierung für Funktionen.

In diesem Abschnitt wird eine OCI-Funktion namens Object-Broker beschrieben. Die Quellen finden Sie im Quell-Repository für diesen Artikel, in Pfadfunktionen/Objekt-Broker. Wie zuvor wird die Funktion mit einer func.yaml-Datei mit Metadaten und einer func.go-Datei mit dem Link zwischen der Funktion und dem Fn-Framework definiert. Die Datei go.mod definiert die Abhängigkeiten für die Funktion. Die Logik für die Interaktion mit OCI Object Storage Service befindet sich in der Datei object-organizer.go. Damit wird eine öffentliche Funktion CreateObject definiert, die von func.go aufgerufen wird.

CreateObject erstellt ein Objekt mit dem angegebenen Namen im angegebenen Bucket. Die ersten Zeilen in der Funktion CreateObject in object-organizer.go wurden geändert, um mit dieser Resource-Principal-Authentifizierung zu arbeiten. Die jetzt verwendete auth.ResourcePrincipalConfigurationProvider()-Datei erfordert nicht, dass eine OCI-Konfigurationsdatei und ein Private Key in die Anwendung aufgenommen werden. Es wird davon ausgegangen, dass der Code in OCI und insbesondere in einer Ressource (bei der es sich um eine Funktion oder einen DevOps-Build-Server handeln kann) ausgeführt wird, die als Resource Principal bezeichnet wird, da er in einer dynamischen Gruppe enthalten ist und Berechtigungen von dieser Gruppenmitgliedschaft erbt. Zu lange kommen Sie zu den Anweisungen, um die dafür erforderlichen Maßnahmen zu ergreifen.





func CreateObject(objectName string, bucketName string, compartmentOCID string) (string, err) {
configurationProvider, err := auth.ResourcePrincipalConfigurationProvider()
if err != nil {
    fmt.Printf("failed to get oci configurationprovider based on resource principal authentication : %s", err)
}
objectStorageClient, cerr := objectstorage.NewObjectStorageClientWithConfigurationProvider(configurationProvider)


	  

Lassen Sie uns unsere Aufmerksamkeit auf func.go richten. Funktion (tion) myHandler verarbeitet den Funktionstrigger. Er ruft CreateObject auf, jedoch nicht, bevor er den Namen des Objekts bestimmt hat, das die Anforderung erzeugen soll, und den Bucket-Namen für den Bucket, der das Objekt enthalten soll. Diese Namen haben einen Standardwert, aber die Funktion versucht, Abfrageparameterwerte für HTTP-Anforderungen zu suchen, die bestimmte Werte angeben. Beachten Sie, dass dies nur für einen HTTP-Trigger für die Funktion und nicht für einen Aufruf mit fn-Aufruf funktioniert.





func myHandler(ctx context.Context, in io.Reader, out io.Writer) {
objectName := "defaultObjectName.txt"
bucketName := "the-bucket"
fnctx := fdk.GetContext(ctx)             // fnctx contains relevant elements about the Function itself
fnhttpctx, ok := fnctx.(fdk.HTTPContext) // fnhttpctx contains relevant elements about the HTTP Request that triggered it
if ok {                                  // an HTTPContent was found which means that this was an HTTP request (not an fn invoke) that triggered the function
    u, err := url.Parse(fnhttpctx.RequestURL())
......


	  

Sie können Details zum Fn-Kontext in Dokumentation für Project Fn Go FDK prüfen, insbesondere im Beispiel.

Die Funktion muss wissen, in welchem OCI-Compartment sich der Bucket befinden soll. Solche Laufzeiteinstellungen werden in der Regel über eine Konfiguration für die Funktion der Anwendung definiert. Die Werte von Konfigurationen können in der Funktion aus einer Karte im Kontext gelesen werden, wie in dieser Zeile dargestellt:





	if compartmentOCID, ok := fnctx.Config()["compartmentOCID"]; ok {

	  

Funktion mit Fn-CLI erstellen und bereitstellen

Angenommen, Sie befinden sich in einem Terminal in der lokalen Entwicklungsumgebung, in dem die Fn-CLI installiert ist und das aktuelle Verzeichnis Functions/object-broker ist, können Sie einen lokalen Build der Funktion mit ausführlicher Ausgabe ausführen:




fn -v build

	  

Wenn der Build gut aussieht, ist der nächste Schritt das Deployment der Funktion. Wenn der Fn-Kontext so eingestellt ist, dass er den Go-on-OCI-Kontext verwendet (mit fn-Listenkontexten prüfen), stellt dies die Funktion für die Go-on-OCI-App-Anwendung auf OCI bereit:




fn -v deploy --app go-on-oci-app

	  

Die Funktion kann nur dann einen aussagekräftigen Job ausführen, wenn die Konfiguration für den Compartment-OCID-Wert definiert wurde. Sie können dies über die Konsole oder mit dieser nächsten Anweisung mit der Fn-CLI tun:




fn cf f go-on-oci-app object-broker compartmentOCID 


	  

Jetzt ist die Funktion bereitgestellt und hat ihre Konfiguration. Sie können erwarten, dass ein Aufruf der Funktion mit diesem Befehl erfolgreich ist:




fn invoke go-on-oci-app object-broker

	  

Ein letzter Aspekt ist jedoch zu behandeln: Die Funktion verwendet OCI Object Storage Service-APIs, um Buckets und Objekte zu bearbeiten. Dafür müssen jedoch explizit Berechtigungen erteilt worden sein! Dazu verwenden wir eine dynamische Gruppe und zwei Richtlinien.

Berechtigungen für Funktionen zum Bearbeiten von Objekten und Buckets

Genau wie wir dynamische Gruppen verwendet haben, um einen Berechtigten zu erstellen, der die Deployment-Pipelines und die Build-Pipelines darstellt, müssen wir auch eine dynamische Gruppe erstellen, die die Funktionen enthält, denen wir Berechtigungen erteilen möchten. Um die dynamische Gruppe zu erstellen, geben Sie in der Suchleiste dyn ein. Klicken Sie im Suchergebnisbereich auf den Link Dynamische Gruppen.

Klicken Sie auf der Übersichtsseite für dynamische Gruppen auf Dynamische Gruppe erstellen.

Geben Sie den Namen für die dynamische Gruppe für die Deployment-Pipeline(n) ein, z.B. Functions-in-go-on-oci, und geben Sie optional eine Beschreibung ein. Definieren Sie die folgende Regel, die alle Funktionen auswählt, die Teil des Compartments sind:




All {resource.type = 'fnfunc', resource.compartment.id = ''}

	  

Ersetzen Sie natürlich durch die ID des Compartments, in dem Sie arbeiten. Drücken Sie dann "Erstellen".

So erstellen Sie eine Policy in der Konsole: Geben Sie in der Suchleiste "poli" ein, und klicken Sie im Suchergebnis-Popup im Bereich "Services" auf "Policys" > "Identität". Dadurch gelangen Sie zur Überblickseite "Policys" für das aktuelle Compartment.

Die erste Policy-Anweisung definiert die Berechtigung für die Funktion zum Verwalten von Objekten im Compartment. Die zweite Anweisung fügt die Berechtigung zur Verwaltung von Buckets hinzu. Definieren Sie einen Namen, eine Beschreibung und die folgenden Anweisungen:




allow dynamic-group functions-in-go-on-oci to manage objects in compartment go-on-oci
allow dynamic-group functions-in-go-on-oci to manage buckets in compartment go-on-oci

	  

Die folgende Abbildung zeigt die Berechtigungen, die jetzt für die Funktion gelten, wenn die Policy mit diesen Anweisungen gespeichert wird:

Way-to-Go-on-oci-Artikel

Jetzt kann die Funktion aufgerufen werden und sollte ihre Aufgabe mit den Standardnamen für Bucket und Objekt ausführen können.

fn invoke go-on-oci-app object-broker

Stellen Sie sicher, dass der Bucket erstellt wurde und das neu erstellte Objekt mit der Konsole von der OCI-URL zur Seite "Buckets" enthält.

Route in API-Gateway zur Triggerfunktion hinzufügen

Um die Object-Broker-Funktion von überall aus über HTTP aufrufen zu können, nutzen wir erneut das API Gateway. Geben Sie gat in die Suchleiste in der Konsole ein. Klicken Sie auf >Gateways > API Management. Klicken Sie auf den Link für das API-Gateway. Klicken Sie auf "Deployments". Klicken Sie in der Liste mit Deployments – die eine einzelne Bereitstellung enthält – auf den Link myserver-api.

Klicken Sie auf "Bearbeiten", um die Deployment-Spezifikation zu öffnen. Klicken Sie auf den Link für den zweiten Schritt: Routen. Scrollen Sie nach unten und klicken Sie auf + Weitere Route.

Geben Sie /object-broker als Pfad für diese neue Route ein. Wählen Sie GET als Methode und Oracle Functions als Typ (Backend). Wählen Sie die Anwendung go-on-oci-app aus, und setzen Sie dann den Funktionsnamen auf object-broker. Drücken Sie auf "Weiter", und klicken Sie dann auf "Änderungen speichern", um die Änderungen anzuwenden und die neue Route zu realisieren.

Das End-to-End-Bild, das jetzt vom HTTP-Consumer über API Gateway zu Function und schließlich zu Bucket und Objekt konfiguriert ist, sieht folgendermaßen aus:

Way-to-Go-on-oci-Artikel

Rufen Sie die Funktion über den Browser auf, oder verwenden Sie curl in der Befehlszeile mit:




curl -X "GET" "http:///my-api/object-broker?objectName=new-exciting-object.txt&bucketName=the-ultimate-collection"


	  

Automatisiertes Build und Deployment

Dieser Funktionsobjekt-Broker wurde manuell über die Befehlszeile mit der Fn-CLI bereitgestellt. Das hat natürlich gut funktioniert. Wenn Sie jedoch jetzt mit der Entwicklung dieser Funktion beginnen und mehrere Entwicklungszyklen durchlaufen würden, sollten Sie wahrscheinlich die Automatisierung im Build-and-Deploy-Prozess einführen.

Genau wie zuvor können Sie die erforderlichen Elemente in OCI DevOps einfach einrichten, um automatisierte Pipelines für das Deployment (des Funktionscontainerimages in der Containerimage-Registry) und den Build (ab dem Code-Repository) zu erreichen, was zu einem frisch gebackenen Containerimage für die Funktion führt). Das Hauptelement, das für die Funktion spezifisch ist, ist die Build-Spezifikationsdatei für die verwaltete Build-Phase in der Build-Pipeline. Diese Datei wird als go-function-build-spec.yaml im selben Verzeichnis wie func.go und func.yaml bereitgestellt.

Nachdem Sie ein DevOps-Artefakt für das Funktionscontainerimage, eine Umgebung für die Funktion und die beiden Pipelines für Build und Deployment erstellt haben, sieht das automatisierte Setup des DevOps-Prozesses folgendermaßen aus:

Way-to-Go-on-oci-Artikel

Fazit

Ein Schwerpunkt in diesem Artikel waren serverlose Funktionen, die in Go geschrieben und auf Oracle Cloud Infrastructure ausgeführt wurden. Die automatisierte Erstellung und Bereitstellung dieser Funktionen wurde ebenso erörtert wie die Verwendung von API Gateway, um externen HTTP-Consumern Zugriff auf die Funktion zu ermöglichen.

Das zweite Hauptthema war das Go-SDK für OCI für die Interaktion mit OCI-Services von Go-Anwendungen. Der Artikel zeigte, wie Sie mit Go-Code auf den Object Storage-Service zugreifen, um Dateien zu speichern und abzurufen.

Die beiden Themen wurden im Funktionsobjekt-Broker zusammengefasst. Diese OCI-Funktion nutzt die Resource Principal-Authentifizierung und Berechtigungen, die über eine dynamische Gruppe erteilt wurden. Durch eine Laufzeitkonfiguration lernt die Funktion die aktuellen umgebungsspezifischen Einstellungen kennen.

Im nächsten Artikel wird die Interaktion mit Oracle Database das Hauptthema sein. Erstellen Sie eine Verbindung von einer Go-Anwendung zu einer lokalen Oracle Database sowie zu einer auf OCI ausgeführten Autonomous Database, und führen Sie SQL-Vorgänge mit diesen Datenbanken bequem von Ihrer Go-Anwendung aus durch. Weitere Themen sind das Arbeiten mit einem Oracle Wallet für die ordnungsgemäße Verwaltung von Datenbankzugangsdaten, einschließlich des Wallets im Deployment-Prozess, und das Kombinieren von Interaktionen mit OCI Object Storage und den Autonomous Database-Services in einer einzigen Anwendung.