Il s'agit du troisième volet d'une série de cinq parties sur Go et Oracle Cloud Infrastructure. Cette série explique comment créer et exécuter des applications Go sur Oracle Cloud Infrastructure (OCI) dans des instances de calcul (VM), en conteneur sur Kubernetes ou en tant que fonctions sans serveur. Les articles montrent comment automatiser la création et le déploiement de ces applications Go à l'aide d'OCI DevOps. Un sujet important est la façon d'utiliser les services OCI à partir des applications Go, à la fois celles exécutées sur OCI et le code Go exécuté ailleurs. Certains des services OCI abordés sont Object Storage, Streaming, Key Vault et Autonomous Database.
Afin de suivre ces articles, les lecteurs doivent avoir au moins des connaissances de base sur la façon de créer des applications Go. On suppose que les lecteurs ont accès à leur propre environnement de développement Go. Certains exemples et captures d'écran mentionneront spécifiquement VS Code comme outil de développement. Cependant, d'autres éditeurs et IDE peuvent également être utilisés. Le code Go présenté dans ces articles démontre un certain nombre de mécanismes dans leur forme la plus simple pour un maximum de clarté et avec le moins de dépendances. Les lecteurs ne doivent pas s'attendre à des fonctionnalités significatives ou à du code prêt pour la production.
Les articles décrivent comment obtenir Going sur OCI. Pour tester les exemples, les lecteurs devront avoir accès à une location OCI avec des droits d'accès permettant de créer les ressources OCI abordées dans ces articles. La plupart des ressources utilisées sont disponibles dans le niveau Toujours gratuit (instance de calcul, VCN, Autonomous Database, Object Storage, Logging, Resource Manager) ou disposent d'un niveau d'affectation gratuit pour une utilisation mensuelle limitée (Functions, API Gateway, Streaming, Vault, DevOps).
La première partie de cette série décrit le provisionnement d'une instance de calcul basée sur l'image Oracle Linux Cloud Developer, l'ouverture pour les activités réseau entrantes et sortantes, la création et l'exécution d'une application Go qui traite les demandes HTTP et la connexion de la journalisation produite par l'application à OCI Logging. La deuxième partie traite de l'ingénierie logicielle, de l'automatisation de la création et du déploiement de l'application avec le service OCI DevOps. Ce service est utilisé pour stocker le code source Go, créer l'exécutable d'application, le stocker en tant qu'artefact déployable et déployer cet artefact vers une instance Compute. Le deuxième article explique également comment exposer une adresse HTTP pour l'application via une passerelle d'API OCI.
Cette troisième partie explique comment créer des fonctions sans serveur dans Go et les déployer sur OCI. Le kit SDK Go pour OCI est introduit, d'abord pour les applications Go locales autonomes, puis pour une utilisation à partir de fonctions, en tirant parti de l'authentification par principal de ressource. Ce kit SDK est utilisé pour interagir avec le service OCI Object Storage afin de créer des buckets, ainsi que pour écrire et lire des fichiers.
Initialement, la fonction est créée et déployée manuellement. Un routage est ajouté au déploiement dans API Gateway pour appeler la fonction à partir d'un client externe à OCI. Ensuite, un pipeline de déploiement OCI DevOps est créé pour déployer la fonction à partir d'une image dans Container Image Registry. Enfin, un pipeline de build est configuré pour prendre des sources dans le référentiel de code, créer et publier une image de conteneur, puis déclencher le pipeline de déploiement pour la création et le déploiement de bout en bout.
La fonction sans serveur dans OCI est basée sur la technologie du projet open source Fn. La logique métier de la fonction est écrite dans votre langue préférée - dans ce cas Go - et intégrée dans la structure Fn qui gère le cycle de vie de la fonction et les interactions avec la fonction. La structure Fn peut être exécutée n'importe où : sur votre machine locale, sur une machine virtuelle, sur n'importe quel cloud ou sur site. Oracle Cloud Infrastructure offre un service PaaS entièrement géré OCI Functions pour les fonctions sans serveur basées sur la même technologie.
Une fonction est intégrée à une image de conteneur. Cette image est propagée vers un registre d'images de conteneur. Pour publier la fonction, cette image est transférée vers un serveur Fn. Chaque fois que la fonction est appelée, un conteneur est démarré à partir de l'image et la demande est traitée. Les conteneurs restent en cours d'exécution pendant un certain temps après la gestion d'un appel, à chaud, prêts à traiter des demandes supplémentaires. Lorsque le nombre de demandes simultanées augmente, des conteneurs supplémentaires pour la même fonction sont démarrés par le serveur Fn pour s'assurer que toutes les demandes peuvent être traitées.
L'attraction des fonctions pour les développeurs et les opérateurs d'applications est le fait qu'aucune énergie ne doit être injectée dans la conception, la création et la gestion de la plate-forme qui exécute la logique métier. Tout peut se concentrer sur l'écriture de cette logique.
Nous allons maintenant examiner la création de la fonction dans Go, la créer dans une image de conteneur, la déployer et l'exécuter localement. Ensuite, nous allons transférer cette fonction vers le service OCI Functions et la rendre exécutée côté cloud.
Pour développer des fonctions, vous aurez besoin d'un environnement qui prend en charge le projet Fn. Fn est une plate-forme légère de fonctions sans serveur basée sur Docker que vous pouvez exécuter sur votre ordinateur portable, votre serveur ou votre cloud. Vous pouvez installer Fn facilement sur Linux ou MacOS en suivant les instructions de la section Fn Project Tutorials – Install Fn.
Vous pouvez choisir de travailler sur l'instance de calcul go-app-vm que nous avons créée dans la première et utilisée également dans le deuxième versement de cette série. Cet environnement Oracle Linux n'est pas fourni avec la configuration Fn, mais son installation est assez simple.
Vous pouvez également travailler dans OCI Cloud Shell. Cet environnement accessible par navigateur est configuré avec Fn. Pour utiliser Fn dans OCI Cloud Shell, reportez-vous à Fonctions de documentation OCI : démarrage à l'aide de Cloud Shell.
Dans l'environnement de développement dans lequel la CLI Fn est installée, accédez au répertoire dans lequel vous souhaitez créer le sous-répertoire de la fonction. Dans la ligne de commande, entrez cette commande et exécutez-la :
fn init --runtime go --trigger http greeter
Un sous-répertoire appelé greeter est créé. Accédez-y et vérifiez son contenu :
cd greeter ls -l
Le fichier func.yaml contient les métadonnées relatives à la fonction, qui doivent être interprétées par la structure Fn lors de la création, puis ultérieurement lors de l'exécution de la fonction. Le fichier go.mod contient la dépendance de la fonction au package fdk-go. La fonction elle-même se trouve dans func.go. La structure de la fonction et son interaction avec l'exécution Fn peuvent être vues ici : la fonction principale enregistre la fonction myHandler avec l'exécution Fn, qui indique et permet à l'exécution d'appeler cette fonction pour toute demande HTTP reçue. La fonction reçoit le corps de la demande dans un paramètre io.Reader. Il reçoit également la sortie de io.Writer, à laquelle le corps de réponse peut être écrit. Le paramètre context.Context ctx contient des métadonnées pour la demande d'origine, notamment les en-têtes HTTP, l'URL complète, la méthode de demande et la fonction elle-même, y compris toutes les variables de configuration définies pour cette dernière.
Actuellement, la fonction myHandler décode le corps de la demande, en s'attendant à ce qu'il contienne une charge utile JSON avec un champ appelé nom. Il crée une personne dont le nom est défini sur la valeur de ce champ ou, en son absence, sur World par défaut. Il crée ensuite la réponse attendue : un objet JSON avec un seul champ appelé message, qui contient une chaîne composée de Hello et de la valeur de nom.
Bien qu'il ne fasse rien de vraiment spectaculaire, la fonction est saine et complète et nous pouvons le déployer et l'invoquer localement. Pour cela, nous avons besoin d'un contexte local et du serveur Fn local en fonctionnement. Vérifiez les contextes à l'aide de :
fn list contexts
Cela montre une liste d'au moins un contexte – peut-être plus d'un. Pour utiliser le serveur Fn local, assurez-vous que le contexte par défaut est le contexte actif. Si nécessaire, utilisez :
fn use context default
Créez maintenant une application en tant qu'hôte pour la fonction :
fn create app go-oci-app fn list apps
Si la première de ces instructions échoue et que la connexion est refusée, le serveur n'est probablement pas encore en cours d'exécution. Utilisez la commande suivante pour démarrer le serveur, puis réessayez de créer l'application.
fn start
Une fois l'application créée, la fonction peut désormais y être déployée. La commande suivante s'occupe de ce déploiement ; elle est précédée du processus de création d'image de conteneur.
fn --verbose deploy --app go-oci-app --local
La spécification de --local fait le déploiement sur le serveur local mais ne pousse pas l'image de la fonction vers un registre Docker, ce qui serait nécessaire si nous déployions sur un serveur Fn distant.
Parce qu'il libère une quantité impressionnante de messages de journal à produire, l'indicateur --verbose n'est pas celui que vous utiliserez tout le temps. Cependant, cela vous donne un assez bon aperçu de ce qui se passe. Plusieurs images de conteneur sont extraites, puis un processus de construction de conteneur en deux étapes est exécuté pour créer une image de conteneur pour la fonction greeter. Les images de projet Fn prédéfinies sont utilisées pour l'étape de construction (fnproject/go :1.15-dev au moment de l'écriture) et comme base de l'image d'exécution (fnproject/go :1.15).
La sortie finale se présente comme suit :
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
L'image de la fonction est appelée greeter :0.0.2. Vous trouverez cette image dans le registre de conteneurs local avec :
docker images | grep greeter
La fonction peut être appelée via la CLI Fn, à l'aide de son nom et de son application, comme suit :
fn invoke go-oci-app greeter
La fonction attend une charge utile JSON contenant un champ de nom, nous allons donc lui fournir exactement ce qui suit :
echo -n '{"name":"Clark Kent"}' | fn invoke go-oci-app greeter --content-type application/json
La sortie du déploiement de la fonction nous a également donné l'adresse de déclenchement de la fonction. Il s'agit d'une adresse HTTP à laquelle nous pouvons envoyer une demande HTTP et pour laquelle elle déclenche la fonction. Nous n'avons aucune interaction (visible) avec Fn, bien que l'adresse que nous appelons soit en réalité l'adresse du serveur Fn. Le chemin d'URL indique à Fn l'application et la fonction spécifique à déclencher.
curl -X "POST" -H "Content-Type: application/json" -d '{"name":"Mickey Mouse"}' http://localhost:8080/t/go-oci-app/greeter
Maintenant, créons cette fonction sur OCI plutôt que simplement dans notre environnement de développement. Les étapes sont très similaires à celles que nous avons utilisées pour créer la fonction localement ; nous avons seulement besoin d'utiliser un contexte différent. Pas le contexte local, mais un contexte pour OCI.
Créer une application
Créons d'abord l'application via la console OCI. Tapez app dans la zone de recherche et cliquez sur Applications Functions dans la zone Services.
Cliquez sur le bouton Créer une application. Saisissez le nom de l'application : go-on-oci-app. Sélectionnez le VCN qui a été créé dans la première partie de la série d'articles et son seul sous-réseau public. Cliquez ensuite sur Créer pour créer l'application.
Préparation de l'environnement local pour l'interaction OCI et la transmission d'images de fonction
Une fois l'application créée, les informations générales de l'application sont présentées. La page contient également des instructions pour la création de votre première fonction, soit dans OCI Cloud Shell, soit dans une configuration locale (qui peut, bien sûr, être également l'instance de calcul go-app-vm).
Si vous utilisez OCI Cloud Shell, les étapes de création de ce contexte sont légèrement différentes (et plus simples) que lorsque vous travaillez dans un environnement de développement standard. N'hésitez pas à suivre la configuration d'OCI Shell. Dans cet article, nous allons prendre l'autre voie, utilisée pour tout environnement de développement local.
Il existe plusieurs étapes à suivre dans un environnement local (dans lequel la CLI Fn a été précédemment installée) :
Après ces étapes, vous pouvez tester le succès des étapes 1, 2 et 4 avec cette commande, qui doit renvoyer la liste des compartiments de votre location :
oci iam compartment list
Facultatif : Créer un référentiel Container Image Registry
Si le compte utilisateur utilisé pour déployer la fonction dispose des droits d'accès IAM nécessaires, le déploiement crée le référentiel pour les images de fonction dans Container Image Registry. Si ces privilèges ne sont pas disponibles ou si vous souhaitez préparer le référentiel, vous pouvez le faire comme suit.
Création d'un contexte pour OCI dans l'interface de ligne de commande Fn
Pour revenir à la ligne de commande de l'environnement local, nous devons créer un contexte Fn pour le compartiment en cours sur OCI, puis le sélectionner pour utilisation dans les opérations Fn. Exécutez les commandes suivantes (que vous pouvez copier à partir de l'onglet Mise en route de la page go-on-oci-app) :
fn create context go-on-oci --provider oracle fn use context go-on-oci
Copiez les commandes de l'étape 4 pour mettre à jour le contexte avec l'OCID de compartiment et l'URL d'API Oracle Functions. Dans mon cas :
fn update context oracle.compartment-id ocid1.compartment.oc1..aaaaaaaaqb4vxvxuho5h7eewd3fl6dmlh4xg5qaqmtlcmzjtpxszfc7nzbyq fn update context api-url https://functions.us-ashburn-1.oraclecloud.com
La commande sera similaire mais différente pour vous.
Indiquez le préfixe de nom de référentiel unique. Utilisez go-on-oci et indiquez le compartiment contenant le référentiel de registre d'images vers lequel l'image de fonction doit être publiée :
fn update context registry iad.ocir.io/idtwlqf2hanz/go-on-oci fn update context oracle.image-compartment-id
Connectez-vous au registre en utilisant le jeton d'authentification comme mot de passe :
docker login iad.ocir.io
Dans mon cas, la région dans laquelle je travaille est Ashburn, identifiée par la clé de région iad.ocir.io. Je suis invité à saisir le nom d'utilisateur. Il s'agit d'une chaîne qui inclut le préfixe d'espace de noms inclus dans le nom de Container Image Registry et de chaque référentiel. Le mot de passe est ensuite demandé. Ici, vous fournissez un jeton d'authentification configuré pour l'utilisateur, que nous avons utilisé précédemment dans l'article précédent lorsque la connexion a été effectuée dans le référentiel de code.
La commande suivante affiche la liste des applications dans le contexte Fn actuel :
fn list apps
La liste contient une application, appelée go-on-oci-app.
L'hôte de fonction créé, déployé localement et appelé précédemment peut désormais également être déployé vers l'application OCI pour devenir une fonction cloud native sans serveur. La commande que nous utilisons pour le déploiement est la même que celle utilisée précédemment. Son effet est radicalement différent en raison du contexte modifié. Au lieu d'un contexte local, il existe désormais un contexte basé sur un fournisseur OCI et lié à une location et un compartiment OCI. L'image de conteneur est propagée vers OCI Container Image Registry et la fonction est créée dans le service Fonction OCI.
fn -v deploy --app go-on-oci-app
La sortie est similaire à celle générée précédemment, mais le processus de création est exactement le même. Une fois que l'image du conteneur de fonction est prête, les choses commencent à dévier. L'image est propagée vers OCI Container Image Registry et la fonction est déployée vers le cloud. Lignes associées dans la sortie :
=> 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
A ce stade, la fonction se trouve dans le cloud et peut être appelée (toujours à l'aide de la CLI Fn) :
fn invoke go-on-oci-app greeter
Le premier appel prendra un certain temps car la fonction démarre à froid et l'image de conteneur sous-jacente doit être instanciée dans un conteneur en cours d'exécution. Chaque appel ultérieur de la fonction sera beaucoup plus rapide. Notez que si vous attendez dix minutes et que la fonction se refroidit, le conteneur est arrêté.
Cette image décrit la situation à laquelle nous sommes arrivés :
Vous pouvez consulter la console OCI pour obtenir des preuves de ce qui vient de se passer. Saisissez greeter dans la zone de recherche de la console. Sous Ressources, il y aura une entrée >greeter > Functions>. Cliquez sur le lien pour accéder à la page affichant les détails de la fonction. Vous trouverez des références à l'image de la fonction, au paramètre de mémoire et à l'adresse pour appeler la fonction. Dans les mesures, vous devez trouver des preuves de l'appel à la fonction effectué à l'aide de la CLI Fn.
Dans les résultats de recherche pour greeter, vous trouverez également le référentiel de conteneurs go-on-oci/greeter. Lorsque vous accédez au référentiel, vous trouverez les détails des images qui y sont publiées.
OCI Functions ne peut pas être simplement appelé. Même s'ils ont un point de terminaison HTTP qui semble suggérer que vous pouvez simplement les appeler à partir de votre navigateur ou à partir de la ligne de commande, ce n'est pas si simple. Les appels HTTP aux fonctions doivent être signés, et ce processus de signature n'est pas simple et simple.
Une meilleure façon d'autoriser les destinataires à appeler des fonctions consiste à utiliser une passerelle d'API. Nous avons utilisé une passerelle d'API dans l'article précédent pour ouvrir un routage public vers l'application myserver exécutée sur une instance de calcul privée (potentiellement). Maintenant, nous allons faire la même chose pour la fonction greeter en utilisant un routage supplémentaire dans la passerelle API the-API-gateway et le déploiement myserver-API créé dans l'article précédent.
Configuration de l'accès IAM pour API Gateway
La passerelle API doit être autorisée à appeler la fonction, à l'aide d'une stratégie qui autorise la passerelle API à appeler des fonctions.
Créez la stratégie permettant à API Gateway d'appeler des fonctions. Pour créer une stratégie dans la console : tapez poli dans la barre de recherche et cliquez sur >Politiques > Identité> dans la zone Services de la fenêtre contextuelle des résultats de recherche. Vous accédez ainsi à la page de présentation des stratégies pour le compartiment en cours.
La stratégie définit le droit d'accès des passerelles d'API aux ressources du compartiment. Créez une stratégie, saisissez un nom (invoke-function-for-api-gateway), une description et l'instruction suivante :
ALLOW any-user to use functions-family in compartment where ALL {request.principal.type= 'ApiGateway', request.resource.compartment.id = ' '}
Remplacez
Définition du routage de la fonction dans le déploiement sur la passerelle d'API
Avec les droits d'accès pris en charge, nous pouvons désormais définir le routage sur API Gateway. Saisissez gat dans la barre de recherche de la console. Cliquez sur Passerelles > Gestion des API. Cliquez sur le lien pour *the-api-gateway. Cliquez sur Deployments. Dans la liste des déploiements (qui contient un seul déploiement), cliquez sur le lien myserver-api.
Cliquez sur le bouton Modifier pour ouvrir la spécification de déploiement. Cliquez sur le lien de la deuxième étape : Itinéraires. Faites défiler vers le bas et cliquez sur le bouton + Autre itinéraire.
Saisissez /greeting comme chemin pour cette nouvelle route. Sélectionnez GET comme méthode et Oracle Functions comme type (de back-end). Sélectionnez application go-on-oci-app, puis affectez à Function Name la valeur greeter.
Appuyez sur Suivant. Appuyez ensuite sur Enregistrer les modifications pour appliquer les modifications et rendre la nouvelle route réelle.
Appeler la fonction via la passerelle d'API
Avec la configuration du nouveau routage et l'actualisation du déploiement sur la passerelle d'API, nous pouvons désormais effectuer une demande HTTP simple et directe vers l'adresse publique de la passerelle d'API, déclenchant indirectement l'hôte de la fonction et recevant sa réponse.
En utilisant cette URL dans n'importe quel navigateur, vous devriez être en mesure d'obtenir la réponse de la fonction :
https:///my-api/greeting
La réponse est un peu décevante, mais cela est attendu avec une fonction aussi simpliste.
En utilisant curl, vous pouvez envoyer une charge utile JSON à la fonction et recevoir une réponse légèrement plus intéressante.
curl -X "GET" -H "Content-Type: application/json" -d '{"name":"Mickey Mouse"}' https:///my-api/greeting
La réponse indique {"message" :"Hello Mickey Mouse"}.
Nous avons donc établi le flux de bout en bout entre API Gateway et la fonction sans serveur. Et nous avons un moyen de déployer manuellement la fonction en fonction des sources dans notre environnement de développement local. Pour tirer parti de notre travail, vous pouvez apporter des modifications au code source dans func.go, puis déployer à nouveau la fonction (une seule commande avec l'interface de ligne de commande Fn) et appeler la route d'accueil sur la passerelle d'API pour vérifier que la modification est active.
Par exemple : modifiez la ligne qui définit la valeur de Msg sur
Msg: fmt.Sprintf("Warmest greetings from your function dear %s", p.Name)
Enregistrez la source func.go mise à jour. Exécutez ensuite les commandes suivantes pour déployer la fonction mise à jour, puis appelez-la :
fn -v deploy --app go-on-oci-app
curl -X "GET" -H "Content-Type: application/json" -d '{"name":"Mickey Mouse"}' https:///my-api/greeting
Cela devrait permettre d'améliorer la réponse. Le processus de création et de déploiement est condensé en une seule commande manuelle dans un environnement préparé. Nous examinerons ensuite un processus de déploiement automatisé pour les fonctions utilisant OCI DevOps, suivi du processus de création automatisé précédent basé sur la source dans un référentiel de code. Ensuite, nous passerons à des fonctions qui font un peu plus que de renvoyer des salutations simples.
Dans la version précédente de cette série, nous avons vu l'utilisation des pipelines de déploiement OCI DevOps pour déployer une application vers une instance de calcul. Nous allons maintenant utiliser un pipeline pour le déploiement automatisé d'une fonction. L'approche globale et les ingrédients sont similaires. Nous avons besoin d'un artefact, d'un environnement (cible) et du pipeline de déploiement avec une phase de déploiement de fonction, ainsi que des droits d'accès IAM pour que le pipeline puisse lire l'artefact et déployer la fonction.
Ces ingrédients sont plus détaillés :
Dans la console OCI, accédez à la page d'accueil du projet DevOps go-on-OCI. Ouvrez l'onglet Artefacts. Cliquez sur le bouton Ajouter un artefact. Notez que ce que nous définissons ici est un lien ou un proxy du projet DevOps vers un artefact réel, et non l'artefact lui-même.
Entrez greeter-function comme nom de l'artefact DevOps. Le type doit être défini sur Référentiel d'images de conteneur. Le chemin qualifié complet de l'image se compose de la clé de région, de l'espace de noms et du préfixe du référentiel, du nom de la fonction et de la balise de version de la fonction. Dans ce cas, utilisez un espace réservé pour la balise de version. Le chemin est maintenant défini comme suit :
///greeter:${imageVersion}
Définissez le champ déroulant Remplacer les paramètres utilisés dans cet artefact sur Oui, remplacer les espaces réservés.
Cliquez sur le bouton Ajouter pour terminer et enregistrer la définition de l'artefact.
Ouvrez l'onglet Environnements dans le projet DevOps. Il contient l'environnement go-on-oci-vm créé pour le déploiement de myserver sur l'instance Compute (dans l'article précédent). Cliquez sur le bouton Créer un environnement.
Dans la première étape, Informations de base, cliquez sur la mosaïque Fonctions - Créer un environnement pour une fonction. Entrez greeter-function-in-app-go-on-oci-app comme nom de l'environnement. Appuyez sur Suivant pour passer à la deuxième étape avec les détails de l'environnement. Confirmez la région, le compartiment, l'application et la fonction. Vous n'avez probablement pas besoin de modifier ces paramètres. Si vous le faites, assurez-vous que la fonction greeter dans l'application go-on-oci-app est sélectionnée.
Cliquez sur Créer un environnement pour enregistrer la définition.
Sur la page de présentation du projet DevOps, cliquez sur Créer un pipeline. Le formulaire Créer un pipeline est présenté. Saisissez un nom (deploy-greeter-function-to-go-on-oci-app) et éventuellement une description. Cliquez ensuite sur Créer un pipeline. Le pipeline de déploiement est créé, bien qu'il soit tout à fait vide : il ne s'agit pas d'un environnement dans lequel il doit être déployé, ni d'artefacts à déployer, ni d'un fichier de configuration pour définir les étapes à exécuter.
Dans l'éditeur de pipeline qui apparaît, cliquez sur la mosaïque Add Stage (ou sur l'icône plus). La page suivante affiche la liste des types de phase. Cliquez sur la mosaïque intitulée Utilise la stratégie de mise à jour intégrée de Functions.
Cliquez sur le bouton Next.
Saisissez le nom de l'étape, par exemple update-function-greeter. Sélectionnez l'environnement défini précédemment pour la fonction : greeter-function-in-app-go-on-oci-app.
Sous l'en-tête Artifact, cliquez sur Select Artifact. La liste de tous les artefacts du projet DevOps de type Image Docker est présentée. Sélectionnez la seule entrée, celle qui a été créée précédemment pour l'image du conteneur de fonctions.
Notez que le bouton Sélectionner un artefact n'est plus activé : une seule image de conteneur peut être associée à cette phase.
Cliquez sur Ajouter. La phase de pipeline est créée dans le pipeline. Et le pipeline est maintenant prêt à être exécuté – sa définition est terminée. Ou bien ? L'artefact utilisé par ce pipeline n'est pas défini sans équivoque : le libellé de version dans le chemin de l'image de conteneur contient l'espace réservé ${imageVersion}. Pour garantir que la version appropriée est utilisée pour le déploiement, cet espace réservé doit être remplacé par la valeur appropriée. Pour ce faire, définissez dans le pipeline un paramètre appelé imageVersion qui est défini sur un libellé de version existant.
Cliquez sur l'onglet Paramètres du pipeline. Définissez un nouveau paramètre appelé imageVersion. Sa valeur par défaut peut être n'importe quoi, mais elle peut tout aussi bien correspondre à une étiquette de version existante pour l'image de conteneur de la fonction greeter. Enregistrez la définition du paramètre.
Il semblerait que le pipeline soit prêt à être exécuté, mais nous devons toujours nous assurer qu'il est autorisé à faire son travail. Avant d'essayer quelque chose d'éruption cutanée, lisez la section suivante.
Dans l'article précédent, un groupe dynamique a été défini pour tous les pipelines de déploiement du compartiment. Le nouveau pipeline est automatiquement membre de ce groupe. Nous avons également défini une stratégie qui octroie des droits d'accès au groupe dynamique pour lire tous les artefacts, notamment les images de conteneur (fonction) dans les référentiels Container Image Registry du compartiment. Une autre stratégie déjà créée accorde au groupe dynamique le droit d'accès très large pour gérer toutes les ressources du compartiment. Nous pouvons bénéficier de l'étendue de cette politique, car elle couvre également la création et la mise à jour de fonctions.
Exécutez le pipeline de déploiement en appuyant sur Exécuter le pipeline.
Une fois le déploiement terminé, vous verrez les marqueurs verts qui annoncent le succès. Cependant, il n'y a pas d'autre indication évidente de ce succès, car le résultat final est exactement la situation que nous avions atteinte avec le déploiement manuel de la fonction à partir de la ligne de commande de la CLI Fn.
Pour rendre les choses un peu plus intéressantes, nous allons modifier le code de la fonction. Créez ensuite l'image de conteneur pour la fonction (localement) et propagez la nouvelle image de fonction vers le registre d'images de conteneur. Ensuite, nous relancerons le pipeline de déploiement ; cette fois, lorsque cela sera réussi, cela rendra une nouvelle situation que nous pourrons rencontrer en appelant le routage my-API/greeting sur la passerelle d'API.
Implémentation de la fonction de modification
Apportez une petite modification visible à func.go dans votre environnement local : assurez-vous que la réponse de la nouvelle version de la fonction est sensiblement différente de la version actuelle. Enregistrez la modification.
Dans les sections suivantes, nous allons créer une nouvelle version de l'image de conteneur de fonctions à partir de la source modifiée et la faire exécuter sur OCI Functions.
Créer une image de conteneur de fonctions (localement)
Ces commandes suivantes modifieront d'abord le libellé de version utilisé pour marquer la fonction avec une augmentation du troisième chiffre (bm est court pour le bump). Ensuite, l'image du conteneur de fonctions est créée à l'aide des sources modifiées. La troisième commande répertorie les images de conteneur local, en filtrant sur les images dont le nom est plus grand. Exécutez à présent les commandes.
fn bm
fn build
docker images | grep greeter
Vous devriez pouvoir trouver l'image nouvellement créée avec son nom qualifié complet, y compris la clé de région OCI, l'espace de noms, le préfixe de référentiel et l'hôte de nom de fonction, avec le libellé de version ajouté.
Baliser une image de conteneur avec un nouveau libellé de version et propager vers le registre
Définissez un nouvel identificateur pour l'image, à l'aide de cette commande qui définit le libellé de version sur 0.1.0 :
docker tag : :0.1.0
Poussez ensuite la nouvelle image de conteneur de fonction vers le référentiel OCI Container Image Registry, à l'aide des éléments suivants :
docker push :0.1.0
Notez qu'à ce stade, nous n'avons pas redéployé la fonction basée sur cette nouvelle version de l'image de conteneur. Tout ce que nous avons fait est de créer l'image et de la propager vers le registre sur OCI. L'appel de la fonction OCI n'affichera aucune différence.
Exécuter le pipeline de déploiement (pour la nouvelle image de fonction)
Exécutez à nouveau le pipeline de déploiement. Définissez la valeur du paramètre imageVersion sur 0.1.0.
Lorsque le pipeline est terminé avec succès, la nouvelle version de la fonction avec toutes les modifications intéressantes que vous lui avez appliquées est active.
Appeler la fonction nouvellement déployée
Vous pouvez voir la nouvelle version de fonction en action en l'appelant sur la ligne de commande à l'aide de la CLI Fn :
fn invoke go-on-oci-app greeter
(Etant donné que le contexte de l'interface de ligne de commande Fn est toujours go-on-OCI du fournisseur Oracle et configuré pour le compartiment go-on-OCI qui contient la fonction greeter, cet appel est dirigé vers la fonction OCI, qui est à ce stade basée sur la nouvelle version de l'image de conteneur.)
Vous pouvez également effectuer une boucle vers le routage sur la passerelle d'API qui appelle la fonction :
curl -X "GET" -H "Content-Type: application/json" -d '{"name":"Mickey Mouse"}' https:///my-api/greeting
Jusqu'à présent, nous avons construit l'image de conteneur de fonctions à la main dans l'environnement de développement local à l'aide de la CLI Fn. Cependant, tout comme nous l'avons fait dans l'article précédent pour l'application Go qui a été produite en tant qu'exécutable par un pipeline de build, nous allons maintenant transformer la construction de la fonction en un processus automatisé. Un pipeline de build OCI DevOps est créé pour extraire des sources du référentiel de code, exécuter une phase de build gérée qui produit une image de conteneur locale, puis publier cette image en tant qu'artefact vers le référentiel Container Image Registry. En dernier lieu, le pipeline de build déclenche le pipeline de déploiement pour publier la dernière définition de la fonction dans l'environnement OCI Functions d'exécution.
Lorsque tous les éléments sont en place, l'ensemble total interconnecté de composants OCI apparaît comme visualisé dans la figure suivante.
L'artefact et le pipeline de déploiement indiqués dans cette figure sont déjà définis dans le projet DevOps, tout comme le référentiel Application, Fonction et Container Image Registry pour les images de la fonction. Nous allons utiliser le référentiel de code configuré dans l'article précédent. Tout ce que nous avons besoin de créer, c'est la fonction de construction de pipeline de construction avec ses trois étapes.
Création du pipeline de build
Sur la page de présentation de DevOps Project go-on-oci, cliquez sur le bouton Créer un pipeline de build. Une page est présentée pour spécifier le nom (par exemple, build-greeter-function) et une description. Cliquez sur Créer pour ajouter le pipeline de build au projet DevOps.
Cliquez sur le lien build-greeter-function dans la liste pour accéder à la page de détails.
Première étape – Construction gérée
La première étape d'un pipeline de build est une phase de build géré. Cette phase fournit des instructions permettant au pipeline de conserver un serveur de build, de copier des sources spécifiées à partir de référentiels de code vers le serveur et d'exécuter un certain nombre d'actions sur ce serveur. Au moment de l'écriture, nous pouvons utiliser une seule image pour le serveur de build. Il s'agit d'une image Oracle Linux (8 Go de mémoire, 1 OCPU) qui dispose d'un certain nombre d'outils préinstallés et de temps d'exécution du langage. Pour créer l'image du conteneur de fonctions, il est important que le serveur de build soit équipé à la fois de Docker et de la CLI Fn.
Cliquez sur l'icône plus ou sur la carte Ajouter une phase. L'assistant Ajouter une phase en deux étapes s'affiche. A la première étape de l'assistant, assurez-vous que la carte de build géré est sélectionnée pour le type de phase. Appuyez sur Suivant.
La deuxième page est affichée. Définissez le nom de la phase de build : build-go-source-to-function-container-image. Vous pouvez éventuellement ajouter une description.
À l'heure actuelle, nous ne pouvons pas sélectionner une image de construction différente. Nous nous contentons donc de celle disponible, ce qui convient à nos fins.
Définissez le chemin du fichier de spécification de build sur /functions/greeter/go-function-build-spec.yaml. Ce fichier contient les instructions pour créer les sources Go dans la fonction greeter (ou toute autre fonction Go) et enfin créer l'image du conteneur de fonction.
Cliquez sur le bouton Select (Sélectionner) sous Primary code repository. Nous pouvons maintenant indiquer à partir de quel référentiel de code le build obtiendra ses sources. Sélectionnez OCI Code Repository comme type de connexion source. Sélectionnez ensuite le référentiel go-on-oci-repo. Nous allons travailler avec des sources sur la branche principale, donc ne modifiez pas cette valeur par défaut. Saisissez go-on-oci-sources comme valeur pour le nom de source de build. Une phase de build gérée peut utiliser des sources provenant de plusieurs référentiels. Dans la spécification de build, nous pouvons faire référence aux emplacements racine de chacune de ces sources à l'aide du libellé défini en tant que nom de source de build. Cliquez sur Enregistrer.
Appuyez sur le bouton Add. Cette opération termine la définition de la phase de build géré. C'est tout ce qui est nécessaire pour prendre les sources et les traiter en artefacts. Les instructions détaillées exécutées par cette phase de build gérée et sur le serveur de build sont définies dans le fichier go-function-build-spec.yaml. C'est ce fichier qui contient les instructions pour les étapes détaillées réelles exécutées sur le serveur de build.
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
La spécification de construction se compose de trois parties :
Les étapes de paramétrage peuvent être résumées comme suit :
Ces étapes sont largement équivalentes au build géré défini dans l'article précédent pour l'application Go qui a finalement été transformée en exécutable binaire déployé sur une machine virtuelle. Les étapes 9 et 10 sont différentes : elles transforment l'application Go en une image de conteneur de fonctions qui est le produit final du paramétrage.
Deuxième étape – Publier l'artefact
Sur la page de présentation du pipeline de build, cliquez sur l'icône plus en bas de la phase de build géré en cours. Dans le menu contextuel qui apparaît, cliquez sur Ajouter une phase. L'assistant de préparation apparaît.
Cliquez sur Livrer des artefacts. Cliquez ensuite sur Suivant.
Entrez le nom de cette phase : publish-greeter-function-container-image. Nous devons sélectionner l'artefact dans le projet DevOps à publier. Cet artefact est l'image de conteneur go-on-oci/greeter :${imageVersion}. Cliquez sur Sélectionner des artefacts et sélectionnez l'image du conteneur.
Dans la zone Associer des artefacts au résultat de build, nous devons indiquer, pour chacun des artefacts sélectionnés, quels sont les résultats d'une phase de build gérée qui constituent la source de publication de l'artefact. La spécification de build définit une sortie intitulée go-function-container-image. Cette sortie fait référence à l'image de conteneur produite sur le serveur de build par le processus de build de fonction. Entrez le libellé go-function-container-image dans le champ Build config/result artifact name. Cliquez sur le bouton Add pour créer la phase Deliver Artifacts.
Troisième étape – Déclencher le pipeline de déploiement
Sur la page de présentation du pipeline de build, cliquez sur l'icône plus en bas de la phase de distribution des artefacts. Dans le menu contextuel qui apparaît, cliquez sur Ajouter une phase. L'assistant de préparation apparaît.
Cliquez sur Déclencher le déploiement. Cliquez ensuite sur Suivant.
Saisissez le nom de l'étape : trigger-deployment-of-greeter-function-to-go-on-oci-app, et éventuellement une description. Cliquez sur le bouton Sélectionner le pipeline de déploiement. Sélectionnez le pipeline deploy-greeter-function-to-go-on-oci-app. Les détails du pipeline sont affichés, y compris les paramètres (imageVersion) et l'artefact utilisé par le déploiement.
Cliquez sur Ajouter pour terminer la définition de phase et l'ajouter au pipeline de build.
Le pipeline de build est terminé : il récupère les sources, les traite dans un artefact déployable, publie l'artefact dans le référentiel d'images et déclenche le pipeline de déploiement pour l'extraire de là.
Cliquez sur Démarrer l'exécution manuelle. Définissez une valeur pour le paramètre imageVersion, par exemple 0.2.0. Cliquez sur le bouton pour lancer le pipeline de build.
L'exécution du pipeline de build prend maintenant quelques minutes et déclenche le déploiement ultérieur de l'image de fonction nouvellement créée.
Lorsque tout est terminé et que le succès est signalé, vous pouvez appeler le routage sur API Gateway qui conduit à la fonction greeter pour vérifier si la réponse est bien la nouvelle attendue.
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"}
C'est un moment pour une petite fête. Nous avons réalisé un processus automatisé de bout en bout qui prend les sources d'application Go du référentiel de code et fournit, après le lintage, la vérification, le test et la création, une nouvelle version en cours d'exécution d'une fonction OCI sans serveur. Pour produire d'autres mises à jour de la fonction, il suffit de valider la modification et de déclencher le pipeline de build. Et comme étape suivante, nous pouvons même déclencher automatiquement le pipeline de build lorsqu'une validation (spécifique) a lieu dans le référentiel de code.
Dans la deuxième partie de cet article, nous aborderons l'utilisation des services OCI à partir des applications Go en général et des fonctions Go en particulier. L'utilisation du kit SDK Go pour OCI est introduite et les interactions avec OCI Object Storage Service sont démontrées.
Oracle Cloud Infrastructure est la plate-forme capable de créer et d'exécuter des applications développées en Go. Dans les instances de calcul ou en tant que fonctions sans serveur, et également en conteneur sur un cluster Kubernetes géré (comme nous le verrons dans la cinquième partie de cette série). Il est bon de réaliser qu'OCI est beaucoup plus pour les équipes de développement Go qu'une plate-forme d'exécution. OCI offre de nombreux services qui peuvent être exploités à partir d'applications. Services de stockage de fichiers, de données relationnelles ou "NoSQL", pour la gestion de la publication et de la consommation de messages. Services de transfert et d'analyse de données. Et les services qui prennent en charge les opérations sur les applications, tels que la surveillance.
L'interaction avec les services OCI peut être effectuée via les API REST disponibles pour tous les aspects de tous les services. Appeler des services REST avec des charges utiles JSON via HTTP est assez facile à partir d'une application Go. Il y a un facteur compliquant : ces appels d'API doivent être signés. La signature Oracle Cloud Infrastructure utilise le modèle d'authentification "Signature" (avec un en-tête d'autorisation), et le processus de signature n'est pas exactement trivial. Pour plus de détails sur ce processus de signature, reportez-vous à Documentation OCI – Signatures de demande d'API REST OCI.Heureusement, pour le développement d'applications Go qui font appel aux services OCI, nous pouvons utiliser le kit SDK Go pour OCI. Ce kit de développement open source facilite la signature des demandes d'API OCI. A l'aide du kit SDK, les appels aux services OCI sont effectués en tant qu'appels locaux à l'aide de structures prédéfinies fortement typées au lieu de traiter les corps de demande et de réponse JSON. Le kit SDK Go pour OCI utilise le même fichier de configuration que celui utilisé précédemment pour l'interface de ligne de commande Fn et l'interface de ligne de commande OCI. L'emplacement par défaut de ce fichier est $HOME/.oci. Ce fichier dirige le kit SDK Go vers une location et une région spécifiques pour un compte utilisateur à l'aide de la moitié de la paire de clés privées configurée pour l'utilisateur. Les applications Go qui utilisent le kit SDK Go pour OCI peuvent simplement s'appuyer sur cette configuration sans avoir à traiter les détails.
La documentation du kit SDK Go pour OCI est disponible dans la documentation OCI - kit SDK pour Go.
Dans cette section, nous allons développer une application Go simple qui utilise OCI Object Storage Service pour créer et lire des fichiers. Au début, il s'agit d'une application autonome qui peut être compilée et exécutée partout (à condition que le fichier de configuration OCI nous soit disponible). Ensuite, nous discutons de l'exécution de l'application Go dans OCI, sur une machine virtuelle ou en tant que fonction. Cette orientation spéciale est pertinente car lorsque le kit SDK Go est utilisé dans OCI, il peut tirer parti de l'identité et des privilèges du composant exécutant l'application. Cela signifie que le code exécuté sur OCI n'a pas besoin d'utiliser son propre fichier de configuration OCI et est donc encore plus simple.
Tout d'abord, créons une application Go très simple qui peut se connecter à OCI via le kit SDK. Nous allons ensuite utiliser cette base pour ajouter l'interaction avec Object Storage Service.
Le client Go le plus simple d'OCI
L'application Go la plus simple qui parle à OCI à l'aide du kit SDK Go pour OCI est créée comme suit :
L'hypothèse est que le fichier de configuration OCI décrit précédemment dans cet article se trouve à l'emplacement par défaut avec le nom par défaut : $HOME/.oci/config. Si le fichier se trouve à un autre emplacement, vous pouvez utiliser la fonction ConfigurationProviderFromFile dans le package github.com/oracle/oci-go-sdk/v65/common, qui accepte l'emplacement personnalisé du fichier de configuration.
Le fichier go.mod contient le contenu suivant :
module oci-client
go 1.16
require github.com/oracle/oci-go-sdk/v65 v65.2.0
Le bit github.com/oracle/oci-go-sdk/v65 référence la version la plus récente (au moment de l'écriture) du kit SDK Go. Les sources sont téléchargées en fonction de cette référence. Dans l'application Go, cela signifie que nous pouvons tirer parti des packages du kit SDK pour accéder à divers services du portefeuille OCI.
Le fichier oci-client.go contient ce code qui utilise les packages communs et d'identité :
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)
}
La première ligne de la fonction acquiert le client qui peut ensuite être utilisé pour la plupart des interactions avec OCI, comme l'appel GetCompartment qui renvoie les détails du compartiment racine.
L'application peut être exécutée à l'aide de go run oci-client.go et produira une sortie très simple :
Name of Root Compartment:
Même si le résultat n'est pas particulièrement remarquable, le fait qu'il existe des résultats prouve que le kit SDK Go pour OCI fonctionne et est prêt à être utilisé pour des activités plus élevées.
Notez que le code ignore complètement les erreurs qui peuvent se produire. Si vous rencontrez des erreurs, remplacez les traits de soulignement par une variable pour capturer et gérer cette erreur.
Accéder à la création et à l'extraction d'objets dans et à partir d'OCI Object Storage Service
OCI Object Storage Service offre un stockage persistant et bon marché pour différents types de données. Les objets, petits et grands, peuvent être stockés par programmation sur ce service pour être conservés pendant de courtes ou longues périodes, et pour être accessibles par programmation ou via des URL directes. Object Storage fournit la gestion des versions, différentes règles de conservation, le cryptage des données stockées, la gestion du cycle de vie des objets, la suppression automatisée basée sur le temps et différents niveaux de stockage.
Les objets d'Object Storage Service sont organisés en buckets. Un bucket est un conteneur logique d'objets qui réside dans un compartiment spécifique. Certaines opérations peuvent être effectuées sur tous les objets d'un bucket.
Le kit SDK Go pour OCI offre des fonctions et des types qui facilitent et facilitent l'utilisation du service Object Storage à partir des applications Go. Les opérations de manipulation de buckets, de création, de suppression et d'extraction d'objets sont facilement disponibles.
Pour plus de détails sur le package Object Storage dans le kit SDK Go pour OCI, consultez cette référence : Documentation du package Object Storage dans le kit SDK Go pour OCI.
Le référentiel source de cet article contient le dossier applications/store-n-retrieve, qui dispose d'une application Go simple qui se connecte à votre location OCI et crée un bucket, suivi de la création d'un objet et de l'extraction de ce même objet. L'application utilise le même fichier DefaultConfigProvider que celui utilisé dans la section précédente pour signer les demandes aux API OCI à l'aide du fichier $HOME/.oci/config.
La dépendance de cette application au kit SDK Go pour OCI est définie dans go.mod.
La première partie du code crée une instance ObjectStorageClient. De nombreuses fonctions sont définies sur l'interface sous-jacente. Elles prennent toutes en charge une forme quelconque d'interaction avec Object Storage Service. Le fichier ObjectStorageClient est créé à l'aide de common.DefaultConfigProvider() (comme précédemment), à l'aide du fichier de configuration OCI par défaut avec une référence à un fichier contenant la clé privée.
La fonction getNamespace est appelée pour obtenir l'espace de noms de la location en cours. Ensuite, ensureBucketExists est appelé avec le nom du bucket pour créer le bucket s'il n'existe pas déjà.
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)
}
.........................
Les fonctions appelées dans ce fragment de code pour extraire l'espace de noms et vérifier l'existence du bucket (et le créer s'il n'existe pas) sont assez simples. Elles sont définies comme suit :
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
}
La deuxième partie de cette application crée un objet dans le bucket, puis l'extrait. Lorsque l'exécution de l'application est terminée, elle a un effet persistant : le bucket a été créé (s'il n'existait pas déjà) et un objet a été créé ou mis à jour (si l'application a déjà été exécutée). Vous pouvez consulter la page de détails du bucket Go-Bucket dans la console OCI pour voir à la fois le bucket et l'objet créé.
..................
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
}
L'exécution de cette application (aller exécuter object-organizer.go) génère le résultat suivant :
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.
La section précédente traitait de toute application Go interagissant avec les services OCI via le kit SDK. Que dire de plus sur OCI Functions in Go ? Continuez à lire, car il s'avère que nous pouvons simplifier la vie lorsque le code Go que vous développez et à partir duquel vous souhaitez travailler avec le kit SDK Go sera déployé en tant que fonction OCI ou exécuté sur une instance de calcul dans OCI. Dans ces cas, nous pouvons tirer parti de l'authentification de principal de ressource (pour Functions) et de l'authentification de principal d'instance (pour le code exécuté sur une instance de calcul), ce qui signifie que nous n'avons pas besoin d'inclure le fichier de configuration OCI, et notre code qui parle du kit SDK peut être un peu plus simple.
Pour en savoir plus sur l'authentification de principal de ressource, reportez-vous à la documentation OCI relative à l'authentification de principal de ressource pour Functions.
Dans cette section, nous présentons une fonction OCI appelée object-broker. Vous trouverez les sources dans le référentiel source de cet article, dans les fonctions de chemin/broker d'objet. Comme précédemment, la fonction est définie à l'aide d'un fichier func.yaml avec des métadonnées et d'un fichier func.go avec le lien entre la fonction et la structure Fn. Le fichier go.mod définit les dépendances de la fonction. La logique d'interaction avec OCI Object Storage Service se trouve dans le fichier object-organizer.go. Définit un func public CreateObject appelé à partir de func.go.
CreateObject crée un objet avec le nom indiqué dans le bucket indiqué. Les premières lignes de la fonction CreateObject dans l'objet-organizer.go ont subi des modifications afin de fonctionner avec cette authentification de principal de ressource. La fonction auth.ResourcePrincipalConfigurationProvider() utilisée à présent n'exige pas qu'un fichier de configuration OCI et une clé privée soient inclus dans l'application. Elle suppose que le code s'exécute dans OCI et plus spécifiquement dans une ressource (qui peut être une fonction ou par exemple un serveur de build DevOps) appelée principal de ressource car elle est incluse dans un groupe dynamique et hérite des droits d'accès de cette appartenance au groupe. Avant trop longtemps, vous obtiendrez les instructions pour prendre les mesures requises pour cela.
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)
Tournons notre attention à côté de func.go. La fonction myHandler gère le déclencheur de fonction. Elle appelle CreateObject, mais pas avant d'avoir déterminé le nom de l'objet que la demande doit produire et le nom du bucket qui doit contenir l'objet. Ces noms ont une valeur par défaut, mais la fonction tente de trouver les valeurs de paramètre de requête de demande HTTP qui fournissent des valeurs spécifiques. Notez que cela fonctionne uniquement pour un déclencheur HTTP de la fonction, et non pour un appel effectué à l'aide de l'appel fn.
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())
......
Vous pouvez inspecter les détails du contexte Fn dans Documentation du Fn Go FDK du projet, en particulier dans l'exemple.
La fonction doit savoir dans quel compartiment OCI le bucket doit résider. Ces paramètres d'exécution sont généralement définis sur la fonction de l'application via une configuration. Les valeurs des configurations peuvent être lues dans la fonction à partir d'une carte dans le contexte, comme le montre cette ligne :
if compartmentOCID, ok := fnctx.Config()["compartmentOCID"]; ok {
Création et déploiement de la fonction à l'aide de l'interface de ligne de commande Fn
En supposant que vous êtes dans un terminal de l'environnement de développement local dans lequel la CLI Fn est installée et que le répertoire actuel est functions/object-broker, vous pouvez effectuer une construction locale de la fonction avec une sortie détaillée :
fn -v build
Lorsque le build semble correct, l'étape suivante consiste à déployer la fonction. Si le contexte Fn est défini pour utiliser le contexte go-on-OCI (vérifiez les contextes de liste fn), la fonction sera déployée vers l'application go-on-OCI-app sur OCI :
fn -v deploy --app go-on-oci-app
La fonction ne peut effectuer un travail significatif que si la configuration a été définie pour la valeur d'OCID de compartiment. Pour ce faire, utilisez la console ou l'instruction suivante avec la CLI Fn :
fn cf f go-on-oci-app object-broker compartmentOCID
Maintenant, la fonction est déployée et a sa configuration. Vous pouvez vous attendre à ce qu'un appel à la fonction avec cette commande réussisse :
fn invoke go-on-oci-app object-broker
Cependant, il existe un dernier aspect à gérer : la fonction utilise les API OCI Object Storage Service pour manipuler les buckets et les objets, mais elle doit disposer de droits d'accès explicites pour ce faire. Pour ce faire, nous utilisons un groupe dynamique et deux stratégies.
Droits d'accès des fonctions permettant de manipuler des objets et des buckets
Tout comme nous avons utilisé des groupes dynamiques pour créer un bénéficiaire représentant les pipelines de déploiement et de build, nous devons également créer un groupe dynamique contenant les fonctions auxquelles nous voulons accorder des droits d'accès. Pour créer le groupe dynamique, saisissez dyn dans la barre de recherche. Cliquez sur le lien Groupes dynamiques dans le volet des résultats de la recherche.
Dans la page de présentation des groupes dynamiques, cliquez sur Create Dynamic Group.
Entrez le nom du groupe dynamique pour les pipelines de déploiement, par exemple functions-in-go-on-oci, et saisissez éventuellement une description. Définissez la règle suivante qui sélectionne toutes les fonctions faisant partie du compartiment :
All {resource.type = 'fnfunc', resource.compartment.id = ''}
Bien sûr, remplacez
Pour créer une stratégie dans la console : tapez poli dans la barre de recherche et cliquez sur Policies > Identity dans la zone Services dans la fenêtre contextuelle des résultats de recherche. Vous accédez ainsi à la page de présentation des stratégies pour le compartiment en cours.
La première instruction de stratégie définit le droit d'accès de la fonction pour gérer les objets dans le compartiment. La deuxième instruction ajoute l'autorisation de gestion des buckets. Définissez un nom, une description et les instructions suivantes :
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
La figure suivante illustre les droits d'accès qui s'appliquent maintenant à la fonction lorsque la stratégie contenant ces instructions est enregistrée :
Maintenant, la fonction peut être appelée et doit pouvoir faire son travail en utilisant les noms par défaut pour le bucket et l'objet.
fn invoke go-on-oci-app object-broker
Vérifiez que le bucket est créé et qu'il contient l'objet nouvellement créé à l'aide de la console, de l'URL OCI à la page des buckets.
Ajouter un routage à la passerelle d'API pour déclencher la fonction
Pour pouvoir appeler la fonction de courtier d'objets sur HTTP depuis n'importe où, nous utiliserons à nouveau API Gateway. Saisissez gat dans la barre de recherche de la console. Cliquez sur >Passerelles > Gestion des API. Cliquez sur le lien de la passerelle-api. Cliquez sur Deployments. Dans la liste des déploiements (qui contient un seul déploiement), cliquez sur le lien myserver-api.
Cliquez sur Edit pour ouvrir la spécification de déploiement. Cliquez sur le lien de la deuxième étape : Itinéraires. Faites défiler vers le bas et cliquez sur + Autre itinéraire.
Saisissez /object-broker comme chemin pour cette nouvelle route. Sélectionnez GET comme méthode et Oracle Functions comme type (de back-end). Sélectionnez application go-on-oci-app, puis définissez Function Name sur object-broker. Appuyez sur Suivant, puis sur Enregistrer les modifications pour appliquer les modifications et rendre la nouvelle route réelle.
L'image de bout en bout qui est maintenant configurée à partir du destinataire HTTP via API Gateway vers Function et enfin bucket et objet se présente comme suit :
Appelez la fonction à partir du navigateur ou utilisez curl sur la ligne de commande en utilisant :
curl -X "GET" "http:///my-api/object-broker?objectName=new-exciting-object.txt&bucketName=the-ultimate-collection"
Création et déploiement automatisés
Cette fonction object-broker a été déployée manuellement sur la ligne de commande à l'aide de la CLI Fn. Cela a bien fonctionné bien sûr. Toutefois, si vous deviez maintenant commencer le développement de cette fonction, en passant par plusieurs cycles de développement, vous voudriez probablement introduire l'automatisation dans le processus de création et de déploiement.
Comme nous l'avons fait auparavant, vous pouvez facilement configurer les éléments requis dans OCI DevOps afin d'obtenir des pipelines automatisés pour le déploiement (de l'image de conteneur de fonction dans le registre d'images de conteneur) et la création (à partir du référentiel de code et résultant en une image de conteneur fraîchement cuite pour la fonction). L'élément principal propre à la fonction est le fichier de spécification de build pour la phase de build gérée dans le pipeline de build. Ce fichier est fourni en tant que go-function-build-spec.yaml dans le même répertoire que func.go et func.yaml.
Après avoir créé un artefact DevOps pour l'image de conteneur de fonction, un environnement pour la fonction et les deux pipelines pour le build et le déploiement, la configuration du processus DevOps automatisé se présente comme suit :
L'un des domaines abordés dans cet article était les fonctions sans serveur, écrites en Go et exécutées sur Oracle Cloud Infrastructure. La création et le déploiement automatisés de ces fonctions ont été discutés, tout comme l'utilisation d'API Gateway pour fournir l'accès à la fonction aux consommateurs HTTP externes.
Le deuxième sujet principal était le kit SDK Go pour OCI permettant d'interagir avec les services OCI à partir des applications Go. L'article a montré comment utiliser Go code pour accéder au service Object Storage afin de stocker et d'extraire des fichiers.
Les deux sujets ont été combinés dans la fonction object-broker. Cette fonction OCI tire parti de l'authentification du principal de ressource et des droits d'accès accordés via un groupe dynamique. Par le biais d'une configuration d'exécution, la fonction apprend les paramètres actuels propres à l'environnement.
Dans l'article suivant, l'interaction avec Oracle Database sera le sujet principal. Création d'une connexion à partir d'une application Go à une instance Oracle Database locale, ainsi qu'à une instance Autonomous Database exécutée sur OCI, et exécution d'opérations SQL avec ces bases de données depuis le confort de votre application Go. D'autres rubriques incluent l'utilisation d'un portefeuille Oracle Wallet pour une gestion appropriée des informations d'identification de base de données, y compris le portefeuille dans le processus de déploiement, et la combinaison d'interactions avec OCI Object Storage et les services Autonomous Database dans une seule application.