1. Introduzione
Ultimo aggiornamento: 01/11/2024
Come si modernizza una vecchia applicazione PHP su Google Cloud?
(📽️ guarda un video introduttivo di 7 minuti per questo codelab)
È comune avere applicazioni legacy in esecuzione on-premise che devono essere modernizzate. Ciò significa renderli scalabili, sicuri e implementabili in ambienti diversi.
In questo workshop imparerai a:
- Containerizza l'applicazione PHP.
- Passa a un servizio di database gestito ( Cloud SQL).
- Esegui il deployment su Cloud Run (è l'alternativa zero-ops a GKE/Kubernetes).
- Proteggi l'applicazione con Identity and Access Management (IAM) e Secret Manager.
- Definisci una pipeline CI/CD tramite Cloud Build. Cloud Build può essere connesso al tuo repository Git ospitato su provider Git popolari come GitHub o GitLab e può essere attivato a ogni push su main, ad esempio.
- Ospita le immagini dell'applicazione su Cloud Storage. Ciò si ottiene tramite il montaggio e non è necessario alcun codice per modificare l'app.
- Introduci la funzionalità di AI generativa tramite Gemini, orchestrata tramite Cloud Functions (serverless).
- Familiarizza con gli SLO e con il funzionamento della tua app appena aggiornata.
Seguendo questi passaggi, puoi modernizzare gradualmente la tua applicazione PHP, migliorandone la scalabilità, la sicurezza e la flessibilità di deployment. Inoltre, il passaggio a Google Cloud ti consente di sfruttare la sua potente infrastruttura e i suoi servizi per garantire che la tua applicazione funzioni senza problemi in un ambiente cloud-native.
Riteniamo che ciò che imparerai seguendo questi semplici passaggi possa essere applicato alla tua applicazione e alla tua organizzazione con una lingua/stack diversa e casi d'uso diversi.
Informazioni sull'app
L'applicazione ( codice, con licenza MIT) di cui eseguirai il fork è un'applicazione PHP 5.7 di base con autenticazione MySQL. L'idea principale dell'app è fornire una piattaforma in cui gli utenti possono caricare foto e gli amministratori hanno la possibilità di taggare le immagini inappropriate. L'applicazione ha due tabelle:
- Utenti. Viene precompilato con gli amministratori. Nuove persone possono registrarsi.
- Immagini. Include alcune immagini di esempio. Gli utenti che hanno eseguito l'accesso possono caricare nuove immagini. Aggiungeremo un po' di magia.
Il tuo obiettivo
Vogliamo modernizzare la vecchia applicazione per averla in Google Cloud. Sfrutteremo i suoi strumenti e servizi per migliorare la scalabilità, aumentare la sicurezza, automatizzare la gestione dell'infrastruttura e integrare funzionalità avanzate come l'elaborazione delle immagini, il monitoraggio e l'archiviazione dei dati utilizzando servizi come Cloud SQL, Cloud Run, Cloud Build, Secret Manager e altri ancora.

Ancora più importante, vogliamo farlo passo dopo passo, in modo che tu possa imparare il ragionamento alla base di ogni passaggio e, di solito, ogni passaggio apre nuove possibilità per quelli successivi (ad esempio, i moduli 2 -> 3 e 6 -> 7).
Non sei ancora convinto? Guarda questo video di 7 minuti su YouTube.
Che cosa ti serve
- Un computer con un browser, connesso a internet.
- Alcuni crediti GCP. Consulta il passaggio successivo.
- Utilizzerai Cloud Shell. È dotato di tutti i comandi preinstallati necessari e di un IDE.
- Account GitHub. Ti serve per creare un ramo del codice originale 🧑🏻💻 gdgpescara/app-mod-workshop con il tuo repository Git. Questo è necessario per avere la tua pipeline CI/CD (commit automatico -> build -> deployment)
Esempi di soluzioni sono disponibili qui:
- Repository dell'autore: https://github.com/Friends-of-Ricc/app-mod-workshop
- Il repository originale del workshop,nelle cartelle
.solutions/, per capitolo.
Questo workshop è progettato per essere completato su Cloud Shell (in un browser).
Tuttavia, può essere tentato anche dal computer locale.
2. Configurazione del credito e Fork

Riscatta il credito Google Cloud e configura l'ambiente Google Cloud [facoltativo]
Per partecipare a questo workshop, devi disporre di un account di fatturazione con del credito. Se hai già la tua fatturazione, puoi saltare questo passaggio.
Crea un nuovo account Google Gmail (*) da collegare al tuo credito GCP. Chiedi all'insegnante il link per riscattare il credito GCP o utilizza i crediti qui: bit.ly/PHP-Amarcord-credits .
Accedi con l'account appena creato e segui le istruzioni.

(
) Perché ho bisogno di un nuovo account Gmail?*
Abbiamo notato che alcune persone non hanno superato il codelab perché il loro account (in particolare le email di lavoro o degli studenti) aveva già avuto accesso a GCP e aveva norme dell'organizzazione che ne limitavano la possibilità di farlo. Ti consigliamo di creare un nuovo account Gmail o di utilizzare un account Gmail esistente (gmail.com) che non sia mai stato esposto a GCP.
Fai clic sul pulsante per riscattare il credito.

Compila il seguente modulo con il tuo nome e cognome e accetta i Termini e condizioni.
Potresti dover attendere qualche secondo prima che l'account di fatturazione venga visualizzato qui: https://console.cloud.google.com/billing
Al termine, apri la console Google Cloud e crea un nuovo progetto facendo clic sul selettore di progetti nel menu a discesa in alto a sinistra, dove viene visualizzato "Nessuna organizzazione". Vedi di seguito

Crea un nuovo progetto se non ne hai uno, come mostrato nello screenshot di seguito. Nell'angolo in alto a destra è presente l'opzione "NUOVO PROGETTO".

Assicurati di collegare il nuovo progetto all'account di fatturazione della prova senza costi di GCP nel seguente modo.

Ora puoi utilizzare Google Cloud Platform. Se sei un principiante o vuoi fare tutto in un ambiente cloud, puoi accedere a Cloud Shell e al relativo editor tramite il seguente pulsante nell'angolo in alto a sinistra, come mostrato di seguito.

Assicurati che il nuovo progetto sia selezionato in alto a sinistra:
Non selezionato (errato):

Selezionato (buono):

Fork dell'app da GitHub
- Vai all'app demo: https://github.com/gdgpescara/app-mod-workshop
- Fai clic su 🍴 Fork.
- Se non hai un account GitHub, devi crearne uno nuovo.
- Modifica gli elementi come preferisci.

- Clona il codice dell'app utilizzando
git clonehttps://github.com/YOUR-GITHUB-USER/YOUR-REPO-NAME
- Apri la cartella del progetto clonato con il tuo editor preferito. Se scegli Cloud Shell, puoi farlo facendo clic su "Apri editor" come mostrato di seguito.

Come mostrato nella figura seguente, hai tutto ciò che ti serve con Google Cloud Shell Editor

Puoi farlo visivamente facendo clic su "Apri cartella" e selezionando la cartella, probabilmente app-mod-workshop nella cartella Home.
3. Modulo 1: crea un'istanza SQL
Crea l'istanza Google Cloud SQL
La nostra app PHP si connetterà a un database MySQL e pertanto dobbiamo replicarlo su Google Cloud per una migrazione senza problemi. Cloud SQL è la soluzione ideale perché ti consente di eseguire un database MySQL completamente gestito nel cloud. Ecco i passaggi da seguire:
- Vai alla pagina Cloud SQL: https://console.cloud.google.com/sql/instances
- Fai clic su "Crea istanza".
- Abilita l'API (se necessario). L'operazione potrebbe richiedere alcuni secondi.
- Scegli MySQL.
- (Stiamo cercando di offrirti la versione più economica, in modo che duri più a lungo):
- Versione: Enterprise
- Preset: development (we tried Sandbox and didn't work for us)
- Mysql Ver: 5.7 (wow, un tuffo nel passato!)
- ID istanza: scegli
appmod-phpapp(se lo modifichi, ricordati di modificare di conseguenza anche gli script e le soluzioni futuri). - Password: quella che preferisci, ma annotala come CLOUDSQL_INSTANCE_PASSWORD
- Regione: mantieni la stessa scelta per il resto dell'app (ad es. Milano =
europe-west8) - Disponibilità a livello di zona: una sola zona (risparmiamo denaro per la demo)
Fai clic sul pulsante Crea istanza per eseguire il deployment del database Cloud SQL. ⌛ L'operazione richiede circa 10 minuti⌛. Nel frattempo, continua a leggere la documentazione. Puoi anche iniziare a risolvere il modulo successivo ("Containerizza la tua app PHP") perché non ha dipendenze da questo modulo nella prima parte (finché non correggi la connessione al database).
Nota: Questa istanza dovrebbe costare circa 7 € al giorno. Assicurati di riprodurlo dopo il workshop.
Crea il database e l'utente image_catalog in Cloud SQL
Il progetto dell'app include una cartella db/ che contiene due file SQL:
- 01_schema.sql : contiene il codice SQL per creare due tabelle contenenti i dati di utenti e immagini.
- 02_seed.sql: contiene il codice SQL per inserire i dati nelle tabelle create in precedenza.
Questi file verranno utilizzati in un secondo momento, una volta creato il database image_catalog. Per farlo, segui questi passaggi:
- Apri l'istanza e fai clic sulla scheda Database:
- fai clic su "Crea database".
- chiamalo
image_catalog(come nella configurazione dell'app PHP).

Poi creiamo l'utente del database. In questo modo possiamo autenticarci nel database image_catalog.
- Ora fai clic sulla scheda Utenti.
- Fai clic su "Aggiungi account utente".
- Utente: creiamone uno:
- Nome utente:
appmod-phpapp-user - Password: scegli qualcosa che puoi ricordare o fai clic su "Genera".
- Mantieni "Consenti qualsiasi host (%)".
- fai clic su AGGIUNGI.
Apri il database degli IP noti.
Tutti i database in Cloud SQL sono "isolati" per impostazione predefinita. Devi configurare in modo esplicito una rete da cui accedere.
- Fai clic sull'istanza.
- Apri il menu "Connessioni".
- Fai clic sulla scheda "Networking".
- Fai clic nella sezione "Reti autorizzate". Ora aggiungi una rete (ad es. una subnet).
- Per ora, scegliamo impostazioni rapide ma NON SICURE per consentire il funzionamento dell'app. In seguito potresti voler limitare l'accesso agli indirizzi IP di cui ti fidi:
- Nome: "Everyone in the world - INSECURE".
- Rete: "
0.0.0.0/0"(nota: questa è la parte NON SICURA!) - Fai clic su FINE.
- Fai clic su Salva.
Il risultato dovrebbe essere simile a questo:

Nota: Questa soluzione è un buon compromesso per completare il workshop in O(ore). Tuttavia, consulta il documento SECURITY per proteggere la tua soluzione per la produzione.
È il momento di testare la connessione al database.
Vediamo se l'utente image_catalog che abbiamo creato in precedenza funziona.
Accedi a "Cloud SQL Studio" all'interno dell'istanza e inserisci il database, l'utente e la password da autenticare come mostrato di seguito:

Ora puoi aprire l'editor SQL e procedere alla sezione successiva.
Importare il database dal codebase
Utilizza l'editor SQL per importare le tabelle image_catalog con i relativi dati. Copia il codice SQL dai file nel repository ( 01_schema.sql e poi 02_seed.sql) ed eseguili uno dopo l'altro in ordine sequenziale.
Dopodiché, dovresti visualizzare due tabelle in image_catalog, ovvero users e images, come mostrato di seguito:

Puoi testarlo eseguendo il seguente comando nell'editor: select * from images;
Assicurati inoltre di annotare l'indirizzo IP pubblico dell'istanza Cloud SQL, ti servirà in seguito. Per ottenere l'IP, vai alla pagina principale dell'istanza Cloud SQL nella pagina Panoramica. (Panoramica > Connettiti a questa istanza > Indirizzo IP pubblico).
4. Modulo 2: containerizza la tua app PHP

Vogliamo creare questa app per il cloud.
Ciò significa inserire il codice in un file ZIP che contenga tutte le informazioni per eseguirlo nel cloud.
Esistono alcuni modi per confezionarlo:
- Docker. Molto popolare, ma piuttosto complesso da configurare correttamente.
- Buildpack. Meno popolare, ma tende a "indovinare automaticamente" cosa creare e cosa eseguire. Spesso funziona e basta.
Nel contesto di questo workshop, presupporremo che tu utilizzi Docker.
Se hai scelto di utilizzare Cloud Shell, è il momento di riaprirla (fai clic in alto a destra nella console Cloud).

Si aprirà una comoda shell nella parte inferiore della pagina, in cui dovresti aver creato un fork del codice nel passaggio di configurazione.

Docker
Se vuoi avere il controllo, questa è la soluzione giusta per te. Ciò ha senso quando devi configurare librerie specifiche e inserire determinati comportamenti non ovvi (un chmod negli upload, un eseguibile non standard nella tua app e così via).
Poiché vogliamo eseguire il deployment della nostra applicazione containerizzata su Cloud Run, consulta la seguente documentazione. Come faresti il backporting da PHP 8 a PHP 5.7? Magari puoi usare Gemini per farlo. In alternativa, puoi utilizzare questa versione precompilata:
# Use the official PHP image: https://hub.docker.com/_/php
FROM php:5.6-apache
# Configure PHP for Cloud Run.
# Precompile PHP code with opcache.
# Install PHP's extension for MySQL
RUN docker-php-ext-install -j "$(nproc)" opcache mysqli pdo pdo_mysql && docker-php-ext-enable pdo_mysql
RUN set -ex; \
{ \
echo "; Cloud Run enforces memory & timeouts"; \
echo "memory_limit = -1"; \
echo "max_execution_time = 0"; \
echo "; File upload at Cloud Run network limit"; \
echo "upload_max_filesize = 32M"; \
echo "post_max_size = 32M"; \
echo "; Configure Opcache for Containers"; \
echo "opcache.enable = On"; \
echo "opcache.validate_timestamps = Off"; \
echo "; Configure Opcache Memory (Application-specific)"; \
echo "opcache.memory_consumption = 32"; \
} > "$PHP_INI_DIR/conf.d/cloud-run.ini"
# Copy in custom code from the host machine.
WORKDIR /var/www/html
COPY . .
# Setup the PORT environment variable in Apache configuration files: https://cloud.google.com/run/docs/reference/container-contract#port
ENV PORT=8080
# Tell Apache to use 8080 instead of 80.
RUN sed -i 's/80/${PORT}/g' /etc/apache2/sites-available/000-default.conf /etc/apache2/ports.conf
# Note: This is quite insecure and opens security breaches. See last chapter for hardening ideas.
# Uncomment at your own risk:
#RUN chmod 777 /var/www/html/uploads/
# Configure PHP for development.
# Switch to the production php.ini for production operations.
# RUN mv "$PHP_INI_DIR/php.ini-production" "$PHP_INI_DIR/php.ini"
# https://github.com/docker-library/docs/blob/master/php/README.md#configuration
RUN mv "$PHP_INI_DIR/php.ini-development" "$PHP_INI_DIR/php.ini"
# Expose the port
EXPOSE 8080
L'ultima versione di Dockerfile è disponibile qui.
Per testare la nostra applicazione localmente, dobbiamo modificare il file config.php in modo che la nostra app PHP si connetta al database MySQL disponibile su Google CloudSQL. In base a ciò che hai configurato in precedenza, compila gli spazi vuoti:
<?php
// Database configuration
$db_host = '____________';
$db_name = '____________';
$db_user = '____________';
$db_pass = '____________';
try {
$pdo = new PDO("mysql:host=$db_host;dbname=$db_name", $db_user, $db_pass);
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
} catch (PDOException $e) {
die("Errore di connessione: " . $e->getMessage());
}
session_start();
?>
DB_HOSTè l'indirizzo IP pubblico di Cloud SQL, che puoi trovare nella console SQL:

DB_NAMEdeve rimanere invariato:image_catalogDB_USERdeve essereappmod-phpapp-userDB_PASSè un'opzione che hai scelto. Configuralo tra virgolette singole ed esegui l'escape in base alle esigenze.
Inoltre, non esitare a tradurre in inglese i pochi brani in 🇮🇹 italiano con l'aiuto di Gemini.
Ok, ora che hai Dockerfile e hai configurato la tua app PHP per connettersi al tuo database, proviamo.
Installa Docker se non lo hai ancora fatto ( link). Non è necessario se utilizzi Cloud Shell (non è fantastico?).
Ora prova a creare ed eseguire la tua app PHP containerizzata con i comandi docker build e run appropriati.
# Build command - don't forget the final . This works if Dockerfile is inside the code folder:
$ docker build -t my-php-app-docker .
# Local Run command: most likely ports will be 8080:8080
$ docker run -it -p <CONTAINER_PORT>:<LOCAL_MACHINE_PORT> my-php-app-docker
Se tutto funziona, dovresti visualizzare la seguente pagina web quando ti connetti all'host locale. Ora l'app è in esecuzione sulla porta 8080, fai clic sull'icona "Anteprima web" (un browser con un occhio) e poi su Anteprima sulla porta 8080 (o "Cambia porta" per qualsiasi altra porta).

Testare il risultato nel browser
Ora la tua applicazione dovrebbe avere un aspetto simile a questo:

Se accedi con Admin/admin123, dovresti vedere qualcosa di simile.

Ottimo! A parte il testo in italiano, funziona! 🎉🎉🎉
Se la dockerizzazione è buona, ma le credenziali del database sono errate, potresti visualizzare un messaggio simile a questo:

Riprova, ci sei quasi.
Salvataggio in Artifact Registry [facoltativo]
A questo punto, dovresti avere un'applicazione PHP containerizzata funzionante pronta per essere implementata nel cloud. Successivamente, abbiamo bisogno di uno spazio nel cloud per archiviare la nostra immagine Docker e renderla accessibile per il deployment nei servizi Google Cloud come Cloud Run. Questa soluzione di archiviazione si chiama Artifact Registry, un servizio Google Cloud completamente gestito progettato per archiviare gli artefatti delle applicazioni, tra cui immagini container Docker, pacchetti Maven, moduli npm e altro ancora.
Creiamo un repository in Google Cloud Artifact Registry utilizzando il pulsante appropriato.

Scegli un nome valido, il formato e la regione adatti per archiviare gli artefatti.

Torna all'ambiente di sviluppo locale e invia l'immagine container dell'app al repository Artifact Registry appena creato. Per farlo, completa i seguenti comandi.
- docker tag SOURCE_IMAGE[:TAG] TARGET_IMAGE[:TAG]
- docker push TARGET_IMAGE[:TAG]
Il risultato dovrebbe essere simile a quello dello screenshot seguente.

Evviva 🎉🎉🎉 puoi passare al livello successivo. Prima di allora, dedica 2 minuti a provare a caricare, accedere e uscire e a familiarizzare con gli endpoint dell'app.Ti serviranno in seguito.
Possibili errori
Se ricevi errori di containerizzazione, prova a utilizzare Gemini per spiegare e correggere l'errore, fornendo:
- Il Dockerfile attuale
- L'errore ricevuto
- [se necessario] il codice PHP in esecuzione.
Autorizzazioni di caricamento. Prova anche l'endpoint /upload.php e carica un'immagine. Potresti visualizzare l'errore riportato di seguito. In questo caso, devi risolvere un problema relativo a chmod/chown in Dockerfile.
Avviso: move_uploaded_file(uploads/image (3).png): impossibile aprire lo stream: Permission denied in /var/www/html/upload.php on line 11
PDOException "could not find driver" (o "Errore di connessione: could not find driver"). Assicurati che il Dockerfile contenga le librerie PDO appropriate per MySQL (pdo_mysql) per connettersi al database. Prendi spunto dalle soluzioni riportate qui.
Impossibile inoltrare la richiesta a un backend. Impossibile connettersi a un server sulla porta 8080. Ciò significa che probabilmente stai esponendo la porta sbagliata. Assicurati di esporre la porta da cui Apache/Nginx vengono effettivamente pubblicati. Non è una cosa banale. Se possibile, prova a impostare la porta 8080 (semplifica la vita con Cloud Run). Se vuoi mantenere la porta 80 (ad esempio perché Apache lo richiede), utilizza un comando diverso per eseguirlo:
$ docker run -it -p 8080:80 # force 80
# Use the PORT environment variable in Apache configuration files.
# https://cloud.google.com/run/docs/reference/container-contract#port
RUN sed -i 's/80/${PORT}/g' /etc/apache2/sites-available/000-default.conf /etc/apache2/ports.conf
5. Modulo 3: esegui il deployment dell'app in Cloud Run

Perché Cloud Run?
Domanda lecita. Anni fa, avresti sicuramente scelto Google App Engine.
In parole semplici, oggi Cloud Run ha uno stack tecnologico più recente, è più facile da implementare, più economico e si riduce a 0 quando non lo utilizzi. Grazie alla flessibilità di eseguire qualsiasi container stateless e all'integrazione con vari servizi Google Cloud, è ideale per il deployment di microservizi e applicazioni moderne con overhead minimo e massima efficienza.
Più nello specifico, Cloud Run è una piattaforma completamente gestita da Google Cloud che consente di eseguire applicazioni containerizzate stateless in un ambiente serverless. Gestisce automaticamente tutta l'infrastruttura, scalando da zero per soddisfare il traffico in entrata e riducendosi quando è inattivo, il che lo rende conveniente ed efficiente. Cloud Run supporta qualsiasi linguaggio o libreria, purché sia incluso in un container, consentendo una grande flessibilità nello sviluppo. Si integra bene con altri servizi Google Cloud ed è adatto alla creazione di microservizi, API, siti web e applicazioni basate su eventi senza la necessità di gestire l'infrastruttura server.
Prerequisiti
Per completare questa attività, devi aver installato gcloud nella tua macchina locale. In caso contrario, consulta le istruzioni qui. Se invece utilizzi Google Cloud Shell, non devi fare nulla.
Prima del deployment…
Se lavori nel tuo ambiente locale, autenticati su Google Cloud con quanto segue
$ gcloud auth login –update-adc # not needed in Cloud Shell
In questo modo, l'autenticazione verrà eseguita tramite un accesso OAuth nel browser. Assicurati di accedere tramite Chrome con lo stesso utente (ad es. vattelapesca@gmail.com) che ha eseguito l'accesso a Google Cloud con la fatturazione abilitata.
Abilita l'API Cloud Run con il seguente comando:
$ gcloud services enable run.googleapis.com cloudbuild.googleapis.com
A questo punto, tutto è pronto per il deployment in Cloud Run.
Esegui il deployment dell'app in Cloud Run tramite gcloud
Il comando che ti consente di eseguire il deployment dell'app su Cloud Run è gcloud run deploy. Esistono diverse opzioni da impostare per raggiungere il tuo obiettivo. Il set minimo di opzioni (che puoi fornire tramite la riga di comando o lo strumento ti chiederà tramite un prompt interattivo) è il seguente:
- Nome del servizio Cloud Run di cui vuoi eseguire il deployment per la tua app. Un servizio Cloud Run ti restituirà un URL che fornisce un endpoint alla tua app.
- Regione Google Cloud in cui verrà eseguita l'app. (
--regionREGION) - Immagine container che contiene la tua app.
- Variabili di ambiente che l'app deve utilizzare durante l'esecuzione.
- Il flag Allow-Unauthenticated che consente a tutti di accedere alla tua app senza ulteriore autenticazione.
Consulta la documentazione (o scorri verso il basso per una possibile soluzione) per scoprire come applicare questa opzione alla riga di comando.
Il deployment richiederà alcuni minuti. Se tutto è corretto, nella console Google Cloud dovrebbe essere visualizzato un messaggio simile a questo.


Fai clic sull'URL fornito da Cloud Run e testa la tua applicazione. Una volta autenticato, dovresti vedere qualcosa di simile.

"gcloud run deploy" senza argomenti
Potresti aver notato che gcloud run deploy ti pone le domande giuste e riempie gli spazi vuoti che hai lasciato. È fantastico!
Tuttavia, in alcuni moduli aggiungeremo questo comando a un trigger Cloud Build, quindi non possiamo permetterci domande interattive. Dobbiamo compilare ogni opzione del comando. Quindi vuoi creare il gcloud run deploy --option1 blah --foo bar --region your-fav-region dorato. Come procedi?
- Ripeti i passaggi 2-3-4 finché gcloud non smette di fare domande:
- [LOOP]
gcloud run deploycon le opzioni trovate finora - I sistemi [LOOP] chiedono l'opzione X
- [LOOP] Search in public docs how to set up X from CLI adding option
--my-option [my-value]. - Torniamo al passaggio 2, a meno che gcloud non venga completato senza ulteriori domande.
- Questo gcloud run deploy BLAH BLAH BLAH è fantastico. Salva il comando da qualche parte, ti servirà in seguito per il passaggio di Cloud Build.
Una possibile soluzione è disponibile qui. La documentazione è disponibile qui.
Evviva 🎉🎉🎉 Hai eseguito il deployment della tua app in Google Cloud, completando il primo passaggio della modernizzazione.
6. Modulo 4: Clean Password con Secret Manager

Nel passaggio precedente abbiamo eseguito il deployment e l'esecuzione della nostra app in Cloud Run. Tuttavia, l'abbiamo fatto con una pratica di sicurezza errata: fornendo alcuni secret in testo non crittografato.
Prima iterazione: aggiorna il file config.php per utilizzare ENV
Potresti aver notato che abbiamo inserito la password del database direttamente nel codice del file config.php. Questa opzione è adatta per i test e per verificare se l'app funziona. Tuttavia, non puoi eseguire il commit/utilizzare il codice in questo modo in un ambiente di produzione. La password (e altri parametri di connessione al database) devono essere letti dinamicamente e forniti all'app in fase di runtime. Modifica il file config.php in modo che legga i parametri del database dalle variabili ENV. Se non funziona, ti consigliamo di impostare valori predefiniti. Questa opzione è utile in caso di mancato caricamento di ENV, in modo che l'output della pagina indichi se vengono utilizzati i valori predefiniti. Riempi gli spazi vuoti e sostituisci il codice in config.php.
<?php
// Database configuration with ENV variables. Set default values as well
$db_host = getenv('DB_HOST') ?: 'localhost';
$db_name = getenv('DB_NAME') ?: 'image_catalog';
$db_user = getenv('DB_USER') ?: 'appmod-phpapp-user';
$db_pass = getenv('DB_PASS') ?: 'wrong_password';
// Note getenv() is PHP 5.3 compatible
try {
$pdo = new PDO("mysql:host=$db_host;dbname=$db_name", $db_user, $db_pass);
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
} catch (PDOException $e) {
die("Errore di connessione: " . $e->getMessage());
}
session_start();
?>
Poiché la tua app è containerizzata, devi fornire un modo per fornire le variabili ENV all'app. Puoi farlo in diversi modi:
- Al momento della build, sul Dockerfile. Aggiungi al Dockerfile precedente i quattro parametri utilizzando la sintassi ENV DB_VAR=ENV_VAR_VALUE. In questo modo verranno impostati valori predefiniti che possono essere sostituiti in fase di runtime. Ad esempio, "DB_NAME" e "DB_USER" possono essere impostati qui e in nessun altro luogo.
- Al momento dell'esecuzione. Puoi configurare queste variabili per Cloud Run, sia dalla CLI che dalla UI. Questo è il posto giusto per inserire tutte e quattro le variabili (a meno che tu non voglia mantenere i valori predefiniti impostati in Dockerfile).
In localhost, ti consigliamo di inserire le variabili ENV in un file .env (controlla la cartella solutions).
Assicurati inoltre che .env sia aggiunto a .gitignore : non vuoi inviare i tuoi segreti a GitHub.
echo .env >> .gitignore
Dopodiché, puoi testare l'istanza localmente:
docker run -it -p 8080:8080 --env-file .env my-php-app-docker
Ora hai ottenuto quanto segue:
- La tua app leggerà la variabile in modo dinamico dal tuo ambiente
- Hai migliorato la sicurezza rimuovendo la password del database dal codice)
Ora puoi eseguire il deployment di una nuova revisione in Cloud Run. Passiamo all'interfaccia utente e impostiamo manualmente le variabili di ambiente:
- Vai a https://console.cloud.google.com/run.
- Fai clic sulla tua app.
- Fai clic su "Modifica ed esegui il deployment di una nuova revisione".
- Nella prima scheda "Container", fai clic sulla scheda in basso "Variabili e secret".
- Fai clic su "+ Aggiungi variabile" e aggiungi tutte le variabili necessarie. Il risultato dovrebbe essere simile a questo:


È perfetto? No, il tuo PASS è ancora visibile alla maggior parte degli operatori. Questo problema può essere mitigato con Secret Manager di Google Cloud.
Seconda iterazione: Secret Manager
Le tue password sono scomparse dal tuo codice: vittoria! Ma aspetta, siamo al sicuro?
Le tue password sono ancora visibili a chiunque abbia accesso alla console Google Cloud. Infatti, se accedi al file di deployment YAML di Cloud Run, puoi recuperarlo. In alternativa, se provi a modificare o a eseguire il deployment di una nuova revisione di Cloud Run, la password è visibile nella sezione Variabili e secret, come mostrato negli screenshot di seguito.
Google Cloud Secret Manager è un servizio sicuro e centralizzato per la gestione di informazioni sensibili come chiavi API, password, certificati e altri secret.
Consente di archiviare, gestire e accedere ai secret con autorizzazioni granulari e crittografia avanzata. Integrato con Identity and Access Management (IAM) di Google Cloud, Secret Manager ti consente di controllare chi può accedere a segreti specifici, garantendo la sicurezza dei dati e la conformità normativa.
Supporta anche la rotazione e il controllo delle versioni automatici dei secret, semplificando la gestione del ciclo di vita dei secret e migliorando la sicurezza nelle applicazioni nei servizi Google Cloud.
Per accedere a Secret Manager, vai dal menu a tre barre ai servizi Sicurezza e lo troverai nella sezione Protezione dei dati, come mostrato nello screenshot di seguito.

Una volta lì, abilita l'API Secret Manager come mostrato nell'immagine seguente.

- Ora fai clic su "Crea un secret". Chiamiamolo in modo razionale:
- Nome:
php-amarcord-db-pass - Valore del secret: "your DB password" (ignora la parte "upload file").
- annotare questo link segreto, che dovrebbe avere il seguente aspetto:
projects/123456789012/secrets/php-amarcord-db-pass. Questo è il puntatore univoco al tuo secret (per Terraform, Cloud Run e altri). Il numero è il numero di progetto univoco.
Suggerimento: prova a utilizzare convenzioni di denominazione coerenti per i tuoi secret, specializzandoti da sinistra a destra, ad esempio: cloud-devrel-phpamarcord-dbpass
- Organizzazione (con l'azienda)
- Team (all'interno dell'organizzazione)
- Applicazione (all'interno del team)
- Nome della variabile (all'interno dell'app)
In questo modo, potrai avere espressioni regolari semplici per trovare tutti i tuoi segreti per una singola app.
Crea una nuova revisione Cloud Run
Ora che abbiamo un nuovo secret, dobbiamo eliminare la variabile di ambiente DB_PASS e sostituirla con il nuovo secret. Pertanto:
- Accesso a Cloud Run utilizzando la console Google Cloud
- Scegli l'app.
- Fai clic su "Modifica ed esegui il deployment di una nuova revisione".
- Individua la scheda "Variabili e secret".
- Utilizza il pulsante "+ Fai riferimento a un secret" per reimpostare la variabile di ambiente DB_PASS.
- Utilizza lo stesso "DB_PASS" per i secret a cui viene fatto riferimento e utilizza l'ultima versione.

Al termine, dovresti ricevere il seguente errore

Cerca di capire come risolvere il problema. Per risolvere il problema, devi accedere alla sezione IAM e amministrazione e modificare le autorizzazioni di concessione. Buon debug!
Una volta risolto il problema, torna a Cloud Run ed esegui nuovamente il deployment di una nuova revisione. Il risultato dovrebbe essere simile alla figura seguente:

Suggerimento: la console per gli sviluppatori (UI) è molto utile per segnalare i problemi di autorizzazione. Prenditi il tempo necessario per esplorare tutti i link delle tue entità cloud.
7. Modulo 5: Configura la CI/CD con Cloud Build

Perché una pipeline CI/CD?
A questo punto, dovresti aver digitato gcloud run deploy un paio di volte, magari rispondendo più volte alla stessa domanda.
Stanco di eseguire manualmente il deployment della tua app con gcloud run deploy? Non sarebbe fantastico se la tua app potesse essere implementata automaticamente ogni volta che esegui il push di una nuova modifica nel repository Git?
Per utilizzare una pipeline CI/CD, ti servono due cose:
- Un repository Git personale: per fortuna, hai già creato un fork del repository del workshop nel tuo account GitHub nel passaggio 2. In caso contrario, torna indietro e completa il passaggio. Il repository di cui hai creato una copia dovrebbe avere il seguente aspetto:
https://github.com/<YOUR_GITHUB_USER>/app-mod-workshop - Cloud Build. Questo servizio fantastico ed economico ti consente di configurare automazioni di build per praticamente qualsiasi cosa: Terraform, app containerizzate e così via.
Questa sezione è incentrata sulla configurazione di Cloud Build.
Ti presentiamo Cloud Build.
Utilizzeremo Cloud Build per farlo:
- crea la tua origine (con Dockerfile). Consideralo un "grande file ZIP" che contiene tutto il necessario per crearlo ed eseguirlo (il tuo "artefatto di build").
- esegui il push di questo artefatto in Artifact Registry (AR).
- Poi esegui un deployment da AR a Cloud Run per l'app "php-amarcord".
- In questo modo verrà creata una nuova versione ("revisione") dell'app esistente (immagina un livello con il nuovo codice) e la configureremo per reindirizzare il traffico alla nuova versione se il push va a buon fine.
Ecco un esempio di alcune build per la mia app php-amarcord:

Come facciamo tutto questo?
- Creando un file YAML perfetto:
cloudbuild.yaml - Creando un trigger di Cloud Build.
- Connettendoti al nostro repository GitHub tramite la UI di Cloud Build.
Crea trigger (e connetti repository)
- Vai a https://console.cloud.google.com/cloud-build/triggers.
- Fai clic su "Crea trigger".
- Compila:
- Nome: qualcosa di significativo come
on-git-commit-build-php-app - Evento: push al ramo
- Origine: "Connetti nuovo repository"

- Si aprirà una finestra sulla destra: "Collega repository".
- Provider di origine: "Github" (primo)
- "Continua"
- Authenticate aprirà una finestra su GitHub per l'autenticazione incrociata. Segui il flusso e sii paziente. Se hai molti repository, l'operazione potrebbe richiedere un po' di tempo.
- "Select repo" (Seleziona repository): seleziona il tuo account/repository e spunta la casella "I understand..." (Ho compreso...).
- Se viene visualizzato il messaggio di errore: L'app GitHub non è installata in nessun repository, procedi facendo clic su "Installa Google Cloud Build" e segui le istruzioni.
Fai clic su Connetti.
- Bingo! Il tuo repository è ora connesso.
- Torniamo alla parte del trigger…
- Configurazione: rilevamento automatico (*)
- Avanzate: seleziona il service account "[PROJECT_NUMBER]- compute@developer.gserviceaccount.com"
- xxxxx è l'ID progetto
- Il service account Compute predefinito è adatto a un approccio di laboratorio, ma non utilizzarlo in produzione. ( Scopri di più).
- lascia tutto il resto invariato.
- Fai clic sul pulsante "Crea".
(*) Questo è il modo più semplice, in quanto controlla la presenza di Dockerfile o cloudbuild.yaml. Tuttavia, cloudbuild.yaml ti offre la possibilità di decidere cosa fare in ogni passaggio.
Ho la forza!
Ora, il trigger non funzionerà a meno che tu non conceda al service account Cloud Build (che cos'è un service account?) L'email di un "robot" che agisce per tuo conto per un'attività, in questo caso la creazione di elementi nel cloud.
La creazione e il deployment del tuo service account non riusciranno a meno che tu non gli conceda l'autorizzazione. Per fortuna è facile.
- vai a "Cloud Build" > "Impostazioni".
- Service account "[PROJECT_NUMBER]- compute@developer.gserviceaccount.com"
- Seleziona queste caselle:
- Cloud Run
- Secret Manager
- Service account
- Cloud Build
- Seleziona anche "Imposta come service account preferito".

Dove si trova il file YAML di Cloud Build?
Ti consigliamo vivamente di dedicare un po' di tempo alla creazione del tuo YAML di Cloud Build.
Tuttavia, se non hai tempo o non vuoi dedicare tempo, puoi trovare ispirazione in questa cartella di soluzioni: .solutions
Ora puoi eseguire il push di una modifica su GitHub e osservare Cloud Build.
La configurazione di Cloud Build può essere complicata. Aspettati qualche scambio di messaggi entro:
- Controllare i log all'indirizzo https://console.cloud.google.com/cloud-build/builds;region=global
- Trovare l'errore.
- Correzione nel codice e riemissione di git commit / git push.
- A volte l'errore non si trova nel codice, ma in una configurazione. In questo caso, puoi emettere una nuova build dalla UI (Cloud Build > "Trigger" > Esegui).

Tieni presente che se utilizzi questa soluzione, devi comunque svolgere alcune operazioni. Ad esempio, devi impostare le variabili ENV per i nuovi endpoint di sviluppo/produzione:

A tale scopo, puoi procedere in uno dei due seguenti modi:
- Tramite l'interfaccia utente, impostando nuovamente le variabili di ambiente
- Tramite la CLI, creando lo script "perfetto" per te. Un esempio è disponibile qui: gcloud-run-deploy.sh . Devi modificare alcune impostazioni, ad esempio l'endpoint e il numero di progetto. Puoi trovare il numero di progetto nella panoramica di Cloud.
Come faccio a eseguire il commit del codice su GitHub?
In questo workshop non ti insegneremo il modo migliore per git push su GitHub. Tuttavia, se sei bloccato e ti trovi in Cloud Shell, ci sono due modi:
- CLI. Aggiungi una chiave SSH in locale e aggiungi un repository remoto con git@github.com:YOUR_USER/app-mod-workshop.git (anziché http)
- VSCode. Se utilizzi l'editor Cloud Shell, puoi utilizzare la scheda Controllo della versione (Ctrl+Maiusc+G), fare clic su "Sincronizza modifiche" e seguire le istruzioni. Dovresti essere in grado di autenticare il tuo account GitHub in VS Code e il pull/push da lì diventerà un gioco da ragazzi.

Ricorda di git add clodubuild.yaml tra gli altri file, altrimenti non funzionerà.
Parità di sviluppo/produzione profonda e superficiale [facoltativo]
Se hai copiato la versione del modello da qui, avrai due versioni DEV e PROD identiche. È una funzionalità interessante e in linea con la regola 10 di The Twelve-Factor App.
Tuttavia, utilizziamo due endpoint web diversi per fare in modo che un'app punti allo stesso database. Questo è sufficiente per un workshop, ma nella vita reale devi dedicare del tempo alla creazione di un ambiente di produzione adeguato. Ciò significa avere due database (uno per lo sviluppo e uno per la produzione) e scegliere dove ospitarli per il disaster recovery / l'alta affidabilità. Questo argomento va oltre l'ambito di questo workshop, ma è un buon punto di partenza.
Se hai tempo per realizzare una versione "approfondita" della produzione, tieni presente tutte le risorse che devi duplicare, ad esempio:
- Database Cloud SQL (e probabilmente istanza SQL).
- Bucket GCS
- Funzione Cloud.
- Puoi utilizzare Gemini 1.5 Flash come modello in fase di sviluppo (più economico, più veloce) e Gemini 1.5 Pro (più potente).
In generale, ogni volta che fai qualcosa nell'app, pensa in modo critico: la produzione deve avere lo stesso valore o no? In caso contrario, duplica il tuo impegno. Naturalmente, questa operazione è molto più semplice con Terraform, dove puoi inserire il tuo ambiente (-dev, -prod) come suffisso delle risorse.
8. Modulo 6: Esegui la migrazione a Google Cloud Storage

Spazio di archiviazione

Attualmente l'app memorizza lo stato in un container Docker. Se la macchina si rompe, l'app si arresta in modo anomalo o semplicemente se inserisci una nuova revisione, verrà pianificata una nuova revisione con uno spazio di archiviazione nuovo e vuoto: 🙈
Come si risolve il problema? Esistono diversi approcci.
- Archivia le immagini nel database. È quello che ho finito per fare con la mia precedente app PHP. È la soluzione più semplice perché non aggiunge complessità. ma aggiunge sicuramente latenza e carico al tuo DB.
- Esegui la migrazione dell'app Cloud Run a una soluzione adatta all'archiviazione: GCE + disco permanente? Forse GKE + Storage? Nota: ciò che guadagni in controllo, lo perdi in agilità.
- Passa a GCS. Google Cloud Storage offre la migliore archiviazione per l'intero Google Cloud ed è la soluzione più idiomatica per il cloud. Tuttavia, richiede di sporcarsi le mani con le librerie PHP. Esistono librerie PHP 5.7 per GCS?
PHP 5.7supportaComposer(sembra che PHP 5.3.2 sia la versione meno recente supportata da Composer)? - Forse puoi usare un docker sidecar.
- In alternativa, utilizza i montaggi dei volumi di Cloud Run. Sembra fantastico.
🤔 Migrate storage (open ended)
[Domanda aperta] In questo esercizio, vogliamo che tu trovi una soluzione per spostare le tue immagini in modo che vengano mantenute in qualche modo.
Test di accettazione
Non voglio dirti la soluzione, ma voglio che accada questo:
- Carichi
newpic.jpg. Lo vedi nell'app. - Esegui l'upgrade dell'app a una nuova versione.
newpic.jpgè ancora presente e visibile.
💡 Soluzione possibile (montaggi di volumi GCS Cloud Run)
Si tratta di una soluzione molto elegante che ci consente di eseguire caricamenti di file stateful senza toccare il codice (a parte la visualizzazione di una descrizione dell'immagine, ma è una cosa banale e solo per la soddisfazione visiva).
In questo modo dovresti riuscire a montare una cartella da Cloud Run a GCS, quindi:
- Tutti i caricamenti su GCS saranno effettivamente visibili nella tua app.
- Tutti i caricamenti nella tua app verranno effettivamente caricati su GCS
- La magia avverrà per gli oggetti caricati in GCS (capitolo 7).
Nota: Leggi i termini e condizioni di FUSE. Questo NON è accettabile se le prestazioni sono un problema.
Crea un bucket GCS
GCS è il servizio di archiviazione onnipresente di Google Cloud. È stato testato sul campo ed è utilizzato da tutti i servizi GCP che richiedono spazio di archiviazione.
Tieni presente che Cloud Shell esporta PROJECT_ID come GOOGLE_CLOUD_PROJECT:
$ export PROJECT_ID=$GOOGLE_CLOUD_PROJECT
#!/bin/bash
set -euo pipefail
# Your Cloud Run Service Name, eg php-amarcord-dev
SERVICE_NAME='php-amarcord-dev'
BUCKET="${PROJECT_ID}-public-images"
GS_BUCKET="gs://${BUCKET}"
# Create bucket
gsutil mb -l "$GCP_REGION" -p "$PROJECT_ID" "$GS_BUCKET/"
# Copy original pictures there - better if you add an image of YOURS before.
gsutil cp ./uploads/*.png "$GS_BUCKET/"
Configura Cloud Run per montare il bucket nella cartella /uploads/
Ora passiamo alla parte elegante. Creiamo un volume php_uploads e chiediamo a Cloud Run di eseguire un montaggio FUSE su MOUNT_PATH (ad esempio /var/www/html/uploads/):
#!/bin/bash
set -euo pipefail
# .. keep variables from previous script..
# Uploads folder within your docker container.
# Tweak it for your app code.
MOUNT_PATH='/var/www/html/uploads/'
# Inject a volume mount to your GCS bucket in the right folder.
gcloud --project "$PROJECT_ID" beta run services update "$SERVICE_NAME" \
--region $GCP_REGION \
--execution-environment gen2 \
--add-volume=name=php_uploads,type=cloud-storage,bucket="$BUCKET" \
--add-volume-mount=volume=php_uploads,mount-path="$MOUNT_PATH"
Ora ripeti questo passaggio per tutti gli endpoint che vuoi indirizzare a Cloud Storage.
Puoi ottenere lo stesso risultato anche dall'interfaccia utente
- Nella scheda "Volumi", crea un montaggio di volumi che rimandi al tuo bucket, di tipo "Bucket Cloud Storage", ad esempio con il nome "php_uploads".
- In Container(s) > Volume Mounts monta il volume che hai appena creato sul punto di montaggio del volume richiesto dalla tua app. Dipende dal Dockerfile, ma potrebbe essere simile a
var/www/html/uploads/.
In entrambi i casi, se funziona, la modifica della nuova revisione di Cloud Run dovrebbe mostrare un risultato simile a questo:

Ora testa la nuova applicazione caricando una nuova immagine nell'endpoint /upload.php.
Le immagini devono essere trasferite senza problemi su GCS senza scrivere una sola riga di PHP:

Che cosa è successo?
È successo qualcosa di magico.
Una vecchia applicazione con un vecchio codice continua a fare il suo lavoro. Un nuovo stack modernizzato ci consente di avere tutte le immagini/foto nella nostra app comodamente archiviate in un bucket Cloud stateful. Ora non ci sono più limiti:
- Vuoi ricevere un'email ogni volta che ricevi un'immagine con contenuti "pericolosi" o "di nudo"? Puoi farlo senza toccare il codice PHP.
- Vuoi utilizzare un modello multimodale Gemini ogni volta che ricevi un'immagine per descriverla e caricare il DB con la sua descrizione? Puoi farlo senza toccare il codice PHP. Non mi credi? Continua a leggere nel capitolo 7.
Abbiamo appena scoperto un'ampia gamma di opportunità.
9. Modulo 7: Potenzia la tua app con Google Gemini

Ora hai una nuova app PHP moderna e fantastica (come una Fiat 126 del 2024) con spazio di archiviazione cloud.
A cosa serve
Prerequisiti
Nel capitolo precedente, una soluzione modello ci ha consentito di montare le immagini /uploads/ su GCS, separando di fatto la logica dell'app dall'archiviazione delle immagini.
Questo esercizio richiede di:
- Aver completato correttamente l'esercizio del capitolo 6 (storage).
- Avere un bucket GCS con i caricamenti di immagini, in cui gli utenti caricano le immagini sulla tua app e le immagini vengono trasferite al tuo bucket.
Configura una Funzione Cloud (in Python)
Ti sei mai chiesto come implementare un'applicazione basata su eventi? Qualcosa del tipo:
- quando si verifica <event> => invia un'email
- quando si verifica <event> => se <condition> è vera, aggiorna il database.
L'evento può essere qualsiasi cosa, da un nuovo record disponibile in BigQuery, a un nuovo oggetto modificato in una cartella in GCS o a un nuovo messaggio in attesa in una coda in Pub/Sub.
Google Cloud supporta diversi paradigmi per raggiungere questo obiettivo. In particolare:
- EventArc. Scopri come ricevere eventi GCS. Ideale per creare DAG e orchestrare azioni basate su istruzioni if-then-else nel cloud.
- Cloud Scheduler. Ideale, ad esempio, per un cron job di mezzanotte nel cloud.
- Cloud Workflows. Analogamente ad Event Arc, ti consente di
- Cloud Run Functions (noto anche come
lambdas). - Cloud Composer. Fondamentalmente la versione Google di Apache Airflow, ideale anche per i DAG.
In questo esercizio, approfondiremo Cloud Functions per ottenere un risultato piuttosto spettacolare. Inoltre, ti forniremo esercizi facoltativi.
Tieni presente che il codice di esempio viene fornito in .solutions/
Configura una Funzione Cloud (🐍 Python)
Stiamo cercando di creare un GCF molto ambizioso.
- Quando viene creata una nuova immagine su GCS… (probabilmente perché qualcuno l'ha caricato sull'app, ma non solo)
- .. call Gemini to describe it and get a textual description of the image .. (would be nice to check the MIME and ensure its an image and not a PDF, MP3, or Text)
- .. and update the DB with this description. (potrebbe essere necessario applicare una patch al database per aggiungere una colonna
descriptionalla tabellaimages).
Applica la patch al database per aggiungere description alle immagini
- Apri Cloud SQL Studio:

- Inserisci l'utente e la password per il database delle immagini
- Inserisci questo SQL che aggiunge una colonna per la descrizione di un'immagine:
ALTER TABLE images ADD COLUMN description TEXT;

E bingo! Prova ora a verificare se ha funzionato:
SELECT * FROM images;
Dovresti visualizzare la nuova colonna della descrizione:

Scrivi la funzione f(x) di Gemini
Nota: Questa funzione è stata creata con l'aiuto di Gemini Code Assist.
Nota: La creazione di questa funzione potrebbe causare errori di autorizzazione IAM. Alcuni sono documentati di seguito nel paragrafo "Possibili errori".
- Abilita le API
- Vai a https://console.cloud.google.com/functions/list.
- Fai clic su "Crea funzione".
- Abilita le API dalla procedura guidata API:

Puoi creare la GCF dall'interfaccia utente o dalla riga di comando. Qui utilizzeremo la riga di comando.
Un possibile codice è disponibile in .solutions/
- Crea una cartella per ospitare il codice, ad esempio "gcf/". Inserisci la cartella.
- Crea un file
requirements.txt:
google-cloud-storage
google-cloud-aiplatform
pymysql
- Crea una funzione Python. Codice di esempio qui: gcf/main.py.
#!/usr/bin/env python
"""Complete this"""
from google.cloud import storage
from google.cloud import aiplatform
import vertexai
from vertexai.generative_models import GenerativeModel, Part
import os
import pymysql
import pymysql.cursors
# Replace with your project ID
PROJECT_ID = "your-project-id"
GEMINI_MODEL = "gemini-1.5-pro-002"
DEFAULT_PROMPT = "Generate a caption for this image: "
def gemini_describe_image_from_gcs(gcs_url, image_prompt=DEFAULT_PROMPT):
pass
def update_db_with_description(image_filename, caption, db_user, db_pass, db_host, db_name):
pass
def generate_caption(event, context):
"""
Cloud Function triggered by a GCS event.
Args:
event (dict): The dictionary with data specific to this type of event.
context (google.cloud.functions.Context): The context parameter contains
event metadata such as event ID
and timestamp.
"""
pass
- Esegui il push della funzione. Puoi utilizzare uno script simile a questo: gcf/push-to-gcf.sh.
Nota 1. Assicurati di recuperare le variabili di ambiente con i valori corretti o semplicemente aggiungile in alto (GS_BUCKET=blah, ..):
Nota 2. In questo modo verrà eseguito il push di tutto il codice locale (.), quindi assicurati di racchiudere il codice in una cartella specifica e di utilizzare .gcloudignore come un professionista per evitare di eseguire il push di librerie di grandi dimensioni. ( esempio).
#!/bin/bash
set -euo pipefail
# add your logic here, for instance:
source .env || exit 2
echo "Pushing ☁️ f(x)☁ to 🪣 $GS_BUCKET, along with DB config.. (DB_PASS=$DB_PASS)"
gcloud --project "$PROJECT_ID" functions deploy php_amarcord_generate_caption \
--runtime python310 \
--region "$GCP_REGION" \
--trigger-event google.cloud.storage.object.v1.finalized \
--trigger-resource "$BUCKET" \
--set-env-vars "DB_HOST=$DB_HOST,DB_NAME=$DB_NAME,DB_PASS=$DB_PASS,DB_USER=$DB_USER" \
--source . \
--entry-point generate_caption \
--gen2
Nota: in questo esempio, generate_caption sarà il metodo richiamato e Cloud Functions gli passerà l'evento GCS con tutte le informazioni pertinenti (nome del bucket, nome dell'oggetto e così via). Dedica un po' di tempo al debug del dizionario Python dell'evento.
Test della funzione
Test unitari
La funzione ha molte parti mobili. Potresti voler testare tutti i singoli annunci.
Un esempio è disponibile in gcf/test.py.
UI di Cloud Functions
Prenditi anche un po' di tempo per esplorare la funzione nella UI. Ogni scheda merita di essere esplorata, in particolare Source (la mia preferita), Variables, Trigger e Logs. Trascorrerai molto tempo nella scheda Logs per risolvere gli errori (vedi anche i possibili errori in fondo a questa pagina). Assicurati anche di controllare Permissions.

E2E Test
È il momento di testare manualmente la funzione.
- Vai all'app e accedi.
- Carica un'immagine (non troppo grande, abbiamo riscontrato problemi con le immagini di grandi dimensioni)
- controlla che l'immagine sia stata caricata nell'interfaccia utente.
- Verifica in Cloud SQL Studio che la descrizione sia stata aggiornata. Accedi ed esegui questa query:
SELECT * FROM images.

E funziona. Potremmo anche voler aggiornare il frontend per mostrare la descrizione.
Aggiorna PHP per mostrare [facoltativo]
Abbiamo dimostrato che l'app funziona. Tuttavia, sarebbe bello se anche gli utenti potessero vedere questa descrizione.
Non è necessario essere esperti di PHP per aggiungere la descrizione a index.php. Questo codice dovrebbe fare (sì, l'ha scritto anche Gemini per me):
<?php if (!empty($image['description'])): ?>
<p class="font-bold">Gemini Caption:</p>
<p class="italic"><?php echo $image['description']; ?></p>
<?php endif; ?>
Posiziona questo codice all'interno di foreach a tuo piacimento.
Nei passaggi successivi vedremo anche una versione dell'interfaccia utente più bella, grazie a Gemini Code Assist. Una versione più carina potrebbe avere il seguente aspetto:

Conclusioni
Hai una Cloud Function attivata sui nuovi oggetti che arrivano su GCS, in grado di annotare il contenuto dell'immagine come farebbe un essere umano e aggiornare automaticamente il database. Wow!
Passaggi successivi Potresti seguire lo stesso ragionamento per ottenere due funzionalità eccezionali.
[Facoltativo] Aggiungi altre Cloud Functions [domanda aperta]
Mi vengono in mente un paio di funzionalità aggiuntive.
📩 Trigger email
Un trigger email che ti invia un'email ogni volta che qualcuno invia una foto.
- Troppo spesso? Aggiungi un ulteriore vincolo: un'immagine di grandi dimensioni o un'immagine i cui contenuti di Gemini contengono le parole "nudo/nudità/violento".
- Ti consigliamo di controllare
EventArc.
🚫 Moderazione automatica di foto inappropriate
Al momento, un amministratore umano contrassegna le immagini come "inappropriate". Che ne dici di lasciare che Gemini si occupi del lavoro pesante e moderi lo spazio? Aggiungi un test per segnalare i contenuti dei trigger inappropriati e aggiorna il database come abbiamo imparato nella funzione precedente. Ciò significa sostanzialmente prendere la funzione precedente, modificare il prompt e aggiornare il database in base alla risposta.
Avvertenza. L'AI generativa produce risultati imprevedibili. Assicurati che l'"output creativo" di Gemini sia "controllato". Puoi chiedere una risposta deterministica come un punteggio di confidenza da 0 a 1, un JSON e così via. Puoi farlo in molti modi, ad esempio: * Utilizzando le librerie Python pydantic, langchain e così via. * Utilizzando l'output strutturato di Gemini.
Suggerimento. Puoi avere PIÙ funzioni o un singolo prompt che impone una risposta JSON (funziona alla grande con "Output strutturato di Gemini" come evidenziato sopra), ad esempio:
Quale prompt devo usare per generare questa immagine?
{
"description": "This is the picture of an arrosticino",
"suitable": TRUE
}
Puoi aggiungere al prompt altri campi per ottenere approfondimenti come: c'è qualcosa di buono? Cosa c'è di male? Riconosci il luogo? C'è del testo (l'OCR non è mai stato così facile):
goods: "Sembra un cibo delizioso"bads: "Sembra cibo malsano"OCR: "Da consumare preferibilmente prima del 10 Novembre 2024"location: "Pescara, Lungomare"
Anche se di solito è meglio avere una funzione N per N risultati, è incredibilmente gratificante farne una che fa 10 cose. Consulta questo articolo di Riccardo per scoprire come fare.
Possibili errori (principalmente IAM / autorizzazioni)
La prima volta che ho sviluppato questa soluzione, ho riscontrato alcuni problemi di autorizzazione IAM. Li aggiungerò qui per empatia e per dare qualche idea su come risolverli.
Errore: autorizzazioni insufficienti per il service account
- Tieni presente che per il deployment di una funzione GCF che ascolta un bucket GCS devi configurare le autorizzazioni appropriate per il service account che utilizzi per il job, come mostrato nella figura:

Potresti anche dover abilitare le API EventArc, che richiedono alcuni minuti prima di diventare completamente disponibili.
Errore: Cloud Run Invoker mancante
- Un altro commento dell'interfaccia utente per la gestione delle autorizzazioni di GCF è questo ( ruolo Cloud Run Invoker):

Questo errore può essere corretto eseguendo il comando nell'immagine, che è simile a fix-permissions.sh.
Questo problema è descritto qui: https://cloud.google.com/functions/docs/securing/authenticating
Errore: limite di memoria superato
La prima volta che l'ho eseguito, i miei log potrebbero aver indicato: "Il limite di memoria di 244 MiB è stato superato con 270 MiB utilizzati. Valuta la possibilità di aumentare il limite di memoria. Per maggiori informazioni, consulta la pagina https://cloud.google.com/functions/docs/configuring/memory". Aggiungi di nuovo RAM al tuo GCF. È semplicissimo farlo nella UI. Ecco un possibile bump:

In alternativa, puoi anche correggere lo script di deployment di Cloud Run per aumentare la MEM/CPU. L'operazione richiede un po' più di tempo.
Errore: Pub/Sub pubblicato
La creazione di un trigger con GCF v1 ha generato una volta questo errore:

Anche in questo caso, il problema è facile da risolvere. Vai ad IAM e assegna all'account di servizio il ruolo "Publisher Pub/Sub".
Errore: Vertex AI non è stato utilizzato
Se ricevi questo errore:
Autorizzazione negata: 403 L'API Vertex AI non è stata utilizzata in precedenza nel progetto YOUR_PROJECT o è disabilitata. Abilitala visitando la pagina https://console.developers.google.com/apis/api/aiplatform.googleapis.com/overview?project=YOR_PROJECT
Devi solo abilitare le API Vertex AI. Il modo più semplice per abilitare TUTTE le API necessarie è il seguente:
- https://console.cloud.google.com/vertex-ai
- Fai clic su "Abilita tutte le API consigliate".

Errore: trigger EventArc non trovato.
Se ricevi questo messaggio, esegui nuovamente il deployment della funzione.

Errore: 400 Gli agenti di servizio sono in fase di provisioning
Vengono sottoposti a provisioning 400 service agent ( https://cloud.google.com/vertex-ai/docs/general/access-control#service-agents ). I service agent sono necessari per leggere il file Cloud Storage fornito. Riprova tra qualche minuto.
In questo caso, attendi un po' o chiedi a un Googler.
10. Modulo 8: crea SLO di disponibilità
Nel capitolo cerchiamo di raggiungere questo obiettivo:
- Creazione di SLI
- Creazione di SLO in base agli SLI
- Creazione di avvisi basati sugli SLO

Questo è un argomento molto caro all'autore, poiché Riccardo lavora nell'area SRE / DevOps di Google Cloud.
(aperto) Crea SLI e SLO per questa app
Quanto è utile un'app se non riesci a capire quando non funziona?
Che cos'è un SLO?
Oh wow! Google ha inventato gli SLO. Per saperne di più, ti consiglio di leggere:
- Libro sulla SRE - capitolo 2 - Implementazione degli SLO. ( 👉 altri libri SRE)
- Art of SLOs ( video fantastico). È un ottimo corso di formazione per scoprire di più su come creare un SLO perfetto per il tuo servizio.
- Corso SRE su Coursera. Ho contribuito anche io.
Passaggio 1: crea lo SLI/SLO di disponibilità
Iniziamo con l'SLO di disponibilità, perché è la più semplice e probabilmente la più importante da misurare.
Fortunatamente, Cloud Run è dotato del supporto SLO predefinito, grazie a Istio.
Una volta che l'app è in esecuzione su Cloud Run, è semplicissimo da ottenere e ci vogliono 30 secondi.
- Vai alla pagina Cloud Run.
- Fai clic/seleziona l'app.
- Seleziona la scheda
SLOs. - Fai clic su "+ Crea SLO".
- Disponibilità, in base alla richiesta
- Continua
- Mese di calendario / 99%.
- fai clic su "Crea SLO".

Passaggio 2: configura gli avvisi per questo SLO
Ti suggerisco di creare due avvisi:
- Uno con un burn rate basso ("Slowburn") per avvisarti via email (simula un ticket a bassa priorità).
- Uno con un burn rate elevato ("Fastburn") per avvisarti via SMS (simula un ticket / cercapersone con priorità elevata)
Vai al tuo SLO tab precedente.
Esegui questa operazione due volte:

- Fai clic su "Crea avviso SLO" (il pulsante 🔔 con un segno più all'interno, a destra).
- Durata ricerca, Soglia burn rate:
- [FAST]. Primo:
60min /10x - [SLOW]. Secondo:
720min /2x - Canale di notifica: fai clic su Gestisci canali di notifica
- Innanzitutto, "Email" -> Aggiungi nuovo -> ..
- Secondo, "SMS" -> Aggiungi nuovo -> Verifica sullo smartphone.
- Suggerimento: mi piace usare le emoji nei nomi. È divertente per le demo.
- Al termine, fai clic sulla X grande in alto a destra.
- Seleziona prima il telefono (veloce), poi l'email (lenta).
- Aggiungi della documentazione di esempio, ad esempio:
[PHP Amarcord] Riccardo told me to type sudo reboot or to check documentation in http://example.com/playbooks/1.php but I guess he was joking.
Bingo!
Risultato finale
Possiamo considerare questo esercizio completato una volta che avrai 1 SLO funzionante + 2 avvisi per la tua disponibilità e che gli avvisi verranno inviati alla tua email e al tuo telefono.
Se vuoi, puoi aggiungere una latenza (e ti consiglio vivamente di farlo) o anche una più complessa. Per la latenza, scegli una latenza che ritieni ragionevole. In caso di dubbio, scegli 200 ms.
11. Passaggi successivi
Hai completato TUTTO, cosa manca?
Alcuni spunti di riflessione:
Gioca con Gemini
Puoi utilizzare Gemini in due versioni:
- Vertex AI. Il "modo Enterprise", intrecciato con GCP, che abbiamo esplorato nel capitolo 7 (GCF+Gemini). Tutta l'autenticazione funziona magicamente e i servizi si interconnettono in modo ottimale.
- Google AI. Il "modo di fare del consumatore". Ottieni una chiave API Gemini da qui e inizia a creare piccoli script che possono essere collegati a qualsiasi carico di lavoro già esistente (lavoro proprietario, altri cloud, localhost e così via). Ti basta sostituire la chiave API e il codice inizia a funzionare magicamente.
Ti invitiamo a esplorare (2) con i tuoi progetti personali.
UI Lifting
Non me ne intendo di UI. Ma Gemini sì. Puoi prendere una singola pagina PHP e dire qualcosa del genere:
I have a VERY old PHP application. I want to touch it as little as possible. Can you help me:
1. add some nice CSS to it, a single static include for tailwind or similar, whatever you prefer
2. Transform the image print with description into cards, which fit 4 per line in the canvas?
Here's the code:
-----------------------------------
[Paste your PHP page, for instance index.php - mind the token limit!]
Puoi ottenerlo facilmente in meno di 5 minuti, con una sola build cloud. :)
La risposta di Gemini era perfetta (ovvero non ho dovuto cambiare nulla):

Ecco il nuovo layout nell'app personale dell'autore:

Nota: il codice viene incollato come immagine perché non vogliamo incoraggiarti a prenderlo, ma a chiedere a Gemini di scriverlo per te, con i tuoi vincoli creativi di UI/frontend. Fidati, dopo dovrai apportare solo modifiche minime.
Sicurezza
Proteggere correttamente questa app non è l'obiettivo di questo workshop di 4 ore, in quanto aumenterebbe il tempo necessario per completarlo di 1-2 ordini di grandezza.
Tuttavia, questo argomento è molto importante. Abbiamo raccolto alcune idee in SECURITY.
12. Complimenti!
Congratulazioni 🎉🎉🎉 , hai modernizzato correttamente la tua applicazione PHP legacy con Google Cloud.

In sintesi, in questo codelab hai imparato:
- Come eseguire il deployment di un database in Google Cloud SQL e come eseguire la migrazione del database esistente.
- Come containerizzare l'applicazione PHP con Docker e Buildpack e archiviare la relativa immagine in Google Cloud Artifact Registry
- Come eseguire il deployment dell'app containerizzata su Cloud Run e farla funzionare con Cloud SQL
- Come archiviare/utilizzare segretamente parametri di configurazione sensibili (come la password del database) utilizzando Google Secret Manager
- Come configurare la pipeline CI/CD con Google Cloud Build per creare ed eseguire il deployment automatico della tua app PHP a ogni push di codice nel tuo repository GitHub.
- Come utilizzare Cloud Storage per "cloudificare" le risorse dell'app
- Come sfruttare le tecnologie serverless per creare flussi di lavoro straordinari su Google Cloud senza modificare il codice dell'app.
- Utilizza le funzionalità multimodali di Gemini per un caso d'uso appropriato.
- Implementa i principi SRE in Google Cloud
Questo è un ottimo inizio per il tuo percorso di modernizzazione delle applicazioni con Google Cloud.
🔁 Feedback
Se vuoi raccontarci la tua esperienza con questo workshop, ti consigliamo di compilare questo modulo di feedback.
Accettiamo feedback e PR�� per i pezzi di codice di cui sei particolarmente orgoglioso.
🙏 Grazie
L'autore ringrazia Mirko Gilioli e Maurizio Ipsale di Datatonic per l'aiuto nella stesura e nel test della soluzione.
