Devop in Pillole #5 - Kuberneters Resource Management
Kubernetes Resource Management: CPU and Memory
Kubernetes gestisce le risorse CPU e memoria nei container tramite richieste e limiti, che vengono convertiti in impostazioni del processo Linux tramite cgroups.
Per la CPU, Kubernetes utilizza le richieste per garantire una quantità minima di tempo CPU. Questo viene fatto convertendo i valori di millicore in valori di peso proporzionali alla capacità, che vengono quindi utilizzati per impostare il controllo cgroup cpu.weight. I limiti della CPU, d'altra parte, vengono utilizzati per limitare la quantità di tempo della CPU che un contenitore può utilizzare. Questo viene fatto impostando il controllo cgroup cpu.max, che determina per quanto tempo il gruppo può essere eseguito in ciascun periodo.
Per la memoria, Kubernetes non utilizza le richieste per impostare alcun controllo cgroup. Invece, utilizza una combinazione di logica di pianificazione e regolazione del punteggio OOM per cercare di proteggere i processi che utilizzano meno memoria di quella richiesta. I limiti di memoria, d'altra parte, vengono applicati direttamente impostando il controllo cgroup memory.max. Se l'utilizzo della memoria per il cgroup supera tale limite, l'OOMKiller terminerà il contenitore.
Punti chiave da ricordare su come Kubernetes gestisce le risorse CPU e memoria:
In generale, le richieste di risorse sono fondamentali per garantire che i container abbiano le risorse di cui hanno bisogno senza sovraccaricare il nodo. I limiti dovrebbero essere usati con parsimonia, poiché possono introdurre latenza evitabile. Idealmente, le richieste di risorse dovrebbero essere impostate correttamente per ogni contenitore, ma questo può essere un compito difficile da svolgere manualmente. Per questo motivo, l'automazione è spesso la soluzione migliore per garantire una corretta gestione delle risorse.
Implicazioni dell'utilizzo dei limiti di CPU in Kubernetes
L'utilizzo dei limiti di CPU in Kubernetes comporta diverse implicazioni, principalmente legate al comportamento di CFS (Completely Fair Scheduler) di Linux e al suo meccanismo di controllo della larghezza di banda.
Latenza evitabile: Quando si definiscono i limiti di CPU per i container, il runtime del container li traduce in valori cpu.max nel cgroup del container. Ciò significa che i processi del container saranno soggetti al meccanismo di controllo della larghezza di banda di CFS. Quando un processo consuma tutta la sua quota di CPU per un determinato periodo, viene limitato (throttled) e messo in pausa, il che può introdurre latenza nell'applicazione. Anche se questo è il comportamento previsto dei limiti, può esserci una perdita di opportunità se non ci sono altri processi in contesa per il tempo di CPU. In tal caso, la limitazione introduce una latenza che potrebbe essere evitata.
Riduzione dell'efficienza della CPU a livello di nodo: La limitazione della CPU può ridurre l'efficienza della CPU a livello di nodo perché il tempo di CPU non viene utilizzato in modo ottimale. Se un container viene limitato mentre c'è tempo di CPU disponibile sul nodo, tale tempo di CPU viene sprecato.
Nessun vantaggio diretto a livello di carico di lavoro: La limitazione della CPU non offre alcun vantaggio diretto a livello di carico di lavoro. Non garantisce che un container riceva una determinata quantità di tempo di CPU, né impedisce che altri container consumino troppa CPU. L'unico vantaggio è impedire a un singolo container di utilizzare troppo tempo di CPU.
Complessità nella gestione delle risorse: Introdurre i limiti di CPU aggiunge un ulteriore livello di complessità alla gestione delle risorse in Kubernetes. È necessario determinare i limiti appropriati per ogni container, il che può essere difficile e richiede tempo.
In generale, i limiti di CPU non dovrebbero essere applicati come politica generale per i carichi di lavoro di Kubernetes. L'utilizzo dei limiti di CPU dovrebbe essere giustificato da esigenze specifiche, come la necessità di prevenire un comportamento anomalo delle applicazioni o di proteggere il nodo da un eccessivo consumo di risorse.
Le richieste di CPU sono generalmente considerate un meccanismo migliore per garantire il tempo di CPU e per assicurare un comportamento "da buon vicino" tra i container. Quando le richieste vengono impostate correttamente, i limiti diventano ridondanti e possono persino avere un impatto negativo sulle prestazioni.
Tuttavia, è importante ricordare che l'impostazione corretta delle richieste di CPU può essere un compito complesso e che l'utilizzo di strumenti automatizzati può semplificare il processo.
I limiti di memoria e l'OOMKiller
I limiti di memoria in Kubernetes influenzano l'azione dell'OOMKiller in modo diretto. Quando si imposta un limite di memoria per un container, il runtime del container lo converte nel valore memory.max per il cgroup del container. Se il container supera questo limite, l'OOMKiller viene attivato e termina il container. Questo meccanismo assicura che un singolo container non possa consumare più memoria di quella allocata, proteggendo gli altri container e il nodo stesso da un'instabilità del sistema.
Consigliati da LinkedIn
Ecco alcuni punti chiave su come i limiti di memoria interagiscono con l'OOMKiller:
Terminazione immediata: Quando un container supera il suo limite di memoria, l'OOMKiller lo termina immediatamente. Questo comportamento è diverso dalla limitazione della CPU, che rallenta il container ma non lo termina.
Terminazione di tutti i processi del container: Il parametro memory.oom_group di cgroup v2 è impostato su "true" per i container in Kubernetes. Ciò significa che quando un container supera il suo limite di memoria, tutti i processi al suo interno vengono terminati dall'OOMKiller, garantendo una pulizia completa delle risorse del container.
Nessuna influenza sulle richieste di memoria: Kubernetes non utilizza i cgroups per applicare le richieste di memoria. Le richieste di memoria sono utilizzate solo durante la pianificazione del pod per garantire che ci sia abbastanza memoria disponibile sul nodo.
OOMKiller come ultima risorsa: L'OOMKiller è un meccanismo del kernel Linux che viene invocato solo quando il sistema esaurisce la memoria fisica. Kubernetes cerca di prevenire l'attivazione dell'OOMKiller pianificando i pod in modo che la somma delle richieste di memoria dei container non superi la memoria allocata del nodo.
Influenza sull'OOMKiller: Anche se Kubernetes non può impedire completamente l'attivazione dell'OOMKiller, può influenzarne il comportamento impostando il parametro oom_score_adj per ogni processo del container. Questo parametro consente a Kubernetes di dare la priorità ai container che utilizzano meno memoria rispetto alla quantità richiesta quando l'OOMKiller viene richiamato. I container che utilizzano più memoria della richiesta avranno un punteggio OOM più alto e verranno terminati per primi.
In sintesi, i limiti di memoria sono un meccanismo importante per proteggere il sistema da un eccessivo consumo di memoria da parte di un singolo container. L'OOMKiller funge da ultima linea di difesa, terminando i container che superano i loro limiti. Sebbene Kubernetes cerchi di prevenire l'attivazione dell'OOMKiller, può influenzarne il comportamento per dare la priorità ai container che rispettano le loro richieste di memoria.
Eviction dei Pod
Un pod viene evitto quando un nodo non ha abbastanza risorse per supportare tutti i pod che sono programmati per eseguirsi su di esso. Kubernetes gestisce l'evizione dei pod dai nodi quando è sotto pressione delle risorse. La pressione delle risorse si verifica quando un nodo sta esaurendo risorse come CPU, memoria o spazio di archiviazione.
Esistono due tipi di soglie di evizione: soft e hard.
Quando una risorsa raggiunge la soglia soft, kubelet inizia a prendere provvedimenti per alleviare la pressione, come l'evizione dei pod. Quando una risorsa raggiunge la soglia hard, kubelet intraprenderà un'azione più aggressiva per alleviare la pressione, come l'immediata terminazione dei pod.
Kubernetes utilizza una serie di fattori per decidere quali pod sfrattare, tra cui:
● Utilizzo delle risorse: i pod che utilizzano più risorse di quelle richieste hanno maggiori probabilità di essere sfrattati.
● Priorità del pod: i pod con priorità inferiore hanno maggiori probabilità di essere sfrattati rispetto ai pod con priorità più alta.
● Qualità del servizio (QoS): I pod con un QoS inferiore, come BestEffort, hanno maggiori probabilità di essere sfrattati rispetto ai pod con un QoS più elevato, come Guaranteed.
● Richieste di risorse: i pod che non hanno impostato richieste o le hanno impostate troppo basse hanno maggiori probabilità di essere sfrattati.
L'evizione è un meccanismo importante per garantire la stabilità di un cluster Kubernetes. Tuttavia, può anche essere fonte di problemi se non viene gestita correttamente. Se i pod vengono sfrattati frequentemente, può indicare che il cluster non è stato dimensionato correttamente o che le richieste di risorse non sono impostate correttamente.
In definitiva, impostare correttamente le richieste di risorse può aiutare a prevenire l'evizione e garantire la stabilità e le prestazioni del cluster.