Integrazione dei microservizi con Pub/Sub e GKE

In questo tutorial, eseguirai il deployment di un'app di condivisione delle foto in Google Kubernetes Engine (GKE), dove imparerai a utilizzare Pub/Sub per richiamare i processi a lunga esecuzione in modo asincrono. Scoprirai anche come utilizzare le notifiche Pub/Sub per Cloud Storage per aggiungere job laterali senza modificare il codice dell'applicazione dell'app. L'app è containerizzata da Cloud Build e archiviata in Container Registry. L'app utilizza Cloud Vision per rilevare immagini inappropriate.

Quando progetti un'app web in base a un'architettura di microservizi, decidi come suddividere le funzionalità dell'app in microservizi e come chiamare i microservizi dalla tua app. Per i servizi dispendiosi in termini di tempo, potresti voler utilizzare le chiamate di servizio asincrone. Questo tutorial è destinato a sviluppatori e architetti che intendono implementare microservizi in modo asincrono utilizzando tecnologie moderne, tra cui GKE e Pub/Sub.

I rapporti State of DevOps identificano le funzionalità che migliorano le prestazioni di distribuzione del software. Questo tutorial ti aiuterà con le funzionalità di Architettura.

Pattern architettonico per un'app di generazione miniature

Il seguente diagramma illustra uno scenario di esempio in cui un'app genera immagini in miniatura.

Architettura dell'app deprecata su Compute Engine.

Nel diagramma precedente, l'app riceve file immagine dai client e poi genera automaticamente le miniature. Attualmente, questa applicazione è implementata con istanze di macchine virtuali (VM) su Compute Engine e con spazio di archiviazione per i file di backend su Cloud Storage. Cloud Load Balancing distribuisce richieste a più VM.

Per ridurre l'overhead operativo per mantenere le VM, esegui la migrazione di questo sistema a una nuova architettura che non utilizza VM.

La nuova architettura, illustrata nel diagramma seguente, è composta da diversi servizi gestiti.

Architettura dell'app di cui è stato eseguito il deployment senza VM.

Nella nuova architettura, il client invia un'immagine all'applicazione direttamente in Cloud Storage. Quindi, le notifiche Pub/Sub inseriscono un messaggio nella coda del messaggio Pub/Sub. Il messaggio chiama un microservizio in esecuzione su GKE. Il microservizio recupera l'immagine da Cloud Storage, genera una miniatura e carica la miniatura su Cloud Storage.

Questa nuova architettura offre i seguenti vantaggi:

  • Scalabilità indipendente: nell'architettura originale, l'app in esecuzione su Compute Engine offre due funzionalità principali. Una riceve file e l'altra genera una miniatura dall'immagine originale. La ricezione di file caricati consuma la larghezza di banda della rete, mentre la generazione delle miniature è un'attività ad alta intensità della CPU. Le istanze di Compute Engine potrebbero esaurire le risorse CPU per generare immagini e avere ancora risorse di rete sufficienti a ricevere i file. Nella nuova architettura, queste attività vengono condivise da Cloud Storage e GKE, rendendo le attività scalabili in modo indipendente.
  • Utilizzo efficiente delle risorse: nell'architettura originale, lo scale out della migrazione delle istanze di Compute Engine consuma più risorse per eseguire i sistemi operativi. Con GKE puoi utilizzare in modo efficiente le risorse server che eseguono più container su pochi server (packing bin). Puoi scalare i container all'esterno e all'interno rapidamente, in modo che la nuova architettura sia in grado di gestire brevi picchi di carico elevato e scalare rapidamente al termine dell'operazione.
  • Aggiunta facile di nuove funzionalità: nell'architettura originale, se vuoi aggiungere funzionalità, devi eseguirne il deployment sulle stesse istanze di Compute Engine. Nella nuova architettura, puoi sviluppare un'app, ad esempio un mittente di posta, per ricevere una notifica quando viene generata una nuova miniatura. Pub/Sub può connettersi alla generazione di miniature e alle app di invio della posta in modo asincrono senza modificare il codice originale nelle istanze di Compute Engine.
  • Accoppiamento ridotto: nella vecchia architettura, un problema comune è l'accoppiamento temporaneo. Se un server di inoltro email non è attivo, l'app tenta di inviare una notifica ma non riesce. Questi processi sono caratterizzati dall'alto accoppiamento e un client potrebbe non ricevere una risposta positiva dall'applicazione. Nella nuova architettura, il client riceve una risposta corretta perché la generazione di una miniatura e l'invio di una notifica sono a basso accoppiamento.

Questa nuova architettura presenta i seguenti svantaggi:

  • Massimo sforzo per modernizzare l'app: la containerizzazione di un'app richiede tempo e impegno. La nuova architettura utilizza più servizi e richiede un approccio diverso all'osservabilità, che include modifiche al monitoraggio dell'app, al processo di deployment e alla gestione delle risorse.
  • Gestisci la duplicazione delle miniature sul lato app: Pub/Sub garantisce la consegna dei messaggi almeno una volta, il che significa che potrebbero essere inviati messaggi duplicati.

Informazioni sull'app Album fotografico

Il seguente diagramma mostra il design iniziale dell'app album fotografico.

Architettura dell'app di album fotografico.

Il diagramma precedente illustra come viene generata la miniatura:

  1. Un client carica un'immagine nell'app.
  2. L'app archivia l'immagine in Cloud Storage.
  3. Viene generata una richiesta per la miniatura.
  4. Il generatore di miniature genera la miniatura.
  5. La risposta correttamente viene inviata all'app Album fotografico.
  6. La risposta riuscita viene inviata al client e puoi trovare la miniatura in Cloud Storage.

Dopo aver eseguito il deployment dell'app, presenta i seguenti problemi:

  • L'app richiede molto tempo per generare la miniatura. Di conseguenza, i client stanno riscontrando molti timeout.
  • Il team di gestione dei servizi vuole rilevare contenuti caricati inappropriati. Tuttavia, il team addetto all'applicazione non ha abbastanza risorse per implementare la funzionalità in questo momento.

Il seguente diagramma estrae la generazione di miniature come servizio separato in modo asincrono.

Architettura di estrazione delle miniature.

Puoi utilizzare Pub/Sub per inviare richieste di servizio al servizio di generazione delle miniature. Questa nuova architettura rende la chiamata di servizio asincrona, in modo che una miniatura venga creata in background dopo che l'app invia la risposta a un client. Questo design permette anche di scalare il servizio di generazione delle miniature in modo che più job possano essere eseguiti in parallelo.

Obiettivi

  • Esegui il deployment di un'app di album di foto di esempio su GKE.
  • Effettuare chiamate di servizio asincrone dall'app.
  • Utilizza le notifiche Pub/Sub per Cloud Storage per attivare l'app quando viene caricato un nuovo file nel bucket Cloud Storage.
  • Utilizza Pub/Sub per eseguire più attività senza modificare l'app.

Costi

Questo tutorial utilizza i seguenti componenti fatturabili di Google Cloud:

Per generare una stima dei costi in base all'utilizzo previsto, utilizza il Calcolatore prezzi. I nuovi utenti di Google Cloud possono beneficiare di una prova gratuita.

Al termine di questo tutorial, puoi evitare una fatturazione continua eliminando le risorse che hai creato. Per scoprire di più, vedi Pulizia.

Prima di iniziare

  1. Accedi al tuo account Google Cloud. Se non conosci Google Cloud, crea un account per valutare le prestazioni dei nostri prodotti in scenari reali. I nuovi clienti ricevono anche 300 $di crediti gratuiti per l'esecuzione, il test e il deployment dei carichi di lavoro.
  2. Nella pagina del selettore dei progetti in Google Cloud Console, seleziona o crea un progetto Google Cloud.

    Vai al selettore progetti

  3. Assicurati che la fatturazione sia attivata per il tuo progetto Cloud. Scopri come verificare se la fatturazione è abilitata su un progetto.

  4. Abilita le API GKE, Cloud SQL, Cloud Build, and Cloud Vision.

    Abilita le API

  5. Nella pagina del selettore dei progetti in Google Cloud Console, seleziona o crea un progetto Google Cloud.

    Vai al selettore progetti

  6. Assicurati che la fatturazione sia attivata per il tuo progetto Cloud. Scopri come verificare se la fatturazione è abilitata su un progetto.

  7. Abilita le API GKE, Cloud SQL, Cloud Build, and Cloud Vision.

    Abilita le API

  8. Nella console, attiva Cloud Shell.

    Attiva Cloud Shell

    Nella parte inferiore della console, viene avviata una sessione di Cloud Shell e viene visualizzato un prompt della riga di comando. Cloud Shell è un ambiente shell con Google Cloud CLI già installato e con valori già impostati per il progetto corrente. L'inizializzazione della sessione può richiedere alcuni secondi.

Configurazione dell'ambiente

In questa sezione assegni le impostazioni predefinite per i valori utilizzati in tutto il documento. Se chiudi la sessione di Cloud Shell, perderai queste impostazioni dell'ambiente.

  1. In Cloud Shell, imposta il tuo progetto Cloud predefinito, sostituendo project-id con l'ID progetto di Google Cloud:

    gcloud config set project project-id
    
  2. Imposta l'area geografica predefinita di Compute Engine, sostituendo region con un'area geografica nelle vicinanze. Per ulteriori informazioni, consulta Aree geografiche e zone.

    gcloud config set compute/region region
    export REGION=region
    
  3. Imposta la zona Compute Engine predefinita, sostituendo zone con una zona vicina a te:

    gcloud config set compute/zone zone
    export ZONE=zone
    
  4. Scarica i file del tutorial e imposta la directory corrente:

    git clone https://github.com/GoogleCloudPlatform/gke-photoalbum-example
    cd gke-photoalbum-example
    

Creazione di un bucket Cloud Storage e caricamento dell'immagine in miniatura predefinita

  1. In Cloud Shell, crea un bucket Cloud Storage per archiviare le immagini e le miniature originali:

    export PROJECT_ID=$(gcloud config get-value project)
    gsutil mb -c regional -l ${REGION} gs://${PROJECT_ID}-photostore
    
  2. Carica il file della miniatura predefinita:

    gsutil cp ./application/photoalbum/images/default.png \
        gs://${PROJECT_ID}-photostore/thumbnails/default.png
    
    • Le immagini caricate sono archiviate nel seguente formato: gs://project-id-photostore/filename dove filename rappresenta il nome del file immagine che viene caricato.
    • Le miniature generate sono archiviate nel seguente formato: gs://project-id-photostore/thumbnails/filename.
    • L'immagine originale e la miniatura corrispondente hanno lo stesso valore filename, ma la miniatura viene archiviata nel bucket thumbnails.
    • Durante la creazione della miniatura, l'immagine in miniatura del segnaposto default.png viene visualizzata nell'app Album fotografico.

      Immagine miniatura segnaposto predefinita.

  3. Rendi pubblico il file della miniatura:

    gsutil acl ch -u AllUsers:R \
        gs://${PROJECT_ID}-photostore/thumbnails/default.png
    

Creazione di un'istanza Cloud SQL e un database MySQL

  1. In Cloud Shell, crea l'istanza Cloud SQL:

    gcloud sql instances create photoalbum-db --region=${REGION} \
        --database-version=MYSQL_5_7
    
  2. Recupera il nome della connessione:

    gcloud sql instances describe photoalbum-db \
        --format="value(connectionName)"
    

    Prendi nota del nome perché lo utilizzerai più tardi nel tutorial.

  3. Imposta la password per l'utente root@% MySQL:

    gcloud sql users set-password root --host=% --instance=photoalbum-db \
        --password=password
    

    Sostituisci password con una password sicura per l'utente root@%.

  4. Connettiti all'istanza Cloud SQL:

    gcloud sql connect photoalbum-db --user=root --quiet
    

    Quando richiesto, inserisci il password da te impostato nel passaggio precedente.

  5. Crea un database denominato photo_db, dove l'utente è appuser con la password pas4appuser:

    create database photo_db;
    grant all privileges on photo_db.* to appuser@"%" \
        identified by 'pas4appuser' with grant option;
    
  6. Conferma il risultato e esci da MySQL:

    show databases;
    select user from mysql.user;
    exit
    

    Nell'output, conferma che il database photo_db e l'utente appuser siano creati:

    MySQL [(none)]> show databases;
    +--------------------+
    | Database           |
    +--------------------+
    | information_schema |
    | mysql              |
    | performance_schema |
    | photo_db           |
    | sys                |
    +--------------------+
    5 rows in set (0.16 sec)
    
    MySQL [(none)]> select user from mysql.user;
    +-----------+
    | user      |
    +-----------+
    | appuser   |
    | root      |
    | mysql.sys |
    +-----------+
    3 rows in set (0.16 sec)
    
    MySQL [(none)]> exit
    Bye
    

Creazione di un argomento Pub/Sub e una sottoscrizione

  1. In Cloud Shell, crea un argomento Pub/Sub denominato thumbnail-service:

    gcloud pubsub topics create thumbnail-service
    

    L'app di album di foto invia richieste al servizio di generazione di miniature pubblicando un messaggio sull'argomento thumbnail-service.

  2. Crea una sottoscrizione Pub/Sub chiamata thumbnail-workers:

    gcloud pubsub subscriptions create --topic thumbnail-service thumbnail-workers
    

    Il servizio di generazione delle miniature riceve richieste dall'abbonamento thumbnail-workers.

Crea un cluster GKE

  1. In Cloud Shell, crea un cluster GKE con l'autorizzazione per chiamare le API:

    gcloud container clusters create "photoalbum-cluster" \
        --scopes "https://www.googleapis.com/auth/cloud-platform" \
        --num-nodes "5"
    
  2. Configura le credenziali di accesso in modo da poter gestire il cluster utilizzando il comando kubectl nei passaggi successivi:

    gcloud container clusters get-credentials photoalbum-cluster
    
  3. Mostra l'elenco dei nodi:

    kubectl get nodes
    

    Nell'output, conferma che ci siano cinque nodi con l'elemento STATUS di Ready:

    NAME                                                STATUS    ROLES     AGE       VERSION
    gke-photoalbum-cluster-default-pool-0912a91a-24vt   Ready     <none>    6m        v1.9.7-gke.6
    gke-photoalbum-cluster-default-pool-0912a91a-5h1n   Ready     <none>    6m        v1.9.7-gke.6
    gke-photoalbum-cluster-default-pool-0912a91a-gdm9   Ready     <none>    6m        v1.9.7-gke.6
    gke-photoalbum-cluster-default-pool-0912a91a-swv6   Ready     <none>    6m        v1.9.7-gke.6
    gke-photoalbum-cluster-default-pool-0912a91a-thv8   Ready     <none>    6m        v1.9.7-gke.6
    

Creare immagini per l'app

  1. In un editor di testo, apri il file application/photoalbum/src/auth_decorator.py e aggiorna il nome utente e la password:

    USERNAME = 'username'
    PASSWORD = 'passw0rd'
    
  2. In Cloud Shell, crea un'immagine per l'app Album fotografico utilizzando il servizio Cloud Build:

    gcloud builds submit ./application/photoalbum -t \
        gcr.io/${PROJECT_ID}/photoalbum-app
    
  3. Crea un'immagine per il servizio di generazione delle miniature thumbnail-worker utilizzando il servizio Cloud Build:

    gcloud builds submit ./application/thumbnail -t \
        gcr.io/${PROJECT_ID}/thumbnail-worker
    

Deployment dell'app Album fotografico

  1. In Cloud Shell, aggiorna i manifest del deployment Kubernetes per l'album di foto e il generatore di miniature con i valori del tuo ambiente:

    connection_name=$(gcloud sql instances describe photoalbum-db \
        --format "value(connectionName)")
    
    digest_photoalbum=$(gcloud container images describe \
        gcr.io/${PROJECT_ID}/photoalbum-app:latest --format \
        "value(image_summary.digest)")
    
    sed -i.bak -e "s/\[PROJECT_ID\]/${PROJECT_ID}/" \
        -e "s/\[CONNECTION_NAME\]/${connection_name}/" \
        -e "s/\[DIGEST\]/${digest_photoalbum}/" \
        config/photoalbum-deployment.yaml
    
    digest_thumbnail=$(gcloud container images describe \
        gcr.io/${PROJECT_ID}/thumbnail-worker:latest --format \
        "value(image_summary.digest)")
    
    sed -i.bak -e "s/\[PROJECT_ID\]/${PROJECT_ID}/" \
        -e "s/\[CONNECTION_NAME\]/${connection_name}/" \
        -e "s/\[DIGEST\]/${digest_thumbnail}/" \
            config/thumbnail-deployment.yaml
    
  2. Crea le risorse di deployment per lanciare l'app Album fotografico e il servizio di generazione miniature:

    kubectl create -f config/photoalbum-deployment.yaml
    kubectl create -f config/thumbnail-deployment.yaml
    
  3. Crea una risorsa di servizio per assegnare un indirizzo IP esterno all'app:

    kubectl create -f config/photoalbum-service.yaml
    
  4. Verifica i risultati dei pod:

    kubectl get pods
    

    Nell'output, conferma che ci sono tre pod per ogni elemento photoalbum-app e thumbail-worker con STATUS di Running:

    NAME                                READY     STATUS    RESTARTS   AGE
    photoalbum-app-555f7cbdb7-cp8nw     2/2       Running   0          2m
    photoalbum-app-555f7cbdb7-ftlc6     2/2       Running   0          2m
    photoalbum-app-555f7cbdb7-xsr4b     2/2       Running   0          2m
    thumbnail-worker-86bd95cd68-728k5   2/2       Running   0          2m
    thumbnail-worker-86bd95cd68-hqxqr   2/2       Running   0          2m
    thumbnail-worker-86bd95cd68-xnxhc   2/2       Running   0          2m
    
  5. Verifica i risultati dei Servizi:

    kubectl get services
    

    Nell'output, verifica che nella colonna EXTERNAL-IP sia presente un indirizzo IP esterno per photoalbum-service. La configurazione potrebbe richiedere alcuni minuti.

    NAME                 TYPE           CLUSTER-IP      EXTERNAL-IP       PORT(S)        AGE
    kubernetes           ClusterIP      10.23.240.1     <none>            443/TCP        20m
    photoalbum-service   LoadBalancer   10.23.253.241   146.148.111.115   80:32657/TCP   2m
    

    Prendi nota dell'indirizzo IP esterno perché viene utilizzato più avanti in questo tutorial. In questo esempio è 146.148.111.115.

Test dell'app album fotografico

  1. Per accedere all'applicazione di cui hai eseguito il deployment in un browser web, vai al seguente URL e inserisci il nome utente e la password che hai configurato in precedenza:

    http://external-ip
    

    Sostituisci external-ip con l'indirizzo IP che hai copiato nel passaggio precedente.

  2. Per caricare un file immagine a tua scelta, fai clic su Carica. Sullo schermo viene visualizzato il segnaposto della miniatura.

    Miniatura segnaposto in attesa che il servizio generi una miniatura univoca.

    In background, il servizio di generazione delle miniature crea una miniatura dell'immagine caricata. Per visualizzare la miniatura generata, fai clic su Aggiorna. L'API Cloud Vision aggiunge etichette alle immagini che rileva.

    Miniatura con etichette immagine associate.

    Per visualizzare l'immagine originale, fai clic sulla miniatura.

Aggiunta di una funzionalità di rilevamento delle immagini inappropriata

L'immagine seguente illustra come utilizzare le notifiche Pub/Sub per Cloud Storage per attivare un servizio che rileva contenuti inappropriati. Questa funzionalità sfoca l'immagine quando un nuovo file con contenuti inappropriati viene archiviato nel bucket Cloud Storage.

Architettura della funzionalità dei contenuti inappropriati.

Nell'immagine precedente, il servizio utilizza la funzionalità di rilevamento di SafeSearch per rilevare i contenuti inappropriati all'interno di un'immagine.

Crea un argomento, una sottoscrizione e una notifica Pub/Sub

  1. In Cloud Shell, crea un argomento Pub/Sub denominato safeimage-service:

    gcloud pubsub topics create safeimage-service
    
  2. Crea una sottoscrizione Pub/Sub chiamata safeimage-workers:

    gcloud pubsub subscriptions create --topic safeimage-service \
        safeimage-workers
    
  3. Configura una notifica Pub/Sub in modo che un messaggio venga inviato all'argomento safeimage-service quando viene caricato un nuovo file nel bucket Cloud Storage:

    gsutil notification create -t safeimage-service -f json \
        gs://${PROJECT_ID}-photostore
    

Crea e sottoponi a deployment l'immagine worker

  1. In Cloud Shell, crea un'immagine container per l'abbonamento a safeimage-workers utilizzando Cloud Build:

    gcloud builds submit ./application/safeimage \
        -t gcr.io/${PROJECT_ID}/safeimage-worker
    
  2. Aggiorna i manifest del deployment Kubernetes per il servizio di immagini sicure con l'ID progetto Cloud, il nome della connessione Cloud SQL e i digest di immagini container:

    digest_safeimage=$(gcloud container images describe \
        gcr.io/${PROJECT_ID}/safeimage-worker:latest --format \
        "value(image_summary.digest)")
    sed -i.bak -e "s/\[PROJECT_ID\]/${PROJECT_ID}/" \
        -e "s/\[CONNECTION_NAME\]/${connection_name}/" \
        -e "s/\[DIGEST\]/${digest_safeimage}/" \
        config/safeimage-deployment.yaml
    

Creare una risorsa di deployment

  1. Crea una risorsa di deployment denominata safeimage-deployment per eseguire il deployment dell'argomento safeimage-service:

    kubectl create -f config/safeimage-deployment.yaml
    
  2. Verifica i risultati:

    kubectl get pods
    

    Nell'output, conferma che ci siano tre pod di safeimage-worker con STATUS di Running.

    NAME                                READY     STATUS    RESTARTS   AGE
    photoalbum-app-555f7cbdb7-cp8nw     2/2       Running   0          30m
    photoalbum-app-555f7cbdb7-ftlc6     2/2       Running   0          30m
    photoalbum-app-555f7cbdb7-xsr4b     2/2       Running   8          30m
    safeimage-worker-7dc8c84f54-6sqzs   1/1       Running   0          2m
    safeimage-worker-7dc8c84f54-9bskw   1/1       Running   0          2m
    safeimage-worker-7dc8c84f54-b7gtp   1/1       Running   0          2m
    thumbnail-worker-86bd95cd68-9wrpv   2/2       Running   0          30m
    thumbnail-worker-86bd95cd68-kbhsn   2/2       Running   2          30m
    thumbnail-worker-86bd95cd68-n4rj7   2/2       Running   0          30m
    

Testare la funzionalità di rilevamento delle immagini inappropriata

In questa sezione, carichi un'immagine di prova per verificare che la funzionalità Rilevamento di SafeSearch sfochi un'immagine inappropriata. L'immagine di prova è l'immagine di una ragazza travestita da zombie (con licenza una licenza CC0 da Pixaby).

  1. Scarica l'immagine di prova.
  2. Per caricare l'immagine, vai a http://external-ip, quindi fai clic su Carica.
  3. Fai clic su Aggiorna. L'app visualizza una miniatura sfocata.

    Miniatura sfocata.

    Per vedere che anche l'immagine caricata è sfocata, fai clic sulla miniatura.

Esegui la pulizia

Il modo più semplice per eliminare la fatturazione è eliminare il progetto Cloud che hai creato per il tutorial. In alternativa, puoi eliminare le singole risorse.

Elimina il progetto

  1. Nella console, vai alla pagina Gestisci risorse.

    Vai a Gestisci risorse

  2. Nell'elenco dei progetti, seleziona il progetto da eliminare, quindi fai clic su Elimina.
  3. Nella finestra di dialogo, digita l'ID del progetto e fai clic su Chiudi per eliminare il progetto.

Eliminare le singole risorse

Anziché eliminare il progetto, puoi eliminare le risorse create in questo tutorial.

  1. Elimina risorse da GKE:

    kubectl delete -f config/safeimage-deployment.yaml
    kubectl delete -f config/photoalbum-service.yaml
    kubectl delete -f config/thumbnail-deployment.yaml
    kubectl delete -f config/photoalbum-deployment.yaml
    
  2. Elimina il cluster da GKE:

    gcloud container clusters delete photoalbum-cluster --quiet
    
  3. Elimina immagini da Container Registry:

    gcloud container images delete safeimage-worker
    gcloud container images delete thumbnail-worker
    gcloud container images delete photoalbum-app
    
  4. Elimina sottoscrizioni e argomenti da Pub/Sub:

    gcloud pubsub subscriptions delete safeimage-workers
    gcloud pubsub topics delete safeimage-service
    gcloud pubsub subscriptions delete thumbnail-workers
    gcloud pubsub topics delete thumbnail-service
    
  5. Elimina l'istanza Cloud SQL:

    gcloud sql instances delete photoalbum-db --quiet
    
  6. Elimina il bucket Cloud Storage:

    gsutil rm -r gs://${PROJECT_ID}-photostore
    gsutil rm -r gs://${PROJECT_ID}_cloudbuild
    
  7. Elimina i file:

    cd ..
    rm -rf gke-photoalbum-example
    

Passaggi successivi