Programar uma VM do Compute Engine para iniciar ou parar


Neste tutorial, mostramos como usar o Cloud Scheduler e o Cloud Functions para iniciar e interromper automaticamente instâncias do Compute Engine em uma programação regular usando rótulos de recursos.

Objetivos

  • Escrever e implantar um conjunto de funções com o Cloud Functions que iniciem e interrompam instâncias do Compute Engine.
  • Crie um conjunto de tarefas com o Cloud Scheduler que agende instâncias com um rótulo de recurso dev para execução das 09:00-17:00, de segunda a sexta-feira, para corresponder ao horário comercial normal.

Custos

Neste documento, você usará os seguintes componentes faturáveis do Google Cloud:

  • Cloud Scheduler
  • Cloud Functions
  • Pub/Sub
  • Compute Engine

Para gerar uma estimativa de custo baseada na projeção de uso deste tutorial, use a calculadora de preços. Novos usuários do Google Cloud podem estar qualificados para uma avaliação gratuita.

Antes de começar

  1. Configure seu ambiente para o Cloud Scheduler.

    Como configurar seu ambiente

  2. Ative as APIs Cloud Functions, Pub/Sub, and Compute Engine.

    Ative as APIs

Arquitetura do aplicativo

Esta solução inclui os seguintes componentes do Google Cloud:

Diagrama da arquitetura do sistema mostrando o Cloud Scheduler agendando uma instância do Compute Engine via Pub/Sub

Requisitos para locais

Alguns componentes são compatíveis apenas com determinadas regiões:

  • Instância do Compute Engine: compatível com qualquer região listada em Regiões e zonas.
  • Cloud Functions: compatível com as regiões listadas em Locais.
  • Mensagens do Pub/Sub: compatíveis globalmente, porque o Pub/Sub é um serviço global.
  • Jobs do Cloud Scheduler com destinos do Pub/Sub: compatíveis com qualquer local do Google Cloud.

Por que não HTTP em vez de Pub/Sub?

Você pode simplificar essa arquitetura usando Gatilhos HTTP do Cloud Functions em vez de Gatilhos do Pub/Sub.

Neste tutorial, usamos o Pub/Sub como o gatilho do Cloud Functions porque esse método anteriormente era mais seguro do que o uso de HTTP. No entanto, o HTTP também é uma opção válida e agora pode ser protegido com a exigência de autenticação.

Para saber como proteger o Cloud Functions, consulte a visão geral de segurança do Cloud Functions. Para uma comparação entre acionadores HTTP e Pub/Sub, consulte a documentação Acionadores do Cloud Functions.

Configurar a instância do Compute Engine

Console

  1. Acesse a página Instâncias de VMs no Console do Google Cloud.
    Faça isso neste link
  2. Clique em Criar instância.
  3. Defina o Nome como dev-instance.
  4. Em Rótulos, clique em Adicionar rótulos.
  5. Clique em Adicionar marcador.
  6. Insira env em Chave e dev em Valor.
  7. Em Região, selecione us-west1.
  8. Em Zona, selecione us-west1-b.
  9. Clique em Save.
  10. Clique em Criar na parte inferior da página.

gcloud

gcloud compute instances create dev-instance \
    --network default \
    --zone us-west1-b \
    --labels=env=dev

Implantar funções acionadas pelo Pub/Sub com o Cloud Functions

Criar e implantar as funções

Console

Criar a função de inicialização

  1. Acesse a página de visão geral do Cloud Functions no console do Google Cloud.
    Acessar a página Cloud Functions.
  2. Clique em Criar função.
  3. Em Ambiente, selecione primeira geração.
  4. Defina o Nome da função como startInstancePubSub.
  5. Deixe o valor padrão em Região.
  6. Em Tipo de acionador, selecione Cloud Pub/Sub.
  7. Em Selecionar um tópico do Cloud Pub/Sub, clique em Criar um tópico.
  8. Uma caixa de diálogo Create topic será exibida.
    1. Em ID do tópico, insira start-instance-event.
    2. Clique em Criar para fechar a caixa de diálogo.
  9. Clique em Salvar na parte inferior da caixa gatilho.
  10. Clique em Avançar na parte inferior da página.
  11. Em Ambiente de execução, selecione Node.js 16 ou posterior.
  12. Em Ponto de entrada, digite startInstancePubSub.
  13. No lado esquerdo do editor de código, selecione index.js.
  14. Substitua o código inicial pelo seguinte:

    const compute = require('@google-cloud/compute');
    const instancesClient = new compute.InstancesClient();
    const operationsClient = new compute.ZoneOperationsClient();
    
    async function waitForOperation(projectId, operation) {
      while (operation.status !== 'DONE') {
        [operation] = await operationsClient.wait({
          operation: operation.name,
          project: projectId,
          zone: operation.zone.split('/').pop(),
        });
      }
    }
    
    /**
     * Starts Compute Engine instances.
     *
     * Expects a PubSub message with JSON-formatted event data containing the
     * following attributes:
     *  zone - the GCP zone the instances are located in.
     *  label - the label of instances to start.
     *
     * @param {!object} event Cloud Function PubSub message event.
     * @param {!object} callback Cloud Function PubSub callback indicating
     *  completion.
     */
    exports.startInstancePubSub = async (event, context, callback) => {
      try {
        const project = await instancesClient.getProjectId();
        const payload = _validatePayload(event);
        const options = {
          filter: `labels.${payload.label}`,
          project,
          zone: payload.zone,
        };
    
        const [instances] = await instancesClient.list(options);
    
        await Promise.all(
          instances.map(async instance => {
            const [response] = await instancesClient.start({
              project,
              zone: payload.zone,
              instance: instance.name,
            });
    
            return waitForOperation(project, response.latestResponse);
          })
        );
    
        // Operation complete. Instance successfully started.
        const message = 'Successfully started instance(s)';
        console.log(message);
        callback(null, message);
      } catch (err) {
        console.log(err);
        callback(err);
      }
    };
    
    /**
     * Validates that a request payload contains the expected fields.
     *
     * @param {!object} payload the request payload to validate.
     * @return {!object} the payload object.
     */
    const _validatePayload = event => {
      let payload;
      try {
        payload = JSON.parse(Buffer.from(event.data, 'base64').toString());
      } catch (err) {
        throw new Error('Invalid Pub/Sub message: ' + err);
      }
      if (!payload.zone) {
        throw new Error("Attribute 'zone' missing from payload");
      } else if (!payload.label) {
        throw new Error("Attribute 'label' missing from payload");
      }
      return payload;
    };
  15. No lado esquerdo do editor de código, selecione o package.json.

  16. Substitua o código inicial pelo seguinte:

    {
      "name": "cloud-functions-schedule-instance",
      "version": "0.1.0",
      "private": true,
      "license": "Apache-2.0",
      "author": "Google Inc.",
      "repository": {
        "type": "git",
        "url": "https://github.com/GoogleCloudPlatform/nodejs-docs-samples.git"
      },
      "engines": {
        "node": ">=16.0.0"
      },
      "scripts": {
        "test": "c8 mocha -p -j 2 test/*.test.js --timeout=20000"
      },
      "devDependencies": {
        "c8": "^8.0.0",
        "mocha": "^10.0.0",
        "proxyquire": "^2.0.0",
        "sinon": "^16.0.0"
      },
      "dependencies": {
        "@google-cloud/compute": "^4.0.0"
      }
    }
    
  17. Clique em Deploy na parte inferior da página.

Criar a função de parada

  1. Você deve estar na página Cloud Functions no console do Google Cloud.
  2. Clique em Criar função.
  3. Em Ambiente, selecione primeira geração.
  4. Defina o Nome da função como stopInstancePubSub.
  5. Deixe o valor padrão em Região.
  6. Em Tipo de acionador, selecione Cloud Pub/Sub.
  7. Em Selecionar um tópico do Cloud Pub/Sub, clique em Criar um tópico.
  8. Uma caixa de diálogo Create topic será exibida.
    1. Em ID do tópico, insira stop-instance-event.
    2. Clique em Criar para fechar a caixa de diálogo.
  9. Clique em Salvar na parte inferior da caixa gatilho.
  10. Clique em Avançar na parte inferior da página.
  11. Em Ambiente de execução, selecione Node.js 16 ou posterior.
  12. Em Ponto de entrada, digite stopInstancePubSub.
  13. No lado esquerdo do editor de código, selecione index.js.
  14. Substitua o código inicial pelo seguinte:

    const compute = require('@google-cloud/compute');
    const instancesClient = new compute.InstancesClient();
    const operationsClient = new compute.ZoneOperationsClient();
    
    async function waitForOperation(projectId, operation) {
      while (operation.status !== 'DONE') {
        [operation] = await operationsClient.wait({
          operation: operation.name,
          project: projectId,
          zone: operation.zone.split('/').pop(),
        });
      }
    }
    
    /**
     * Stops Compute Engine instances.
     *
     * Expects a PubSub message with JSON-formatted event data containing the
     * following attributes:
     *  zone - the GCP zone the instances are located in.
     *  label - the label of instances to stop.
     *
     * @param {!object} event Cloud Function PubSub message event.
     * @param {!object} callback Cloud Function PubSub callback indicating completion.
     */
    exports.stopInstancePubSub = async (event, context, callback) => {
      try {
        const project = await instancesClient.getProjectId();
        const payload = _validatePayload(event);
        const options = {
          filter: `labels.${payload.label}`,
          project,
          zone: payload.zone,
        };
    
        const [instances] = await instancesClient.list(options);
    
        await Promise.all(
          instances.map(async instance => {
            const [response] = await instancesClient.stop({
              project,
              zone: payload.zone,
              instance: instance.name,
            });
    
            return waitForOperation(project, response.latestResponse);
          })
        );
    
        // Operation complete. Instance successfully stopped.
        const message = 'Successfully stopped instance(s)';
        console.log(message);
        callback(null, message);
      } catch (err) {
        console.log(err);
        callback(err);
      }
    };
    
    /**
     * Validates that a request payload contains the expected fields.
     *
     * @param {!object} payload the request payload to validate.
     * @return {!object} the payload object.
     */
    const _validatePayload = event => {
      let payload;
      try {
        payload = JSON.parse(Buffer.from(event.data, 'base64').toString());
      } catch (err) {
        throw new Error('Invalid Pub/Sub message: ' + err);
      }
      if (!payload.zone) {
        throw new Error("Attribute 'zone' missing from payload");
      } else if (!payload.label) {
        throw new Error("Attribute 'label' missing from payload");
      }
      return payload;
    };
  15. No lado esquerdo do editor de código, selecione o package.json.

  16. Substitua o código inicial pelo seguinte:

    {
      "name": "cloud-functions-schedule-instance",
      "version": "0.1.0",
      "private": true,
      "license": "Apache-2.0",
      "author": "Google Inc.",
      "repository": {
        "type": "git",
        "url": "https://github.com/GoogleCloudPlatform/nodejs-docs-samples.git"
      },
      "engines": {
        "node": ">=16.0.0"
      },
      "scripts": {
        "test": "c8 mocha -p -j 2 test/*.test.js --timeout=20000"
      },
      "devDependencies": {
        "c8": "^8.0.0",
        "mocha": "^10.0.0",
        "proxyquire": "^2.0.0",
        "sinon": "^16.0.0"
      },
      "dependencies": {
        "@google-cloud/compute": "^4.0.0"
      }
    }
    
  17. Clique em Deploy na parte inferior da página.

gcloud

Crie os tópicos do Pub/Sub.

gcloud pubsub topics create start-instance-event
gcloud pubsub topics create stop-instance-event

Receber o código

  1. Faça o download do código.

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

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

  2. Acesse o diretório correto.

    cd nodejs-docs-samples/functions/scheduleinstance/
    

Criar as funções de inicialização e parada

Você deve estar no diretório nodejs-docs-samples/functions/scheduleinstance/.

gcloud functions deploy startInstancePubSub \
    --trigger-topic start-instance-event \
    --runtime nodejs18 \
    --allow-unauthenticated
gcloud functions deploy stopInstancePubSub \
    --trigger-topic stop-instance-event \
    --runtime nodejs18 \
    --allow-unauthenticated

(Opcional) Verificar se as funções são executadas corretamente

Console

Interromper a instância

  1. Acesse a página de visão geral do Cloud Functions no console do Google Cloud.
    Acessar a página Cloud Functions.
  2. Clique na função denominada stopInstancePubSub.
  3. Você verá várias guias: Geral, gatilho, Origem, Permissões e Testes. Clique na guia Testes.
  4. Em Evento acionador, digite o seguinte:

    {"data":"eyJ6b25lIjoidXMtd2VzdDEtYiIsICJsYWJlbCI6ImVudj1kZXYifQo="}
    

    • Essa é simplesmente a sequência codificada em base64 para {"zone":"us-west1-b", "label":"env=dev"}

    • Se quiser codificar sua própria string, use qualquer ferramenta de codificação base64 on-line.

  5. Clique no botão Testar a função.

  6. Quando terminar de executar, você verá Successfully stopped instance dev-instance impresso em Saída. A execução pode levar até 60 segundos para ser concluída.

    • Se você vir error: 'Error: function failed to load.', aguarde 10 segundos ou mais para que a função conclua a implantação e tente novamente.

    • Se você vir error: 'Error: function execution attempt timed out.', passe para a próxima etapa para ver se a instância está demorando muito para ser encerrada.

    • Se a execução for encerrada e nenhuma informação for exibida, provavelmente o tempo expirou. Basta seguir para a próxima etapa e confirmar se a instância está demorando muito para ser desativada.

  7. Acesse a página Instâncias de VMs no Console do Google Cloud.
    Faça isso neste link

  8. Verifique se a instância chamada dev-instance possui um quadrado cinza ao lado do nome, indicando que parou. Pode levar até 30 segundos para a desativação ser concluída.

    • Se a instância não for encerrada, clique em Atualizar na parte superior da página.

Iniciar a instância

  1. Acesse a página de visão geral do Cloud Functions no console do Google Cloud.
    Acessar a página Cloud Functions.
  2. Clique na função denominada startInstancePubSub.
  3. Você verá várias guias: Geral, gatilho, Origem, Permissões e Testes. Clique na guia Testes.
  4. Em Evento acionador, digite o seguinte:

    {"data":"eyJ6b25lIjoidXMtd2VzdDEtYiIsICJsYWJlbCI6ImVudj1kZXYifQo="}
    

    • Novamente, essa é simplesmente a sequência codificada em base64 para {"zone":"us-west1-b", "label":"env=dev"}
  5. Clique no botão Testar a função.

  6. Quando terminar de executar, você verá Successfully started instance dev-instance impresso em Saída.

  7. Acesse a página Instâncias de VMs no Console do Google Cloud.
    Faça isso neste link

  8. Verifique se a instância chamada dev-instance possui uma marca de seleção verde ao lado do nome, indicando que está em execução. Pode levar até 30 segundos para a inicialização ser concluída.

gcloud

Interromper a instância

  1. Chame a função para interromper a instância.

    gcloud functions call stopInstancePubSub \
        --data '{"data":"eyJ6b25lIjoidXMtd2VzdDEtYiIsICJsYWJlbCI6ImVudj1kZXYifQo="}'
    
    • Essa é simplesmente a sequência codificada em base64 para {"zone":"us-west1-b", "label":"env=dev"}

    • Se você quiser codificar sua própria string, poderá utilizar qualquer ferramenta. Aqui está um exemplo usando a ferramenta de linha de comando base64:

      echo '{"zone":"us-west1-b", "label":"env=dev"}' | base64
      
      eyJ6b25lIjoidXMtd2VzdDEtYiIsICJsYWJlbCI6ImVudj1kZXYifQo=
      

    Quando a função for encerrada, você verá o seguinte:

    result: Successfully stopped instance dev-instance
    

    A execução pode levar até 60 segundos para ser concluída.

    • Se, em vez disso, você receber o erro:

      error: 'Error: function failed to load.`
      

      Aguarde cerca de 10 segundos até que a implantação seja concluída e tente novamente.

    • Se, em vez disso, você receber o erro:

      error: `Error: function execution attempt timed out.`
      

      Siga para a próxima etapa e confirme se a instância está demorando muito para ser desativada.

    • Se, em vez disso, você não receber resultados, provavelmente o tempo limite da função expirou. Siga para a próxima etapa e confirme se a instância está demorando muito para ser desativada.

  2. Verifique se a instância tem um status TERMINATED. Pode levar até 30 segundos para a desativação ser concluída.

    gcloud compute instances describe dev-instance \
        --zone us-west1-b \
        | grep status
    
    status: TERMINATED
    

Iniciar a instância

  1. Chame a função para iniciar a instância.

    gcloud functions call startInstancePubSub \
        --data '{"data":"eyJ6b25lIjoidXMtd2VzdDEtYiIsICJsYWJlbCI6ImVudj1kZXYifQo="}'
    
    • Novamente, essa é simplesmente a sequência codificada em base64 para {"zone":"us-west1-b", "label":"env=dev"}

    Quando a função for encerrada, você verá o seguinte:

    result: Successfully started instance dev-instance
    
  2. Verifique se a instância tem um status RUNNING. Pode levar até 30 segundos para a inicialização ser concluída.

    gcloud compute instances describe dev-instance \
        --zone us-west1-b \
        | grep status
    
    status: RUNNING
    

Configure os trabalhos do Cloud Scheduler para chamar o Pub/Sub

Criar os jobs

Console

Criar o job de inicialização

  1. Vá para a página Cloud Scheduler no Console do Google Cloud.
    Acessar a página do Cloud Scheduler.
  2. Clique em Criar um job.
  3. Deixe a região padrão.
  4. Defina o Nome como startup-dev-instances.
  5. Em Frequência, insira 0 9 * * 1-5.
    • Isso será executado às 9h todos os dias, de segunda a sexta-feira.
  6. Em Fuso horário, selecione o país e o fuso horário desejados. Este exemplo usará United States e Los Angeles.
  7. Clique em Continuar.
  8. Em Tipo de destino, selecione Pub/Sub.
  9. Selecione start-instance-event no menu suspenso de temas.
  10. Em Mensagem, insira o seguinte:
    {"zone":"us-west1-b","label":"env=dev"}
    
  11. Clique em Criar.

Criar o job de parada

  1. Você deve estar na página Cloud Scheduler no console do Google Cloud.
  2. Clique em Criar job.
  3. Mantenha a região padrão e clique em Avançar na parte inferior da página.
  4. Defina o Nome como shutdown-dev-instances.
  5. Em Frequência, insira 0 17 * * 1-5.
    • Isso será executado às 17h todos os dias, de segunda a sexta-feira.
  6. Em Fuso horário, selecione o país e o fuso horário desejados. Este exemplo usará United States e Los Angeles.
  7. Clique em Continuar.
  8. Em Tipo de destino, selecione Pub/Sub.
  9. Selecione stop-instance-event no menu suspenso de temas.
  10. Em Mensagem, insira o seguinte:
    {"zone":"us-west1-b","label":"env=dev"}
    
  11. Clique em Criar.

gcloud

Criar o job de inicialização

gcloud scheduler jobs create pubsub startup-dev-instances \
    --schedule '0 9 * * 1-5' \
    --topic start-instance-event \
    --message-body '{"zone":"us-west1-b", "label":"env=dev"}' \
    --time-zone 'America/Los_Angeles' \
    --location us-central1

Criar o job de parada

gcloud scheduler jobs create pubsub shutdown-dev-instances \
    --schedule '0 17 * * 1-5' \
    --topic stop-instance-event \
    --message-body '{"zone":"us-west1-b", "label":"env=dev"}' \
    --time-zone 'America/Los_Angeles' \
    --location us-central1

(Opcional) Verificar se os jobs são executados corretamente

Console

Interromper a instância

  1. Vá para a página Cloud Scheduler no Console do Google Cloud.
    Acessar a página do Cloud Scheduler.
  2. Para o job chamado shutdown-dev-instances, clique no botão Executar agora no lado direito da página.
  3. Acesse a página Instâncias de VMs no Console do Google Cloud.
    Faça isso neste link
  4. Verifique se a instância chamada dev-instance possui um quadrado cinza ao lado do nome, indicando que parou. Pode levar até 30 segundos para a desativação ser concluída.

Iniciar a instância

  1. Vá para a página Cloud Scheduler no Console do Google Cloud.
    Acessar a página do Cloud Scheduler.
  2. Para o job chamado startup-dev-instances, clique no botão Executar agora no lado direito da página.
  3. Acesse a página Instâncias de VMs no Console do Google Cloud.
    Faça isso neste link
  4. Verifique se a instância chamada dev-instance possui uma marca de seleção verde ao lado do nome, indicando que está em execução. Pode demorar até 30 segundos para a inicialização ser concluída.

gcloud

Interromper a instância

  1. Execute o job do programador para interromper a instância.

    gcloud beta scheduler jobs run shutdown-dev-instances
    
  2. Verifique se a instância tem um status TERMINATED. Pode levar até 30 segundos para que o desligamento seja concluído.

    gcloud compute instances describe dev-instance \
        --zone us-west1-b \
        | grep status
    
    status: TERMINATED
    

Iniciar a instância

  1. Execute o job do programador para iniciar a instância.

    gcloud beta scheduler jobs run startup-dev-instances
    
  2. Verifique se a instância tem um status RUNNING. Pode levar até 30 segundos para a inicialização ser concluída.

    gcloud compute instances describe dev-instance \
        --zone us-west1-b \
        | grep status
    
    status: RUNNING
    

Limpar

Depois de concluir o tutorial, você pode limpar os recursos que criou para que eles parem de usar a cota e gerar cobranças. Nas seções a seguir, você aprenderá a excluir e desativar esses recursos.

Excluir os jobs do Cloud Scheduler

  1. Vá para a página Cloud Scheduler no Console do Google Cloud.

    Acessar a página do Cloud Scheduler.

  2. Clique nas caixas de seleção ao lado dos seus jobs.

  3. Clique no botão Excluir na parte superior da página e confirme.

Excluir os tópicos do Pub/Sub

  1. Acesse a página Pub/Sub no Console do Google Cloud.

    Acessar a página do Pub/Sub

  2. Clique nas caixas de seleção ao lado dos seus tópicos.

  3. Clique em Excluir na parte superior da página e confirme.

Excluir as funções implantadas com o Cloud Functions

  1. Acesse a página de visão geral do Cloud Functions no console do Google Cloud.

    Acessar a página Cloud Functions.

  2. Clique nas caixas de seleção ao lado das funções.

  3. Clique no botão Excluir na parte superior da página e confirme.

Excluir a instância do Compute Engine

Para excluir uma instância do Compute Engine:

  1. No Console do Google Cloud, acesse a página Instâncias de VMs.

    Acessar instâncias de VM

  2. Marque a caixa de seleção de a instância que você quer excluir.
  3. Para excluir a instância, clique em Mais ações, clique em Excluir e siga as instruções.

Exclua o projeto

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

Para excluir o projeto, faça o seguinte:

  1. No Console do Google 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.

A seguir