Questa guida fornisce le best practice per progettare, implementare, testare e implementare un servizio di pubblicazione Knative. Per ulteriori suggerimenti, consulta la pagina Eseguire la migrazione di un servizio esistente.
Scrivere servizi efficaci
Questa sezione descrive le best practice generali per la progettazione e l'implementazione di un servizio di pubblicazione Knative.
Evitare le attività in background
Quando un'applicazione in esecuzione su Knative serving termina l'elaborazione di una richiesta, l'accesso della CPU all'istanza del contenitore viene disattivato o limitato notevolmente. Pertanto, non devi avviare thread o routine in background che vengono eseguiti al di fuori dell'ambito dei gestori delle richieste.
L'esecuzione di thread in background può comportare un comportamento imprevisto perché qualsiasi richiesta successiva alla stessa istanza del contenitore riprende qualsiasi attività in background sospesa.
L'attività in background è tutto ciò che accade dopo che la risposta HTTP è stata comunicata. Esamina il codice per assicurarti che tutte le operazioni asincrone vengano completate prima di inviare la risposta.
Se sospetti che nel tuo servizio sia presente un'attività in background non immediatamente visibile, puoi controllare i log: cerca eventuali elementi registrati dopo la voce relativa alla richiesta HTTP.
Eliminazione dei file temporanei
Nell'ambiente Cloud Run, lo spazio di archiviazione su disco è un file system in memoria. I file scritti su disco consumano memoria altrimenti disponibile per il servizio e possono persistere tra le invocazioni. La mancata eliminazione di questi file può eventualmente portare a un errore di esaurimento della memoria e a un successivo avvio a freddo.
Ottimizzazione delle prestazioni
Questa sezione descrive le best practice per ottimizzare il rendimento.
Avvio rapido dei servizi
Poiché le istanze di container vengono scalate in base alle esigenze, un metodo tipico è inizializzare completamente l'ambiente di esecuzione. Questo tipo di initialization è chiamato "cold start". Se una richiesta del client attiva un avvio a freddo, l'avvio dell'istanza del contenitore comporta una latenza aggiuntiva.
La routine di avvio è composta da:
- Avvio del servizio
- Avvio del contenitore
- Esegui il comando entrypoint per avviare il server.
- Controllo della porta del servizio aperta.
L'ottimizzazione per la velocità di avvio del servizio riduce al minimo la latenza che ritarda l'invio di richieste da parte di un'istanza del contenitore.
Utilizzare le dipendenze in modo oculato
Se utilizzi un linguaggio dinamico con librerie dipendenti, ad esempio l'importazione di moduli in Node.js, il tempo di caricamento di questi moduli aggiunge latenza durante un avvio a freddo. Riduci la latenza di avvio nei seguenti modi:
- Riduci al minimo il numero e le dimensioni delle dipendenze per creare un servizio snello.
- Carica in modo lento il codice che non viene utilizzato di frequente, se la tua lingua lo supporta.
- Utilizza ottimizzazioni di caricamento del codice come l'ottimizzazione dell'autoloader di Composer di PHP.
Utilizzo di variabili globali
In Knative serving, non puoi assumere che lo stato del servizio venga mantenuto tra le richieste. Tuttavia, il servizio Knative riutilizza le singole istanze dei contenitori per gestire il traffico in corso, quindi puoi dichiarare una variabile a livello globale per consentire il riutilizzo del relativo valore nelle chiamate successive. Non è possibile sapere in anticipo se una singola richiesta beneficia di questo riutilizzo.
Puoi anche memorizzare nella cache gli oggetti in memoria se sono costosi da ricreare a ogni richiesta di servizio. Se lo sposti dalla logica di richiesta all'ambito globale, il rendimento migliorerà.
Node.js
Python
Vai
Java
Esecuzione dell'inizializzazione lazy delle variabili globali
L'inizializzazione delle variabili globali avviene sempre durante l'avvio, il che aumenta il tempo di avvio a freddo. Utilizza l'inizializzazione lazy per gli oggetti usati di rado per posticipare il costo in termini di tempo e ridurre i tempi di avvio a freddo.
Node.js
Python
Vai
Java
Ottimizzazione della concorrenza
Le istanze di servizio Knative possono gestire più richieste contemporaneamente,
"in modo concorrente", fino a una contemporaneità massima configurabile.
È diverso dalle funzioni Cloud Run, che utilizzano concurrency = 1
.
Devi mantenere l'impostazione predefinita della concorrenza massima, a meno che il tuo codice non abbia requisiti di concorrenza specifici.
Ottimizzazione della concorrenza per il servizio
Il numero di richieste in parallelo che ogni istanza di container può gestire può essere limitato dall'architettura tecnologica e dall'utilizzo di risorse condivise come variabili e connessioni al database.
Per ottimizzare il servizio per una contemporaneità massima stabile:
- Ottimizza il rendimento del servizio.
- Imposta il livello di supporto della concorrenza previsto in qualsiasi configurazione della concorrenza a livello di codice. Non tutti gli stack tecnologici richiedono un'impostazione di questo tipo.
- Esegui il deployment del servizio.
- Imposta la concorrenza di Knative serving per il tuo servizio su un valore uguale o inferiore a qualsiasi configurazione a livello di codice. Se non è presente alcuna configurazione a livello di codice, utilizza la concorrenza prevista.
- Utilizza strumenti di test di carico che supportano una concorrenza configurabile. Devi verificare che il servizio rimanga stabile con il carico e la concorrenza previsti.
- Se il servizio non funziona correttamente, vai al passaggio 1 per migliorarlo o al passaggio 2 per ridurre la concorrenza. Se il servizio funziona correttamente, torna al passaggio 2 e aumenta la concorrenza.
Continua a eseguire l'iterazione finché non trovi la concorrenza stabile massima.
Adattamento della memoria alla concorrenza
Ogni richiesta gestita dal servizio richiede una certa quantità di memoria aggiuntiva. Pertanto, quando aumenti o diminuisci la concorrenza, assicurati di regolare anche il limite di memoria.
Evitare lo stato globale mutabile
Se vuoi sfruttare lo stato globale mutabile in un contesto concorrente, esegui passaggi aggiuntivi nel codice per assicurarti che venga eseguito in sicurezza. Riduci al minimo le contese limitando le variabili globali all'inizializzazione una tantum e al riutilizzo, come descritto sopra nella sezione Rendimento.
Se utilizzi variabili globali mutabili in un servizio che gestisce più richieste contemporaneamente, assicurati di utilizzare lock o mutex per evitare condizioni di gara.
Sicurezza dei container
Molte pratiche di sicurezza del software per uso generico si applicano alle applicazioni containerizzate. Esistono alcune pratiche specifiche per i container o che si allineano alla filosofia e all'architettura dei container.
Per migliorare la sicurezza del contenitore:
Utilizza immagini di base sicure e sottoposte a manutenzione attiva, come le immagini di base di Google o le immagini ufficiali di Docker Hub.
Applica gli aggiornamenti della sicurezza ai tuoi servizi ricostruendo regolarmente le immagini dei contenitori e reimplementando i servizi.
Includi nel contenitore solo ciò che è necessario per eseguire il servizio. Il codice, i pacchetti e gli strumenti aggiuntivi sono potenziali vulnerabilità di sicurezza. Consulta la sezione precedente per informazioni sull'impatto sul rendimento.
Implementa un processo di compilazione deterministico che includa versioni specifiche di software e librerie. In questo modo, il codice non verificato non verrà incluso nel contenitore.
Imposta il container in modo che venga eseguito come utente diverso da
root
con l'istruzioneUSER
del Dockerfile. Per alcune immagini contenitore potrebbe essere già configurato un utente specifico.
Automatizzare la scansione di sicurezza
Abilita l'analisi delle vulnerabilità per la scansione di sicurezza delle immagini container archiviate in Artifact Registry.
Puoi anche utilizzare Autorizzazione binaria per garantire che venga eseguito il deployment solo di immagini container sicure.
Creazione di immagini container minime
Le immagini container di grandi dimensioni probabilmente aumentano le vulnerabilità di sicurezza perché contengono più di quanto necessario per il codice.
In Knative Serving, le dimensioni dell'immagine del contenitore non influiscono sul tempo di avvio a freddo o sull'elaborazione delle richieste e non vengono conteggiate ai fini della memoria disponibile del contenitore.
Per creare un container minimo, ti consigliamo di utilizzare un'immagine di base snella, ad esempio:
Ubuntu è di dimensioni maggiori, ma è un'immagine di base di uso comune con un ambiente server out-of-box più completo.
Se il tuo servizio ha un processo di compilazione che richiede molti strumenti, ti consigliamo di utilizzare le costruzioni a più fasi per mantenere il contenitore leggero in fase di esecuzione.
Queste risorse forniscono ulteriori informazioni sulla creazione di immagini contenitore snelle:
- Best practice di Kubernetes: come e perché creare immagini di container di piccole dimensioni
- 7 best practice per la creazione di container