Ambiente di esecuzione di Cloud Run Functions

Le funzioni Cloud Run vengono eseguite in un ambiente serverless completamente gestito in cui Google gestisce l'infrastruttura, i sistemi operativi e gli ambienti di runtime. Ogni funzione viene eseguita nel proprio contesto di esecuzione sicuro isolato, si scala automaticamente e ha un ciclo di vita indipendente dalle altre funzioni.

Runtime

Le funzioni Cloud Run supportano più runtime di linguaggi. Ognuno contiene un insieme standard di pacchetti di sistema, nonché gli strumenti e le librerie necessari per quel linguaggio. Ti servirà il valore ID runtime se esegui il deployment delle funzioni dalla riga di comando o tramite Terraform.

Gli aggiornamenti di sicurezza e manutenzione vengono resi disponibili per tutti gli ambienti di esecuzione delle funzioni Cloud Run e delle funzioni Cloud Run (1ª generazione.). Questi aggiornamenti vengono applicati automaticamente o manualmente, a seconda dell'ambiente e della relativa configurazione. Per ulteriori informazioni sugli aggiornamenti dell'ambiente di esecuzione, consulta Proteggere la funzione Cloud Run.

Le immagini di runtime sono ospitate in ogni regione in cui è disponibile Artifact Registry. Puoi personalizzare il percorso dell'immagine di runtime sostituendo la prima parte dell'URI con la regione che preferisci:

REGION-docker.pkg.dev/serverless-runtimes/STACK/runtimes/RUNTIME_ID

Sostituisci:

  • REGION con la regione preferita, ad esempio us-central1
  • STACK con lo stack del sistema operativo preferito, ad esempio google-22-full
  • RUNTIME_ID con l'ID runtime utilizzato dalla funzione, ad esempio python310

Ad esempio, l'immagine di base Node.js 20 più recente che utilizza lo stack google-22-full, ospitata in us-central1, verrà richiamata con questo URL: us-central1-docker.pkg.dev/serverless-runtimes/google-22-full/runtimes/nodejs22

Node.js

Runtime Generazione ID runtime Serie Immagine di base del runtime
Node.js 22 Esegui funzioni nodejs22 google-22-full google-22-full/nodejs22
Node.js 20 1ª generazione., Esegui funzioni nodejs20 google-22-full google-22-full/nodejs20
Node.js 18 1ª generazione., Esegui funzioni nodejs18 google-22-full google-22-full/nodejs18
Node.js 16 1ª generazione., Esegui funzioni nodejs16 google-18-full google-18-full/nodjes16
Node.js 14 1ª generazione., Esegui funzioni nodejs14 google-18-full google-18-full/nodjes14
Node.js 12 1ª generazione., Esegui funzioni nodejs12 google-18-full google-18-full/nodjes14
Node.js 10 1ª generazione., Esegui funzioni nodejs10 google-18-full google-18-full/nodjes10
Node.js 8 1ª generazione., Esegui funzioni nodejs8 Dismesso Dismesso
Node.js 6 1ª generazione., Esegui funzioni nodejs6 Dismesso Dismesso

Python

Runtime Generazione ID runtime Serie Immagine di base del runtime
Python 3.12 1ª generazione., Esegui funzioni python312 google-22-full google-22-full/python312
Python 3.11 1ª generazione., Esegui funzioni python311 google-22-full google-22-full/python311
Python 3.10 1ª generazione., Esegui funzioni python310 google-22-full google-22-full/python310
Python 3.9 1ª generazione., Esegui funzioni python39 google-18-full google-18-full/python39
Python 3.8 1ª generazione., Esegui funzioni python38 google-18-full google-18-full/python38
Python 3.7 1ª generazione., Esegui funzioni python37 google-18-full google-18-full/python37

Vai

Runtime Generazione ID runtime Serie Immagine di base del runtime
Go 1.23
(solo anteprima)
Esegui funzioni go123 google-22-full google-22-full/go123
Go 1.22 Esegui funzioni go122 google-22-full google-22-full/go122
Go 1.21 Esegui funzioni go121 google-22-full google-22-full/go121
Go 1.20 Esegui funzioni go120 google-22-full google-22-full/go120
Go 1.19 1ª generazione., Esegui funzioni go119 google-22-full google-22-full/go119
Go 1.18 1ª generazione., Esegui funzioni go118 google-22-full google-22-full/go120
Go 1.16 1ª generazione., Esegui funzioni go116 google-18-full google-18-full/go116
Go 1.13 1ª generazione., Esegui funzioni go113 google-18-full google-18-full/go113
Go 1.11 1ª generazione., Esegui funzioni go111 Dismesso Dismesso

Java

Runtime Generazione ID runtime Serie Immagine di base del runtime
Java 21 Esegui funzioni java21 google-22-full google-22-full/java21
Java 17 1ª generazione., Esegui funzioni java17 google-22-full google-22-full/java17
Java 11 1ª generazione., Esegui funzioni java11 google-18 google-18/java11

Ruby

Runtime Generazione ID runtime Serie Immagine di base del runtime
Ruby 3.3 1ª generazione., Esegui funzioni ruby33 google-22-full google-22-full/ruby33
Ruby 3.2 1ª generazione., Esegui funzioni ruby32 google-22-full google-22-full/ruby32
Ruby 3.0 1ª generazione., Esegui funzioni ruby30 google-18-full google-18-full/ruby30
Ruby 2.7 1ª generazione., Esegui funzioni ruby27 google-18-full google-18-full/ruby27
Ruby 2.6 1ª generazione., Esegui funzioni ruby26 google-18-full google-18-full/ruby26

PHP

Runtime Generazione ID runtime Serie Immagine di base del runtime
PHP 8.3 Esegui funzioni php83 google-22-full google-22-full/php83
PHP 8.2 1ª generazione., Esegui funzioni php82 google-22-full google-22-full/php82
PHP 8.1 1ª generazione., Esegui funzioni php81 google-18-full google-18-full/php81
PHP 7.4 1ª generazione., Esegui funzioni php74 google-18-full google-18-full/php74

.NET Core

Runtime Generazione ID runtime Serie Immagine di base del runtime
.NET Core 8 Esegui funzioni dotnet8 google-22-full google-22-full/dotnet8
.NET Core 6 1ª generazione., Esegui funzioni dotnet6 google-22-full google-22-full/dotnet6
.NET Core 3 1ª generazione., Esegui funzioni dotnet3 google-18-full google-18-full/dotnet3

Comportamento della scalabilità automatica

Le funzioni Cloud Run implementano il paradigma serverless, in cui esegui il codice senza preoccuparti dell'infrastruttura sottostante, come i server o le macchine virtuali. Una volta dipistate, le funzioni vengono gestite e scalate automaticamente.

Le funzioni Cloud Run gestiscono le richieste in entrata assegnandole alle istanze della funzione. A seconda del volume delle richieste e del numero di istanze di funzione esistenti, le funzioni Cloud Run possono assegnare una richiesta a un'istanza esistente o crearne una nuova.

Se il volume delle richieste in entrata supera il numero di istanze esistenti, le funzioni Cloud Run possono avviare più nuove istanze per gestire le richieste. Questo comportamento di scalabilità automatica consente alle funzioni Cloud Run di gestire molte richieste in parallelo, ciascuna utilizzando un'istanza diversa della funzione.

In alcuni casi, la scalabilità illimitata potrebbe non essere auspicabile. Per risolvere il problema, le funzioni Cloud Run ti consentono di configurare un numero massimo di istanze che possono coesistere in un determinato momento per una determinata funzione.

Condizione stateless

Per abilitare la gestione e la scalabilità automatiche delle funzioni, queste devono essere senza stato: un'invocazione di funzione non deve fare affidamento sullo stato in memoria impostato da un'invocazione precedente. Le invocazioni potrebbero essere gestite da diverse istanze di funzione, che non condividono variabili globali, memoria, file system o altro stato.

Se devi condividere lo stato tra le chiamate di funzione, la funzione deve utilizzare un servizio come Memorystore, Datastore, Firestore o Cloud Storage per rendere permanenti i dati. Per ulteriori informazioni sulle opzioni di database e archiviazione fornite da Google Cloud, consulta Database Google Cloud e Prodotti di archiviazione Google Cloud.

Contemporaneità

Funzioni Cloud Run

Le funzioni Cloud Run supportano la gestione di più richieste in parallelo su una singola istanza di funzione. Ciò può essere utile per evitare gli avviamenti a freddo, poiché un'istanza già attivata può elaborare più richieste contemporaneamente, riducendo così la latenza complessiva. Per maggiori dettagli, consulta Contemporaneità.

Funzioni Cloud Run (1ª generazione.)

Nelle funzioni Cloud Run (1ª generazione.), ogni istanza di una funzione gestisce una sola richiesta in parallelo. Ciò significa che, mentre il codice elabora una richiesta, non è possibile che una seconda richiesta venga indirizzata alla stessa istanza. Pertanto, la richiesta originale può utilizzare l'intera quantità di risorse (memoria e CPU) che assegni.

Poiché le richieste concorrenti nelle funzioni Cloud Run (1ª generazione.) vengono elaborate da diverse istanze di funzione, non condividono variabili o memoria locale. Per ulteriori informazioni, consulta Stato incentrato e Vita utile dell'istanza di funzione.

Avvii a freddo

Una nuova istanza di funzione viene avviata in due casi:

  • Quando esegui il deployment della funzione.

  • Quando viene creata automaticamente una nuova istanza di funzione per eseguire il ridimensionamento in base al carico o, occasionalmente, per sostituire un'istanza esistente.

L'avvio di una nuova istanza di funzione comporta il caricamento del runtime e del codice. Le richieste che includono l'avvio delle istanze di funzione, chiamate avvii a freddo, possono essere più lente delle richieste indirizzate a istanze di funzione esistenti. Tuttavia, se la funzione riceve un carico costante, il numero di avvii a freddo è in genere trascurabile, a meno che la funzione non abbia frequenti arresti anomali e richieda il riavvio dell'ambiente della funzione.

Se il codice della funzione genera un'eccezione non rilevata o arresta in modo anomalo il processo corrente, l'istanza della funzione potrebbe essere riavviata. Ciò può comportare un aumento degli avviamenti a freddo, con conseguente aumento della latenza, pertanto consigliamo di intercettare le eccezioni ed evitare la terminazione dell'attuale processo. Consulta Segnalazione di errori per una discussione su come gestire e segnalare gli errori nelle funzioni Cloud Run.

Se la tua funzione è sensibile alla latenza, valuta la possibilità di impostare un numero minimo di istanze per evitare gli avvii a freddo.

Durata dell'istanza di funzione

In genere le istanze di funzione sono resilienti e riutilizzate dalle chiamate di funzione successive, a meno che il numero di istanze non venga ridotto a causa della mancanza di traffico in corso o di arresti anomali della funzione. Ciò significa che al termine dell'esecuzione di una funzione, l'invocazione di un'altra funzione può essere gestita dalla stessa istanza della funzione.

Ambito della funzione e ambito globale

Una singola chiamata di funzione comporta l'esecuzione solo del corpo della funzione dichiarata come punto di ingresso. L'ambito globale del codice sorgente della funzione viene eseguito solo durante gli avvii a freddo e non nelle istanze già inizializzate.

Node.js

const functions = require('@google-cloud/functions-framework');

// TODO(developer): Define your own computations
const {lightComputation, heavyComputation} = require('./computations');

// Global (instance-wide) scope
// This computation runs once (at instance cold-start)
const instanceVar = heavyComputation();

/**
 * HTTP function that declares a variable.
 *
 * @param {Object} req request context.
 * @param {Object} res response context.
 */
functions.http('scopeDemo', (req, res) => {
  // Per-function scope
  // This computation runs every time this function is called
  const functionVar = lightComputation();

  res.send(`Per instance: ${instanceVar}, per function: ${functionVar}`);
});

Python

import time

import functions_framework


# Placeholder
def heavy_computation():
    return time.time()


# Placeholder
def light_computation():
    return time.time()


# Global (instance-wide) scope
# This computation runs at instance cold-start
instance_var = heavy_computation()


@functions_framework.http
def scope_demo(request):
    """
    HTTP Cloud Function that declares a variable.
    Args:
        request (flask.Request): The request object.
        <http://flask.pocoo.org/docs/1.0/api/#flask.Request>
    Returns:
        The response text, or any set of values that can be turned into a
        Response object using `make_response`
        <http://flask.pocoo.org/docs/1.0/api/#flask.Flask.make_response>.
    """

    # Per-function scope
    # This computation runs every time this function is called
    function_var = light_computation()
    return f"Instance: {instance_var}; function: {function_var}"

Vai


// h is in the global (instance-wide) scope.
var h string

// init runs during package initialization. So, this will only run during an
// an instance's cold start.
func init() {
	h = heavyComputation()
	functions.HTTP("ScopeDemo", ScopeDemo)
}

// ScopeDemo is an example of using globally and locally
// scoped variables in a function.
func ScopeDemo(w http.ResponseWriter, r *http.Request) {
	l := lightComputation()
	fmt.Fprintf(w, "Global: %q, Local: %q", h, l)
}

Java


import com.google.cloud.functions.HttpFunction;
import com.google.cloud.functions.HttpRequest;
import com.google.cloud.functions.HttpResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Arrays;

public class Scopes implements HttpFunction {
  // Global (instance-wide) scope
  // This computation runs at instance cold-start.
  // Warning: Class variables used in functions code must be thread-safe.
  private static final int INSTANCE_VAR = heavyComputation();

  @Override
  public void service(HttpRequest request, HttpResponse response)
      throws IOException {
    // Per-function scope
    // This computation runs every time this function is called
    int functionVar = lightComputation();

    var writer = new PrintWriter(response.getWriter());
    writer.printf("Instance: %s; function: %s", INSTANCE_VAR, functionVar);
  }

  private static int lightComputation() {
    int[] numbers = new int[] { 1, 2, 3, 4, 5, 6, 7, 8, 9 };
    return Arrays.stream(numbers).sum();
  }

  private static int heavyComputation() {
    int[] numbers = new int[] { 1, 2, 3, 4, 5, 6, 7, 8, 9 };
    return Arrays.stream(numbers).reduce((t, x) -> t * x).getAsInt();
  }
}

Ruby

# Global (instance-wide) scope.
# This block runs on cold start, before any function is invoked.
#
# Note: It is usually best to run global initialization in an on_startup block
# instead at the top level of the Ruby file. This is because top-level code
# could be executed to verify the function during deployment, whereas an
# on_startup block is run only when an actual function instance is starting up.
FunctionsFramework.on_startup do
  instance_data = perform_heavy_computation

  # To pass data into function invocations, the best practice is to set a
  # key-value pair using the Ruby Function Framework's built-in "set_global"
  # method. Functions can call the "global" method to retrieve the data by key.
  # (You can also use Ruby global variables or "toplevel" local variables, but
  # they can make it difficult to isolate global data for testing.)
  set_global :my_instance_data, instance_data
end

FunctionsFramework.http "tips_scopes" do |_request|
  # Per-function scope.
  # This method is called every time this function is called.
  invocation_data = perform_light_computation

  # Retrieve the data computed by the on_startup block.
  instance_data = global :my_instance_data

  "instance: #{instance_data}; function: #{invocation_data}"
end

Puoi utilizzare le variabili globali come ottimizzazione del rendimento, ma non devi fare affidamento sullo stato impostato nell'ambito globale dalle chiamate di funzioni precedenti. Per ulteriori informazioni, consulta Stato non in memoria.

Puoi assumere che per ogni istanza di funzione, l'ambito globale sia stato eseguito esattamente una volta prima dell'invocazione del codice della funzione. Tuttavia, non devi basarti sul numero totale o sulle tempistiche delle esecuzioni a livello globale, in quanto potrebbero variare a seconda dell'attività di scalabilità automatica.

Cronologia di esecuzione della funzione

Una funzione ha accesso alle risorse allocate (memoria e CPU) solo per la durata dell'esecuzione della funzione. L'esecuzione del codice al di fuori del periodo di esecuzione non è garantita e può essere interrotta in qualsiasi momento. Pertanto, devi sempre segnalare correttamente la fine dell'esecuzione della funzione ed evitare di eseguire altro codice. Per indicazioni, consulta Funzioni HTTP, Funzioni in background e Funzioni CloudEvent.

L'esecuzione della funzione è soggetta anche alla durata del timeout della funzione. Per ulteriori informazioni, consulta Timeout della funzione.

Tieni conto della sequenza temporale di esecuzione durante l'inizializzazione dell'applicazione. Le attività in background non devono essere create a livello globale durante l'inizializzazione, poiché verrebbero eseguite al di fuori della durata di una richiesta.

Garanzie di esecuzione

In genere, le funzioni vengono richiamate una volta per ogni evento in entrata. Tuttavia, le funzioni Cloud Run non garantiscono una singola chiamata in tutti i casi a causa delle differenze negli scenari di errore.

Il numero massimo o minimo di volte in cui la funzione può essere invocata per un singolo evento dipende dal tipo di funzione:

  • Le funzioni HTTP vengono richiamate al massimo una volta. Questo accade a causa della natura sincrona delle chiamate HTTP e significa che qualsiasi errore che si verifica durante l'invocazione della funzione verrà restituito senza riprovare. È previsto che il chiamante di una funzione HTTP gestisca gli errori e riprovi, se necessario.

  • Le funzioni basate su eventi vengono richiamate almeno una volta. Questo è dovuto alla natura asincrona degli eventi, in cui non c'è un chiamante che attende la risposta. In rare circostanze, il sistema potrebbe invocare una funzione basata su eventi più di una volta per garantire l'invio dell'evento. Se l'invocazione di una funzione basata su eventi non va a buon fine a causa di un errore, la funzione non verrà invocata di nuovo, a meno che non siano abilitati i nuovi tentativi in caso di errore per la funzione.

Per assicurarti che la funzione si comporti correttamente nei tentativi di esecuzione ripetuti, devi renderla idempotente implementandola in modo che i risultati e gli effetti collaterali desiderati siano prodotti anche se un evento viene inviato più volte. Nel caso delle funzioni HTTP, significa anche restituire il valore desiderato anche se chi chiama riprova a chiamare l'endpoint della funzione HTTP. Per ulteriori informazioni su come rendere idempotente la funzione, consulta Nuovo tentativo per le funzioni basate su eventi.

Memoria e file system

A ogni funzione viene allocata una certa quantità di memoria per il suo utilizzo. Puoi configurare la quantità di memoria al momento del deployment. Per ulteriori informazioni, consulta Limiti di memoria.

L'ambiente di esecuzione della funzione include un file system in memoria che contiene i file di origine e le directory di cui è stato eseguito il deployment con la funzione (consulta Organizzare il codice sorgente). La directory contenente i file di origine è di sola lettura, ma il resto del file system è scrivibile (tranne i file utilizzati dal sistema operativo). L'utilizzo del file system viene conteggiato ai fini dell'utilizzo della memoria di una funzione.

La funzione può interagire con il file system utilizzando metodi standard in ogni linguaggio di programmazione.

Rete

La funzione può accedere a internet pubblico utilizzando metodi standard in ogni linguaggio di programmazione, tramite librerie integrate offerte dal runtime o librerie di terze parti incluse come dipendenze.

Prova a riutilizzare le connessioni di rete nelle chiamate alle funzioni, come descritto in Ottimizzazione della rete. Tuttavia, tieni presente che una connessione che rimane inutilizzata per 10 minuti potrebbe essere chiusa dal sistema e ulteriori tentativi di utilizzare una connessione chiusa causeranno un errore di "ripristino della connessione". Il codice deve utilizzare una libreria che gestisca bene le connessioni chiuse o gestirle esplicitamente se si utilizzano costrutti di rete di basso livello.

Isolamento delle funzioni

Ogni funzione di cui è stato eseguito il deployment è isolata da tutte le altre funzioni, anche quelle di cui è stato eseguito il deployment dallo stesso file di origine. In particolare, non condividono memoria, variabili globali, file system o altri stati.

Per condividere i dati tra le funzioni di cui è stato eseguito il deployment, puoi utilizzare servizi come Memorystore, Datastore, Firestore o Cloud Storage. In alternativa, puoi richiamare una funzione da un'altra utilizzando gli attivatori appropriati e passando i dati necessari. Ad esempio, puoi inviare una richiesta HTTP all'endpoint di una funzione HTTP o pubblicare un messaggio in un argomento Pub/Sub per attivare una funzione Pub/Sub.