Programa una VM de Compute Engine para que se inicie o se detenga


En este instructivo, se muestra cómo usar Cloud Scheduler y Cloud Functions para iniciar y detener automáticamente instancias de Compute Engine de forma periódica mediante etiquetas de recursos.

Objetivos

  • Escribir e implementar un conjunto de funciones con Cloud Functions que inicien y detengan instancias de Compute Engine
  • Cree un conjunto de trabajos con Cloud Scheduler que programe instancias con una etiqueta de recursos dev para ejecutar de 09:00 a 17:00, de lunes a viernes para que coincida con el horario comercial habitual.

Costos

En este documento, usarás los siguientes componentes facturables de Google Cloud:

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

Para generar una estimación de costos en función del uso previsto, usa la calculadora de precios. Es posible que los usuarios nuevos de Google Cloud califiquen para obtener una prueba gratuita.

Antes de comenzar

  1. Configura tu entorno para Cloud Scheduler.

    Configura tu entorno

  2. Habilita las API de Cloud Functions, Pub/Sub, and Compute Engine.

    Habilita las API

Arquitectura de aplicaciones

Esta solución incluye los siguientes componentes de Google Cloud:

Diagrama de arquitectura del sistema que muestra cómo Cloud Scheduler programa una instancia de Compute Engine a través de Pub/Sub

Requisitos de ubicación

Algunos componentes solo están disponibles en determinadas regiones:

  • Instancia de Compute Engine: Se admite en cualquier región que se enumera en Regiones y zonas.
  • Cloud Functions: compatible con las regiones enumeradas en Ubicaciones.
  • Mensajes de Pub/Sub: admitidos globalmente, ya que Pub/Sub es un servicio global.
  • Trabajos de Cloud Scheduler con destinos de Pub/Sub: compatibles con cualquier ubicación de Google Cloud

¿Por qué no usar HTTP en lugar de Pub/Sub?

Es posible que desee simplificar esta arquitectura utilizando activadores HTTP de Cloud Functions en lugar de activadores de Pub/Sub.

En este instructivo, se usa Pub/Sub como el activador de Cloud Functions porque este método antes era más seguro que el uso de HTTP. Sin embargo, HTTP también es una opción válida y ahora se puede proteger mediante la solicitud de autenticación.

Para obtener información sobre cómo proteger Cloud Functions, consulta la descripción general de seguridad de Cloud Functions. Para ver una comparación entre los activadores HTTP y de Pub/Sub, consulta la documentación de activadores de Cloud Functions.

Cómo configurar la instancia de Compute Engine

Console

  1. Ve a la página Instancias de VM en la consola de Google Cloud.
    Ir a la página Instancias de VM
  2. Haz clic en Crear instancia.
  3. Configura el campo Nombre como dev-instance.
  4. En Etiquetas, haz clic en Agregar etiquetas.
  5. Haz clic en Agregar etiqueta.
  6. Ingresa env en Clave y dev en Valor.
  7. En Región, selecciona us-west1.
  8. En Zona, selecciona us-west1-b.
  9. Haz clic en Guardar.
  10. Haz clic en Crear en la parte inferior de la página.

gcloud

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

Implementa funciones activadas por Pub/Sub a través de Cloud Functions

Crea e implementa las funciones

Console

Crea la función de inicio.

  1. Ve a la página de Cloud Functions en la consola de Google Cloud.
    Ir a la página Cloud Functions
  2. Haz clic en Crear función.
  3. En Entorno, selecciona 1ª gen.
  4. Configura el Nombre de la función como startInstancePubSub.
  5. Deja Región en el valor predeterminado.
  6. En Tipo de activador, selecciona Cloud Pub/Sub.
  7. En Selecciona un tema de Cloud Pub/Sub, haz clic en Crear un tema.
  8. Aparecerá el diálogo Crear tema.
    1. En ID del tema, ingresa start-instance-event.
    2. Haz clic en Crear para finalizar el diálogo.
  9. Haz clic en Guardar en la parte inferior del cuadro Activador.
  10. Haz clic en Siguiente en la parte inferior de la página.
  11. En Entorno de ejecución, selecciona Node.js 16 o una versión posterior.
  12. En Punto de entrada, ingresa startInstancePubSub.
  13. En el lado izquierdo del editor de código, selecciona index.js.
  14. Reemplaza el código de inicio con lo siguiente:

    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. En el lado izquierdo del editor de código, selecciona package.json.

  16. Reemplaza el código de inicio con lo siguiente:

    {
      "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. Haz clic en Implementar en la parte inferior de la página.

Crea la función de detención.

  1. Deberías estar en la página de Cloud Functions en la consola de Google Cloud.
  2. Haz clic en Crear función.
  3. En Entorno, selecciona 1ª gen.
  4. Configura el Nombre de la función como stopInstancePubSub.
  5. Deja Región en el valor predeterminado.
  6. En Tipo de activador, selecciona Cloud Pub/Sub.
  7. En Selecciona un tema de Cloud Pub/Sub, haz clic en Crear un tema.
  8. Aparecerá el diálogo Crear tema.
    1. En ID del tema, ingresa stop-instance-event.
    2. Haz clic en Crear para finalizar el diálogo.
  9. Haz clic en Guardar en la parte inferior del cuadro Activador.
  10. Haz clic en Siguiente en la parte inferior de la página.
  11. En Entorno de ejecución, selecciona Node.js 16 o una versión posterior.
  12. En Punto de entrada, ingresa stopInstancePubSub.
  13. En el lado izquierdo del editor de código, selecciona index.js.
  14. Reemplaza el código de inicio con lo siguiente:

    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. En el lado izquierdo del editor de código, selecciona package.json.

  16. Reemplaza el código de inicio con lo siguiente:

    {
      "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. Haz clic en Implementar en la parte inferior de la página.

gcloud

Crea los temas de Pub/Sub.

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

Cómo obtener el código

  1. Descarga el código.

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

    De manera opcional, puedes descargar la muestra como un archivo zip y extraerla.

  2. Ve al directorio correcto.

    cd nodejs-docs-samples/functions/scheduleinstance/
    

Crea las funciones de inicio y detención.

Debes estar en el directorio 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: Verifica que tus funciones actúen correctamente

Console

Detener la instancia

  1. Ve a la página de Cloud Functions en la consola de Google Cloud.
    Ir a la página Cloud Functions
  2. Haz clic en la función denominada stopInstancePubSub.
  3. Deberías ver varias pestañas: Consideraciones Generales : Activador : Búsqueda , Permisos y Realiza una prueba. Haz clic en la pestaña Prueba.
  4. Para Evento de activación, ingresa lo siguiente:

    {"data":"eyJ6b25lIjoidXMtd2VzdDEtYiIsICJsYWJlbCI6ImVudj1kZXYifQo="}
    

    • Esto es simplemente una string codificada en base64 para {"zone":"us-west1-b", "label":"env=dev"}

    • Si deseas codificar tu propia string, puedes usar cualquier herramienta de codificación en base64 en línea.

  5. Haz clic en el botón Probar la función.

  6. Cuando termine de ejecutarse, deberías ver Successfully stopped instance dev-instance impreso en Resultado. La ejecución puede tardar hasta 60 segundos en completarse.

    • Si, en cambio, ves error: 'Error: function failed to load.', espera unos 10 segundos para que la función termine de implementarse y vuelve a intentarlo.

    • Si, en cambio, ves error: 'Error: function execution attempt timed out.', continúa con el paso siguiente para comprobar si la instancia está tardando mucho en desactivarse.

    • Si, en cambio, termina de ejecutarse, pero no se muestra ningún resultado, probablemente también se haya agotado el tiempo de espera. Simplemente continúa con el siguiente paso para comprobar si la instancia está tardando mucho en desactivarse.

  7. Ve a la página Instancias de VM en la consola de Google Cloud.
    Ir a la página Instancias de VM

  8. Verifica que la instancia denominada dev-instance tenga un cuadrado gris junto a su nombre, lo que indica que que se detuvo. Puede tardar hasta 30 segundos en terminar de desactivarse.

    • Si parece que no va a finalizar, prueba hacer clic en Actualizar en la parte superior de la página.

Inicia la instancia

  1. Ve a la página de Cloud Functions en la consola de Google Cloud.
    Ir a la página Cloud Functions
  2. Haz clic en la función denominada startInstancePubSub.
  3. Deberías ver varias pestañas: Consideraciones Generales : Activador : Búsqueda , Permisos y Realiza una prueba. Haz clic en la pestaña Prueba.
  4. Para Evento de activación, ingresa lo siguiente:

    {"data":"eyJ6b25lIjoidXMtd2VzdDEtYiIsICJsYWJlbCI6ImVudj1kZXYifQo="}
    

    • Nuevamente, esta es simplemente la string codificada en base64 para {"zone":"us-west1-b", "label":"env=dev"}
  5. Haz clic en el botón Probar la función.

  6. Cuando termine de ejecutarse, deberías ver Successfully started instance dev-instance impreso en Resultado.

  7. Ve a la página Instancias de VM en la consola de Google Cloud.
    Ir a la página Instancias de VM

  8. Verifica que la instancia denominada dev-instance tenga una marca de verificación verde junto a su nombre, lo que indica que se está ejecutando. Puede tardar hasta 30 segundos en terminar de iniciarse.

gcloud

Detén la instancia

  1. Llama la función para detener la instancia.

    gcloud functions call stopInstancePubSub \
        --data '{"data":"eyJ6b25lIjoidXMtd2VzdDEtYiIsICJsYWJlbCI6ImVudj1kZXYifQo="}'
    
    • Esto es simplemente una string codificada en base64 para {"zone":"us-west1-b", "label":"env=dev"}

    • Si deseas codificar tu propia string, puedes usar cualquier herramienta. Aquí hay un ejemplo usando la herramienta de línea de comandos de base64:

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

    Cuando la función haya finalizado, deberías ver lo siguiente:

    result: Successfully stopped instance dev-instance
    

    La ejecución puede tardar hasta 60 segundos en completarse.

    • Si, en cambio, aparece el siguiente error:

      error: 'Error: function failed to load.`
      

      Simplemente espera unos 10 segundos para que la función termine de implementarse y vuelve a intentarlo.

    • Si, en cambio, aparece el siguiente error:

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

      Continúa con el siguiente paso para comprobar si la instancia está tardando mucho en desactivarse.

    • Si, en cambio, no obtienes ningún resultado, es probable que se haya agotado el tiempo de espera de la función. Continúa con el siguiente paso para comprobar si la instancia está tardando mucho en desactivarse.

  2. Comprueba que la instancia tenga el estado TERMINATED. Puede tardar hasta 30 segundos en terminar de apagarse.

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

Inicia la instancia

  1. Llama la función para iniciar la instancia.

    gcloud functions call startInstancePubSub \
        --data '{"data":"eyJ6b25lIjoidXMtd2VzdDEtYiIsICJsYWJlbCI6ImVudj1kZXYifQo="}'
    
    • Nuevamente, esta es simplemente la string codificada en base64 para {"zone":"us-west1-b", "label":"env=dev"}

    Cuando la función haya finalizado, deberías ver lo siguiente:

    result: Successfully started instance dev-instance
    
  2. Comprueba que la instancia tenga el estado RUNNING. Puede tardar hasta 30 segundos en terminar de iniciarse.

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

Configura los trabajos de Cloud Scheduler para llamar a Pub/Sub

Crea los trabajos

Console

Crea el trabajo de inicio.

  1. Ve a la página de Cloud Scheduler en la consola de Google Cloud.
    Ir a la página Cloud Scheduler
  2. Haz clic en Crea un trabajo.
  3. Deja la región predeterminada.
  4. Configura el campo Nombre como startup-dev-instances.
  5. En Frecuencia, ingresa 0 9 * * 1-5.
    • Se ejecutará a las 9:00 a.m. todos los días, de lunes a viernes.
  6. En Zona horaria, selecciona el país y la zona horaria que desees. En este ejemplo, se usarán United States y Los Angeles.
  7. Haz clic en Continuar.
  8. En Tipo de destino, selecciona Pub/Sub.
  9. Selecciona start-instance-event en el menú desplegable de temas.
  10. En Mensaje, ingresa lo siguiente:
    {"zone":"us-west1-b","label":"env=dev"}
    
  11. Haz clic en Crear.

Crea el trabajo de detención.

  1. Deberías estar en la página de Cloud Scheduler en la consola de Google Cloud.
  2. Haz clic en Crear trabajo.
  3. Deja la región predeterminada y haz clic en Siguiente en la parte inferior de la página.
  4. Establece el campo Nombre como shutdown-dev-instances.
  5. En Frecuencia, ingresa 0 17 * * 1-5.
    • Se ejecutará a las 17:00 p.m. todos los días, de lunes a viernes.
  6. En Zona horaria, selecciona el país y la zona horaria que desees. En este ejemplo, se usarán United States y Los Angeles.
  7. Haz clic en Continuar.
  8. En Tipo de destino, selecciona Pub/Sub.
  9. Selecciona stop-instance-event en el menú desplegable de temas.
  10. En Mensaje, ingresa lo siguiente:
    {"zone":"us-west1-b","label":"env=dev"}
    
  11. Haz clic en Crear.

gcloud

Crea el trabajo de inicio.

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

Crea el trabajo de detención.

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: Verifica que los trabajos funcionen

Console

Detener la instancia

  1. Ve a la página de Cloud Scheduler en la consola de Google Cloud.
    Ir a la página Cloud Scheduler
  2. En el trabajo denominado shutdown-dev-instances, haz clic en el botón Ejecutar ahora en el extremo derecho de la página.
  3. Ve a la página Instancias de VM en la consola de Google Cloud.
    Ir a la página Instancias de VM
  4. Verifica que la instancia denominada dev-instance tenga un cuadrado gris junto a su nombre, lo que indica que que se detuvo. Puede tomar hasta 30 segundos para que termine de desactivarse.

Inicia la instancia

  1. Ve a la página de Cloud Scheduler en la consola de Google Cloud.
    Ir a la página Cloud Scheduler
  2. En el trabajo denominado startup-dev-instances, haz clic en el botón Ejecutar ahora en el extremo derecho de la página.
  3. Ve a la página Instancias de VM en la consola de Google Cloud.
    Ir a la página Instancias de VM
  4. Verifica que la instancia denominada dev-instance tenga una marca de verificación verde junto a su nombre, lo que indica que se está ejecutando. Puede tardar hasta 30 segundos para que termine de iniciarse.

gcloud

Detén la instancia

  1. Ejecuta el trabajo de Scheduler para detener la instancia.

    gcloud beta scheduler jobs run shutdown-dev-instances
    
  2. Comprueba que la instancia tenga el estado TERMINATED. Puede tardar hasta 30 segundos en terminar de apagarse.

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

Inicia la instancia

  1. Ejecuta el trabajo de Scheduler para iniciar la instancia.

    gcloud beta scheduler jobs run startup-dev-instances
    
  2. Comprueba que la instancia tenga el estado RUNNING. Puede tardar hasta 30 segundos en terminar de iniciarse.

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

Limpia

Una vez que completes el instructivo, puedes limpiar los recursos que creaste para que dejen de usar la cuota y generar cargos. En las siguientes secciones, se describe cómo borrar o desactivar estos recursos.

Borra los trabajos de Cloud Scheduler

  1. Ve a la página de Cloud Scheduler en la consola de Google Cloud.

    Ir a la página de Cloud Scheduler

  2. Haz clic en las casillas de verificación junto a tus trabajos.

  3. Haz clic en el botón Borrar en la parte superior de la página y confirma la eliminación.

Eliminar los temas de /Sub

  1. Ve a la página de Pub/Sub en la consola de Google Cloud.

    Ir a la página de Pub/Sub

  2. Haz clic en las casillas de verificación junto a tus temas.

  3. Haz clic en Borrar en la parte superior de la página y confirma la eliminación.

Borra las funciones implementadas a través de Cloud Functions

  1. Ve a la página de Cloud Functions en la consola de Google Cloud.

    Ir a la página de Cloud Functions

  2. Haz clic en las casillas de verificación junto a tus funciones.

  3. Haz clic en el botón Borrar en la parte superior de la página y confirma la eliminación.

Borra la instancia de Compute Engine

Para borrar una instancia de Compute Engine:

  1. En la consola de Google Cloud, ve a la página Instancias de VM.

    Ir a Instancias de VM

  2. Selecciona tu instancia en la casilla de verificación de es la instancia que deseas borrar.
  3. Para borrar la instancia, haz clic en Más acciones, haz clic en Borrar y, luego, sigue las instrucciones.

Borra el proyecto

La manera más fácil de eliminar la facturación es borrar el proyecto que creaste para el instructivo.

Para borrar el proyecto, haz lo siguiente:

  1. En la consola de Google Cloud, ve a la página Administrar recursos.

    Ir a Administrar recursos

  2. En la lista de proyectos, elige el proyecto que quieres borrar y haz clic en Borrar.
  3. En el diálogo, escribe el ID del proyecto y, luego, haz clic en Cerrar para borrar el proyecto.

¿Qué sigue?

  • Explora arquitecturas de referencia, diagramas y prácticas recomendadas sobre Google Cloud. Consulta nuestro Cloud Architecture Center.