Edge Computing: un approccio A.I.

La soluzione di edge computing proposta da AIknow è un sistema per pianificare l’esecuzione di processi su sistemi in produzione con risorse inutilizzate.

Contesto

Si immagini questa situazione: si ha una flotta di cento gateway (pc industriali connessi in rete che eseguono applicazioni di monitoraggio, raccolta e inoltro dati). Ciascuno di questi gateway esegue i processi necessari a tenerlo attivo e l’insieme delle applicazioni per cui è stato installato in campo.

Nella situazione descritta, si immagini che il carico di lavoro sui gateway si aggiri tra il 5% (minimo) ed il 20% (massimo) di risorse CPU e RAM impegnate (variabili per ciascun gateway). Tuttavia, non è un’opzione accettabile l’utilizzo di macchine meno performanti: si danno, infatti, situazioni in cui è cruciale che le risorse a disposizione del gateway possano eseguire carichi di lavoro improvvisi senza generare disservizi o down-time applicativi.

Ci si trova, cioè, nella situazione in cui vi sono risorse computazionali disponibili e non utilizzate. Potremmo dire, in termini economici, che c’è un’offerta di risorse computazionali.

Si immagini ora di avere una lista di applicazioni che si vorrebbero eseguire, ma non si dispone delle macchine necessarie, vuoi perché il costo del provisioning di risorse adeguate supererebbe il ricavo ottenuto dalle applicazioni stesse, vuoi perché queste sono tendenzialmente poco dispendiose in termini di risorse, motivo per cui si vuole evitare di mantenere un sistema attivo solamente per l’esecuzione di una di queste applicazioni.

Ci si trova, questa volta, in una situazione di domanda di risorse computazionali: si necessita di risorse che non si hanno a disposizione.

Come si possono incontrare la domanda e l’offerta precedenti? La risposta che proponiamo è l’edge computing.

Soluzione di edge computing

L’idea è nella sua essenza semplice: utilizzare le risorse messe a disposizione dalla flotta di gateway già in utilizzo per eseguire delle applicazioni aggiuntive.

Sorge però immediatamente un problema: cosa può andare storto se si assegnano nuovi processi ad una risorsa che già ne esegue alcuni? Il carico di lavoro aumenta, esponendo la risorsa al rischio di crash: ciò va evitato nella situazione precedentemente descritta. I gateway a nostra disposizione debbono restare attivi. In altri termini, non si deve impattare eccessivamente sulle risorse computazionali (diciamo processore e memoria, per semplificare) a disposizione di ogni singola macchina (gateway).

La soluzione che si propone per quest’ultimo problema è quella di assegnare in maniera “intelligente” le applicazioni ai vari gateway, in modo da:

  • tenere in considerazione il corrente utilizzo di risorse di ciascun gateway;
  • bilanciare il più possibile il carico di lavoro, così da mantenere ciascun gateway al sicuro da crash.

Una possibile soluzione: pianificare l’assegnazione delle applicazioni ai nodi della flotta (i gateway) utilizzando un’Intelligenza Artificiale.

Ma quale tipo di Intelligenza Artificiale?

Lo strumento

OptaPlanner sembra fornire la soluzione alle esigenze presentate.

artificial_intelligence_types

 

OptaPlanner è un Constraint Solver, ossia un’Intelligenza Artificiale specializzata nella soluzione di problemi particolarmente complessi da un punto di vista computazionale: quando il problema scala (ovvero, quando aumenta il numero dei nodi e delle applicazioni in gioco), la difficoltà nella ricerca della soluzione aumenta a dismisura. Il nostro problema è precisamente uno di questi.

Inoltre, OptaPlanner introduce la possibilità di diversificare il tipo dei constraint (vincoli) nell’assegnazione delle applicazioni ai nodi. Si parla infatti di:

  • hard constraint: un vincolo che non deve essere violato nella pianificazione, ovvero che, se violato, rende inutilizzabile la pianificazione stessa – ad esempio: nell’assegnazione delle applicazioni (ciascuna delle quali richiede una certa quantità di memoria) a un certo nodo, non si deve eccedere la memoria a disposizione del nodo stesso;
  • soft constraint: un vincolo che si preferirebbe non violare, ma una violazione non preclude l’utilizzo della soluzione trovata – ad esempio, è preferibile bilanciare il più possibile i carico di lavoro sui nodi della flotta, ma qualche oscillazione in tal senso è accettabile.

Casi d’uso simili

Una rapida ricerca delle possibilità offerte da OptaPlanner ci porta a scoprire un caso d’uso interessante: Cloud optimization. Il caso d’uso richiede di assegnare una serie di processi a una serie di pc ottimizzando l’utilizzo delle risorse e minimizzando i costi. Ma, al di là della prima somiglianza del modello, il caso di edge computing qui in analisi presenta delle significative differenze, ad esempio:

  • non vi è necessità di minimizzare il costo delle risorse, dal momento che queste sono già disponibili;
  • non è necessario, anzi è controproducente concentrare il carico di lavoro su poche unità di elaborazione; al contrario, è opportuno distribuire in modo più possibile uniforme le applicazioni tra i vari nodi.

Perciò, oltre che a un’ispirazione data dal caso d’uso, è necessario provvedere a una ri-modellizzazione del problema ed una re-implementazione delle regole previste per la pianificazione.

Il modello

Descriviamo i punti chiave del modello impiegato per rappresentare il problema considerato.

Nel seguito, diciamo applicazione un processo che dobbiamo assegnare a una delle risorse computazionali a nostra disposizione. Diciamo inoltre nodo una delle risorse computazionali stesse, ovvero un gateway della flotta.

Ciascuna applicazione richiede una certa quantità di risorse per essere eseguita. Semplificando, possiamo dire che un’applicazione necessita di:

  • requiredCpuPower: la potenza di calcolo richiesta per l’esecuzione (non ci interessa qui specificare una misura per ciò);
  • requiredMemory: la quantità di memoria necessaria all’esecuzione dell’applicazione.
  • node: il nodo della flotta su cui dev’essere eseguita (per motivi di modellizzazione mediante OptaPlanner, risulta più conveniente parlare del nodo di un’applicazione rispetto alle applicazioni di un nodo).

Ciascun nodo ha:

  • cpuPower: quantità di potenza di calcolo massima;
  • memory: quantità di memoria massima;
  • usedCpuPower: potenza di calcolo già impegnata per il nodo stesso – aggiornata periodicamente;
  • usedMemory: memoria già impegnata sul nodo – anche questa aggiornata periodicamente.

Si può dare una prima formalizzazione del problema come segue: si assegnino le varie applicazioni ai nodi della flotta mantenendo il più possibile il bilanciamento del carico di lavoro tra i nodi e garantendo un margine di risorse a disposizione di eventuali carichi di lavoro improvvisi per la flotta stessa.

In altri termini, bisogna cercare di:

  • bilanciare il più possibile il carico di processore e memoria impegnati;
  • garantire una quota di risorse libera per ciascun gateway.

Nota

Una possibilità è che le applicazioni siano containerizzate, ovvero, predisposte per l’esecuzione tramite Docker. Si dà talvolta il caso che i gateway a disposizione eseguano sistemi abbastanza complessi da includere Docker stesso. In questo caso, ogni applicazione va considerata “a sé”, ovvero non si hanno esigenze di orchestrazione tra applicazioni diverse. In altre parole, non abbiamo bisogno di sistemi di orchestrazione del tipo Docker Swarm o Kubernetes per integrare tra loro le applicazioni: ciascuna di esse è indipendente.

Ecco perché si rende necessaria l’implementazione di un pianificatore A.I. che distribuisca il carico di lavoro.

Il progetto

Abbiamo implementato una PoC per la soluzione di edge computing proposta.

Il progetto prevede varie componenti: una prima componente, eseguita in cloud, è un backend Spring Boot (con persistenza su database PostgreSQL); si prevede poi un frontend Angular che permetta di interagire con l’applicazione, schedulare la pianificazione e lanciare l’esecuzione delle applicazioni stesse sulla flotta. Vi è inoltre una componente che implementa la comunicazione tra il backend ed i vari nodi, ciascuno dei quali esegue un agent per lo scambio di informazioni con il cloud.

Per la trasmissione di messaggi tra il backend ed i nodi si è deciso di fare uso del protocollo MQTT: il backend cloud include, quindi, un client che lo interfaccia con un broker MQTT, sempre installato in cloud, il quale inoltra i messaggi ai vari nodi. Ciascun nodo esegue nel suo agent un client MQTT che riceve i messaggi ed implementa una procedura di esecuzione per le applicazioni assegnategli. Una volta terminata l’esecuzione, l’agent sul nodo notifica l’esito al backend in cloud sempre via MQTT.

Il backend

Il backend prevede varie componenti:

  • una classica applicazione Spring Boot che espone REST API consumate da possibili frontend;
  • una componente di pianificazione che utilizza OptaPlanner;
  • un client MQTT per la comunicazione con la flotta di gateway.

Vediamo di approfondire brevemente come viene implementata la componente di pianificazione.

Il pianificatore

OptaPlanner fa utilizzo di algoritmi euristici per la ricerca di soluzioni. I problemi della classe a cui anche il nostro appartiene hanno le seguenti caratteristiche:

  • data una soluzione candidata, è semplice verificare in tempo ragionevole se questa rispetta i requisiti richiesti;
  • è invece molto complicato cercare delle soluzioni ottimali in tempo ragionevole.

Il trucco allora è il seguente: eseguire assegnazioni in modo “intelligente”, proponendo soluzioni del problema, per verificare poi quanto queste rispettino i requisiti richiesti. A tal fine, si fa utilizzo della nozione di punteggio di una soluzione, che rappresenta quanto la soluzione stessa si adatti ai vincoli hard e soft. Il punteggio è calcolato valutando la soluzione rispetto ad alcune regole, che possono essere implementate in diversi modi con OptaPlanner. Si può dare il caso in cui vi siano diverse soluzioni con il medesimo punteggio per uno stesso problema.

Definizione del problema

La componente di pianificazione si basa su alcune entità previste per il problema.

  • Si descrive con un Node un nodo della flotta. Un nodo ha certe quantità di cpuPower e di memory a disposizione; inoltre, un nodo ha certe quantità di usedCpuPower (potenza di calcolo utilizzata) e di usedMemory (memoria utilizzata) che non sono a disposizione nella pianificazione: queste ultime vengono periodicamente aggiornate dal backend mediante la comunicazione con i vari nodi.
  • Si rappresenta con una App un’applicazione che si deve eseguire sulla flotta di nodi. Una App prevede delle quantità di CPU e memoria richieste, rappresentate da requiredCpuPower e requiredMemory; inoltre, una App ha un Node a cui viene assegnata dal pianificatore.

Utilizzando il linguaggio di OptaPlanner:

  • App è la @PlanningEntity, ovvero l’oggetto che OptaPlanner va a modificare durante la pianificazione (l’oggetto su cui compie le assegnazioni);
  • la @PlanningVariable è la proprietà Node di una App: il pianificatore valuta diverse assegnazioni per questo valore e calcola la soluzione migliore tra quelle proposte facendo uso di un punteggio.

Vediamo le principali regole utilizzate per la pianificazione.

Regole hard

Ricordiamo che queste regole non devono essere violate nella soluzione proposta dal pianificatore.

  • la somma della quantità totale di CPU richiesta a un nodo dalle applicazioni a esso assegnate e della quantità di CPU già utilizzata sul nodo stesso non deve eccedere la quantità di CPU totale a disposizione del nodo;
  • la somma della quantità totale di memoria richiesta a un nodo dalle applicazioni a esso assegnate e della quantità di memoria già utilizzata sul nodo stesso non deve eccedere la quantità di memoria totale a disposizione del nodo;
  • si prevede un margine di 1/10 di ciascuna delle risorse che deve essere lasciato libero su ciascun nodo.

Regole soft

  • È preferibile assegnare tutte le applicazioni della lista fornita (ma è accettabile che qualche applicazione rimanga non assegnata, se l’alternativa prevede la violazione delle precedenti regole).
  • È preferibile bilanciare il carico di lavoro tra i vari nodi, evitando situazioni di eccessivo overloading su alcuni particolari nodi.

In particolare, l’ultima regola considerata richiede un fine tuning del modello e dell’implementazione proposta ed è il punto su cui è maggiormente necessario lavorare, magari considerando diversi benchmark.

Edge computing demo

Includiamo una GIF che presenta una demo vista dal frontend di pianificazione ed esecuzione di alcune applicazioni su una flotta di gateway.

edgeComputingDemo

La demo mostra le due fasi, lanciate dai due bottoni presenti sulla destra della schermata: Launch allocation e Launch execution.

La prima fase esegue il pianificatore, che assegna le applicazioni ai vari nodi disponibili. Questa fase, a seconda dei dati e delle regole introdotte, può avere durate anche significative.

La seconda fase, invece, lancia l’esecuzione delle applicazioni sulla flotta di gateway; questi, terminata l’esecuzione di un’applicazione, notificano l’esito al cloud, che provvede a rimuovere l’applicazione dalla schermata visualizzata.

Deployment

Il deployment della soluzione di edge computing può avvenire con diverse modalità, a seconda delle esigenze e delle opportunità di installazione.

Vediamo innanzitutto le possibili soluzioni di deploy che prevedono l’installazione direttamente sul sistema del server, ovvero che non fanno uso di servizi di deploy particolari. In questo caso, si ha una macchina server (che sia in cloud oppure on premises ha qui poca importanza) e si procede alla configurazione del sistema.

Installazione diretta

Una prima ovvia soluzione è l’installazione diretta delle componenti dell’applicazione sul sistema operativo della macchina server. In tal caso, si deve prevedere l’installazione di un database server PostgreSQL, un broker MQTT (ad esempio, Mosquitto è una soluzione leggera e versatile) e di una componente server che possa eseguire l’applicazione Spring Boot con il backend (ad esempio, un JAR). Il frontend può essere servito dallo stesso server utilizzato per il backend, oppure da un server apposito, ad esempio Nginx, che può essere anche configurato come reverse proxy per il backend, gestendo così anche la cifratura TLS.

Containerizzazione

Una seconda soluzione, più moderna, prevede la containerizzazione dei singoli servizi, che vengono poi eseguiti da un servizio di gestione dei contaner, ad esempio Docker. Questa soluzione offre alcuni vantaggi dal punto di vista dell’indipendenza, della riproducibilità e della scalabilità dei vari servizi. Per orchestrare i vari container è possibile fare utilizzo di uno strumento più avanzato, ad esempio, si può configurare uno swarm di Docker (si veda lo Swarm engine di Docker) oppure un cluster Kubernetes.

Nel caso si decida invece di installare la soluzione mediante un servizio gestito, si può optare per Elastic Container Service o per Elastic Kubernetes Service, entrambi forniti da Amazon Web Services; altri provider di servizi cloud forniscono soluzioni analoghe.

Conclusioni

L’esigenza di eseguire applicazioni senza incorrere in costi di provisioning delle necessarie infrastrutture può trovare soddisfazione se si incontra con la disponibilità di risorse computazionali inutilizzate che può sorgere da una flotta di gateway installati in produzione.

L’utilizzo di un software di Intelligenza Artificiale come OptaPlanner fornisce un solido framework per l’implementazione di una soluzione di pianificazione e schedulazione delle applicazioni, che presenta molte possibilità di configurazione e approfondimento.

La PoC qui introdotta realizza un primo passo nell’attuazione di una soluzione di edge computing, anche su vasta scala.