App de función de eventos de Storage

Arquitectura

La app de función de eventos de almacenamiento es un creador de directorios de imágenes y miniaturas. Consta de los siguientes componentes:

  • Una aplicación cliente en la que los usuarios pueden subir imágenes.
    • API alojada en contenedor y sitio estático: Golang - Cloud Run
    • Almacenamiento - Almacenamiento de archivos - Cloud Storage
  • Un procesador de imágenes que crea miniaturas de las imágenes.
    • Funciones como servicio: Golang y Cloud Functions
  • Una canalización de implementación
    • Deployment: Cloud Build

Comenzar

Haz clic en el siguiente vínculo para obtener una copia del código fuente en Cloud Shell. Una vez allí, un solo comando iniciará una copia de trabajo de la aplicación en tu proyecto.

Abrir en Cloud Shell

Consulta el código fuente en GitHub


Componentes de la app de función de eventos de Storage

La arquitectura de la app de función de eventos de almacenamiento usa varios productos. A continuación, se enumeran los componentes y más información sobre ellos, además de vínculos a videos relacionados, documentación del producto y explicaciones interactivas.
Video Documentos Explicaciones
Cloud Run Cloud Run permite ejecutar aplicaciones en un contenedor, pero de forma sin servidores y sin necesidad de configurar la cantidad de instancias, procesadores o memoria. Sube un contenedor y obtén una URL.
Cloud Storage Cloud Storage proporciona almacenamiento de archivos y entrega pública de imágenes a través de http(s).
Cloud Functions Cloud Functions es una plataforma de servicios con funciones que permite detectar las cargas de archivos de Cloud Storage y ejecutar código para crear miniaturas de ellos.
Cloud Build Cloud Build es la herramienta que empaqueta los contenedores y los implementa para que estén disponibles como servicios de Cloud Run.

Secuencias de comandos

La secuencia de comandos de instalación usa un archivo ejecutable escrito en go y las herramientas de la CLI de Terraform para tomar un proyecto vacío y, luego, instalar la aplicación en él. El resultado debe ser una aplicación que funcione y una URL para la dirección IP del balanceo de cargas.

./main.tf

Habilita los servicios

Los servicios de Google Cloud están inhabilitados en un proyecto de forma predeterminada. Para poder usar cualquiera de estas soluciones, tenemos que activar lo siguiente:

  • Cloud Build: Crea imágenes de contenedor y, luego, impleméntalas en Cloud Run.
  • Cloud Storage: Aloja archivos estáticos.
  • Cloud Functions: plataforma de funciones como servicio
  • Cloud Run: Es la herramienta sin servidores que alojará el contenedor y proporcionará una URL desde la cual acceder a la aplicación.
  • Artifact Registry: almacena las imágenes de Docker para usar con Cloud Build.
variable "gcp_service_list" {
    description = "The list of apis necessary for the project"
    type        = list(string)
    default = [
        "cloudbuild.googleapis.com",
        "storage.googleapis.com",
        "cloudfunctions.googleapis.com",
        "run.googleapis.com",
        "artifactregistry.googleapis.com",
    ]
}

resource "google_project_service" "all" {
    for_each                   = toset(var.gcp_service_list)
    project                    = var.project_number
    service                    = each.key
    disable_dependent_services = false
    disable_on_destroy         = false
}

Configurar permisos

Establece roles y permisos de IAM que permiten que Cloud Build implemente todos los servicios.

  • Habilita la cuenta de servicio de Cloud Build para implementar en Cloud Run
  • Habilitar que la cuenta de servicio de Cloud Build realice las actividades de la cuenta de servicio
  • Habilita la cuenta de servicio de Cloud Build para publicar en Cloud Run
  • Habilita la cuenta de servicio de Cloud Build para almacenar contenedores en Artifact Registry
variable "build_roles_list" {
    description = "The list of roles that build needs for"
    type        = list(string)
    default = [
        "roles/run.developer",
        "roles/iam.serviceAccountUser",
        "roles/run.admin",
        "roles/cloudfunctions.admin",
        "roles/artifactregistry.admin",
    ]
}

resource "google_project_iam_member" "allbuild" {
    for_each   = toset(var.build_roles_list)
    project    = var.project_number
    role       = each.key
    member     = "serviceAccount:${local.sabuild}"
    depends_on = [google_project_service.all]
}

Crea buckets de Storage

Crea la ubicación de almacenamiento para las imágenes y miniaturas subidas y proporciona una ubicación de almacenamiento temporal para la carga de Cloud Functions.

resource "google_storage_bucket" "target_bucket" {
    name     = var.bucket
    project  = var.project_number
    location = var.location
}

resource "google_storage_bucket" "function_bucket" {
    name     = "${var.project_id}-function-deployer"
    project  = var.project_number
    location = var.location
}

Crea un repositorio de Artifact Registry

En el siguiente código, se describen los parámetros del repositorio de Artifact Registry en el que se almacenan los contenedores.

resource "google_artifact_registry_repository" "app" {
    provider      = google-beta
    format        = "DOCKER"
    location      = var.region
    project       = var.project_id
    repository_id = "${var.basename}-app"
    depends_on    = [google_project_service.all]
}

Contenedor de compilación para la aplicación de Cloud Run

A continuación, se compila una imagen y se sube a Artifact Registry para usarla con Cloud Build.

resource "null_resource" "cloudbuild_app" {
    provisioner "local-exec" {
        working_dir = "${path.module}/code/app"
        command     = "gcloud builds submit . --substitutions=_REGION=${var.region},_BASENAME=${var.basename}"
    }

    depends_on = [
        google_artifact_registry_repository.app,
        google_project_service.all
    ]
}

Realiza la implementación en Cloud Run

En el siguiente ejemplo, se usa Cloud Build para implementar la app web cliente en Cloud Run.

resource "google_cloud_run_service" "app" {
    name     = "${var.basename}-app"
    location = var.region
    project  = var.project_id

    template {
        spec {
            containers {
                image = "${var.region}-docker.pkg.dev/${var.project_id}/${var.basename}-app/prod"
                env {
                name  = "BUCKET"
                value = var.bucket
                }
            }
        }

        metadata {
            annotations = {
                "autoscaling.knative.dev/maxScale" = "1000"
                "run.googleapis.com/client-name"   = "terraform"
            }
        }
    }
    autogenerate_revision_name = true
    depends_on = [
        null_resource.cloudbuild_app,
    ]
}

data "google_iam_policy" "noauth" {
    binding {
        role = "roles/run.invoker"
        members = [
        "allUsers",
        ]
    }
}

resource "google_cloud_run_service_iam_policy" "noauth_app" {
    location    = google_cloud_run_service.app.location
    project     = google_cloud_run_service.app.project
    service     = google_cloud_run_service.app.name
    policy_data = data.google_iam_policy.noauth.policy_data
}

Implementa el código de la función en Cloud Functions

Envíalo directamente a las funciones y realiza la activación.

resource "google_storage_bucket_object" "archive" {
    name   = "index.zip"
    bucket = google_storage_bucket.function_bucket.name
    source = "index.zip"
    depends_on = [
        google_project_service.all,
        google_storage_bucket.function_bucket,
        null_resource.cloudbuild_function
    ]
}

resource "google_cloudfunctions_function" "function" {
    name    = var.basename
    project = var.project_id
    region  = var.region
    runtime = "go116"

    available_memory_mb   = 128
    source_archive_bucket = google_storage_bucket.function_bucket.name
    source_archive_object = google_storage_bucket_object.archive.name
    entry_point           = "OnFileUpload"
    event_trigger {
        event_type = "google.storage.object.finalize"
        resource   = google_storage_bucket.target_bucket.name
    }

    depends_on = [
        google_storage_bucket.function_bucket,
        google_storage_bucket.target_bucket,
        google_storage_bucket_object.archive,
        google_project_service.all
    ]
}

./code/app/cloudbuild.yaml

Contenedor de API de compilación

A continuación, se crea una imagen de Docker para la aplicación web.

- name: "gcr.io/cloud-builders/docker"
  args: [ "build", "-t", "$_REGION-docker.pkg.dev/$PROJECT_ID/$_BASENAME-app/prod", ".", ]

Envía el contenedor de la API a Artifact Registry

Enviar el contenedor a Artifact Registry permite que Cloud Run obtenga la imagen y la entregue.

- name: "gcr.io/cloud-builders/docker"
  args: ["push", "$_REGION-docker.pkg.dev/$PROJECT_ID/$_BASENAME-app/prod"]

Sustituciones

Crea una variable con un valor predeterminado para que se puedan cambiar en el momento de la implementación.

substitutions:
  _REGION: us-central1
  _BASENAME: scaler

Conclusión

Ahora tienes una solución para crear miniaturas que se ejecuta en tu proyecto con Cloud Functions para responder a los cambios en un bucket de Storage. También tienes todo el código para modificar o extender esta solución.