Programar uma VM do Compute Engine para iniciar ou parar


Este tutorial mostra como usar o Cloud Scheduler e as funções do Cloud Run para iniciar e interromper automaticamente as 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 Run que iniciam e param 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 Run 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. Enable the Cloud Run functions, Pub/Sub, and Compute Engine APIs.

    Enable the 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 de localização

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 Run functions: compatível com as regiões listadas em Locais.
  • Mensagens do Pub/Sub: compatíveis globalmente, uma vez que o Pub/Sub é um serviço global.
  • Jobs do Cloud Scheduler com destinos do Pub/Sub: são aceitos em qualquer local do Google Cloud.

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

Você pode simplificar essa arquitetura usando os acionadores HTTP do Cloud Run Functions em vez dos acionadores do Pub/Sub.

Este tutorial usa o Pub/Sub como o gatilho das funções do Cloud Run porque esse método era mais seguro do que usar o HTTP. No entanto, o HTTP também é uma opção válida e agora pode ser protegido exigindo autenticação.

Para saber como proteger o Cloud Run functions, consulte a visão geral de segurança do Cloud Run functions. Para uma comparação entre os acionadores HTTP e do Pub/Sub, consulte a documentação de acionadores do Cloud Run 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 rótulo.
  6. Digite env para Chave e dev para Valor.
  7. Em Região, selecione us-west1.
  8. Em Zona, selecione us-west1-b.
  9. Clique em Salvar.
  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 usando funções do Cloud Run

Criar e implantar as funções

Console

Criar a função de inicialização

  1. Acesse a página do Cloud Run functions no console do Google Cloud.
    Acesse a página de funções do Cloud Run.
  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 gatilho, selecione Cloud Pub/Sub.
  7. Em Selecionar um tópico do Cloud Pub/Sub, clique em Criar um tópico.
  8. A caixa de diálogo Criar tópico vai aparecer.
    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 mais recente.
  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 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": "^10.0.0",
        "mocha": "^10.0.0",
        "proxyquire": "^2.0.0",
        "sinon": "^18.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 Funções do Cloud Run 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 gatilho, selecione Cloud Pub/Sub.
  7. Em Selecionar um tópico do Cloud Pub/Sub, clique em Criar um tópico.
  8. A caixa de diálogo Criar tópico vai aparecer.
    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 mais recente.
  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 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": "^10.0.0",
        "mocha": "^10.0.0",
        "proxyquire": "^2.0.0",
        "sinon": "^18.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 do Cloud Run functions no console do Google Cloud.
    Acesse a página de funções do Cloud Run.
  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 do Cloud Run functions no console do Google Cloud.
    Acesse a página de funções do Cloud Run.
  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 que a desativação seja 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 tópicos.
  10. Em Mensagem, digite 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 tópicos.
  10. Em Mensagem, digite 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 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. 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 pelo Cloud Run

  1. Acesse a página do Cloud Run functions no console do Google Cloud.

    Acesse a página de funções do Cloud Run.

  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. In the Google Cloud console, go to the VM instances page.

    Go to VM instances

  2. Select the checkbox for the instance that you want to delete.
  3. To delete the instance, click More actions, click Delete, and then follow the instructions.

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:

  1. In the Google Cloud console, go to the Manage resources page.

    Go to Manage resources

  2. In the project list, select the project that you want to delete, and then click Delete.
  3. In the dialog, type the project ID, and then click Shut down to delete the project.

A seguir