Neste artigo, vou explicar por que vale a pena redimensionar as imagens do seu site e como usar o Google Cloud Functions junto com o GCP Load Balancer e o Cloud CDN para entregar imagens redimensionadas on-the-fly, melhorando a performance, economizando espaço de armazenamento (que sai caro) e reduzindo o tempo de carregamento das páginas.

Imagem gerada com o ChatGPT 5
Quase todo grande site redimensiona as fotos exibidas, e isso acontece por três motivos principais:
- Baixar imagens grandes gera alto consumo de banda e custos elevados de transferência de dados.
- Reduzir imagens via HTML obriga o navegador do cliente a baixar a imagem inteira e gastar tempo de CPU para redimensioná-la, o que deixa o site mais lento.
- Sem o redimensionamento on-the-fly, você precisa armazenar diferentes tamanhos da mesma imagem para as versões Desktop, Mobile, Tablet e e-mail (newsletter) do site. Como os custos de armazenamento na nuvem começam em US$ 0,02/GB por mês, a conta pode pesar.
Saiba mais sobre otimização de imagens no livro de Steve Souders, Even Faster Web Sites , Capítulo 10.
Usar o Google Cloud Content Delivery Network (CDN) permite oferecer tempos de carregamento mais rápidos para o conteúdo estático, já que os objetos ficam em cache na rede de borda, distribuída globalmente.

A solução apresentada neste artigo usa os seguintes serviços:
- Google Cloud Storage — para armazenar as imagens em tamanho original.
- Cloud Function — para redimensionar os objetos on-the-fly.
- Google Cloud Load Balancer com Cloud CDN habilitado — os usuários acessam o load balancer para recuperar as imagens.
Criamos um GCP Cloud Load Balancer (LB) e o configuramos para invocar uma Cloud Function. A função busca uma imagem no Google Cloud Storage (GCS) e a redimensiona; o LB devolve a imagem redimensionada e a salva na borda para futuras requisições.

Como implantar
Criamos um bucket no Google Cloud Storage (GCS) e fazemos upload de todos os objetos (imagens) em seu tamanho original.

Definimos um nome para o bucket, configuramos uma localização regional e clicamos no botão Criar:

Subimos uma imagem, por exemplo, nasdaq.jpg (do prédio da Nasdaq na Times Square, que tirei em 2019), para esse bucket no tamanho original, 6,3 MB.

O próximo passo é criar uma function. Acesse o console do Cloud Functions e clique em Write a function.

Se for a primeira vez que você usa o Cloud Functions, talvez apareça a mensagem "Cloud Functions API is enabled". Isso significa que os recursos do Cloud Functions estão sendo habilitados no seu projeto sem custo adicional.
Nesta etapa, precisamos configurar a function:
- Usamos o editor inline.
- Definimos um nome para a function.
- Escolhemos a região — que deve ser a mesma do GCS.
- Definimos o runtime. Nesta demonstração, usamos Python.
- Desmarcamos a autenticação IAM para que o LB consiga invocar a function.

Agora clique no botão Criar. Se for a primeira vez que você usa serviços do GCP, pode ser que precise habilitar mais algumas APIs:

neste exemplo, precisamos habilitar o Cloud Build para construir o container que executa a function,
Agora vem a parte divertida: precisamos escrever o código que de fato busca o arquivo no GCS, redimensiona e devolve o arquivo redimensionado.
Queríamos ver se isso poderia ser feito inteiramente pelo ChatGPT, então fiz algumas iterações com o LLM, usando os seguintes prompts:
"Write Python code to resize jpg png images to x,y based on the string provided."
"Let's assume it's google cloud function that pass the params as query string and the file needs to be loaded from gcs and the result should be sent to a load balancer with the appropriate mime type."
"What should I put in requirements.txt for this code?"
"Now I'm getting 'Error processing image: module 'PIL.Image' has no attribute 'ANTIALIAS''"
O resultado é o código a seguir. Na linha 9, defina o bucket que utilizamos:
Este é o arquivo requirements.txt usado para configurar o Cloud Functions, indicando quais bibliotecas Python devem ser usadas para construir o container:
Quando atualizamos o código em main.py e as bibliotecas em requirements.txt, aparece o aviso: "The specified function (entry point) might not be present in your source code. Please ensure the entry point in your code matches the input field."
Isso acontece porque a function está chamando a função resize_image, e o nome padrão da função no Cloud Functions é hello_http:

Alteramos o Function entry point para resize_image e clicamos em Save and redeploy.
O Cloud Functions constrói o container do nosso código. Pode levar alguns minutos, e dá para acompanhar o status do build no topo do dashboard:

Observação: o Cloud Function invoca uma function. A function usa a Compute Engine Default Service Account, que dá acesso a todos os buckets do GCS no projeto. Não vamos abordar isso neste artigo, mas, como boa prática, use uma service account dedicada com o mínimo de privilégios necessários.
O próximo passo é criar um Load Balancer. Pesquise por "Load Balancer" no console, acesse o dashboard do Load Balancer e clique em Create load balancer:

Queremos criar um Application Load Balancer Global, voltado para o público externo. Em tipo de load balancer, escolha Application Load Balancer (HTTP/HTTPS):

Como queremos que usuários do mundo todo consigam acessar o load balancer, escolhemos Public Facing (external):

Optamos pelo load balancer Global para aproveitar o recurso Google Cloud CDN, que permite armazenar em cache a imagem redimensionada no servidor de borda (sem custo adicional).

Em seguida, escolhemos a geração mais recente do Load Balancer e clicamos em Next e depois em Create.

Para simplificar esta etapa, criamos um endpoint HTTP de frontend para o Load Balancer.
Defina um nome para o Load Balancer e o IP do frontend.

Clique em Backend Configuration e, na caixa Backend services & backend buckets, clique em Create a backend service.

O backend service informa ao Load Balancer para qual recurso a requisição deve ser encaminhada; na nossa configuração, é uma Cloud Function.
Defina um nome e uma descrição para o backend service e altere o Backend type para Serverless network endpoint group.

Role até a área Backends. Em New backend, clique em Serverless network endpoint groups e depois em Create Serverless network endpoint group.

Defina o nome do Endpoint e escolha a região onde a Cloud Function foi criada.

Se você nunca invocou a function antes, este erro vai aparecer:

Acesse o link indicado no erro e habilite a Cloud Function API:

Volte para a página Serverless network endpoint, defina um nome para o endpoint, a Região onde a function está localizada e escolha Cloud Run (pode parecer confuso, mas o Cloud Functions agora se chama Cloud Run Functions e usa a mesma tecnologia subjacente).
Selecione a function e clique em Create.

Na página do backend, dá para configurar as opções de cache do Cloud CDN:
Podemos configurar o CDN para fazer cache da resposta pela duração definida (1 hora, na linha 54 do script) ou por um período mais longo.
Vale destacar que o CDN usa um warm cache, ou seja, só conteúdos solicitados com frequência ficam armazenados no cache do CDN. Se você definir um cache de 1 ano para o conteúdo, mas não houver requisições frequentes, depois de algum tempo o CDN vai buscar o conteúdo na origem (a function).

Repare no recurso de Logging (marcado pela seta acima). Ele é ótimo para depuração e acompanhamento de uso, mas também sai caro (US$ 512 por 1 TB de logs).
Se você gera muitas requisições, pode definir o Sample rate como 0,01 para registrar 1 a cada 100 requisições.
Vamos rolar até Security:
Por padrão, o Google habilita o Cloud Armor, o Web Application Firewall (WAF), no nosso backend. Mas, como há custo adicional e isto é uma demonstração, vamos desabilitá-lo clicando em Cloud Armor backend security policy e mudando para None.

O próximo passo é clicar em Create e, no dashboard do Load Balancer, clicar em Create de novo.
Observação: a criação e a atualização de um Load Balancer no GCP podem levar até 15 minutos para se propagar.
Assim que o load balancer estiver pronto, clique no nome dele e você verá o IP gerado:

Acessamos esse IP e informamos o nome da imagem, além da altura e da largura:
http://34.49.21.153/?image=nasdaq.jpg&size=500x600
O resultado: em vez de um arquivo de 6,5 MB, recebemos uma imagem menor, redimensionada, de 63 KB.

Meu trabalho é orientar clientes no uso da nuvem. Veja o que podemos fazer por você em doit.com/services