Automatiza las optimizaciones de costos con Cloud Functions, Cloud Scheduler y Cloud Monitoring


En este documento, se muestra cómo usar Cloud Functions para identificar y limpiar recursos de la nube desperdiciados, programar funciones en se ejecutan con Cloud Scheduler y políticas de alertas de Cloud Monitoring para ejecutarlas según el uso observado. Este documento está dirigido a desarrolladores, SRE, arquitectura de nube y administradores de infraestructura de nube que buscan un enfoque automatizado y sistematizado para identificar y reducir el gasto innecesario en la nube.

En este documento, se supone que estás familiarizado con los siguientes productos:

Objetivos

  • Borra las direcciones IP sin usar: en Google Cloud, las direcciones IP estáticas son un recurso gratuito cuando se conectan a una instancia de máquina virtual o balanceador de cargas. Cuando se reserva una dirección IP estática, pero no se utiliza, se acumula un cargo por hora. En las aplicaciones que dependen en gran medida de direcciones IP estáticas y de aprovisionamiento dinámico a gran escala, ese desperdicio puede aumentar de forma significativa con el paso del tiempo.
  • Borra discos persistentes huérfanos o sin usar: los discos persistentes no se usan o quedan huérfanos si se crean sin estar conectados a una VM, o si una máquina tiene varios discos y uno o más discos desconectados.
  • Migra a clases de almacenamiento menos costosas: Google Cloud ofrece varias clases de almacenamiento de objetos. Usa la clase que mejor se adapte a tus necesidades.

Arquitectura

En el siguiente diagrama, se describe la primera parte de la implementación, en la que programas una función de Cloud Functions para identificar y limpiar las direcciones IP que no se usan.

Arquitectura de una función de Cloud Functions que identifica y limpia las direcciones IP sin usar

En el primer ejemplo, se tratan estos objetivos:

  • Crear una VM de Compute Engine con una dirección IP externa estática y una dirección IP externa estática separada sin usar
  • Implementar una función de Cloud Functions para identificar direcciones sin usar
  • Crear un trabajo de Cloud Scheduler para programar la función a fin de que se ejecute con un activador HTTP

En el siguiente diagrama, programa una función de Cloud Functions para identificar y limpiar discos persistentes sin conexión y huérfanos.

Arquitectura de una función de Cloud Functions que identifica y borra los discos persistentes no utilizados

En el segundo ejemplo, se tratan estos objetivos:

  • Crear una VM de Compute Engine con dos discos persistentes y un disco persistente separado sin conectar. Uno de los discos es huérfano por estar desconectado de la VM
  • Implementar una función de Cloud Functions para identificar discos persistentes sin conexión y huérfanos
  • Crear un trabajo de Cloud Scheduler para programar la ejecución de la función de Cloud Functions con un activador HTTP

Según el siguiente diagrama, activará una Cloud Function para migrar un bucket de almacenamiento desde una política de alertas de Monitoring hacia una clase de almacenamiento menos costosa.

Arquitectura de una función de Cloud Functions que migra un bucket de almacenamiento

En el tercer ejemplo, se tratan estos objetivos:

  • Crear dos bucket s de almacenamiento, agregar un archivo al bucket de entrega y generar tráfico en él
  • Crear un panel de Monitoring para visualizar el uso del bucket
  • Implementar una función de Cloud Functions para migrar el bucket inactivo a una clase de almacenamiento menos costosa
  • Activar la función mediante una carga útil destinada a simular una notificación recibida de una política de alertas de Monitoring

Costos

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

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.

Cuando finalices las tareas que se describen en este documento, puedes borrar los recursos que creaste para evitar que continúe la facturación. Para obtener más información, consulta Cómo realizar una limpieza.

Antes de comenzar

  1. Accede a tu cuenta de Google Cloud. Si eres nuevo en Google Cloud, crea una cuenta para evaluar el rendimiento de nuestros productos en situaciones reales. Los clientes nuevos también obtienen $300 en créditos gratuitos para ejecutar, probar y, además, implementar cargas de trabajo.
  2. En la página del selector de proyectos de la consola de Google Cloud, selecciona o crea un proyecto de Google Cloud.

    Ir al selector de proyectos

  3. Asegúrate de que la facturación esté habilitada para tu proyecto de Google Cloud.

  4. Habilita las API de Compute Engine, Cloud Functions, and Cloud Storage.

    Habilita las API

  5. En la página del selector de proyectos de la consola de Google Cloud, selecciona o crea un proyecto de Google Cloud.

    Ir al selector de proyectos

  6. Asegúrate de que la facturación esté habilitada para tu proyecto de Google Cloud.

  7. Habilita las API de Compute Engine, Cloud Functions, and Cloud Storage.

    Habilita las API

  8. En la consola de Google Cloud, activa Cloud Shell.

    Activar Cloud Shell

    En la parte inferior de la consola de Google Cloud, se inicia una sesión de Cloud Shell en la que se muestra una ventana de línea de comandos. Cloud Shell es un entorno de shell con Google Cloud CLI ya instalada y con valores ya establecidos para el proyecto actual. La sesión puede tardar unos segundos en inicializarse.

  9. Ejecuta todos los comandos de este documento desde Cloud Shell.

Configura tu entorno

En esta sección, configurarás la infraestructura y las identidades que se requieren para esta arquitectura.

  1. En Cloud Shell, clona el repositorio y cambia al directorio gcf-automated-resource-cleanup:

    git clone https://github.com/GoogleCloudPlatform/gcf-automated-resource-cleanup.git && cd gcf-automated-resource-cleanup/
    
  2. Configura las variables de entorno y haz que la carpeta del repositorio sea tu carpeta $WORKDIR, en la que ejecutas todos los comandos:

    export PROJECT_ID=$(gcloud config list \
        --format 'value(core.project)' 2>/dev/null)
        WORKDIR=$(pwd)
    
  3. Instala Apache Bench, una herramienta de generación de carga de código abierto:

    sudo apt-get install apache2-utils
    

Limpia direcciones IP sin usar

En esta sección, completarás los siguientes pasos:

  • Crear dos direcciones IP estáticas
  • Crear una VM que use una dirección IP estática
  • Revisar el código de Cloud Functions
  • Implementará la Cloud Function.
  • Usar trabajos de Cloud Scheduler para probar la función de Cloud Functions

Crea direcciones IP

  1. En Cloud Shell, cambia al directorio de unused-ip:

    cd $WORKDIR/unused-ip
    
  2. Exporta los nombres de las direcciones IP como variables:

    export USED_IP=used-ip-address
    export UNUSED_IP=unused-ip-address
    
  3. Cree dos direcciones IP estáticas:

    gcloud compute addresses create $USED_IP \
        --project=$PROJECT_ID --region=us-central1
    gcloud compute addresses create $UNUSED_IP \
        --project=$PROJECT_ID --region=us-central1
    

    En este ejemplo, se usa la región us-central1, pero puedes elegir una región diferente y hacer referencia a ella de manera coherente durante el resto de este documento.

  4. Controle que se hayan creado las dos direcciones:

    gcloud compute addresses list --filter="region:(us-central1)"
    

    En el resultado, el estado de RESERVED significa que las direcciones IP no están en uso:

    NAME               ADDRESS/RANGE  TYPE      REGION       SUBNET  STATUS
    unused-ip-address  35.232.144.85  EXTERNAL  us-central1          RESERVED
    used-ip-address    104.197.56.87  EXTERNAL  us-central1          RESERVED
    
  5. Configura la dirección IP utilizada como una variable de entorno:

    export USED_IP_ADDRESS=$(gcloud compute addresses describe $USED_IP \
        --region=us-central1 --format=json | jq -r '.address')
    

Crea una VM

  1. En Cloud Shell, crea una instancia:

    gcloud compute instances create static-ip-instance \
        --zone=us-central1-a \
        --machine-type=n1-standard-1 \
        --subnet=default \
        --address=$USED_IP_ADDRESS
    
  2. Confirma que una de las direcciones IP esté en uso:

    gcloud compute addresses list --filter="region:(us-central1)"
    

    El resultado es similar a este:

    NAME               ADDRESS/RANGE  TYPE      REGION       SUBNET  STATUS
    unused-ip-address  35.232.144.85  EXTERNAL  us-central1          RESERVED
    used-ip-address    104.197.56.87  EXTERNAL  us-central1          IN_USE
    

Revisa el código de Cloud Functions

  • En Cloud Shell, obtén el resultado de la sección principal del código:

    cat $WORKDIR/unused-ip/function.js | grep "const compute" -A 31
    

    El resultado es el siguiente:

    const compute = new Compute();
    compute.getAddresses(function(err, addresses){ // gets all addresses across regions
         if(err){
             console.log("there was an error: " + err);
         }
         if (addresses == null) {
             console.log("no addresses found");
             return;
         }
         console.log("there are " + addresses.length + " addresses");
    
         // iterate through addresses
         for (let item of addresses){
    
              // get metadata for each address
              item.getMetadata(function(err, metadata, apiResponse) {
    
                  // if the address is not used AND if it's at least ageToDelete days old:
                  if ((metadata.status=='RESERVED') & (calculateAge(metadata.creationTimestamp) >= ageToDelete)){
                      // delete address
                      item.delete(function(err, operation, apiResponse2){
                          if (err) {
                              console.log("could not delete address: " + err);
                          }
                      })
                  }
              })
          }
           // return number of addresses evaluated
          res.send("there are " + addresses.length + " total addresses");
      });
    }
    

    En la muestra de código anterior, presta atención a lo siguiente:

    • compute.getAddresses(function(err, addresses){ // gets all addresses across regions
      

      Usa el método getAddresses para recuperar direcciones IP en todas las regiones del proyecto.

    • // get metadata for each address
      item.getMetadata(function(err, metadata, apiResponse) {
         // if the address is not used:
             if (metadata.status=='RESERVED'){
      

      Obtiene los metadatos de cada dirección IP y verifica su campo STATUS.

    • if ((metadata.status=='RESERVED') &
      (calculateAge(metadata.creationTimestamp) >= ageToDelete)){
      

      Comprueba si la dirección IP está en uso, calcula su antigüedad mediante una función auxiliar y compara su antigüedad con una constante (establecida en 0 para los fines del ejemplo).

    • // delete address
      item.delete(function(err, operation, apiResponse2){
      

      Borra la dirección IP.

Implementa la función de Cloud Functions

  1. En Cloud Shell, implementa la función de Cloud Functions:

    gcloud functions deploy unused_ip_function --trigger-http --runtime=nodejs8
    
  2. Configura la URL activadora como una variable de entorno:

    export FUNCTION_URL=$(gcloud functions describe unused_ip_function \
        --format=json | jq -r '.httpsTrigger.url')
    

Programa y prueba la función de Cloud Functions

  1. En Cloud Shell, crea una tarea de Cloud Scheduler para ejecutar la función de Cloud Functions a las 2:00 a.m. todos los días:

    gcloud scheduler jobs create http unused-ip-job \
        --schedule="* 2 * * *" \
        --uri=$FUNCTION_URL
    
  2. Activa manualmente el trabajo para probarlo:

    gcloud scheduler jobs run unused-ip-job
    
  3. Controla que se haya borrado la dirección IP sin uso:

    gcloud compute addresses list --filter="region:(us-central1)"
    

    El resultado es similar a este:

    NAME             ADDRESS/RANGE  TYPE      REGION       SUBNET  STATUS
    used-ip-address  104.197.56.87  EXTERNAL  us-central1          IN_USE
    

Limpia discos persistentes sin usar y huérfanos

En esta sección, completarás los siguientes pasos:

  • Crear dos discos persistentes
  • Crearás una VM que use uno de los discos.
  • Desconectarás el disco de la VM.
  • Revisarás el código de la Cloud Function.
  • Implementarás la Cloud Function.
  • Usar trabajos de Cloud Scheduler para probar la función de Cloud Functions

Crea discos persistentes

  1. En Cloud Shell, cambia al directorio de unattached-pd:

    cd $WORKDIR/unattached-pd
    
  2. Exporta los nombres de los discos como variables de entorno:

    export ORPHANED_DISK=orphaned-disk
    export UNUSED_DISK=unused-disk
    
  3. Crea los dos discos:

    gcloud beta compute disks create $ORPHANED_DISK \
       --project=$PROJECT_ID \
       --type=pd-standard \
       --size=500GB \
       --zone=us-central1-a
    gcloud beta compute disks create $UNUSED_DISK \
        --project=$PROJECT_ID \
        --type=pd-standard \
        --size=500GB \
        --zone=us-central1-a
    
  4. Confirma que se crearon los dos discos:

    gcloud compute disks list
    

    El resultado es el siguiente:

    NAME                LOCATION       LOCATION_SCOPE SIZE_GB TYPE         STATUS
    orphaned-disk       us-central1-a  zone           500     pd-standard  READY
    static-ip-instance  us-central1-a  zone           10      pd-standard  READY
    unused-disk         us-central1-a  zone           500     pd-standard  READY
    

Crea una VM e inspecciona los discos

  1. En Cloud Shell, crea la instancia:

    gcloud compute instances create disk-instance \
        --zone=us-central1-a \
        --machine-type=n1-standard-1 \
        --disk=name=$ORPHANED_DISK,device-name=$ORPHANED_DISK,mode=rw,boot=no
    
  2. Inspecciona el disco que se vinculó a la VM:

    gcloud compute disks describe $ORPHANED_DISK \
        --zone=us-central1-a \
        --format=json | jq
    

    El resultado es similar a este:

    {
      "creationTimestamp": "2019-06-12T12:21:25.546-07:00",
      "id": "7617542552306904666",
      "kind": "compute#disk",
      "labelFingerprint": "42WmSpB8rSM=",
      "lastAttachTimestamp": "2019-06-12T12:24:53.989-07:00",
      "name": "orphaned-disk",
      "physicalBlockSizeBytes": "4096",
      "selfLink": "https://www.googleapis.com/compute/v1/projects/automating-cost-optimization/zones/us-central1-a/disks/orphaned-disk",
      "sizeGb": "500",
      "status": "READY",
      "type": "https://www.googleapis.com/compute/v1/projects/automating-cost-optimization/zones/us-central1-a/diskTypes/pd-standard",
      "users": [
        "https://www.googleapis.com/compute/v1/projects/automating-cost-optimization/zones/us-central1-a/instances/disk-instance"
      ],
      "zone": "https://www.googleapis.com/compute/v1/projects/automating-cost-optimization/zones/us-central1-a"
    }
    

    En la muestra de código anterior, presta atención a lo siguiente:

    • users identifica la VM a la que está conectado el disco.
    • lastAttachTimestamp identifica cuándo se conectó el disco por última vez a una VM.
  3. Inspecciona el disco que no se conectó a una VM:

    gcloud compute disks describe $UNUSED_DISK \
        --zone=us-central1-a \
        --format=json | jq
    

    El resultado es similar a este:

    {
      "creationTimestamp": "2019-06-12T12:21:30.905-07:00",
      "id": "1313096191791918677",
      "kind": "compute#disk",
      "labelFingerprint": "42WmSpB8rSM=",
      "name": "unused-disk",
      "physicalBlockSizeBytes": "4096",
      "selfLink": "https://www.googleapis.com/compute/v1/projects/automating-cost-optimization/zones/us-central1-a/disks/unused-disk",
      "sizeGb": "500",
      "status": "READY",
      "type": "https://www.googleapis.com/compute/v1/projects/automating-cost-optimization/zones/us-central1-a/diskTypes/pd-standard",
      "zone": "https://www.googleapis.com/compute/v1/projects/automating-cost-optimization/zones/us-central1-a"
    }
    

    Estos son los aspectos importantes de la muestra de código anterior:

    • El disco no tiene users en la lista porque no una VM no lo usa actualmente.
    • El disco no tiene lastAttachedTimestamp porque nunca se usó.
  4. Desconecta el disco persistente huérfano de la VM:

    gcloud compute instances detach-disk disk-instance \
        --device-name=$ORPHANED_DISK \
        --zone=us-central1-a
    
  5. Inspecciona el disco huérfano:

    gcloud compute disks describe $ORPHANED_DISK \
        --zone=us-central1-a \
        --format=json | jq
    

    El resultado es similar a este:

    {
      "creationTimestamp": "2019-06-12T12:21:25.546-07:00",
      "id": "7617542552306904666",
      "kind": "compute#disk",
      "labelFingerprint": "42WmSpB8rSM=",
      "lastAttachTimestamp": "2019-06-12T12:24:53.989-07:00",
      "lastDetachTimestamp": "2019-06-12T12:34:56.040-07:00",
      "name": "orphaned-disk",
      "physicalBlockSizeBytes": "4096",
      "selfLink": "https://www.googleapis.com/compute/v1/projects/automating-cost-optimization/zones/us-central1-a/disks/orphaned-disk",
      "sizeGb": "500",
      "status": "READY",
      "type": "https://www.googleapis.com/compute/v1/projects/automating-cost-optimization/zones/us-central1-a/diskTypes/pd-standard",
      "zone": "https://www.googleapis.com/compute/v1/projects/automating-cost-optimization/zones/us-central1-a"
    }
    

    Estos son los aspectos importantes de la muestra de código anterior:

    • El disco no tiene users en la lista, lo que indica que no está en uso actualmente.
    • Ahora hay una entrada lastDetachTimestamp que indica cuándo se desconectó el disco por última vez de una VM y, por lo tanto, cuándo se usó por última vez.
    • El campo lastAttachTimestamp todavía está presente.

Revisa el código de Cloud Functions

  1. En Cloud Shell, obtén el resultado de la sección del código que recupera todos los discos persistentes del proyecto:

    cat $WORKDIR/unattached-pd/main.py | grep "(request)" -A 12
    

    El resultado es el siguiente:

    def delete_unattached_pds(request):
        # get list of disks and iterate through it:
        disksRequest = compute.disks().aggregatedList(project=project)
        while disksRequest is not None:
            diskResponse = disksRequest.execute()
            for name, disks_scoped_list in diskResponse['items'].items():
                if disks_scoped_list.get('warning') is None:
                    # got disks
                    for disk in disks_scoped_list['disks']: # iterate through disks
                        diskName = disk['name']
                        diskZone = str((disk['zone'])).rsplit('/',1)[1]
                        print (diskName)
                        print (diskZone)
    

    La función usa el método aggregatedList para obtener todos los discos persistentes en el proyecto de Google Cloud en el que se ejecuta y repite cada uno de ellos.

  2. Obtén el resultado de la sección del código que verifica el campo lastAttachTimestamp y borra el disco si no existe:

    cat $WORKDIR/unattached-pd/main.py | grep "handle never" -A 11
    

    El resultado es el siguiente:

    # handle never attached disk - delete it
    # lastAttachedTimestamp is not present
    if disk.get("lastAttachTimestamp") is None:
           print ("disk " + diskName + " was never attached - deleting")
           deleteRequest = compute.disks().delete(project=project,
                  zone=diskZone,
                  disk=diskName)
           deleteResponse = deleteRequest.execute()
           waitForZoneOperation(deleteResponse, project, diskZone)
           print ("disk " + diskName + " was deleted")
           Continue
    

    Esta sección borra el disco si lastAttachTimestamp no está presente, lo que significa que este disco nunca estuvo en uso.

  3. Obtén el resultado de la sección del código que calcula la antigüedad del disco si es huérfano, crea una instantánea y lo borra:

    cat $WORKDIR/unattached-pd/main.py | grep "handle detached" -A 32
    

    El resultado es el siguiente:

    # handle detached disk - snapshot and delete
    # lastAttachTimestamp is present AND users is not present AND it meets the age criterium
    if disk.get("users") is None \
        and disk.get("lastDetachTimestamp") is not None \
        and diskAge(disk['lastDetachTimestamp'])>=deleteAge:
    
        print ("disk " + diskName + " has no users and has been detached")
        print ("disk meets age criteria for deletion")
    
        # take a snapshot
        snapShotName = diskName + str(int(time.time()))
        print ("taking snapshot: " + snapShotName)
        snapshotBody = {
            "name": snapShotName
        }
        snapshotRequest = compute.disks().createSnapshot(project=project,
             zone=diskZone,
             disk=diskName,
             body=snapshotBody)
        snapshotResponse = snapshotRequest.execute()
        waitForZoneOperation(snapshotResponse, project, diskZone)
        print ("snapshot completed")
    
        # delete the disk
        print ("deleting disk " + diskName)
        deleteRequest = compute.disks().delete(project=project,
            zone=diskZone,
            disk=diskName)
        deleteResponse = deleteRequest.execute()
        waitForZoneOperation(deleteResponse, project, diskZone)
        print ("disk " + diskName + " was deleted")
        continue
    

    Esta sección de código se usa cuando el disco tiene users y lastDetachTimestamp está presente, lo que significa que el disco no está en uso actualmente, pero se usó en algún momento. En este caso, la función de Cloud Functions crea una instantánea del disco para retener los datos y, luego, lo borra.

Implementa la función de Cloud Functions

  1. En Cloud Shell, implemente la Cloud Function:

    gcloud functions deploy delete_unattached_pds \
        --trigger-http --runtime=python37
    
  2. Establece la URL activadora de la función de Cloud Functions como una variable de entorno:

    export FUNCTION_URL=$(gcloud functions describe delete_unattached_pds \
        --format=json | jq -r '.httpsTrigger.url')
    

Programa y prueba la función de Cloud Functions

  1. En Cloud Shell, crea una tarea de Cloud Scheduler para ejecutar la función de Cloud Functions a las 2:00 a.m. todos los días:

    gcloud scheduler jobs create http unattached-pd-job \
        --schedule="* 2 * * *" \
        --uri=$FUNCTION_URL
    
  2. Prueba el trabajo:

    gcloud scheduler jobs run unattached-pd-job
    
  3. Controla que se haya creado la instantánea del disco huérfano:

    gcloud compute snapshots list
    

    El resultado es similar a este:

    NAME                     DISK_SIZE_GB  SRC_DISK                           STATUS
    orphaned-disk1560455894  500           us-central1-a/disks/orphaned-disk  READY
    
  4. Controla que se hayan borrado el disco sin uso y el huérfano:

    gcloud compute disks list
    

    El resultado es el siguiente:

    NAME                LOCATION       LOCATION_SCOPE SIZE_GB  TYPE         STATUS
    disk-instance       us-central1-a  zone           10       pd-standard  READY
    static-ip-instance  us-central1-a  zone           10       pd-standard  READY
    

Migra depósitos de almacenamiento a clases de almacenamiento menos costosas

Google Cloud proporciona reglas de ciclo de vida de objetos de almacenamiento que puedes usar para mover objetos automáticamente a diferentes clases de almacenamiento según un conjunto de atributos, como su fecha de creación o estado. Sin embargo, estas reglas no identifican si se accedió a los objetos. A veces, es posible que quieras mover objetos más nuevos a Nearline Storage si no se accedió a ellos durante un tiempo determinado.

En esta sección, completarás los siguientes pasos:

  • Crear dos depósitos de Cloud Storage
  • Agregar un objeto a uno de los depósitos
  • Configurar Monitoring para observar el acceso a los objetos del bucket
  • Revisar el código de Cloud Functions que migra los objetos de un bucket de Regional Storage a un bucket de Nearline Storage
  • Implementará la Cloud Function.
  • Probar la función de Cloud Functions con una alerta de Monitoring

Crea depósitos de Cloud Storage y agrega un archivo

  1. En Cloud Shell, cambia al directorio de migrate-storage:

    cd $WORKDIR/migrate-storage
    
  2. Crea el depósito de Cloud Storage serving-bucket que se usará más adelante para cambiar las clases de almacenamiento:

    export PROJECT_ID=$(gcloud config list \
        --format 'value(core.project)' 2>/dev/null)
    gsutil mb -c regional -l us-central1 gs://${PROJECT_ID}-serving-bucket
    
  3. Configura el bucket como público:

    gsutil acl ch -u allUsers:R gs://${PROJECT_ID}-serving-bucket
    
  4. Agrega un archivo de texto al bucket:

    gsutil cp $WORKDIR/migrate-storage/testfile.txt  \
        gs://${PROJECT_ID}-serving-bucket
    
  5. Configura el archivo como público:

    gsutil acl ch -u allUsers:R gs://${PROJECT_ID}-serving-bucket/testfile.txt
    
  6. Confirma que puedas acceder al archivo:

    curl http://storage.googleapis.com/${PROJECT_ID}-serving-bucket/testfile.txt
    

    El resultado es el siguiente:

    this is a test
    
  7. Crea un segundo depósito llamado idle-bucket que no entregue ningún dato:

    gsutil mb -c regional -l us-central1 gs://${PROJECT_ID}-idle-bucket
    

Configura un lugar de trabajo de Cloud Monitoring

En esta sección, configurarás Cloud Monitoring para observar el uso del bucket a fin de comprender cuándo no se usan los objetos del bucket. Cuando no se usa el bucket de entrega, una función de Cloud Functions migra el bucket de la clase Regional Storage a la clase Nearline Storage.

  1. En la consola de Google Cloud, ve a Monitoring.

    Ir a Cloud Monitoring

  2. Haz clic en Nuevo espacio de trabajo y, luego, en Agregar.

    Espera a que se complete la configuración inicial.

Crea un panel de Cloud Monitoring

  1. En Monitoring, ve a Paneles y, luego, haz clic en Crear panel.

  2. Haz clic en Agregar gráfico.

  3. En el campo Nombre, ingresa Bucket Access.

  4. Para encontrar la métrica de contenido de la solicitud del depósito de Cloud Storage, en el campo Find resource and metric, ingresa request y, luego, selecciona la métrica Recuento de solicitudes para el recurso gcs_bucket.

  5. Para agrupar las métricas por nombre de depósito, en la lista desplegable Agrupar por, haz clic en bucket_name.

  6. Para filtrar por el nombre del método, en el campo Filtro, ingresa ReadObject y haz clic en Aplicar.

  7. Haz clic en Guardar.

  8. En el campo Nombre, ingresa Bucket Usage.

  9. Para confirmar que se puede acceder al panel, mantén el puntero sobre Paneles y verifica que aparezca Bucket Usage.

    Configuraste Monitoring para observar el acceso a objetos en tus depósitos. El gráfico no muestra ningún dato porque los depósitos de Cloud Storage no están recibiendo tráfico.

Genera carga en el bucket de entrega

Ahora que ya configuraste la supervisión, usa Apache Bench para enviar tráfico al bucket de entrega.

  1. En Cloud Shell, envíe solicitudes al objeto en el bucket de entrega:

    ab -n 10000 \
        http://storage.googleapis.com/$PROJECT_ID-serving-bucket/testfile.txt
    
  2. En la consola de Google Cloud, ve a Monitoring.

    Ir a Cloud Monitoring

  3. Para seleccionar el panel Bucket Usage, mantén el puntero sobre Paneles y selecciona Bucket Usage. Confirma que el tráfico se envíe solo al bucket de entrega. La serie temporal request_count metric solo se muestra para el depósito de entrega, ya que el depósito inactivo no tiene tráfico.

Revisa e implementa la función de Cloud Functions

  1. En Cloud Shell, obtén el resultado del código que usa la función de Cloud Functions para migrar un bucket de almacenamiento a la clase de Nearline Storage:

    cat $WORKDIR/migrate-storage/main.py | grep "migrate_storage(" -A 15
    

    El resultado es el siguiente:

    def migrate_storage(request):
        # process incoming request to get the bucket to be migrated:
        request_json = request.get_json(force=True)
        # bucket names are globally unique
        bucket_name = request_json['incident']['resource_name']
    
        # create storage client
        storage_client = storage.Client()
    
        # get bucket
        bucket = storage_client.get_bucket(bucket_name)
    
        # update storage class
        bucket.storage_class = "NEARLINE"
        bucket.patch()
    

    La función de Cloud Functions usa el nombre del bucket que se pasa en la solicitud para cambiar su clase de almacenamiento a Nearline Storage.

  2. Implemente la Cloud Function:

    gcloud functions deploy migrate_storage --trigger-http --runtime=python37
    
  3. Configura la URL del activador como una variable de entorno que usarás en la siguiente sección:

    export FUNCTION_URL=$(gcloud functions describe migrate_storage \
        --format=json | jq -r '.httpsTrigger.url')
    

Prueba y valida la automatización de alertas

  1. Establezca el nombre del bucket inactivo:

    export IDLE_BUCKET_NAME=$PROJECT_ID-idle-bucket
    
  2. Envía una notificación de prueba a la función de Cloud Functions que implementaste con el archivo incident.json:

    envsubst < $WORKDIR/migrate-storage/incident.json | curl -X POST \
        -H "Content-Type: application/json" $FUNCTION_URL -d @-
    

    El resultado es el siguiente:

    OK
    

    El resultado no termina con un salto de línea y, por lo tanto, lo sigue de inmediato el símbolo del sistema.

  3. Confirma que el bucket inactivo se migró a Nearline Storage:

    gsutil defstorageclass get gs://$PROJECT_ID-idle-bucket
    

    El resultado es el siguiente:

    gs://automating-cost-optimization-idle-bucket: NEARLINE
    

Consideraciones para un entorno de producción

Cuando automatices las optimizaciones de costos en tu propio entorno de Google Cloud, ten en cuenta estos puntos:

  • Consideraciones generales: Debes aumentar la seguridad de las funciones de Cloud Functions que tienen el poder de modificar o borrar recursos de Google Cloud.
  • Identificación de desperdicios: En este documento, se explican algunos gastos innecesarios. Existen muchos otros ejemplos que generalmente se clasifican en una de tres categorías:
    • Recursos aprovisionados en exceso: recursos aprovisionados a fin de que sean más grandes de lo necesario para una carga de trabajo determinada, como VM con más capacidad de CPU y memoria de las necesarias
    • Recursos inactivos: recursos que no se usan por completo
    • Recursos inactivos a tiempo parcial: recursos que solo se usan durante el horario de atención
  • Automatización de limpieza: en este documento, se necesitó un proceso de varios pasos con varias operaciones asíncronas para generar una instantánea y borrar el disco. Otros recursos de Google Cloud, como las direcciones IP sin usar, pueden usar operaciones síncronas.
  • Implementación a gran escala: en este documento, el ID del proyecto de Google Cloud se define en el código de Cloud Functions. Para implementar una solución de este tipo a gran escala, considera usar las APIs de Facturación de Cloud o Cloud Resource Manager para obtener la lista de proyectos con una cuenta de facturación o una organización. Luego, pasa esos IDs del proyecto de Google Cloud como variables a una función. En esta configuración, debes agregar la cuenta de servicio de Cloud Functions a los proyectos en los que puede limpiar o borrar recursos. Recomendamos usar un framework de implementación automatizado, como Cloud Deployment Manager o Terraform.
  • Automatización de alertas: en este documento, se muestra cómo usar una carga útil simulada de una alerta de Monitoring para activar la migración de la clase de almacenamiento. Las políticas de alertas de Monitoring se pueden evaluar en un máximo de 23 horas y 59 minutos. En un entorno de producción, es posible que esta restricción no sea lo suficientemente larga como para considerar un bucket inactivo antes de migrar su clase de almacenamiento. Considera habilitar los registros de auditoría de acceso a datos en el bucket de Cloud Storage y crear una canalización que consuma estos registros de auditoría para evaluar si se usó un bucket en los últimos 30 días. Para obtener más información, consulta cómo comprender los registros de auditoría y considera crear un receptor agregado para enviar registros a Pub/Sub y una canalización de Dataflow a fin de procesarlos.

Realiza una limpieza

Para evitar que se apliquen cargos a tu cuenta de Google Cloud por los recursos usados en este instructivo, borra el proyecto que contiene los recursos o conserva el proyecto y borra los recursos individuales.

Borra el proyecto

  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?