Tutorial: como proteger serviços do Cloud Run

Este tutorial explica como criar um aplicativo seguro de dois serviços em execução no Cloud Run. Esse aplicativo é um editor Markdown que inclui um serviço "front-end" público que qualquer um pode usar para escrever texto de markdown e um serviço de "back-end" particular que renderiza o texto de Markdown para HTML.

Diagrama mostrando o fluxo de solicitação do "editor" de front-end para o "renderizador" de back-end.
O back-end do "Renderizador" é um serviço privado. Isso permite garantir um padrão de transformação de texto em uma organização sem rastrear alterações em bibliotecas em vários idiomas.

O serviço de back-end é privado, usando o recurso de Autenticação de serviço a serviço com base em IAM integrado ao Cloud Run (totalmente gerenciado), que limita quem pode chamar o serviço. Os dois serviços são criados com o princípio de menor privilégio, sem acesso ao restante do Google Cloud, exceto quando necessário.

Limitações ou não objetivos deste tutorial

Objetivos

  • Criar uma conta de serviço dedicada com permissões mínimas de autenticação de serviço a serviço e acesso de serviço ao restante do Google Cloud.
  • Gravar, criar e implantar dois serviços no Cloud Run que interagem.
  • Fazer solicitações entre um serviço público e privado do Cloud Run.

Custos

Neste tutorial, há componentes faturáveis do Google Cloud, entre eles:

Use a calculadora de preços para gerar uma estimativa de custo com base no uso previsto.

Novos usuários do Google Cloud podem estar qualificados para uma avaliação gratuita.

Antes de começar

  1. Faça login na sua conta do Google Cloud. Se você começou a usar o Google Cloud agora, crie uma conta para avaliar o desempenho de nossos produtos em situações reais. Clientes novos também recebem US$ 300 em créditos para executar, testar e implantar cargas de trabalho.
  2. No Console do Google Cloud, na página do seletor de projetos, selecione ou crie um projeto do Google Cloud.

    Acessar o seletor de projetos

  3. Verifique se o faturamento está ativado para seu projeto na nuvem. Saiba como confirmar se o faturamento está ativado para o projeto.

  4. Ative a API Cloud Run.

    Ative a API

  5. Instale e inicie o SDK do Cloud.
  6. Instale o curl para testar o serviço

Como configurar padrões do gcloud

Para configurar a gcloud com os padrões do serviço do Cloud Run, realize as etapas a seguir:

  1. Defina seu projeto padrão:

    gcloud config set project PROJECT-ID

    Substitua PROJECT-ID pelo nome do projeto que você criou para este tutorial.

  2. Se você estiver usando o Cloud Run (totalmente gerenciado), configure o gcloud para a região escolhida e especifique managed como sua plataforma do Cloud Run:

    gcloud config set run/region REGION
    gcloud config set run/platform managed

    Substitua REGION pela região compatível do Cloud Run.

Locais do Cloud Run

O Cloud Run é regional, o que significa que a infraestrutura que executa seus serviços do Cloud Run está localizada em uma região específica e é gerenciada pelo Google para estar disponível de maneira redundante em todas as zonas da região.

Atender aos seus requisitos de latência, disponibilidade ou durabilidade são os principais fatores para selecionar a região em que seus serviços do Cloud Run são executados. Geralmente, é possível selecionar a região mais próxima de seus usuários, mas considere a localização dos outros produtos do Google Cloud usados pelo serviço do Cloud Run. O uso de produtos do Google Cloud em vários locais pode afetar a latência e o custo do serviço.

O Cloud Run está disponível nas regiões a seguir:

Sujeitas aos preços do nível 1

  • asia-east1 (Taiwan)
  • asia-northeast1 (Tóquio)
  • asia-northeast2 (Osaka)
  • europe-north1 (Finlândia)
  • europe-west1 (Bélgica)
  • europe-west4 (Países Baixos)
  • us-central1 (Iowa)
  • us-east1 (Carolina do Sul)
  • us-east4 (Norte da Virgínia)
  • us-west1 (Oregon)

Sujeitas aos preços do nível 2

  • asia-east2 (Hong Kong)
  • asia-northeast3 (Seul, Coreia do Sul)
  • asia-southeast1 (Singapura)
  • asia-southeast2 (Jacarta)
  • asia-south1 (Mumbai, Índia)
  • australia-southeast1 (Sydney)
  • europe-central2 (Varsóvia, Polônia)
  • europe-west2 (Londres, Reino Unido)
  • europe-west3 (Frankfurt, Alemanha)
  • europe-west6 (Zurique, Suíça)
  • northamerica-northeast1 (Montreal)
  • southamerica-east1 (São Paulo, Brasil)
  • us-west2 (Los Angeles)
  • us-west3 (Las Vegas)
  • us-west4 (Salt Lake City)

Se você já criou um serviço do Cloud Run, poderá ver a região no painel do Cloud Run no Console do Cloud.

Como recuperar a amostra de código

Para recuperar a amostra de código para uso, siga estas etapas:

  1. Clone o repositório do aplicativo de amostra na máquina local:

    Node.js

    git clone https://github.com/GoogleCloudPlatform/nodejs-docs-samples.git

    Outra alternativa é fazer o download da amostra como um arquivo ZIP e extraí-lo.

    Python

    git clone https://github.com/GoogleCloudPlatform/python-docs-samples.git

    Outra alternativa é fazer o download da amostra como um arquivo ZIP e extraí-lo.

    Go

    git clone https://github.com/GoogleCloudPlatform/golang-samples.git

    Outra alternativa é fazer o download da amostra como um arquivo ZIP e extraí-lo.

    Java

    git clone https://github.com/GoogleCloudPlatform/java-docs-samples.git

    Outra alternativa é fazer o download da amostra como um arquivo ZIP e extraí-lo.

  2. Mude para o diretório que contém o código de amostra do Cloud Run:

    Node.js

    cd nodejs-docs-samples/run/markdown-preview/

    Python

    cd python-docs-samples/run/markdown-preview/

    Go

    cd golang-samples/run/markdown-preview/

    Java

    cd java-docs-samples/run/markdown-preview/

Como revisar o serviço de renderização Markdown privado

Do ponto de vista do front-end, há uma especificação simples da API para o serviço do Markdown:

  • Um endpoint em /
  • Espera solicitações POST
  • O corpo da solicitação POST é o texto Markdown

Analise todo o código em busca de problemas de segurança ou saiba mais sobre ele explorando o diretório ./renderer/. O tutorial não explica o código de transformação do Markdown.

Como enviar o serviço de renderização Markdown privado

Para enviar seu código, crie com o Cloud Build, faça o upload para o Container Registry e implante no Cloud Run (totalmente gerenciado):

  1. Altere para o diretório renderer:

    cd renderer/
  2. Execute o comando a seguir para criar seu contêiner e publicar no Container Registry.

    Node.js

    gcloud builds submit --tag gcr.io/PROJECT_ID/renderer

    PROJECT_ID é o ID do projeto do GCP e renderer é o nome que você quer dar ao serviço.

    Após a conclusão, você verá uma mensagem de SUCESSO contendo o ID, a hora da criação e o nome da imagem. A imagem é armazenada no Container Registry e poderá ser reutilizada, se você quiser.

    Python

    gcloud builds submit --tag gcr.io/PROJECT_ID/renderer

    PROJECT_ID é o ID do projeto do GCP e renderer é o nome que você quer dar ao serviço.

    Após a conclusão, você verá uma mensagem de SUCESSO contendo o ID, a hora da criação e o nome da imagem. A imagem é armazenada no Container Registry e poderá ser reutilizada, se você quiser.

    Go

    gcloud builds submit --tag gcr.io/PROJECT_ID/renderer

    PROJECT_ID é o ID do projeto do GCP e renderer é o nome que você quer dar ao serviço.

    Após a conclusão, você verá uma mensagem de SUCESSO contendo o ID, a hora da criação e o nome da imagem. A imagem é armazenada no Container Registry e poderá ser reutilizada, se você quiser.

    Java

    Esta amostra usa o Jib (em inglês) para criar imagens do Docker usando ferramentas comuns do Java. O Jib otimiza builds de contêiner sem a necessidade de um Dockerfile ou de ter o Docker (em inglês) instalado. Saiba mais sobre como criar contêineres Java com o Jib.

    1. Use o auxiliar de credencial do gcloud para autorizar o Docker a enviar por push ao Container Registry.

      gcloud auth configure-docker

    2. Use o plug-in do Maven do Jib para criar e enviar por push o contêiner ao Container Registry.

      mvn compile jib:build -Dimage=gcr.io/PROJECT_ID/renderer

    PROJECT_ID é o ID do projeto do GCP e renderer é o nome que você quer dar ao serviço.

    Após a conclusão, você verá uma mensagem "BUILD SUCCESS". A imagem é armazenada no Container Registry e poderá ser reutilizada, se você quiser.

  3. Implante como um serviço particular com acesso restrito.

    O Cloud Run (totalmente gerenciado) fornece recursos de controle de acesso e identidade de serviço prontos para uso. O controle de acesso fornece uma camada de autenticação que impede que usuários e outros serviços invoquem o serviço. A identidade de serviço permite impedir que o serviço acesse outros recursos do Google Cloud criando uma conta de serviço dedicada com permissões limitadas.

    1. Crie uma conta de serviço para servir como a “identidade de computação” do serviço de renderização. Por padrão, isso não tem privilégios além da associação ao projeto.

       gcloud iam service-accounts create renderer-identity
      

      O serviço de renderização Markdown não se integra diretamente a nada no Google Cloud. Ele não precisa de mais permissões.

    2. Implante com a conta de serviço renderer-identity e negue o acesso não autenticado.

      gcloud run deploy renderer \
        --image gcr.io/PROJECT_ID/renderer \
        --service-account renderer-identity \
        --no-allow-unauthenticated

      O Cloud Run pode usar o nome da conta do serviço de formulário curto em vez do endereço de e-mail completo se a conta de serviço fizer parte do mesmo projeto.

Como testar o serviço de renderização Markdown privado

Os serviços privados não podem ser carregados diretamente por um navegador da Web. Em vez disso, use curl ou uma ferramenta de CLI de solicitação HTTP semelhante que permita injetar um cabeçalho Authorization.

Para enviar um texto em negrito ao serviço e vê-lo converter os asteriscos de marcação em tags HTML <strong>:

  1. Receba o URL da saída da implantação.

  2. Use gcloud para derivar um token de identidade especial somente para desenvolvimento para autenticação:

    TOKEN=$(gcloud auth print-identity-token)
  3. Crie uma solicitação de curl que transmita o texto bruto de Markdown como um parâmetro de string de consulta com escape de URL:

    curl -H "Authorization: Bearer $TOKEN" \
       -H 'Content-Type: text/plain' \
       -d '**Hello Bold Text**' \
       SERVICE_URL
  4. A resposta será um snippet HTML:

     <strong>Hello Bold Text</strong>
    

Como analisar a integração entre o editor e os serviços de renderização

O serviço de edição fornece uma IU de entrada de texto simples e um espaço para visualização de HTML. Antes de continuar, revise o código recuperado anteriormente abrindo o diretório ./editor/.

Em seguida, explore as seguintes seções de código que integram com segurança os dois serviços.

Node.js

O módulo render.js cria solicitações autenticadas para o serviço de renderizador particular. Ele usa o servidor de metadados do Google Cloud no ambiente do Cloud Run para criar um token de identidade e adicioná-lo à solicitação HTTP como parte de um cabeçalho Authorization.

Em outros ambientes, render.js usa o Application Default Credentials para solicitar um token dos servidores do Google.

const {GoogleAuth} = require('google-auth-library');
const got = require('got');
const auth = new GoogleAuth();

let client, serviceUrl;

// renderRequest creates a new HTTP request with IAM ID Token credential.
// This token is automatically handled by private Cloud Run (fully managed) and Cloud Functions.
const renderRequest = async markdown => {
  if (!process.env.EDITOR_UPSTREAM_RENDER_URL)
    throw Error('EDITOR_UPSTREAM_RENDER_URL needs to be set.');
  serviceUrl = process.env.EDITOR_UPSTREAM_RENDER_URL;

  // Build the request to the Renderer receiving service.
  const serviceRequestOptions = {
    method: 'POST',
    headers: {
      'Content-Type': 'text/plain',
    },
    body: markdown,
    timeout: 3000,
  };

  try {
    // Create a Google Auth client with the Renderer service url as the target audience.
    if (!client) client = await auth.getIdTokenClient(serviceUrl);
    // Fetch the client request headers and add them to the service request headers.
    // The client request headers include an ID token that authenticates the request.
    const clientHeaders = await client.getRequestHeaders();
    serviceRequestOptions.headers['Authorization'] =
      clientHeaders['Authorization'];
  } catch (err) {
    throw Error('could not create an identity token: ', err);
  }

  try {
    // serviceResponse converts the Markdown plaintext to HTML.
    const serviceResponse = await got(serviceUrl, serviceRequestOptions);
    return serviceResponse.body;
  } catch (err) {
    throw Error('request to rendering service failed: ', err);
  }
};

Analise a marcação a partir do JSON e envie-a ao serviço do Renderizador para ser transformada em HTML.

app.post('/render', async (req, res) => {
  try {
    const markdown = req.body.data;
    const response = await renderRequest(markdown);
    res.status(200).send(response);
  } catch (err) {
    console.log('error: markdown rendering:', err);
    res.status(500).send(err);
  }
});

Python

O método new_request cria solicitações autenticadas para serviços particulares. Ele usa o servidor de metadados do Google Cloud no ambiente do Cloud Run para criar um token de identidade e adicioná-lo à solicitação HTTP como parte de um cabeçalho Authorization.

Em outros ambientes, new_request solicita um token de identidade dos servidores do Google, fazendo a autenticação com Application Default Credentials.

import os
import urllib

import google.auth.transport.requests
import google.oauth2.id_token

def new_request(data):
    """
    new_request creates a new HTTP request with IAM ID Token credential.
    This token is automatically handled by private Cloud Run (fully managed)
    and Cloud Functions.
    """

    url = os.environ.get("EDITOR_UPSTREAM_RENDER_URL")
    if not url:
        raise Exception("EDITOR_UPSTREAM_RENDER_URL missing")

    req = urllib.request.Request(url, data=data.encode())
    auth_req = google.auth.transport.requests.Request()
    target_audience = url

    id_token = google.oauth2.id_token.fetch_id_token(auth_req, target_audience)
    req.add_header("Authorization", f"Bearer {id_token}")

    response = urllib.request.urlopen(req)
    return response.read()

Analise a marcação a partir do JSON e envie-a ao serviço do Renderizador para ser transformada em HTML.

@app.route("/render", methods=["POST"])
def render_handler():
    body = request.get_json()
    if not body:
        return "Error rendering markdown: Invalid JSON", 400

    data = body["data"]
    try:
        parsed_markdown = render.new_request(data)
        return parsed_markdown, 200
    except Exception as err:
        return f"Error rendering markdown: {err}", 500

Go

RenderService cria solicitações autenticadas para serviços particulares. Ele usa o servidor de metadados do Google Cloud no ambiente do Cloud Run para criar um token de identidade e adicioná-lo à solicitação HTTP como parte de um cabeçalho Authorization.

Em outros ambientes, RenderService solicita um token de identidade dos servidores do Google, fazendo a autenticação com Application Default Credentials.

import (
	"bytes"
	"context"
	"fmt"
	"io/ioutil"
	"net/http"
	"time"

	"golang.org/x/oauth2"
	"google.golang.org/api/idtoken"
)

// RenderService represents our upstream render service.
type RenderService struct {
	// URL is the render service address.
	URL string
	// tokenSource provides an identity token for requests to the Render Service.
	tokenSource oauth2.TokenSource
}

// NewRequest creates a new HTTP request to the Render service.
// If authentication is enabled, an Identity Token is created and added.
func (s *RenderService) NewRequest(method string) (*http.Request, error) {
	req, err := http.NewRequest(method, s.URL, nil)
	if err != nil {
		return nil, fmt.Errorf("http.NewRequest: %w", err)
	}

	ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
	defer cancel()

	// Create a TokenSource if none exists.
	if s.tokenSource == nil {
		s.tokenSource, err = idtoken.NewTokenSource(ctx, s.URL)
		if err != nil {
			return nil, fmt.Errorf("idtoken.NewTokenSource: %w", err)
		}
	}

	// Retrieve an identity token. Will reuse tokens until refresh needed.
	token, err := s.tokenSource.Token()
	if err != nil {
		return nil, fmt.Errorf("TokenSource.Token: %w", err)
	}
	token.SetAuthHeader(req)

	return req, nil
}

A solicitação é enviada ao serviço renderizador após adicionar o texto de marcação a ser transformado em HTML. Os erros de resposta são tratados diferenciando problemas de comunicação e funcionalidade de renderização.


var renderClient = &http.Client{Timeout: 30 * time.Second}

// Render converts the Markdown plaintext to HTML.
func (s *RenderService) Render(in []byte) ([]byte, error) {
	req, err := s.NewRequest(http.MethodPost)
	if err != nil {
		return nil, fmt.Errorf("RenderService.NewRequest: %w", err)
	}
	req.Body = ioutil.NopCloser(bytes.NewReader(in))
	defer req.Body.Close()

	resp, err := renderClient.Do(req)
	if err != nil {
		return nil, fmt.Errorf("http.Client.Do: %w", err)
	}

	out, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return nil, fmt.Errorf("ioutil.ReadAll: %w", err)
	}

	if resp.StatusCode != http.StatusOK {
		return out, fmt.Errorf("http.Client.Do: %s (%d): request not OK", http.StatusText(resp.StatusCode), resp.StatusCode)
	}

	return out, nil
}

Java

makeAuthenticatedRequest cria solicitações autenticadas para serviços particulares. Ele usa o servidor de metadados do Google Cloud no ambiente do Cloud Run para criar um token de identidade e adicioná-lo à solicitação HTTP como parte de um cabeçalho Authorization.

Em outros ambientes, makeAuthenticatedRequest solicita um token de identidade dos servidores do Google, fazendo a autenticação com Application Default Credentials.

// makeAuthenticatedRequest creates a new HTTP request authenticated by a JSON Web Tokens (JWT)
// retrievd from Application Default Credentials.
public String makeAuthenticatedRequest(String url, String markdown) {
  String html = "";
  try {
    // Retrieve Application Default Credentials
    GoogleCredentials credentials = GoogleCredentials.getApplicationDefault();
    IdTokenCredentials tokenCredentials =
        IdTokenCredentials.newBuilder()
            .setIdTokenProvider((IdTokenProvider) credentials)
            .setTargetAudience(url)
            .build();

    // Create an ID token
    String token = tokenCredentials.refreshAccessToken().getTokenValue();
    // Instantiate HTTP request
    MediaType contentType = MediaType.get("text/plain; charset=utf-8");
    okhttp3.RequestBody body = okhttp3.RequestBody.create(markdown, contentType);
    Request request =
        new Request.Builder()
            .url(url)
            .addHeader("Authorization", "Bearer " + token)
            .post(body)
            .build();

    Response response = ok.newCall(request).execute();
    html = response.body().string();
  } catch (IOException e) {
    logger.error("Unable to get rendered data", e);
  }
  return html;
}

Analise a marcação a partir do JSON e envie-a ao serviço do Renderizador para ser transformada em HTML.

// '/render' expects a JSON body payload with a 'data' property holding plain text
// for rendering.
@PostMapping(value = "/render", consumes = "application/json")
public String render(@RequestBody Data data) {
  String markdown = data.getData();

  String url = System.getenv("EDITOR_UPSTREAM_RENDER_URL");
  if (url == null) {
    String msg =
        "No configuration for upstream render service: "
            + "add EDITOR_UPSTREAM_RENDER_URL environment variable";
    logger.error(msg);
    throw new IllegalStateException(msg);
  }

  String html = makeAuthenticatedRequest(url, markdown);
  return html;
}

Como enviar o serviço de editor público

Para criar e implantar seu código:

  1. Altere para o diretório editor:

    cd ../editor
  2. Execute o comando a seguir para criar seu contêiner e publicar no Container Registry.

    Node.js

    gcloud builds submit --tag gcr.io/PROJECT_ID/editor

    PROJECT_ID é o ID do projeto do GCP e editor é o nome que você quer dar ao serviço.

    Após a conclusão, você verá uma mensagem de SUCESSO contendo o ID, a hora da criação e o nome da imagem. A imagem é armazenada no Container Registry e poderá ser reutilizada, se você quiser.

    Python

    gcloud builds submit --tag gcr.io/PROJECT_ID/editor

    PROJECT_ID é o ID do projeto do GCP e editor é o nome que você quer dar ao serviço.

    Após a conclusão, você verá uma mensagem de SUCESSO contendo o ID, a hora da criação e o nome da imagem. A imagem é armazenada no Container Registry e poderá ser reutilizada, se você quiser.

    Go

    gcloud builds submit --tag gcr.io/PROJECT_ID/editor

    PROJECT_ID é o ID do projeto do GCP e editor é o nome que você quer dar ao serviço.

    Após a conclusão, você verá uma mensagem de SUCESSO contendo o ID, a hora da criação e o nome da imagem. A imagem é armazenada no Container Registry e poderá ser reutilizada, se você quiser.

    Java

    Esta amostra usa o Jib (em inglês) para criar imagens do Docker usando ferramentas comuns do Java. O Jib otimiza builds de contêiner sem a necessidade de um Dockerfile ou de ter o Docker (em inglês) instalado. Saiba mais sobre como criar contêineres Java com o Jib.

    mvn compile jib:build -Dimage=gcr.io/PROJECT_ID/editor

    PROJECT_ID é o ID do projeto do GCP e editor é o nome que você quer dar ao serviço.

    Após a conclusão, você verá uma mensagem "BUILD SUCCESS". A imagem é armazenada no Container Registry e poderá ser reutilizada, se você quiser.

  3. Implante como um serviço particular com acesso especial ao serviço de renderização.

    1. Crie uma conta de serviço para servir como a “identidade de computação” do serviço de renderização. Por padrão, isso não tem privilégios além da associação ao projeto.

       gcloud iam service-accounts create editor-identity
      

      O serviço Editor não precisa interagir com mais nada no Google Cloud além do serviço de renderização Markdown.

    2. Conceda acesso à identidade de computação editor-identity para invocar o serviço de renderização Markdown. Qualquer serviço que use isso como uma identidade de computação terá esse privilégio.

      gcloud run services add-iam-policy-binding renderer \
        --member serviceAccount:editor-identity@PROJECT_ID.iam.gserviceaccount.com \
        --role roles/run.invoker

      Como isso recebe o papel de invocador no contexto do serviço de renderização, o serviço de renderização é o único serviço privado do Cloud Run que o editor pode invocar.

    3. Implante com a conta de serviço editor-identity e permita acesso público e não autenticado.

      gcloud run deploy editor --image gcr.io/PROJECT_ID/editor \
        --service-account editor-identity \
        --set-env-vars EDITOR_UPSTREAM_RENDER_URL=RENDERER_SERVICE_URL \
        --allow-unauthenticated

      Substitua:

      • PROJECT_ID pelo ID do projeto;
      • RENDERER_SERVICE_URL pelo URL fornecido após a implantação do serviço de renderização Markdown.

Noções básicas sobre o tráfego HTTPS

Há três solicitações HTTP envolvidas na renderização de marcação usando esses serviços.

Diagrama mostrando o fluxo de solicitação do usuário para o editor, editor para receber um token do servidor de metadados, editor para fazer a solicitação de renderização do serviço, renderizar serviço para retornar HTML ao editor.
O serviço de front-end com o editor-identity invoca o serviço de renderização. editor-identity e renderer-identity têm permissões limitadas. Portanto, qualquer exploração de segurança ou injeção de código tem acesso limitado a outros recursos do Google Cloud.

Como testar

Para testar o aplicativo de dois serviços completo:

  1. Navegue até o URL fornecido pela etapa de implantação acima.

  2. Tente editar o texto Markdown à esquerda e clique no botão para visualizá-lo à direita.

    O resultado será parecido com este:

    Captura de tela da interface do usuário do Markdown Editor

Se você optar por continuar desenvolvendo esses serviços, lembre-se de que eles têm acesso restrito do gerenciamento de identidade e acesso (IAM, na sigla em inglês) ao restante do Google Cloud e precisarão receber papéis adicionais do IAM para acessar muitos outros serviços.

Limpeza

Se você criou um novo projeto para este tutorial, exclua o projeto. Se você usou um projeto atual e quer mantê-lo sem as alterações incluídas neste tutorial, exclua os recursos criados para o tutorial.

Como excluir o projeto

O jeito mais fácil de evitar cobranças é excluindo o projeto que você criou para o tutorial.

Para excluir o projeto:

  1. No Console do Cloud, acesse a página Gerenciar recursos:

    Acessar "Gerenciar recursos"

  2. Na lista de projetos, selecione o projeto que você quer excluir e clique em Excluir .
  3. Na caixa de diálogo, digite o ID do projeto e clique em Encerrar para excluí-lo.

Como excluir recursos do tutorial

  1. Exclua o serviço do Cloud Run que você implantou neste tutorial:

    gcloud run services delete SERVICE

    SERVICE é o nome escolhido do serviço.

    Também é possível excluir os serviços do Cloud Run no Console do Google Cloud.

  2. Remova as configurações padrão do gcloud que você adicionou durante a configuração do tutorial.

     gcloud config unset run/region
    
  3. Remova a configuração do projeto:

     gcloud config unset project
    
  4. Exclua outros recursos do Google Cloud criados neste tutorial:

A seguir