Présentation de l'orchestration sans serveur avec Workflows

1. Introduction

c9b0cc839df0bb8f.png

Workflows vous permet de créer des workflows sans serveur qui associent une série de tâches sans serveur dans un ordre que vous définissez. Vous pouvez combiner la puissance des API Google Cloud, des produits sans serveur tels que Cloud Functions et Cloud Run, et des appels à des API externes pour créer des applications sans serveur flexibles.

Les workflows ne nécessitent aucune gestion de l'infrastructure et s'adaptent parfaitement à la demande, y compris en cas de scaling à zéro instance. Avec son modèle de paiement à l'utilisation, vous ne payez que pour la durée d'exécution.

Dans cet atelier de programmation, vous allez apprendre à connecter divers services Google Cloud et API HTTP externes à Workflows. Plus précisément, vous allez connecter deux services Cloud Functions publics, un service Cloud Run privé et une API HTTP publique externe dans un workflow.

Points abordés

  • Principes de base des workflows.
  • Connecter des fonctions Cloud publiques à Workflows
  • Connecter des services Cloud Run privés avec Workflows
  • Connecter des API HTTP externes avec Workflows

2. Préparation

Configuration de l'environnement d'auto-formation

  1. Connectez-vous à Cloud Console, puis créez un projet ou réutilisez un projet existant. (Si vous n'avez pas encore de compte Gmail ou G Suite, vous devez en créer un.)

H_hgylo4zxOllHaAbPKJ7VyqCKPDUnDhkr-BsBIFBsrB6TYSisg6LX-uqmMhh4sXUy_hoa2Qv87C2nFmkg-QAcCiZZp0qtpf6VPaNEEfP_iqt29KVLD-gklBWugQVeOWsFnJmNjHDw

dcCPqfBIwNO4R-0fNQLUC4aYXOOZhKhjUnakFLZJGeziw2ikOxGjGkCHDwN5x5kCbPFB8fiOzZnX-GfuzQ8Ox-UU15BwHirkVPR_0RJwl0oXrhqZmMIvZMa_uwHugBJIdx5-bZ6Z8Q

jgLzVCxk93d6E2bbonzATKA4jFZReoQ-fORxZZLEi5C3D-ubnv6nL-eP-iyh7qAsWyq_nyzzuEoPFD1wFOFZOe4FWhPBJjUDncnTxTImT3Ts9TM54f4nPpsAp52O0y3Cb19IceAEgQ

Mémorisez l'ID du projet. Il s'agit d'un nom unique permettant de différencier chaque projet Google Cloud (le nom ci-dessus est déjà pris ; vous devez en trouver un autre). Il sera désigné par le nom PROJECT_ID tout au long de cet atelier de programmation.

  1. Vous devez ensuite activer la facturation dans Cloud Console pour pouvoir utiliser les ressources Google Cloud.

L'exécution de cet atelier de programmation est très peu coûteuse, voire gratuite. Veillez à suivre les instructions de la section "Nettoyer" qui indique comment désactiver les ressources afin d'éviter les frais une fois ce tutoriel terminé. Les nouveaux utilisateurs de Google Cloud peuvent participer au programme d'essai gratuit pour bénéficier d'un crédit de 300 $.

Démarrer Cloud Shell

Bien que Google Cloud puisse être utilisé à distance depuis votre ordinateur portable, nous allons nous servir de Google Cloud Shell pour cet atelier de programmation, un environnement de ligne de commande exécuté dans le cloud.

Depuis la console GCP, cliquez sur l'icône Cloud Shell de la barre d'outils située dans l'angle supérieur droit :

STgwiN06Y0s_gL7i9bTed8duc9tWOIaFw0z_4QOjc-jeOmuH2TBK8l4udei56CKPLoM_i1yEF6pn5Ga88eniJQoEh8cAiTH79gWUHJdKOw0oiBZfBpOdcEOl6p29i4mvPe_A6UMJBQ

Le provisionnement et la connexion à l'environnement prennent quelques instants seulement. Une fois l'opération terminée, le résultat devrait ressembler à ceci :

r6WRHJDzL-GdB5VDxMWa67_cQxRR_x_xCG5xdt9Nilfuwe9fTGAwM9XSZbNPWvDSFtrZ7DDecKqR5_pIq2IJJ9puAMkC3Kt4JbN9jfMX3gAwTNHNqFmqOJ-3iIX5HSePO4dNVZUkNA

Cette machine virtuelle contient tous les outils de développement nécessaires. Elle comprend un répertoire d'accueil persistant de 5 Go et s'exécute sur Google Cloud, ce qui améliore nettement les performances du réseau et l'authentification. Vous pouvez réaliser toutes les activités de cet atelier dans un simple navigateur.

3. Présentation des workflows

Principes de base

Un workflow est composé d'une série d'étapes décrites à l'aide de la syntaxe basée sur YAML de Workflows. Il s'agit de la définition du workflow. Pour en savoir plus sur la syntaxe YAML de Workflows, consultez la page de référence sur la syntaxe.

Lorsqu'un workflow est créé, il est déployé, ce qui le prépare à être exécuté. Il s'agit d'une exécution unique de la logique contenue dans la définition d'un workflow. Toutes les exécutions de workflow sont indépendantes, et le produit accepte un grand nombre d'exécutions simultanées.

Activer des services

Dans cet atelier de programmation, vous allez connecter Cloud Functions et des services Cloud Run à Workflows. Vous utiliserez également Cloud Build et Cloud Storage lors de la création de services.

Activez tous les services nécessaires :

gcloud services enable \
  cloudfunctions.googleapis.com \
  run.googleapis.com \
  workflows.googleapis.com \
  cloudbuild.googleapis.com \
  storage.googleapis.com

À l'étape suivante, vous allez connecter deux fonctions Cloud dans un workflow.

4. Déployer la première fonction Cloud

La première fonction est un générateur de nombres aléatoires en Python.

Créez un répertoire pour le code de la fonction et accédez-y:

mkdir ~/randomgen
cd ~/randomgen

Dans le répertoire, créez un fichier main.py avec le contenu suivant:

import random, json
from flask import jsonify

def randomgen(request):
    randomNum = random.randint(1,100)
    output = {"random":randomNum}
    return jsonify(output)

Lorsqu'elle reçoit une requête HTTP, cette fonction génère un nombre aléatoire compris entre 1 et 100 et la renvoie au format JSON à l'appelant.

La fonction s'appuie sur Flask pour le traitement HTTP et nous devons l'ajouter en tant que dépendance. Dans Python, les dépendances sont gérées avec pip et exprimées dans un fichier de métadonnées appelé requirements.txt.

Dans le même répertoire, créez un fichier requirements.txt avec le contenu suivant:

flask>=1.0.2

Déployez la fonction avec un déclencheur HTTP et en autorisant les requêtes non authentifiées à l'aide de cette commande:

gcloud functions deploy randomgen \
    --runtime python37 \
    --trigger-http \
    --allow-unauthenticated

Une fois la fonction déployée, vous pouvez voir son URL sous la propriété httpsTrigger.url affichée dans la console ou avec la commande gcloud functions describe.

Vous pouvez également accéder à cette URL de la fonction à l'aide de la commande curl suivante:

curl $(gcloud functions describe randomgen --format='value(httpsTrigger.url)')

La fonction est prête pour le workflow.

5. Déployer une deuxième fonction Cloud

La seconde fonction est un multiplicateur. Elle multiplie l'entrée reçue par 2.

Créez un répertoire pour le code de la fonction et accédez-y:

mkdir ~/multiply
cd ~/multiply

Dans le répertoire, créez un fichier main.py avec le contenu suivant:

import random, json
from flask import jsonify

def multiply(request):
    request_json = request.get_json()
    output = {"multiplied":2*request_json['input']}
    return jsonify(output)

Lorsqu'elle reçoit une requête HTTP, cette fonction extrait la valeur input du corps JSON, la multiplie par 2 et la renvoie au format JSON à l'appelant.

Créez le même fichier requirements.txt dans le même répertoire avec le contenu suivant:

flask>=1.0.2

Déployez la fonction avec un déclencheur HTTP et en autorisant les requêtes non authentifiées à l'aide de cette commande:

gcloud functions deploy multiply \
    --runtime python37 \
    --trigger-http \
    --allow-unauthenticated

Une fois la fonction déployée, vous pouvez également accéder à son URL à l'aide de la commande curl suivante:

curl $(gcloud functions describe multiply --format='value(httpsTrigger.url)') \
-X POST \
-H "content-type: application/json" \
-d '{"input": 5}'

La fonction est prête pour le workflow.

6. Connecter deux fonctions Cloud

Dans le premier workflow, associez les deux fonctions.

Créez un fichier workflow.yaml avec le contenu suivant.

- randomgenFunction:
    call: http.get
    args:
        url: https://<region>-<project-id>.cloudfunctions.net/randomgen
    result: randomgenResult
- multiplyFunction:
    call: http.post
    args:
        url: https://<region>-<project-id>.cloudfunctions.net/multiply
        body:
            input: ${randomgenResult.body.random}
    result: multiplyResult
- returnResult:
    return: ${multiplyResult}

Dans ce workflow, vous obtenez un nombre aléatoire généré par la première fonction, puis vous le transmettez à la seconde fonction. Le résultat est le nombre aléatoire multiplié.

Déployez le premier workflow:

gcloud workflows deploy workflow --source=workflow.yaml

Exécutez le premier workflow:

gcloud workflows execute workflow

Une fois le workflow exécuté, vous pouvez voir le résultat en transmettant l'ID d'exécution indiqué à l'étape précédente:

gcloud workflows executions describe <your-execution-id> --workflow workflow

Le résultat inclura result et state:

result: '{"body":{"multiplied":108},"code":200 ... } 

...
state: SUCCEEDED

7. Connecter une API HTTP externe

Vous allez maintenant connecter math.js en tant que service externe dans le workflow.

Dans math.js, vous pouvez évaluer des expressions mathématiques comme ceci:

curl https://api.mathjs.org/v4/?'expr=log(56)'

Cette fois, vous allez utiliser la console Cloud pour mettre à jour notre workflow. Recherchez Workflows dans la console Google Cloud:

7608a7991b33bbb0.png

Recherchez votre workflow, puis cliquez sur l'onglet Definition:

f3c8c4d3ffa49b1b.png

Modifiez la définition du workflow et incluez un appel à math.js.

- randomgenFunction:
    call: http.get
    args:
        url: https://<region>-<project-id>.cloudfunctions.net/randomgen
    result: randomgenResult
- multiplyFunction:
    call: http.post
    args:
        url: https://<region>-<project-id>.cloudfunctions.net/multiply
        body:
            input: ${randomgenResult.body.random}
    result: multiplyResult
- logFunction:
    call: http.get
    args:
        url: https://api.mathjs.org/v4/
        query:
            expr: ${"log(" + string(multiplyResult.body.multiplied) + ")"}
    result: logResult
- returnResult:
    return: ${logResult}

Le workflow insère désormais la sortie de la fonction de multiplication dans un appel de fonction journal dans math.js.

L'interface utilisateur vous guidera pour modifier et déployer le workflow. Une fois déployé, cliquez sur Execute pour exécuter le workflow. Les détails de l'exécution s'affichent:

b40c76ee43a1ce65.png

Notez le code d'état 200 et une body avec le résultat de la fonction de journal.

Vous venez d'intégrer un service externe à notre workflow, c'est super !

8. Déployer un service Cloud Run

Enfin, finalisez le workflow en appelant un service Cloud Run privé. Cela signifie que le workflow doit être authentifié pour appeler le service Cloud Run.

Le service Cloud Run renvoie la valeur math.floor du nombre transmis.

Créez un répertoire pour le code du service et accédez-y:

mkdir ~/floor
cd ~/floor

Dans le répertoire, créez un fichier app.py avec le contenu suivant:

import json
import logging
import os
import math

from flask import Flask, request

app = Flask(__name__)

@app.route('/', methods=['POST'])
def handle_post():
    content = json.loads(request.data)
    input = float(content['input'])
    return f"{math.floor(input)}", 200

if __name__ != '__main__':
    # Redirect Flask logs to Gunicorn logs
    gunicorn_logger = logging.getLogger('gunicorn.error')
    app.logger.handlers = gunicorn_logger.handlers
    app.logger.setLevel(gunicorn_logger.level)
    app.logger.info('Service started...')
else:
    app.run(debug=True, host='0.0.0.0', port=int(os.environ.get('PORT', 8080)))

Cloud Run déploie les conteneurs. Vous avez donc besoin d'un Dockerfile et votre conteneur doit être lié aux variables d'environnement 0.0.0.0 et PORT, d'où le code ci-dessus.

Lorsqu'elle reçoit une requête HTTP, cette fonction extrait input du corps JSON, appelle math.floor et renvoie le résultat à l'appelant.

Dans le même répertoire, créez le fichier Dockerfile suivant:

# Use an official lightweight Python image.
# https://hub.docker.com/_/python
FROM python:3.7-slim

# Install production dependencies.
RUN pip install Flask gunicorn

# Copy local code to the container image.
WORKDIR /app
COPY . .

# Run the web service on container startup. Here we use the gunicorn
# webserver, with one worker process and 8 threads.
# For environments with multiple CPU cores, increase the number of workers
# to be equal to the cores available.
CMD exec gunicorn --bind 0.0.0.0:8080 --workers 1 --threads 8 app:app

Créez le conteneur :

export SERVICE_NAME=floor
gcloud builds submit --tag gcr.io/${GOOGLE_CLOUD_PROJECT}/${SERVICE_NAME}

Une fois le conteneur créé, déployez-le sur Cloud Run. Notez l'indicateur no-allow-unauthenticated. Cela permet de s'assurer que le service n'accepte que les appels authentifiés:

gcloud run deploy ${SERVICE_NAME} \
  --image gcr.io/${GOOGLE_CLOUD_PROJECT}/${SERVICE_NAME} \
  --platform managed \
  --no-allow-unauthenticated

Une fois déployé, le service est prêt pour le workflow.

9. Connecter le service Cloud Run

Avant de pouvoir configurer Workflows pour appeler le service privé Cloud Run, vous devez créer un compte de service que Workflows doit utiliser:

export SERVICE_ACCOUNT=workflows-sa
gcloud iam service-accounts create ${SERVICE_ACCOUNT}

Attribuez le rôle run.invoker au compte de service. Le compte de service pourra ainsi appeler des services Cloud Run authentifiés:

gcloud projects add-iam-policy-binding ${GOOGLE_CLOUD_PROJECT} \
    --member "serviceAccount:${SERVICE_ACCOUNT}@${GOOGLE_CLOUD_PROJECT}.iam.gserviceaccount.com" \
    --role "roles/run.invoker"

Mettez à jour la définition du workflow dans workflow.yaml pour inclure le service Cloud Run. Notez que vous incluez également le champ auth pour vous assurer que Workflows transmet le jeton d'authentification dans ses appels au service Cloud Run:

- randomgenFunction:
    call: http.get
    args:
        url: https://<region>-<project-id>.cloudfunctions.net/randomgen
    result: randomgenResult
- multiplyFunction:
    call: http.post
    args:
        url: https://<region>-<project-id>.cloudfunctions.net/multiply
        body:
            input: ${randomgenResult.body.random}
    result: multiplyResult
- logFunction:
    call: http.get
    args:
        url: https://api.mathjs.org/v4/
        query:
            expr: ${"log(" + string(multiplyResult.body.multiplied) + ")"}
    result: logResult
- floorFunction:
    call: http.post
    args:
        url: https://floor-<random-hash>.run.app
        auth:
            type: OIDC
        body:
            input: ${logResult.body}
    result: floorResult
- returnResult:
    return: ${floorResult}

Mettez à jour le workflow. Cette fois, en transmettant le compte de service:

gcloud workflows deploy workflow \
    --source=workflow.yaml \
    --service-account=${SERVICE_ACCOUNT}@${GOOGLE_CLOUD_PROJECT}.iam.gserviceaccount.com

Exécutez le workflow :

gcloud workflows execute workflow

En quelques secondes, vous pouvez examiner l'exécution du workflow pour voir le résultat:

gcloud workflows executions describe <your-execution-id> --workflow workflow

Le résultat inclut les nombres entiers result et state:

result: '{"body":"5","code":200 ... } 

...
state: SUCCEEDED

10. Félicitations !

Bravo ! Vous avez terminé cet atelier de programmation.

Points abordés

  • Principes de base des workflows.
  • Connecter des fonctions Cloud publiques à Workflows
  • Connecter des services Cloud Run privés avec Workflows
  • Connecter des API HTTP externes avec Workflows