Workshop de modernização de apps

1. Introdução

Última atualização:01/11/2024

Como modernizar um aplicativo PHP antigo para o Google Cloud?

(📽️ assista um vídeo introdutório de 7 minutos para este codelab)

É comum ter aplicativos legados em execução no local que precisam ser modernizados. Isso significa torná-los escalonáveis, seguros e implantáveis em diferentes ambientes.

Neste workshop, você vai:

  1. Coloque o aplicativo PHP em um contêiner.
  2. Mova para um serviço de banco de dados gerenciado ( Cloud SQL).
  3. Implante no Cloud Run, que é uma alternativa de operações zero para o GKE/Kubernetes.
  4. Proteja o aplicativo com o Identity and Access Management (IAM) e o Secret Manager.
  5. Defina um pipeline de CI/CD usando o Cloud Build. O Cloud Build pode ser conectado ao seu repositório Git hospedado em provedores populares, como GitHub ou GitLab, e ser acionado a qualquer push para o principal, por exemplo.
  6. Hospede as imagens do aplicativo no Cloud Storage. Isso é feito por montagem, e nenhum código é necessário para mudar o app.
  7. Introduzir a funcionalidade de IA generativa usando o Gemini, orquestrado pelo Cloud Functions (sem servidor).
  8. Familiarize-se com os SLOs e a operação do app recém-atualizado.

Seguindo estas etapas, você pode modernizar gradualmente seu aplicativo PHP, melhorando a escalonabilidade, a segurança e a flexibilidade de implantação. Além disso, a migração para o Google Cloud permite aproveitar a infraestrutura e os serviços avançados para garantir que seu aplicativo seja executado sem problemas em um ambiente nativo da nuvem.

Acreditamos que o que você vai aprender seguindo estas etapas simples pode ser aplicado ao seu próprio aplicativo e organização com diferentes linguagens/stacks e casos de uso.

Sobre o app

O aplicativo ( código, sob licença MIT) que você vai fork é um aplicativo básico do PHP 5.7 com autenticação do MySQL. A principal ideia do app é oferecer uma plataforma em que os usuários possam fazer upload de fotos e os administradores possam marcar imagens inadequadas. O aplicativo tem duas tabelas:

  • Usuários. Vem pré-compilado com administradores. Novas pessoas podem se registrar.
  • Images Vem com algumas imagens de exemplo. Os usuários conectados podem fazer upload de novas fotos. Vamos adicionar um pouco de magia aqui.

Sua meta

Queremos modernizar o aplicativo antigo para que ele fique no Google Cloud. Vamos aproveitar as ferramentas e os serviços dele para melhorar a escalonabilidade, aumentar a segurança, automatizar o gerenciamento de infraestrutura e integrar recursos avançados, como processamento de imagens, monitoramento e armazenamento de dados usando serviços como Cloud SQL, Cloud Run, Cloud Build, Secret Manager e muito mais.

445f7a9ae37e9b4d.png

Mais importante ainda, queremos fazer isso etapa por etapa para que você aprenda o processo de pensamento por trás de cada etapa. Geralmente, cada etapa abre novas possibilidades para as próximas (por exemplo, módulos 2 -> 3 e 6 -> 7).

Ainda não se convenceu? Confira este vídeo de 7 minutos no YouTube.

O que é necessário

  • Um computador com um navegador conectado à Internet.
  • Alguns créditos do GCP. Confira a próxima etapa.
  • Você vai usar o Cloud Shell. Ele vem com todos os comandos pré-instalados necessários e um IDE.
  • Conta do GitHub. Você precisa disso para ramificar o código original 🧑🏻‍💻 gdgpescara/app-mod-workshop com seu próprio repositório git. Isso é necessário para ter seu próprio pipeline de CI/CD (commit automático -> build -> implantação)

Confira exemplos de soluções:

Este workshop foi criado para ser concluído no Cloud Shell (em um navegador).

No entanto, também é possível tentar fazer isso no seu computador local.

2. Configuração de crédito e fork

6dafc658860c0ce5.png

Resgate o crédito do GCP e configure seu ambiente do GCP [opcional]

Para fazer este workshop, você precisa de uma conta de faturamento com algum crédito. Se você já tiver sua própria forma de faturamento, pule esta etapa.

Crie uma nova conta do Google Gmail (*) para vincular ao seu crédito do GCP. Peça ao instrutor o link para resgatar o crédito do GCP ou use os créditos aqui: bit.ly/PHP-Amarcord-credits .

Faça login com a conta recém-criada e siga as instruções.

ff739240dbd84a30.png

(

) Por que preciso de uma conta do Gmail totalmente nova?*

Notamos que algumas pessoas não conseguiram concluir o codelab porque a conta delas (principalmente e-mails de trabalho ou de estudantes) já tinha sido exposta ao GCP e tinha políticas organizacionais que restringiam a capacidade de fazer isso. Recomendamos criar uma conta do Gmail ou usar uma conta do Gmail (gmail.com) sem exposição anterior ao GCP.

Clique no botão para resgatar o crédito.

331658dc50213403.png

Preencha o formulário a seguir com seu nome e sobrenome e concorde com os Termos e Condições.

Talvez seja necessário aguardar alguns segundos para que a conta de faturamento apareça aqui: https://console.cloud.google.com/billing

Depois disso, abra o console do Google Cloud e crie um projeto clicando no seletor de projetos no menu suspenso no canto superior esquerdo, onde "Nenhuma organização" é mostrado. Consulte abaixo

bd7548f78689db0b.png

Crie um projeto se você não tiver um, conforme mostrado na captura de tela abaixo. Há uma opção "NOVO PROJETO" no canto superior direito.

6c82aebcb9f5cd47.png

Vincule o novo projeto à conta de faturamento de teste do GCP da seguinte maneira.

f202527d254893fb.png

Agora você já pode usar o Google Cloud Platform. Se você é iniciante ou quer fazer tudo em um ambiente de nuvem, acesse o Cloud Shell e o editor dele usando o botão no canto superior esquerdo, conforme mostrado abaixo.

7d732d7bf0deb12e.png

Verifique se o novo projeto está selecionado no canto superior esquerdo:

Não selecionado (ruim):

c2ffd36a781b276a.png

Selecionado (bom):

594563c158f4f590.png

Bifurque o app do GitHub

  1. Acesse o app de demonstração: https://github.com/gdgpescara/app-mod-workshop
  2. Clique em 🍴 bifurcar.
  3. Se você não tiver uma conta do GitHub, crie uma.
  4. Edite o que quiser.

734e51bfc29ee5df.png

  1. Clone o código do app usando
  2. git clone https://github.com/YOUR-GITHUB-USER/YOUR-REPO-NAME
  1. Abra a pasta do projeto clonado com seu editor favorito. Se você escolher o Cloud Shell, clique em Abrir editor, conforme mostrado abaixo.

40f5977ea4c1d1cb.png

Você tem tudo o que precisa com o editor do Google Cloud Shell, como mostra a figura a seguir.

a4e5ffb3e9a35e84.png

Para fazer isso visualmente, clique em "Abrir pasta" e selecione a pasta, provavelmente app-mod-workshop na sua pasta inicial.

3. Módulo 1: criar uma instância do SQL

645902e511a432a6.png

Criar a instância do Google Cloud SQL

Nosso app PHP se conecta a um banco de dados MySQL. Por isso, precisamos replicá-lo no Google Cloud para uma migração sem problemas. O Cloud SQL é a opção perfeita, já que permite executar um banco de dados MySQL totalmente gerenciado na nuvem. Siga estas etapas:

  1. Acesse a página do Cloud SQL: https://console.cloud.google.com/sql/instances
  2. Clique em "Criar instância".
  3. Ative a API (se necessário). Isso pode demorar alguns segundos…
  4. Escolha MySQL.
  5. (Estamos tentando oferecer a versão mais barata para que ela dure mais):
  • Edição: Enterprise
  • Predefinição: development (tentamos o sandbox, mas não funcionou para nós)
  • Versão do MySQL: 5.7 (uau, uma volta ao passado!)
  1. ID da instância: escolha appmod-phpapp. Se você mudar isso, lembre-se de alterar também scripts e soluções futuros de acordo.
  2. Senha: o que você quiser, mas anote como CLOUDSQL_INSTANCE_PASSWORD
  3. Região: mantenha a mesma escolhida para o restante do app (por exemplo, Milão = europe-west8)
  4. Disponibilidade por zona: zona única (para economizar dinheiro na demonstração)

Clique no botão "Criar instância" para implantar o banco de dados do Cloud SQL. ⌛ Isso leva cerca de 10 minutos para ser concluído.⌛ Enquanto isso, continue lendo a documentação. Você também pode começar a resolver o próximo módulo ("Containerize your PHP App"), já que ele não tem dependências deste módulo na primeira parte (até corrigir a conexão do banco de dados).

Observação: Essa instância custa cerca de US$ 7/dia. Não se esqueça de fazer isso depois do workshop.

Criar o banco de dados image_catalog e o usuário no Cloud SQL

O projeto do app vem com uma pasta db/ que contém dois arquivos SQL:

  1. 01_schema.sql : contém código SQL para criar duas tabelas com dados de usuários e imagens.
  2. 02_seed.sql: contém código SQL para inserir dados nas tabelas criadas anteriormente.

Esses arquivos serão usados mais tarde, quando o banco de dados image_catalog for criado. Para isso, siga estas etapas:

  1. Abra sua instância e clique na guia "Bancos de dados":
  2. clique em "Criar banco de dados"
  3. chame-o de image_catalog (como na configuração do app PHP).

997ef853e5ebd857.png

Em seguida, criamos o usuário do banco de dados. Com isso, podemos fazer a autenticação no banco de dados image_catalog.

  1. Agora clique na guia Usuários.
  2. Clique em "Adicionar conta de usuário".
  3. Usuário: vamos criar um:
  • Nome de usuário: appmod-phpapp-user
  • Senha: escolha algo que você consiga lembrar ou clique em "gerar".
  • Mantenha "Permitir qualquer host (%)".
  1. clique em ADICIONAR.

Abra o banco de dados para IPs conhecidos.

Todos os bancos de dados no Cloud SQL são criados "isolados". É necessário configurar explicitamente uma rede para que ela possa ser acessada.

  1. Clique na sua instância.
  2. Abra o menu "Conexões".
  3. Clique na guia "Rede".
  4. Clique em "Redes autorizadas". Agora adicione uma rede (ou seja, uma sub-rede).
  • Por enquanto, vamos escolher uma configuração rápida, mas INSEGURA, para permitir que o app funcione. Depois, você pode restringir o acesso aos IPs em que confia:
  • Nome: "Todo mundo no mundo - INSECURE".
  • Rede: "0.0.0.0/0" (observação: esta é a parte INSEGURA!)
  • Clique em CONCLUÍDO
  1. Clique em "Salvar".

Você verá algo como:

5ccb9062a7071964.png

Observação: Essa solução é um bom compromisso para terminar o workshop em O(horas). No entanto, confira o documento SEGURANÇA para proteger sua solução para produção.

Hora de testar a conexão com o banco de dados!

Vamos ver se o usuário image_catalog que criamos antes funciona.

Acesse o "Cloud SQL Studio" na instância e insira o banco de dados, o usuário e a senha para autenticação, conforme mostrado abaixo:

d56765c6154c11a4.png

Agora que você está conectado, abra o editor SQL e siga para a próxima seção.

Importar o banco de dados do codebase

Use o editor de SQL para importar as tabelas image_catalog com os dados delas. Copie o código SQL dos arquivos no repositório ( 01_schema.sql e 02_seed.sql) e execute-os em ordem sequencial.

Depois disso, você vai receber duas tabelas no image_catalog, que são users e images, conforme mostrado abaixo:

65ba01e4c6c2dac0.png

Para testar, execute o seguinte no editor: select * from images;

Anote também o endereço IP público da instância do Cloud SQL, porque você vai precisar dele mais tarde. Para acessar o IP, acesse a página principal da instância do Cloud SQL em Visão geral. (Visão geral > Conectar a esta instância > Endereço IP público).

4. Módulo 2: conteinerizar seu app PHP

e7f0e9979d8805f5.png

Queremos criar esse app para a nuvem.

Isso significa empacotar o código em algum tipo de arquivo ZIP que contenha todas as informações para executá-lo na nuvem.

Há algumas maneiras de empacotar:

  • Docker Muito popular, mas bastante complexo de configurar corretamente.
  • Buildpacks. Menos popular, mas tende a "adivinhar automaticamente" o que criar e o que executar. Muitas vezes, ele simplesmente funciona!

Neste workshop, vamos presumir que você usa o Docker.

Se você escolheu usar o Cloud Shell, é hora de reabri-lo. Clique no canto superior direito do console do Cloud.

ec6a6b90b39e03e.png

Isso vai abrir um shell conveniente na parte de baixo da página, onde você deve ter bifurcado o código na etapa de configuração.

6999b906c0dedeb7.png

Docker

Se você quiser ter controle, essa é a solução certa. Isso faz sentido quando você precisa configurar bibliotecas específicas e injetar determinados comportamentos não óbvios (um chmod em uploads, um executável não padrão no seu app etc.).

Como queremos implantar nosso aplicativo conteinerizado no Cloud Run, consulte a seguinte documentação. Como você faria o backport do php 8 para o PHP 5.7? Talvez você possa usar o Gemini para isso. Como alternativa, use esta versão pré-criada:

# 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

A versão mais recente do Dockerfile está disponível aqui.

Para testar o aplicativo localmente, precisamos mudar o arquivo config.php para que o app PHP se conecte ao banco de dados MySQL disponível no Cloud SQL. Com base no que você configurou antes, preencha os espaços em branco:

<?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 é o endereço IP público do Cloud SQL. Ele pode ser encontrado no console do SQL:

bd27071bf450a8d0.png

  • DB_NAME não foi alterado: image_catalog
  • DB_USER precisa ser appmod-phpapp-user
  • DB_PASS é algo que você escolheu. Defina entre aspas simples e use escape conforme necessário.

Traduza os poucos trechos em 🇮🇹 italiano para inglês com a ajuda do Gemini!

Agora que você tem o Dockerfile e configurou o app PHP para se conectar ao banco de dados, vamos testar!

Instale o Docker se ainda não tiver feito isso ( link). Isso não é necessário se você estiver usando o Cloud Shell.

Agora, tente criar e executar seu app PHP conteinerizado com os comandos de build e execução do Docker adequados.

# 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 tudo estiver funcionando, você vai conseguir ver a seguinte página da Web quando estiver conectado ao host local. Agora que o app está sendo executado na porta 8080, clique no ícone "Visualização na Web" (um navegador com um olho) e em Visualizar na porta 8080 (ou "Alterar porta" para qualquer outra porta).

33a24673f4550454.png

Testar o resultado no navegador

Agora, seu aplicativo vai ficar assim:

2718ece96b1f18b6.png

Se você fizer login com Admin/admin123, verá algo assim.

68b62048c2e86aea.png

Ótimo! Além do texto em italiano, está tudo funcionando! 🎉🎉🎉

Se a dockerização estiver boa, mas as credenciais do banco de dados estiverem erradas, você poderá receber algo assim:

e22f45b79bab86e1.png

Tente de novo, você está quase lá!

Salvar no Artifact Registry [opcional]

Neste momento, você já deve ter um aplicativo PHP conteinerizado funcional pronto para ser implantado na nuvem. Em seguida, precisamos de um lugar na nuvem para armazenar a imagem Docker e torná-la acessível para implantação em serviços do Google Cloud, como o Cloud Run. Essa solução de armazenamento é chamada de Artifact Registry, um serviço totalmente gerenciado do Google Cloud projetado para armazenar artefatos de aplicativos, incluindo imagens de contêiner do Docker, pacotes Maven, módulos npm e muito mais.

Vamos criar um repositório no Artifact Registry do Google Cloud usando o botão apropriado.

e1123f0c924022e6.png

Escolha um nome válido, o formato e a região adequados para armazenar os artefatos.

4e516ed209c470ee.png

Volte ao ambiente de desenvolvimento local, marque e envie a imagem do contêiner do app para o repositório do Artifact Registry que você acabou de criar. Conclua os comandos a seguir para fazer isso.

  • docker tag SOURCE_IMAGE[:TAG] TARGET_IMAGE[:TAG]
  • docker push TARGET_IMAGE[:TAG]

O resultado será semelhante à captura de tela a seguir.

1e498feb4e88be9f.png

Oba 🎉🎉🎉, você pode passar para o próximo nível. Antes disso, talvez passe dois minutos tentando fazer upload/login/logout e se familiarizando com os endpoints do app.Você vai precisar deles mais tarde.

Possíveis erros

Se você receber erros de contêinerização, use o Gemini para explicar e corrigir o erro, fornecendo:

  • Seu Dockerfile atual
  • O erro recebido
  • [se necessário] o código PHP em execução.

Permissões de upload. Tente também o endpoint /upload.php e faça o upload de uma imagem. Você pode receber o erro abaixo. Se for o caso, você precisa fazer uma correção de chmod/chown no Dockerfile.

Aviso: move_uploaded_file(uploads/image (3).png): falha ao abrir o fluxo: permissão negada em /var/www/html/upload.php na linha 11

PDOException "could not find driver" (ou "Errore di connessione: could not find driver"). Verifique se o Dockerfile tem as bibliotecas PDO adequadas para mysql (pdo_mysql) para se conectar ao banco de dados. Inspire-se com as soluções aqui.

Não foi possível encaminhar sua solicitação para um back-end. Não foi possível se conectar a um servidor na porta 8080. Isso significa que você provavelmente está expondo a porta errada. Verifique se você está expondo a porta em que o Apache/Nginx está veiculando. Isso não é trivial. Se possível, use a porta 8080 (facilita o uso do Cloud Run). Se você quiser manter a porta 80 (por exemplo, porque o Apache quer assim), use um comando diferente para executar:

$ 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. Módulo 3: implante o app no Cloud Run

9ffca42774f6c5d1.png

Por que o Cloud Run?

Boa pergunta! Há alguns anos, você certamente teria escolhido o Google App Engine.

Em resumo, hoje, o Cloud Run tem uma pilha de tecnologia mais recente, é mais fácil de implantar, mais barato e reduz a escala para zero quando você não o usa. Com a flexibilidade de executar qualquer contêiner sem estado e a integração com vários serviços do Google Cloud, ele é ideal para implantar microsserviços e aplicativos modernos com sobrecarga mínima e eficiência máxima.

Mais especificamente, o Cloud Run é uma plataforma totalmente gerenciada pelo Google Cloud que permite executar aplicativos conteinerizados sem estado em um ambiente sem servidor. Ele processa automaticamente toda a infraestrutura, escalonando de zero para atender ao tráfego de entrada e diminuindo quando está ocioso, o que o torna econômico e eficiente. O Cloud Run oferece suporte a qualquer linguagem ou biblioteca, desde que esteja empacotada em um contêiner, o que permite grande flexibilidade no desenvolvimento. Ele se integra bem a outros serviços do Google Cloud e é adequado para criar microsserviços, APIs, sites e aplicativos orientados a eventos sem precisar gerenciar a infraestrutura de servidores.

Pré-requisitos

Para realizar essa tarefa, você precisa ter o gcloud instalado na sua máquina local. Caso contrário, consulte as instruções aqui. Se você estiver no Google Cloud Shell, não será necessário fazer nada.

Antes de implantar...

Se você estiver trabalhando no seu ambiente local, faça a autenticação no Google Cloud com o seguinte

  • $ gcloud auth login –update-adc # not needed in Cloud Shell

Isso vai autenticar você com um login do OAuth no navegador. Faça login no Chrome com o mesmo usuário (por exemplo, vattelapesca@gmail.com) que fez login no Google Cloud com o faturamento ativado.

Ative a API Cloud Run com o seguinte comando:

  • $ gcloud services enable run.googleapis.com cloudbuild.googleapis.com

Nesse momento, tudo está pronto para ser implantado no Cloud Run.

Implantar o app no Cloud Run usando o gcloud

O comando que permite implantar o app no Cloud Run é gcloud run deploy. Há várias opções para definir e alcançar sua meta. O conjunto mínimo de opções (que você pode fornecer pela linha de comando ou a ferramenta vai perguntar com uma solicitação interativa) é o seguinte:

  1. O nome do serviço do Cloud Run que você quer implantar para seu app. Um serviço do Cloud Run vai retornar um URL que fornece um endpoint para seu app.
  2. A região do Google Cloud em que o app será executado. (--region REGIÃO)
  3. Imagem do contêiner que encapsula seu app.
  4. Variáveis de ambiente que seu app precisa usar durante a execução.
  5. A flag Allow-Unauthenticated que permite que todos acessem seu app sem mais autenticação.

Consulte a documentação (ou role a tela para baixo e confira uma possível solução) para saber como aplicar essa opção à sua linha de comando.

A implantação leva alguns minutos. Se tudo estiver correto, você verá algo assim no Console do Google Cloud.

ef1029fb62f8de81.png

f7191d579c21ca3e.png

Clique no URL fornecido pelo Cloud Run e teste o aplicativo. Depois da autenticação, você vai ver algo assim.

d571a90cd5a373f9.png

"gcloud run deploy" sem argumentos

Você deve ter notado que o gcloud run deploy faz as perguntas certas e preenche as lacunas que você deixou. Que máximo!

No entanto, em alguns módulos, vamos adicionar esse comando a um gatilho de build do Cloud Build, então não podemos ter perguntas interativas. Precisamos preencher todas as opções no comando. Então você quer criar o gcloud run deploy --option1 blah --foo bar --region your-fav-region de ouro. Como fazer isso?

  1. Repita as etapas 2 a 4 até que o gcloud pare de fazer perguntas:
  2. [LOOP] gcloud run deploy com as opções encontradas até agora
  3. [LOOP] sistemas pedem a opção X
  4. [LOOP] Pesquise na documentação pública como configurar X na CLI, adicionando a opção --my-option [my-value].
  5. Volte à etapa 2, a menos que o gcloud seja concluído sem mais perguntas.
  6. Este gcloud run deploy BLAH BLAH BLAH é incrível! Salve o comando em algum lugar, porque você vai precisar dele mais tarde na etapa do Cloud Build.

Uma possível solução está aqui. A documentação está aqui.

Parabéns 🎉🎉🎉 Você implantou seu app no Google Cloud e concluiu a primeira etapa da modernização.

6. Módulo 4: senha limpa com o Secret Manager

95cd57b03b4e3c73.png

Na etapa anterior, conseguimos implantar e executar nosso app no Cloud Run. No entanto, fizemos isso com uma prática ruim de segurança: fornecer alguns secrets em texto simples.

Primeira iteração: atualize o config.php para usar ENV

Talvez você tenha notado que colocamos a senha do banco de dados diretamente no código do arquivo config.php. Isso é bom para fins de teste e para verificar se o app funciona. Mas não é possível confirmar/usar código dessa forma em um ambiente de produção. A senha (e outros parâmetros de conexão do banco de dados) precisa ser lida dinamicamente e fornecida ao app no ambiente de execução. Mude o arquivo config.php para que ele leia os parâmetros do banco de dados das variáveis de ambiente. Se isso não funcionar, considere definir valores padrão. Isso é bom caso você não consiga carregar o ENV. Assim, a saída da página vai informar se ela está usando os valores padrão. Preencha os espaços em branco e substitua o código em 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();
?>

Como o app está conteinerizado, você precisa fornecer uma maneira de disponibilizar as variáveis de ambiente para ele. Isso pode ser feito de algumas maneiras:

  • No momento do build, no Dockerfile. Adicione ao Dockerfile anterior os quatro parâmetros usando a sintaxe ENV DB_VAR=ENV_VAR_VALUE. Isso vai definir valores padrão que podem ser substituídos no ambiente de execução. Por exemplo, "DB_NAME" e "DB_USER" podem ser definidos aqui e em nenhum outro lugar.
  • No tempo de execução. É possível configurar essas variáveis para o Cloud Run, tanto na CLI quanto na interface. Este é o lugar certo para colocar todas as quatro variáveis, a menos que você queira manter os padrões definidos no Dockerfile.

No localhost, talvez seja interessante colocar as variáveis de ambiente em um arquivo .env (confira a pasta solutions).

Além disso, verifique se .env foi adicionado ao seu .gitignore. Não é recomendável enviar seus secrets para o GitHub.

echo .env >> .gitignore

Depois disso, teste a instância localmente:

docker run -it -p 8080:8080 --env-file .env my-php-app-docker

Agora você tem o seguinte:

  1. Seu app vai ler a variável dinamicamente do ENV
  2. Você melhorou a segurança ao remover a senha do banco de dados do seu código)

Agora é possível implantar uma nova revisão no Cloud Run. Vamos acessar a interface e definir as variáveis de ambiente manualmente:

  • Acesse https://console.cloud.google.com/run.
  • Clique no seu app
  • Clique em "Editar e implantar uma nova revisão".
  • Na primeira guia "Contêiner(es)", clique na guia de baixo "Variáveis e secrets".
  • Clique em "+ Adicionar variável" e inclua todas as variáveis necessárias. O resultado será algo parecido com isto:

7a5fbfa448544d3.png

f2780c35585388ca.png

Isso é perfeito? Não. Seu PASS ainda está visível para a maioria dos operadores. Isso pode ser evitado com o Google Cloud Secret Manager.

Segunda iteração: Secret Manager

Suas senhas desapareceram do seu próprio código: vitória! Mas espere, já estamos seguros?

Suas senhas ainda ficam visíveis para qualquer pessoa que tenha acesso ao console do Google Cloud. Na verdade, se você acessar o arquivo YAML de implantação do Cloud Run, poderá recuperá-lo. Ou, se você tentar editar ou implantar uma nova revisão do Cloud Run, a senha vai aparecer na seção "Variáveis e chaves secretas", conforme mostrado nas capturas de tela abaixo.

O Secret Manager do Google Cloud é um serviço seguro e centralizado para gerenciar informações sensíveis, como chaves de API, senhas, certificados e outros secrets.

Ele permite armazenar, gerenciar e acessar secrets com permissões refinadas e criptografia robusta. Integrado ao Identity and Access Management (IAM) do Google Cloud, o Secret Manager permite controlar quem pode acessar segredos específicos, garantindo a segurança dos dados e a conformidade regulatória.

Ele também oferece suporte à rotação e ao controle de versões automáticos de secrets, simplificando o gerenciamento do ciclo de vida de secrets e aumentando a segurança em aplicativos nos serviços do Google Cloud.

Para acessar o Secret Manager, navegue do menu de hambúrguer até os serviços de Segurança e encontre-o na seção Proteção de dados, como mostrado na captura de tela abaixo.

6df83a1c3cb757f6.png

Ative a API Secret Manager conforme mostrado na imagem a seguir.

a96c312e2c098db1.png

  • Agora clique em Criar um secret. Vamos chamar de forma racional:
  • Nome: php-amarcord-db-pass
  • Valor do secret: sua senha do banco de dados (ignore a parte "fazer upload do arquivo").
  • anote este link secreto, que deve ser parecido com projects/123456789012/secrets/php-amarcord-db-pass. Este é o ponteiro exclusivo para seu secret (para Terraform, Cloud Run e outros). Esse é o número exclusivo do seu projeto.

Dica: tente usar convenções de nomenclatura consistentes para seus secrets, especializando da esquerda para a direita, por exemplo: cloud-devrel-phpamarcord-dbpass

  • Organização (com a empresa)
  • Equipe (na organização)
  • Aplicativo (na equipe)
  • Nome da variável (no app)

Assim, você terá expressões regulares fáceis para encontrar todos os seus segredos em um único app.

Criar uma revisão do Cloud Run

Agora que temos um novo secret, precisamos remover a variável de ambiente DB_PASS e substituí-la pelo novo secret. Então:

  • Acesso ao Cloud Run usando o console do Google Cloud
  • Escolha o app.
  • Clique em "Editar e implantar uma nova revisão".
  • Localize a guia "Variáveis e secrets".
  • Use o botão "+ Referenciar um secret" para redefinir a variável de ambiente DB_PASS.
  • Use a mesma "DB_PASS" para os Secrets referenciados e use a versão mais recente.

9ed4e35be7654dcb.png

Quando terminar, você vai receber o seguinte erro:

da0ccd7af39b04ed.png

Tente descobrir como corrigir isso. Para resolver isso, acesse a seção IAM e administrador e mude as permissões de concessão. Boa depuração!

Depois de descobrir, volte ao Cloud Run e reimplante uma nova revisão. O resultado será semelhante à figura a seguir:

e89f9ca780169b6b.png

Dica: o console para desenvolvedores (interface) é ótimo para apontar problemas de permissão. Navegue por todos os links das suas entidades do Cloud.

7. Módulo 5: configurar o CI/CD com o Cloud Build

ba49b033c11be94c.png

Por que um pipeline de CI/CD?

Até agora, você já digitou gcloud run deploy algumas vezes, talvez respondendo à mesma pergunta várias vezes.

Cansou de implantar manualmente seu app com gcloud run deploy? Não seria ótimo se o app pudesse se implantar automaticamente sempre que você enviasse uma nova mudança para o repositório Git?

Para usar um pipeline de CI/CD, você precisa de duas coisas:

  1. Um repositório Git pessoal: felizmente, você já deve ter ramificado o repositório do workshop para sua conta do GitHub na etapa 2. Se não, volte e conclua essa etapa. O repositório bifurcado vai ficar assim: https://github.com/<YOUR_GITHUB_USER>/app-mod-workshop
  2. Cloud Build. Esse serviço incrível e barato permite configurar automações de build para praticamente tudo: Terraform, apps em contêineres Docker etc.

Esta seção vai se concentrar na configuração do Cloud Build.

Entre no Cloud Build!

Vamos usar o Cloud Build para isso:

  • crie sua origem (com Dockerfile). Pense nisso como um "arquivo .zip grande" que contém tudo o que você precisa para criar e executar (seu "artefato de build").
  • envie o artefato para o Artifact Registry (AR).
  • Em seguida, faça uma implantação do AR no Cloud Run para o app "php-amarcord".
  • Isso vai criar uma nova versão ("revisão") do app atual (pense em uma camada com o novo código), e vamos configurá-la para desviar o tráfego para a nova versão se o push for bem-sucedido.

Este é um exemplo de algumas builds do meu app php-amarcord:

f30f42d4571ad5e2.png

Como fazemos tudo isso?

  1. Criando um arquivo YAML perfeito: cloudbuild.yaml
  2. Criando um gatilho de build do Cloud Build.
  3. Conectando-se ao nosso repositório do GitHub pela interface do Cloud Build.

Criar gatilho (e conectar repositório)

  • Acesse https://console.cloud.google.com/cloud-build/triggers.
  • Clique em "Criar gatilho".
  • Compilar:
  • Nome: algo significativo, como on-git-commit-build-php-app
  • Evento: enviar por push para a ramificação
  • Origem: "Conectar novo repositório" texto alternativo
  • Uma janela será aberta à direita: "Conectar repositório".
  • Provedor de origem: "Github" (primeiro)
  • "Continuar"
  • A autenticação vai abrir uma janela no GitHub para fazer a autenticação cruzada. Siga o fluxo e tenha paciência. Se você tiver muitos repositórios, isso pode levar um tempo.
  • "Selecionar repositório": escolha sua conta/repositório e marque a caixa "Eu entendo...".
  • Se você receber o erro: o app GitHub não está instalado em nenhum dos seus repositórios, clique em "Instalar o Google Cloud Build" e siga as instruções.
  • 23e0e0f1219afea3.pngClique em "Conectar".
  • bafd904ec07122d2.png
  • Bingo! Seu repositório está conectado.
  • Voltando à parte do acionador...
  • Configuração: detectada automaticamente (*)
  • Avançado: selecione a conta de serviço "[PROJECT_NUMBER]- compute@developer.gserviceaccount.com"
  • xxxxx é o ID do projeto
  • A conta de serviço de computação padrão é adequada para uma abordagem de laboratório, mas não a use em produção. ( Saiba mais).
  • deixe todo o resto como está.
  • Clique no botão "Criar".

(*) Essa é a maneira mais simples, já que verifica Dockerfile ou cloudbuild.yaml. No entanto, o cloudbuild.yaml dá a você o poder real de decidir o que fazer em cada etapa.

Eu tenho o poder!

Agora, o gatilho não vai funcionar a menos que você conceda à conta de serviço do Cloud Build (o que é uma conta de serviço? O e-mail de um "robô" que age em seu nome para uma tarefa, neste caso, criar coisas na nuvem.

Seu SA não vai conseguir criar e implantar a menos que você o autorize a fazer isso. Felizmente, é fácil!

  • Acesse "Cloud Build" > " Configurações".
  • Conta de serviço "[PROJECT_NUMBER]- compute@developer.gserviceaccount.com"
  • Marque estas caixas:
  • Cloud Run
  • Secret Manager
  • Contas de serviço
  • Cloud Build
  • Marque também a opção "Definir como conta de serviço preferencial".

8715acca72286a46.png

Onde está o YAML do Cloud Build?

Recomendamos que você crie seu próprio YAML do Cloud Build.

No entanto, se você não tiver tempo ou não quiser dedicar tempo, confira algumas ideias na pasta de soluções: .solutions

Agora você pode enviar uma mudança para o GitHub e observar o Cloud Build fazer a parte dele.

Configurar o Cloud Build pode ser complicado. Aguarde algumas trocas de mensagens por:

  • Verificar registros em https://console.cloud.google.com/cloud-build/builds;region=global
  • Encontrar seu erro.
  • Corrigir no código e emitir novamente git commit / git push.
  • Às vezes, o erro não está no código, mas em alguma configuração. Nesse caso, você pode emitir um novo build na interface (Cloud Build > "Gatilhos" > Executar).

97acd16980a144ab.png

Se você usar essa solução, ainda vai precisar fazer algumas coisas. Por exemplo, definir as variáveis de ambiente para os endpoints de desenvolvimento/produção recém-criados:

3da8723e4ff80c0a.png

Faça isso de duas maneiras:

  • Pela interface: definindo variáveis de ambiente novamente
  • Pela CLI, criando o script "perfeito" para você. Um exemplo pode ser encontrado aqui: gcloud-run-deploy.sh . Você precisa ajustar algumas coisas, como o endpoint e o número do projeto. Encontre o número do projeto na Visão geral do Cloud.

Como faço commit de código no GitHub?

Não é o objetivo deste workshop ensinar a melhor maneira de git push para o GitHub. No entanto, se você estiver com dificuldades e estiver no Cloud Shell, há duas maneiras de resolver isso:

  1. CLI. Adicione uma chave ssh localmente e um remoto com git@github.com:YOUR_USER/app-mod-workshop.git (em vez de http)
  2. VSCode. Se você usa o editor do Cloud Shell, pode usar a guia "Controle de origem" (ctrl+shift+G), clicar em "Sincronizar mudanças" e seguir as instruções. Você poderá autenticar sua conta do GitHub no VS Code, e o pull/push vai ficar muito mais fácil.

f0d53f839c7fa3b6.png

Não se esqueça de git add clodubuild.yaml entre outros arquivos, ou ele não vai funcionar.

Paridade de desenvolvimento/produção completa x superficial [opcional]

Se você copiou a versão do modelo aqui, terá duas versões DEV e PROD idênticas. Isso é legal e está de acordo com a regra 10 do app de 12 fatores.

No entanto, estamos usando dois endpoints da Web diferentes para ter um app apontando para o mesmo banco de dados. Isso é bom o suficiente para um workshop, mas, na vida real, é melhor dedicar algum tempo para criar um ambiente de produção adequado. Isso significa ter dois bancos de dados (um para desenvolvimento e outro para produção) e também escolher onde eles vão ficar para recuperação de desastres / alta disponibilidade. Isso está fora do escopo deste workshop, mas é algo para pensar.

Se você tiver tempo para fazer uma versão "detalhada" da produção, lembre-se de todos os recursos que precisam ser duplicados, como:

  • Banco de dados do Cloud SQL (e provavelmente instância do SQL).
  • Bucket do GCS
  • Função do Cloud.
  • Você pode usar o Gemini 1.5 Flash como um modelo em desenvolvimento (mais barato e rápido) e o Gemini 1.5 Pro (mais eficiente).

Em geral, sempre que você fizer algo no app, pense de forma crítica: a produção deve ter o mesmo valor ou não? Caso contrário, duplique seu esforço. Isso é muito mais fácil com o Terraform, em que você pode injetar seu ambiente (-dev, -prod) como um sufixo nos seus recursos.

8. Módulo 6: Migrar para o Google Cloud Storage

a680e0f287dd2dfb.png

Armazenamento

dc3a4b8ea92aaef6.png

No momento, o app armazena o estado em um contêiner do Docker. Se a máquina quebrar, o app falhar ou se você enviar uma nova revisão, uma nova revisão será programada com um armazenamento vazio: 🙈

Como podemos corrigir isso? Há várias abordagens.

  1. Armazenar imagens no banco de dados. Foi o que acabei fazendo com meu app PHP anterior. É a solução mais simples, já que não adiciona complexidade. Mas isso adiciona latência e carga ao seu banco de dados com certeza.
  2. Migre seu app do Cloud Run para uma solução compatível com armazenamento: GCE + disco permanente? Talvez GKE + Storage? Observação: o que você ganha em controle, perde em agilidade.
  3. Mova para o GCS. O Google Cloud Storage oferece o melhor armazenamento da categoria para todo o Google Cloud e é a solução mais idiomática da nuvem. No entanto, isso exige que trabalhemos com bibliotecas PHP. Temos bibliotecas PHP 5.7 para o GCS? O PHP 5.7 é compatível com Composer? Parece que o PHP 5.3.2 é a versão mais antiga compatível com o Composer.
  4. Talvez usar um sidecar do Docker?
  5. Ou use montagens de volume do Cloud Run do GCS. Isso parece incrível.

🤔 Migrar armazenamento (resposta aberta)

[Resposta aberta] Neste exercício, queremos que você encontre uma solução para mover suas imagens de uma forma que seja mantida de alguma forma.

Teste de aceitação

Não quero contar a solução, mas quero que isso aconteça:

  1. Você faz upload de newpic.jpg. Ele aparece no app.
  2. Você atualiza o app para uma nova versão.
  3. O newpic.jpg ainda está lá, visível.

💡 Solução possível (montagens de volume do Cloud Run do GCS)

Essa é uma solução muito elegante que permite fazer uploads de arquivos com estado sem tocar no código (além de mostrar uma descrição da imagem, mas isso é trivial e apenas para satisfação visual).

Isso permite montar uma pasta do Cloud Run no GCS. Portanto:

  1. Todos os uploads para o GCS vão ficar visíveis no seu app.
  2. Todos os uploads para seu app serão enviados ao GCS
  3. A mágica vai acontecer com os objetos enviados para o GCS (capítulo 7).

Observação: Leia os termos em letras miúdas do FUSE. Isso NÃO é aceitável se o desempenho for um problema.

Criar um bucket do GCS

O GCS é o serviço de armazenamento onipresente do Google Cloud. Ele é testado em batalha e usado por todos os serviços do GCP que precisam de armazenamento.

O Cloud Shell exporta PROJECT_ID como 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/"

Configure o Cloud Run para montar o bucket na pasta /uploads/

Agora vamos para a parte elegante. Criamos um volume php_uploads e instruímos o Cloud Run a fazer uma montagem do FUSE em MOUNT_PATH (algo como /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"

Agora, repita essa etapa para todos os endpoints que você quer apontar para o Cloud Storage.

Você também pode fazer isso na interface

  1. Na guia "Volumes", crie uma montagem de volume que aponte para seu bucket, do tipo "Bucket do Cloud Storage", por exemplo, com o nome "php_uploads".
  2. Em "Contêineres" > "Montagens de volume", monte o volume que você acabou de criar no ponto de volume solicitado pelo app. Isso depende do Dockerfile, mas pode ser algo como var/www/html/uploads/ .

De qualquer forma, se funcionar, a edição da nova revisão do Cloud Run vai mostrar algo assim:

6c2bb98fc1b0e077.png

Agora teste o novo aplicativo enviando uma nova imagem para o endpoint /upload.php.

As imagens precisam fluir sem problemas no GCS sem escrever uma única linha de PHP:

70032b216afee2d7.png

O que aconteceu?

Algo mágico aconteceu.

Um aplicativo antigo com código antigo ainda está fazendo o trabalho dele. Uma nova pilha modernizada permite que todas as imagens/fotos no nosso app fiquem confortavelmente em um bucket do Cloud com estado. Agora o céu é o limite:

  • Quer enviar um e-mail sempre que uma imagem com "perigoso" ou "nudez" chegar? Você pode fazer isso sem mexer no código PHP.
  • Quer usar um modelo multimodal do Gemini sempre que uma imagem chegar para descrevê-la e fazer upload do banco de dados com a descrição? Você pode fazer isso sem mexer no código PHP. Não acredita em mim? Continue lendo no capítulo 7.

Acabamos de abrir um grande espaço de oportunidade aqui.

9. Módulo 7: Potencialize seu app com o Google Gemini

c00425f0ad83b32c.png

Agora você tem um app PHP moderno, novo e incrível (como um Fiat 126 de 2024) com armazenamento em nuvem.

O que você pode fazer com ele?

Pré-requisitos

No capítulo anterior, uma solução de modelo permitiu montar imagens /uploads/ no GCS, separando de fato a lógica do app do armazenamento de imagens.

Neste exercício, você vai:

  • Concluir o exercício do capítulo 6 (armazenamento).
  • Tenha um bucket do GCS com os uploads de imagens, em que as pessoas fazem upload de fotos no seu app e elas são enviadas para o bucket.

Configurar uma função do Cloud (em Python)

Você já se perguntou como implementar um aplicativo orientado a eventos? Algo assim:

  • quando <event> acontecer => envie um e-mail
  • quando <event> acontece => se <condition> for verdadeiro, atualize o banco de dados.

Um evento pode ser qualquer coisa, desde um novo registro disponível no BigQuery, um novo objeto alterado em uma pasta no GCS ou uma nova mensagem aguardando em uma fila no Pub/Sub.

O Google Cloud oferece suporte a vários paradigmas para isso. Em especial:

Neste exercício, vamos nos aprofundar na função do Cloud para alcançar um resultado espetacular. e vamos oferecer exercícios opcionais para você.

O exemplo de código é fornecido em .solutions/

Configurar uma função do Cloud (🐍 Python)

Estamos tentando criar um GCF muito ambicioso.

  1. Quando uma nova imagem é criada no GCS… (provavelmente porque alguém fez upload no app, mas não apenas)
  2. .. peça ao Gemini para descrever e receber uma descrição textual da imagem .. (seria bom verificar o MIME e garantir que é uma imagem e não um PDF, MP3 ou texto)
  3. .. e atualize o banco de dados com essa descrição. Isso pode exigir a correção do banco de dados para adicionar uma coluna description à tabela images.

Adicione description às imagens ao banco de dados

  1. Abra o Cloud SQL Studio:

b92b07c4cba658ef.png

  1. Insira seu usuário e senha do banco de dados de imagens.
  2. Injete este SQL, que adiciona uma coluna para uma descrição da imagem:

ALTER TABLE images ADD COLUMN description TEXT;

3691aced78a6389.png

E bingo! Teste agora para verificar se funcionou:

SELECT * FROM images;

A nova coluna de descrição vai aparecer:

bed69d6ad0263114.png

Escreva a função f(x) do Gemini

Observação: Essa função foi criada com a ajuda do Gemini Code Assist.

Observação: Ao criar essa função, você pode ter erros de permissão do IAM. Alguns estão documentados abaixo no parágrafo "Possíveis erros".

  1. Ative as APIs
  2. Acesse https://console.cloud.google.com/functions/list.
  3. Clique em "Criar função".
  4. Ative as APIs no assistente de APIs:

d22b82658cfd4c48.png

É possível criar a GCF na interface ou na linha de comando. Aqui vamos usar a linha de comando.

Um possível código pode ser encontrado em .solutions/

  1. Crie uma pasta para hospedar seu código, por exemplo, "gcf/". Acesse a pasta.
  2. Crie um arquivo requirements.txt:
google-cloud-storage
google-cloud-aiplatform
pymysql
  1. Crie uma função em Python. Exemplo de código aqui: 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
  1. Envie a função. Você pode usar um script semelhante a este: gcf/push-to-gcf.sh.

Observação 1. Verifique se você está usando os ENVs com os valores corretos ou apenas adicione-os na parte de cima (GS_BUCKET=blah, ..):

Observação 2. Isso vai enviar todo o código local (.). Portanto, coloque o código em uma pasta específica e use .gcloudignore como um profissional para evitar o envio de bibliotecas grandes. ( exemplo).

#!/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

Observação: neste exemplo, generate_caption será o método invocado, e a Função do Cloud vai transmitir o evento do GCS com todas as informações relevantes (nome do bucket, nome do objeto etc.). Dedique algum tempo para depurar esse dicionário Python de eventos.

Como testar a função

Testes de unidade

A função tem muitas variáveis. Talvez você queira testar todos os solteiros.

Um exemplo está em gcf/test.py.

Interface do Cloud Functions

Reserve um tempo para explorar a função na interface. Vale a pena explorar todas as guias, principalmente Source (minha favorita), Variables, Trigger e Logs. Você vai passar muito tempo em Logs para resolver erros. Consulte também os possíveis erros na parte de baixo desta página. Não se esqueça de verificar Permissions.

cf3ded30d532a2c7.png

Teste E2E

É hora de testar a função manualmente.

  1. Acesse o app e faça login.
  2. Faça upload de uma foto (não muito grande, já tivemos problemas com imagens grandes)
  3. Verifique na interface se a imagem foi enviada.
  4. Verifique no Cloud SQL Studio se a descrição foi atualizada. Faça login e execute esta consulta: SELECT * FROM images.

43a680b12dbbdda0.png

E funciona. Também podemos atualizar o front-end para mostrar essa descrição.

Atualizar o PHP para mostrar [opcional]

Provamos que o app funciona. No entanto, seria bom que os usuários também pudessem ver essa descrição.

Não é preciso ser especialista em PHP para adicionar a descrição ao index.php. Este código deve fazer (sim, o Gemini também escreveu para mim!):

<?php if (!empty($image['description'])): ?>
    <p class="font-bold">Gemini Caption:</p>
    <p class="italic"><?php echo $image['description']; ?></p>
<?php endif; ?>

Posicione esse código dentro do foreach como preferir.

Nas próximas etapas, também vamos mostrar uma versão mais bonita da interface, graças ao Gemini Code Assist. Uma versão mais bonita seria assim:

fdc12de0c88c4464.png

Conclusões

Você tem uma função do Cloud acionada em novos objetos que chegam ao GCS, capaz de anotar o conteúdo da imagem como um humano faria e atualizar automaticamente o banco de dados. Uau!

A seguir Você pode seguir o mesmo raciocínio para alcançar duas funcionalidades incríveis.

[opcional] Adicionar mais Cloud Functions [aberto]

Alguns outros recursos vêm à mente.

📩 Gatilho de e-mail

Um gatilho de e-mail que envia uma mensagem sempre que alguém envia uma foto.

  • Com muita frequência? Adicione outra restrição: uma imagem GRANDE ou uma imagem cujo conteúdo do Gemini contenha as palavras "nudez/violento".
  • Considere verificar EventArc para isso.

🚫 Moderação automática de fotos inapropriadas

No momento, um administrador humano está sinalizando imagens como "inadequadas". Que tal deixar o Gemini fazer a parte mais difícil e moderar o espaço? Adicione um teste para sinalizar conteúdo de gatilho inadequado e atualize o banco de dados como aprendemos na função anterior. Isso significa basicamente pegar a função anterior, mudar o comando e atualizar o banco de dados com base na resposta.

Ressalva. A IA generativa tem saídas imprevisíveis. Verifique se a "saída criativa" do Gemini está "nos trilhos". Você pode pedir uma resposta determinista, como um nível de confiança de 0 a 1, um JSON etc. Isso pode ser feito de várias maneiras, por exemplo: * Usando bibliotecas Python pydantic, langchain etc. * Usando a saída estruturada do Gemini.

Dica. Você pode ter VÁRIAS funções ou um único comando que exige uma resposta JSON (funciona muito bem com a "Saída estruturada do Gemini", conforme destacado acima), como:

Qual seria o comando para gerar isso?

{
    "description": "This is the picture of an arrosticino",
    "suitable": TRUE
}

Você pode adicionar mais campos ao comando para receber insights como: há algo de bom nisso? Isso é ruim? Você reconhece o lugar? Há algum texto (o OCR nunca foi tão fácil):

  • goods: "Parece uma comida deliciosa"
  • bads: "Parece comida não saudável"
  • OCR: "Da consumare preferibilmente prima del 10 Novembre 2024"
  • location: "Pescara, Lungomare"

Embora geralmente seja melhor ter uma função N para N resultados, é muito gratificante fazer uma que faça 10 coisas. Confira este artigo do Riccardo para saber como.

Possíveis erros (principalmente IAM / permissões)

Na primeira vez que desenvolvi essa solução, encontrei alguns problemas de permissão do IAM. Vou adicioná-los aqui por empatia e para dar algumas ideias de como corrigir esses problemas.

Erro: não há permissões suficientes para a conta de serviço

  1. Para implantar uma função do GCF que escuta um bucket do GCS, configure as permissões adequadas para a conta de serviço que você está usando no job, conforme mostrado na figura:

22f51012fa6b4a24.png

Talvez seja necessário ativar as APIs do EventArc alguns minutos antes de elas ficarem totalmente disponíveis.

Erro: invocador do Cloud Run ausente

  1. Outro comentário da interface para permissões do GCF é este ( papel de invocador do Cloud Run):

be72e17294f2d3f3.png

Para corrigir esse erro, execute o comando na imagem, que é semelhante a fix-permissions.sh.

Esse problema é descrito aqui: https://cloud.google.com/functions/docs/securing/authenticating

Erro: limite de memória excedido

Na primeira vez que executei o comando, meus registros disseram: "O limite de memória de 244 MiB foi excedido com 270 MiB usados. Considere aumentar o limite de memória. Consulte https://cloud.google.com/functions/docs/configuring/memory". Adicione RAM ao GCF novamente. É muito fácil fazer isso na interface. Confira um possível aumento:

bed69d6ad0263114.png

Como alternativa, é possível corrigir o script de implantação do Cloud Run para aumentar a MEM/CPU. Isso leva um pouco mais de tempo.

Erro: PubSub publicado

Ao criar um gatilho com o GCF v1, este erro foi exibido:

e5c338ee35ad4c24.png

Novamente, isso é fácil de corrigir. Basta acessar IAM e conceder à sua conta de serviço o papel de "Publicador do Pub/Sub".

Erro: a Vertex AI não foi usada

Se você receber este erro:

Permissão negada: a API Vertex AI 403 não foi usada no projeto YOUR_PROJECT ou está desativada. Acesse https://console.developers.google.com/apis/api/aiplatform.googleapis.com/overview?project=YOR_PROJECT para ativar.

Basta ativar as APIs da Vertex AI. A maneira mais fácil de ativar TODAS as APIs necessárias é esta:

  1. https://console.cloud.google.com/vertex-ai
  2. Clique em "Ativar todas as APIs recomendadas".

492f05ac377f3630.png

Erro: gatilho do EventArc não encontrado.

Se isso acontecer, implante a função novamente.

8ec4fc11833d7420.png

Erro: 400 Agentes de serviço estão sendo provisionados

400 Service agents are being provisioned ( https://cloud.google.com/vertex-ai/docs/general/access-control#service-agents ). Os agentes de serviço são necessários para ler o arquivo do Cloud Storage fornecido. Tente de novo em alguns minutos.

Se isso acontecer, aguarde um pouco ou pergunte a um Googler.

10. Módulo 8: criar SLOs de disponibilidade

No capítulo, tentamos fazer o seguinte:

  1. Como criar SLIs
  2. Como criar SLOs com base nos SLIs
  3. Como criar alertas com base em SLOs

f63426182c052123.png

Este é um tema muito importante para o autor, já que Riccardo trabalha na área de SRE / DevOps do Google Cloud.

(aberta) Crie SLIs e SLOs para este app

De que adianta um app se você não consegue saber quando ele está inativo?

O que é um SLO?

Ah! O Google inventou os SLOs! Para saber mais sobre isso, sugiro:

Etapa 1: criar um SLI/SLO de disponibilidade

Vamos começar com o SLO de disponibilidade, já que é o mais fácil e talvez o mais importante que você quer medir.

Felizmente, o Cloud Run vem com suporte a SLOs pré-criado, graças ao Istio.

Depois que o app estiver no Cloud Run, isso será muito simples de fazer. Leva 30 segundos.

  • Acesse a página do Cloud Run.
  • Clique/selecione seu app.
  • Selecione a guia SLOs.
  • Clique em "+ Criar SLO".
  • Disponibilidade, com base em solicitações
  • Continuar
  • Mês do calendário / 99%.
  • clique em "Criar SLO".

e471c7ebdc56cdf6.png

Etapa 2: configurar alertas para esse SLO

Sugiro criar dois alertas:

  1. Um com uma taxa de consumo baixa ("Slowburn") para alertar você por e-mail (simula um tíquete de baixa prioridade).
  2. Um com uma taxa de consumo alta ("Fastburn") para alertar você por SMS (simula um tíquete de alta prioridade / pager)

Acesse sua SLO tab anterior.

Faça isso duas vezes:

314bfd6b9ef0a260.png

  • Clique em "Criar alerta de SLO" (o botão 🔔 com um sinal de adição dentro, à direita).
  • Duração do lookback, limite da taxa de uso:
  • [FAST]. Primeiro: 60 min / 10 x
  • [LENTO]. Segunda: 720 min / 2 x
  • Canal de notificação: clique em "Gerenciar canais de notificação"
  • Primeiro, "E-mail" -> Adicionar novo -> ..
  • Segundo, "SMS" -> Adicionar novo -> Verificar no smartphone.
  • Dica: gosto de usar emojis nos nomes! É divertido para demonstrações.
  • Quando terminar, clique no X grande no canto superior direito.
  • Selecione primeiro o telefone (rápido) e depois o e-mail (lento).
  • Adicione alguns exemplos de documentação, como:
  • [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!

Resultado final

Considere este exercício concluído quando você tiver um SLO funcionando e alertas em dobro para sua disponibilidade, que serão enviados por e-mail e para seu smartphone.

Se quiser, adicione uma latência (recomendo muito que você faça isso) ou até mesmo uma mais complexa. Para latência, escolha um valor que você considere razoável. Na dúvida, escolha 200 ms.

11. Próximas etapas

Você concluiu TUDO. O que está faltando?

Algumas ideias:

Brinque com o Gemini

Você pode usar o Gemini de duas maneiras:

  1. Vertex AI. A "maneira empresarial", interligada ao GCP, que exploramos no capítulo 7 (GCF+Gemini). Toda a autenticação funciona magicamente, e os serviços se interconectam de maneira perfeita.
  2. IA do Google. O "jeito do consumidor". Você recebe uma API Gemini aqui e começa a criar pequenos scripts que podem ser vinculados a qualquer carga de trabalho que você já tenha (trabalho proprietário, outras nuvens, localhost etc.). Basta substituir sua chave de API e o código começa a funcionar magicamente.

Recomendamos que você tente explorar o (2) com seus próprios projetos pessoais.

UI Lifting

Não sou bom em interfaces. Mas o Gemini é! Você pode pegar uma única página PHP e dizer algo assim:

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!]

É fácil fazer isso em menos de 5 minutos com o Cloud Build. :)

A resposta do Gemini foi perfeita (ou seja, não precisei mudar nada):

8a3d5fe37ec40bf8.png

E aqui está o novo layout no app pessoal do autor:

81620eb90ae3229a.png

Observação: o código é colado como imagem porque não queremos incentivar você a copiar o código, mas sim a pedir para o Gemini escrever o código para você, com suas próprias restrições criativas de interface/front-end. Confie em mim, você terá mudanças muito pequenas depois.

Segurança

Proteger adequadamente esse app não é o objetivo deste workshop de quatro horas, já que isso aumentaria o tempo de conclusão em uma ou duas ordens de magnitude.

No entanto, esse assunto é muito importante. Reunimos algumas ideias em SECURITY.

12. Parabéns!

Parabéns 🎉🎉🎉 , você modernizou seu aplicativo PHP legado com o Google Cloud.

24cb9a39b1841fbd.png

Em resumo, neste codelab, você aprendeu a:

  • Como implantar um banco de dados no Google Cloud SQL e migrar seu banco de dados atual para ele.
  • Como conteinerizar seu aplicativo PHP com Docker e buildpacks e armazenar a imagem no Artifact Registry do Google Cloud
  • Como implantar seu app conteinerizado no Cloud Run e executá-lo com o Cloud SQL
  • Como armazenar/usar secretamente parâmetros de configuração sensíveis (como senha do banco de dados) usando o Google Secret Manager
  • Como configurar seu pipeline de CI/CD com o Google Cloud Build para criar e implantar automaticamente seu app PHP em qualquer envio de código para o repositório do GitHub.
  • Como usar o Cloud Storage para "cloudificar" os recursos do app
  • Como usar tecnologias sem servidor para criar fluxos de trabalho incríveis no Google Cloud sem mexer no código do app.
  • Use os recursos multimodais do Gemini para um caso de uso adequado.
  • Implementar princípios de SRE no Google Cloud

Este é um ótimo começo para sua jornada de modernização de aplicativos com o Google Cloud.

🔁 Feedback

Se quiser contar sobre sua experiência com este workshop, preencha este formulário de feedback.

Aceitamos seu feedback e PRs (em inglês) para trechos de código de que você se orgulha.

🙏 Agradecemos

O autor agradece a Mirko Gilioli e Maurizio Ipsale da Datatonic pela ajuda na redação e no teste da solução.