Computing infrastructures
Questa pagina contiene una breve rielaborazione delle lezioni di Computing Infrastructures che ho seguito al Politecnico di Milano.
Sono solito scrivere questo tipo di riassunti al termine del processo di studio di ogni materia, in modo da rimodellare i concetti ed essere certo di averli compresi appieno. Spero possa essere d’aiuto ai futuri studenti.
1. COMPUTING INFRASTRUCTURE, DI COSA SI TRATTA?
In breve, è un sistema che fornisce hardware e software utile all’esecuzione di altri servizi.
Vi sono molti sistemi appartenenti a tale categoria, ognuno dei quali ha caratteristiche che rispondono ad un particolare tipo di bisogno (potenza di calcolo, prezzo, dimensioni, reperibilità, architettura, …).
Si dice che, nel loro complesso, i vari tipi di computing infrastructures formano un Computing Continuum, nel senso che è come se formassero uno spettro continuo di caratteristiche dal quale è possibile “pescare” il componente che fa al caso nostro.
Analizzeremo nel seguito le sottocategorie più importanti.
IOT devices
Immaginiamo di voler gestire la logica di accensione del condizionatore di camera nostra. La regola è molto semplice: se la temperatura è superiore ad un valore X misurato da un sensore, allora accendiamo il sistema di raffreddamento.
Quando abbiamo a che fare con casi simili non è necessaria molta potenza di calcolo. Al contrario, spesso risulta utile usare dispositivi molto semplici e che consumano davvero poco, in modo da poterli alimentare a batteria o in generale rendere il sistema molto più efficiente. Anche le dimensioni ne giovano, visto che i dispositivi industriali possono arrivare ad avere dimensioni davvero minuscole.
Tali componenti sono proprio i dispositivi IOT (Internet Of Things). Un esempio famoso che tutti conosciamo è l’Arduino UNO, ma ce ne sono molti altri.
Embedded Computers
Ci sono casi d’uso in cui la potenza disponibile su un IOT device non basta: abbiamo bisogno di un vero e proprio “mini computer” che permetta il parallelismo, magari anche accelerazione grafica, … . E’ in questi casi che si usano gli embedded computers: dispositivi più potenti, ma anche più grandi in dimensioni e più energivori rispetto agli IOT devices.
E’ possibile acquistare dispositivi già pronti (come per esempio il famoso Raspberry PI), ma spesso le aziende progettano il loro componente embedded da sè, in modo da inserirvi dentro tutti e soli i componenti necessari all’erogazione del loro servizio, diminuendo dimensioni e costi.
C’è un intero corso al Poli riguardante questo tipo di dispositivi, ma non è questo il core del riassunto in questione.
Potenza computazionale massima: i Data Centers
Progetti di gestire un servizio cloud usato da milioni di utenti? O magari vorresti trainare una rete neurale da miliardi di parametri? Beh, in tal caso hai bisogno di tantissima potenza di calcolo: hai bisogno di un data center.
I data centers sono edifici contenenti centinaia o migliaia di servers, i quali vanno posti in maniera ordinata (corridoi, racks) alimentati (sistema di alimentazione con eventuali backup), raffreddati (sistema di cooling aria o liquido) e gestiti dal punto di vista della partizione delle risorse di calcolo. Proprio quest’ultimo aspetto è solitamente gestito tramite due principali strumenti, simili ma differenti:
- Virtual Machines: le VM sono come “contenitori” che permettono di installare al loro interno interi sistemi operativi, ai quali vengono fornite risorse virtuali generate dalla “spartizione” delle reali risorse fisiche della macchina.
- Containers: simili alle VM, i containers non fanno girare interi OS, bensì permettono di installare gli applicativi e le librerie necessarie a farli funzionare. Questo rende i Containers più leggeri e veloci delle VM, ma meno flessibili.
I DC e gli approcci di SaaS, PaaS, IaaS, … hanno permesso agli erogatori di servizi di poter accedere a soluzioni più economiche e stabili e, di conseguenza, hanno permesso di abbassare i prezzi per gli utenti finali. I vantaggi appena citati non sono però gratuiti: il prezzo che si paga è la necessità di una connessione ad internet stabile e a banda larga, un’impatto ambientale più elevato (per diversi fattori, uno tra tutti il fatto che spesso i programmi girano su macchine con architetture non ottimizzate per lo specifico software, per cui l’efficenza del processo è molto inferiore rispetto al caso in cui il provider scegliesse da sè il tipo di server da usare), un aumento di rischio dal punto di vista della privacy ed un aumento della latenza.
Tutti questi aspetti negativi rendono i DC una soluzione eccezionale o pessima dipendentemente dal contesto d’uso. Se per esempio stessimo costruendo un’auto a guida autonoma, la scelta di far girare il modello che decide quando sterzare/frenare su un Data Center sarebbe davvero pessima: è necessaria una latenza decisionale molto minore rispetto a quella restituibile da un DC. L’opzione migliore sarebbe probabilmente quella di runnare il modello in locale, almeno per le decisioni cruciali.
L’anello mancante: Edge/Fog computing
Immaginiamo di trovarci nel seguente contesto: stiamo costruendo un sistema costituito da:
- tantissimi sensori IOT che registrano dati grezzi relativi alla sicurezza stradale (es. temperatura, umidità, …) installati su un vasto territorio.
- un sistema cloud che riceve questi dati grezzi e ne genera decisioni immediate (es. quale strada è più sicuro percorrere al momento) e di lungo periodo (es. percentuale di incidenti date determinate caratteristiche del territorio).
- dei veicoli autonomi che ricevono le decisioni immediate calcolate dal sistema cloud e agiscono di conseguenza.
Come converrebbe quindi costituire il nostro sistema cloud in questo caso? Una prima idea potrebbe essere quella di usare “semplicemente” dei DC, ma ciò genererebbe due problemi:
- inviare direttamente ai DC i dati grezzi ed eseguire su di essi elaborazioni che potremmo svolgere in locale è economicamente svantaggioso, poiché ogni bit che mandiamo, processiamo e memorizziamo lo stiamo anche pagando.
- la latenza per il nostro caso d’uso potrebbe essere eccessiva a seconda della locazione del DC.
Una soluzione a questi problemi è quella di implementare il concetto di Fog/Edge Computing (due concetti spesso sovrapponibili, ne spiegherò tra poco le differenze). Ciò consisterebbe nell’interporre tra i sensori IOT e i DC dei componenti a “potenza intermedia” (es. Embedded Devices, oppure veri e propri computer dotati di chip Intel/AMD, …) posti più vicini ai sensori, che permettano di effettuare il primo processamento dei dati grezzi e calcolare le decisioni immediate da mandare alle auto, per poi mandare i dati pre-elaborati (e quindi molto compressi in dimensione) al Data Center, che potrà poi procedere con l’elaborazione di lungo periodo (magari anche computazionalmente più complessa) e l’archiviazione.
Le modifiche apportate dall’implementazione di tale paradigma possono essere osservate da due punti di vista:
- Prospettiva dei dati: il processamento dei dati avviene vicino (geograficamente parlando) ai sensori IOT che li generano. Questo è ciò che si chiama Edge Computing.
- Prospettiva della rete: i dati vengono pre-processati da un nodo presente nella stessa LAN in cui si trovano i sensori IOT, il quale manderà poi il risultato dell’elaborazione ai DC/veicoli. Questo è ciò che si chiama Fog Computing.
Potete notare come la differenza sia talmente minima che spesso i concetti sono sovrapponibili.
L’evoluzione dei datacenter
Proviamo a creare una mappa cronologica dell’evoluzione dei servizi digitali e delle sottostanti infrastrutture.
1. On-Device services
Inizialmente, l’unico modo di distribuire servizi digitali era quello di creare applicazioni che girassero interamente sui client. Questo approccio complica la gestione degli aggiornamenti e dell’ottimizzazione hardware: ad ogni nuovo update è necessario convincere gli utenti ad aggiornare la loro versione dell’app ed inoltre non si può controllare l’hardware sul quale l’user fa girare il servizio, per cui non si può testare il corretto funzionamento del software in ogni circostanza.
D’altro canto, questo approccio semplifica la gestione di privacy, sicurezza e tempi di risposta per le applicazioni in cui tali caratteristiche sono critiche, per cui è ancora usato in determinati contesti.
2. Cloud services: Datacenter
Con il miglioramento dell’infrastruttura di rete globale è stato possibile spostare il carico computazionale dal client a dei “centri di calcolo” (server) separati, per lasciare sul dispositivo dell’utente solo il front-end dell’app ed eventuali funzionalità time-critical. Questo approccio porta con sè vantaggi e, ovviamente, svantaggi che lo rendono più o meno adatto a diversi contesti.
Prima di tutto, ora il provider ha il completo controllo sull’hardware su cui gira il back-end, per cui può effettuare ottimizzazioni software, testare il funzionamento del servizio sullo specifico hardware, aggiornare il software correggendo eventuali bug o introducendo nuove funzionalità in maniera completamente trasparente per l’utente, il quale non dovrà installare nulla sul suo device se non il solo front-end. Altri conseguenti vantaggi sono la possibiltà di fornire servizi alla base dei quali ci sono requisiti di calcolo che i singoli clients non potrebbero supportare, spostando l’elaborazione in remoto con una diminuizione di consumi e temperature lato utente (ottimo per dispositivi a batteria come laptop o smartphones).
Gli svantaggi sono speculari a quelli dela soluzione on-device: tempi di risposta minimi superiori, dipendenza da una rete internet a banda larga, possibili problemi di privacy e sicurezza.
I server necessari all’erogazione di servizi di più provider sono spesso inseriti nello stesso datacenter, così da renderne più efficente la gestione in termini di raffreddamento, availability, reliability (analizzeremo questi concetti in seguito) e costi. Spesso, un datacenter contiene centinaia di servers, disposti in edifici ridondanti e alimentati da fonti di energia diversificate e ridondanti anch’esse. La causa di ciò è che i datacenter sono considerati “critical-infrastructures”, ovvero infrastrutture il cui tasso di funzionamento (specificheremo meglio questo concetto in seguito) deve superare il 99.99%.
3. Servizi che crescono: Warehouse-Scale Computers (WSC)
Tra i servizi che si sono spostati in cloud, ce ne sono alcuni che sono cresciuti a dismisura in termini di numero di utenti e requisiti di calcolo, tanto da “occupare” interi datacenter per l’erogazione di un singolo servizio. E’ così che sono nati i WSC: datacenter composti da hardware omogeneo, di proprietà di una singola organizzazione, al cui interno girano poche decine di applicazioni il cui scopo è l’erogazione di un singolo servizio. A differenza dei datacenter tradizionali, in cui le innumerevoli applicazioni installate sono indipendenti e non comunicano tra loro, nei Warehouse-Scale Computers le applicazioni collaborano per raggiungere uno scopo comune.
Esempi sono per esempio i servizi Google, Amazon, Dropbox, … .
La diffusione di queste strutture è stata incentivata anche dalla nascita di servizi che per loro natura necessitano di essere installate su dei WSC, come i servizi di ricerca e indicizzazione o l’addestramento di modelli neurali generativi.
4. Virtualization: WSC come datacenter
Il diffondersi delle tecnologie di virtualizzazione (delle quali parleremo in seguito) ha permesso di creare dei WSC il cui servizio erogato è simile a quello offerto da un classico datacenter: si offrono servizi di hosting (SaaS, PaaS, IaaS, ..) attraverso tante macchine omogenee “nascoste” da uno “strato di virtualizzazione”. Ciò che l’utente vede è soltanto la possibilità di “comprare” potenza di calcolo proporzionale al pagamento offerto.
E’ questo il caso di AWS o di Microsoft Azure.
Organizzazione geografica dei datacenter
I requisiti delle grandi compagnie erogatrici di servizi digitali non sono soddisfatti da un solo datacenter: ne servono molti. Ma come distribuirli in maniera coerente e sensata?
L’approccio più comunemente usato è quello spiegato di seguito, basato su una suddivisione gerarchica del territorio.
I) Geographic zones
Il mondo è suddiviso in geographic zones, ovvero in regioni politiche nelle quali vigono regole e leggi diverse, soprattutto dal punto di vista della gestione dei dati e della privacy degli utenti. Idealmente, un provider dovrebbe essere presente “fisicamente” in ognuna delle geographic zones in cui intende erogare il proprio servizio. Da notare il fatto che le geographic zones possono anche non essere molto distanti geograficamente, ma lo sono dal punto di vista legale.
II) Computing regions
All’interno di ogni geographic zone sono presenti due o più computing regions. Una computing region è una zona in cui la round-trip latency massima è di 2ms. E’ inoltre il livello di granularità minimo visibile dall’utente.
Solitamente, i gestori dei datacenters posizionano i loro centri i calcolo in zone a rischio diversificato, nel senso che nella scelta del territorio di installazione per due datacenter, difficilmente si sceglierà di inserire entrambi all’interno della stessa zona sensibile a terremmoti e tsunami; magari un datacenter verrà piazzato in quella zona se la si ritiene strategica, ma un altro sarà installato lontano, in un posto a basso rischio. In questo modo, nel caso di calamità, uno dei due DC continuerà a funzionare ed erogare il servizio.
La sincronia tra i due datacenter non sarà perfetta a causa della grande distanza, ma per il disaster recovery una sincronia non perfetta è più che accettabile.
III) Availability zones
All’interno della stessa computing region è possibile inserire più datacenter, ognuno dei quali costituirà una availability zone. Questa ulteriore suddivisione porta con sè diversi vantaggi:
- possibilità di runnare operazioni latency-critical;
- maggior resistenza ai guasti (aumento di availability e reliability dentro la computing region). Inoltre, se si hanno almeno 3 availability zones, è possibile effettuare il recupero dei dati in caso di guasto o interruzione di servizio di uno dei datacenter: se si avessero solo due DC ed ad un certo punto si avesse una discrepanza di dati dovuta ad un guasto, non si potrebbe capire qual è la versione corretta dei dati. Con 3 datacenter è possibile invece usare il quorum, ovvero si assegna come corretta la versione condivisa dai due datacenter integri, rendendo possibile riconoscere il singolo server guasto.
- conseguente possibilità di far girare applicazioni availability/reliability-critical.
Panoramica dei possibili tipi di server
E’ curioso notare come lo schema di base dell’intero datacenter e quello di un suo singolo server siano molto simili. Entrambi sono costituiti da:
- Componente di processamento -> nel caso dell’intero DC si tratta di uno o più server, del caso del server si tratta di una o più CPU (aventi diversi cores) ed, eventualmente, una o più GPU/TPU.
- Componente di memoria veloce -> nel caso del DC si tratta di dischi ad alta velocità (es. SSD) che fanno si che i dati siano velocemente accessibili. Nel caso del singolo server si tratta della RAM.
- Componente di memoria a lungo termine -> nel caso dei DC si tratta degli HHD, nel caso del singolo server si tratta di SSD o HHD (o eventualmente SAN, vedremo in seguito cosa è).
- Networking-> il DC ha una sua connessione a banda ultralarga che lo collega al resto del mondo. Il singolo server possiede anch’esso una connessione individuale alla rete.
- Sistema di alimentazione -> il DC è fornito di un complesso sistema di alimentazione. Anche il singolo server deve essere alimentato.
- Raffreddamento -> la dissipazione del calore è un aspetto essenziale del datacenter e dei singoli server. Vedremo in seguito che è possibile utilizzare diverse soluzioni, con approcci a diverse granularità (raffreddamento sull’intero DC o sui singoli corridoi/rack/servers).
- Failure recovering -> il sistema di raffreddamento o di alimentazione potrebbero avere dei malfunzionamenti: è necessario installare sistemi di recovery che permettano di evitare che tali malfunzionamenti (foult) si tramutino in veri e propri errori (failures). Questo avviene anche a livello del singolo server.
Nei server, questi elementi fontamentali possono essere assemblati in modi diversi, a seconda del caso d’uso e dei requisiti di sistema. Analizzeremo in questo paragrafo le soluzioni più comuni.
Tower Server
I componenti di questa tipologia di server sono assemblati all’interno di una torre simile a quella dei computer “casalinghi” a cui siamo abituati: un case verticale contenente scheda madre, processore, alimentatore, dissipazione, … . Questo tipo di approccio porta con sè vantaggi dal punto di vista della modularità e delle temperature: il tower case permette di alloggiare le componenti comodamente, lasciando moltissimo spazio libero. Il server è quindi facilmente aggiornabile con nuove componenti e, vista la bassa densità di componenti presenti al suo interno, facilmente raffreddabile. Un’ulteriore pro è il prezzo, essendo la soluzione più economica.
D’altro canto, proprio lo spazio non usato che permette di ottenere i vantaggi precitati fa si chel’area usata per un singolo server sia di molto superiore a quella richiesta da server costruiti con altri approcci (che spiegheremo a breve). Questo crea un incremento di costi dovuti all’acquisto di nuovo spazio per il DC talmente alto da annullare il vantaggio economico dovuto al prezzo inferiore di questa soluzione.
Domanda lecita: ok, occupano più spazio. Ma se costruisco un datacenter a diversi piani, espandendo lo spazio verticalmente, non devo comprare nuova terra per installare più datacenter. Perché non si usa questo approccio?
Risposta triste: il problema è che in un datacenter la densità dei componenti informatici è talmente alta da far si che la struttura debba sostenere diverse volte il peso che ad un normale edificio è richiesto reggere. Sarebbe necessario usare tecniche di costruzione apposite con un incremento dei costi che va oltre quello del semplice acquisto di nuova terra. Per questo motivo, spesso i datacenters sono costruiti a singolo piano (a dammuso).
In generale i Tower Servers sono una soluzione ottima per piccole aziende che necessitano di massimo una decina di servers, ma diventano inefficenti dal punto di vista dell’area occupata quando i numeri aumentano.
Rack Server
I rack server risolvono i problemi dell’alternativa a torre, introducendone inevitabilmente degli altri.
Il concetto generale è quello di introdurre nel datacenter i racks, ovvero scaffali sui quali posizionare uno sull’altro dei server di dimensioni molto minori a quelle di un tipico tower. Nello specifico, le dimensioni sono:
- larghezza = 19 pollici ≈ 48,26 cm,
- altezza = 1, 2, 4 … U, dove una U (unit) corrisponde a 1,75 pollici = 4,445 cm (dannato sistema di misura imperiale).
In questo modo la densità di potenza di calcolo aumenta molto, riducendo drasticamente gli spazi necessari. Solitamente, ogni rack ospita al suo apice componenti che non sono propriamente dei server, ma servono a supporto di questi ultimi (es. router, alimentazione, …). Tutti i server di un rack condividono queste componenti, minimizzando in questo modo il numero di cavi e semplificandone la gestione. I componenti installati in quest’area sono detti TOR <-> Top Of Rack (es. TOR Switch).
Capito, ma cosa succede se aumentiamo la densità di componenti? Accade che aumentano anche le temperature. Questo aspetto ha delle ripercussioni sia sul singolo server (se aumenta il calore generato, la frequenza del processore diminuirà per tentare di diminuire la temperatura), ma anche ripercussioni inter-server (se i server A e B sono posti uno sopra l’altro, e sul server A sta girando un applicativo molto intensivo che fa aumentare drasticamente le temperature, il calore verrà trasferito anche al server B, che abbasserà la sua frequenza di clock come provvedimento contenitivo. In questo modo, anche se i due servers generano applicativi completamente separati e indipendenti, si ha un’interazione indiretta indesiderata).
Proprio per questo motivo si rendono necessari sistemi di raffreddamento avanzati che permettono di dissipare tutto il calore generato nei racks, con conseguente aumento dei costi per quanto riguarda l’hardware da acquistare e l’energia elettrica consumata.
Anche la gestione dei server diventa più complessa: avendo tanti componenti in poco spazio, al verificarsi di un problema diventa più difficile individuarne la causa e, data la minore modularità dei rack server, diventa più complessa anche l’eventuale riparazione.
Blade Server
I blade sono l’ultimo formato di server disponibile. La struttura di un blade server è costituita come segue:
- Il “componente più esterno” è il rack, che è identico a quello usato per i rack server e che rispetta le stesse misure standard specificate dall’IEE.
- All’interno del rack viene inserito il case che conterrà i blade servers. Il case rispetta le dimensioni standard dei rack, con una larghezza di 48.26cm e un’altezza di varie U, con U = 4.445cm. Questo contenitore integra il sistema di switch e alimentazione dei blade servers e anche l’eventuale sistema di raffreddamento a liquido.
- Il case descritto è caratterizzato da delle feritoie verticali, nelle quali vengono inseriti i blade servers. Questi ultimi sono dei server ancor più “miniaturizzati”: la densità di potenza di calcolo è altissima, con conseguente aumento delle temperature.
In pratica i blade server stanno ai rack server come questi ultimi stanno ai tower server: si ha una semplificazione ulteriore del cable management e un aumento in prestazioni per area occupata a discapito di un aumento di temperature e prezzi.
Si semplifica in questo caso anche la gestione del datacenter dato che il case contenente i blade rende più semplice il monitoraggio e il mantenimento dei server, riducendo la “parte complicata” alla sola gestione dell’interfaccia del case e non dei singoli servers contenuti.
Un nuovo lato negativo nel caso della soluzione blade è la perdita della standardizzazione: nonostante il case rispetti gli standard IEEE, i blade servers contenuti in esso non hanno un formato standardizzato. I blade servers infatti usano standard proprietari, il chè crea una “dipendenza” nei confronti del provider a cui si ci affida inizialmente.
2. LE COMPONENTI PRINCIPALI DI UN SERVER/DATACENTER
Incredibile ma vero: nei paragrafi seguenti “approfondirò” i principali elementi che costituiscono un server.
Processamento dei dati
Nei server i dati vengono prelevati dalla memoria, modificati, processati e sovrascritti, … . Sono diversi i componenti usati a tale scopo, ma tutti hanno una struttura di base “simile”, che può essere schematizzata (semplificandola all’osso) come segue:
- ALU (Arithmetic Logic Unit) -> sono cores che eseguono le operazioni di calcolo.
- Cache memory -> sono memorie SRAM (Static RAM, diverse dalle DRAM che puoi acquistare come espansione di memoria per il tuo computer) che permettono di mantenere i dati su cui si sta lavorando molto vicini ai core. Si dividono in L1, L2, L3, dove L1 è la più piccola e veloce, L3 è la più grande e lenta.
- Control unit -> decide quali istruzioni eseguire e quando, gestendo i salti e gli accessi in memoria.
Il modo in cui questi elementi sono distribuiti su un modulo ne decreta il tipo e il caso d’uso.
CPU
Il principale (e, fino a qualche decennio fa, unico) elemento calcolatore è la CPU (Central Processing Unit). E’ un componente di calcolo “generale”, pensato per il calcolo sequenziale o con accesso disordinato alla memoria. E’ solitamente costituito da una control unit molto “ingombrante”, che è capace di riordinare le istruzioni in modo che vengano eseguite sui core paralleli in maniera efficente senza modificare il comportamento del programma e senza che vi siano interferenze di accesso alla cache da parte delle ALU. Queste ultime sono molto potenti ed in grado di effettuare operazioni complesse. La cache è di dimensione discreta e permette di mantenere i dati essenziali per il calcolo “vicini” alle ALU per un accesso più veloce.
In generale, questo tipo di componente dà il meglio di se in contesti di single thread o di lieve parallelismo, nei quali la grande control unit, la discreta cache e le (poche) potenti ALU superano in prestazioni le altre tecnologie (per motivi che spiegheremo tra poco).
GPU
Come si può evincere dal nome, le Graphic Processing Units sono componenti nate inizialmente per l’elaborazione grafica: le immagini e i frames dei video sono composti da tanti pixel che possono essere processati indipendentemente (parallelamente) e con operazioni semplici. E’ proprio questo il concetto cardine che caratterizza le GPU: invece che possedere poche ALU potenti, esse contengono migliaia di semplici ALU, meno potenti di quelle presenti nelle CPU ma in quantità molto maggiore, per processare parallelamente i pixel.
Se inizialmente questo approccio era usato per la grafica, oggi sono moltissime le applicazioni per le quali un componente del genere rende il processamento dei dati molto più veloce (training di neural networks, calcoli scientifici, …).
Ma se le GPU sono così potenti, perché non le usiamo per tutto sostituendo completamente le CPU? Vediamo.
Le Graphic Processing Unit nascono da un problema: secondo la “legge” di Moore, il numero di transistors fisicamente inseribili all’interno di un chip raddoppia ogni 18 mesi; tuttavia, la richiesta di potenza grafica per le varie applicazioni moderne (gaming, rendering, ma sopratutto AI) cresce con una frequenza molto maggiore. Questo ha reso necessario trovare il modo di aumentare drasticamente le prestazioni di certi tipi di task senza aver a disposizione un aumento proporzionale del numero di transistors nei chip.
Si è pensato quindi all’approccio GPU e al parallelismo: non possiamo inserire migliaia di ALU potenti come quelle delle CPU in un singolo chip insieme ad una cache e una control unit altrettanto grande, ma possiamo “rimpicciolire” la cache e la control unit, inserendo migliaia di ALU molto semplici, che siano “brave” ad effettuare soltanto certi tipi di istruzioni critiche (in particolare operazioni matriciali).
Questo rende le GPU non adatte a carichi sequenziali intensivi. Nello specifico, le ALU delle GPU sono meno potenti di quelle delle CPU. Questo fa si che le prestazioni in single (o pochi) core siano più lente. Inoltre, la GPU non fa girare il sistema operativo. Quando si vuole effettuare un’elaborazione tramite Processing Graphic Unit, si deve passare per il percorso
## Lettura dati
DRAM -> CPU_cache -> GPU_VRAM
## Salvataggio dati
GPU_VRAM -> CPU_cache -> DRAM
Il passaggio CPU_cache -> GPU_VRAM (e viceversa) è un collo di bottiglia che introduce un overhead fisso non evitabile. Per capire l’impatto di questa latenza, immaginiamo che il tempo di trasferimento tra la cache del processore e la VRAM della GPU sia di 30 secondi. Ci sono due possibili casi:
- se usiamo la GPU per calcoli sequenziali e non ottimizzati per GPU, dove ogni “ciclo di lavoro” (il lavoro che la GPU svolge sui dati una volta che essi sono nella sua VRAM) dura 1 minuto, l’overhead grava molto sulle prestazioni.
- se invece eseguiamo calcoli massicci in parallelo, in cui immaginiamo che un “ciclo di lavoro” duri 10 ore, i 30 secondi di overhead diventano irrilevanti rispetto al vantaggio nel calcolo parallelo, che avrebbe richiesto alla CPU 1000X lo stesso tempo.
Le GPU sono quindi componenti eccezionali per determinati tipi di task e hanno reso possibile superare l’ostacolo della legge di Moore per calcoli paralleli massivi, ma non sono d’uso universale.
Multiple GPUs
L’incremento dei requisiti prestazionali dovuti all’ingigantimento delle reti neurali ha reso necessario connettere più GPU per farle lavorare in parallelo come se fossero un’unica componente. Per far ciò si usano protocolli (proprietari) come l’NVLink, il protocollo Multiple GPUs di Nvidia. Si tratta di un protocollo in cui una o più CPU host è/sono connessa/e a diverse GPU, a loro volta connesse tra loro tramite una connessione PCI-express a larghissima banda. Non sono stati dati ulteriori dettagli perché non è questo il core del corso.
TPU
Come detto, le CPU sono inizialmente nate per la grafica e, nonostante non siano “tuttofare” come le CPU, sono comunque dei componenti abbastanza general-use (limitatamente all’ambito del processamento parallelo). Ma possiamo fare di meglio? Possiamo creare un componente specificatamente progettato per effettuare il training di reti neurali?
Questa è l’idea alla base delle TPU (Tensor Processing Units), nelle quali Google ha:
- limitato il problema del collo di bottiglia CPU_cache -> GPU_VRAM sostituendo la VRAM con una HBM (High Bandwidth Memory),
- incrementato il quantitativo di SRAM così da poter salvare i dati più vicino alle unità di calcolo,
- sostituito (almeno parzialmente) le ALU “semi-generaliste” delle GPU con delle MAC (Multiply - ACcumulate), unità ancor più specializzate (in termini di efficienza e velocità) in calcoli di moltiplicazione-somma, fondamentale per il training delle reti neurali.
Questi accorgimenti permettono di ottenere un throughput molto maggiore a parità di area ed energia spesa.
FPGA
Tutto molto bello. Ma se volessimo programmare un chip senza costruirlo noi stessi? Costruire un chip è molto costoso: è necessario progettarlo e rivolgersi ai (pochi) produttori di chip miniaturizzati per farlo realizzare, spendendo tempo e soldi. Le FPGA (Field Programmable Gate Array) propongono un’alternativa a questo processo: invece di fabbricare un chip su misura, si prende un dispositivo riconfigurabile composto da CLB (Configurable Logic Blocks) e una rete di interconnessione programmabile. L’utente descrive l’hardware con VHDL/Verilog riconfigurando il chip.
La flessibilità di progettazione è estrema: si possono creare pipeline su misura e formati numerici specifici. Inoltre, in molti casi real-time e streaming le FPGA offrono latenza molto bassa e buona efficienza per watt rispetto a CPU e, per carichi ben strutturati, anche rispetto a GPU.
Il prezzo da pagare è la complessità di programmazione: serve un flusso di sintesi non banale, tempi di compilazione lunghi e conoscenze hardware. Inoltre, l’uso è meno flessibile: se CPU e GPU sono dispositivi general-purpose, una volta programmata un’FPGA la si può usare “solo” per quel compito (anche se alcune famiglie supportano la riconfigurazione parziale, che permette di cambiare parti del design a runtime).
In generale, questo tipo di componente è ottimo per streaming a bassa latenza, pipeline di inferenza o per costruire prototipi prima di una eventuale versione ASIC (Application Specific Integrated Circuit).
Archiviazione dei dati
Parliamo ora della tendenza che l’essere umano ha di < accumulare cose > .
Essendo io uno studente, sono al momento povero e gli unici possedimenti che ho sono un paio di tastiere con cui faccio studio, il PC tramite il quale sto scrivendo questo articolo e tanta voglia di fare. Ho bisogno di davvero poco spazio per vivere (e credetemi, l’ho testato viste le dimensioni delle camere a Milano).
Probabilmente in futuro troverò un lavoro, guadagnerò qualche soldo in più e comprerò più cose (spero non troppe di più, visto che amo vivere con un approccio minimalista), e avrò bisogno di uno spazio più grande.
Arriva sempre, nella vita di un uomo, il punto in cui le cose che hai sono troppe e devi far spazio, devi mantenere solo le cose davvero importanti. Certo, sarebbe carino poter conservare anche il portaniente comprato a Malta nella vacanza del 95 con gli amici, ma proprio non c’è posto.
Come mai sto facendo questo discorso insensato? Beh, perché è proprio quello che sta succedendo con i dati che produciamo.
L’andamento del rapporto tra la creazione dei dati e la loro memorizzazione
Negli anni 80 e 90 la maggior parte dei dati era human-generated, e la maggior parte di questi dati era memorizzata nei dispositivi degli utenti. Proprio per questo, la quantità di dati generata era molto inferiore, così come inferiore era la richiesta di strumenti di archiviazione.
Oggi invece non solo i dati degli utenti sono aumentati, ma rappresentano una minuscola parte della totalità dei dati generati. La maggior parte dell’informazione creata non è infatti human-generated, ma registrata tramite sensori o prodotta tramite AI generative. Questo ha incrementato a dismisura la necessità di spazio, che però non cresce ad un rate pari a quello dell’aumento dei dati, rendendo praticamente impossibile memorizzare tutto. Le aziende sono costrette ad effettuare un filtraggio: si salvano solo i dati più importanti o li si condensa in informazioni di più alto livello, perdendo però la possibilità di effettuare ulteriori valutazioni a più alta granularità in momenti successivi o di trainare dei modelli neurali con i dati grezzi.
Non c’è una soluzione al problema in questo momento, è semplicemente un collo di bottiglia con cui dobbiamo convivere.
Ok, ma su cosa memorizziamo i dati generati? Analizziamo i principali dispositivi di archiviazione usati nei DC.
Nastri (Tapes)
I nastri sono la prima forma di “archiviazione informatica” usata nei datacenter. Consistono in nastri magnetici avvolti in bobine sui quali i dati possono essere scritti e letti in maniera sequenziale. Ovviamente l’accesso ai dati è lentissimo: bisogna prima scorrere il nastro fino al punto corretto per poi srotolare la parte contenente l’informazione cercata.
Nonostante questo difetto, i nastri sono dispositivi ancora usati come “cold storage”, ovvero un metodo di archiviazione a lungo termine che permette di conservare i dati a freddo per lunghissimi periodi di tempo. I nastri, se conservati correttamente, sono infatti molto resistenti e possono durare anche diversi decenni. I casi d’uso possono essere diversi: backup dei dati, archiviazione di dati scientifici a lungo termine, … .
HDD
Gli Hard Disk Drive sono dispositivi di archiviazione più versatili, ad “accesso casuale” (e non sequenziale come i nastri). Sono composti da:
- Disks: sono dei dischi magnetici impilati uno sopra l’altro, che ruotano ad una velocità fissa indicata in RPM (Rotations Per Minute). Ogni disco è suddiviso in tracce (tracks) che rappresentano delle corone di raggio r sul disco, a loro volta suddivise in settori (sectors) o data blocks, l’unità di memoria più piccola che si può leggere/scrivere. Ogni settore è caratterizzato da un indirizzo univoco LBA (Logical Block Address) che permette di individuarlo. Solitamente, il sistema operativo del device a cui è connesso il disco accorpa più settori in una struttura più grande chiamata cluster, la cui dimensione può variare da 1 a molti settori e che è quindi l’unità di memoria minima che l’OS può leggere/scrivere. Attenzione: è necessario valutare bene la dimensione di sectors e sopratutto clusters: quando l’OS scrive sul disco può occupare/cancellare soltanto quantità di memoria che sono multiple della dimensione del cluster. Questo vuol dire che, se un dato ha una dimensione (N-1) * cluster_size < file_size < N * cluster_size, si spreca uno spazio pari a N * cluster_size - file_size, dando vita al fenomeno della frammentazione interna (internal fragmentation) per cui, con l’aumentare delle letture e scritture sul disco, aumenta la quantità di memoria sprecata.
- Arm, R&W head: il braccio mobile permette di spostarsi sul disco rotante e di leggere il settore desiderato attraverso la testina montata sulla sua punta.
Ok, abbiamo capito a questo punto il processo generale: il disco gira, la testina si posta sulle diverse tracce, la lettura del settore avviene quando la posizione del braccio e la rotazione del disco fanno si che la testina si trovi esattamente su di esso. E’ un processo in gran parte meccanico, e per tale motivo ci sono latenze non trascurabili da valutare nell’accesso ai dati. Vediamo quindi come creare un modello semplificato che ci permetta di calcolare il tempo medio di accesso ai dati date le caratteristiche principali di un disco.
Possiamo considerare il tempo di accesso ai dati di un HHD come somma delle seguenti componenti:
- Seek delay -> è il tempo che il braccio impiega a spostarsi sulla traccia corretta del disco. Consideriamo:
- Rotation delay -> consiste nel tempo necessario al disco per spostare il settore sotto la testina. Lo consideriamo pari a:
\(t_{rotat.}^{avg} = 60/2 * \text{RPM} \text{ [secondi]}\)
- Data transfer time -> è il tempo di lettura e trasferimento vero e proprio, spesso espresso in \(Mb/s\). (Nota: ricorda che il fattore di conversione tra Mb, Kb, … è 1024).
- Control overhead -> prima di accedere ad un settore, è necessario attendere che vengano eseguite le “operazioni propedeutiche” alla lettura/scrittura da parte del controller del disco. Spesso trascureremo questo fattore, poichè dominato dalle altre componenti.
Il tempo di accesso è dominato dai primi due ritardi. Ci sono però casi in cui questi ritardi sono trascurabili e la lettura dei dati è molto più veloce: si tratta dei casi in cui la lettura è sequenziale (i dati sono posti su settori contigui). Proprio per questo motivo si introduce il concetto di locality, ovvero la percentuale di dati che è organizzata in maniera contigua tra quelli a cui vogliamo accedere.
In generale abbiamo che:
- Tempo di accesso senza locality (accesso casuale) ->
\(t_{no\_loc} = t_{seek}^{avg} + t_{rotat.}^{avg} + t_{transf.} + t_{contr. ovh.}\)
- Tempo di accesso considerando locality completa (accesso sequenziale) ->
Solitamente, non si ha locality pari a 0% o 100%, bensì una pecentuale intermedia. In tal caso, il tempo di accesso ai dati si può approssimare come
\[t = t_{loc} * X\% + t_{no\_loc}*(1-X\%)\]Questo concetto ci porta a definire un’altro tipo di frammentazione: l’External Fragmentation. A differenza della frammentazione interna, questo tipo di frammentazione non comporta uno spreco di memoria, bensì una perdita di prestazioni nella lettura e scrittura quando la locality è troppo bassa (poiché i dati sono “frammentati” e si devono effettuare tante rotation + seek per leggere tutti i chunks presenti sul disco e ricostruire l’intera informazione).
Il tempo appena calcolato è chiamato service time, ed è quello che il disco impiega a soddisfare una richiesta di lettura/scrittura (per semplicità le consideriamo di uguale durata media). Spesso però il sistema operativo deve gestire più richieste di accesso allo storage, per cui queste vengono messe in una coda. La somma tra il tempo necessario affinché la richiesta R venga avviata (arrivi il suo “turno” nella coda) e il tempo che il disco impiega a soddisfarla (service time) è detta response time.
Ci sono 4 principali approcci per gestire le code di richieste W/R, ovvero il disk scheduling:
- FCFS (First Come First Served): è in pratica una semplice coda FIFO, in cui non si cambia l’ordine delle richieste. Ok se vogliamo un approccio veramente semplice, ma il peggiore dal punto di vista della velocità di accesso.
- SSTF (Shortest Seek Time First): si cerca di minimizzare il tempo di accesso ai dati riordinando la queue in modo che vengano eseguite per prime le richieste sui settori vicini alla posizione attuale della testina. Questo approccio riesce effettivamente a diminuire il tempo di accesso, ma ha un grave problema: se una richiesta R riguarda un settore molto lontano dalla posizione corrente della testina e continuano ad arrivare richieste a settori più vicini, R non verrà eseguita per molto tempo. Per risolvere ciò si è pensato alle successive soluzioni.
- SCAN: l’idea è semplice: la testina effettua un movimento di lettura fisso, dalla traccia più interna a quella più esterna per poi leggere nel verso opposto. Le richieste vengono riordinate per assecondare questo movimento. Si riesce in questo modo ad eliminare il problema “starvation” della soluzione precedente, al costo di una latenza leggermente maggiore. Questo approccio ha però una caratteristica che non sempre è accettabile: la parte centrale del disco viene servita più spesso di quelle esterne, rendendo la coda non “fair”. Per risolvere ciò si è pensato alla soluzione successiva.
- C-SCAN: invece che leggere sia “in andata” che “al ritorno”, nel C-SCAN la testina esegue un movimento di lettura dalla traccia più interna a quella più esterna, per poi effettuare un rientro velocissimo in cui non si legge. Il ciclo ricomincia. In questo modo si ottiene una coda “fair” in cui tutti i settori hanno la stessa probabilità di essere serviti per primi, al prezzo di un’ulteriore aumento della latenza.
- C-LOOK: è una C-SCAN che vuol sembrare più furba. L’unica differenza con l’approccio della riga sopra è che la scansione non parte dalla traccia 0, bensi dalla traccia più piccola in quel momento presente nella coda delle richieste. Il vantaggio è ovviamente che si accorcia il movimento di ritorno, limitando potenzialmente la latenza. L’aspetto negativo è che se contemporaneamente al movimento di rientro della testina viene effettuata una richiesta su un settore inferiore a quelli precedentemente presenti nella queue, essa verrà ignorata fino alla scan successiva.
Lo scheduling del disco può essere implementato sia a livello dell’OS che nel controller dell’HHD.
Un’ulteriore modalità per tentare di diminuire i tempi di accesso ad un HHD è il caching, ovvero l’inserimento di una memoria veloce (DRAM o SSD) nel case del dispositivo. In tale memoria saranno salvati i dati con più probabilità di accesso da parte dell’utente (rule of thumb: l’utente accede al 10% dei dati il 90% delle volte), così che l’impressione sia quella di avere una memoria molto capiente a velocità molto elevata. Ancora una volta, nuovo approccio = nuovi problemi da risolvere. In questo caso l’intoppo è che in caso di scrittura, l’utente riceve la conferma del successo dell’operazione quando i dati sono scritti nella cache, ma non per forza il trasferimento cache -> disco è stato anch’esso completato. Se in questo contesto l’alimentazione dell’HHD viene spenta, il dispositivo rimane in uno stato non consistente in cui i dati della cache vengono persi (nel caso si usi una DRAM). Proprio per evitare ciò si incorpora in questo tipo di dischi anche una batteria tampone, capiente abbastanza da far si che il trasferimento dalla memoria veloce a quella stabile termini anche in caso di mancata alimentazione esterna. Il side-effect di questo provvedimento è l’aumento del prezzo del dispositivo.
SSD
I Solid State Drive sono pensati come alternativa ad alte prestazioni per gli HHD. A differenza di questi ultimi, il loro funzionamento non si basa su parti meccaniche mobili, bensì su chip di memorie NAND.
“L’atomo” che compone una memoria SSD è la cella, il dispositivo che è effettivamente in grado di mantenere l’informazione nel tempo. Ogni cella mantiene una carica, ed è proprio leggendo questa carica che si estrae l’informazione. Vi sono diversi approcci di memorizzazione/lettura su cella (in ordine cronologico):
- Single Level Cell (SLC): è il metodo più costoso e performanete, nonché il primo ad essere ideato. La carica è quantizzata a due soli livelli, per cui ogni cella contiene solo 1bit di informazione (0 o 1).
- Multi Level Cell (MLC): per abbassare i costi, si è pensato di quantizzare la carica della singola cella a 4 livelli, potendo quindi salvare 2 bit per cella. Questo diminuisce la quantità di celle necessarie per memorizzare lo stesso quantitativo di dati e di conseguenza abbassa il prezzo, al costo dell’introduzione di un piccolo overhead dovuto alla necessità di decodificare l’informazione.
- Triple Level Cell (TLC): si aumenta il numero di livelli di quantizzazione a 6 così da poter contenere 6 bit di informazione (lo so, la nomenclatura multi=2 e tri=3 non è la più felice mai ideata). In questo modo si abbassano ulteriormente i costi. Il problema è però che bisogna essere molto precisi nello scrivere/leggere le soglie di carica: aumentando i livelli di quantizzazione, aumenta la probabilità di imprecisione in lettura/scrittura.
Tutte e 3 le soluzioni sono oggi sul mercato e l’acquirente sceglie quella che preferisce a seconda del caso.
Da una prospettiva di più alto livello, la memoria di un SSD è organizzata così:
- SSD blocks: è il quantitativo minimo di memoria che è possibile cancellare. (Nota: puoi scrivere un file in una cella solo se essa è “erased”). Ogni blocco contiene molte pages.
- SSD pages: le pagine sono la minima unità di memoria leggibile/scrivibile. Ogni page è caratterizzata da uno dei 3 stati: ERASED, VALID, INVALID.
Si, negli SSD i blocchi contengono le pagine e non viceversa.
Il mismatching tra la minimima quantità di r/w e la minima quantità di cancellazione crea un grosso problema: in teoria la velocità di lettura e scrittura di un SSD sarebbe molto più alta di quella di un HHD, vista l’assenza di parti meccaniche mobili, tuttavia ciò che avviene nella realtà (nel caso non si prendano le precauzioni che verranno spiegate successivamente) è che, dopo un pò di scritture, le prestazioni dei due dispositivi si allineano. Ma come mai avviene questo fenomeno?
Il problema è che, come detto, non possiamo scrivere su una cella finché essa non è ERASED. Se poniamo di avere un blocco costituito da 5 celle delle quali 4 sono VALID e una è INVALID e di voler scrivere un dato sulla pagina INVALID (contenente il dato flaggato come non più utile) non possiamo semplicemente sovrascriverla come facevamo sugli HHD (limite imposto dall’hardware), ma dobbiamo prima cancellarne il contenuto (effettuare l’ERASE). Essendo il Block l’unità minima cancellabile, si rende necessaria la seguente sequenza di passi:
- leggere e copiare tutto il contenuto del blocco su una qualche sorta di cache;
- eliminare dalla cache la pagina INVALID;
- cancellare l’intero blocco così che la pagina target si liberi;
- riscrivere nel blocco tutte le 4 pagine precedentemente VALID + la nuova.
Come è facilmente intuibile, tutto questo genera un ritardo enorme nel completamento delle richieste.
Ma la latenza non è l’unico effetto negativo di questo fenomeno (chiamato write amplification): le celle di memoria hanno un numero di cicli di scrittura limitato, dopo il quale smettono di funzionare correttamente (non è più possibile memorizzare nuovi dati, è come se diventassero memorie read-only). Il write amplification problem accorcia di molto il lifetime dell’SSD andando a consumare il numero di scritture disponibili nelle celle di memoria.
Pensate che i problemi siano finiti qui? Neanche per sogno. Gli SSD sono stati creati per essere visti dall’OS come se fossero degli HHD, ma sorpresa sorpresa: NON SONO DEGLI HHD :D
Il write amplification problem è co-causato dal fatto che l’OS si “rivolge” all’SSD usando i comandi “read sectors” e “write sectors” tipici degli HHD (dato che negli HHD i dati non vengono effettivamente eliminati, ma flaggati come non validi e poi sovrascritti), mentre il “linguaggio che il Solid State Disk comprende” è composto dai comandi “read”, “program” ed “erase”.
Inoltre l’OS usa l’SSD considerando la suddivisione in settori, quando in realtà il dispositivo è organizzato in blocchi e pagine. E’ quindi necessario un livello intermedio che permetta di adattare le due interfacce, e tale livello è chiamato Flash Transation Layer ed è integrato all’interno dell’SSD. Questo componente si occupa del mapping tra gli indirizzi virtuali dell’OS e quelli reali della memoria flash, che può essere effettuato a diversi livelli di granularità:
- Page level mapping: permette di avere una corrispondenza sempre immediata, ma la tabella delle pagine può arrivare ad occupare 1GB per ogni TB di memoria di archiviazione, il ché potrebbe essere scomodo.
- Block based mapping: invece che mappare a pagine, mappiamo a livello di blocco. Questo permette di ridurre moltissimo la dimensione della tabella di corrispondenza, ma aggiunge un overhead in scrittura (se cambia anche una sola pagina, bisogna gestire un intero blocco).
- Hybrid mapping: in questo caso si tiene in memoria la tabella block based, ma per i dati con accesso più frequente si ha una sottotabella con mappatura page level.
- Page mapping + caching: consiste nel mantenere l’approccio page level, ma caricare in DRAM solo la porzione di tabella usata al momento (o quella relativa ai dati più frequentemente usati).
Ritornando al write amplification problem, per per limitare i problemi che ne derivano si è pensato di implementare due ulteriori componenti:
- Garbage collector: è un componente che, tramite processi a bassa priorità, si occupa di “accorpare” le pagine VALID nel “minor numero di blocchi” in modo da poter effettuare l’erase delle pagine con i blocchi INVALID e permettere ad una successiva richiesta di scrittura di essere eseguita senza overhead. Notare che queste operazioni non sono gratis: costano in termini di vita dell’SSD, dato che si aumenta il numero di scritture sulle celle.
Nota: il garbage collector assume di conoscere quali siano le pagine cancellate. Il problema è che i vecchi file systems non cancellavano realmente i dati, ma semplicemente ne eliminavano i metadati così che se ne “perdesse traccia”. Tanto poi sugli HHD bastava sovrascrivere. E’ stato necessario introdurre il comando SATA trim per permettere agli OS di effettuare l’eliminazione (segnalare come INVALID).
- Wear levelling: come detto, i cicli di scrittura delle celle sono limitati. Tuttavia le celle non sono tutte usate allo stesso modo: ci saranno dati acceduti e sostituiti molto più rapidamente di altri, che non verranno mai cancellati. Le celle che contengono i primi saranno molto più consumate delle seconde. Ecco, il wear levelling consiste semplicemente nell’invertire i due tipi di dati di posto: porre i files spesso sovrascritti in celle nuove, mentre i dati “cold” in celle già usurate, così da livellare l’uso dell’SSD ed estenderne la vita.
Ma con tutti sti problemi, quanto può durare un SSD? E’ difficile dare una risposta: tutto dipende dal tipo di uso, dalla tecnologia usata e dalle condizioni esterne. In generale, può durare da pochi mesi (in casi irrealisticamente pessimi in cui si continua a sovrascrivere tutto continuamente) fino a decenni.
RAID
Come ottenere una storage caratterizzata da più capienza, prestazioni o/e reliability? E’ proprio questo l’intento alla base dei RAID (Redundant Array of Inexpensive/Indipendant Disks).
L’idea è quella di prendere più dischi, metterli “insieme” dentro un “case” dotato di un controller che espone all’esterno l’interfaccia tipica di un singolo dispositivo di storage, ottenendo (a seconda del tipo di RAID usato):
- più memoria, data dalla somma delle capienze delle singole storage;
- più ridondanza (copia su più dischi in contemporanea) e quindi più reliability;
- più velocità grazie alla possibilità di dividere i files in chunks e scriverli in parallelo su più dischi.
In realtà non è possibile ottenere il massimo di queste caratteristiche usando un solo tipo di RAID; ogni variante ha dei pregi e dei difetti che la rendono più adatta ad un caso e meno ad altri.
Nota: nelle successive valutazioni useremo la lettera S per indicare la velocità di lettura/scrittura sequenziale di un singolo disco, R per indicare la velocità di scrittura/lettura randomica di un singolo disco, X per indicare la capienza di un singolo disco.
RAID 0
E’ la variante a “prestazioni massime”. Consiste nella parallelizzazione di N dischi e nella suddivisione dei dati in chuncks, ovvero piccoli pezzetti da leggere/scrivere in parallelo (N alla volta) sui dischi.
Quello che si ottiene in questo modo è una velocità di lettura/scrittura sequenziale e randomica che è pari a SN e RN, ma una reliability molto bassa, misurabile tramite il parametro del Mean Time To Fault, ovvero il tempo medio prima che un fault del sistema avvenga (perdita di dati permanente), che nel caso dei RAID 0 è pari a
\[MTTF_{RAID_0}=MTTF_{single}/N\]La capienza della memoria complessiva è invece $XN$ (la massima possibile*).
RAID 1
Noi però siamo persone ansiose e non ci piace vivere con la paura di perdere i nostri dati: vogliamo che siano in una botte di ferro! Per far ciò, la via più semplice (ed efficace, dal solo punto di vista della reliability) è il RAID 1.
Il concetto è semplice: abbiamo N dischi in parallelo ed ogni volta che scriviamo qualcosa, la scriviamo su tutti i dischi contemporaneamente. In questo modo si ottengono N copie, per cui il Mean Time to Fault è pari a
\[MTTF_{RAID1} = \dfrac{MTTF_{singleDisk}^2}{2 \cdot MTTR}\]Da qui in poi però, dati N dischi, consideriamo di effettuare la duplicazione solo una volta (solo due copie). In questo modo si avranno N/2 dischi contenenti la copia_1 e gli altri N/2 dischi con la copia_2 (magari in striping = RAID 0).
In questo caso, le velocità di lettura e scrittura sono:
- Random read: se le letture sono randomiche, possiamo parallelizzarle su tutti i dischi e quindi ottenere una velocità di lettura pari a:
E’ l’ipotesi d’uso ideale per il RAID 1;
- Sequential read: se invece le letture sono sequenziali, possiamo utilizzare solo una delle due copie (e quindi $N/2$ dischi) per cui si ha un througput dimezzato:
\(R_{RAID_1} = (N/2) * R_{single}\)
- Random/sequential write: in entrambi i casi dobbiamo scrivere due volte lo stesso dato, per cui si ha:
e
\[R_{RAID_1} = (N/2) * R_{single}\]Possiamo concludere che il RAID 1 è un’ottima soluzione per quanto riguarda reliability e nei casi in cui ci sono poche scritture sui dischi e molte letture randomiche, mentre performa peggio negli altri casi. Notare che perdiamo metà della capienza di memoria disponibile.
RAID 0+1
Mixando le due precedenti soluzioni possiamo ottenere un sistema sicuro ma che non rinunci ad un boost prestazionale. Ci sono due possibili combinazioni, e la prima che analizziamo è il RAID 0+1.
Consideriamo di avere N dischi: effettueremo “dapprima” la duplicazione della memoria (RAID 1) ottenendo due copie costituite da N/2 dischi, e su di essere scriviamo i dati in striping (RAID 0). Avremo quindi due copie di N/2 dischi con striping.
Se per esempio avessimo 6 dischi, otterremmo 2 copie (controllate da un controller RAID 1) da 3 dischi ciascuna. All’interno di ognuna delle copie vi è memorizzata l’interezza dei dati, organizzati in 3 dischi di stripes (i 3 dischi all’interno di una delle pertizioni sono gestiti da un controller RAID 0).
Il sistema è resistente alla perdita di 1 disco, poiché quando ciò succede il sistema è ancora in grado di usare la seconda copia dello striping. Tuttavia, in tale condizione il sistema diventa un RAID 0 perché il controller RAID 0 che gestisce il trio di dischi incriminato dice “bye bye”.
\[MTTF_{RAID0+1} = \dfrac{MTTF_{singleDisk}^2}{N^2 \cdot G \cdot MTTR} \text{, dove} \begin{cases} G=\text{numero di duplicazioni RAID1;} \\ N= \text{numero di stripes per duplicazione.} \end{cases}\]RAID 1+0
La seconda combinazione è caratterizzata dall’inversione delle due tecniche: prima si effettua lo striping dei dati su N/2 dischi (RAID 0), poi si duplicano i singoli stripes (RAID 1).
Nel caso di un sistema composto da 6 dischi, si avrebbero 3 stripes di dischi (gestite da un controller RAID 0), ognuna contenente la duplicazione di un disco di stripe (la duplicazione è gestita da un controller RAID 1 per ogni stripe).
In questo caso il sistema può sostenere fino a N/2 guasti di dischi! Poniamoci infatti nel caso dell’esempio sopra: se si rompesse un disco per ognuno dei 3 stripes, i controller RAID 0 renderebbero comunque disponibili i dati attraverso l’altro disco. Quello appena illustrato è però ovviamente un caso fortuito: se due dei dischi guasti appartenessero allo stesso “gruppo stripe”, i dati verrebbero comunque compromessi irrimediabilmente.
Nonostante ciò, la possibilità di resistere a più guasti rende la soluzione RAID 1+0 in generale migliore rispetto alla 1+0: le prestazioni e la capienza sono praticamente identiche, ma la reliability è più alta nella configurazione 1+0.
\[MTTF_{RAID1+0} = \dfrac{MTTF_{singleDisk}^2}{N \cdot G \cdot MTTR} \text[, dove ] \begin{cases} G=\text{numero di duplicazioni RAID1;} \\ N= \text{numero di stripes per duplicazione.} \end{cases}\]RAID 4
Bella la sicurezza, ma sarebbe cosa gradita anche il non perdere metà della nostra capacità di archiviazione per ottenerla. Cosa possiamo migliorare nella nostra strategia?
Il RAID 4 nasce proprio per questo: dati N dischi, se ne usano N-1 per contenere i dati (striping, similmente a RAID 0) ed 1 per memorizzare la parità. Ma in che senso parità?
In pratica si calcola un’operazione (es. XOR) usando come dati tutti i “chunks dello stesso livello” presenti sugli N-1 dischi, e si memorizza il risultato sull’N-esimo disco. In questo caso, immaginando di avere 5 dischi, e considerando che i primi chunk dei dischi di memoria siano 1,0,0,1, allora il disco di parità conterrà per quel livello di chunks il dato \(1+0+0+1=0\). Questo permette, nel caso di malfunzionamento di un disco, di recuperare i dati effettuando l’operazione inversa. Immaginiamo per esempio che il disco 3 si rompa, avendo nella nostra archiviazione \(1+0+...+1=0\); in tal caso è facile calcolare che il dato mancante è:
\[0-(1+0+1)=0\]La capienza del sistema in questo caso diventa $N-1$ e i dati sopravvivono alla perdita di un singolo disco. Le prestazioni sono:
- Sequential/random read: ottime, sono pari a:
e
\(R_{RAID_4} = (N-1) * R_{single}\)
- Sequential write: anche in questo caso ottime prestazioni. Siccome scriviamo solo su “un livello alla volta” dei chunk dei dischi, possiamo scrivere in parallelo su tutti e 4 i dischi e poi memorizzare un solo risultato di parità sul disco di parità. In pratica le prestazioni sono:
\(S_{RAID_4} = (N-1) * S_{single}\)
- Random write: ed è qui che arrivano i problemi. Se scriviamo su 4 livelli random dei nostri 4 dischi, su ogni livello dovrà essere calcolato il risultato di parità ed esso dovrà essere memorizzato sul disco di parità. Questo vuol dire che possiamo anche parallelizzare le 4 scritture random, ma poi avremo 4 ulteriori scritture concorrenti sul disco di parità, rendendo quest’ultimo un grave collo di bottiglia. In pratica si ottengono prestazioni pari a:
RAID 5
La soluzione al grave problema di scrittura randomica precedente è la distribuzioni dei bit di parità su tutti i dischi. Il RAID 5 fa questo.
In tal modo, anche nel caso di scrittura randomica si avrà una naturale distribuzione del carico di write sui 5 dischi. Ciò che si ottiene sono delle prestazioni migliorate:
- Sequential read/write: invariate rispetto al RAID 4, quindi:
e
\(R_{RAID_5} = (N-1) * R_{single}\)
- Random read: in questo caso, leggendo in maniera randomica si potrà spesso parallelizzare su tutti i dischi invece che su N-1, dato che i bit di parità sono distribuiti. Ciò porta ad avere che in lettura:
\(R_{RAID_5} = (N) * R_{single}\)
- Random write: per quanto riguarda la scrittura non sequenziale, non si avrà più il collo di bottiglia dovuto al singolo disco di parità. Per ogni scrittura è però necessario effettuare: 1) lettura del dato che vogliamo sostituire; 2) lettura del bit di parità; 3) scrittura del nuovo dato; 4) aggiornamento del bit di parità. Questo vuol dire che la velocità di scrittura è:
RAID 6
In pratica come il RAID 5, ma si usano 2 “dischi distribuiti” di parità, potendo così sostenere la perdita di due dischi.
\[MTTF_{RAID6} = \dfrac{2 \cdot MTTF_{singleDisk}^3}{N \cdot (N - 1) \cdot (N - 2) \cdot MTTR^2}\]Riassunto “matematico” della reliability dei vari sistemi RAID
Considerando la MTTF = MeanTimeToFailure e la MTTR = MeanTimeToRepair, abbiamo le seguenti formule:
| Schema RAID | Formula MTTF |
|---|---|
| $RAID_{0}$ | $MTTF_{RAID0} = \dfrac{MTTF_{singleDisk}}{N}$ |
| $RAID_{1}$ | $MTTF_{RAID1} = \dfrac{MTTF_{singleDisk}^2}{2 \cdot MTTR}$ |
| $RAID_{0+1}$ | $MTTF_{RAID0+1} = \dfrac{MTTF_{singleDisk}^2}{N^2 \cdot G \cdot MTTR}$ |
| $RAID_{1+0}$ | $MTTF_{RAID1+0} = \dfrac{MTTF_{singleDisk}^2}{N \cdot MTTR}$ |
| $RAID_{4}$ | $MTTF_{RAID4} = \dfrac{MTTF_{singleDisk}^2}{N \cdot (N - 1) \cdot MTTR}$ |
| $RAID_{5}$ | $MTTF_{RAID5} = \dfrac{MTTF_{singleDisk}^2}{N \cdot (N - 1) \cdot MTTR}$ |
| $RAID_{6}$ | $MTTF_{RAID6} = \dfrac{2 \cdot MTTF_{singleDisk}^3}{N \cdot (N - 1) \cdot (N - 2) \cdot MTTR^2}$ |
Connessione server <-> storage
Fino ad adesso abbiamo parlato di storage come oggetto “isolato”. Ovviamente, un HHD/SSD/RAID sarebbe inutile se non connesso ad un dispositivo che lo usa. Ci sono diversi modi per effettuare tale connessione.
DAS (Direct Attached Storage)
E’ la classica soluzione di “attaccare” direttamente il dispositivo di memoria al server. Dal sistema operativo, il file system mostrerà lo storage come un disco a cui è possibile accedere in maniera completa. Il vantaggio di questa soluzione è la facilità di implementazione e la bassa latenza, ma la condivisione di files tra diversi server è in questo modo limitata (certo, potremmo copiare tutto su una chiavetta e passarlo su un altro server, ma sarebbe inefficente e irrealistico per un DC).
NAS (Network Attached Storage)
Una soluzione migliore per la condivisione di files è usare un NAS, ovvero uno o più dispositivi di memoria connessi alla rete ed accessibili tramite la rete. Questo tipo di condivisione è “file based”, nel senso che il sistema operativo non vede dei dischi accessibili, ma dei files condivisi tramite rete (il file manager sta “dietro” alla rete). Può essere una soluzione “economica” e semplice da implementare, ma ha un grosso svantaggio: nel caso di congestione di rete nel DC (magari in un momento di alta richiesta), l’accesso ai files potrebbe essere molto rallentato (visto che i files viaggiano sulla stessa rete dei pacchetti IP).
E’ una soluzione adatta a piccoli datacenters.
SAN (Storage Area Network)
In questo caso, invece di connettere lo storage tramite ethernet implementiamo una rete apposita per l’archiviazione (fibra ottica), separata dalla rete internet. In questo caso il file system si trova “davanti” alla rete e i dispositivi di memoria sono visti come dischi connessi direttamente ai server.
L’accesso ai dati è più robusto rispetto ai NAS e la scalabilità è maggiore, così come anche il costo implementativo. In generale è più adatto a grandi datacenters.
Nota: se in un datacenter è implementata una NAS o una SAN, ciò non vuol dire che non vi siano anche delle DAS! Per esempio si potrebbe implementare una NAS e, nel caso un server debba effettuare operazioni su dei dati presenti nella memoria remota, questi potrebbero essere prelevati tramite la rete, copiati nella DAS una singola volta e acceduti più volte con latenza più bassa e minor dipendenza dallo stato corrente della rete.
Networking
Abbiamo tanto parlato di rete, è arrivato il momento di analizzare come è effettivamente strutturata la rete interna di un datacenter. Ma che cos’è un pacchetto IP?! È fisicamente un pacchetto o no?!
Questi e altri dubbi saranno risolti nel paragrafo che segue.
Concetti fondamentali
E’ importante per prima cosa chiarire i seguenti concetti:
- North<->South traffic: consiste nel traffico di rete “verticale”, quello che entra ed esce dal datacenter, proporzionale al numero di richieste e pacchetti che provengono dall’esterno e che vengono inviati verso fuori dai servers. Questo tipo di traffico dati era predominante nella prima era di internet, quando i server erano per lo più macchine “indipendenti”.
- East<->West traffic: è il traffico “orizzontale”, quello dovuto alla comunicazione tra servers appartenenti allo stesso database. Ma come mai dovrebbe esistere una comunicazione orizzontale all’interno del DC? Beh, abbiamo già visto che con la nascita del Warehouse Computing interi datacenters sono dediti all’erogazione di un singolo servizio, e questo vuol dire che più server eroganti diversi sottoservizi devono comunicare tra loro affinché il compito complessivo venga eseguito. Notare che, per ogni richiesta dall’esterno, spesso si instaurano più comunicazioni tra server interni al DC e quindi oggi il traffico East-West è molto più fitto del corrispettivo North-South.
- Bisection bandwidth: immaginiamo di avere N servers interconnessi tra loro. Vogliamo dividere questo iniseme di servers in due, “bisectionandolo”. Immaginando di farlo nel punto con banda più stretta (il bottleneck), la banda che otteniamo in tal punto è proprio la bisectional bandwidth.
- Oversubscription: poniamo di avere un rack contenente diversi server. Come detto precedentemente, in cima ai rack è solitamente predisposta un’area per moduli utili ai servers, come per esempio un router. Ecco, al router sono connessi tutti i server del rack. Consideriamo ora le due connessioni downlink = connessione dall’esterno verso i servers e uplink = connessione dai server verso l’esterno (in direzione degli altri router o di internet). L’oversubscription consiste nel predisporre più banda in downlink rispetto alla banda uplink. Se per esempio abbiamo una banda complessiva di 20Gb in downlink e di 10Gb in uplink, l’oversubscription è di 1:2 (uplink : downlink). E’ utile per abbassare i costi implementativi e per non sprecare risorse, ma deve essere progettato con cura perché, nel caso di molti pacchetti verso l’esterno, il router diventerebbe un collo di bottiglia importante.
Chiariti questi concetti, possiamo passare all’analisi delle varie architetture di connessione nei DC. In generale, si usano 2 possibili approcci: soluzioni router-centriche in cui i link della rete sono i router, che collegano i diversi nodi; soluzioni server-centriche in cui sono i server stessi ad agire come router, integrando network interfaces multiple per connettere più nodi ed effettuare il processo di reindirizzamento dei pacchetti, addizionalmente ai processi di calcolo “classici”.
Ci concentreremo principalmente sul primo tipo (anche se verranno citati “superficialmente” anche esempi delle altre categorie).
Three Tier Architecture
Router Centric Architecture
E’ l’architettura classica dei vecchi datacenter. Si compone in 3 livelli ben adattabili alla struttura fisica dei DC. Partendo dal basso e quindi dai singoli server, abbiamo:
- Access Level Switches: i servers di un singolo rack sono tutti connessi allo stesso router, posto alla cima della torre e chiamato TOR (Top Of Rack) router. E’ questo l’Access Level Switch. Si tratta di un router di banda modesta, proporzionata al relativamente piccolo numero di dispositivi da gestire (singolo rack).
- Aggregation Level Switches: il livello intermedio è composto da un insieme di router aventi maggior portata, che aggregano diversi Access Level Routers. Spesso, la posizione fisica dei router di aggregazione è un rack dedicato posto alla fine del corridoio. Per tale motivo si parla di EOR (End Of Row) servers.
- Core Level Switches: il livello più alto è costituito da uno o più Core Router a cui sono connessi tutti gli aggregation switches. I router di questo livello hanno banda larghissima e gestiscono la connessione tra l’esterno del DC e i servers al suo interno.
Tutto molto bello e ordinato. C’è solo un problema: l’architettura Three Tier è ottima per gestire il traffico North<->South e per questo era l’approccio dominante in passato, ma non è altrettanto performante quando il traffico East<->West aumenta. Immaginiamo infatti di voler mandare un pacchetto dal Server_A al Server_B. Nel caso perggiore, esso dovrà passare per il percorso:
Server_A -> access_switch_A -> aggregation_switch_A -> core switch -> aggregation_switch_B -> access_switch_B -> Server_B
Si può immaginare come, all’aumentare del traffico orizzontale, la pressione sui core e aggregation switches diventa enorme, e scalare l’infrastruttura per reggere tale incremento senza cambiare architettura è molto costoso.
Nota: sono spesso presenti connessioni ridondanti tra gli Aggregation Level Switches e tra i Core Level Switches per aumentare la banda e l’affidabilità della rete, al costo di una gestione del routing dei pacchetti più complessa.
EOR Architecture
Router Centric Architecture
Volendo, è possibile modificare il concetto di Three Tier Architecture appiattendolo ad una Two Tier: il livello di accesso si elimina e tutti i router di una singola riga vengono connessi all’EOR router. Si diminuisce così il numero i cavi da connettere, ma bisogna far attenzione al carico sui router di fine riga: saranno attraversati da un traffico molto maggiore.
Spine-Leaf Architecture
Router Centric Architecture
E’ uno degli approcci oggi più usati nei datacenter odierni. E’ un’architettura presa in prestito dal mondo della telefonia in cui si usano solo due livelli:
- Leaf Layer: è quello che precedentemente chiamavamo access layer, ovvero il TOR router.
- Spine Layer: è il livello più elevato, che aggrega più Leaf Layer.
La differenza rispetto all’approccio Three Tier sta nel fatto che nello Spine Leaf tutti i Leaf sono connessi con tutti gli Spine.
Facciamo un esempio. Leaf Layer: abbiamo 1000 server, ognuno con banda di 10Gb. Consideriamo di avere 100 racks da 10 server ciascuno, e quindi ogni TOR router gestirà 10 servers. Per non aver oversubscription (banda in entrata = banda in uscita) potremmo scegliere di usare un Leaf Switch avente 10 porte in downlink e 10 in uplink, tutte da 10Gb l’una.
Spine Layer: a questo punto, considerata la struttura di rete, ci troviamo con 100 leafes aventi ognuna banda complessiva in uscita di \(10*10Gb=100Gb\). Vogliamo scegliere la struttura del nostro Spine Layer: il totale di banda da supportare è \(100*100Gb=10.000Gb\). Potremmo gestirla in diversi modi.
Il *metodo classico è quello di connettere un’uscita uplink del leaf switch ad uno spine switch diverso. Vediamo come.
- Consideriamo un singolo Leaf Switch. Avendo 10 porte in uplink e volendo connettere ogni porta ad uno Spine diverso, necessitiamo 10 spines.
- Dobbiamo considerare che abbiamo 100 Leaf Switches, e su ogni spine sarà connessa una porta di ognuno dei Leafs, quindi ogni Spine avrà 100 porte.
- La banda di una singola porta dello Spine deve essere pari alla banda della porta Leaf connessa, e tale banda è 10Gb. Considerando che su uno spine abbiamo 100 porte, la banda totale su un singolo Spine sarà \(100_{\text{porte}}*10Gb = 1.000Gb\)
- Infine, la banda complessiva in uplink è ovviamente \(10_{\text{Spine_switches}}*1.000Gb = 10.000Gb\)
Un metodo alternativo può essere quello di condensare gli Spine Switches e, per esempio, usare solo 5 Spines ma col doppio delle porte, oppure 5 Spines con lo stesso numero di porte ma in cui la banda di ogni porte è doppia. Insomma, tutto per ottenere Oversubscription = 0.
I vantaggi di questa architettura sono una banda East-West molto superiore (vista la presenza di percorsi duplicati tra i nodi) e una bassa latenza (visto che, nel caso peggiore, il numero di hops che un pacchetto deve attraversare da un server all’altro è 3). Inoltre è possibile costruire l’intera infrastruttura di rete usando un singolo tipo di router, mentre nella variante Three Tier Architecture i tre livelli erano costituiti da switches di portata molto diversa. Questo permette di avere una diminuizione dei costi iniziali e una semplificazione della gestione del sistema.
Pod based Architecture / Fat three
Router Centric Architecture
Proviamo ora a rendere l’architettura Leaf-Spine più scalabile. Consideriamo inizialmente una rete di tipo Leaf-Spine e raggruppiamo la rete in sottogruppi, chiamati Pods (point Of Delivery): ogni POD sarà composto da K Leafs (rinominati Edges) e K Spines (rinominati aggregators).
Per ogni pod avremo quindi:
- K Edges switches, aventi K porte di Donwlink (\(K\) servers per ogni edge, in totale \(K*K\)) e K porte di Uplink, ognuna connessa ad un Aggregator.
- K Aggregators switches, aventi K porte di Downlink connesse ai K Edges e K porte di Uplink, che vedremo tra poco come connettere.
I Pods non sono direttamente connessi tra loro, ed è proprio qui che introduciamo un nuovo livello della rete: i Cores Switches, che connettono i Pods tra loro. Abbiamo detto che gli Aggregators hanno \(K\) porte di Uplink ed, essendo che un singolo Pod ha \(K\) aggregators, le porte di Uplink in uscita dal Pod sono \(K*K=K^2\). Ognuna di queste porte sarà connessa ad un Core Switch diverso.
Considerando di avere \(P = 2K\) Pods, il numero di porte di Uplink totale che i Core devono gestire è \(N_{\text{Pods Up. Tot.}}=K^2*2K=2K^3\).
Se consideriamo per esempio di usare Core switches aventi \(2K\) porte di Downlink, il numero di Core necessari è \(N_{\text{Core Switches}}2K^3/2K=K^2\).
Notare che avendo \(K^2\) da \(2K\) porte -> \(2K^3\) porte di Uplink da parte degli aggregators, e il numero di Uplink è uguale al numero di Downlinks nelle nostre configurazioni senza Oversubscription, per cui anche \(N_{\text{Aggr. Dwnlnk}} = N_{\text{Edg Uplnk}} = N_{\text{Edg Dwnlnk}}=N_{\text{Srvrs}}\), tutti uguali a \(2K^3\).
Possiamo intuire come in questo tipo di architettura vi siano due tipi di “traffici”:
- Traffico intra-pod: se due server sono connessi allo stesso pod, allora potranno comunicare attraverso molteplici percorsi di soli 2 hops (in pratica è esattamente come una Leaf Spines):
Server_A -> Access_A -> Aggregator -> Access_B -> Server_B
- Traffico extra-pod: se i due server sono connessi a pods diversi, allora i molteplici percorsi che li collegano saranno di 3 hops:
Server_A -> Access_A -> Aggregator_A -> Core -> Aggregator_B -> Access_B -> Server_B
In ogni caso la latenza è bassa e la struttura è molto modulare: basta aumentare il numero di Core per introdurre ulteriori Pods con la differenza che, differentemente da quanto avveniva con la classica Three Tier Architecture, qui si può costruire la rete con router omogenei e non servono Cores a banda ultra larga. Inoltre, la East-West Intersectional Bandwidth è molto superiore, rendendo questo approccio molto adatto ai datacenter odierni.
CamCube
Server Centric Architecture
Le architetture trattate fino ad ora erano tutte router-centriche. Diamo adesso una breve occhiata alle soluzioni server-centriche e, successivamente, a quelle ibride.
Partiamo con l’Architettura CamCube, un approccio basato sulla connessione diretta tra i server, disposti secondo una topologia toroidale (3D Torus Topology). Questa soluzione sfrutta il concetto di “località”, ovvero il fatto che se due server sono “vicini” nel reticolo toroidale, il percorso che li collega sarà più corto.
Si elimina così il problema di gestione dei router, semplificando la manutenzione e diminuendo i costi. Al contempo di introduce però una maggiore complessità nella gestione del routing dei pacchetti, che dovrà essere effettuato attraverso i server sui quali dovranno essere installate più NIC (Network Interconnection Cards, o qualcosa del genere).
DCell
Hybrid Architecture
Si tratta di una struttura di rete ricorsiva che usa sia connessioni server<->server che connessioni tramite commodity routers. In pratica si ha:
- \(Dcell_0\): consiste nella struttura base di questa architettura. E’ composta da \(N<8\) servers, connessi tra loro tramite un commodity router.
- \(Dcell_k\): è una struttura superiore composta da \(N-1\) \(DCell_{k-1}\).
In pratica, per aumentare le dimensioni della rete si aggiunge un livello di \(DCell\). Questo rende la rete facilmente scalabile e molto economica, al costo di percorsi generalmente più lunghi con conseguente latenza superiore.
BCube
Hybrid Architecture
Anche in questo caso si tratta di una architettura ricorsiva. Il blocco fondamentale è il \(BCube_0\), costituito da \(N\) servers connessi ad uno switch da N porte. I successivi livelli \(BCube_k\) si formano combinando \(N\) \(BCube_{k-1}\) e \(N^2\) switches da \(N\) porte.
I vantaggi di questa architettura sono una buona Bisectional BW e il fatto che le prestazioni decrementano in maniera “armonica” con il verificarsi di guasti. Al contempo, i cablaggi diventano molto lunghi e la facilità di manutenzione ne risente.
MDCube
Hybrid Architecture
E’ una variante utile a semplificare l’interconnessione di Containers (ne parleremo più approfonditamente in seguito). Ogni container è mappato ad un ID che lo associa ad una specifia multidimensional tuple. I server all’interno di uno stesso container sono connessi tramite (ovviamente) delle Intra-container connection. Le connessioni tra container diversi sono ad alta banda e sono effettuate tra server aventi *tuple-id differente per solo un bit (es. 010 <-> 011). Queste ultime connessioni sono chiamate High-speed inter-container connections.
Si forma così in reticolo simile al caso precedente.
In questo caso il numero di cavi diminuisce molto, semplificando il mantenimento del DC.
3. MODULI DI SUPPORTO ALLA PARTE DI CALCOLO
Parlando di datacenters spesso si ci concentra sui servers, sulla rete ed in generale sulla parte “calcolatrice” del sistema. In realtà, spesso meno della metà dello spazio del DC e dell’energia da esso consumata sono usate per alimentare i servers. Ma dove finisce il resto delle risorse?
Supply systems
Un modulo fondamentale per i DC è quello di alimentazione, per diversi motivi:
- il sistema complessivo consuma moltissimo, e necessita di una struttura di alimentazione sottostante in grado di erogare la quantità di energia necessaria.
- l’availability del sistema deve essere altissima, per cui il sistema di alimentazione deve essere super affidabile.
Per questi motivi, il sistema supply system è composto da sistemi di alimentazione primaria molto robusti, associati a sistemi di backup nel caso i primi abbiano malfunzionamenti. In generale, si usano due tipi di sistemi di recovery che sono tra loro complementari:
- Sistemi di riserva “immediati”: sono sistemi che si attivano subito al verificarsi di un’interruzione di corrente. In generale, la loro immediatezza ha come contro il fatto che non possano alimentare il DC per lunghi periodi di tempo in quanto l’energia che è possibile stockare in essi è limitata. Gli esempi visti a lezione sono le più intuitive batterie di emergenza che non necessitano di spiegazione, oppure i più “esotici” Rotary UPS Systems, ovvero sistemi composti da una centrifuga all’interno della quale è posta una grossa massa che funge da accumulatore di energia cinetica. Nel caso di un’interruzione di corrente, la centrifuga continuerà a ruotare a causa della conservazione della quantità di moto della massa, alimentando il sistema per un breve lasso di tempo (che strana soluzione).
- Sistemi di riserva di medio periodo: come detto, le precedenti soluzioni alimentano il DC per un breve intervallo (minuti o ore), utile per avviare dei sistemi di riserva meno immediati ma capaci di erogare energia per più tempo, come per esempio i generatori a diesel (con buona pace della sostenibilità ambientale).
Cooling systems “globali” in un datacenter
Ogni elemento del DC (servers, routers, supply systems) produce calore proporzionalmente alla densità energetica che lo caratterizza. Ciò rende necessario usare sistemi per estrarre il calore dal nostro sistema e dissiparlo verso l’esterno (con buona pace del riscaldamento globale).
A breve parleremo delle principali tecniche usate, ma prima di ciò dobbiamo chiarire un concetto: i racks in un server sono disposti in righe separate tra loro, formando dei corridoi. I corridoi non sono tutti uguali: ci sono i cold aisle, ovvero i corridoi freddi corrispondenti alla regione dalla quale sono accessibili le parti frontali dei servers, e ci sono poi gli hot aisle, ovvero i corridoi caldi sui quali si affacciano le interfacce posteriori dei racks. In pratica:
|| hot_aisle - R_server_F - cold_aisle - F_server_R - hot_aisle - - R_server_F - cold_aisle - F_server_R - ... ||
(dove con R intendo rear e con F intendo front)
Inoltre, nei DC spesso sono posti su un pavimento rialzato (raised floor) che permette di convogliare più facilmente i cavi e, come vedremo a breve, ha anche un ruolo nella dissipazione del calore.
Dal punto di vista del delle tecniche di raffreddamento, se ne usano principalmente 3:
- Open Loop Cooling: è il metodo più semplice e consiste nel raffreddare l’ambiente interno all’edifico usando l’aria esterna, appositamente filtrata da eventuali impurità. In pratica si fa entrare l’aria fredda da aperture laterali dell’edificio e, in qualche modo, si incanala l’aria calda prodotta dalle componenti verso l’alto, espellendola. E’ un sistema semplice ed economico, ma è adatto solo a piccoli datacenters che non hanno una grande densità energetica, poichè in caso contrario questo singolo open loop non è sufficiente per raffreddare efficentemente le componenti.
- Dual Closed Loop Cooling: per migliorare l’efficacia del raffreddamento si aggiunge un ulteriore componente: il CRAC. Si chiama “dual loop” poichè vi è un primo ciclo di raffreddamento in cui l’aria viene prelevata dagli hot aisle e convogliata verso il CRAC che la raffredda e, attraverso il raised floor, viene portata ai cold aisle così da raffreddare i server; il secondo ciclo di raffreddamento è quello interno ai CRAC che, essendo macchine termiche, usano processi termonidamici (espansione, compressione, …) per raffreddare un liquido che scorre in tubi al loro interno e che, a contatto con l’aria calda del DC, permette di raffreddarla. Notare che la dicitura “closed loop” deriva dal fatto che l’aria non esce mai dal datacenter. Questo può essere utile per un duplice motivo: si evita che impurità entrino all’interno del DC e si ha la possibilità di diminuire la quantità di ossiggendo presente all’interno dell’edificio, diminuendo la probabilità di incendio.
- Triple Closed Loop Cooling: un ulteriore miglioramento della dissipazione è possibile introducendo un nuovo elemento: le Cooling Towers. Si tratta di strutture di grande dimensione attraverso le quali viene fatto passare un liquido refrigerante che, a contatto con l’ampia superficie della tower, scambia calore con l’esterno raffreddandosi per poi rientrare in circolo nel sistema. Il liquido raffreddato dalla cooling tower viene posto a contatto con il liquido ancora caldo che scorre dentro i CRAC (i tubi attraverso cui i due liquidi passano sono posti gli uni vicini agli altri, ovviamente il liquido non viene “mischiato”), migliorando l’efficienza del sistema. L’altro lato della medaglia è che il costo iniziale per la costruzione dell’infrastruttura è molto più elevato.
Cooling systems per singoli racks/servers
Spesso un raffreddamento “generalista” non è abbastanza per mantenere i server al massimo delle loro capacità e si rende necessaria l’implementazioni di sistemi applicati direttamente sulle fonti di calore.
- On-chip cooling: con l’aumentare della densità energetica (pensiamo ai blade servers), applicare un sistema di dissipazione direttamente sui chip è diventato comune. E’ possibile usare soluzioni ad aria (ventole) o a liquido, con la necessità per queste ultime di una manutenzione del sistema davvero minuziosa dato che perdite di liquido del sistema di dissipazione potrebbero causare gravi danni ai servers sottostanti.
- Immersion cooling: un sistema più esotico di raffreddamento è quello della dissipazione da immersione: i servers sono posti in racks riempidi di un liquido specifico, che non interagisce con le componenti elettroniche, rendendo la dissipazione del calore ancor più efficiente. Si può immaginare che in questo caso la difficoltà implementativa aumenta ancora.
- In-rack cooling: è una soluzione più “tranquilla”, in cui il sistema di raffreddamento (ventole o liquido) è posto sul retro dei rack così da estrarre il calore abbastanza vicino alla sorgente, ma non tanto vicino da creare problemi (diretti) seri ai server in caso di malfunzionamento.
Modular Container-based Datacenters
Citiamo brevemente l’esistenza di un particolare tipo di datacenter, caratterizzato dal fatto di essere contenuto all’interno di classici containers (quelli per spedire merce, per intenderci). Si tratta di sistemi che integrano (in piccolo) tutti i moduli sopracitati, costituendo quindi veri e propri DC trasportabili “lì dove c’è necessità”.
Se per esempio volessimo abbassare la latenza di una particolare regione geografica spostando i calcoli effettuati più frequentemente su un nodo del datacenter più vicino alla sorgente della richiesta, potremmo portare un container-based DC proprio lì.
Indicatori della “qualità” di un Datacenter
Come per ogni sistema, è utile definire dei parametri di valutazione per i datacenters:
- PUE (Power Usage Effectiveness): è un indicatore di quanta energia è effettivamente usata per alimentare la “parte di calcolo” del DC e si calcola come: \(PUE=\frac{\text{Energia}_\text{tot}}{\text{Energia}_\text{calcolo}}\). Il suo inverso è l’efficienza \(\mu = \frac{\text{Energia}_\text{calcolo}}{\text{Energia}_\text{tot}}\) e misura la percentuale di energia consumata dal datacenter che è effettivamente usata per alimentare la sua “parte di calcolo”.
- Datacenter Tiers: si tratta di 4 categorie (tiers) in cui è possibile suddividere i datacenter a seconda della loro availability (approfondiremo questo concetto in seguito, ma di base si tratta della percentuale di tempo che il sistema è funzionante = \(\frac{\text{UPtime}}{(\text{UPtime}+\text{DOWNtime})}\). I tiers sono:
- Tier 1: si tratta di sistemi in cui è presente una sola catena IT ed una sola catena di almentazione e raffreddamento. Il sistema singolo è comunque molto affidabile (\(\text{av}_\text{Tier_1}>99.671%\)) ma un singolo guasto interrompe il funzionamento del sistema.
- Tier 2: si soddisfano tutti i requisiti dei Tier 1 aggiungendo però sistemi di alimentazione “generale” ridondanti (batterie di emergenza, Rotary UPS, Diesel generators, …). L’availability attesa è \(\text{av}_\text{Tier_2}>99.741%\).
- Tier 3: in questo caso i sistemi IT sono provvisti di doppia alimentazione ed è possibile effettuare la manutenzione ad ogni componente IT senza interrompere il servizio. \(\text{av}_\text{Tier_3}>99.982%\)
- Tier 4: è il livello di “affidabilità” massima, in cui si aggiunge doppia alimentazione energetica anche al sistema di dissipazione e si rende il sistema “fault tolerant”, ovvero il datacenter non interrompe un servizio con un singolo fault (questo vuol dire che al “rompersi” di un modulo, il sistema si rivolge automaticamente al corrispettivo modulo ridondante). \(\text{av}_\text{Tier_4}>99.995%\)
Attenzione: non confondere i Tiers dei datacenters con i Tiers delle Networks.
4. DEPENDABILITY
Prerequisiti
Definiamo dapprima alcuni concetti fondamentali, dei quali abbiamo già avuto un assaggio precedentemente:
- MTTF (Mean Time To Failure): è definito come l’intervallo di tempo medio prima che si verifichi il primo failure di un sistema.
- MTBF (Mean Time Between Failures): indica qual’è l’intervallo di tempo medio tra due guasti consecutivi del sistema. Nel caso di componenti singole \(MTTF=MTBF\).
- Fault_rate = \(\lambda = 1/MTBF\)
Dependability
Passiamo ora all’argomento principale del capitolo: ci concentriamo sul definire degli indicatori per valutare quanto “ci fidiamo” di un determinato sistema. In particolare, useremo il concetto di Dependability, costituito dall’inisieme dei parametri:
- Reliability: è definita come la probabilità che il sistema non abbia interruzioni di servizio fino al tempo t. E’ associata all’affidabilità nel tempo ed è un principio cardine dei sistemi che sono difficilmente riparabili come per esempio i devices spaziali, dai quali ci si aspetta un’alta reliability, dato che una eventuale riparazione sarebbe (quando possibile) costosa in termini di tempo e risorse. E’ possibile calcolarla (solo nel caso di sistemi con \(R\) a distribuzione esponenziale) come \(R(t)=e^{\frac{1}{\lambda}*t}=e^{\text{MTBF}*t}\) (considera che per singole componenti MTBF = MTTF).
- Availability: è definita come la probabilità che il sistema funzioni al tempo \(t\). E’ importantissima per i sistemi real time, in cui i componenti sono magari facilmente riparabili (quindi la reliability è utile ma non fondamentale) ma è importantissimo che, all’arrivare di una richiesta, il sistema risponda prontamente. Si calcola come \(Av=\frac{\text{UPtime}}{\text{Uptime}+\text{DOWNtime}}\).
- Mantainability: nei vari esercizi dei TDE si intravede il termine $MTTR = \text{Mean Time To Repair}$$. E’ l’unico riferimento alla riparabilità dei componenti, dato che non l’abbiamo trattata nel corso.
- Safety (non trattato).
- Security (non trattato).
Dependability di sistemi compositi
Ipotizziamo di connettere tra loro più componenti:
- Connessione in serie: dati due sistemi A e B connessi in serie, conoscendo la Reliability dei singoli moduli possiamo calcolare la reliability complessiva come \(R_{\text{tot}} = R_A * R_B\). Lo stesso discorso vale nel caso dell’Availability, con \(Av_{\text{tot}} = Av_A * Av_B\).
- Connessione in parallelo: dati due sistemi A e B connessi in parallelo, se conosciamo la Reliability dei singoli moduli possiamo calcolare \(R_\text{tot}=1-(1-R_A)(1-R_B)\). Allo stesso modo, \(Av_\text{tot}=1-(1-Av_A)(1-Av_B)\).
Notare che un sistema formato soltanto da serie di componenti aventi R distribuita esponenzialmente è ancora un sistema esponenziale. Vale lo stesso per i sistemi formati solo da paralleli. Ciò vuol dire che in questi casi si può usare direttamente la formula \(R_\text{tot}=e^{\frac{1}{\lambda_tot}}\). Ciò però non vale per sistemi in cui si “mischiano” paralleli e serie, per i quali bisogna effettuare prima il calcolo sui singoli componenti e, successivamente, calcolare i paralleli e le serie.
Diversi tipi di Fault
Ok ci piace avere un sistema affidabile e che possiamo tRuStArE, ma affidabile rispetto a cosa? Ai “probBlemi”.
Non essendo questo un grado di formalità accettabile per delle definizioni, nel mondo ingegneristico distinguiamo:
- Fault: è un difetto software o hardware presente nel nostro sistema. E’ lì, esiste, ma potrebbe non causare mai problemi (oppure potrebbe farci esplodere il datacenter).
Un drone con un’ala leggermente storta. Esso potrebbe comunque riuscire a raggiungere il punto B prestabilito.
- Error: questo è il caso in cui il Fault ha generato un problema che si è manifestato nel comportamento del sistema. Ciò non vuol ancora dire che il sistema ha fallito il suo compito: è possibile che in qualche modo esso si riprenda e riesca comunque a fornire il servizio prestabilito.
A causa dell’ala storta, il drone percorre un tragitto diverso da quello previsto, ma tramite correzioni GPS atterra comunque nel punto prestabilito
- Failure: la catena di eventi si è conclusa con un fallimento del sistema, ovvero quest’ultimo non è riuscito ad eseguire il suo compito. E’ ovviamente il caso peggiore tra i tre.
Il drone precipita rovinosamente.
Ci sono due approcci con cui è possibile sopperire a questi possibili problemi:
- Avoidance: verifichiamo così bene il funzionamento del progetto che siamo certi che non si verificheranno “problemi”.
- Tollerance: ci aspettiamo dei problemi, ma introduciamo ridondanza e controllo degli errori per rendere il sistema fault-resistant.
Solitamente, l’approccio utilizzato è un mix tra i due estremi.
5. VIRTUALIZATION E CLOUD COMPUTING
Dopo aver parlato di hardware, parliamo ora del modo in cui esso è partizionato tra i vari processi. La gran parte dei servizi di Cloud Computing odierni si basano sulla tecnologia della Virtualizzazione.
La virtualizzazione
La virtualizzazione è una tecnologia che permette di organizzare le risorse fisiche (reali) di una macchina in risorse virtuali da rendere poi disponibili a diverse macchine virtuali. I software che opereranno sulle singole VM (Virtual Machines) avranno l’impressione di essere installati su server dedicati, mentre stanno in realtà girando su una delle istanze di VM installate su un server comune.
Questo approccio ha molteplici vantaggi:
- Si può ottimizzare l’uso delle risorse di un datacenter grazie ad una gestione più flessibile di tali risorse.
- La sicurezza tra le varie istanze di VM è assicurata da un alto livello di isolamento: le VM non possono comunicare direttamente tra loro ed è come se operassero su macchine diverse. Questo vuol anche dire che un attacco ad una delle VM non si propaga alle altre.
- E’ possibile rendere l’installazione del software indipendente dall’hardware, virtualizzando quest’ultimo così da fornire sempre la medesima interfaccia indipendentemente dall’hardware reale sottostante.
Ok, ma come si implementa più nello specifico?
Machine IT levels
Prima di addentrarci nell’argomento principale del capitolo dobbiamo introdurre il concetto di Machine IT levels, ovvero dei layers in cui possiamo organizzare il funzionamento di un “calcolatore” informatico. Dal basso verso l’alto abbiamo:
- 0. Digital Logic Level: è il livello hardware. Sono i singoli moduli logici.
- 1. Microarchitecture Level: è l’insieme dei singoli moduli logici e il modo in cui essi sono connessi tra loro.
- 2. Istruction Set Level: è il livello che fornisce al software l’interfaccia per comunicare con l’hardware (ISA = Istruction Set Architecture). Ci sono due tipi di ISA: il System ISA costituito dalle istruzioni accessibili dall’OS e che nascondono la complessità della gestione dell’hardware e l’ User ISA, che comprende le istruzioni accessibili dall’utente (lo sviluppatore) per far funzionare la sua app.
- 3. Operating System Level: dal nome si può intuire essere il livello del sistema operativo. Qui l’OS espone alle applicazioni le ABI = Application Binary Interface che permettono a queste ultime di usare l’User ISA e le System Calls, ovvero richieste dell’uso di risorse condivise dell’hardware.
- 4. Assembly Language Level e 5. Problem-oriented Language Level.
L’architettura base della virtualizzazione
I moduli di un sistema di virtualizzazione sono:
- Hypervisor o VM Monitor: è il modulo che gestisce il layer di virtualizzazione e fornisce al livello superiore l’interfaccia virtualizzata. Teoricamente viene chiamato Hypervisor se installato direttamente sull’hardware (livello 2) e VM Monitor se hostato su un OS (livello 3). In pratica spesso si usa il termine Hypervisor per entrambi i casi.
- Virtual Machines: sono i moduli che si installano sull’hypervisor. Come vedremo, possono essere di vari tipi.
System Virtual Machines
Possiamo distinguere due macrotipi di sistemi di virtualizzazione, a loro volta suddivisi in tante sottocategorie.
Il primo dei macrotipi che analizziamo sono le System VM: in questo caso, l’Hypervisor virtualizza l’intera ISA, dando l’impressione agli OS nelle VM installate su di esso di girare su una macchina interamente dedicata a loro (o su un’altro tipo di macchina compatibile con l’OS, quando invece il server reale non lo sarebbe - Emulazione). Le System VM possono essere installate su diversi layers IT:
- Se installiamo il sistema di virtualizzazione direttamente sull’hardware (livello 2 dei layers IT) ottiamo un sistema bare metal (con hypervisor di tipo 1). In questo caso sono possibili due approcci: possiamo usare un Hypervisor con Kernel Monolitico, ovvero che contiene l’interezza dei driver di comunicazione con l’hardware e che si occupa di gestire le chiamate ISA verso l’hardware, oppure un sistema con Microkernel, in cui il Kernel è molto più piccolo e parte dell’interazione software-hardware (compresa l’installazione dei drivers) è assegnata ad una Service VM che gira sull’hypervisor e su cui è possibile installare i driver necessari per il funzionamento del sistema. Le due soluzioni hanno vantaggi e svantaggi: col kernel monolitico otteniamo migliori performances (il percorso è VM -> HPV e non VM -> SrvVM -> HPV, quindi meno latenza) e un miglior isolamento tra le VM (dato che l’unico nodo in comune è l’Hypervisor) mentre il microkernel ha dalla sua una maggiore flessibilità, dato che possiamo installare i drivers di sistema direttamente nella Service VM senza andare a ricompilare il kernel per intero.
- E’ anche possibile installare il sistema di virtualizzazione sull’OS, ed in questo caso si dice che è OS Hosted (Hypervisor di tipo 2). Si ha in pratica una maggiore flessibilità con vantaggi simili a quelli della soluzione microkernel (invece che installare i driver nel Kernel della VM, li si può gestire dall’OS sottostante) al costo di prestazioni inferiori. Notare che il software di virtualizzazione, nonostante in questo caso giri su un OS Host, fornisce alle VM che vi sono installate sopra l’interfaccia ISA completa di un “hardware” compatibile con gli OS hosted.
Sempre restando nell’ambito delle System VM, è possibile approcciarsi all’emulazione in due modi differenti:
- Full Virtualization: si presenta agli OS hostati un’interfaccia ISA completa e identica a quella della macchina hardware su cui tali OS si aspettano di essere installati. In questo caso si ha la comodità di poter eseguire OS non modificati, al costo di performances inferiori alla successiva opzione che vedremo, perché in questo primo caso bisogna effettuare una “emulazione completa”.
- Para Virtualization: l’hypervisor presenta ai livelli superiori un’interfaccia ISA simile ma non identica a quella dell’hardware sottostante. L’OS in questo caso è a conoscenza di essere installato su una VM e invece che effettuare System Calls, effettua delle Hypercalls, che vengono lette dall’hypervisor e convertite in chiamate all’ISA sottostante. Si ottiene in questo caso maggior velocità ed efficienza, ma si possono far girare solo OS modificati.
Nota: la paravirtualizzation può essere effettuata sia bare-metal che OS-hosted.
Process Virtual Machines
In questo caso l’hypervisor fornisce ai livelli superiori l’ABI (Application Binary Interface), collocandosi al livello 3 dei Layer IT. Il software di virtualizzazione è anche detto Runtime Software. Le VM di questa categoria sono in grado di far girare processi singoli (è il caso per esempio della Java Virtual Machine).
Containers
Similmente alle Process Virtual Machines, i containers portano la virtualizzazione al livello di sistema operativo (3). Sono in pratica dei moduli preconfigurati al cui interno sono installate tutte le librerire e variabili necessarie a far runnare correttamente un determinato applicativo. In questo modo il comportamento del container diventa predicibile indipendentemente dall’environment su cui lo si installa, semplificando la gestione dei processi nei DC. Possono essere molto utili nel caso di servizi forniti come Platform As A Service (PAAS).
Cloud Computing
Le tecnologie appena viste hanno reso possibile lo sviluppo di servizi di computing/storage/networking complessi forniti tramite internet e on-demand: il Cloud Computing.
Elenco qui di seguito i principali tipi di approccio al Cloud Computing (ce ne sono molti altri):
- SaaS (Software As A Service): il provider fornisce tramite cloud l’intero software e l’user deve soltanto inserirvi i dati all’interno per usarlo (es. Gmail, GDrive, …).
- PaaS (Platform As A Service): il cloud offre un’interfaccia (API) semplificata affinché l’utente (in questo caso lo sviluppatore) scriva il suo applicativo rivolgendosi a quelle API. Si semplifica la gestione del sistema dato che l’OS e i livelli inferiori sono gestiti dal provider del servizio, ma si rischia di rimanere intrappolati all’interno dell’ambiente di un singolo provider, dato che le API sono proprietarie.
- IaaS (Infrastructure As A Service): in questo caso si può accedere on-demand a risorse virtuali sulle quali installare il “proprio” sistema operativo. In questo caso il provider si occupa di gestire solo le VM sottostanti, lasciando all’user il compito di assicurarsi che il resto funzioni correttamente. Servizi simili sono il DaaS (Data As A Service) e il CaaS (Communication As A Service) che forniscono rispettivamente infrastrutture di storage e di rete tramite internet.
Sono inoltre possibili diversi approcci dal punto di vista della proprietà dell’infrastruttura:
- Public Cloud: servizi accessibili pubblicamente tramite una qualche forma di pagamento (anche i tuoi dati sono una forma di pagamento).
- Private Cloud: l’infrastruttura cloud è gestita da una singola organizzazione, che è la stessa che farà uso delle risorse. Utile in ambienti in cui la privacy e la riservatezza dei dati sono critiche, nonché soluzione più economica nel lungo periodo (per servizi molto grandi).
- Community Cloud: si tratta di un sistema di cloud simile al private, ma gestito da più compagnie federate.
- Hybrid solutions: una combinazione qualsiasi delle soluzioni precedenti (es. private cloud nella quotidiantià + un pizzico di public cloud quando si hanno i picchi di richieste che non si riescono a gestire con le proprie risorse).
6. OK, PUO’ BASTARE
Nel corso abbiamo approfondito anche alcuni modelli per studiare le prestazioni di sistemi come datacenters o dischi, ma suggerisco di approfondire tali argomenti tramite esercizi invece che teoricamente.
Per questo motivo, io mi fermo qui.
Buona fortuna per l’esame! ☘️