1. Visão geral
Neste codelab, você vai criar um novo serviço do Cloud Run, o serviço de colagem, que será acionado pelo Cloud Scheduler em um intervalo de tempo regular. O serviço busca as fotos mais recentes enviadas e cria uma colagem delas: ele encontra a lista de fotos recentes no Cloud Firestore e faz o download dos arquivos de imagem reais do Cloud Storage.

O que você vai aprender
- Cloud Run
- Cloud Scheduler
- Cloud Storage
- Cloud Firestore
2. Configuração e requisitos
Configuração de ambiente autoguiada
- Faça login no Console do Google Cloud e crie um novo projeto ou reutilize um existente. Crie uma conta do Gmail ou do Google Workspace, se ainda não tiver uma.



- O Nome do projeto é o nome de exibição para os participantes do projeto. Ele é uma string de caracteres que não é usada pelas APIs do Google e pode ser atualizada a qualquer momento.
- O ID do projeto precisa ser exclusivo em todos os projetos do Google Cloud e não pode ser alterado após a definição. O Console do Cloud gera automaticamente uma string única, geralmente não importa o que seja. Na maioria dos codelabs, você precisará fazer referência ao ID do projeto, que geralmente é identificado como
PROJECT_ID. Então, se você não gostar dele, gere outro ID aleatório ou crie um próprio e veja se ele está disponível. Em seguida, ele fica "congelado" depois que o projeto é criado. - Há um terceiro valor, um Número de projeto, que algumas APIs usam. Saiba mais sobre esses três valores na documentação.
- Em seguida, você precisará ativar o faturamento no Console do Cloud para usar os recursos/APIs do Cloud. A execução deste codelab não será muito cara, se tiver algum custo. Para encerrar os recursos e não gerar cobranças além deste tutorial, siga as instruções de "limpeza" encontradas no final do codelab. Novos usuários do Google Cloud estão qualificados para o programa de US$ 300 de avaliação sem custos.
Inicie o Cloud Shell
Embora o Google Cloud e o Spanner possam ser operados remotamente do seu laptop, neste codelab usaremos o Google Cloud Shell, um ambiente de linha de comando executado no Cloud.
No Console do GCP, clique no ícone do Cloud Shell na barra de ferramentas localizada no canto superior direito:

O provisionamento e a conexão com o ambiente levarão apenas alguns instantes para serem concluídos: Quando o processamento for concluído, você verá algo como:

Essa máquina virtual contém todas as ferramentas de desenvolvimento necessárias. Ela oferece um diretório principal persistente de 5 GB, além de ser executada no Google Cloud. Isso aprimora o desempenho e a autenticação da rede. Todo o trabalho neste laboratório pode ser feito apenas com um navegador.
3. Ativar APIs
Você vai precisar de um Cloud Scheduler para acionar o serviço do Cloud Run em um intervalo regular. Verifique se ele está ativado:
gcloud services enable cloudscheduler.googleapis.com
A operação será concluída com sucesso:
Operation "operations/acf.5c5ef4f6-f734-455d-b2f0-ee70b5a17322" finished successfully.
4. Clonar o código
Clone o código, caso ainda não tenha feito isso no codelab anterior:
git clone https://github.com/GoogleCloudPlatform/serverless-photosharing-workshop
Em seguida, acesse o diretório que contém o serviço:
cd serverless-photosharing-workshop/services/collage/nodejs
Você terá o seguinte layout de arquivo para o serviço:
services
|
├── collage
|
├── nodejs
|
├── Dockerfile
├── index.js
├── package.json
Dentro da pasta, há três arquivos:
index.jscontém o código Node.jspackage.jsondefine as dependências da bibliotecaDockerfiledefine a imagem do contêiner
5. Explorar o código
Dependências
O arquivo package.json define as dependências de biblioteca necessárias:
{
"name": "collage_service",
"version": "0.0.1",
"main": "index.js",
"scripts": {
"start": "node index.js"
},
"dependencies": {
"bluebird": "^3.7.2",
"express": "^4.17.1",
"imagemagick": "^0.1.3",
"@google-cloud/firestore": "^4.9.9",
"@google-cloud/storage": "^5.8.3"
}
}
Dependemos da biblioteca do Cloud Storage para ler e salvar arquivos de imagem no Cloud Storage. Declaramos uma dependência do Cloud Firestore para buscar os metadados de imagens que armazenamos anteriormente. O Express é um framework da Web JavaScript / Node. O Bluebird é usado para processar promessas, e o imagemagick é uma biblioteca para manipular imagens.
Dockerfile
Dockerfile define a imagem do contêiner para o aplicativo:
FROM node:14-slim
# installing Imagemagick
RUN set -ex; \
apt-get -y update; \
apt-get -y install imagemagick; \
rm -rf /var/lib/apt/lists/*
WORKDIR /picadaily/services/collage
COPY package*.json ./
RUN npm install --production
COPY . .
CMD [ "npm", "start" ]
Estamos usando uma imagem de base leve do Node 14. Estamos instalando a biblioteca imagemagick. Em seguida, instalamos os módulos NPM necessários para nosso código e executamos o código do nó com npm start.
index.js
Vamos analisar o código index.js:
const express = require('express');
const imageMagick = require('imagemagick');
const Promise = require("bluebird");
const path = require('path');
const {Storage} = require('@google-cloud/storage');
const Firestore = require('@google-cloud/firestore');
Precisamos das várias dependências necessárias para a execução do nosso programa: o Express é a estrutura da Web do Node que vamos usar, o ImageMagick é a biblioteca para manipulação de imagens, o Bluebird é uma biblioteca para processar promessas do JavaScript, o Path é usado para lidar com caminhos de arquivos e diretórios, e o Storage e o Firestore são para trabalhar respectivamente com o Google Cloud Storage (nossos buckets de imagens) e o repositório de dados do Cloud Firestore.
const app = express();
app.get('/', async (req, res) => {
try {
console.log('Collage request');
/* ... */
} catch (err) {
console.log(`Error: creating the collage: ${err}`);
console.error(err);
res.status(500).send(err);
}
});
Acima, temos a estrutura do nosso gerenciador do Node.js: nosso app responde a solicitações HTTP GET. Também estamos fazendo um pouco de tratamento de erros caso algo dê errado. Agora vamos ver o que há dentro dessa estrutura.
const thumbnailFiles = [];
const pictureStore = new Firestore().collection('pictures');
const snapshot = await pictureStore
.where('thumbnail', '==', true)
.orderBy('created', 'desc')
.limit(4).get();
if (snapshot.empty) {
console.log('Empty collection, no collage to make');
res.status(204).send("No collage created.");
} else {
/* ... */
}
Nosso serviço de colagem precisa de pelo menos quatro fotos (com miniaturas geradas). Portanto, faça upload de quatro fotos primeiro.
Recuperamos as quatro fotos mais recentes enviadas pelos nossos usuários dos metadados armazenados no Cloud Firestore. Verificamos se a coleção resultante está vazia ou não e continuamos na ramificação "else" do nosso código.
Vamos coletar a lista de nomes de arquivos:
snapshot.forEach(doc => {
thumbnailFiles.push(doc.id);
});
console.log(`Picture file names: ${JSON.stringify(thumbnailFiles)}`);
Vamos baixar cada um desses arquivos do bucket de miniaturas, cujo nome vem de uma variável de ambiente definida no momento da implantação:
const thumbBucket = storage.bucket(process.env.BUCKET_THUMBNAILS);
await Promise.all(thumbnailFiles.map(async fileName => {
const filePath = path.resolve('/tmp', fileName);
await thumbBucket.file(fileName).download({
destination: filePath
});
}));
console.log('Downloaded all thumbnails');
Depois que as miniaturas mais recentes forem enviadas, vamos usar a biblioteca ImageMagick para criar uma grade 4x4 com essas imagens. Usamos a biblioteca Bluebird e a implementação de promessas dela para transformar o código orientado por callback em um código compatível com async / await. Em seguida, aguardamos a promessa que está criando a colagem de imagens:
const collagePath = path.resolve('/tmp', 'collage.png');
const thumbnailPaths = thumbnailFiles.map(f => path.resolve('/tmp', f));
const convert = Promise.promisify(im.convert);
await convert([
'(', ...thumbnailPaths.slice(0, 2), '+append', ')',
'(', ...thumbnailPaths.slice(2), '+append', ')',
'-size', '400x400', 'xc:none', '-background', 'none', '-append',
collagePath]);
console.log("Created local collage picture");
Como a imagem da colagem foi salva no disco localmente na pasta temporária, agora precisamos fazer upload dela para o Cloud Storage e retornar uma resposta de sucesso (código de status 2xx):
await thumbBucket.upload(collagePath);
console.log("Uploaded collage to Cloud Storage bucket ${process.env.BUCKET_THUMBNAILS}");
res.status(204).send("Collage created.");
Agora é hora de fazer com que nosso script do Node detecte solicitações recebidas:
const PORT = process.env.PORT || 8080;
app.listen(PORT, () => {
console.log(`Started collage service on port ${PORT}`);
});
No final do arquivo de origem, temos as instruções para que o Express inicie o aplicativo da Web na porta padrão 8080.
6. Testar localmente
Teste o código localmente para garantir que ele funcione antes de implantar na nuvem.
Na pasta collage/nodejs, instale as dependências do npm e inicie o servidor:
npm install; npm start
Se tudo der certo, o servidor será iniciado na porta 8080:
Started collage service on port 8080
Use CTRL-C para sair.
7. Criar e implantar no Cloud Run
Antes de fazer a implantação no Cloud Run, defina a região do Cloud Run como uma das regiões compatíveis e a plataforma como managed:
gcloud config set run/region europe-west1 gcloud config set run/platform managed
Para verificar se a configuração está definida:
gcloud config list ... [run] platform = managed region = europe-west1
Em vez de criar e publicar a imagem de contêiner manualmente usando o Cloud Build, você também pode usar o Cloud Run para criar a imagem de contêiner usando os buildpacks do Google Cloud.
Execute o comando a seguir para criar a imagem do contêiner:
BUCKET_THUMBNAILS=thumbnails-$GOOGLE_CLOUD_PROJECT
SERVICE_NAME=collage-service
gcloud run deploy $SERVICE_NAME \
--source . \
--no-allow-unauthenticated \
--update-env-vars BUCKET_THUMBNAILS=$BUCKET_THUMBNAILS
Observe a flag –-source. Essa é a implantação baseada em origem no Cloud Run. Se houver um Dockerfile no diretório de código-fonte, o código-fonte enviado será criado usando esse Dockerfile. Se nenhum Dockerfile estiver presente no diretório do código-fonte, os buildpacks do Google Cloud vão detectar automaticamente a linguagem que você está usando e buscar as dependências do código para criar uma imagem de contêiner pronta para produção, usando uma imagem de base segura gerenciada pelo Google. Isso sinaliza para o Cloud Run usar os buildpacks do Google Cloud para criar a imagem de contêiner definida em Dockerfile.
Observe também que a implantação baseada em origem usa o Artifact Registry para armazenar contêineres criados. O Artifact Registry é uma versão moderna do Google Container Registry. A CLI vai pedir para ativar a API se ela ainda não estiver ativada no projeto e vai criar um repositório com o nome cloud-run-source-deploy na região em que você está fazendo a implantação.
A flag --no-allow-unauthenticated faz com que o serviço do Cloud Run seja um serviço interno que só será acionado por contas de serviço específicas.
8. Configurar o Cloud Scheduler
Agora que o serviço do Cloud Run está pronto e implantado, é hora de criar a programação regular para invocar o serviço a cada minuto.
Crie uma conta de serviço:
SERVICE_ACCOUNT=collage-scheduler-sa gcloud iam service-accounts create $SERVICE_ACCOUNT \ --display-name "Collage Scheduler Service Account"
Conceda permissão à conta de serviço para invocar o serviço do Cloud Run:
gcloud run services add-iam-policy-binding $SERVICE_NAME \ --member=serviceAccount:$SERVICE_ACCOUNT@$GOOGLE_CLOUD_PROJECT.iam.gserviceaccount.com \ --role=roles/run.invoker
Crie um job do Cloud Scheduler para executar a cada 1 minuto:
SERVICE_URL=$(gcloud run services describe $SERVICE_NAME --format 'value(status.url)') gcloud scheduler jobs create http $SERVICE_NAME-job --schedule "* * * * *" \ --http-method=GET \ --location=europe-west1 \ --uri=$SERVICE_URL \ --oidc-service-account-email=$SERVICE_ACCOUNT@$GOOGLE_CLOUD_PROJECT.iam.gserviceaccount.com \ --oidc-token-audience=$SERVICE_URL
Acesse a seção "Cloud Scheduler" no console do Cloud para verificar se ele está configurado e apontando para o URL do serviço do Cloud Run:

9. Testar o serviço
Para testar se a configuração está funcionando, verifique no bucket thumbnails a imagem da colagem (chamada collage.png). Também é possível verificar os registros do serviço:

10. Limpeza (opcional)
Se você não pretende continuar com os outros laboratórios da série, limpe os recursos para economizar custos e ser um bom usuário da nuvem. É possível limpar os recursos individualmente da seguinte maneira.
Exclua o serviço:
gcloud run services delete $SERVICE_NAME -q
Exclua o job do Cloud Scheduler:
gcloud scheduler jobs delete $SERVICE_NAME-job -q
Se preferir, exclua todo o projeto:
gcloud projects delete $GOOGLE_CLOUD_PROJECT
11. Parabéns!
Parabéns! Você criou um serviço programado: graças ao Cloud Scheduler, que envia uma mensagem a cada minuto em um tópico do Pub/Sub, seu serviço de colagem do Cloud Run é invocado e pode anexar imagens para criar a imagem resultante.
O que vimos
- Cloud Run
- Cloud Scheduler
- Cloud Storage
- Cloud Firestore