
Introduzione
Ciao!
In questo articolo condividerò l'esperienza nella costruzione di un'architettura microservizi per un progetto che utilizza reti neurali.
Parleremo dei requisiti per l'architettura, daremo un'occhiata ai diversi diagrammi strutturali, analizzeremo ciascuno dei componenti dell'architettura pronta e valuteremo le metriche tecniche della soluzione.
Buona lettura!
Qualche parola sul compito e la sua soluzione
L'idea principale è quella di fornire una valutazione dell'attrattività di una persona basata su una foto, utilizzando una scala da uno a dieci.
In questo articolo ci allontaneremo dalla descrizione delle reti neurali utilizzate e del processo di preparazione dei dati e di addestramento. Tuttavia, in una delle prossime pubblicazioni, torneremo sicuramente ad analizzare il pipeline di valutazione a un livello più approfondito.
Ora percorreremo a livello alto il pipeline di valutazione, concentrandoci sull'interazione dei microservizi nel contesto dell'architettura generale del progetto.
Durante il lavoro sul pipeline di valutazione dell'attrattività, il compito è stato scomposto nei seguenti componenti:
- Identificazione dei volti nelle foto
- Valutazione di ciascuno dei volti
- Rendering del risultato
Il primo viene risolto attraverso una rete pre-addestrata di . Per il secondo è stata addestrata una rete neurale convoluzionale su PyTorch, utilizzando come backbone – dal bilancio "qualità / velocità di inferenza su CPU"

Diagramma funzionale del pipeline di valutazione
Analisi dei requisiti per l'architettura del progetto
Nel ciclo di vita di un progetto, le fasi di lavoro sull'architettura e sull'automazione del rilascio del modello sono spesso tra le più dispendiose in termini di tempo e risorse.

Ciclo di vita di un progetto ML
Questo progetto non fa eccezione: è stata presa la decisione di racchiudere il pipeline di valutazione in un servizio online, per il quale è stato necessario immergersi nell'architettura. Sono stati definiti i seguenti requisiti di base:
- Un'unica archiviazione dei log: tutti i servizi devono scrivere i log in un unico posto, con la possibilità di analizzarli facilmente.
- Possibilità di scaling orizzontale del servizio di valutazione — come il più probabile collo di bottiglia.
- A ciascuna immagine deve essere allocato lo stesso numero di risorse della CPU — per evitare sbalzi nella distribuzione del tempo di inferenza.
- Rilascio rapido (ripristino) sia di servizi specifici che dell'intero stack.
- La possibilità, se necessario, di utilizzare oggetti comuni in diversi servizi
Architettura
Dopo aver analizzato i requisiti, è diventato chiaro che l'architettura a microservizi si adatta praticamente perfettamente.
Per evitare ulteriori grattacapi, è stata scelta l'API di Telegram come front-end.
Iniziamo esaminando il diagramma strutturale dell'architettura pronta, quindi passeremo alla descrizione di ciascun componente e formalizzeremo il processo di elaborazione delle immagini con successo.

Diagramma strutturale dell'architettura pronta
Parliamo più in dettaglio di ciascun componente del diagramma, evidenziando il loro Single Responsibility nel processo di valutazione dell'immagine.
Microservizio «attrai-telegram-bot»
Questo microservizio incapsula tutte le interazioni con l'API di Telegram. Si possono individuare 2 scenari principali: lavoro con l'immagine dell'utente e lavoro con il risultato del pipeline di valutazione. Analizzeremo entrambi gli scenari in modo generale.
Quando si riceve un messaggio dall'utente contenente un'immagine:
- Viene eseguita una filtrazione, composta dai seguenti controlli:
- Presenza di dimensioni ottimali dell'immagine
- Numero di immagini dell'utente già in coda
- Superata la filtrazione iniziale, l'immagine viene salvata nel volume Docker
- Nel queue 'to_estimate' viene prodotto un task, nel quale è incluso il percorso dell'immagine nel nostro volume
- Se le fasi sopra indicate vengono superate con successo, l'utente riceverà un messaggio con il tempo approssimativo di elaborazione dell'immagine, calcolato in base al numero di task in coda. In caso di errore, l'utente verrà informato chiaramente tramite un messaggio con informazioni su ciò che potrebbe essere andato storto.
Inoltre, questo microservizio, come lavoratore Celery, ascolta la coda 'after_estimate', destinata ai task che hanno superato il pipeline di valutazione.
Quando si riceve un nuovo task da 'after_estimate':
- Se l'immagine è stata elaborata con successo, inviamo il risultato all'utente; se no, informiamo dell'errore.
- Eliminiamo l'immagine, che è il risultato del pipeline di valutazione.
Microservizio di valutazione «attrai-estimator»
Questo microservizio è un lavoratore Celery e incapsula tutto ciò che riguarda il pipeline di valutazione dell'immagine. L'algoritmo di funzionamento è semplice: lo analizziamo.
Quando si riceve un nuovo task da 'to_estimate':
- Passiamo l'immagine attraverso il pipeline di valutazione:
- Carichiamo l'immagine in memoria
- Modifichiamo l'immagine alla dimensione necessaria
- Troviamo tutti i volti (MTCNN)
- Valutiamo tutti i volti (incapsuliamo i volti trovati nel passo precedente in un batch e inferring ResNet34)
- Renderizziamo l'immagine finale
- Disegniamo i bounding box
- Disegniamo le valutazioni
- Eliminiamo l'immagine dell'utente (originale)
- Salviamo l'uscita dal pipeline di valutazione
- Mettiamo il task nella coda 'after_estimate', ascoltata dal microservizio 'attrai-telegram-bot' sopra descritto
Graylog (+ mongoDB + Elasticsearch)
— è una soluzione per la gestione centralizzata dei log. In questo progetto, è stato utilizzato per il suo scopo originale.
La scelta è caduta proprio su di lui, e non sul noto stack, per il motivo della facilità di lavoro con esso da Python. Tutto ciò che è necessario fare per il logging in Graylog è aggiungere GELFTCPHandler dal pacchetto agli altri handler del root logger del nostro microservizio Python.
Io, come persona che ha lavorato solo con lo stack ELK fino ad ora, ho avuto un'esperienza positiva durante il lavoro con Graylog. L'unica cosa che delude è la superiorità delle funzionalità di Kibana rispetto all'interfaccia web di Graylog.
RabbitMQ
— è un broker di messaggi basato sul protocollo AMQP.
In questo progetto è stato utilizzato come per Celery e ha funzionato in modalità durable.
Redis
— è un DBMS NoSQL che lavora con strutture di dati di tipo 'chiave — valore'
A volte c'è la necessità di utilizzare oggetti comuni tra diversi microservizi Python che implementano determinate strutture di dati.
Ad esempio, in Redis si memorizza una hashmap del tipo 'telegram_user_id => numero di task attivi in coda', il che consente di limitare il numero di richieste da un singolo utente a un valore specifico e, in tal modo, prevenire attacchi DoS.
Formalizziamo il processo di elaborazione dell'immagine
- L'utente invia un'immagine al bot di Telegram
- ‘attrai-telegram-bot’ riceve il messaggio dall'API di Telegram e lo analizza
- Il task con l'immagine viene aggiunto alla coda asincrona 'to_estimate'
- L'utente riceve un messaggio con il tempo previsto di valutazione
- ‘attrai-estimator’ prende il task dalla coda 'to_estimate', lo passa attraverso il pipeline di valutazione e produce il task nella coda 'after_estimate'
- ‘attrai-telegram-bot’, che ascolta la coda 'after_estimate', invia il risultato all'utente
DevOps
Finalmente, dopo aver esaminato l'architettura, possiamo passare a una parte altrettanto interessante: il DevOps
Docker Swarm

— un sistema di clustering, la cui funzionalità è implementata all'interno del Docker Engine ed è disponibile 'out of the box'.
Attraverso il "swoop", tutti i nodi del nostro cluster possono essere divisi in 2 tipi: worker e manager. Sulle macchine del primo tipo vengono distribuiti gruppi di container (stack), mentre le macchine del secondo tipo si occupano della scalabilità, del bilanciamento e . I manager, per impostazione predefinita, sono anche worker.

Cluster con un leader manager e tre worker
La dimensione minima possibile del cluster è di 1 nodo, l'unica macchina fungerà sia da leader manager che da worker. In base alle dimensioni del progetto e ai requisiti minimi di tolleranza ai guasti, è stata presa la decisione di utilizzare proprio questo approccio.
In anticipo, posso dire che, dall'implementazione della prima produzione, avvenuta a metà giugno, non ci sono stati problemi legati a questa organizzazione del cluster (ma questo non significa che tale organizzazione sia minimamente accettabile in qualsiasi progetto medio-grande, a cui si applicano requisiti di tolleranza ai guasti).
Docker Stack
In modalità "swoop", la distribuzione degli stack (insiemi di servizi docker) è gestita da
Supporta le configurazioni docker-compose, consentendo di utilizzare anche parametri di deploy.
Ad esempio, utilizzando questi parametri sono state limitate le risorse per ciascuno degli istanze del microservizio di valutazione (dedicando N core a N istanze, nel microservizio limitiamo il numero di core utilizzato da PyTorch a uno)
attrai_estimator:
image: 'erqups/attrai_estimator:1.2'
deploy:
replicas: 4
resources:
limits:
cpus: '4'
restart_policy:
condition: on-failure
…È importante notare che Redis, RabbitMQ e Graylog sono servizi stateful e non è così semplice scalare anche loro come "attrai-estimator".
Anticipando la domanda: perché non Kubernetes?
Sembra che utilizzare Kubernetes in progetti di piccole e medie dimensioni sia un sovraccarico, tutta la funzionalità necessaria può essere ottenuta da Docker Swarm, che è piuttosto user friendly come orchestratore di container, e ha anche una bassa barriera all'ingresso.
Infrastruttura
Tutto ciò è stato distribuito su un VDS con le seguenti caratteristiche:
- CPU: 4 core Intel® Xeon® Gold 5120 CPU @ 2.20GHz
- RAM: 8 GB
- SSD: 160 GB
Dopo un test di carico locale, sembrava che con un forte afflusso di utenti, questa macchina fosse al limite.
Ma subito dopo il deploy, ho condiviso il link a uno dei più popolari imageboard della CSI (sì, proprio quello), dopo di che le persone si sono interessate e in poche ore il servizio ha elaborato con successo decine di migliaia di immagini. In questo caso, nei momenti di picco, le risorse CPU e RAM non sono state utilizzate nemmeno a metà.


Un'altra grafica
Numero di utenti unici e richieste di valutazione, dal momento del deploy, a seconda del giorno

Distribuzione dei tempi di inferenza del pipeline di valutazione

Conclusioni
In sintesi, posso dire che l'architettura e l'approccio all'orchestrazione dei contenitori si sono dimostrati completamente validi: anche nei momenti di picco non ci sono stati cali o ritardi nei tempi di elaborazione.
Penso che i progetti di piccole e medie dimensioni che utilizzano l'inferenza in tempo reale delle reti neurali su CPU possano adottare con successo le pratiche descritte in questo articolo.
Aggiungo che inizialmente l'articolo era più lungo, ma per non pubblicare un lungo post ho deciso di omettere alcuni punti in questo articolo — torneremo su di essi nei prossimi articoli.
Puoi provare il bot su Telegram — @AttraiBot, funzionerà almeno fino alla fine dell'autunno 2020. Ricordo che nessun dato utente viene memorizzato — né le immagini originali né i risultati del pipeline di valutazione — tutto viene eliminato dopo l'elaborazione.
Fonte: habr.com
