Développement InnerLoop avec Python

1. Présentation

Cet atelier présente des fonctionnalités conçues pour simplifier le workflow de développement des ingénieurs logiciels chargés de développer des applications Python dans un environnement conteneurisé. Le développement de conteneurs typique exige de l'utilisateur qu'il comprenne les détails des conteneurs et du processus de création de conteneurs. De plus, les développeurs doivent généralement interrompre leur flux de travail et quitter leur IDE pour tester et déboguer leurs applications dans des environnements distants. Grâce aux outils et technologies mentionnés dans ce tutoriel, les développeurs peuvent travailler efficacement avec des applications conteneurisées sans quitter leur IDE.

Objectifs de l'atelier

Dans cet atelier, vous allez découvrir des méthodes de développement avec des conteneurs dans GCP, y compris :

  • Créer une application de démarrage Python
  • Parcourir le processus de développement
  • Développer un service REST CRUD simple

2. Préparation

Configuration de l'environnement d'auto-formation

  1. Connectez-vous à la console Google Cloud, puis créez un projet ou réutilisez un projet existant. (Si vous ne possédez pas encore de compte Gmail ou Google Workspace, vous devez en créer un.)

b35bf95b8bf3d5d8.png

a99b7ace416376c4.png

bd84a6d3004737c5.png

  • Le nom du projet est le nom à afficher pour les participants au projet. Il s'agit d'une chaîne de caractères qui n'est pas utilisée par les API Google, et que vous pouvez modifier à tout moment.
  • L'ID du projet doit être unique sur l'ensemble des projets Google Cloud et doit être immuable (vous ne pouvez pas le modifier une fois que vous l'avez défini). Cloud Console génère automatiquement une chaîne unique dont la composition importe peu, en général. Dans la plupart des ateliers de programmation, vous devrez référencer l'ID du projet (généralement identifié comme PROJECT_ID), donc s'il ne vous convient pas, générez-en un autre au hasard ou définissez le vôtre, puis vérifiez s'il est disponible. Il est ensuite "gelé" une fois le projet créé.
  • La troisième valeur est le numéro de projet, utilisé par certaines API. Pour en savoir plus sur ces trois valeurs, consultez la documentation.
  1. Vous devez ensuite activer la facturation dans Cloud Console afin d'utiliser les ressources/API Cloud. L'exécution de cet atelier de programmation est très peu coûteuse, voire sans frais. Pour arrêter les ressources afin d'éviter qu'elles ne vous soient facturées après ce tutoriel, suivez les instructions de nettoyage indiquées à la fin de l'atelier. Les nouveaux utilisateurs de Google Cloud peuvent participer au programme d'essai sans frais pour bénéficier d'un crédit de 300$.

Démarrer l'éditeur Cloudshell

Cet atelier a été conçu et testé pour être utilisé avec l'éditeur Cloud Shell. Pour accéder à l'éditeur :

  1. Accédez à votre projet Google à l'adresse https://console.cloud.google.com.
  2. En haut à droite, cliquez sur l'icône de l'éditeur Cloud Shell.

8560cc8d45e8c112.png

  1. Un nouveau volet s'ouvre en bas de la fenêtre.
  2. Cliquez sur le bouton "Ouvrir l'éditeur".

9e504cb98a6a8005.png

  1. L'éditeur s'ouvre avec un explorateur à droite et un éditeur dans la zone centrale.
  2. Un volet de terminal doit également être disponible en bas de l'écran.
  3. Si le terminal n'est PAS ouvert, utilisez la combinaison de touches "ctrl+`" pour ouvrir une nouvelle fenêtre de terminal.

Configuration de l'environnement

Dans Cloud Shell, définissez l'ID et le numéro de votre projet. Enregistrez-les en tant que variables PROJECT_ID et PROJECT_ID.

export PROJECT_ID=$(gcloud config get-value project)
export PROJECT_NUMBER=$(gcloud projects describe $PROJECT_ID \
    --format='value(projectNumber)')

Obtenir le code source

  1. Le code source de cet atelier se trouve dans container-developer-workshop dans GoogleCloudPlatform sur GitHub. Clonez-le à l'aide de la commande ci-dessous, puis accédez au répertoire.
git clone https://github.com/GoogleCloudPlatform/container-developer-workshop.git &&
cd container-developer-workshop/labs/python
mkdir music-service && cd music-service 
cloudshell workspace .

Si le terminal n'est PAS ouvert, utilisez la combinaison de touches "ctrl+`" pour ouvrir une nouvelle fenêtre de terminal.

Provisionner l'infrastructure utilisée dans cet atelier

Dans cet atelier, vous allez déployer du code sur GKE et accéder aux données stockées dans une base de données Spanner. Le script de configuration ci-dessous prépare cette infrastructure pour vous. Le processus de provisionnement prendra plus de 10 minutes. Vous pouvez passer aux étapes suivantes pendant le traitement de la configuration.

../setup.sh

3. Créer une application de démarrage Python

  1. Créez un fichier nommé requirements.txt et copiez-y le contenu suivant :
Flask
gunicorn
google-cloud-spanner
ptvsd==4.3.2
  1. Créez un fichier nommé app.py et collez-y le code suivant :
import os
from flask import Flask, request, jsonify
from google.cloud import spanner

app = Flask(__name__)

@app.route("/")
def hello_world():
    message="Hello, World!"
    return message

if __name__ == '__main__':
    server_port = os.environ.get('PORT', '8080')
    app.run(debug=False, port=server_port, host='0.0.0.0')

  1. Créez un fichier nommé Dockerfile et collez-y le code suivant :
FROM python:3.8
ARG FLASK_DEBUG=0
ENV FLASK_DEBUG=$FLASK_DEBUG
ENV FLASK_APP=app.py
WORKDIR /app
COPY requirements.txt .
RUN pip install --trusted-host pypi.python.org -r requirements.txt
COPY . .
ENTRYPOINT ["python3", "-m", "flask", "run", "--port=8080", "--host=0.0.0.0"]

Remarque : FLASK_DEBUG=1 vous permet de recharger automatiquement les modifications de code dans une application Python Flask. Ce Dockerfile vous permet de transmettre cette valeur en tant qu'argument de compilation.

Générer des fichiers manifestes

Dans votre terminal, exécutez la commande suivante pour générer un fichier skaffold.yaml et deployment.yaml par défaut.

  1. Initialisez Skaffold à l'aide de la commande suivante :
skaffold init --generate-manifests

Lorsque vous y êtes invité, utilisez les flèches pour déplacer le curseur et la barre d'espace pour sélectionner les options.

Choisissez :

  • 8080 pour le port
  • y pour enregistrer la configuration

Mettre à jour les configurations Skaffold

  • Modifier le nom de l'application par défaut
  • Ouvrir skaffold.yaml
  • Sélectionnez le nom de l'image actuellement défini sur dockerfile-image.
  • Effectuez un clic droit et sélectionnez "Modifier toutes les occurrences".
  • Saisissez le nouveau nom, par exemple python-app.
  • Modifiez la section "build" pour
  • Ajouter docker.buildArgs à la carte FLASK_DEBUG=1
  • Synchroniser les paramètres pour charger les modifications apportées aux fichiers *.py de l'IDE au conteneur en cours d'exécution

Après les modifications, la section de compilation du fichier skaffold.yaml se présente comme suit :

build:
 artifacts:
 - image: python-app
   docker:
     buildArgs:
       FLASK_DEBUG: 1
     dockerfile: Dockerfile
   sync:
     infer:
     - '**/*.py'

Modifier le fichier de configuration Kubernetes

  1. Modifier le nom par défaut
  • Ouvrir le fichier deployment.yaml
  • Sélectionnez le nom de l'image actuellement défini sur dockerfile-image.
  • Effectuez un clic droit et sélectionnez "Modifier toutes les occurrences".
  • Saisissez le nouveau nom, par exemple python-app.

4. Parcourir le processus de développement

Maintenant que la logique métier est ajoutée, vous pouvez déployer et tester votre application. La section suivante mettra en évidence l'utilisation du plug-in Cloud Code. Ce plug-in s'intègre, entre autres, à skaffold pour simplifier votre processus de développement. Lorsque vous déployez sur GKE lors des étapes suivantes, Cloud Code et Skaffold créent automatiquement votre image de conteneur, la transfèrent vers Container Registry, puis déploient votre application sur GKE. Cela se produit en arrière-plan, en masquant les détails du flux de développement.

Déployer sur Kubernetes

  1. Dans le volet situé au bas de l'éditeur Cloud Shell, sélectionnez Cloud Code .

fdc797a769040839.png

  1. Dans le panneau qui s'affiche en haut, sélectionnez Exécuter sur Kubernetes. Si vous y êtes invité, sélectionnez "Oui" pour utiliser le contexte Kubernetes actuel.

cfce0d11ef307087.png

Cette commande compile le code source, puis exécute les tests. L'exécution de la compilation et des tests prend quelques minutes. Ces tests incluent des tests unitaires ainsi qu'une étape de validation qui vérifie les règles définies pour l'environnement de déploiement. Cette étape de validation est déjà configurée. Elle vous permet d'être averti en cas de problème de déploiement, même lorsque vous travaillez toujours dans votre environnement de développement.

  1. La première fois que vous exécutez la commande, une invite s'affiche en haut de l'écran pour vous demander si vous souhaitez utiliser le contexte Kubernetes actuel. Sélectionnez "Oui" pour accepter et utiliser le contexte actuel.
  2. Une invite s'affiche ensuite pour vous demander quel registre de conteneurs utiliser. Appuyez sur Entrée pour accepter la valeur par défaut fournie.
  3. Sélectionnez l'onglet "Sortie" dans le volet inférieur pour afficher la progression et les notifications.

f95b620569ba96c5.png

  1. Sélectionnez "Kubernetes: Run/Debug - Detailed" (Kubernetes : Exécuter/Déboguer – Détails) dans le menu déroulant à droite pour afficher des informations supplémentaires et les journaux diffusés en direct depuis les conteneurs.

94acdcdda6d2108.png

Une fois la compilation et les tests terminés, l'onglet "Output" (Sortie) indique Attached debugger to container "python-app-8476f4bbc-h6dsl" successfully. et l'URL http://localhost:8080 est listée.

  1. Dans le terminal Cloud Code, pointez sur la première URL de la sortie (http://localhost:8080), puis sélectionnez "Ouvrir l'aperçu sur le Web" dans l'info-bulle qui s'affiche.
  2. Un nouvel onglet de navigateur s'ouvre et affiche le message Hello, World!.

Hot reload

  1. Ouvrez le fichier app.py.
  2. Modifie le message d'accueil en Hello from Python

Vous remarquerez immédiatement que, dans la fenêtre Output, vue Kubernetes: Run/Debug, le détecteur synchronise les fichiers mis à jour avec le conteneur dans Kubernetes.

Update initiated
Build started for artifact python-app
Build completed for artifact python-app

Deploy started
Deploy completed

Status check started
Resource pod/python-app-6f646ffcbb-tn7qd status updated to In Progress
Resource deployment/python-app status updated to In Progress
Resource deployment/python-app status completed successfully
Status check succeeded
...
  1. Si vous passez à la vue Kubernetes: Run/Debug - Detailed, vous remarquerez qu'elle reconnaît les modifications apportées aux fichiers, puis compile et redéploie l'application.
files modified: [app.py]
Syncing 1 files for gcr.io/veer-pylab-01/python-app:3c04f58-dirty@sha256:a42ca7250851c2f2570ff05209f108c5491d13d2b453bb9608c7b4af511109bd
Copying files:map[app.py:[/app/app.py]]togcr.io/veer-pylab-01/python-app:3c04f58-dirty@sha256:a42ca7250851c2f2570ff05209f108c5491d13d2b453bb9608c7b4af511109bd
Watching for changes...
[python-app] * Detected change in '/app/app.py', reloading
[python-app] * Restarting with stat
[python-app] * Debugger is active!
[python-app] * Debugger PIN: 744-729-662
  1. Actualisez votre navigateur pour afficher les résultats mis à jour.

Débogage

  1. Accédez à la vue Déboguer et arrêtez le thread actuel 647213126d7a4c7b.png.
  2. Cliquez sur Cloud Code dans le menu du bas, puis sélectionnez Debug on Kubernetes pour exécuter l'application en mode debug.
  • Dans la vue Kubernetes Run/Debug - Detailed de la fenêtre Output, notez que Skaffold déploie cette application en mode débogage.
  1. La première fois que vous exécutez cette commande, une invite vous demande où se trouve la source dans le conteneur. Cette valeur est liée aux répertoires du fichier Dockerfile.

Appuyez sur Entrée pour accepter la valeur par défaut.

583436647752e410.png

La création et le déploiement de l'application prennent quelques minutes.

  1. Lorsque le processus est terminé. Vous remarquerez qu'un débogueur est associé.
Port forwarding pod/python-app-8bd64cf8b-cskfl in namespace default, remote port 5678 -> http://127.0.0.1:5678
  1. La couleur de la barre d'état inférieure passe du bleu à l'orange, ce qui indique qu'elle est en mode Débogage.
  2. Dans la vue Kubernetes Run/Debug, notez qu'un conteneur débogable est démarré.
**************URLs*****************
Forwarded URL from service python-app: http://localhost:8080
Debuggable container started pod/python-app-8bd64cf8b-cskfl:python-app (default)
Update succeeded
***********************************

Utiliser les points d'arrêt

  1. Ouvrez le fichier app.py.
  2. Recherchez l'instruction return message.
  3. Ajoutez un point d'arrêt à cette ligne en cliquant sur l'espace vide à gauche du numéro de ligne. Un indicateur rouge s'affiche pour indiquer que le point d'arrêt est défini.
  4. Rechargez votre navigateur et notez que le débogueur arrête le processus au point d'arrêt et vous permet d'examiner les variables et l'état de l'application qui s'exécute à distance dans GKE.
  5. Cliquez sur la section "VARIABLES".
  6. Cliquez sur "Locals" (Variables locales), puis recherchez la variable "message".
  7. Double-cliquez sur le nom de la variable "message", puis, dans le pop-up, remplacez la valeur par une autre valeur, par exemple "Greetings from Python".
  8. Cliquez sur le bouton "Continuer" dans le panneau de configuration du débogage 607c33934f8d6b39.png.
  9. Examinez la réponse dans votre navigateur. Elle affiche désormais la valeur modifiée que vous venez de saisir.
  10. Arrêtez le mode "Débogage" en appuyant sur le bouton d'arrêt 647213126d7a4c7b.png, puis supprimez le point d'arrêt en cliquant à nouveau dessus.

5. Développer un service REST CRUD simple

À ce stade, votre application est entièrement configurée pour le développement conteneurisé et vous avez parcouru le workflow de développement de base avec Cloud Code. Dans les sections suivantes, vous allez mettre en pratique ce que vous avez appris en ajoutant des points de terminaison de service REST qui se connectent à une base de données gérée dans Google Cloud.

Coder le service REST

Le code ci-dessous crée un service REST simple qui utilise Spanner comme base de données pour l'application. Créez l'application en copiant le code suivant dans votre application.

  1. Créez l'application principale en remplaçant app.py par le contenu suivant :
import os
from flask import Flask, request, jsonify
from google.cloud import spanner


app = Flask(__name__)


instance_id = "music-catalog"

database_id = "musicians"

spanner_client = spanner.Client()
instance = spanner_client.instance(instance_id)
database = instance.database(database_id)


@app.route("/")
def hello_world():
    return "<p>Hello, World!</p>"

@app.route('/singer', methods=['POST'])
def create():
    try:
        request_json = request.get_json()
        singer_id = request_json['singer_id']
        first_name = request_json['first_name']
        last_name = request_json['last_name']
        def insert_singers(transaction):
            row_ct = transaction.execute_update(
                f"INSERT Singers (SingerId, FirstName, LastName) VALUES" \
                f"({singer_id}, '{first_name}', '{last_name}')"
            )
            print("{} record(s) inserted.".format(row_ct))

        database.run_in_transaction(insert_singers)

        return {"Success": True}, 200
    except Exception as e:
        return e



@app.route('/singer', methods=['GET'])
def get_singer():

    try:
        singer_id = request.args.get('singer_id')
        def get_singer():
            first_name = ''
            last_name = ''
            with database.snapshot() as snapshot:
                results = snapshot.execute_sql(
                    f"SELECT SingerId, FirstName, LastName FROM Singers " \
                    f"where SingerId = {singer_id}",
                    )
                for row in results:
                    first_name = row[1]
                    last_name = row[2]
                return (first_name,last_name )
        first_name, last_name = get_singer()  
        return {"first_name": first_name, "last_name": last_name }, 200
    except Exception as e:
        return e


@app.route('/singer', methods=['PUT'])
def update_singer_first_name():
    try:
        singer_id = request.args.get('singer_id')
        request_json = request.get_json()
        first_name = request_json['first_name']
        
        def update_singer(transaction):
            row_ct = transaction.execute_update(
                f"UPDATE Singers SET FirstName = '{first_name}' WHERE SingerId = {singer_id}"
            )

            print("{} record(s) updated.".format(row_ct))

        database.run_in_transaction(update_singer)
        return {"Success": True}, 200
    except Exception as e:
        return e


@app.route('/singer', methods=['DELETE'])
def delete_singer():
    try:
        singer_id = request.args.get('singer')
    
        def delete_singer(transaction):
            row_ct = transaction.execute_update(
                f"DELETE FROM Singers WHERE SingerId = {singer_id}"
            )
            print("{} record(s) deleted.".format(row_ct))

        database.run_in_transaction(delete_singer)
        return {"Success": True}, 200
    except Exception as e:
        return e

port = int(os.environ.get('PORT', 8080))
if __name__ == '__main__':
    app.run(threaded=True, host='0.0.0.0', port=port)

Ajouter des configurations de base de données

Pour vous connecter à Spanner de manière sécurisée, configurez l'application pour qu'elle utilise les identités de charge de travail. Cela permet à votre application d'agir en tant que compte de service et de disposer d'autorisations individuelles lors de l'accès à la base de données.

  1. Mettez à jour deployment.yaml. Ajoutez le code suivant à la fin du fichier (assurez-vous de conserver les retraits de tabulation de l'exemple ci-dessous)
      serviceAccountName: python-ksa
      nodeSelector:
        iam.gke.io/gke-metadata-server-enabled: "true" 

Déployer et valider l'application

  1. Dans le volet situé au bas de l'éditeur Cloud Shell, sélectionnez Cloud Code, puis Debug on Kubernetes en haut de l'écran.
  2. Une fois la compilation et les tests terminés, l'onglet "Résultat" indique Resource deployment/python-app status completed successfully et affiche l'URL "URL transférée depuis le service python-app : http://localhost:8080".
  3. Ajoutez quelques entrées.

Dans le terminal Cloud Shell, exécutez la commande ci-dessous.

curl -X POST http://localhost:8080/singer -H 'Content-Type: application/json' -d '{"first_name":"Cat","last_name":"Meow", "singer_id": 6}'
  1. Testez la requête GET en exécutant la commande ci-dessous dans le terminal.
curl -X GET http://localhost:8080/singer?singer_id=6
  1. Test de suppression : essayez maintenant de supprimer une entrée en exécutant la commande suivante. Modifiez la valeur de item-id si nécessaire.
curl -X DELETE http://localhost:8080/singer?singer_id=6
    This throws an error message
500 Internal Server Error

Identifier et résoudre le problème

  1. Passez en mode débogage et identifiez le problème. Voici quelques conseils :
  • Nous savons que quelque chose ne va pas avec la méthode DELETE, car elle ne renvoie pas le résultat souhaité. Vous définissez donc le point d'arrêt dans app.py dans la méthode delete_singer.
  • Exécutez l'exécution pas à pas et observez les variables à chaque étape pour voir les valeurs des variables locales dans la fenêtre de gauche.
  • Pour observer des valeurs spécifiques telles que singer_id et request.args, ajoutez ces variables à la fenêtre "Espion".
  1. Notez que la valeur attribuée à singer_id est None. Modifiez le code pour résoudre le problème.

L'extrait de code corrigé se présente comme suit.

@app.route('/delete-singer', methods=['DELETE', 'GET'])
def delete_singer():
    try:
        singer_id = request.args.get('singer_id')
  1. Une fois l'application redémarrée, réessayez de supprimer le fichier.
  2. Arrêtez la session de débogage en cliquant sur le carré rouge dans la barre d'outils de débogage 647213126d7a4c7b.png.

6. Nettoyage

Félicitations ! Dans cet atelier, vous avez créé une application Python à partir de zéro et l'avez configurée pour qu'elle fonctionne efficacement avec les conteneurs. Vous avez ensuite déployé et débogué votre application sur un cluster GKE distant en suivant le même flux de développement que celui utilisé dans les piles d'applications traditionnelles.

Pour effectuer le nettoyage une fois l'atelier terminé :

  1. Supprimer les fichiers utilisés dans l'atelier
cd ~ && rm -rf container-developer-workshop
  1. Supprimer le projet pour supprimer toute l'infrastructure et les ressources associées