ΠΑΝΕΠΙΣΤΗΜΙΟ ΠΑΤΡΩΝ ΤΜΗΜΑ ΗΛΕΚΤΡΟΛΟΓΩΝ ΜΗΧΑΝΙΚΩΝ ΚΑΙ ΤΕΧΝΟΛΟΓΙΑΣ ΥΠΟΛΟΓΙΣΤΩΝ ΤΟΜΈΑΣ: Ηλεκτρονικής και Υπολογιστών ΕΡΓΑΣΤΉΡΙΟ ΣΥΣΤΗΜΆΤΩΝ ΥΠΟΛΟΓΙΣΤΏΝ Διπλωματική Εργασία του φοιτητή του Τμήματος Ηλεκτρολόγων Μηχανικών και Τεχνολογίας Υπολογιστών της Πολυτεχνικής Σχολής του Πανεπιστημίου Πατρών Σολάνου Αλέξανδρου του Παναγιώτη Αριθμός Μητρώου: 228197 Θέμα Σχεδιασμός και πρότυπη υλοποίηση IoT συστήματος βασισμένη σε container και Lightweight M2M πρωτόκολλο Επιβλέπων ΘΡΑΜΠΟΥΛΙΔΗΣ ΚΛΕΑΝΘΗΣ Αριθμός Διπλωματικής Εργασίας: Πάτρα, Ιούλιος 2020 1 ΠΙΣΤΟΠΟΙΗΣΗ Πιστοποιείται ότι η Διπλωματική Εργασία με θέμα «Σχεδιασμός και πρότυπη υλοποίηση IoT συστήματος βασισμένη σε container και Lightweight M2M πρωτόκολλο» Του φοιτητή του Τμήματος Ηλεκτρολόγων Μηχανικών και Τεχνολογίας Υπολογιστών Αλέξανδρου Σολάνου του Παναγιώτη Αριθμός Μητρώου: 228197 Παρουσιάστηκε δημόσια και εξετάστηκε στο Τμήμα Ηλεκτρολόγων Μηχανικών και Τεχνολογίας Υπολογιστών στις / / Ο Επιβλέπων Ο Διευθυντής του Τομέα Καθηγητής Θραμπουλίδης Κλεάνθης Καθηγητής Παλιουράς Βασίλειος 2 Αριθμός Διπλωματικής Εργασίας: 3 Θέμα: «Σχεδιασμός και πρότυπη υλοποίηση IoT συστήματος βασισμένη σε container και Lightweight M2M πρωτόκολλο» Φοιτητής Επιβλέπων Σολάνος Αλέξανδρος του Π. Καθηγητής Θραμπουλίδης Κλεάνθης 4 Περίληψη Διανύουμε την 4η βιομηχανική επανάσταση, που υπόσχεται μια πιο δυναμική γραμμή παραγωγής. Η παραγωγή μπορεί να ελέγχεται μέσω προσαρμοσμένων παραγγελιών, μηχανήματα μπορούν να επισκευάζονται με βάση την ανάλυση ιστορικών δεδομένων που παρέχονται μέσω έξυπνων αισθητήρων πριν προκληθεί κάποια βλάβη, και οι αλλαγές στη γραμμή παραγωγής συχνά αντιστοιχούν μόνο σε αλλαγές στο λογισμικό και στην παραμετροποίησή του. O ρόλος του λογισμικού στο Industry 4.0 είναι κρίσιμος. H διπλωματική αυτή εργασία ερευνά τη χρησιμότητα μιας πληθώρας τεχνολογιών για την 4η βιομηχανική επανάσταση: (α) των containers, οι οποίοι ενθυλακώνουν τις εφαρμογές και την παραμετροποίησή τους σε εκτελέσιμες «εικόνες», κάνοντας έτσι τη διαχείριση και την ανανέωσή τους σημαντικά πιο εύκολη, (β) της αρχιτεκτονικής των microservices, η οποία υπαγορεύει ότι ένα σύστημα μπορεί να τμηματοποιηθεί κατάλληλα με αποτέλεσμα να αποτελείται από ελαφρώς συνδεδεμένες εφαρμογές και (γ) της τεχνολογίας του διαδικτύου των αντικειμένων (IoT) και πώς αυτές μπορούν να δώσουν σε συσκευές τη δυνατότητα διαλειτουργικότητας, στοιχείο πολύ σημαντικό για μια ευέλικτη γραμμή παραγωγής. Ως γλώσσα προγραμματισμού επιλέχθηκε η Node.js, λόγω του ότι αποφεύγεται η ανάγκη για compilation και η βασική της βιβλιοθήκη δίνει εύκολη δυνατότητα επικοινωνίας μέσω διαδικτύου. Ως container engine χρησιμοποιήθηκε το Docker διότι είναι δημοφιλές και δοκιμασμένο. Τα containers, λόγω του ότι δεν χρειάζεται να κάνουν hardware virtualization, αποδείχθηκε ότι μπορούν να εκτελούνται χωρίς αισθητή επίπτωση στην επίδοση. Από τους τρόπους επικοινωνίας μεταξύ microservices που μελετήθηκαν, επιλέχθηκε η χρήση των Unix Domain Sockets επειδή παρουσιάζει ιδιαίτερο ενδιαφέρον η χρήση τους για επικοινωνία μεταξύ containers, η ταχύτητα που προσφέρουν, και η ομοιότητά τους σε σχέση με τα TCP sockets. Διαπιστώθηκε ότι η χρήση microservices δεν είναι κατάλληλη για όλες τις εφαρμογές. Πρέπει να υπάρχουν σημαντικοί λόγοι για την εφαρμογή τους, καθώς, πέρα από τα πλεονεκτήματα που προσφέρουν, εισάγουν επιπρόσθετη πολυπλοκότητα σε ένα σύστημα και πιθανόν να επηρεάζουν τις επιδόσεις του. Ως IoT πρωτόκολλο επικοινωνίας χρησιμοποιήθηκε το LwM2M λόγω του ότι προωθεί τη διαλειτουργικότητα διαφορετικών συσκευών, αλλά και λόγω της χαμηλής κατανάλωσης ισχύος. Λέξεις Κλειδιά: Διαδίκτυο των Αντικειμένων, Container, Microservice, LwM2M, Liqueur Plant System 5 Summary We are going through the 4th industrial revolution, which promises a more dynamic production line. The production can be controlled through custom orders, machines can be repaired according to the analysis of historical data that are being provided through smart sensors before a failure happens, and changes in the production line usually correspond only to changes in the software and its configuration. The role of software in Industry 4.0 is vital. This master’s thesis researches the usefulness of a plethora of different technologies for the 4th industrial revolution: (a) containers, which encapsulate the applications and their configuration in executable “images”, making their management and update easier, (b) the architecture of microservices, which dictates that a system can be divided appropriately so that it consists of loosely coupled programs and (c) the technology of Internet of Things (IoT) and how it can make devices more interoperable, which is a very important feature of a dynamic production line. The chosen programming language was Node.js, because it avoids the need for compilation and its basic library makes it easy to communicate through the network. The chosen container engine was Docker because it is popular and well tested. The containers, because of their capability to not need to do hardware virtualization, proved to be able to run without noticeable degradation of performance. From the various ways of communication between microservices that were researched, Unix Domain Sockets were chosen, because they can be used to communicate between containers providing exceptional performance and they can be handled similarly to TCP sockets. It was found that the microservices architecture is not the appropriate choice for all kinds of applications. There need to be important reasons to proceed with it, because it adds additional complexity to a system and there is also the possibility of performance degradation. LwM2M was chosen as IoT protocol, because it promotes interoperability and also because of its low power consumption. Key Words: Internet of Things, Container, Microservice, LwM2M, Liqueur Plant System 6 Ευχαριστίες Θα ήθελα να ευχαριστήσω τον καθηγητή Κλεάνθη Θραμπουλίδη για την παρότρυνση να ασχοληθώ με το αντικείμενο του IoT και για όλη την καθοδήγηση που μου έδωσε για την πραγματοποίηση της έρευνας και για τη συγγραφή αυτής της διπλωματικής εργασίας. Οι γνώσεις και η εμπειρία που έλαβα μέσω της εκπόνησης αυτής της διπλωματικής εργασίας είναι ένα εξαιρετικής σημασίας εφόδιο στην επαγγελματική μου ζωή. Επίσης, θα ήθελα να ευχαριστήσω θερμά τον Μπόχαλη Παναγιώτη, Μπουλούμπαση Ιωάννη και Ιάσονα Αθανάσογλου για την πολύτιμη βοήθειά τους και συνεργασία τους σε θέματα που αφορούν τη λειτουργία των IoT πρωτοκόλλων και για την πραγματοποίηση των εξομοιώσεων. Τέλος, ευχαριστώ την οικογένειά μου για όλη την υπομονή, κατανόηση και στήριξη που έδειξαν κατά τη διάρκεια του ακαδημαϊκού μου έργου. 7 Περιεχόμενα Περίληψη 4 Ευχαριστίες 6 Περιεχόμενα 7 1. Εισαγωγή 9 1.1 Οι τεχνολογίες του διαδικτύου των αντικειμένων, των containers και των microservices 1.2 Το αντικείμενο και η δομή της διπλωματικής εργασίας 2. Διαδίκτυο των Αντικειμένων 9 10 12 2.1 Ιστορία του Internet of Things και Industry 4.0 12 2.2 IoT πρωτόκολλα 14 2.2.1 Τεχνικές ανταλλαγής μηνυμάτων 14 2.2.2 Το MQTT πρωτόκολλο 16 2.2.3 Το LwM2M πρωτόκολλο 17 3. Liqueur Plant case study 19 3.1 The case study 19 3.2 Έξυπνο σιλό με το Bosch XDK 20 4. Containers and microservices 4.1 Virtualization 4.2 Containers 28 28 30 4.2.1 Επίπεδο αφαιρετικότητας από το λειτουργικό σύστημα 34 4.2.2 Πλεονεκτήματα των containers 36 4.3 Microservices 38 4.3.1 Η αρχιτεκτονική της monolithic εφαρμογής 39 4.3.2 Η αρχιτεκτονική των microservices 40 4.3.3 Πλεονεκτήματα και μειονεκτήματα των microservices 42 5. Eπικοινωνία των microservices 5.1 Ανταλλαγή μηνυμάτων μεταξύ microservices 45 45 5.1.1 Εισαγωγή 45 5.1.2 Ανταλλαγή μηνυμάτων στο δίκτυο 45 5.1.2.1 Advanced Message Queuing Protocol (AMQP) 46 5.1.2.2 ZeroMQ 47 5.1.3 Ανταλλαγή μηνυμάτων με τεχνικές IPC 51 5.1.3.1 Pipes 51 5.1.3.2 Unix Domain Sockets 53 5.1.3.3 Shared Memory (Κοινή μνήμη) 54 5.2 Performance Measurements 5.2.1 Απόδοση της χρήσης function call 55 55 8 5.2.2 Απόδοση της χρήσης UDP 56 5.2.3 Απόδοση της χρήσης Unix Domain Sockets 56 5.2.4 Απόδοση της χρήσης LwM2M 57 5.2.5 Απόδοση της χρήσης AMQP 59 5.2.6 Απόδοση της χρήσης ZeroMQ 60 6. Υλοποίηση του Smart Silo με microservices 62 6.1 Τμηματοποίηση του συστήματος 62 6.2 Χρήση Node.js modules για επικοινωνία 65 6.2.1 Έρευνα για το Seneca module 65 6.2.2 Έρευνα για το node-ipc module 66 6.3 Ενθυλάκωση του συστήματος σε containers 66 6.4 Τρόπος λειτουργίας του συστήματος 68 6.4.1 Silo Controller Microservice 68 6.4.2 Heater and Mixer Microservice 72 6.4.3 In-Valve και Out-Valve Microservice 73 6.4.4 SiloDriver Microservice 74 7. Συμπεράσματα 76 7.1 Συμπεράσματα 76 7.2 Προτάσεις για μελλοντική εργασία 77 Bibliography 78 9 Κεφάλαιο 1 1. Εισαγωγή 1.1 Οι τεχνολογίες του διαδικτύου των αντικειμένων, των containers και των microservices Στις παραδοσιακές βιομηχανικές εφαρμογές, η συγγραφή προγραμμάτων γίνεται με την κλασική προσέγγιση της μονολιθικής εφαρμογής που η εκτέλεσή της πραγματοποιείται με χρήση PLCs (Programmable Logic Controllers). Πρακτικά αυτό σημαίνει ότι όλη η διαδικασία παραγωγής περιγράφεται από την αρχή ως το τέλος μέσω ενός αλγορίθμου ο οποίος συντονίζει όλη τη διαδικασία παραγωγής. Η αλλαγή ενός εξαρτήματος που συμμετέχει στην παραγωγή, ή η αλλαγή της ίδιας της διαδικασίας παραγωγής, σημαίνει ότι θα πρέπει να γίνει τροποποίηση του εν λόγω προγράμματος, εκ νέου δοκιμές ολόκληρης της λειτουργίας για την αποφυγή λαθών, και ανανέωση του λογισμικού των PLCs που συντονίζουν τη διαδικασία. Ταυτόχρονα, τα τελευταία χρόνια παρατηρείται μεγάλο ενδιαφέρον για την τεχνολογία του Διαδικτύου των Αντικειμένων - Internet of Things (IoT) [1]. Η χρήση τεχνολογιών IoT μπορεί να μετατρέψει τα αντικείμενα σε «έξυπνα», δηλαδή σε αντικείμενα που μπορούν να παρέχουν μία ή και περισσότερες υπηρεσίες μέσω διαδικτύου αλλά και να επικοινωνούν μεταξύ τους, συνήθως έχοντας αισθητήρες και ανταλλάζοντας ή/και αποθηκεύοντας πληροφορίες. Ενώ τα έξυπνα αντικείμενα μπαίνουν όλο και περισσότερο στη ζωή μας, παρατηρείται το φαινόμενο της χρήσης των containers [2], κυρίως στο κομμάτι της ανάπτυξης διαδικτυακών εφαρμογών, οι οποίες μπορούν να γίνουν πολύ μεγάλες σε μέγεθος και δύσκολα διαχειρίσιμες, με πολλές συγκεκριμένες εκδόσεις λογισμικού και παραμετροποιήσεις, καθώς και ανάγκη για canaryτύπου deployments, deployments που εξασφαλίζουν την συνεχή πρόσβαση χρηστών στο σύστημα κ.ά.. Η τεχνολογία των containers δίνει τη δυνατότητα του αυστηρού ελέγχου εκτέλεσης των προγραμμάτων καθώς και της ενθυλάκωσης των εφαρμογών για ευκολότερη διαχείρισή τους. Πλέον η εκτέλεση ενός προγράμματος συγκεκριμένης έκδοσης και παραμετροποίησης δεν χρειάζεται να γίνει χειροκίνητα. Αντίθετα, μπορεί η εφαρμογή να ενθυλακωθεί μέσα σε έναν container του οποίου η μορφή περιγράφεται δηλωτικά. Η αντικατάστασή του με διαφορετικό container είναι όσο απλή όσο η αλλαγή της δηλωτικής περιγραφής του αρχείου που είναι υπεύθυνο για τη μορφή του container. Η χρήση containers ανοίγει το δρόμο σε αυτοματοποιήσεις όχι μόνο σε επίπεδο εφαρμογής, αλλά και σε επίπεδο συστήματος. Οι μονολιθικές εφαρμογές αποτελούν την κλασική προσέγγιση ανάπτυξης λογισμικού. Όσο όμως μια εφαρμογή μεγαλώνει, είναι πιο χρονοβόρο να ελεγχθεί (unit, integration και end-to-end testing), πιο χρονοβόρο να χτισθεί ως εκτελέσιμο (build time), πιο δύσκολο να χρησιμοποιηθεί ποικιλία διαφορετικών γλωσσών προγραμματισμού όταν το απαιτούν οι προδιαγραφές, και πιο δύσκολο ο κώδικάς της να περιηγηθεί και να οικειοποιηθεί από έναν καινούργιο προγραμματιστή. Η αρχιτεκτονική κατασκευής λογισμικού με microservices [3] έρχεται να λύσει αυτό το πρόβλημα, χωρίζοντας τη μονολιθική εφαρμογή σε πολλές μικρότερες οι οποίες παρέχουν καλώς ορισμένη λειτουργικότητα και μπορούν να αναπτυχθούν και να δοκιμαστούν ξεχωριστά από το υπόλοιπο σύστημα. Όπως είναι φυσικό, αυτό προκαλεί μεγάλη ανάγκη για μετρήσιμα σταθερές και ικανοποιητικές επιδόσεις επικοινωνίας μεταξύ των διαφορετικών microservices. 10 1.2 Το αντικείμενο και η δομή της διπλωματικής εργασίας Μέσω της διπλωματικής αυτής εργασίας, εξερευνάται η βιωσιμότητα των τεχνολογιών του IoT, δηλαδή συνεργαζόμενων επικοινωνούντων «έξυπνων» αντικειμένων, και των containerized microservices, δηλαδή της κατανεμημένης επεξεργασίας σε μικρά, σαφώς ορισμένα μικροσυστήματα, σε ένα βιομηχανικό περιβάλλον. Πιο συγκεκριμένα αυτή η διπλωματική εργασία εξετάζει αν οι τεχνολογίες αυτές μπορούν να χρησιμοποιηθούν με επιτυχία, αλλά και ποια είναι τα πλεονεκτήματα και τα μειονεκτήματά τους σε σχέση με πιο κλασικές προσεγγίσεις. Αρχικά, πραγματοποιήθηκε μία έρευνα για τους τρόπους με τους οποίους η βιομηχανία χρησιμοποιεί software και hardware για τον έλεγχο των διεργασιών που έχουν να πραγματοποιήσουν και τι προβλήματα έχουν αυτές οι κλασικές προσεγγίσεις. Μελετήθηκε ο τρόπος που το Internet of Things μπορεί να βοηθήσει στην επίλυση αυτών των προβλημάτων, καθώς και οι καινούργιες προκλήσεις που δημιουργούνται με την υιοθέτηση των τεχνολογιών αυτών. Στα πλαίσια εξερεύνησης και εξοικείωσης με τις τεχνολογίες IoT, αναπτύχθηκε σε συνεργασία με τον Αθανάσογλου Ιάσονα, μία πειραματική διάταξη με το Bosch XDK [4], μια προγραμματιζόμενη συσκευή με αισθητήρες για χρήση σε IoT διατάξεις. Η εν λόγω διάταξη συμμετείχε σε διαγωνισμό της Bosch με τίτλο «XDK Ideation Jam» και έλαβε το πρώτο βραβείο. Συνεχίζοντας την έρευνα στις τεχνολογίες του IoT και στους τρόπους αξιοποίησης του, καθώς και λόγω της φύσης πολλών IoT διατάξεων, οι οποίες μπορεί να αποτελούνται από πολλές συσκευές που πραγματοποιούν ίδιες ή και διαφορετικές διεργασίες, η κατεύθυνση της έρευνας στράφηκε στις ανερχόμενες τεχνολογίες των microservices και των containers και πώς αυτές μπορούν να αξιοποιηθούν μαζί με τις IoT τεχνολογίες. Πολύ σημαντικός για τη χρήση microservices, κρίθηκε ότι είναι ο τρόπος που τα services αυτά επικοινωνούν μεταξύ τους, καθώς εκ φύσεως προσθέτουν καθυστέρηση στην επικοινωνία μεταξύ των διάφορων στοιχείων του συστήματος σε σχέση με την επικοινωνία των διαφορετικών τμημάτων μιας μονολιθικής εφαρμογής. Για το λόγο αυτό, έγινε αξιολόγηση της απόδοσης των διαφορετικών τρόπων επικοινωνίας των microservices και σύγκρισή τους ανάλογα με τις δυνατότητες που δίνουν σε επίπεδο εφαρμογής. Στη συνέχεια, στα πλαίσια της διπλωματικής εργασίας μελετήθηκε ένα case study που αφορά την κατασκευή liqueur σε ένα εργοστάσιο. Το σύστημα αυτό αποτελείται από 4 σιλό και δευτερεύοντα εξαρτήματα. Τα σιλό διαθέτουν αισθητήρες θερμοκρασίας και αισθητήρες στάθμης του υγρού που περιέχουν. Τα Τέσσερα σιλό μπορούν και πραγματοποιούν ταυτόχρονα παραγωγή 2 διαφορετικών ειδών liqueur ενώ μοιράζονται εξαρτήματα. Η χρήση των εξαρτημάτων από την κάθε διαδικασία παραγωγής πρέπει να είναι προσεκτική για την ταυτόχρονη παραγωγή, και ο συγχρονισμός αυτός των σιλό γίνεται με χρήση τεχνολογιών IoT. Για τη μελέτη αυτού του συστήματος και για την επίδειξη της συμβατότητας διαφορετικών τεχνολογιών που χρησιμοποιούν κοινά IoT-πρωτόκολλα επικοινωνίας, προγραμματίστηκε ένα έξυπνο σιλό, χρησιμοποιώντας τις προαναφερθείσες τεχνολογίες. Αυτό το σιλό επικοινωνεί με άλλα έξυπνα αντικείμενα, όπως άλλα σιλό και ένα έξυπνο pipe, καθένα από τα οποία χρησιμοποιεί διαφορετικό υλικό και λογισμικό αλλά μπορούν και συνεργάζονται λόγω χρήσης κοινών IoT πρωτοκόλλων. Το ίδιο το silo που προσομοιάστηκε, αποτελείται από διάφορα συνεργαζόμενα εξαρτήματα, των οποίων η λειτουργία ελέγχεται από microservices που επικοινωνούν μεταξύ τους και που ενθυλακώνονται μέσα σε containers. Στα πλαίσια αυτού του συστήματος έγιναν μετρήσεις για την αξιολόγηση της απόδοσής του. Τέλος, μελετήθηκε η συμπεριφορά των microservices όταν αυτά υλοποιούνται μέσω containers και αν αυτό μπορεί να δώσει λύση σε περιορισμούς πραγματικού χρόνου. 11 Τα συμπεράσματα που προέκυψαν από αυτή τη διπλωματική εργασία κυρίως αφορούν τα πλεονεκτήματα που θα έχουν τα συστήματα τα οποία εφαρμόζουν τεχνολογίες τύπου Internet of Things και container-based virtualization. Πιο συγκεκριμένα, μέσω αυτής της εργασίας φάνηκε ότι η διαδικασία παραγωγής μπορεί να γίνει πολύ δυναμική με τη χρήση των ΙοΤ-τεχνολογιών, καθώς επιτρέπει on-the-fly αλλαγή της παραμετροποίησης του συστήματος, αφού κάθε εξάρτημα δεν είναι περιορισμένο σε μία συγκεκριμένη διαδικασία παραγωγής. Ακόμα, φάνηκε η ευελιξία που δίνει η χρήση containers για τη διαδικασία ανάπτυξης και συντήρησης του λογισμικού σε ένα βιομηχανικό σύστημα. Παρ’ όλα αυτά, έγιναν αισθητά και τα μειονεκτήματα που έχει η παραπάνω προσέγγιση ανάπτυξης λογισμικού για συγκεκριμένες εφαρμογές, όπως αυτές που είναι πολύ ευαίσθητες ως προς τον χρόνο και την επίδοση ή τις εφαρμογές που έχουν εξαρτήματα τα οποία είναι πολύ σφιχτά συζευγμένα μεταξύ τους και δεν θα επωφελούνταν ιδιαίτερα από την χρήση μίας κατανεμημένης αρχιτεκτονικής ή/και των container. Οι παραπάνω μελέτες θα αναλυθούν περισσότερο στα κεφάλαια που ακολουθούν, με τον εξής τρόπο: Στο κεφάλαιο 2 γίνεται μια λεπτομερής αναφορά για την τεχνολογία του Internet of Things, και οι βασικές προσεγγίσεις που έχουν γίνει για την χρήση της. Στο κεφάλαιο 3 παρουσιάζεται ένα case study που αφορά ταυτόχρονη παραγωγή δύο ειδών liqueur με χρήση τεσσάρων σιλό. Επίσης, γίνεται αναφορά στα πειράματα που έγιναν για εξοικείωση με την τεχνολογία του ΙοΤ με χρήση του liqueur case study, και πιο συγκεκριμένα με τον μικροεπεξεργαστή XDK της Bosch, ένα από τα οποία έλαβε και διάκριση σε αντίστοιχο διαγωνισμό ιδεών της κατασκευάστριας εταιρείας της ΙοΤ συσκευής. Στο κεφάλαιο 4 γίνεται μία εκτενής αναφορά σε μία από τις βασικές τεχνολογίες που χρησιμοποιήθηκαν, αυτή των containers, καθώς και τι είναι τα microservices, πώς συνδέονται με το container-based virtualization και αν αυτές οι τεχνολογίες έχουν θέση σε ένα βιομηχανικό περιβάλλον. Στο κεφάλαιο 5 μελετώνται τεχνολογίες που μπορούν να χρησιμοποιηθούν για επικοινωνία μεταξύ των microservices. Στην αρχή, παρουσιάζονται τα χαρακτηριστικά του κάθε πρωτοκόλλου και στη συνέχεια γίνεται σύγκριση της απόδοσης τους. Στο κεφάλαιο 6 παρουσιάζεται μία πλήρης υλοποίηση ενός σιλό του liqueur plant case study με τεχνολογίες IoT, containers και αρχιτεκτονικής συστήματος τύπου microservices. Το σιλό αυτό συνεργάζεται με άλλα σιλό υλοποιημένα με διαφορετικές τεχνολογίες, για την προσομοίωση παραγωγής δύο διαφορετικών ειδών liqueur. Στο κεφάλαιο 7 παρουσιάζονται μετρήσεις της υλοποίησης που πραγματοποιήθηκε στο κεφάλαιο 6. Στο κεφάλαιο 8 παρουσιάζονται τα συμπεράσματα που προέκυψαν από τη συγκεκριμένη εργασία, καθώς και προτάσεις α) για βελτίωση του συστήματος το οποίο κατασκευάστηκε και β) για τη συνέχεια της έρευνας. 12 Κεφάλαιο 2 2. Διαδίκτυο των Αντικειμένων Το Internet of Things (IoT) είναι ένα δίκτυο φυσικών αντικειμένων που έχουν κατασκευαστεί έτσι ώστε να έχουν «έξυπνη» συμπεριφορά. Τα αντικείμενα αυτά συνήθως έχουν στη διάθεσή τους ένα εύρος αισθητήρων, υλικό και λογισμικό καθώς και σύνδεση στο δίκτυο. Έτσι, έχουν την δυνατότητα να συλλέγουν και να ανταλλάσσουν δεδομένα, επιτρέποντάς τους να χρησιμοποιηθούν σε ένα εύρος εφαρμογών. Σε αυτό το κεφάλαιο θα γίνει μία εκτενής αναφορά στο τι είναι το Internet of Things, η συνοπτική του ιστορία και πού συναντάται ως τεχνολογία στις μέρες μας. Ακόμα, θα παρουσιαστούν τα προβλήματα που αντιμετωπίζει μία σύγχρονη αλλά κλασική βιομηχανική μονάδα και γιατί το Internet of Things αξίζει να ερευνηθεί ως μία βιώσιμη λύση για μερικά από αυτά τα προβλήματα. 2.1 Ιστορία του Internet of Things και Industry 4.0 Η 1η βιομηχανική επανάσταση χαρακτηρίζεται από τη μετάβαση σε νέες διαδικασίες παραγωγής. Ο άνθρωπος, αντί να χρησιμοποιεί ζώα ή τα ίδια του τα χέρια για την πραγματοποίηση σκληρών εργασιών, άρχισε να χρησιμοποιεί μηχανές, οι οποίες δούλευαν με τη δύναμη του ατμού [5]. Η 2η βιομηχανική επανάσταση, γνωστή και ως «Τεχνολογική Επανάσταση», έκανε την εμφάνισή της όταν πολύ σημαντικές εφευρέσεις του Nikola Tesla έκαναν τη χρήση του ηλεκτρικού ρεύματος δυνατή από τις βιομηχανίες. Το ηλεκτρικό ρεύμα ήταν πλέον εύκολο να μεταφερθεί σε σχετικά μεγάλες αποστάσεις και η χρήση του ήταν σημαντικά πιο πρακτική από αυτή του ατμού και του νερού. Αυτή την περίοδο οι πρώτες γραμμές παραγωγής έκαναν και την εμφάνισή τους και τα εργοστάσια άρχισαν να έχουν μεγαλύτερες και πιο οργανωμένες δομές [6]. Η 3η βιομηχανική επανάσταση, γνωστή και ως «Ψηφιακή Επανάσταση», έκανε την εμφάνισή της τις τελευταίες δεκαετίες του 20ου αιώνα με την εφεύρεση του transistor. Υπολογιστικά συστήματα και λογισμικό άρχισαν να χρησιμοποιούνται στις γραμμές παραγωγής και ο έλεγχος και η επεξεργασία των δεδομένων άρχισε να γίνεται με τρόπο αυτοματοποιημένο και ψηφιακό, αντικαθιστώντας σημαντικό ποσοστό ανθρώπων ελεγκτών και επισπεύδοντας σημαντικά τις διαδικασίες αυτές. Η 4η βιομηχανική επανάσταση, η οποία συμβαίνει τη στιγμή που γράφεται αυτό το κείμενο, αφορά την εμφάνιση cyber-physical συστημάτων (CPS) και ΙοΤ στο πλαίσιο του Industry (Industrial Internet of Things, IIot) [7] [8]. Η εργασία αυτή ερευνά τη χρησιμότητα του IoT σε ένα βιομηχανικό περιβάλλον. Το διαδίκτυο των αντικειμένων (Internet of Things - IoT) είναι ένα δίκτυο φυσικών αντικειμένων τα οποία σε συνδυασμό με ηλεκτρονικά, λογισμικό και αισθητήρες μπορούν να μαζεύουν και να ανταλλάσσουν δεδομένα για να πετύχουν κάποιο σκοπό [9]. Υπάρχουν και άλλοι ορισμοί για το τι είναι το διαδίκτυο των αντικειμένων, όπως αυτός που δόθηκε από τη Cisco Systems: “Η στιγμή όπου θα υπάρχουν περισσότερα “αντικείμενα” από ανθρώπους συνδεδεμένα στο Internet”. Αυτή η στιγμή έχει περάσει εδώ και πάνω από μία δεκαετία και η διαφορά ολοένα και αυξάνεται. 13 Η πρώτη αναφορά για το IoT έγινε στο Carnegie Mellon University στις Ηνωμένες πολιτείες της Αμερικής το 1982, όπου ένα τροποποιημένο μηχάνημα κόκα-κόλας μπορούσε να απαντήσει για το τι ποτά έχει μέσα και για το αν είναι κρύα. Στη συνέχεια έγιναν προσπάθειες από εταιρείες για την κατασκευή επίσημων πρωτοκόλλων που θα επιτρέπουν την επικοινωνία των συσκευών, όπως το Novell Embedded Systems Technology [10] και το At Work [11]. Μερικά χρόνια αργότερα ο όρος “Internet of Things” παρουσιάστηκε πρώτη φορά σε μία παρουσίαση που έκανε ο Kevin Ashton το 1999 [12]. Στις αρχές του 21ου αιώνα ο όρος άρχισε να χρησιμοποιείται όλο και πιο συχνά. Τις τελευταίες δεκαετίες το internet έχει αρχίσει να εισβάλλει μέσα στις ζωές των ανθρώπων πολύ δυναμικά και τα τελευταία χρόνια βλέπουμε αύξηση των έξυπνων αντικειμένων στην καθημερινότητα των ανθρώπων, όπως έξυπνα κινητά τηλέφωνα, έξυπνα ηχεία, έξυπνες τηλεοράσεις, πλυντήρια, ρολόγια κ.ά.. Αυτό έχει ως συνέπεια ο όρος “Internet of Things” να χρησιμοποιείται στην καθομιλουμένη. Στις μέρες μας, το πιο συχνό αντικείμενο τύπου IoT είναι με μεγάλη διαφορά το smartphone. Το smartphone ταιριάζει στην κλασική περιγραφή ενός IoT, καθώς περιέχει ένα σύνολο από αισθητήρες και σύνδεση στο δίκτυο μέσα από το οποίο μπορεί να συνεργάζεται με άλλες IoT συσκευές, όπως για παράδειγμα άλλα κινητά τηλέφωνα. Παρ ’όλα αυτά στην καθημερινότητα των ανθρώπων αρχίζουν και εμφανίζονται όλο και συχνότερα και άλλα αντικείμενα τύπου IoT όπως τα fit-bands (fitness bands) και τα smart-watches. Τα fit-bands αποτελούν ακόμα ένα κλασικό παράδειγμα μιας IoT τεχνολογίας, καθώς περιλαμβάνουν αισθητήρες που μπορούν να μετράνε τα βήματα που έχει κάνει ένας χρήστης, πότε κοιμάται και πόσο ποιοτικός ήταν ο ύπνος του, εάν έχει μείνει αδρανής για μεγάλο χρονικό διάστημα αλλά και πόσους σφυγμούς έχει. Η σύνδεση στο δίκτυο των fit-bands συνήθως γίνεται μέσω BLE (Bluetooth Low Energy). Έτσι το fit-band συνδέεται με κάποια άλλη ηλεκτρονική συσκευή, τυπικά ένα κινητό τηλέφωνο, η οποία αναλαμβάνει την επεξεργασία δεδομένων και τη σύνδεση στο διαδίκτυο, διεργασίες οι οποίες είναι υπολογιστικά και ενεργειακά ακριβές. Με αυτόν τον τρόπο, τα fit-bands έχουν καταφέρει να διατηρήσουν τις τιμές τους πολύ χαμηλά, εφόσον η επεξεργασία και η παρουσίαση των δεδομένων γίνεται σε μια συσκευή με μεγαλύτερη υπολογιστική ισχύ και κατά συνέπεια η χρήση τους είναι καθημερινή από μεγάλο ποσοστό του πληθυσμού. Fitness Band 120 100 80 60 40 20 2004-01 2004-08 2005-03 2005-10 2006-05 2006-12 2007-07 2008-02 2008-09 2009-04 2009-11 2010-06 2011-01 2011-08 2012-03 2012-10 2013-05 2013-12 2014-07 2015-02 2015-09 2016-04 2016-11 2017-06 2018-01 2018-08 2019-03 2019-10 0 Εικόνα 1 Google Trends για τον όρο αναζήτησης «Fitness Band» [13] 14 2.2 IoT πρωτόκολλα Ένα από τα βασικά χαρακτηριστικά των IoT συσκευών είναι ότι επικοινωνούν μεταξύ τους, αφού IoT κυριολεκτικά σημαίνει «Διαδίκτυο των Αντικειμένων». Τα αντικείμενα είναι πολυποίκιλων ειδών και έχουν την ανάγκη να ανταλλάσσουν πληροφορίες με έναν γρήγορο, ασφαλή και ενεργειακά αποδοτικό τρόπο. Αυτό δημιουργεί την ανάγκη της τυποποίησης του τρόπου με τον η επικοινωνία λαμβάνει χώρα, δηλαδή την δημιουργία πρωτοκόλλων επικοινωνίας που επιτρέπουν σε συσκευές που έχουν κατασκευαστεί με διαφορετικές τεχνολογίες να μπορούν να «μιλάνε την ίδια γλώσσα», ανοίγοντας τον δρόμο για τη διαλειτουργικότητα (interoperability) των IoT συσκευών. Όμως, δεν είναι όλα τα πρωτόκολλα επικοινωνίας ίδια. Διαφορετικές ανάγκες οδηγούν στη δημιουργία διαφορετικών πρωτοκόλλων, όπως έγινε και με το Διαδίκτυο, με τα πρωτόκολλα επιπέδου μεταφοράς TCP και UDP που παρουσιάζουν σημαντικές διαφορές. Έτσι και στις τεχνολογίες του IoT, εκτός από τα πρωτόκολλα επικοινωνίας, δημιουργήθηκε η ανάγκη ανάπτυξης διαφορετικών τεχνικών ανταλλαγής μηνυμάτων ανάλογα με τον τύπο του προβλήματος που οι IoT συσκευές καλούνται να λύσουν. 2.2.1 Τεχνικές ανταλλαγής μηνυμάτων Το message queuing είναι μία τεχνική η οποία είναι σχεδιασμένη για τύπους εργασίας που έχουν μορφή λίστας από διεργασίες (task list / work queue). Ένα πρακτικό παράδειγμα είναι ένα πρόγραμμα το οποίο λαμβάνει αρχεία από εξωτερική πηγή και τα προωθεί για επεξεργασία σε ένα διαφορετικό πρόγραμμα (service to service communication). Αυτή η τεχνική περιλαμβάνει έναν ή περισσότερους παραγωγούς (producers), οι οποίοι παράγουν εργασίες οι οποίες πρέπει να διεκπεραιωθούν, και τις τοποθετούν στη λίστα, αλλά και έναν ή περισσότερους consumers, οι οποίοι αναλαμβάνουν, φέρουν εις πέρας και απομακρύνουν από τη λίστα τις αντίστοιχες διεργασίες. Σε πολλές περιπτώσεις ο αριθμός τόσο των producers όσο των consumers δεν είναι σταθερός, αλλά δυναμικά μεταβαλλόμενος ανάλογα με τον αριθμό και τη δυσκολία των εργασιών που πρέπει να διεκπεραιωθούν. Το message queuing είναι συνήθως κατάλληλο για διεργασίες που δεν έχουν αυστηρούς χρονικούς περιορισμούς και πρακτικά χρησιμοποιείται για επεξεργασίες δεδομένων που απαιτούν πολλούς πόρους. Σε μία κλασική διαδικτυακή εφαρμογή, για παράδειγμα, οι διακομιστές μπορεί να είναι οι producers που τοποθετούν διεργασίες στο work queue σύμφωνα με τα αρχεία προς επεξεργασία τα οποία ανεβάζουν (upload) οι χρήστες της εφαρμογής. Από την άλλη, οι consumers μπορεί να είναι εξαιρετικά βελτιστοποιημένα προγράμματα που αναλαμβάνουν την επεξεργασία των αρχείων αυτών. Τέτοιου είδους τμηματοποίηση των ευθυνών κάθε στοιχείου του συστήματος μπορεί να κάνει το σύστημα πολύ πιο αποδοτικό, γιατί η κάθε τεχνολογία μπορεί και χρησιμοποιείται για να λύσει προβλήματα τα οποία έχει σχεδιαστεί να λύνει αποτελεσματικά. Αντίστοιχα, στο χώρο του IoT, ένα σύνολο από συσκευές μπορούν να συνεργάζονται μεταξύ τους και η κάθε μία να έχει διαφορετικές ευθύνες. Μηνύματα μπορούν να παράγονται από διαφορετικές συσκευές και να καταναλώνονται από IoT-worker-συσκευές. Ένα απλό παράδειγμα είναι η χρήση robot σε μία αποθήκη δεμάτων. Μία ή περισσότερες IoT συσκευές με αισθητήρες (κάμερα, QR-code scanner) μπορούν να προσθέτουν στο work queue εργασίες που αφορούν τη μεταφορά δεμάτων. IoT-ρομπότ τα οποία έχουν πρόσβαση στο work queue και μπορούν να αναλάβουν εργασίες από αυτό, μπορούν να κάνουν τη μεταφορά των δεμάτων μέσα στην αποθήκη. Έτσι μπορεί να αυτοματοποιηθεί η μέγιστη παραλληλία εκπόνησης διεργασιών αλλά και όχι μόνο. Αυτόματα, IoT-ρομπότ θα μπορούσαν να απενεργοποιούνται και να ενεργοποιούνται ανάλογα με το μέγεθος και τη συχνότητα προσθήκης διεργασιών στο work queue, μειώνοντας και τα κόστη λειτουργίας και συντήρησης των μηχανημάτων της αποθήκης. Στις περισσότερες εφαρμογές που γίνεται χρήση ενός work queue, χρειάζεται ένας message broker ο οποίος είναι υπεύθυνος για την παραλαβή των μηνυμάτων, την αποθήκευσή τους, και την προώθησή τους στους consumers που είναι διαθέσιμοι. Στην περίπτωση του message queuing ο 15 broker μπορεί να είναι υλοποιημένος με απλό τρόπο, καθώς σήμερα πολλές από τις απαιτήσεις ενός broker μπορούν να ικανοποιηθούν και από ένα τυπικό σύστημα βάσης δεδομένων, όπως η PostgreSQL. Αυτή η βάση δεδομένων δίνει τη δυνατότητα κλειδώματος γραμμών / διεργασιών (LOCK) [14] καθώς και δυνατότητες querying αποφεύγοντας τις ήδη κλειδωμένες γραμμές (SKIP LOCKED) [15], δηλαδή αποφυγή των διεργασιών οι οποίες βρίσκονται προς επεξεργασία από άλλους workers. Publish-Subscribe Messaging είναι μία τεχνική ανταλλαγής μηνυμάτων κατά την οποία τα συστήματα που θέλουν να επικοινωνήσουν μεταξύ τους κάνουν subscribe και publish σε topics. Εδώ μπορεί να θεωρηθεί ότι τα services που επικοινωνούν μεταξύ τους μπορούν να δρουν ταυτόχρονα ως producers και ως consumers, χωρίς δηλαδή το καθένα να έχει ένα συγκεκριμένο ρόλο. Ένα service μπορεί να είναι subscribed σε ένα ή περισσότερα topics και να μπορεί να κάνει publish σε ένα ή περισσότερα topics. Εικόνα 2 Publish-Subscribe-Messaging Αυτού του είδους η επικοινωνία είναι χρήσιμη όταν για ένα συγκεκριμένο γεγονός δεν χρειάζεται απαραίτητα να δράσει μόνο ένα διαφορετικό πρόγραμμα/service, αλλά πολλά. Επιστρέφοντας στο παράδειγμα της αποθήκης, όταν λαμβάνεται ένα νέο δέμα, πέρα από το να ειδοποιηθεί ένα IoT-ρομπότ, αν θέλουμε να καταγράψουμε αυτήν την παραλαβή σε μία βάση δεδομένων για data analytics, μπορεί να χρησιμοποιηθεί το Publish-Subscribe μοντέλο. Δηλαδή, μόλις γίνει παραλαβή ενός δέματος, ένα μήνυμα να σταλεί σε ένα topic στο οποίο είναι εγγεγραμμένοι τόσο τα IoT-ρομπότ όσο και ένα πρόγραμμα εγγραφής δεδομένων σε data-analytics βάσης δεδομένων. Με την τεχνική του pub-sub messaging, όταν ένα μήνυμα γίνει publish σε ένα topic, αυτό το μήνυμα μεταφέρεται όσο το δυνατόν γρηγορότερα στα services που έχουν κάνει subscribe σε αυτό το topic. Οι publishers δεν γνωρίζουν τι γίνεται με το μήνυμα το οποίο στέλνουν. Αυτό πιστοποιεί ότι τα διαφορετικά services είναι χαλαρά συνδεδεμένα και διατηρείται το modularity. Η χρησιμότητα του publish-subscribe messaging φαίνεται κατά κύριο λόγο όταν υπάρχει ανάγκη για parallel processing της πληροφορίας, δηλαδή όταν χρειάζεται να εκτελεστούν ταυτόχρονα δύο ή παραπάνω μέρη του business logic. Αυτή η τεχνική χρειάζεται έναν message broker ο οποίος θα πρέπει να ξέρει τη λίστα από τα topics καθώς και ποια services είναι subscribed σε ποια topics και να αναλαμβάνει και την προώθηση των μηνυμάτων στους subscribers. Η βάση δεδομένων PostgreSQL μπορεί να χρησιμοποιηθεί και ως ένας broker ενός pub-sub messaging συστήματος, μέσω των λειτουργιών NOTIFY [16], LISTEN [17] και UNLISTEN [18]. Η τεχνικές ανταλλαγής μηνυμάτων «message queuing» και «pub-sub messaging» μπορούν να χρησιμοποιηθούν ταυτόχρονα σε διαφορετικά μέρη ενός συστήματος, ανάλογα με τις ανάγκες της κάθε εφαρμογής. Για παράδειγμα, στην αποθήκη δεμάτων μπορεί να θέλουμε να ειδοποιηθούν 2 16 διεργασίες για την άφιξη ενός δέματος, αλλά ταυτόχρονα τα IoT-ρομπότ να αναλαμβάνουν από ένα δέμα το καθένα και να πετυχαίνουμε την μέγιστη παραλληλία, κάτι που καθιστά τη χρήση και των δύο αυτών τρόπων ανταλλαγής μηνυμάτων, συνετή. Ένας τρόπος απλής υλοποίησης αυτού του συστήματος είναι όλες οι IoT συσκευές να είναι συνδεδεμένες σε μία PostgreSQL βάση δεδομένων. Οι subscribers κάνουν LISTEN σε channels και οι publishers κάνουν NOTIFY τις κατάλληλες στιγμές, όταν για παράδειγμα έρχεται ένα καινούργιο δέμα. Ταυτόχρονα, ένας πίνακας της βάσης δεδομένων μπορεί να χρησιμοποιηθεί για το work queue με χρήση των εντολών LOCK και SKIP LOCKED και να αποφευχθεί η χρήση του «polling» από τα IoT robots για νέες διεργασίες που πρέπει να εκπονηθούν, αφού πλέον μπορούν να ειδοποιηθούν για νέα δέματα μέσω του pub-sub messaging. 2.2.2 Το MQTT πρωτόκολλο Το MQTT (Message Queuing Telemetry Transport) πρωτόκολλο είναι βασισμένο στην τεχνική του publish-subscribe και δουλεύει χρησιμοποιώντας το TCP/IP πρωτόκολλο. Αυτό το πρωτόκολλο συναντάται συχνά σε IoT εφαρμογές και ένα σύστημα το οποίο το χρησιμοποιεί απαρτίζεται από έναν server, ο οποίος παίζει τον ρόλο του message broker, και πολλούς clients, οι οποίοι παίζουν τον ρόλο των subscribers και των publishers. Κάθε client μπορεί να είναι publisher, subscriber ή και τα δύο ταυτόχρονα, χωρίς περιορισμούς. Με αυτό το χαρακτηριστικό του publishsubscribe messaging, οι subscribers και οι publishers δεν χρειάζεται να έχουν καμία πληροφορία οι μεν για τους δε πέρα από το πώς να επικοινωνήσουν με τον broker, είτε για να δηλώσουν ενδιαφέρον σε ένα topic είτε για να κάνουν publish σε ένα topic [19]. Ένα ισχυρό χαρακτηριστικό του MQTT πρωτοκόλλου είναι ότι τα topics μπορούν να οργανωθούν ιεραρχικά και με τον τρόπο αυτό ένα service μπορεί να κάνει subscribe σε πολλά topics χωρίς να ξέρει τα συγκεκριμένα ονόματα αυτών. Για παράδειγμα, ένα topic μπορεί να έχει το όνομα /car/seat1/temperature και ένα άλλο /car/seat2/temperature και ένας subscriber, χρησιμοποιώντας το topic car/+/temperature να κάνει subscribe ταυτόχρονα και στα δύο αυτά topics. Αυτός ο subscriber τώρα μπορεί να ενημερώνεται για τη θερμοκρασία των θέσεων ενός αυτοκινήτου ανεξάρτητα από τον αριθμό αυτών. Τέλος, αξίζει να αναφερθεί ότι το specification του MQTT αναφέρει 3 διαφορετικά επίπεδα QoS (Quality of Service). Το κάθε επίπεδο προσθέτει επιπλέον καθυστέρηση στην επικοινωνία αλλά παρέχει και περισσότερες λειτουργίες ασφαλείας: 1. Επίπεδο Fire and Forget: Το μήνυμα στέλνεται μόνο μία φορά και δεν γίνεται κάποια προσπάθεια επιβεβαίωσης ότι το μήνυμα έχει ληφθεί από τους ενδιαφερόμενους subscribers. Αυτό είναι το πιο γρήγορο επίπεδο που προσφέρει το MQTT και όπως φαίνεται έχει πολλά κοινά χαρακτηριστικά με το UDP. 2. Επίπεδο Acknowledged Delivery: Το μήνυμα στέλνεται από τον MQTT client τουλάχιστον μία φορά. Το μήνυμα ξαναστέλνεται μέχρις ότου πιστοποιηθεί από τον broker ότι κάποιος έχει λάβει το μήνυμα. Αυτό έχει ως αποτέλεσμα ότι το μήνυμα μπορεί να ληφθεί παραπάνω από μία φορές και πρέπει πιθανόν να αναπτυχθούν τεχνικές τύπου idempotency, δηλαδή το μήνυμα να φέρει μέσα του ένα μοναδικό αναγνωριστικό το οποίο οι subscribers να αποθηκεύουν και να αγνοούν μελλοντικά μηνύματα με το ίδιο αναγνωριστικό. 3. Επίπεδο Assured Delivery: Ο MQTT client και broker κάνουν ένα four-way handshake που πιστοποιεί ότι το μήνυμα έχει σταλθεί και ληφθεί ακριβώς μία φορά από τον publisher και τους subscribers αντίστοιχα. Αυτό προσθέτει ακόμα μεγαλύτερη καθυστέρηση στην επικοινωνία, αλλά πλέον δεν χρειάζεται σε επίπεδο εφαρμογής να κατασκευαστεί η λογική αγνόησης διπλών μηνυμάτων, κάνοντας την κατασκευή τόσο του publisher όσο και του subscriber πιο απλή. 17 Η χρησιμότητα του MQTT στο IIoT γίνεται προφανής αν κανείς αναλογιστεί ότι οι έξυπνοι αισθητήρες αποτελούν βασικό κομμάτι του IoT. Πολλές έξυπνες συσκευές μπορούν να κάνουν subscribe στις τιμές που δίνει ένας αισθητήρας, να ενημερώνονται γι’ αυτές και να εκτελούν παράλληλες διεργασίες. Μία ακόμα σύνηθες χρήση είναι για μαζικό έλεγχο έξυπνων συσκευών, όπως για παράδειγμα ο έλεγχος ενός wind farm όπου πρέπει να ενεργοποιηθεί το φρένο των ανεμογεννητριών λόγω ενημέρωσης από ένα έξυπνο κέντρου ελέγχου για θυελλώδεις ανέμους. Τα μεγαλύτερα πλεονεκτήματα του MQTT είναι η μαζικότητα με την οποία κάποιος μπορεί να κάνει subscribe σε topics και τα διαφορετικά επίπεδα ασφαλείας τα οποία προσφέρει, κάνοντάς το κατάλληλο για πολλές διαφορετικές εφαρμογές με διαφορετικές προδιαγραφές. 2.2.3 Το LwM2M πρωτόκολλο Το LwM2M (Lightweight Machine-2-Machine) πρωτόκολλο επικοινωνίας είναι κατασκευασμένο από την OMA (Open Mobile Alliance) με σκοπό το να υπάρχει η δυνατότητα ενός standard για την επικοινωνία μεταξύ ελαφρών και χαμηλής κατανάλωσης ενέργειας συσκευών. Τα βασικά χαρακτηριστικά του LwM2M είναι: 1. Παρέχει ένα σύνολο από smart objects και τα χαρακτηριστικά τους, γνωστό ως το LwM2M Object and Resource Registry [20]. Αυτό το registry περιλαμβάνει μία λίστα από smart objects και τους πόρους τους. Η registry είναι επεκτάσιμη, δηλαδή μία εταιρεία μπορεί να δηλώσει καινούργια αντικείμενα στο registry. Αυτό σημαίνει ότι 2 μηχανήματα, χωρίς να έχουν επικοινωνήσει ποτέ πριν το ένα με το άλλο, μπορούν να γνωρίζουν το ένα την προγραμματιστική διεπαφή του άλλου αν τα χαρακτηριστικά αυτής της διεπαφής είναι δηλωμένα στο registry, και να επικοινωνήσουν με άψογη συμβατότητα. Το πρωτόκολλο ορίζει objects (όπως για παράδειγμα το θερμόμετρο), instances των objects (π.χ. θερμόμετρο #1, θερμόμετρο #2 κλπ) και πόρους (resources, π.χ. θερμοκρασία). Τα resources μπορούν να είναι διαφορετικών τύπων, όπως αριθμοί ή χαρακτήρες, και μπορούν να είναι Readable, Readable/Writable και Observable. Ένα Observable resource μπορεί να ειδοποιήσει τους Observators του όταν η τιμή του αλλάζει, χωρίς να χρειάζεται οι ενδιαφερόμενοι να κάνουν polling, μειώνοντας σημαντικά τον όγκο των δεδομένων, την ενεργειακή κατανάλωση και τη συμφόρηση στο δίκτυο. 2. Ένας LwM2M client πρέπει να κάνει register σε έναν LwM2M server και να δηλώσει το Object ID του, αν αυτό υπάρχει στο registry ή ακόμα και να δηλώσει κάποιο τροποποιημένο ανεπίσημο Object. Στη συνέχεια τού δίνεται η δυνατότητα να κάνει αναζήτηση των υπόλοιπων LwM2M enabled clients και να δει τα resources τους. Μέσω του LwM2M server οι clients που έχουν κάνει register σε αυτόν μπορούν να επικοινωνούν με τους υπόλοιπους clients, παρατηρώντας ή αλλάζοντας τα resources τους. Ο LwM2M server έχει τη δυνατότητα να ζητήσει κωδικό πριν ένας client μπορέσει να μπει στο δίκτυο συσκευών που αυτός διαχειρίζεται για λόγους ασφαλείας. 3. Είναι βασισμένο στο πρωτόκολλο CoAP (Constrained Application Protocol), το οποίο χρησιμοποιείται κυρίως μέσω UDP και είναι βασισμένο στην αρχιτεκτονική Rest, όπου δηλαδή οι διακομιστές διαθέτουν πόρους σε συγκεκριμένα URLs και οι clients έχουν πρόσβαση σε αυτά τα URLs μέσω των HTTP verbs GET, PUT, POST και DELETE. Το CoAP έχει πολλά κοινά με το HTTP γι’ αυτό δεν έχει απότομη καμπύλη εκμάθησης από τους developers οι οποίοι πιθανόν είναι ήδη εξοικειωμένοι με το HTTP. Το CoAP είναι ειδικά σχεδιασμένο για να δουλεύει σε μικροεπεξεργαστές χαμηλής ισχύος και συσκευές χαμηλής κατανάλωσης ενέργειας. Συγκεκριμένα, είναι σχεδιασμένο για να τρέξει σε συσκευές με Random Access Memory μεγέθους από 10 KiB και πάνω, το οποίο είναι εντυπωσιακό [21]. 18 Οι τελευταίες εκδόσεις του LwM2M υποστηρίζουν UDP, TCP (+TLS), SMS, Cellular IoT και LoRaWAN (low power - wide area network protocol). Ενώ μπορεί να χρησιμοποιηθεί data transport layer security (DTLS), το CoAP παρέχει υποστήριξη και για το OSCORE extension, το οποίο πιστοποιεί την προστασία όχι μόνο των δεδομένων αλλά και των endpoints και των χρησιμοποιούμενων verbs (π.χ. OBSERVE) σε περίπτωση που αυτά τα δεδομένα μπορούν να αποκαλύψουν ευαίσθητες πληροφορίες. Εικόνα 3 Το LwM2M protocol stack στην έκδοση v1.1 [22] Υπάρχουν δύο βασικά πλεονεκτήματα στο LwM2M πρωτόκολλο επικοινωνίας. Πρώτον, το πρωτόκολλο αυτό είναι ειδικά σχεδιασμένο να δουλεύει σε συσκευές χαμηλής κατανάλωσης ενέργειας. Τέτοιου είδους απαιτήσεις συναντώνται συχνά σε συσκευές τύπου IoT γιατί πολλές από αυτές λειτουργούν με μπαταρίες λόγω της ανάγκης για φορητότητα, όπως fit-bands, smartwatches και smartphones. Παρ’ όλα αυτά, η μικρή κατανάλωση ενέργειας είναι καθολικά θεμιτή. Για παράδειγμα, σε ένα έξυπνο εργοστάσιο η μείωση της ενέργειας που καταναλώνεται από τις επιμέρους συσκευές κατά 5% μπορεί να σημαίνει εξοικονόμηση πολλών χιλιάδων ή και εκατομμυρίων ευρώ το χρόνο. Και φυσικά, δεν πρέπει να αγνοούνται οι επιπτώσεις στο περιβάλλον από τις υψηλές ενεργειακές ανάγκες. Δεύτερον, η χρήση του registry που περιλαμβάνει την περιγραφή όλων των επίσημων smart αντικειμένων ανοίγει δρόμο για interoperability. Ένα έξυπνο αντικείμενο που χρησιμοποιεί το LwM2M πρωτόκολλο και ακολουθεί την περιγραφή του έξυπνου αντικειμένου όπως αυτή υπάρχει μέσα στο registry, μπορεί να χρησιμοποιηθεί στη θέση ενός άλλου που κάνει την ίδια λειτουργία, ακόμα και αν εσωτερικά χρησιμοποιούν τελείως διαφορετικές τεχνολογίες λογισμικού ή και υλικού. Ακόμα, το γεγονός ότι το registry είναι δημόσια προσπελάσιμο σημαίνει ότι μία συσκευή μπορεί να «μάθει» πώς να μιλάει σε μία άλλη μέσα από το registry. Έτσι, διαφορετικές συσκευές φτιαγμένες από διαφορετικές εταιρείες και διαφορετικές τεχνολογίες που απλά υπακούν στο LwM2M πρωτόκολλο μπορούν να επικοινωνούν χωρίς πρόβλημα και με μικρή κατανάλωση ενέργειας σε ό,τι αφορά τις ανάγκες για δικτύωση. 19 Κεφάλαιο 3 3. Liqueur Plant case study Σε αυτό το κεφάλαιο μελετάται ένα case study που αφορά την κατασκευή liqueur σε ένα εργοστάσιο που αποτελείται από έξυπνα αντικείμενα τα οποία επικοινωνούν μεταξύ τους. Τα αντικείμενα συγχρονίζονται για την αποτελεσματική παραγωγή 2 διαφορετικών ειδών liqueur μέσα από 4 διαφορετικά σιλό. Για την μελέτη του συστήματος αυτού, προσομοιάζεται ένα σιλό χρησιμοποιώντας έναν μικροεπεξεργαστή της Bosch με αισθητήρες (Bosch XDK) σε συνεργασία με έναν hardware emulator ενός σιλό που δείχνει τις καταστάσεις που μπορεί να βρίσκεται αυτό μέσω της χρήσης LEDs, ενός buzzer και seven-segment displays. Χρησιμοποιώντας το LwM2M πρωτόκολλο, επιδεικνύονται οι διάφορες λειτουργίες που μπορεί να εκτελέσει το σιλό. Σκοπός του κεφαλαίου είναι η εξοικείωση με τις τεχνολογίες του IoT και η επίδειξη της λειτουργίας μιας συσκευής τύπου IoT μέσα από γνωστά IoT πρωτόκολλα. 3.1 The case study Το liqueur plant case study [23] [24] [25] [26] [27] [28] αφορά ένα εργοστάσιο παραγωγής liqueur μέσα από πέντε έξυπνα αντικείμενα: Τέσσερα έξυπνα σιλό και ένα έξυπνο σωλήνα ο οποίος τα ενώνει. Το εργοστάσιο μπορεί να παράγει 2 διαφορετικά είδη liqueur ταυτόχρονα. Εικόνα 4 Το Liqueur Plant Case Study [29] Κάθε σιλό αποτελείται από μία βαλβίδα εισόδου (IN VALVE) και μία βαλβίδα εξόδου (OUT VALVE). Το σιλό 2 και το σιλό 4 περιέχουν μία αντίσταση που ζεσταίνεται με χρήση ηλεκτρικού 20 ρεύματος για θέρμανση του υγρού. Το σιλό 3 και το σιλό 4 περιέχουν έναν αναδευτήρα για ανάδευση του υγρού. Τα σιλό μπορούν να μεταφέρουν το υγρό μεταξύ τους με χρήση ενός σωλήνα ο οποίος έχει 4 εισόδους/εξόδους που μπορούν να λειτουργούν ανεξάρτητα η μία από την άλλη, οδηγώντας το υγρό από και προς την επιθυμητή κατεύθυνση. Όλα τα έξυπνα αντικείμενα του συστήματος έχουν πρόσβαση στο Local Cloud μέσω WiFi, όπου μπορούν να επικοινωνούν μεταξύ τους και να συγχρονίζουν τη διαδικασία παραγωγής. Ο Operator μέσω του Local Cloud μπορεί να συντονίζει και να ελέγχει τη διαδικασία αυτή. Επίσης, ένας χρήστης του Internet μπορεί να κάνει παραγγελία ενός είδους liqueur του οποίου η παραγωγή θα αρχίσει ταυτόχρονα. Η παραγωγή του 1ου είδους liqueur γίνεται μέσα από τα σιλό 1 και 4. Το liqueur εισέρχεται για επεξεργασία στο σιλό 1. Μόλις τελειώσει η επεξεργασία στο σιλό 1, το liqueur περνάει στο σιλό 4 όπου γίνεται περαιτέρω επεξεργασία του μέσω θέρμανσης και ανάδευσης. Τέλος, το liqueur εξέρχεται από το σιλό 4. Η παραγωγή του 2ου είδους liqueur γίνεται μέσα από τα σιλό 2 και 3. Το liqueur εισέρχεται στο σιλό 2 όπου θερμαίνεται. Μετά, μέσω του σωλήνα, εισέρχεται στο σιλό 3 όπου αναδεύεται, πριν την έξοδό του. Η παραγωγή του κάθε είδους liqueur μπορεί να γίνεται ανεξάρτητα ή και τα δύο είδη να παράγονται ταυτόχρονα. Για την ταυτόχρονη παραγωγή liqueur όμως υπάρχουν οι εξής περιορισμοί: 1. Δεν μπορεί να γίνεται ταυτόχρονη χρήση του σωλήνα και από τις δύο διαδικασίες παραγωγής γιατί θα υπάρξει ανεπιθύμητο ανακάτεμα των δύο διαφορετικών ειδών liqueur. 2. Δεν μπορεί να υπάρξει ταυτόχρονη λειτουργία των αναδευτήρων στα σιλό 3 και 4 λόγω περιορισμούς στη συνολική κατανάλωσης ισχύος σε αυτό το μέρος του εργοστασίου. Σκοπός του liqueur plant case study δεν είναι τόσο να είναι ένα ρεαλιστικό παράδειγμα ενός εργοστασίου παραγωγής liqueur όσο να είναι ένα case study που αναδεικνύει προβλήματα και περιορισμούς που συναντώνται συχνά σε πραγματικές γραμμές παραγωγής και εργοστάσια. Μελετώντας αυτό το σύστημα στα πλαίσια του ΙοΤ μπορούμε να δούμε πώς συνεργάζονται οι διαφορετικές έξυπνες συσκευές για να ικανοποιήσουν τις απαιτήσεις του συστήματος και πώς το IoT μπορεί να είναι μια αποτελεσματική λύση σε αυτά τα προβλήματα. 3.2 Έξυπνο σιλό με το Bosch XDK Αν μία συσκευή περιέχει αισθητήρες που κάνουν γνωστή την κατάστασή τους μέσω ηλεκτρικών σημάτων και μπορούν να δεχτούν και εντολές για να αλλάξουν την κατάσταση κάποιου εξαρτήματός τους μέσω ηλεκτρικών σημάτων, τότε μπορεί εύκολα να μετατραπεί σε έξυπνη συσκευή ακολουθώντας τα εξής βήματα: 1. Ένας προγραμματιζόμενος επεξεργαστής ή ένας μικρό υπολογιστικό σύστημα που περιέχει και λειτουργικό σύστημα, τοποθετείται κοντά στη συσκευή. 2. Ο επεξεργαστής του υπολογιστικού συστήματος πρέπει να μπορεί να δέχεται και να στέλνει ηλεκτρικά σήματα με εξωτερικά στοιχεία του συστήματος. Πολλές συσκευές μπορούν να στείλουν και να λάβουν πολλών ειδών σήματα μέσω pins. Για παράδειγμα, τα Raspberry Pi μοντέλα δίνουν αυτή τη δυνατότητα μέσω μίας διεπαφής GPIO (GeneralPurpose Input/Output). 21 Εικόνα 5 Το GPIO interface του Raspberry Pi 2 Model B [30] 3. Μεταξύ των pins του υπολογιστικού συστήματος και των εισόδων/εξόδων ηλεκτρικών σημάτων της συσκευής, μπορεί να χρειαστεί να μπει ένα άλλο ηλεκτρικό κύκλωμα που να υποβιβάζει/εξυψώνει τα ηλεκτρικά σήματα της συσκευής και του συστήματος έτσι ώστε οι στάθμες και η διάρκειά τους να είναι συμβατές μεταξύ των δύο στοιχείων (levelshifting circuit). 4. Τα pins συνδέονται με τους εισόδους/εξόδους της συσκευής κατάλληλα. 5. Το υπολογιστικό σύστημα προγραμματίζεται έτσι ώστε να πραγματοποιεί την επιθυμητή λογική της έξυπνης συσκευής. Δηλαδή, διαβάζει από τα pins για να πάρει τις τιμές των αισθητήρων της συσκευής και στέλνει σε αυτά οποιαδήποτε εντολή θέλει με σκοπό να αλλάξει την κατάσταση της συσκευής. 6. Το υπολογιστικό σύστημα, για να μετατρέψει πραγματικά τη συσκευή σε μία τύπου IoT, θα πρέπει να έχει σύνδεση σε κάποιο δίκτυο, συνήθως μέσω Bluetooth ή Wi-Fi. Πληροφορίες σχετικά με την κατάσταση του συστήματος μπορεί να στέλνονται στο δίκτυο και εντολές για αλλαγή της κατάστασης του συστήματος μπορεί να έρχονται από το δίκτυο. Η συσκευή μπορεί να διαβάζει πληροφορίες από το διαδίκτυο και να δρα ανάλογα. Για παράδειγμα, ένα εργοστάσιο κατασκευής liqueur μπορεί να αυξάνει ή να μειώνει την παραγωγή του ανάλογα με το πόσες φορές αναφέρθηκε η λέξη liqueur στο Twitter τις προηγούμενες ώρες. Αν μία έξυπνη συσκευή προορίζεται για μαζική παραγωγή, το υπολογιστικό σύστημα θα μειωθεί στο μέγεθος μιας μικρής πλακέτας ειδικής για την εκάστοτε εφαρμογή και η σύνδεσή του με τη συσκευή θα γίνεται μέσω καλωδίων, ακριβώς όσων χρειάζονται για την μεταξύ τους επικοινωνία. Παρ’ όλα αυτά, πολλές εταιρείες παράγουν μικρά υπολογιστικά συστήματα και προγραμματιζόμενους επεξεργαστές με σκοπό το prototyping. Ένα από αυτά τα προγραμματιζόμενα συστήματα, που είναι και πιο συγκεκριμένα ειδικά σχεδιασμένο για το prototyping IoT εφαρμογών, είναι το XDK της Bosch: 22 Εικόνα 6 Το XDK της Bosch [4] Το Bosch XDK είναι ειδικά κατασκευασμένο για εφαρμογές τύπου Internet of Things, καθώς, πέρα από τον προγραμματιζόμενο επεξεργαστή και τα κατάλληλα εξαρτήματα για σύνδεση στο δίκτυο, τόσο με Bluetooth όσο και με Wi-Fi, περιλαμβάνει και μία σειρά από αισθητήρες. Αυτό σημαίνει ότι, για την κατασκευή του IoT prototype, δεν χρειάζεται να περιοριστούμε μόνο στους αισθητήρες που μας παρέχει η συσκευή που θέλουμε να μετατρέψουμε σε έξυπνη, αλλά μπορούμε να λαμβάνουμε τιμές και από τους αισθητήρες που έχει ήδη πάνω το XDK και να δρούμε ανάλογα. Η τελευταία έκδοση του XDK, τη στιγμή που γράφεται αυτό το κείμενο, περιλαμβάνει αξελερόμετρο, γυροσκόπιο, μαγνητόμετρο, θερμόμετρο καθώς και αισθητήρες πίεσης, υγρασίας, ήχου και φωτός [31]. Το Bosch XDK είναι προγραμματιζόμενο με χρήση της γλώσσας C και περιλαμβάνει βασικές βιβλιοθήκες για να μπορέσει ο προγραμματιστής να χειριστεί τα διαφορετικά του components, όπως τους αισθητήρες ή το Wi-Fi. Το Πανεπιστήμιο Πατρών έδωσε τη δυνατότητα σε εμένα και το συμφοιτητή μου, Ιάσονα Αθανάσογλου, τη δυνατότητα να πειραματιστούμε με 2 Bosch XDK προγραμματιζόμενους επεξεργαστές. Μελετήσαμε το Liqueur Plant case study που παρουσιάστηκε νωρίτερα σε αυτό το κεφάλαιο και με χρήση δύο Bosch XDK προσομοιάσαμε την έξυπνη λειτουργία δύο από των τεσσάρων σιλό, των σιλό 1 και 4, κατασκευάζοντας έτσι δύο IIoTs. Επειδή η κατασκευή του 2ου είδους liqueur γίνεται με συνεργασία των σιλό 1 και 4 και ενός έξυπνου σωλήνα, υλοποιήσαμε την διαδικασία αυτή, προσομοιώνοντας τον έξυπνο σωλήνα με χρήση λογισμικού, δηλαδή χωρίς να έχει κάποια φυσική υπόσταση. 23 Εικόνα 7 Ένα έξυπνο σιλό [29] Κάθε έξυπνο σιλό αποτελείται από 1. Μία συσκευή που προσομοιώνει με hardware ένα σιλό. 2. Το XDK της Bosch που έχει προγραμματιστεί έτσι ώστε να δρα σαν έναν Lightweight Machine-2-Machine client και έχει σύνδεση μέσω Wi-Fi στο τοπικό cloud. 3. Ένα level shifting κύκλωμα για interfacing μέσω to GPIO του XDK της Bosch με τα pins του σιλό. Το φυσικό σιλό προσομοιώθηκε με το SiloSimulator [32] που αναπτύχθηκε στα πλαίσια της διπλωματικής εργασίας του φοιτητή Χριστουλάκη Φοίβου [23]. To silo simulator είναι μία ηλεκτρονική κάρτα η οποία υλοποιεί τις διάφορες λειτουργίες του σιλό, όπως περιεγράφηκαν στο αντίστοιχο κεφάλαιο, με χρήση software και hardware, και είναι βασισμένο στο arduino. Εικόνα 8 Το Silo Simulator [32] περιέχει ένδειξη θερμοκρασίας, στάθμης υγρού, heater και mixer Το σιλό simulator περιέχει όλες τις λειτουργίες που θα μπορεί να έχει ένα σιλό και έχει pins με τα οποία μπορεί το Raspberry Pi να επικοινωνεί: 1. Pin FULL (OUT) που δηλώνει ότι το σιλό έχει γεμίσει 2. Pin EMPTY (OUT) που δηλώνει ότι το σιλό έχει αδειάσει 3. Pin HEATER (IN) αντίστροφης λογικής, που δίνει εντολή ξεκινήματος του heater 24 4. Pin INVALVE (IN) αντίστροφης λογικής, που δίνει εντολή ανοίγματος ή κλεισίματος της βαλβίδας IN 5. Pin OUTVALVE (IN) αντίστροφης λογικής, που δίνει εντολή ανοίγματος ή κλεισίματος της βαλβίδας OUT 6. Pin MIXER (IN) αντίστροφης λογικής, που δίνει εντολή ξεκινήματος του mixer Μέσω τη λογικής σύνδεσης αυτών των pins με αντίστοιχα pins του Raspberry Pi, δίνεται η δυνατότητα στον σιλό controller να ανταλλάσσει μηνύματα με το SiloSimulator. Για παράδειγμα, θα μπορεί να στέλνει εντολή στο simulator να γεμίσει (μέσω του INVALVE), να αδειάσει (μέσω του OUTVALVE), να θερμάνει (μέσω του HEATER) ή να ανακατέψει (μέσω του MIXER) το υγρό, αλλά και να λάβει μηνύματα για το πότε γέμισε και άδειασε το σιλό (μέσω των FULL, EMPTY). Ακόμα, το SiloSimulator δίνει τη δυνατότητα ανάγνωσης της τρέχουσας θερμοκρασίας του σιλό έτσι ώστε να πραγματοποιηθεί θέρμανση μέχρι μία συγκεκριμένη θερμοκρασία. Στα πλαίσια αυτής της διπλωματικής εργασίας δεν γίνεται ανάγνωση αυτής της τιμής αλλά ο heater ενεργοποιείται για μόνο για συγκεκριμένο χρονικό διάστημα. Μέσα από τα pins του silo hardware simulator μπορούν να διαβαστούν οι αισθητήρες του, δηλαδή η στάθμη του υγρού μέσα στο σιλό και η θερμοκρασία του. Ακόμα, μπορούν να ελεγχθούν οι βαλβίδες εισόδου και εξόδου του σιλό καθώς και να ενεργοποιηθεί ή να απενεργοποιηθεί ο αναδευτήρας και η αντίσταση θέρμανσης. Ο κώδικας αρχικοποίησης του Bosch XDK δημιουργεί ένα προσαρμοσμένο LwM2M object, το Smart Silo Object του οποίου τα χαρακτηριστικά φαίνονται στον παρακάτω πίνακα: Πίνακας 1: χαρακτηριστικά του Smart Silo Object Όνομα resource Τύπος Access Level Περιγραφή state String Read Δίνει την τρέχουσα κατάσταση του σιλό, όπως “filled” κλπ. initialize Boolean Execute Δίνει εντολή αρχικοποίησης στο σιλό. fill Boolean Execute empty Boolean Execute heat Boolean Execute mix Boolean Execute stop_filling Boolean Execute stop_emptying Boolean Execute stop Boolean Execute start Boolean Execute filling completed Boolean Read emptying completed Boolean Read Δίνει εντολή στο σιλό να ανοίξει τη βαλβίδα εισόδου έως ότου γεμίσει με υγρό. Δίνει εντολή στο σιλό να ανοίξει τη βαλβίδα εξόδου έως ότου αδειάσει από το υγρό. Δίνει εντολή στο σιλό να ενεργοποιήσει τις αντιστάσεις που θερμαίνουν το υγρό έως μία συγκεκριμένη θερμοκρασία. Δίνει εντολή στο σιλό να ενεργοποιήσει τον αναμικτήρα που ανακατεύει το υγρό για συγκεκριμένο χρονικό διάστημα Δίνει εντολή στο σιλό να σταματήσει το γέμισμα υγρού, δηλαδή να κλείσει την βαλβίδα εισόδου. Δίνει εντολή στο σιλό να σταματήσει το άδειασμα υγρού, δηλαδή να κλείσει τη βαλβίδα εξόδου. Δίνει εντολή στο σιλό να σταματήσει οποιαδήποτε διεργασία κι αν κάνει. Δίνει εντολή στο σιλό να ξεκινήσει τη διεργασία. Παίρνει τιμή true όταν το σιλό έχει γεμίσει με υγρό. Παίρνει τιμή true όταν το σιλό έχει αδειάσει από το υγρό. 25 heating completed Boolean Read mixing completed Boolean Read target temperature Float Read/Write event String Read/Write Παίρνει τιμή true όταν το σιλό έχει τελειώσει με τη θέρμανση του υγρού. Παίρνει τιμή true όταν το σιλό έχει τελειώσει με το ανακάτεμα του υγρού. Με λειτουργία write μπορεί να προσδιοριστεί ποια είναι η επιθυμητή θερμοκρασία του υγρού και με λειτουργία read μπορεί να διαβαστεί αυτή η επιθυμητή θερμοκρασία. Έχει μορφή JSON που περιέχει πληροφορίες για τα events που έχουν έρθει στο σιλό, όπως ποιο ήταν το event, ποιος ο αποστολέας και πότε έγινε η αποστολή. Η διαδικασία παραγωγής του 2ου είδους liqueur, υλοποιημένη με το XDK της Bosch, περιλαμβάνει τα εξής: 1. Έναν LwM2M server υλοποιημένος σε Java, επεκτείνοντας την ανοικτού-κώδικα υλοποίηση του OMA LwM2M server «Eclipse Leshan» [33]. Η επέκταση αυτή της βασικής υλοποίησης ενός LwM2M server αφορά την προσθήκη της λογικής της παραγωγής liqueur, δηλαδή τον εντοπισμό της σύνδεσης όλων των απαραίτητων συσκευών και της ενορχήστρωσής τους για την παραγωγή liqueur. Αξίζει να σημειωθεί ότι αυτού του είδους επέκτασης του υπάρχον κώδικα ενός LwM2M server δεν είναι απαραίτητη. Ο έλεγχος των έξυπνων αντικειμένων της διεργασίας θα μπορούσε να γίνεται μέσω ενός τρίτου LwM2M client που συνδέεται σε έναν κλασικό (μη τροποποιημένο) LwM2M server. 2. Τρεις LwM2M clients, δύο από τους οποίους είναι τα έξυπνα σιλό όπως περιεγράφηκαν νωρίτερα. Ο τρίτος LwM2M client είναι ένα smart pipe που προσομοιάστηκε σε Java και η εκτέλεσή του, για λόγους απλότητας, έγινε στο ίδιο υπολογιστικό σύστημα στο οποίο πραγματοποιήθηκε η εκτέλεση και του τροποποιημένου LwM2M server. Τα XDK τα οποία συμμετείχαν στην υλοποίηση του smart silo προγραμματίστηκαν ώστε να συνδέονται στο ίδιο τοπικό δίκτυο με τον LwM2M server και να κάνουν register σε αυτόν ως LwM2M clients. Η περιγραφή των custom IoT αντικειμένων προστέθηκε στο registry του server αφού δεν υπάρχουν ήδη στο επίσημο LwM2M registry [34]. 3. Ο LwM2M server ξεκινάει και παρουσιάζει ένα GUI (Graphical User Interface), περιμένοντας τις έξυπνες συσκευές να συνδεθούν. Ο έξυπνος σωλήνας ξεκινάει και συνδέεται στον server. Τα XDK της Bosch ενεργοποιούνται και αρχίζουν να εκτελούν αυτόματα το λογισμικό το οποίο έχει περαστεί σε αυτά. Έτσι, συνδέονται στο δίκτυο και κάνουν register ως LwM2M clients. 26 Εικόνα 9 Το GUI του server, περιμένοντας τη σύνδεση των συσκευών 4. Ο server ξεκινάει τη διαδικασία παραγωγής του 2ου τύπου liqueur, στέλνοντας τις κατάλληλες εντολές μέσω του LwM2M πρωτοκόλλου στους συνδεδεμένους σε αυτόν LwM2M clients και συγχρονίζοντας την παραγωγή, ελέγχοντας τις εκάστοτε καταστάσεις των clients. Εικόνα 10 Τα δύο σιλό με τους level shifters, συνεργαζόμενα για την παραγωγή liqueur 5. Η διαδικασία παραγωγής επαναλαμβάνεται μέχρι να πατηθεί το κουμπί «Stop» από το GUI του server. Το κουμπί «stop» κάνει τον LwM2M server να στείλει την LwM2M execute εντολή «stop» και στα δύο σιλό. 27 Την ίδια περίοδο που το άνω πείραμα έλαβε χώρα, η Bosch διοργάνωσε έναν διαγωνισμό ιδεών για το XDK με όνομα «XDK Ideation Jam». Η ομάδα μας των δύο ατόμων που συμμετείχε στο πείραμα με καθοδήγηση του καθηγητή μας Κλεάνθη Θραμπουλίδη, έλαβε μέρος στο διαγωνισμό αυτό όπου απέσπασε το πρώτο βραβείο. 28 Κεφάλαιο 4 4. Containers and microservices Όταν ένα σύστημα είναι κατανεμημένο, όπως για παράδειγμα μία συστάδα από IoT συσκευές, μπορούν να παρουσιαστούν αρκετά προβλήματα που δεν συναντώνται σε πιο κλασικές εφαρμογές. Μερικά από αυτά είναι η εύκολη αναβάθμιση του λογισμικού μιας συσκευής χωρίς φόβο για προβλήματα συμβατότητας με άλλα πακέτα λογισμικού, η αποτελεσματική επικοινωνία μεταξύ των διάφορων στοιχείων του συστήματος και ο μαζικός έλεγχος της κατάστασής του, και οι μαζικές ταυτόχρονες αναβαθμίσεις λογισμικού με αλληλεξαρτήσεις. Αυτό το κεφάλαιο μελετά πώς οι τεχνολογίες των container και των microservices μπορούν ίσως να δώσουν λύση σε μερικά από αυτά τα προβλήματα και συγκρίνει διαφορετικές υλοποιήσεις αυτών των τεχνολογιών μεταξύ τους. Έτσι, μέσω της αντικειμενικής σύγκρισης, γίνεται διαπίστωση για το ποιες από αυτές τις τεχνολογίες είναι πιθανόν να μπορούν να χρησιμοποιηθούν με επιτυχία σε IIoT εφαρμογές που πολλές φορές συναντώνται να παρουσιάζουν κατανεμημένη μορφή. 4.1 Virtualization Ο όρος virtualization χρησιμοποιείται για να περιγράψει τη διαδικασία κατά την οποία δημιουργούνται εικονικές εκδόσεις από στοιχεία λογισμικού ή υλικού. Το virtualization μπορεί να πάρει πάρα πολλές μορφές και έχει πολλές χρήσεις. Πιο συγκεκριμένα, μπορεί να χρησιμοποιηθεί σε 1. Hardware virtualization [35]: Αφορά τη δημιουργία ενός εικονικού μηχανήματος (virtual machine) που συμπεριφέρεται σαν ένας ξεχωριστός υπολογιστής με το δικό του hardware. Το λογισμικό που είναι υπεύθυνο για τη δημιουργία και το περιβάλλον εκτέλεσης του virtual machine λέγεται hypervisor ή virtual machine monitor (VMM). Με hardware virtualization, το οποίο γίνεται με χρήση λογισμικού, το host machine μπορεί να επιτύχει εκτέλεση προγραμμάτων που είναι σχεδιασμένα να τρέχουν σε τελείως διαφορετικό περιβάλλον από το δικό του. Όμως, το hardware virtualization επιφέρει σημαντική μείωση στην επίδοση των προγραμμάτων που τρέχουν στο περιβάλλον του guest machine. Για βελτίωση της επίδοσης αυτής μπορεί να χρησιμοποιηθεί μία hardware-assisted virtualization λύση που απαιτεί επεξεργαστές που υποστηρίζουν ειδικά την προσομοίωση στοιχείων λογισμικού. Ο χρήστης ενός virtual machine έχει τη δυνατότητα να παίρνει snapshots, δηλαδή να αποθηκεύει την κατάστασή του σε μία χρονική στιγμή [36]. Αυτό μπορεί να έχει πολλές χρήσεις, όπως backup και αντιγραφή ή migration σε διαφορετικά host machines. 29 Εικόνα 11 Η αρχιτεκτονική του hardware virtualization 2. Remote Desktop virtualization [37]: Αφορά τα λειτουργικά συστήματα ή και προγράμματα που τρέχουν απομακρυσμένα αλλά ο χρήστης έχει πρόσβαση σε αυτά στο φυσικό του μηχάνημα. Ο χρήστης του φυσικού μηχανήματος έχει στα χέρια του τις δυνατότητες του απομακρυσμένου συστήματος. Ο χρήστης έχει τη δυνατότητα να δώσει input και να δει το output. Συνήθως το input του χρήστη αφορά το ποντίκι και το πληκτρολόγιό του. Το Desktop virtualization απαιτεί μεγάλη μεταφορά δεδομένων μεταξύ του υπολογιστή που ελέγχεται και του υπολογιστή που ελέγχει, οπότε προϋποθέτει μια σύνδεση στο internet μεγάλου bandwidth. Σε μερικές υλοποιήσεις μεμονωμένα προγράμματα μπορούν να εκτελούνται στον υπολογιστή που ελέγχεται και να εμφανίζονται και να δέχονται input στον υπολογιστή που ελέγχει. Χαρακτηριστικό παράδειγμα αποτελεί το X11-forwarding, το οποίο επιτρέπει αυτού του είδους εκτέλεσης εφαρμογών μέσα από μία SSH (Secure Shell) σύνδεση [38]. 3. OS-level virtualization/Containerization: Είναι ένα χαρακτηριστικό του πυρήνα (kernel) μερικών συστημάτων το οποίο επιτρέπει τη δημιουργία πολλών απομονωμένων instances του user-space. Ως user-space αναφέρεται ο κώδικας που τρέχει εκτός του πυρήνα (kernel) του λειτουργικού συστήματος. Οι εφαρμογές που τρέχουν μέσα σε έναν container χρησιμοποιούν άμεσα τον πυρήνα και έτσι, ενώ είναι απομονωμένες μεταξύ τους (τα user-spaces είναι απομονωμένα), πετυχαίνουν μικρή μόνο μείωση της απόδοσής τους. Η πιο γνωστή υλοποίηση containers είναι αυτή του docker, αλλά πέρα από αυτή, OS-level virtualization υλοποιείται από το FreeBSD jail [39], το Oracle Solaris [40], το Sandboxie [41] κ.ά. 30 Εικόνα 12 Η αρχιτεκτονική του containerization 4. Application/Process virtualization: Η εφαρμογή που τρέχει στο σύστημα τρέχει μέσα σε ένα περιβάλλον τύπου sandbox. Κάποιο λογισμικό ή και το ίδιο το λειτουργικό σύστημα, δίνει στην εφαρμογή ένα επίπεδο το οποίο παρέχει εικονικούς πόρους σε αυτήν, όπως έναν virtual hard drive. Η εφαρμογή δεν χρειάζεται να είναι ειδικά σχεδιασμένη για να γίνει virtualized και δεν έχει γνώση κατά τη διάρκεια της εκτέλεσής της (runtime) ότι εκτελείται σε virtualized/sandboxed περιβάλλον. Χαρακτηριστικό παράδειγμα Application virtualization αποτελεί η υλοποίηση του Wine (Wine Is Not an Emulator) το οποίο επιτρέπει την εκτέλεση μερικών προγραμμάτων που είναι σχεδιασμένα για εκτέλεση σε Windows περιβάλλον κάτω από ένα Linux περιβάλλον [42]. Το Wine παρέχει ένα layer το οποίο είναι μέχρι ένα σημείο συμβατό με τις δυνατότητες που δίνει στις εφαρμογές ένα Windows λειτουργικό σύστημα και μεταφράζει τις αντίστοιχες δυνατότητες στο Linux σύστημα στο οποίο η εφαρμογή τρέχει. 5. Network virtualization [43]: Παρέχει τη δυνατότητα δημιουργίας εικονικών δικτύων για τη δοκιμή εφαρμογών χωρίς να χρειάζεται να υπάρχουν οι φυσικές οντότητες των αντίστοιχων στοιχείων από τα οποία αποτελούνται τα δίκτυα αυτά. Μπορεί να χρησιμοποιηθεί για τη δοκιμή λογισμικού σε εικονικό περιβάλλον που προσομοιώνει όσο γίνεται το πραγματικό περιβάλλον στο οποίο πρόκειται να τρέξει η εφαρμογή, χωρίς να χρειαστεί να υπάρχουν τα φυσικά μηχανήματα που υλοποιούν το αντίστοιχο δίκτυο. Η δοκιμή αυτή μπορεί να αφορά τόσο την ορθή λειτουργία του λογισμικού όσο και για την αξιολόγηση της απόδοσης του συστήματος κάτω από διαφορετικές συνθήκες δικτύου. 4.2 Containers Ο όρος container χρησιμοποιείται για να περιγράψει οποιοδήποτε δοχείο, θήκη ή κιβώτιο που χρησιμοποιείται για την αποθήκευση, συσκευασία και μεταφορά προϊόντων. Containers χρησιμοποιούνταν από τα αρχαία χρόνια για να πραγματοποιηθεί εμπόριο μεταξύ των ανθρώπων αλλά και για την αποθήκευση αγαθών. Στο πλαίσιο του λογισμικού, container ονομάζεται ένα απομονωμένο περιβάλλον μέσα στο οποίο ζει μία ή περισσότερες εφαρμογές. Το πόσο απομονωμένο είναι το περιβάλλον έχει να κάνει με το είδος του virtualization που χρησιμοποιείται. 31 Και κάποιος δεν θα μπορούσε να μιλήσει για containers αν δεν αναφερθεί στην έννοια του virtualization. Στις μέρες μας, ο πρόδρομος του virtualization θεωρείται το time-sharing, δηλαδή η ικανότητα ενός υπολογιστικού συστήματος να εκτελεί πολλά προγράμματα ταυτόχρονα, και που το καθένα να έχει δική του μνήμη και να είναι ανατεθειμένη σε συγκεκριμένο χρήστη. Το time-sharing είναι ένας τρόπος διαχωρισμού των processes μεταξύ τους και δημιουργήθηκε από την ανάγκη καλύτερης αξιοποίησης των πόρων ενός υπολογιστή όταν αυτός ο υπολογιστής τρέχει παραπάνω από ένα προγράμματα ή/και έχει παραπάνω από έναν χρήστη. Το time-sharing άρχισε να αναπτύσσεται μεταξύ του τέλους της δεκαετίας του ’50 και τις αρχές της δεκαετίας του ’60 από τον John McCarthy, στο MIT [44]. Το time-sharing συνέχισε να αναπτύσσεται σαν ιδέα και τελικά εξελίχθηκε στις πρώτες μορφές των hypervisors (γνωστοί και ως VMM – Virtual Machine Monitors). Hypervisor μπορεί να είναι οποιοδήποτε λογισμικό, firmware ή υλικό, που είναι υπεύθυνο για την δημιουργία και την εκτέλεση εικονικών μηχανημάτων. Το μηχάνημα στο οποίο το σύστημα λειτουργεί άμεσα ο hypervisor ονομάζεται «host operating system» και τα λειτουργικά συστήματα που εκτελούνται μέσα στα εικονικά μηχανήματα ονομάζονται «guest operating systems». Το κύριο χαρακτηριστικό των hypervisors είναι ότι προσομοιώνουν το υλικό. Υπάρχουν δύο τύποι hypervisors. Ο πρώτος τύπος είναι οι native ή bare-metal hypervisors, οι οποίοι τρέχουν στο υλικό του host operating system και μπορούν και διαχειρίζονται το υλικό που χρειάζονται τα guest operating systems. Το πρώτο λειτουργικό σύστημα που υποστήριζε πραγματικό virtualization και έφυγε και εκτός εργαστηρίου για να χρησιμοποιηθεί από το ευρύ κοινό, ήταν το CP/CMS (Control Program/Cambridge Monitor System) από την IBM [45] και χρησιμοποιούσε bare-metal hypervisors. Ο δεύτερος τύπος είναι οι hosted hypervisors που είναι η πιο μοντέρνα προσέγγιση στην υλοποίηση των hypervisors, και έγινε δυνατή λόγω της μεγαλύτερης υπολογιστικής ισχύς των υπολογιστικών συστημάτων με το πέρασμα των χρόνων. Οι hosted hypervisors εκτελούνται σαν απλά προγράμματα στο host operating system και τα guest operating systems είναι και αυτά απλά προγράμματα που τρέχουν με τη βοήθεια του hypervisor στο host operating system. Το κύριο πλεονέκτημα του hypervisor-based virtualization είναι ότι, επειδή ακριβώς κάνει virtualization σε επίπεδο hardware, τα λειτουργικά συστήματα του host και των guest μπορούν να είναι πλήρως ανεξάρτητα μεταξύ τους. Για παράδειγμα, ένας υπολογιστής ο οποίος έχει ως host machine Windows 10 μπορεί με χρήση ενός hypervisor να εκτελέσει ως guest machine Linux Mint ή το αντίθετο. Το μεγαλύτερο μειονέκτημα των hypervisor είναι το performance overhead που προκαλούν με το να πρέπει να γίνει προσομοίωση του υλικού, ειδικά όσον αφορά την περίπτωση των hosted supervisors [46]. Μετά τα πρώτα αυτά βήματα της IBM, ήρθαν πολλές εξελίξεις στο χώρο του virtualization και εισάχθηκαν έννοιες όπως το Paravirtualization, Full virtualization, Hardware-assisted virtualization, Hybrid virtualization και έπειτα το Container-based virtualization, στο οποίο επικεντρώνεται η εργασία αυτή. Στο Paravirtualization, το guest operating system έχει γνώση ότι είναι εικονικό και μπορεί να επικοινωνεί με το host operating system έτσι ώστε να μη χρειαστεί να γίνει virtualization του hardware. Από την άλλη, το full virtualization αφορά virtualization όλων των component ενός συστήματος και έχει τη δυνατότητα προσομοίωσης και συγκεκριμένων αρχιτεκτονικών hardware. Χαρακτηριστικά παραδείγματα full virtualization είναι οι υλοποιήσεις του VMWare Workstation [47] και του Virtualbox [48]. Το hardware assisted virtualization είναι ένας τύπος full virtualization όπου η αρχιτεκτονική του ίδιου του φυσικού επεξεργαστή είναι ειδικά σχεδιασμένη έτσι ώστε να βοηθά το virtualization του hardware του guest operating system, προσφέροντας έτσι καλύτερες επιδόσεις. Τα τελευταία χρόνια, το container-based virtualization έχει γίνει και αυτό δημοφιλές. 32 Docker Container 120 100 80 60 40 20 2004-01 2004-08 2005-03 2005-10 2006-05 2006-12 2007-07 2008-02 2008-09 2009-04 2009-11 2010-06 2011-01 2011-08 2012-03 2012-10 2013-05 2013-12 2014-07 2015-02 2015-09 2016-04 2016-11 2017-06 2018-01 2018-08 2019-03 2019-10 0 Εικόνα 13 Google Trends για τον όρο αναζήτησης «Docker Container» [49] Στις μέρες μας, με τη χρήση του γενικού όρου «container», συνήθως γίνεται αναφορά στα containers που χρησιμοποιούνται στα μεγάλα εμπορικά πλοία (πλοία εμπορευματοκιβωτίων) τα οποία εύστοχα ονομάζονται container ships. Εικόνα 14 A modern Container ship [50] Σε επίπεδο ανάπτυξης λογισμικού η επιλογή του όρου αυτού έγινε γιατί οι containers ενθυλακώνουν τις εφαρμογές, και είναι lightweight και γι’ αυτό μπορούν εύκολα να τρέχουν πολλοί σε ένα host σύστημα. Το docker.com ορίζει τον container ως: «A container is a standard unit of software that packages up code and all its dependencies so the application runs quickly and reliably from one computing environment to another.» [51]. Η βασική διαφορά των containerized εφαρμογών 33 από τους hypervisors είναι ότι πλέον, δεν γίνεται προσομοίωση υλικού. Αντίθετα, το guest operating system μπορεί και επικοινωνεί άμεσα με τον πυρήνα (kernel) του host operating system το οποίο είναι υπεύθυνο για το interfacing με το hardware. Αυτό από τη μία προσφέρει πολύ καλύτερες επιδόσεις και κάνει τους containers ένα πολύ πιο lightweight τρόπο virtualization από το hypervisorbased virtualization, αλλά έχει και τα μειονεκτήματά του. Πιο συγκεκριμένα, πρώτον, το επίπεδο της απομόνωσης του guest operating system είναι μικρότερο σε container-based virtualization, αφού host και guest operating system μοιράζονται τον ίδιο πυρήνα. Αυτό όμως σημαίνει ότι ένα bug στον πυρήνα μπορεί να οδηγήσει σε προβλήματα ασφαλείας, όπου δηλαδή μία εφαρμογή που τρέχει ενθυλακωμένη μέσα σε έναν container να καταλήξει να έχει πρόσβαση στους πόρους του host operating system, όπως έχει παρατηρηθεί να συμβαίνει στο παρελθόν [52]. Δεύτερον, το οποίο είναι και το μεγαλύτερο μειονέκτημα σε σχέση με το hypervisor-based virtualization, τα ήδη των container που τρέχουν ως guests πρέπει να είναι συμβατά με τον πυρήνα του host operating system. Αυτό σημαίνει ότι ένα host operating system το οποίο έχει Linux kernel δεν μπορεί να τρέξει έναν container ο οποίος απαιτεί τον πυρήνα των Windows. Για παράδειγμα, για την εκτέλεση Linux-based containers σε Windows host operating systems, απαιτείται η χρήση και ενός hypervisor [53], του Hyper-V [54], μειώνοντας σημαντικά την απόδοση. Το κάθε container εκτελείται ενθυλακωμένο, δηλαδή έχει το δικό του περιβάλλον εσωτερικά και δεν έχει γνώση του host συστήματος ή των υπόλοιπων container, δηλαδή δεν μπορεί να δει τους πόρους ή τις διεργασίες αυτών. Παρ’ όλα αυτά, και αυτός είναι ο λόγος για τον οποίο είναι τόσο lightweight, τα containers δεν «τρέχουν» μόνα τους, αλλά με τη βοήθεια του υποκείμενου λειτουργικού συστήματος και πυρήνα. Αυτό σημαίνει ότι τα containers έχουν άμεση πρόσβαση στις δυνατότητες που προσφέρει το λειτουργικό σύστημα χωρίς να γίνεται virtualization σε βαθύτερο επίπεδο (όπως για παράδειγμα σε επίπεδο hardware), τουλάχιστον όχι όταν το host operating system και το guest operating system έχουν συμβατούς πυρήνες. Εικόνα 15 Τα containers χρησιμοποιούν το host kernel και έτσι δεν χρειάζεται να χαθούν CPU cycles σε hardware virtualization ή σε ξεχωριστό kernel Συνεχίζοντας την αναλογία, ένα container ship βοηθάει στην αποστολή (shipping) των προϊόντων, όπως κάνουν και τα containers στο λογισμικό. Τα containers, λόγω της ενθυλάκωσης λογισμικού που προσφέρουν, μπορούν να τρέχουν στο ίδιο σύστημα εφαρμογές με τελείως διαφορετικές απαιτήσεις σε παραμετροποίηση αλλά και στο απαιτούμενο λειτουργικό σύστημα, αρκεί να υπάρχει συμβατότητα πυρήνων μεταξύ των δύο συστημάτων. Επίσης, τα containers 34 ενθυλακώνουν, πέρα από την εφαρμογή αυτή καθ’ αυτή, και οποιαδήποτε εξάρτηση του λογισμικού αυτού. Δηλαδή αν η εφαρμογή που θέλουμε να εκτελεστεί μέσα σε έναν container προαπαιτεί την εγκατάσταση κάποιου τρίτου πακέτου λογισμικού ή βιβλιοθηκών, αυτές θα συμπεριληφθούν μέσα στον container. Αυτό κάνει το “shipping” των εφαρμογών πολύ πιο εύκολο καθώς η εφαρμογή πλέον δεν μπορεί να επηρεαστεί από τον τρόπο που τρέχουν άλλες εφαρμογές στο host operating system ή από την παραμετροποίηση του συστήματος το οποίο έχει αλλάξει λόγω άλλης εφαρμογής (sideeffects/παρενέργειες). Η αναβάθμιση μιας εφαρμογής πλέον μπορεί να γίνει με την κατασκευή ενός νέου container που ενθυλακώνει τη νέα έκδοση της εφαρμογής, μαζί με οποιαδήποτε εξάρτησή της ή την αναβαθμισμένη έκδοση αυτών, της απομάκρυνσης του παλιού container και της εκκίνησης του νέου. Χωρίς container-based virtualization o διαχειριστής του συστήματος θα έπρεπε να σταματήσει την παλιά έκδοση του λογισμικού, να εγκαταστήσει την καινούργια, να ελέγξει ότι οι εκδόσεις των εξαρτήσεων είναι στη σωστή έκδοση και να ξεκινήσει την καινούργια έκδοση του λογισμικού. Αυτή η διαδικασία είναι περίπλοκη, χρονοβόρα και επιρρεπής σε λάθη, πράγμα που δείχνει γιατί το container-based virtualization έχει γίνει τόσο δημοφιλές. Λόγω αυτών των πλεονεκτημάτων, τα containers έχουν γίνει πολύ δημοφιλή για web εφαρμογές που εσωτερικά χρησιμοποιούν πολλούς διαφορετικούς servers ή/και βοηθητικές εφαρμογές αλλά και σε συστήματα που χρησιμοποιούν microservices για να υλοποιήσουν την αρχιτεκτονική τους. Όταν χρησιμοποιείται ο όρος container από εδώ και στο εξής θα χρησιμοποιείται εννοώντας τον Docker Container [55], που θεωρείται ότι είναι το πρότυπο της σημερινής βιομηχανίας και τρέχει σε όλα τα δημοφιλή λειτουργικά συστήματα. 4.2.1 Επίπεδο αφαιρετικότητας από το λειτουργικό σύστημα Τα containers είναι ένας τύπος OS-level virtualization τα οποία εμπίπτουν στην κατηγορία των Application Containers. Ένα πολύ δημοφιλές εργαλείο μεταξύ των Linux Administrators είναι το chroot [56], το οποίο δημιουργεί εκ νέου ένα ξεχωριστό και απομονωμένο σύστημα αρχείων μέσα στο οποίο μπορεί να εκτελεστεί μία ή και περισσότερες διεργασίες, το οποίο ονομάζεται chroot jail (φυλακή chroot). Το chroot jail εξασφαλίζει έτσι ότι δεν θα επηρεαστούν αλλά ούτε και θα επηρεάσουν το host operating system, λόγω παρενεργειών ή λόγω διαφορετικής παραμετροποίησης. Παρ’ όλα αυτά, το chroot δεν εξασφαλίζει περισσότερες δυνατότητες απομόνωσης, δηλαδή ο χρήστης που τρέχει μέσα στο chroot μπορεί, εάν έχει τα κατάλληλα δικαιώματα να φύγει έξω από αυτό, να κάνει mount/unmount δίσκους τους οποίους μπορεί να δει και το host operating system και δεν προσφέρει απομόνωση σε επίπεδο δικτύου και ούτε μπορεί να περιορίσει τους πόρους συστήματος που μπορεί να καταναλώσει. Επίσης, μόνο ένας χρήστης με δικαιώματα root μπορεί να ξεκινήσει ένα chroot για λόγους ασφαλείας (για να μην υπάρξει privilege escalation πρόβλημα μέσα στο chroot συστήματος αρχείων). Ένα πιο εκλεπτυσμένο και σύγχρονο εργαλείο που προσφέρει μεγαλύτερα επίπεδα απομόνωσης από το host operating system είναι το docker [57]. Η πρώτη έκδοση του docker έγινε διαθέσιμη το 2013 [58] και δούλευε με χρήση του LXC (Linux Containers). Το docker καταφέρνει την απομόνωση μίας εφαρμογής από το host operating system σε πολλαπλά επίπεδα, μέσω της χρήσης των namespaces και των cgroups. Namespaces είναι μία λειτουργία του Linux kernel η οποία επιτρέπει μία ομάδα διεργασιών να έχει πρόσβαση μόνο σε ένα συγκεκριμένο σύνολο από πόρους. Όταν ένα λειτουργικό σύστημα που χρησιμοποιεί Linux πυρήνα ξεκινάει, χρησιμοποιεί από προεπιλογή μόνο ένα από κάθε είδος namespace για όλο το σύνολο των εφαρμογών. Αυτό πρακτικά σημαίνει ότι οι διεργασίες δεν είναι οργανωμένες σε απομονωμένες ομάδες αλλά μπορούν και έχουν πρόσβαση στους ίδιους πόρους του συστήματος, όντας περιορισμένες μόνο με βάση τα δικαιώματα χρήστη με τα οποία εκτελούνται. Λόγω των διαφορετικών ειδών πόρων τους οποίους διαχειρίζεται ο πυρήνας ενός λειτουργικού συστήματος, υπάρχουν και πολλά είδη namespaces μέσα στα οποία οργανώνονται οι πόροι αυτοί με βάση το είδος τους. Τα βασικά namespaces που είναι διαθέσιμα σε ένα σύγχρονο Linux πυρήνα είναι τα εξής: 35 1. Το mnt (mount) namespace [59], το οποίο μπορεί και παρέχει σε μία ή παραπάνω διεργασίες ένα ξεχωριστό filesystem, όπως αυτό που παρέχει το εργαλείο chroot (change root filesystem) που προαναφέρθηκε. Το κάθε filesystem έχει τη δική του δομή φακέλων και αρχείων και δεν έχει πρόσβαση στα υπόλοιπα mnt namespaces. 2. Το pid (process id) namespace [60], το οποίο περιορίζει την εφαρμογή να έχει πρόσβαση μόνο στα process ids τα οποία τρέχουν μέσα στο ίδιο pid namespace. Δύο διεργασίες που τρέχουν σε διαφορετικά pid namespaces μπορούν ακόμα και να χρησιμοποιούν το ίδιο process id. Οποιαδήποτε προσπάθεια για προσπέλαση ενός process id περιορίζεται μέσα στο ίδιο pid namespace. Για παράδειγμα, η χρήση της εντολής kill [61] για τερματισμό μιας διεργασίας έχει αποτέλεσμα μόνο στο τρέχον pid namespace στο οποίο ανήκει και η ίδια κατά τη διάρκεια της εκτέλεσής της. 3. Το network namespace [62], το οποίο επιτρέπει σε μια ή παραπάνω διεργασίες να έχουν τους δικούς τους πόρους συστήματος που έχουν να κάνουν με το networking. Αυτό αφορά τα IPv4 και IPv6 πρωτόκολλα, πίνακες για IP routing, κανόνες του firewall, απομόνωση των sockets και του αριθμού τον πορτών και άλλα. 4. Το user namespace [63], το οποίο απομονώνει τα IDs των χρηστών καθώς και τα IDs των ομάδων των χρηστών μεταξύ άλλων χρηστών και ομάδων χρηστών που ανήκουν σε διαφορετικό user namespace. Μεταξύ άλλων, αυτή η ιδιότητα είναι αυτή που δίνει τη δυνατότητα σε ένα non-root χρήστη στο host operating system να είναι root μέσα σε έναν container που χρησιμοποιεί διαφορετικό user namespace από το host operating system χωρίς ο ενθυλακωμένος αυτός root χρήστης να έχει root-επίπεδο πρόσβασης στο host operating system. 5. Το uts (Unix Timesharing System) namespace [64], το οποίο απομονώνει το hostname του συστήματος, δηλαδή δύο διεργασίες που ανήκουν σε διαφορετικό uts namespace μπορούν να έχουν πρόσβαση σε ένα διαφορετικό hostname. Η αλλαγή hostname θα έχει δράση μόνο στο uts namespace στο οποίο εκτελείται η διεργασία που είναι υπεύθυνη για την αλλαγή του. 6. Το ipc (Inter-Process Communication) namespace [65], το οποίο δίνει τη δυνατότητα σε ένα σύνολο διεργασιών να έχουν τα δικά τους ιδιωτικά κομμάτια της μνήμης. Αυτό κατ’ επέκταση δίνει τη δυνατότητα στους containers να έχουν τη δική τους ιδιωτική τους μνήμη αλλά ακόμα και μοίρασμα της shared μνήμης μεταξύ των containers ή και μεταξύ του host operating system. Η shared μνήμη είναι ένας πιο γρήγορος τρόπος επικοινωνίας από τα pipes ή από τη χρήση του network stack. Η απομόνωση κομματιών της μνήμης είναι απαραίτητο στοιχείο ασφαλείας για τους containers καθώς διασφαλίζει ότι ένας container δεν θα μπορεί να έχει πρόσβαση στη μνήμη του host operating system ή στη μνήμη άλλων container. Ακόμα, είναι σημαντικό να γίνει αναφορά στα cgroups (Control Groups) [66]. Τα cgroups είναι μία λειτουργία του Linux kernel που επιτρέπει σε διεργασίες να οργανωθούν σε ιεραρχικές ομάδες των οποίων η χρήση των πόρων του συστήματος μπορεί να περιοριστεί και να παρακολουθείται. Τέτοιου είδους πόροι είναι για παράδειγμα η χρήστη του επεξεργαστή (CPU) ή της κυρίας μνήμης (RAM). Στο επίπεδο των containers, τα cgroups χρησιμοποιούνται έτσι ώστε ο χρήστης να μπορεί να περιορίσει και να ελέγξει τους πόρους που χρησιμοποιεί ο κάθε container. Για παράδειγμα, μπορεί να περιοριστεί το ποσοστό της χρήσης του επεξεργαστή αλλά και η χρήση της κυρίας μνήμης από έναν συγκεκριμένο container έτσι ώστε να εξασφαλιστεί ότι και τα υπόλοιπα containers ή/και το host operating system θα έχει αρκετή υπολογιστική ισχύ έτσι ώστε να μπορούν να εκτελεστούν και κάποιες άλλες διεργασίες. Πέρα από την υπολογιστική ισχύ που καταναλώνει μία ομάδα διεργασιών, μέσω των cgroups μπορούν να γίνουν και άλλοι περιορισμοί, όπως περιορισμός του αριθμού των διεργασιών μέσα στην ομάδα [67] και το πάγωμα των διεργασιών που ανήκουν στην ίδια ομάδα (freezing) [68]. 36 Εικόνα 16 Τα βασικά τμήματα της αρχιτεκτονικής του Docker 4.2.2 Πλεονεκτήματα των containers Σε επιχειρήσεις οι οποίες ασχολούνται με την ανάπτυξη λογισμικού συναντώνται: 1. Developers: Είναι τα άτομα που γράφουν τον κώδικα του λογισμικού και είναι υπεύθυνοι έτσι ώστε αυτό να τρέχει ορθά. Συνήθως παρέχουν και κώδικα ο οποίος δοκιμάζει το λογισμικό (unit testing) καθώς και πώς το λογισμικό συνεργάζεται με άλλα συστήματα ή πώς τα διαφορετικά συστήματα λογισμικού συνεργάζονται μεταξύ τους (integration testing). 2. System administrators (sysadmins): Είναι υπεύθυνοι για τη διαχείριση των συστημάτων μιας επιχείρησης. Είναι υπεύθυνοι για την ανόρθωση διακομιστών, τη συντήρησή τους, την εγκατάσταση ενημερώσεων λογισμικού, την ασφάλειά τους, η πραγματοποίηση αντιγράφων ασφαλείας και άλλα. Σε επιχειρήσεις οι οποίες χρησιμοποιούν κατά κύριο λόγο το Cloud για την εκτέλεση των εφαρμογών τους δεν υπάρχουν system administrators. 3. DevOps engineers: Είναι υπεύθυνοι για την αυτοματοποίηση του release cycle. Αν κάθε έκδοση του λογισμικού πρέπει να χτιστεί, δοκιμαστεί και γίνει deploy, είναι υπεύθυνοι για την αυτοματοποίηση κάθε βήματος της διαδικασίας αυτής. Οι διεργασίες αυτές έχουν λάβει το όνομα Continuous Integration (CI) και Continuous Deployment (CD) [69] (συχνά συναντώνται μαζί υπό τον όρο CI/CD). Τα containers, λόγω των χαρακτηριστικών τους τα οποία αναφέρθηκαν στις προηγούμενες παραγράφους, είναι ένα σημαντικό εργαλείο στα χέρια των developers, των system administrators αλλά και των DevOps. Για τους developers: 1. Τα containers ελευθερώνουν του developers από τη χρήση ενός συγκεκριμένου λειτουργικού συστήματος. Το production περιβάλλον (το περιβάλλον στο οποίο είναι σχεδιασμένη τελικά να τρέχει η εφαρμογή η οποία αναπτύσσεται) μπορεί να στηρίζεται στον πυρήνα του Linux και ο developer να προτιμάει ένα Windows λειτουργικό σύστημα. Η ανάπτυξη του λογισμικού μπορεί να γίνει στο Windows σύστημα και η εκτέλεση του κώδικα μπορεί να γίνει μέσα σε έναν Linux container που προσομοιάζει το production σύστημα. 37 2. Με τη χρήση volumes [70], το host operating system και το guest operating system μπορούν να μοιράζονται αρχεία και φακέλους. Με αυτόν τον τρόπο, ο developer μπορεί να χρησιμοποιήσει όποιο IDE (Integrated Development Environment) επιθυμεί. Δηλαδή, δεν χρειάζεται η επεξεργασία του λογισμικού να γίνεται μέσα από το container (το οποίο τις περισσότερες φορές είναι headless, δηλαδή χωρίς γραφικό περιβάλλον). Αντίθετα, οποιοδήποτε πρόγραμμα προτιμάει ο developer μπορεί να χρησιμοποιηθεί, και η εκτέλεσή του θα γίνεται στο host operating system. Ύστερα, οποιουδήποτε είδος buildδιεργασίας όπως compilation/transpilation αλλά και η εκτέλεση του αποτελέσματος μέσω ενός interpreter ή άμεσα, μπορεί να γίνεται μέσα στον container. Τα αρχεία που υπάρχουν μέσα στο volume διατηρούνται ακόμα και μετά τον τερματισμό του container, οπότε ο developer δεν χρειάζεται να τα μεταφέρει μέσα ή έξω από το volume μεταξύ των επανεκκινήσεων του container. 3. Λύνει το δημοφιλές πρόβλημα «It Works on My Machine», όπου το πρόγραμμα υπό κατασκευή μπορεί να δουλεύει ορθά στο development μηχάνημα του developer αλλά αποτυχαίνει να τρέξει στο production περιβάλλον, ή αποτυχαίνει να τρέξει στο περιβάλλον κάποιου συνάδελφου του developer, ή αποτυχαίνουν να τρέξουν τα unit ή/και integration tests (αν υπάρχουν) σε ένα Continuous Integration (CI) περιβάλλον ενώ έτρεχαν τοπικά στον υπολογιστή του developer. Αυτό που αλλάζει στα διαφορετικά περιβάλλοντα εκτέλεσης του προγράμματος και των test του είναι η παραμετροποίησή τους. Οι developers έχουν τη δυνατότητα να τρέχουν το υπό κατασκευή σύστημα είτε σε containers που είναι ειδικά σχεδιασμένοι να προσομοιώνουν όσο το δυνατόν καλύτερα το production περιβάλλον, ή και οι ίδιοι οι containers να γίνονται deploy στο production περιβάλλον. Η δεύτερη λύση είναι ακόμα πιο ασφαλής διότι δεν στηρίζεται στην ποιότητα της προσομοίωσης και επίσης δεν προϋποθέτει την συντήρηση των container από τους developers τις φορές που αλλάζει το production περιβάλλον. Οι containers, καθώς αλλάζουν μορφή λόγω της εξέλιξης του production περιβάλλοντος, μπορούν να επαναχρησιμοποιούνται και από τους ίδιους τους developers. Υπάρχουν ειδικά εργαλεία που μπορούν να χρησιμοποιηθούν που κάνουν την ανάπτυξη λογισμικού μέσα σε containers πιο εύκολη. 4. Δεν χρειάζεται να γίνει επιβάρυνση του host με την εγκατάσταση διερμηνευτών ή άλλων συστημάτων ή εξαρτήσεων του υπό κατασκευή λογισμικού τα οποία απαιτούνται για τη διεργασία κτισίματος και για την εκτέλεση του προγράμματος. Ένας container μέσα του περιλαμβάνει όλα τα πακέτα λογισμικού από τα οποία υπάρχει εξάρτηση και με την απομάκρυνση του container απομακρύνονται και αυτά. To host operating system δεν επηρεάζεται καθόλου από την ύπαρξη αυτών των προγραμμάτων. Για παράδειγμα, μπορεί να γίνεται η εκτέλεση μίας βάσης δεδομένων σε ένα container και της κυρίας εφαρμογής που βρίσκεται υπό κατασκευή σε δύο διαφορετικούς containers, χωρίς αυτές οι εφαρμογές και τα dependencies (εξαρτήσεις) τους να είναι εγκατεστημένα στο host σύστημα. Το μόνο επιπλέον πρόγραμμα που απαιτείται να είναι εγκατεστημένο στο host σύστημα είναι το docker. 5. Μπορούν να τρέχουν πολλές διαφορετικές εκδόσεις ίδιων προγραμμάτων ή/και γλωσσών προγραμματισμού που παρέχουν διαφορετικές δυνατότητες, κάτω από το ίδιο σύστημα, κάτι το οποίο πολλές φορές δεν είναι δυνατό να συμβεί. Αυτό μπορεί να κάνει τη μετάβαση σε διαφορετικές τεχνολογίες πιο εύκολη, καθώς δεν απαιτείται η εγκατάσταση ενός Virtual Machine ή κάποιο άλλο φυσικό μηχάνημα για την πραγματοποίηση δοκιμών. Ένα κομμάτι λογισμικού μπορεί να δοκιμασθεί ότι εκτελείται κάτω από πολλές διαφορετικές συνθήκες, όπως διαφορετικές εκδόσεις των εξαρτήσεών του (dependencies), ταυτόχρονα. Για παράδειγμα ένα πρόγραμμα-script που είναι γραμμένο σε Bash μπορεί – με χρήση containers – να δοκιμαστεί σε πολλές διαφορετικές εκδόσεις του Bash με χρήση κάποιας αυτοματοποίησης. Τέτοιου είδους δοκιμές είναι σημαντικά πιο περίπλοκες ή και αδύνατες χωρίς τη χρήση των container. 38 Για τους system administrators η δουλειά τους γίνεται πιο απλή καθώς δεν χρειάζεται πλέον προσεκτική διαχείριση της παραμετροποίησης των συστημάτων στα οποία εκτελούνται οι εφαρμογές. Για τους DevOps engineers: 1. Κάνει τα continuous integration processes πιο εύκολα. Υπάρχουν πλέον ακόμα και SaaS (Software as a Service) εταιρείες που χάρη στα containers, ενώ έχουν ως πρωταρχικό στόχο το version control του κώδικά τους, μπορούν πλέον να τρέξουν με ελάχιστη παραμετροποίηση τα CI processes της εφαρμογής τους. Το configuration ενός docker container λέγεται Dockerfile [71]. Μέσα στο Dockerfile απλά αρκεί να περιγραφεί το περιβάλλον κάτω από το οποίο χρειάζεται να τρέξει η εφαρμογή καθώς και τα εξωτερικά services που απαιτούνται (π.χ. βάση δεδομένων, caches κλπ). Βέβαια υπάρχουν και άλλες πιο εξελιγμένες selfmanaged λύσεις όπως το Jenkins [72] που χρειάζονται εκτενέστερο configuration και management. Βέβαια πλέον τα δημοφιλέστερα version control systems παρέχουν τις δικές τους λύσεις για το CI/CD, όπως το Bitbucket Pipelines [73] και το Github Actions [74]. 2. Το deployment της εφαρμογής γίνεται πιο εύκολο, καθώς πλέον δεν απαιτείται η προσεκτική ανανέωση του προϊόντος σε καινούργια έκδοση, αλλά η αφαίρεση του παλιού και η προσθήκη του καινούργιου container. Αυτή η διαδικασία είναι πολύ πιο γρήγορη από το να δημιουργηθεί ένας καινούργιος bare-metal ή virtual server από την αρχή ή από image που παρέχει ήδη το σωστό configuration ή από το να γίνει format ενός σκληρού δίσκου και η εγκατάσταση όλων των απαιτούμενων εξαρτήσεων. 3. Αυξάνονται οι δυνατότητες ενορχήστρωσης του συστήματος, με εργαλεία όπως το Kubernetes [75]. Η χρήση τέτοιων εργαλείων στις μέρες μας δεν απαιτεί απαραίτητα βαθιές γνώσεις, μιας και η συντήρησή τους μπορεί να γίνει από τρίτους, συνήθως μέσα από εταιρείες που προσφέρουν Cloud υπηρεσίες με managed services. Για παράδειγμα, τέτοιες υπηρεσίες προσφέρει ο Digital Ocean [76], το Google Cloud Platform [77] και το Microsoft Azure [78]. Μέσω του Kubernetes ένας Devops Engineer έχει τη δυνατότητα να περιγράψει την θεμιτή κατάσταση ολόκληρου του συστήματος και αυτό να έρθει στη θεμιτή κατάσταση αυτόματα. 4.3 Microservices Υπάρχουν αρκετοί παρόμοιοι ορισμοί για τα microservices: 1. «Microservices are small, autonomous services that work together» όπως τα όρισε ο Sam Newman to 2015 [79] 2. «[Microservices describe a] loosely coupled service-oriented architecture with bounded contexts» όπως τα όρισε ο Adrian Cockcroft το 2014 [80] Τα microservices είναι μια αρχιτεκτονική για ανάπτυξη συστημάτων λογισμικού. Το σύστημα που ακολουθεί την αρχιτεκτονική των microservices αποτελείται από πολλά μικρά (εξού και το πρόθεμα «micro») προγράμματα, που καλούνται services και που είναι «χαλαρά συνδεδεμένα» μεταξύ τους. Το ότι τα microservices έχουν bounded context σημαίνει ότι οι υποχρεώσεις ενός microservice πρέπει να είναι περιορισμένες και καλώς ορισμένες. Τα microservices αποτελούν μία παραλλαγή του SOA (service-oriented architecture). Η φιλοσοφία των microservices μοιάζει πολύ με την αντίστοιχη φιλοσοφία του Unix και των εργαλείων του GNU η οποία αναφέρει: «Κάνε μία συγκεκριμένη, καλώς ορισμένη εργασία, και 39 πραγματοποίησέ την ορθά» [81]. Το όνομα «micro» αναφέρεται ακριβώς σε αυτό το γεγονός, ότι δηλαδή κάθε service δεν έχει μεγάλο εύρος εργασιών, αλλά αναλαμβάνει να ενθυλακώσει τη λογική ενός μόνο μικρού domain του business logic, γνωστό και ως Single Responsibility Principle (SRP) [82]. 4.3.1 Η αρχιτεκτονική της monolithic εφαρμογής Ως monolithic ή single-tier εφαρμογή χαρακτηρίζεται μία εφαρμογή η οποία αναλαμβάνει όλες τις διεργασίες που έχει να πραγματοποιήσει το σύστημα, δηλαδή, έχει την υποχρέωση να σχεδιάζει το γραφικό περιβάλλον με το οποίο αλληλοεπιδρά ο τελικός χρήστης, τους κανόνες της επιχείρησης (business rules) και την επεξεργασία των δεδομένων [83]. Σύμφωνα με τον ορισμό που δίνει η Microsoft, μία εφαρμογή η οποία επικοινωνεί με διαφορετικά μέρη του συστήματος, όπως για παράδειγμα ένα caching σύστημα ή μία βάση δεδομένων, παραμένει να θεωρείται ως monolithic, δεδομένου ότι η εφαρμογή αυτή είναι αυτή που πραγματοποιεί τους κανόνες τις επιχείρησης και οτιδήποτε άλλο που χρειάζεται προσαρμοσμένη λογική. Κατά τη κατασκευή μίας τέτοιας εφαρμογής είναι δύσκολο τα διαφορετικά επιμέρους κομμάτια της να μην δημιουργήσουν ισχυρή σύνδεση (tight coupling) μεταξύ τους, και απαιτείται πολλές φορές επιπλέον κώδικας (όπως για παράδειγμα interfaces, επιπλέον abstractions) και code refactoring για να συνεχίσει o κώδικάς της να είναι διαχειρίσιμος μετά από την αύξηση της πολυπλοκότητάς του. Ακόμα, τυπικά ολόκληρη η monolithic εφαρμογή είναι γραμμένη στην ίδια γλώσσα προγραμματισμού. Όμως σε πολλές περιπτώσεις, λόγω του ότι μία επιχείρηση έχει πολλές ανάγκες που θέλει να ικανοποιήσει με τη χρήση προσαρμοσμένου λογισμικού, σπάνια μία μόνο γλώσσα προγραμματισμού μπορεί να τις καλύψεις όλες ικανοποιητικά. Για παράδειγμα, μερικές γλώσσες προγραμματισμού είναι ειδικά σχεδιασμένες για μεγάλες επιδόσεις κατά τη διάρκεια υπολογισμών, ενώ άλλες για εύκολη κατασκευή διακομιστών. Όταν μία εφαρμογή καλείται να πραγματοποιήσει πολλές διαφορετικές λειτουργίες οι οποίες δεν μπορούν όλες να πραγματοποιηθούν ικανοποιητικά από μία και μόνο γλώσσα προγραμματισμού, είναι επόμενο ότι είτε η εφαρμογή δεν θα λειτουργεί με ικανοποιητική απόδοση, είτε ότι ο κώδικας δεν θα μπορεί να είναι εύκολα διαχειρίσιμος, καθώς έχει χρησιμοποιηθεί ακατάλληλη γλώσσα προγραμματισμού. 40 Εικόνα 17 Παράδειγμα μονολιθικής εφαρμογής Η monolithic εφαρμογή δεν έχει μόνο μειονεκτήματα και είναι το πιο συχνό είδος εφαρμογών για υπολογιστές. Συνήθως όταν το προϊόν υπό κατασκευή αφορά εφαρμογές χρήστη σε έναν προσωπικό υπολογιστή, το monolithic application είναι η προτεινόμενη μέθοδος κατασκευής λογισμικού ακόμα και στις μέρες μας, αφού η λογική της εφαρμογής σπάνια είναι τόσο περίπλοκη όσο οι απαιτήσεις των κανόνων μιας επιχείρησης. Παρ’ όλα αυτά, τελευταία έχει παρατηρηθεί μία τάση κάποια προγράμματα να χρησιμοποιούν διαφορετικά child-processes, διότι αυτό παρέχει πλεονεκτήματα όσον αφορά την ασφάλεια και το responsiveness. Για παράδειγμα, οι δημοφιλείς περιηγητές Chrome και Firefox χρησιμοποιούν διαφορετικά child-processes για τα tabs και για τα browser-plugins ή για να δείχνουν το γραφικό περιβάλλον και να προσπελάζουν ιστοσελίδες [84] [85]. Έτσι αν ένα tab για παράδειγμα γίνει unresponsive, η υπόλοιπη εφαρμογή μπορεί να συνεχίσει να τρέχει κανονικά καθώς αποτελείται από διαφορετικά processes μέσα στο σύστημα. Αυτό αποτελεί ένα παράδειγμα της μίξης μιας monolithic εφαρμογής με την service-oriented αρχιτεκτονική. 4.3.2 Η αρχιτεκτονική των microservices Τα microservices έχουν μερικά χαρακτηριστικά που είναι ευρέως αποδεχτά μεταξύ των developers τα οποία είναι: 1. Καλώς ορισμένη συμπεριφορά. Ως συμπεριφορά του microservice ορίζουμε το ποια μηνύματα μπορεί να δεχτεί από εξωτερικές πηγές ή άλλα services και το πώς αντιδρά σε αυτά. 41 2. Λύνει ένα συγκεκριμένο πρόβλημα. Το πρόβλημα αυτό αποτελεί μέρος των κανόνων της επιχείρησης. Το microservice δεν χρειάζεται να ενθυλακώνει όλη τη λογική των κανόνων της επιχείρησης. Αντίθετα, θα πρέπει να λύνει μόνο ένα λογικά ομαδοποιημένο σύνολο προβλημάτων αυτής. 3. Πρέπει να είναι ασθενώς συνδεδεμένο (loosely coupled) με τα υπόλοιπα microservices του συστήματος. Αυτό σημαίνει ότι εσωτερικές αλλαγές σε ένα microservice δεν θα πρέπει να επηρεάζουν άλλα microservices. Ένα microservice πρέπει να έχει τη δυνατότητα να αλλάξει ακόμα και γλώσσα προγραμματισμού χωρίς να πρέπει να γίνουν αλλαγές στα άλλα microservices με τα οποία επικοινωνεί. 4. Πρέπει να μπορεί να επαναχρησιμοποιηθεί, δηλαδή αν διαφορετικά microservices απαιτούν τη χρήση ενός service το οποίο προσφέρεται από ένα τρίτο microservice, θα πρέπει και τα δύο να μπορούν να εξυπηρετηθούν. 5. Να μπορεί να αναβαθμιστεί ανεξάρτητα από τα υπόλοιπα microservices του συστήματος. Σε μία μονολιθική εφαρμογή, η μικρότερη αλλαγή στον κώδικα μπορεί να οδηγήσει σε ολόκληρη την εφαρμογή να πρέπει να ξαναγίνει compile/transpile, unit και integration testing και να πρέπει να αντικαταστήσει την εφαρμογή που τρέχει στο production περιβάλλον. Σε ένα σύστημα που ακολουθεί την αρχιτεκτονική των microservices, κάθε microservice που έχει αλλαγές πρέπει να μπορεί να αναβαθμιστεί ανεξάρτητα, μια διαδικασία πολύ πιο εύκολη από την αναβάθμιση μιας μονολιθικής εφαρμογής. Εικόνα 18 Παράδειγμα της microservice αρχιτεκτονικής. Παρατηρήστε ότι υπάρχουν δύο instances του Video Processing microservice Τυπικά τα microservices, λόγω του ότι τρέχουν σε διαφορετικά μηχανήματα (είτε φυσικά, είτε virtual machines, containers κλπ), επικοινωνούν μεταξύ τους μέσω του HTTP πρωτοκόλλου για την ανταλλαγή μηνυμάτων. Αρκετά δημοφιλές είναι και το gRPC framework για επικοινωνία μεταξύ microservices [86]. Σε πολλές περιπτώσεις χρησιμοποιείται μία publish-subscribe αρχιτεκτονική έτσι ώστε να γίνεται ανταλλαγή μηνυμάτων με loosely-coupled τρόπο. Σε άλλες περιπτώσεις τα microservices μπορεί να τρέχουν κάτω από το ίδιο σύστημα, ακόμα και μέσα σε διαφορετικούς containers, και να επικοινωνούν μεταξύ τους με τεχνικές IPC (Inter-Process Communication), όπως είναι η shared memory, κάνοντας χρήση του ipc namespace του docker που έχει προαναφερθεί. Τα microservices συχνά δουλεύουν σε συνεργασία με το containerization και γίνεται container – και κατά συνέπεια και microservice – orchestration μέσω των κατάλληλων εργαλείων όπως είναι το kubernetes. Για παράδειγμα, το ίδιο το kubernetes δίνει τη δυνατότητα ομαδοποίησης διαφορετικών instances ενός microservice σε πόρους που ονομάζει «Services» [87]. Κάθε 42 kubernetes Service έχει το δικό του domain name service (DNS) όνομα μέσα στο kubernetes cluster και έτσι οι υπόλοιπες ενθυλακωμένες εφαρμογές μπορούν να επικοινωνήσουν εύκολα με άλλες, χωρίς γνώση των δυναμικών IPs των δεκτών. Αν μέσα σε ένα Service έχουν ομαδοποιηθεί παραπάνω από μία εφαρμογές, τα μηνύματα που κατευθύνονται προς το συγκεκριμένο Service καταλήγουν εν τέλει στις εν λόγω ομαδοποιημένες εφαρμογές με χρήση του round-robin αλγορίθμου. Το kubernetes δίνει και άλλες χρήσιμες και πολύπλοκες δυνατότητες orchestration, όπως τη δυνατότητα χρήσης διαφορετικού τύπου VM ανάλογα με τις ανάγκες των εφαρμογών προς εκτέλεση, τη χρήση horizontal autoscaling σε επίπεδο εφαρμογής και σε επίπεδο VM, τη χρήση κανόνων για εκτέλεση ίδιου τύπου services σε διαφορετικά VMs για πιστοποίηση του high availability σε περίπτωση hardware failure κ.ά. Εικόνα 19 Επικοινωνία μεταξύ μservices με τη χρήση broker και kubernetes Services 4.3.3 Πλεονεκτήματα και μειονεκτήματα των microservices Τα microservices παρουσιάζουν πολλά πλεονεκτήματα: 1. Τα microservices χρησιμοποιούν programming-language-agnostic πρωτόκολλα επικοινωνίας και έτσι η γλώσσα προγραμματισμού σε κάθε microservice μπορεί να είναι διαφορετική. Αυτό είναι πολύ σημαντικό γιατί το κάθε microservice μπορεί να επικεντρωθεί στη λύση συγκεκριμένου είδους προβλημάτων και έτσι μπορεί να χρησιμοποιηθεί μία γλώσσα προγραμματισμού που είναι πιο κατάλληλη για τη λύση αυτών. Για παράδειγμα, ένα microservice που είναι υπεύθυνο στο να απαντάει σε network requests μπορεί να είναι γραμμένο σε Node.js [88] ή Ruby [89] (ίσως και με χρήση του Ruby on Rails [90]) αλλά ένα άλλο μέρος της εφαρμογής που είναι υπεύθυνο για τη συμπίεση δεδομένων ή την επεξεργασία μεγάλου όγκου αυτών, θα μπορούσε να είναι γραμμένο σε C++ [91] ή Go [92]. 43 2. Κάθε διαφορετικό πρόβλημα έχει διαφορετικές ανάγκες από υπολογιστικούς πόρους. Όταν μία μονολιθική εφαρμογή έχει την υποχρέωση να ικανοποιεί όλες τις περίπλοκες προδιαγραφές, αυτό σημαίνει ότι όλα τα διαφορετικά components του συστήματος αναγκάζονται να μοιράζονται τους ίδιους πόρους, καθώς ολόκληρη η εφαρμογή εκτελείται μέσα στο ίδιο υπολογιστικό σύστημα. Αυτό το πρόβλημα δεν μπορεί να λυθεί με χρήση κάποιας τεχνολογίας που παρέχεται με χρήστη των container, καθώς οι περιορισμοί που μπορούν να επέλθουν μέσω των cgroups (Control Groups) αφορά ολόκληρο τον container. Τα cgroups δεν μπορούν να εφαρμόσουν περιορισμούς σε διαφορετικά μέρη μιας εφαρμογής. Εφόσον όλη η μονολιθική εφαρμογή θα τρέχει σε έναν container, οι περιορισμοί που θα επέλθουν θα αφορούν όλο τον container. Αυτό το πρόβλημα όμως λύνεται με τη χρήση microservices, καθώς κάθε microservice μπορεί να τρέχει σε διαφορετικό σύστημα, είτε φυσικό είτε εικονικό. Αυτό σημαίνει ότι τα κομμάτια της εφαρμογής που έχουν μεγάλες απαιτήσεις από διαφορετικούς πόρους μπορεί να βρίσκονται σε διαφορετικά μηχανήματα ή εικονικά περιβάλλοντα που να τους ικανοποιούν αυτές τις συγκεκριμένες απαιτήσεις. Για παράδειγμα, μία in-memory keyvalue βάση δεδομένων όπως το Redis [93] μπορεί να έχει υψηλές απαιτήσεις σε κύρια μνήμη, μία διεργασία συμπίεσης (compression task) μπορεί να έχει μεγάλες απαιτήσεις σε επεξεργαστική ισχύ και κάποιο άλλο service μπορεί να έχει υψηλές απαιτήσεις για καλή απόδοση στο input/output του σκληρού δίσκου. Ο διαχωρισμός αυτός των services μπορεί ταυτόχρονα (α) να μειώσει πολύ το κόστος του συστήματος καθώς το horizontal scaling της εφαρμογής πλέον αφορά μόνο το microservice που το έχει ανάγκη, και δεν χρειάζεται το κάθε instance της εφαρμογής να περιλαμβάνει τους πόρους που απαιτούνται από όλα τα διαφορετικά components της μονολιθικής εφαρμογής, και (β) να αυξήσει τη συνολική απόδοση του συστήματος εφόσον πλέον η κάθε διεργασία αναπτύσσεται χρησιμοποιώντας τις κατάλληλες τεχνολογίες. 3. Τα microservices μπορούν μερικές φορές να είναι πιο εύκολα στη διαχείριση, έχοντας τα σωστά εργαλεία, καθώς η αναβάθμιση ενός δεν απαιτεί την αναβάθμιση όλου του συστήματος και μπορεί να γίνει μπορεί να γίνει ανεξάρτητα από αυτό. Επίσης η ανάπτυξη του λογισμικού τους, λόγω του μικρού τους μεγέθους είναι συνήθως πιο εύκολη. Επίσης, πιο εύκολα και σε λιγότερο χρόνο από αυτό που απαιτείται σε μια μονολιθική εφαρμογή, μπορούν να εκτελεστούν τα unit tests και τα διεργασίες κτισίματος της εφαρμογής. 4. Τα microservices μερικές φορές μπορούν να ρίξουν το κόστος κατασκευής ενός συστήματος, καθώς οι ομάδες μπορούν να δουλέψουν ανεξάρτητα στο κάθε service καθορίζοντας αυστηρά μόνο τον τρόπο επικοινωνίας μεταξύ των services. Δεν χρειάζεται να συμφωνηθεί η γλώσσα προγραμματισμού ή ο τρόπος κατασκευής του service ή οποιεσδήποτε άλλες συνεννοήσεις που μπορούν να κοστίσουν σε χρόνο, ανάλυση και σχεδιασμό. Όπως έχει υποστηριχθεί από την εμπειρία, η ταχύτητα με την οποία αναπτύσσεται ένα σύστημα είναι έχει άμεση σχέση με το πόσα άτομα χρειάζεται να επικοινωνούν και να συνεννοούνται μεταξύ τους για την κατασκευή του [94]. Παρ’ όλα αυτά, όπως και κάθε τεχνολογία, τα microservices παρουσιάζουν αντίστοιχα και μειονεκτήματα: 1. Η επικοινωνία δύο ή και περισσότερων microservices μέσω δικτύου βάζει πολύ μεγάλες επιβαρύνσεις στη συνολική απόδοση του συστήματος. Η επιβάρυνση αυτή είναι πολλές τάξεις μεγέθους μεγαλύτερη από αυτή που παρουσιάζεται από τα in-process calls που γίνονται σε μία τυπική μονολιθική εφαρμογή [95]. Τα πλεονεκτήματα ταχύτητας, λόγω της χρήσης των κατάλληλων τεχνολογιών, που κερδίζει κάποιος με χρήση των microservices σε ένα σύστημα, μπορεί να ισοσταθμίζουν το μειονέκτημα αυτό σε μερικές περιπτώσεις, ενώ σε άλλες όχι. Προσεκτική μελέτη πρέπει να πραγματοποιηθεί για να γίνει η σωστή επιλογή μεταξύ χρήσης ή μη των microservices, σύμφωνα με τις αντίστοιχες απαιτήσεις του συστήματος. 2. Η αλλαγή των απαιτήσεων μίας εφαρμογής είναι ένα πολύ συνηθισμένο φαινόμενο. Οι κανόνες μιας επιχείρησης δεν είναι στατικοί και αλλάζουν με την πάροδο του χρόνου 44 λόγω καινούργιων δεδομένων. Η επικοινωνία των προδιαγραφών ενός συστήματος πρέπει να πραγματοποιηθεί με χρήση γραπτού και προφορικού λόγου και μερικές φορές μπορεί να είναι ατελείς και να χρειάζονται διορθώσεις. Επειδή η τμηματοποίηση ενός συστήματος σε microservices στηρίζεται ριζικά μέσα στους κανόνες της επιχείρησης (business logic), μεγάλες αλλαγές σε αυτούς τους κανόνες μπορεί να οδηγήσουν σε μεγάλες αρχιτεκτονικές αλλαγές στο σύστημα. Τα microservices μπορούν να χρησιμοποιηθούν με μεγάλη επιτυχία όταν οι απαιτήσεις της εφαρμογής δεν αλλάζουν τόσο ριζικά, αλλά υπάρχουν μικρές επαναληπτικές αλλαγές. Μία σημαντική αλλαγή στους κανόνες της επιχείρησης μπορεί να έχει ως αποτέλεσμα χρήση ακόμα και διαφορετικών τεχνολογιών από τις προοριζόμενες, πριν την αλλαγή. 3. Μία φαινομενικά μικρή αλλαγή στις προδιαγραφές της εφαρμογής μπορεί να έχει ως αποτέλεσμα την απαίτηση χρήσης διαφορετικής τεχνολογίας για την κατασκευή ενός microservice. Η τεχνολογία με την οποία έχει κατασκευαστεί ένα microservice είναι προσεκτικά επιλεγμένη με βάση τους κανόνες της επιχείρησης τους οποίους υλοποιεί ή γενικότερα το πρόβλημα που ζητείται να λύσει. Αν αυτοί οι κανόνες αλλάζουν, με την πάροδο του χρόνου η τεχνολογία που χρησιμοποιείται για την κατασκευή του microservice μπορεί να μην είναι πλέον η πιο κατάλληλη. 4. Η πολυπλοκότητα του συνολικού συστήματος μεγαλώνει. Ενώ η συγγραφή κώδικα σε ένα microservice μπορεί να είναι πιο εύκολη απ’ ότι σε μία μονολιθική εφαρμογή, η διαχείριση όλου του συστήματος από microservices πρέπει να γίνει με πολύ μεγαλύτερη προσοχή από την αντίστοιχη διαχείριση ενός συστήματος που στηρίζεται σε μονολιθική εφαρμογή. Για αρχή, πρωτόκολλα επικοινωνίας πρέπει να χρησιμοποιηθούν για την επικοινωνία μεταξύ των microservices και πρέπει να διασφαλισθεί δικτυακή επικοινωνία μεταξύ τους. Επιπλέον, σε διαφορετικά microservices μπορούν να χρησιμοποιούνται διαφορετικές γλώσσες προγραμματισμού, διαφορετικά λειτουργικά συστήματα και έχουν διαφορετικές απαιτήσεις σε πόρους, καθώς και μπορούν και κάνουν horizontal scaling ανεξάρτητα. Ενώ υπάρχουν εργαλεία που είναι σχεδιασμένα για την διαχείριση τέτοιου είδους συστημάτων, η πολυπλοκότητά τους είναι αδιαμφησβήτητα μεγαλύτερη από αυτών που είναι στηριγμένα σε μία μονολιθική εφαρμογή. Αυτό έχει ως αποτέλεσμα και την ανάγκη ατόμων με την κατάλληλη εμπειρία για τη διαχείριση των πολύπλοκων αυτών συστημάτων. 5. Αξίζει να αναφερθεί ότι απλές προγραμματιστικές τεχνικές, όπως για παράδειγμα η εύρεση προγραμματιστικών λαθών (debugging) ή η αποθήκευση αρχείων καταγραφής (logging) είναι διαδικασίες αυξημένης πολυπλοκότητας στα microservices. Όταν κάποιο λάθος εντοπιστεί σε κάποιο microservice, θα πρέπει να εντοπιστεί από πού είναι η πηγή του. Το microservice στο οποίο εμφανίστηκε το λάθος δεν είναι απαραίτητο ότι έχει πρόβλημα στον κώδικά του το ίδιο. Αντιθέτως, προσεκτική έρευνα πρέπει να γίνει για τον τρόπο με τον οποίο επικοινώνησαν τα microservices και τα δεδομένα που αντάλλαξαν έτσι ώστε να εντοπιστεί η πηγή του προβλήματος. Έχουν αναπτυχθεί βέβαια ειδικά εργαλεία που βοηθάνε σε αυτή τη διαδικασία, όπως το Squash [96], το Telepresence [97] και το OpenTracing [98]. Ως συμπέρασμα, τα microservices, αν και δίνουν πολλά πλεονεκτήματα, δεν είναι κατάλληλα να χρησιμοποιηθούν για τη λύση οποιουδήποτε προβλήματος, και σίγουρα δεν είναι η σωστή επιλογή για την κατασκευή ενός small-scale συστήματος το οποίο δεν έχει πολλές διαφορετικές και περίπλοκες απαιτήσεις. 45 Κεφάλαιο 5 5. Επικοινωνία των microservices Σε αυτό το κεφάλαιο εξετάζονται οι τρόποι με τους οποίους τα microservices μπορούν να επικοινωνήσουν μεταξύ τους. Αρχικά, γίνεται αναφορά σε διαφορετικούς τρόπους και πρωτόκολλα επικοινωνίας καθώς και τα πλεονεκτήματα και τα μειονεκτήματα που παρουσιάζει καθένα από αυτά και στη συνέχεια παρουσιάζονται τα αποτελέσματα μετρήσεων που έγιναν για τη σύγκριση της μεταξύ τους απόδοσης. 5.1 Ανταλλαγή μηνυμάτων μεταξύ microservices 5.1.1 Εισαγωγή Ο τρόπος με τον οποίο θα γίνεται η ανταλλαγή μηνυμάτων μεταξύ των microservices είναι μία σημαντική απόφαση κατά την κατασκευή ενός συστήματος βασισμένου σε microservices. Η καθυστέρηση που επιφέρει η χρήση του δικτύου για την ανταλλαγή μηνυμάτων μερικές φορές δημιουργεί σημαντικά προβλήματα που μπορεί να οδηγήσουν, στη χειρότερη περίπτωση, σε μηιδανικές επιλογές τμηματοποίησης του συστήματος σε services για την αποφυγή αυτών ή και σε μη ορθές αρχιτεκτονικές επιλογές. Αξίζει να αναφερθεί ότι τα microservices δεν είναι υποχρεωτικό να βρίσκονται σε διαφορετικά host μηχανήματα. Δύο microservices για παράδειγμα τα οποία επικοινωνούν συχνά και έχουν πολύ αυστηρούς περιορισμούς χρόνου, μπορεί να τρέχουν στο ίδιο host ακόμα και containerized και να επικοινωνούν με IPC (Inter-Process Communication) τεχνικές που δίνουν τη δυνατότητα πολύ γρήγορης ανταλλαγής μηνυμάτων, όπως η τεχνική χρήσης κοινής μνήμης. Μία τέτοια τεχνική μπορεί να ενεργοποιηθεί μέσω του ipc namespace του linux kernel και να χρησιμοποιηθεί με docker containers, για παράδειγμα. Όμως σε μεγάλα συστήματα, η χρήση περισσότερων από ενός μηχανημάτων (συνήθως virtual) συνηθίζεται, διότι γίνεται εκμετάλλευση των διαφορετικών πόρων που μπορεί να προσφέρει το κάθε σύστημα σύμφωνα με τις ανάγκες του προβλήματος που λύνει το κάθε microservice. Ακόμα, οι IPC τεχνικές σπάνια προσφέρουν τις δυνατότητες πιο εξελιγμένων τρόπων επικοινωνίας, όπως publish-subscribe messaging. Γι’ αυτούς τους λόγους, συνήθως δεν είναι δυνατή η επικοινωνία μεταξύ όλων των microservices με τεχνικές IPC και γι’ αυτό γίνεται αναφορά και σε διαφορετικούς τρόπους επικοινωνίας. 5.1.2 Ανταλλαγή μηνυμάτων στο δίκτυο Σε πολλές περιπτώσεις συνιστάται τα microservices να έχουν μόνο χαλαρή σύνδεση μεταξύ τους και να μην εξαρτάται η λειτουργία του ενός από τη λειτουργία κάποιου άλλου. Το σύστημα επίσης γίνεται πολύ πιο ευέλικτο και απλό όταν ο τρόπος επικοινωνίας μεταξύ των διαφορετικών services δεν εξαρτάται από την ίδια την υλοποίηση του service καθ’ αυτού, αλλά είναι υποχρέωση μίας άλλης οντότητας λογισμικού, τον broker. Ως message broker, integration broker ή interface engine, όπως έγινε αναφορά και στο κεφάλαιο 2, ορίζεται ένα υποσύστημα το οποίο διευκολύνει την ανταλλαγή μηνυμάτων μεταξύ των διαφορετικών συστημάτων και κρύβει την πολυπλοκότητα του συστήματος. Σύμφωνα με το Gartner Glossary: «Also called an interface engine or a message broker, an IB is a third-party intermediary that facilitates interactions between applications. IBs minimally provide message transformation and 46 routing services. They mostly communicate program to program; they integrate previously independent applications at the application-logic level of the software design» [99] Για παράδειγμα, με χρήση ενός publish-subscribe messaging πρωτοκόλλου, όταν ένα σύστημα χρειάζεται να στείλει ένα μήνυμα σε 3 υποσυστήματα και δεν γίνει χρήση ενός message broker, θα πρέπει να ξέρει πώς να επικοινωνήσει με αυτά τα 3 υποσυστήματα. Έτσι η σύνδεση μεταξύ των συστημάτων δεν είναι χαλαρή (δηλαδή πραγματοποιείται tight coupling) και ο κώδικας των κάθε από τα 3 αυτά υποσυστήματα δεν περιλαμβάνει μόνο τη λογική του αλγόριθμου που απαιτείται για τη λύση του προβλήματος, αλλά και λογική για το πώς πρέπει να γίνει η επικοινωνία μεταξύ των υπόλοιπων microservices. Παρ’ όλα αυτά, με τη χρήση ενός message broker, το σύστημα δεν χρειάζεται να ξέρει πόσα ή ποια είναι τα συστήματα που χρειάζεται να λάβουν το μήνυμα. Το μήνυμα απλά στέλνεται στον message broker από κάθε ξεχωριστό microservice που χρειάζεται να επικοινωνήσει με κάποιο άλλο. Ο message broker αναλαμβάνει την παράδοσή του. Αυτός ο τρόπος κατασκευής κατανεμημένων συστημάτων μειώνει σημαντικά το πόσο σφιχτά διασυνδεδεμένα είναι τα συστήματα μεταξύ τους και την πολυπλοκότητα του κώδικα που χρειάζεται να γραφτεί για την επικοινωνία μεταξύ των συστημάτων αυτών. Στη συνέχεια αυτού του κεφαλαίου παρουσιάζονται γνωστά πρωτόκολλα επικοινωνίας που χρησιμοποιούνται συχνά σε συστήματα που χρησιμοποιούν microservice αρχιτεκτονική και κάνουν χρήση κάποιου message broker. 5.1.2.1 Advanced Message Queuing Protocol (AMQP) Το AMQP είναι ένα ανοιχτό πρωτόκολλο επικοινωνίας σε επίπεδο εφαρμογής [100] [101]. Τα κύρια χαρακτηριστικά του τρόπου λειτουργίας του είναι τα εξής: 1. Το σύστημα τύπων του (type system) [102], που αποτελείται από a. Primitive types, όπως strings, αριθμούς, ημερομηνίες, δυαδικά δεδομένα, universally unique identifiers (UUIDs) και άλλα. Συνολικά υπάρχουν 24 διαφορετικά primitive types που υποστηρίζονται από το AMQP. b. Described types (περιγραφόμενοι τύποι), οι οποίοι χρησιμοποιούνται για να δώσουν context στον τύπο των δεδομένων ο οποίος μεταφέρεται από το AMQP. Για παράδειγμα, αν τα δεδομένα που μεταφέρονται αναφέρονται σε έναν πελάτη, στα δεδομένα αυτά μπορεί να μπει ο σχολιασμός (descriptor) «customer». Το service που λαμβάνει την πληροφορία μπορεί να χρησιμοποιήσει το descriptor για να κάνει την κατάλληλη επεξεργασία των δεδομένων. c. Composite types, που χρησιμοποιούνται για την κωδικοποίηση δομημένων δεδομένων. d. Restricted types, που είναι τύποι δεδομένων με περιορισμένο πεδίο τιμών, όπως για παράδειγμα τα enums (enumerations), που επιτρέπουν σε ένα string να λάβει τιμές μόνο από ένα συγκεκριμένο σύνολο. 2. Το transport layer [103], το οποίο αποτελείται από nodes (όπως producers, consumers και queues) και links μεταξύ τους. 3. Το messaging layer [104], το οποίο καθορίζει τι format πρέπει να έχουν τα προς μετάδοση μηνύματα και σε ποιες καταστάσεις μπορούν να καταλήξουν. 4. Το transactions layer [105], που επιτρέπει την συντονισμένη μεταφορά μηνυμάτων που θα μεταδίδονταν ανεξάρτητα χωρίς αυτό το layer. 5. Το security layer [106], το οποίο υποστηρίζει τόσο αυθεντικοποίηση όσο και κρυπτογράφηση των δεδομένων χρησιμοποιώντας SASL (Simple Authentication and Security Layer) και TLS (Transport Layer Security). Η χρήση του AMQP θα μπορούσε να προτιμηθεί λόγω των παρακάτω πλεονεκτημάτων που προσφέρει: 1. Ασύγχρονη message-oriented επικοινωνία, η οποία επιτρέπει ασύγχρονη επικοινωνία μεταξύ 2 οντοτήτων. Μία οντότητα που πρέπει να παραλάβει ένα μήνυμα μπορεί να 47 αντιμετωπίζει προβλήματα, όπως δυσλειτουργίες στο hardware ή software bugs. Λόγω αυτού, μπορεί να μη μπορέσει να λάβει μηνύματα τα οποία απευθύνονταν σε αυτό κατά τη διάρκεια του προβλήματος. Όμως, τα μηνύματα αυτά μπορεί να είναι πολύ σημαντικό να προσπελαστούν και το πρόβλημα αυτό λύνεται με χρήση της ασύγχρονης επικοινωνίας. Επίσης, με χρήση της ασύγχρονης επικοινωνίας, στην περίπτωση των microservices, ένα service μπορεί να απομακρυνθεί από το δίκτυο για να αναβαθμιστεί χωρίς να χάσει τα μηνύματα τα οποία έχουν σταλθεί κατά τη διάρκεια που αυτό δεν βρίσκεται σε λειτουργία. Μόλις το microservice επανασυνδεθεί στο δίκτυο, θα λάβει τα μηνύματα αυτά. 2. Message queuing, η οποία επιτρέπει την κατασκευή μίας λίστας από διεργασίες από τους producers που πρέπει να πραγματοποιηθούν/καταναλωθούν από servicesworkers/consumers. 3. Point-to-point επικοινωνία, η οποία επιτρέπει την decoupled επικοινωνία μεταξύ 2 services. Τα services δεν χρειάζεται να γνωρίζουν λεπτομέρειες για τον παραλήπτη του μηνύματός τους. Η επικοινωνία μπορεί να γίνει χρησιμοποιώντας μοναδικά identifiers που περιγράφουν το service-παραλήπτη. 4. Publish-subscribe επικοινωνία, κατά την οποία ένα ή περισσότερα services μπορούν να κάνουν subscribe σε ένα ή περισσότερα topics και ένα ή περισσότερα services μπορούν να κάνουν publish σε ένα ή περισσότερα topics. 5. Ασφάλεια και αξιοπιστία. Το AMQP προσφέρει τη δυνατότητα αποστολής μηνυμάτων που απαιτούν αυθεντικοποίηση και μπορούν να υποστηρίξουν κρυπτογράφηση. Η πιο γνωστή message broker εφαρμογή η οποία υλοποιεί το AMQP είναι το RabbitMQ [107]. 5.1.2.2 ZeroMQ To ZeroMQ (0MQ) [108] είναι μία πολύ ελαφριά βιβλιοθήκη που ορίζει ένα application programming interface το οποίο είναι ειδικά σχεδιασμένο για να μειώσει το overhead του δικτύου σε κατανεμημένες εφαρμογές όπως τα microservices και να βοηθήσει στο να αυξηθεί η γενική απόδοση του συστήματος. Ιδιαίτερο χαρακτηριστικό αυτής της βιβλιοθήκης αποστολής μηνυμάτων είναι ότι μπορεί να δουλέψει και χωρίς να υπάρχει ένας συγκεκριμένος message broker [109]. Αυτό μπορεί να συμβεί διότι η συγκεκριμένη βιβλιοθήκη προσφέρει στους προγραμματιστές εργαλεία χαμηλού επιπέδου έτσι ώστε οι ίδιοι να δημιουργήσουν την πιο αποδοτική αρχιτεκτονική για επικοινωνία μεταξύ services σύμφωνα με τις ανάγκες του συστήματος. Το ZeroMQ αναγνωρίζει τα τρία βασικά μειονεκτήματα του να υπάρχει ένας message broker: 1. Απαιτείται πολύ μεγάλος όγκος δεδομένων για την επικοινωνία η οποία όλη περνάει από τον message broker. 2. Ο message broker χρειάζεται να διαχειριστεί αυτόν τον πολύ μεγάλο όγκο δεδομένων και σε μερικές περιπτώσεις μπορεί να είναι το σημείο του συστήματος που καθυστερεί περισσότερο από τα υπόλοιπα (bottleneck of the system). 3. Εκτός και αν έχει προβλεφθεί και ο ίδιος ο message broker είναι κατασκευασμένος με τη λογική του high availability, ο message broker αποτελεί μοναδικό σημείο αποτυχίας (single point of failure) για το σύστημα, πράγμα που σημαίνει ότι αν, για οποιοδήποτε λόγο, ο broker σταματήσει να λειτουργεί, τότε όλο το σύστημα θα σταματήσει να λειτουργεί. 48 Εικόνα 20 [109] Το αποτέλεσμα ενός remote procedure call όταν υπάρχουν αλληλεξαρτήσεις και χρήση broker Η παραπάνω εικόνα δείχνει ένα παράδειγμα RPC (remote procedure call). Υπάρχουν τέσσερεις εφαρμογές που συμμετέχουν σε μία service-oriented αρχιτεκτονική και ένας broker που αναλαμβάνει τη μεταξύ τους επικοινωνία. Μία εφαρμογή ζητάει από κάποια άλλη εφαρμογή να εκτελέσει κάποιον υπολογισμό και να της επιστρέψει το αποτέλεσμα. Στο συγκεκριμένο παράδειγμα, το App A ζητάει μία εκτέλεση από το App B, όμως το B για να ολοκληρώσει τον υπολογισμό, πρέπει να κάνει ένα RPC στο App C και το C εν συνεχεία στο D. Μόλις το App D τελειώσει την εκτέλεσή του, πρέπει να γυρίσει το αποτέλεσμά του στο App C, το C στο B και τελικά το B στο A. Έτσι, στη συγκεκριμένη περίπτωση, λόγω των αλληλεξαρτήσεων μεταξύ των εφαρμογών, καθώς και της ανάγκης του broker που πρέπει να διαμεσολαβήσει σε καθεμία από αυτές τις επικοινωνίες, μία απλή remote procedure call από το App A στο B έχει ως αποτέλεσμα την πραγματοποίηση 12 διαδικτυακών πηδημάτων (12 network hops), τα οποία είναι μαρκαρισμένα με τους αριθμούς 2 έως 13. Αυτό αποτελεί ρεαλιστικό παράδειγμα πραγματικής χρήσης microservices καθώς οι διαφορετικές υποχρεώσεις του κάθε microservice συχνά τα αναγκάζει να κάνουν RPCs σε διαφορετικά microservices έτσι ώστε να τηρείται το SRP. Επίσης, ο broker θα πρέπει να επεξεργαστεί και να κάνει routing συνολικά σε 6 διαφορετικά μηνύματα. Τα μηνύματα αυτά μπορούν να μειωθούν αν εφαρμοστεί μία λιγότερο ευέλικτη αρχιτεκτονική, αυτή του pipelining, δηλαδή το App A να μιλάει στο B, το B στο C, το C στο D αλλά το output να παίρνεται από το App D, χωρίς δηλαδή να χρειαστεί οι απαντήσεις να γυρνάνε στις προηγούμενες εφαρμογές τις αλυσίδας. Το πλεονέκτημα είναι ότι τα network hops μειώνονται στα 6 αλλά το μεγάλο μειονέκτημα είναι η δυσκαμψία μιας τέτοιας υλοποίησης. Επίσης, ακόμα και με αυτήν την αρχιτεκτονική επικοινωνίας μέσω των διαφορετικών services, ανάλογα με την ποσότητα των μηνυμάτων που μεταδίδονται μεταξύ τους, ο broker μπορεί να παραμένει να είναι το bottleneck του συστήματος. Σκοπός αυτής της βιβλιοθήκης είναι να παρέχει ένα πιο low-level API πάνω στο οποίο οι developers μπορούν να χτίσουν τις δικές τους λύσεις ανάλογα με το πρόβλημα το οποίο έχουν να αντιμετωπίσουν. Κατά συνέπεια, η χρήση της βιβλιοθήκης αυτής περιορίζεται σε προγραμματιστές που έχουν ήδη αρκετή εμπειρία σε επικοινωνία μέσω δικτύου και μπορούν να γράψουν τον κώδικα που απαιτείται για να συνεργαστούν τα ξεχωριστά κομμάτια της βιβλιοθήκης με τρόπο τέτοιο ώστε να δημιουργείται η απαιτούμενη αρχιτεκτονική. Μερικές από τις ενδεικτικές αρχιτεκτονικές είναι οι εξής: 1. Νο Broker – η απουσία του broker 49 Εικόνα 21 [109] Το remote procedure call, απουσία broker Tα μεγαλύτερα πλεονέκτημα αυτής της απλής αρχιτεκτονικής είναι ότι υπάρχει γρηγορότερη επικοινωνία μεταξύ των services και ότι δεν υπάρχει κάποιο σημείο στο σύστημα στο οποίο περνάει μεγάλος όγκος δεδομένων λόγω του σχεδιασμού του, και έτσι δεν υπάρχει σημείο που μπορεί να γίνει bottleneck λόγω σχεδιασμού. Βέβαια όλες τα services πρέπει να ξέρουν τις διευθύνσεις όλων των υπόλοιπων και το σύστημα αν χρησιμοποιεί μερικά δεκάδες microservices γίνεται πολύ γρήγορα μη διαχειρίσιμο. 2. Broker as a Directory Service, γνωστοποίηση των διευθύνσεων των υπόλοιπων services και απευθείας επικοινωνία Εικόνα 22 [109] Αρχιτεκτονική συστήματος που χρησιμοποιεί Directory Service Σε αυτήν την περίπτωση ο broker αναλαμβάνει μόνο έναν από τους βασικούς ρόλους του. Αντί να ξέρει ποιων ειδών εφαρμογές τρέχουν πού και να αναλαμβάνει την αποστολή μηνυμάτων, πραγματοποιεί μόνο την πρώτη από αυτές τις λειτουργίες. Όταν μία εφαρμογή ξεκινάει την εκτέλεσή της, κάνει γνωστή την παρουσία της στο directory service. Όταν μία εφαρμογή θέλει να επικοινωνήσει με κάποια άλλη, ρωτάει το directory service για τη διεύθυνσή της και στη συνέχεια αναλαμβάνει μόνη της την επικοινωνία αυτή. Στην αρχιτεκτονική αυτή μπορεί να εφαρμοστεί και το publish-subscribe messaging, καθώς οι εφαρμογές με τη σύνδεσή τους στο δίκτυο μπορούν να αναφέρουν στο directory service τα topics για τα οποία ενδιαφέρονται και όταν αυτά αλλάζουν να ενημερώνουν κατάλληλα το directory service. Όταν μία εφαρμογή θέλει να κάνει publish σε ένα topic, 50 ζητάει από το directory service τις διευθύνσεις των εφαρμογών που έχουν κάνει subscribe σε αυτό το topic και στη συνέχεια η αποστολή του μηνύματος αναλαμβάνεται από την εφαρμογή αυτή. Το σύστημα αυτό μπορεί να βελτιωθεί περαιτέρω με τη χρήση caching, δηλαδή την τοπική αποθήκευση των διευθύνσεων των services με τα οποία θέλει να μιλήσει το κάθε service και μόνο η περιοδική ανανέωσή τους από το directory service, καθώς και ανανέωσή τους σε περίπτωση αποστολής μηνύματος που δεν βρει το παραλήπτη του (έγκυρη διεύθυνση που με την πάροδο του χρόνου κατέληξε σε μη έγκυρη λόγω αλλαγής της κατάστασης του συστήματος). Παρά τα πολλά της πλεονεκτήματα, το μεγαλύτερο μειονέκτημα αυτής της μεθόδου αποτελεί η μεγαλύτερη πολυπλοκότητα υλοποίησης σε κάθε διαφορετικό service που συμμετέχει στο δίκτυο αυτό. Δηλαδή, κάθε διαφορετικό service θα πρέπει να υλοποιήσει τη λογική επικοινωνίας με το directory service, τη λογική του caching και της επικοινωνίας με όλα τα υπόλοιπα services, καθώς και ίσως τη διαχείριση αποθήκευσης των μηνυμάτων έως ότου ο παραλήπτης είναι διαθέσιμος. Μία τέτοια υλοποίηση δεν είναι απλή, πόσο μάλλον όταν τα διαφορετικά services του συστήματος μπορεί να είναι υλοποιημένα και με διαφορετική γλώσσα προγραμματισμού, μειώνοντας τις δυνατότητες επαναχρησιμοποίησης του κώδικα. 3. Distributed broker όπου η λογική μεταφοράς και αποθήκευσης μηνυμάτων γίνεται σε διαφορετικά message queues Εικόνα 23 [109] Distributed broker, όπου διαφορετικές εφαρμογές παίζουν τον ρόλο των message queues Αυτή η αρχιτεκτονική μοιάζει με την 2η εναλλακτική που παρουσιάστηκε, με τη διαφορά ότι τώρα η λογική μεταφοράς, αποθήκευσης και caching των διευθύνσεων γίνεται σε διαφορετικές εφαρμογές που παίζουν τον ρόλο των message queues. Το Directory Service χρησιμοποιείται από τις εφαρμογές μόνο για την αναζήτηση των διευθύνσεων των message queues. Τα message queues χρησιμοποιούν το directory service για να ξέρουν σε ποια διεύθυνση πρέπει να προωθήσουν το μήνυμα. Αυτός ο τρόπος λύνει τα προβλήματα της 2ης αρχιτεκτονικής, δηλαδή πλέον η λογική του message queue δεν βρίσκεται μέσα στην εφαρμογή που υλοποιεί τους κανόνες τις επιχείρησης, αλλά μέσα σε διαφορετικές εφαρμογές που η αποκλειστική τους ευθύνη είναι το να παίζουν το ρόλο του message queue. Έτσι, αν η εφαρμογή App A θέλει να στείλει ένα μήνυμα στην App D, θα στείλει το μήνυμα στην εφαρμογή Q3. Αν το App D δεν μπορεί να εξυπηρετήσει το μήνυμα αμέσως, το μήνυμα θα μείνει στο queue της Q3 έως ότου το App D γίνει διαθέσιμο και δεν υπάρχουν άλλα μηνύματα στο queue προς επεξεργασία. Οι message queue εφαρμογές μπορούν να ενθυλακώνουν και πιο πολύπλοκες λειτουργίες, όπως το να αναγνωρίζουν την προτεραιότητα των μηνυμάτων και να αναδιοργανώνουν το queue τους (priority queue). Η βιβλιοθήκη 0MQ υλοποιεί το ZMTP (ZeroMQ Message Transfer Protocol), το οποίο δρα στο επίπεδο μεταφοράς της πληροφορίας και παρέχει λειτουργίες όπως ασφάλεια μεταφοράς των δεδομένων, metadata μεταξύ των συνδέσεων και άλλες παρόμοιες λειτουργίες επιπέδου μεταφοράς (transport layer) [110]. 51 5.1.3 Ανταλλαγή μηνυμάτων με τεχνικές IPC Σε μερικές περιπτώσεις, έχει νόημα τα microservices να τρέχουν μέσα στο ίδιο operating system ή μέσα σε containers που έχουν κοινό host operating system. Αυτό μπορεί να συμβαίνει για πολλούς λόγους, ένας από τους οποίους είναι ότι τα microservices μπορεί να πρέπει να επικοινωνούν μεταξύ τους τακτικά και υπάρχουν αυστηροί περιορισμοί χρόνου για την εκτέλεση των διεργασιών τους. Ένας άλλος λόγος μπορεί να είναι ότι μπορεί να χρειάζονται πολλά αντίγραφα του ίδιου microservice τα οποία επικοινωνούν μεταξύ τους και έχει νόημα από οικονομική και διαχειριστική άποψη αυτά τα microservices να γίνουν deploy στο ίδιο host μηχάνημα. Αξίζει να αναφερθεί ότι σε ένα σύστημα από microservices δεν είναι απαραίτητη προϋπόθεση η χρήση κοινών τρόπων επικοινωνίας μεταξύ των διαφορετικών microservices. Μερικά services έχει νόημα να επικοινωνούν με διαφορετικούς τρόπους από άλλα, ανάλογα με την αρχιτεκτονική του συστήματος και το πρόβλημα που εξετάζεται. Οι τεχνικές που χρησιμοποιούνται όταν τα services βρίσκονται κάτω από το ίδιο μηχάνημα, δεν διαφέρουν από τις τεχνικές που χρησιμοποιούνται για την επικοινωνία μεταξύ προγραμμάτων σε έναν κλασικό υπολογιστή. Η φιλοσοφία του Unix είναι ότι υπάρχουν πολλά διαφορετικά σχετικά μικρά προγράμματα τα οποία πραγματοποιούν συγκεκριμένες και καλώς ορισμένες διεργασίες. Αυτό φυσικά δημιουργεί την ανάγκη αποτελεσματικής επικοινωνίας μεταξύ αυτών των διεργασιών, έτσι ώστε να εξασφαλιστεί η ορθή συνεργασία τους για την λύση προβλημάτων μεγαλύτερων από αυτών που μπορεί να λύσει κάθε από αυτά τα προγράμματα ξεχωριστά. Αυτό το υποκεφάλαιο μιλάει για τις περιπτώσεις όπου έχει νόημα τα microservices να επικοινωνούν μεταξύ τους ενώ μοιράζονται το ίδιο host operating system, και οι τρόποι που μπορεί να γίνει αυτό με τεχνικές IPC (Inter-Process Communication), χωρίς να γίνει χρήση του network stack. 5.1.3.1 Pipes Τα Unix pipes (ή pipelines) είναι ένας τρόπος επικοινωνίας μίας κατεύθυνσης των προγραμμάτων. Πιο συγκεκριμένα, το output (stdout - standard output) της μίας εντολής μπορεί να γίνει input (stdin - standard input) μίας άλλης εντολής. Περισσότερες από δύο εντολές μπορούν να επικοινωνούν με pipes, και τότε δημιουργείται ένα pipe chain. Χρησιμοποιώντας κλασικές εντολές από ένα σύστημα το οποίο χρησιμοποιεί GNU εργαλεία, το pipe chain find . -type f -name ‘*.zip’ | du -h | sort -h πραγματοποιεί τρία διαφορετικά βήματα χρησιμοποιώντας τρεις διαφορετικές εντολές. Αρχικά, η πρώτη εντολή, ψάχνει για όλα τα αρχεία με την επέκταση αρχείου .zip κάτω από το τρέχον φάκελο και τα τυπώνει στο standard output. Το output αυτό γίνεται input της δεύτερης εντολής η οποία υπολογίζει και τυπώνει το μέγεθος αυτών των αρχείων ακολουθούμενο από το όνομά τους, σε φιλική προς τον άνθρωπο μορφή (παράμετρος «-h» από το «human»). Στο τελευταίο βήμα, η τρίτη εντολή διαβάζει τα μεγέθη και τα ταξινομεί από το μικρότερο στο μεγαλύτερο λαμβάνοντας υπόψιν ότι είναι τυπωμένα σε φιλική προς τον άνθρωπο μορφή (παράμετρος «-h» και σε αυτήν την εντολή). Με αυτόν τον τρόπο μπορεί να γίνει χρήση τριών τελείως διαφορετικών εργαλείων για να γίνει συνδυασμός των δυνατοτήτων του κάθε ξεχωριστού εργαλείου και αυτό να οδηγήσει στη λύση ενός πιο δύσκολου προβλήματος. Η λογική των pipes δεν υπάρχει μόνο σε διαφορετικά προγράμματα που τρέχουν κάτω από κοινό host operating system. Για παράδειγμα, πολλές γλώσσες προγραμματισμού ορίζουν την έννοια των streams και ο προγραμματιστής έχει τη δυνατότητα να δουλέψει με pipes μέσα στο ίδιο το πρόγραμμα, όπως στη Node.js [111]. Επίσης, η λογική των pipes παρουσιάζεται και σε επικοινωνία πάνω από το δίκτυο, όπου ένα service μπορεί να επεξεργαστεί δεδομένα πριν τα στείλει στο επόμενο κ.ο.κ. έως ότου το τελευταίο service παρέχει τα δεδομένα ως output (για παράδειγμα, τα αποθηκεύει σε μία βάση δεδομένων, ή τα στέλνει σαν απάντηση σε κάποια αίτηση όπως ένα network request). 52 Εικόνα 24 Το unidirectional data flow του παραδείγματος με τα pipes. Παρατηρήστε τα «ενδιάμεσα αποτελέσματα» που αποτελούν input για την επόμενη εντολή στην αλυσίδα (pipe chain) Το μεγάλο πλεονέκτημα των pipes είναι ότι επιτρέπουν την ασύγχρονη μεταφορά της πληροφορίας. Αυτό σημαίνει ότι, στο προηγούμενο παράδειγμα οι εντολές find, du και sort τρέχουν παράλληλα, όχι σειριακά. Ενώ η εντολή find ψάχνει αρχεία, η εντολή du είναι διαθέσιμη να υπολογίζει το μέγεθός τους και η sort μπορεί να αρχίσει την ταξινόμηση χωρίς να έχει όλα τα δεδομένα. Σε ένα πολυπύρηνο σύστημα υπάρχει μεγάλο πλεονέκτημα από άποψη απόδοσης όταν συγκριθεί με κάθε μία από αυτές τις εντολές να τρέχουν σειριακά. Εν τέλει μόνο το output της τελευταίας εντολής δεν καταναλώνεται από κάποια άλλη εντολή και γίνεται το χρήσιμο output που ενδιαφέρει τον χρήστη. Έτσι φαίνεται και η χρησιμότητα των pipes για την επικοινωνία μεταξύ των microservices. Δύο microservices μπορούν να χρησιμοποιήσουν pipes για να επικοινωνήσουν μεταξύ τους και η μεταφορά των δεδομένων θα γίνεται κατά τη διάρκεια της εκτέλεσής τους. Επίσης, όπως ίσως έγινε ήδη προφανές, μπορούν περισσότερα από ένα microservices να συμμετέχουν σε ένα pipe chain για να επικοινωνούν σαν αλυσίδα. Για να σταλεί ένα μήνυμα από ένα microservice στο επόμενο της αλυσίδας, αρκεί η προηγούμενη εφαρμογή να τυπώσει κάτι στο standard output, όπως για παράδειγμα με τη χρήση της printf στην γλώσσα προγραμματισμού C ή του console.log στην Node.js, και η επόμενη εφαρμογή να το «καταναλώσει», διαβάζοντας από το standard input της, όπως για παράδειγμα με τη χρήση της συνάρτησης read στη C ή με χρήση του readline API στη Nodejs [112]. Όπως γίνεται προφανές, η δημιουργία δύο ή περισσότερων προγραμμάτων που επικοινωνούν μέσω ενός pipe chain είναι πολύ απλή αφού μπορεί να δημιουργηθεί χρησιμοποιώντας απλές συναρτήσεις που υπάρχουν στην standard βιβλιοθήκη κάθε τυπικής γλώσσας προγραμματισμού. Παρόλο που παρουσιάζουν αρκετά πλεονεκτήματα, tα pipes παρουσιάζουν και σημαντικά μειονεκτήματα που έρχονται σε αντίθεση με τους βασικούς «κανόνες αρχιτεκτονικής» των microservices. Πρώτον, τα microservices χάνουν την χαλαρή τους σύνδεση, αφού ένα microservice καταναλώνει το output ενός άλλου microservice. Δεν υπάρχει δυνατότητα ούτε τα services να ξεκινήσουν να τρέχουν σε διαφορετικές χρονικές στιγμές, ούτε να γίνει αποθήκευση των μηνυμάτων σε περίπτωση που ένα service δεν είναι διαθέσιμο για κάποιο χρονικό διάστημα. Επίσης, στη μορφή που περιεγράφηκαν τα pipes, δηλαδή μέσω ανάγνωσης του standard input, τα services θα πρέπει να έχουν κοινό host operating system και υποχρεωτικά θα πρέπει να ξεκινήσουν ταυτόχρονα στο ίδιο pipe chain. Αν κάποιο microservice χρειάζεται να αναβαθμιστεί θα πρέπει να «σπάει» το pipe chain και ουσιαστικά να επανεκκινηθούν όλα τα ανεξάρτητα microservices για να συνεχίσει η ομαλή λειτουργία του συστήματος. Άλλο ένα βασικό μειονέκτημα είναι ότι τα pipes δεν επιτρέπουν την αμφίδρομη επικοινωνία μεταξύ των εντολών, κάτι που εφαρμόζοντας τη λογική της «συγκεκριμένης λειτουργίας» (SRP), είναι χρήσιμο πολύ συχνά. Άλλο ένα χαρακτηριστικό που δείχνει ότι τα pipes δημιουργούν μία ισχυρή σύνδεση μεταξύ των microservices είναι ότι εάν έχουν σχεδιαστεί έτσι ώστε να δουλεύουν μόνο μέσω του standard input τους και είναι περιορισμένα στο να τρέχουν στο ίδιο host operating system, απαιτούνται μεγάλες αλλαγές στην αρχιτεκτονική του κώδικά τους για να μπορούν να αρχίσουν να δέχονται το input τους από το network stack. Αυτό σημαίνει ότι τα microservices, λόγω αυτής της ισχυρής σύνδεσης, απαιτείται να έχουν μία αρχιτεκτονική του κώδικα που δεν είναι αρκετά ευέλικτη σε πιθανή εξέλιξη της αρχιτεκτονικής του συστήματος. Ακόμα, τα pipes 53 δεν επιτρέπουν την παράλειψη nodes πάνω στην αλυσίδα, και γι’ αυτό επιτρέπει την επικοινωνία μόνο μεταξύ 2 nodes σε κάθε βήμα. Για παράδειγμα, το pipe chain A | B | C που αποτελείται από τρία διαφορετικά προγράμματα, επιτρέπει καθαρά και μόνο την μεταφορά πληροφορίας από το Α στο Β και από το Β στο C. Το A δεν έχει τη δυνατότητα επικοινωνίας με το C, παρά μόνο αν το service B προωθήσει τα μηνύματά που δέχεται από το A στο C, αναλλοίωτα. 5.1.3.2 Unix Domain Sockets Τα Unix Domain Sockets (UDS ή IPC sockets) έχουν παρόμοια συμπεριφορά με τα internet sockets με τη διαφορά ότι χρησιμοποιούνται για inter-process communication και όλη η επικοινωνία λαμβάνει χώρα μέσα στο kernel του συστήματος. Σε αντίθεση με τα named pipes, τα unix domain sockets υποστηρίζουν αρκετούς διαφορετικούς τρόπους επικοινωνίας, είτε αμφίδρομους είτε μονόδρομους [113]. Αυτός ο τρόπος επικοινωνίας επιτρέπει στις εφαρμογές να έχουν τις δυνατότητες που προσφέρει ένα internet socket με έναν πολύ πιο lightweight τρόπο, αφού πλέον τα πακέτα δεν χρειάζεται να περάσουν από την κάρτα δικτύου του υπολογιστικού συστήματος και πολλοί έλεγχοι (όπως αυτοί του routing) δεν χρειάζεται να πραγματοποιηθούν, βελτιώνοντας την απόδοση της επικοινωνίας. Για τη χρήση των unix sockets χρησιμοποιούνται αρχεία τύπου unix socket στα οποία οι εφαρμογές μπορούν να λάβουν και να στείλουν δεδομένα διαβάζοντας και γράφοντας σε αυτά, αντίστοιχα. Εικόνα 25 Ο τρόπος επικοινωνίας δύο εφαρμογών με χρήση ενός Unix Domain Socket Οι πιο απλοί τρόποι επικοινωνίας δύο ή περισσότερων προγραμμάτων που μοιράζονται το host operating system είναι είτε μέσω κάποιου κλασικού αρχείου (για τεχνική επικοινωνίας producerconsumer, για παράδειγμα) είτε μέσω του loopback interface (127.0.0.0/8), δηλαδή μέσω ενός internet socket (client-server αρχιτεκτονική). Τα Unix Domain Sockets προσφέρουν πολύ μεγαλύτερη ταχύτητα και από τις δύο αυτές λύσεις και αυξάνουν πολύ το throughput. Ένα από τα μεγαλύτερα πλεονεκτήματα που παρουσιάζει η επικοινωνία μέσω UDS είναι ότι οι εφαρμογές που 54 είναι κατασκευασμένες να επικοινωνούν μέσω αυτών μπορούν πολύ εύκολα να μετατραπούν σε εφαρμογές που επικοινωνούν μέσω του δικτύου. Αυτό συμβαίνει λόγω της κοινής συμπεριφοράς που εμφανίζουν τα UDS με τα internet sockets. Για παράδειγμα, το net module της Node.js υποστηρίζει τόσο Unix Domain Dockets όσο και κλασικά tcp sockets [114] όπως και το socket module της Python [115]. Τα κύρια μειονεκτήματα των unix domain sockets είναι ότι τα επικοινωνούντα services πρέπει να μοιράζονται το ίδιο host operating system και το γεγονός ότι είναι πολύ χαμηλού επιπέδου. Δηλαδή, δεν προσφέρουν ένα υψηλότερο επίπεδο αφαιρετικότητας στην επικοινωνία που είναι χρήσιμο στις περισσότερες εφαρμογές. Ένας προγραμματιστής δεν έχει τη δυνατότητα να χρησιμοποιήσει άμεσα πιο προχωρημένα features όπως αυτό του publish-subscribe ή queuemessaging χωρίς τη χρήση κάποιου broker που να τα παρέχει. 5.1.3.3 Shared Memory (Κοινή μνήμη) Η shared memory, δηλαδή η χρήση κοινού μέρους της μνήμης μεταξύ διαφορετικών εφαρμογών, είναι σίγουρα ο πιο γρήγορος τρόπος πραγματοποίησης του inter-process communication. Τα προγράμματα έχουν τη δυνατότητα να διαβάζουν και να γράφουν σε ένα κοινό κομμάτι της μνήμης του host operating system και έτσι να γίνεται πολύ γρήγορη ανταλλαγή μηνυμάτων που περιορίζεται μόνο από τις ταχύτητες εγγραφής και ανάγνωσης της κυρίας μνήμης του υπολογιστικού συστήματος, δηλαδή πολλών GiB/s. Τα κύρια μειονεκτήματα της χρήσης κοινής μνήμης είναι: 1. Δεν είναι εύκολο να γίνει το σύστημα κατανεμημένο στο μέλλον, αν αυτό χρειαστεί. Ο τρόπος εγγραφής και ανάγνωσης από την κοινή μνήμη δεν έχει κοινά χαρακτηριστικά με τον τρόπο επικοινωνίας δύο services μέσω του δικτύου και γι’ αυτό θα πρέπει να πραγματοποιηθούν πολλές αλλαγές σε επίπεδο κώδικα για να γίνει κάτι τέτοιο εφικτό. 2. Το σύστημα ανταλλαγής μηνυμάτων μέσω της κοινής μνήμης μπορεί να είναι περίπλοκο και συνήθως απαιτεί τη χρήση σεμαφόρων (semaphores) [116]. Οι σεμαφόροι είναι δομές συγχρονισμού μεταξύ των διαφορετικών προγραμμάτων έτσι ώστε να μην δημιουργηθούν προβλήματα συγχρονισμού μεταξύ τους. Για παράδειγμα, αν δύο διαφορετικά προγράμματα θέλουν να προσθέσουν μηνύματα σε ένα priority queue, η πρόσβαση στο queue αυτό πρέπει να γίνει σειριακά. Η δομή της τελικής λύσης που περιλαμβάνει σεμαφόρους, μπορεί να πάρει μορφές που θυμίζουν τις δυνατότητες που δίνουν μηχανισμοί όπως τα Unix Domain Sockets και συνολικά η τελική λύση να μην είναι τόσο πιο γρήγορη έτσι ώστε να δικαιολογείται η χρήση μίας προσαρμοσμένης πολύπλοκης λύσης κοινής μνήμης έναντι μιας δοκιμασμένης όπως τα Unix Domain Sockets. 3. Η χρήση της κοινής μνήμης πρέπει να επιτρέπεται από τη γλώσσα προγραμματισμού που χρησιμοποιεί ο χρήστης, καθώς μερικές γλώσσες, όπως η Node.js, δεν έχουν (ακόμα) μηχανισμούς για τη χρήση της. 4. Ενώ μπορεί να γίνει χρήση της κοινής μνήμης μεταξύ προγραμμάτων που έχουν κατασκευαστεί με διαφορετικές γλώσσες προγραμματισμού, αυτό δεν είναι πάντα εύκολο, διότι πολλές φορές διαφορετικές γλώσσες προγραμματισμού χρησιμοποιούν διαφορετικές υλοποιήσεις μεταξύ των εσωτερικών δομών δεδομένων και μπορούν εύκολα να δημιουργηθούν προβλήματα συμβατότητας. Αυτό περιορίζει ακόμα περισσότερο την ευελιξία του συστήματος από services. 55 5.2 Performance Measurements Πέρα από τα συγκεκριμένα χαρακτηριστικά του καθενός από τους διαφορετικούς τρόπους επικοινωνίας, αξίζει να γίνει μια πιο λεπτομερής σύγκριση της απόδοσής τους, καθώς και μόνο η απόδοση μπορεί να κρίνει ένα πρωτόκολλο κατάλληλο ή ακατάλληλο ανάλογα με τις ανάγκες ενός προβλήματος. Έγιναν μετρήσεις πολλών διαφορετικών πρωτοκόλλων τα οποία προσφέρουν διαφορετικές δυνατότητες στους αρχιτέκτονες ενός συστήματος, έτσι ώστε να μπορεί να γίνει μία σχετική σύγκριση της απόδοσής τους, ανάλογα με τις δυνατότητες που θέλει να έχει στο επίπεδο της επικοινωνίας μεταξύ των node του. Για την πραγματοποίηση των μετρήσεων χρησιμοποιήθηκε ως server ένας σταθμός εργασίας ο οποίος είναι εξοπλισμένος με έναν επεξεργαστή Intel i5-4590 και 8GB από DDR3 κυρία μνήμη. Το λειτουργικό σύστημα του σταθμού εργασίας είναι το Ubuntu 16.10 (64-bit). Ως client χρησιμοποιήθηκε ένα Raspberry Pi 3 Model B, το οποίο είναι εξοπλισμένο με 1GB από DDR3 κυρία μνήμη και με έναν επεξεργαστή Boardcom BCM 2837 64-bit ARMv7 Quad Core στα 1.2GHz. Η έκδοση της Java που χρησιμοποιήθηκε είναι η Java JRE 1.8.0_131 και η έκδοση της Node.js είναι η v6.12.2. Η σύνδεση μεταξύ server και client έγινε μέσω τοπικού δικτύου με χρήση ethernet καλωδίου. Για να πιστοποιηθεί η ακρίβεια των μετρήσεων, έγινε χρήση του Node.js module nodeposix-clock [117]. Το συγκεκριμένο module χρησιμοποιεί C++ bindings έτσι ώστε να μπορεί να γίνει χρήση του POSIX clock [118] μέσα στη Node.js. Έτσι οι παρακάτω μετρήσεις έγιναν με ακρίβεια της τάξης του nanosecond. 5.2.1 Απόδοση της χρήσης function call Ενώ δεν αποτελεί πρωτόκολλο επικοινωνίας μεταξύ nodes, οι μετρήσεις ενός function call μπορούν να βοηθήσουν στην κατανόηση του πόσο μεγάλη καθυστέρηση (σε τάξεις μεγέθους) προστίθεται ακόμα και από τα πιο απλά πρωτόκολλα επικοινωνίας όπως το UDP και το γιατί είναι σημαντικό να υπάρχει μεγάλη ανάγκη η αρχιτεκτονική του συστήματος να είναι κατανεμημένη πριν προχωρήσει η υλοποίησή του με αυτόν τον τρόπο. Οι μετρήσεις έγιναν με χρήση της Node.js μέσα στο Raspberry Pi. Εικόνα 26 Function call - Ο χρόνος απάντησης χιλίων fill request με χρήση της κλήσης μιας συνάρτησης 56 Min (μs) 1 Πίνακας 2: αποτελέσματα του function call performance test Max (μs) Average (μs) Stdev (μs) 158 4.312 8.032 5.2.2 Απόδοση της χρήσης UDP Σε αυτή τη μέτρηση τόσο ο server όσο και ο client εκτελούσαν Node.js υλοποιήσεις ενός UDP server, χρησιμοποιώντας το module dgram [119] της βασικής βιβλιοθήκης του Node.js. Ο client κάνει request στον server να κάνει fill, και ο server απαντάει στον client ότι το fill ξεκίνησε. Εικόνα 27 UDP - Ο χρόνος απάντησης χιλίων fill request με χρήση ενός Node.js server και Node.js client Πίνακας 3: αποτελέσματα του UDP performance test Min (ms) Max (ms) Average (ms) Stdev (ms) 0.12 5.412 0.422 0.24 Αρχίζοντας με ένα από τα πιο βασικά πρωτόκολλα επικοινωνίας όπως το UDP, βλέπουμε ότι η απόδοσή του είναι 100 φορές χειρότερη κατά μέσο όρο σε σχέση με ένα απλό function call μέσα στο ίδιο πρόγραμμα. 5.2.3 Απόδοση της χρήσης Unix Domain Sockets Ως UDS χρησιμοποιήθηκε ένα προσωρινό αρχείο τύπου unix socket μέσα στο σύστημα αρχείων του Raspberry Pi. Το interfacing μέσω του socket αρχείου έγινε με χρήση του net module [114] της Node.js. Η χρήση ενός Unix Domain Socket απαιτεί πρόσβαση στο τοπικό σύστημα αρχείων, οπότε είναι περιορισμένη και μπορεί να γίνει μόνο αν οι επικοινωνούντες εφαρμογές βρίσκονται κάτω από το ίδιο host operating system. Παρ’ όλα αυτά, προσφέρει πολύ μεγάλες αποδόσεις: 57 Εικόνα 28 UDS - Ο χρόνος απάντησης χιλίων fill request με χρήση ενός Node.js server και Node.js client Πίνακας 4: αποτελέσματα του UDS performance test Max (ms) Average (ms) Stdev (ms) Min (ms) 0.026 4.884 0.089 0.24 Ο μόνος λόγος που το άνω διάγραμμα είναι σε milliseconds, είναι λόγω των πολύ λίγων ακραίων τιμών. Κάνοντάς τες ίσες με την μέση τιμή των 1000 requests δίνει μία πιο πλήρη εικόνα του τι συμβαίνει σε επίπεδο microsecond: UDS performance test 700 600 time in μs 500 400 300 200 100 0 0 200 400 600 800 1000 1200 request Εικόνα 29 UDS - Ο χρόνος απάντησης χιλίων fill request απομακρύνοντας τις λίγες ακραίες τιμές 5.2.4 Απόδοση της χρήσης LwM2M Μέχρι τώρα έχει γίνει αναφορά σε μετρήσεις μόνο βασικών τρόπων επικοινωνίας όπως function calls και επικοινωνία μέσω sockets. Το LwM2M προσφέρει πολλές επιπλέον λειτουργίες σε σχέση με τους προηγούμενους τρόπους και για τον λόγο αυτό προσθέτει και μεγαλύτερη καθυστέρηση στην επικοινωνία. Σε αυτή τη μέτρηση ο σταθμός εργασίας έπαιξε το ρόλο του LwM2M 58 server και το raspberry pi έπαιξε το ρόλο του LwM2M client. Ως LwM2M object χρησιμοποιήθηκε το σιλό, η περιγραφή του οποίου αναφέρθηκε στο αντίστοιχο κεφάλαιο. Για την πραγματοποίηση της μέτρησης αυτής, ο LwM2M client ζητάει από τον server να κάνει «fill» σε ένα object και ο server απαντάει ότι έλαβε το request και ότι έχει αρχίσει η πραγματοποίησή του. Ο LwM2M client δημιουργήθηκε με χρήση της Node.js και χρησιμοποιήθηκαν τα εξής node modules: το coap-node [120], το οποίο χρησιμοποιείται για την υλοποίηση ενός LwM2M client και το smartobject [121] το οποίο χρησιμοποιείται για την περιγραφή ενός έξυπνου αντικειμένου και δουλεύει συνεργατικά με το coap-node. Όσον αφορά τον LwM2M server, δύο διαφορετικές υλοποιήσεις χρησιμοποιήθηκαν. Η 1η αφορά την υλοποίηση ενός LwM2M server μέσω του Leshan [33] σε Java, και η 2η αφορά την υλοποίησή του με Node.js, κάνοντας χρήση του Node.js module coap-shepherd [122]. LWM2M performance test - Java Server 120 100 time in ms 80 60 40 20 0 0 200 400 600 800 1000 1200 requests Εικόνα 30 LwM2M - Ο χρόνος απάντησης χιλίων fill request με χρήση ενός Java server και Node.js client Τα χαρακτηριστικά της άνω μέτρησης συνοψίζονται στον παρακάτω πίνακα. Min (ms) 1.875 Πίνακας 5: αποτελέσματα του LwM2M performance test με Java server Max (ms) Average (ms) Stdev (ms) 49.144 3.336 2.808 59 LWM2M performance test - Node.js Server 120 100 time in ms 80 60 40 20 0 0 200 400 600 800 1000 1200 requests Εικόνα 31 LwM2M - Ο χρόνος απάντησης χιλίων fill request με χρήση ενός Node.js server και Node.js client Τα χαρακτηριστικά της άνω μέτρησης συνοψίζονται στον παρακάτω πίνακα: Min (ms) 2.259 Πίνακας 6: αποτελέσματα του LwM2M performance test με Node.js server Max (ms) Average (ms) Stdev (ms) 104.403 5.360 7.071 Τόσο με τη χρήση ενός Java server όσο και με τη χρήση ενός Node.js server, παρατηρείται ότι περίπου ένα 0.5% των αιτημάτων για fill καθυστέρησε πολλαπλάσιο χρόνο από το μέσο όρο, και στις δύο περιπτώσεις περίπου 20 φορές περισσότερο από το μέσο όρο. Παρ’ όλα αυτά, στην 2η περίπτωση όπου ο LwM2M server είναι γραμμένος σε Node.js, παρατηρείται ότι παρουσιάζονται και πολλά δευτερεύοντα spikes στον χρόνο που απαιτείται, αυξάνοντας το μέσο όρο αλλά και την τυπική απόκλιση. 5.2.5 Απόδοση της χρήσης AMQP Οι μετρήσεις για το πρωτόκολλο AMQP έγιναν με χρήση της RabbitMQ υλοποίησής του. Στη διάταξη αυτή, ο RabbitMQ server εκτελείται στο σταθμό εργασίας, και οι δύο clients, ένας στον σταθμό εργασίας και ένας στο Raspberry Pi, επικοινωνούν μεταξύ τους. Ο AMQP client που εκτελείται στο Raspberry Pi κάνει ένα αίτημα «fill» στον AMQP client που εκτελείται στο σταθμό εργασίας. Ο client του σταθμού εργασίας απαντάει ότι έλαβε το αίτημα. Ο χρόνος που μεσολαβεί από το αίτημα του client στο Raspberry Pi έως ότου ληφθεί η επιβεβαίωση από τον client του σταθμού εργασίας, παρουσιάζεται στο παρακάτω διάγραμμα, κάνοντας ένα πείραμα 1000 αιτημάτων: 60 AMQP (RabbitMQ) performance test 80 70 time in ms 60 50 40 30 20 10 0 0 200 400 600 800 1000 1200 request Εικόνα 32 AMQP – ο χρόνος απόκρισης χιλίων fill request Πίνακας 7: αποτελέσματα του AMQP performance test Min (ms) Max (ms) Average (ms) Stdev (ms) 0.707 66.988 1.571 2.576 Για την υλοποίηση των AMQP clients χρησιμοποιήθηκε η Node.js και το module amqplib [123]. Η υλοποίηση χρησιμοποιεί ένα απλό message queue από το οποίο κάποιος μπορεί να διαβάσει ή να γράψει. Ο ένας client γράφει το έτοιμά του για fill στο queue ενώ ο άλλος client γράφει στο queue την επαλήθευσή του, ότι δηλαδή έλαβε το μήνυμα. Επίσης, για βελτίωση της απόδοσης έγινε χρήση του noAck, δηλαδή «no acknowledgement» όπου ο broker δεν περιμένει acknowledgement από τους clients ότι έχουν λάβει με επιτυχία τα μηνύματα τα οποία προορίζονται γι’ αυτούς. 5.2.6 Απόδοση της χρήσης ZeroMQ Οι μετρήσεις για το ZeroMQ έγιναν με χρήση Node.js και του zeromq module [124]. Το ZeroMQ, προσπαθώντας να βρει την ισορροπία μεταξύ επίδοσης και δυνατοτήτων, δίνει low-level εργαλεία στους προγραμματιστές έτσι ώστε οι ίδιοι να είναι οι αρχιτέκτονες του τρόπου με τον οποίο δουλεύει η επικοινωνία μεταξύ των services του συστήματός τους. 61 ZeroMQ performance test 12 time in ms 10 8 6 4 2 0 0 200 400 600 800 1000 1200 request Εικόνα 33 ZeroMQ – ο χρόνος απόκρισης χιλίων fill request Πίνακας 8: αποτελέσματα του ZeroMQ performance test Min (ms) Max (ms) Average (ms) Stdev (ms) 0.462 9.707 0.649 0.46 Οι μετρήσεις αυτές έγιναν με χρήση ZeroMQ sockets. Όπως περιμέναμε, με χρήση του ZeroMQ, βλέπουμε αρκετά χαμηλότερη μέση τιμή σε σχέση με το AMQP. Εξίσου σημαντικό είναι το γεγονός ότι η τυπική απόκλιση παίρνει πολύ μικρές τιμές, εξασφαλίζοντας πιο προβλέψιμη επίδοση της επικοινωνίας μεταξύ των services. 62 Κεφάλαιο 6 6. Υλοποίηση microservices του Smart Silo με Στα προηγούμενα κεφάλαια έγινε έρευνα για τις τεχνολογίες που είναι διαθέσιμες για την αποτελεσματική ενθυλάκωση και απομόνωση των εφαρμογών από το host operating system καθώς και για τα πρωτόκολλα επικοινωνίας που μπορούν να χρησιμοποιηθούν για την επικοινωνία μεταξύ δύο ή περισσότερων services. Σε αυτό το κεφάλαιο γίνεται μία υλοποίηση ενός smart silo του liqueur plant case study [29] κάνοντας μελέτη των τρόπων τμηματοποίησης του συστήματος με χρήση των docker containers και δοκιμάζοντας διαφορετικούς τρόπους επικοινωνίας μεταξύ αυτών. Στο τέλος, γίνεται αναφορά στους επιλεγμένους τρόπους επικοινωνίας καθώς και στα εργαλεία που επιλέχθηκαν να χρησιμοποιηθούν και στον τρόπο υλοποίησης όλου του συστήματος κάνοντας χρήση microservices. 6.1 Τμηματοποίηση του συστήματος Εκτενής αναφορά για τις λειτουργίες ενός σιλό, όπως αυτό παρουσιάζεται στο liqueur plant case study [29], έγινε στο κεφάλαιο 3. Από τη μελέτη των σιλό αυτών και επιλέγοντας την κατασκευή αυτού με την πιο περίπλοκη δομή (ένα σιλό «i» το οποίο μπορεί και δουλεύει ως μία οντότητα ανεξάρτητη από το σύστημα μέσα στο οποίο θα χρησιμοποιηθεί), μπορούν να ξεχωρίσουν τα εξής διακριτά μέρη του, τα οποία πραγματοποιούν διαφορετικές εργασίες, συνεργαζόμενα αρμονικά για την παραγωγή liqueur: 1. Μία βαλβίδα INi (in valve) η οποία μπορεί να ελέγχει τη ροή του υγρού για την είσοδό του στο σιλό. 2. Μία βαλβίδα OUTi (out valve) η οποία μπορεί να ελέγχει τη ροή του για την έξοδό του από το σιλό. 3. Ένας αισθητήρας Ei που μπορεί να ανιχνεύσει ότι το υγρό μέσα στο σιλό έχει φτάσει ένα χαμηλό επίπεδο στάθμης. 4. Ένας αισθητήρας Fi που μπορεί να ανιχνεύσει ότι το υγρό μέσα στο σιλό έχει φτάσει ένα υψηλό επίπεδο στάθμης. 5. Μία ηλεκτρική αντίσταση Ri που μπορεί με χρήση ηλεκτρικού ρεύματος να θερμανθεί και να μεταφέρει τη θερμότητά της στο υγρό. 6. Ένας αισθητήρας Ti που μπορεί να χρησιμοποιηθεί για να ανιχνευθεί η θερμοκρασία του υγρού μέσα στο σιλό κάθε χρονική στιγμή. 7. Ένας αναδευτήρας Mi που μπορεί να χρησιμοποιηθεί για την ανάδευση του υγρού για συγκεκριμένο χρονικό διάστημα. Τα διακριτά αυτά μέρη του σιλό έχουν ήδη πολύ διακριτές και συγκεκριμένες εργασίες να φέρουν εις πέρας. Οπότε η τμηματοποίηση του συστήματος του σιλό σε microservices θα γίνει με βάση αυτά τα διακριτά μέρη. Παρ’ όλα αυτά, παρατηρείται ότι μερικά από αυτά τα μέρη του συστήματος πραγματοποιούν συμμετρικές ή συμπληρωματικές διεργασίες, όπως για παράδειγμα ο 63 αισθητήρας Ei και o αισθητήρας Fi ή ακόμα και η ηλεκτρική αντίσταση Ri και ο αισθητήρας θερμοκρασίας Ti. Για όλους αυτούς του λόγους, η τμηματοποίηση του συστήματος επιλέχθηκε να γίνει στα εξής διακριτά μέρη τα οποία αποτελούν και τα microservices του συστήματος: 1. Το Heater service. Προσφέρει το heating service, δηλαδή δίνει τη δυνατότητα της θέρμανσης του υγρού μέχρι μία συγκεκριμένη θερμοκρασία. Αυτό σημαίνει ότι αυτό το μέρος του συστήματος κάνει χρήση τόσο της ηλεκτρικής αντίστασης Ri όσο και του αισθητήρα θερμοκρασίας (θερμόμετρο) Ti. Τα αιτήματα για την παροχή του service πρέπει να παρέχουν πληροφορία για την επιθυμητή θερμοκρασία στην οποία πρέπει να φτάσει το υγρό. 2. Το Mixer service. Προσφέρει το mixing service, δηλαδή δίνει τη δυνατότητα της ανάδευσης του υγρού για συγκεκριμένο χρόνο. Αυτό το μέρος του συστήματος κάνει χρήση μόνο του αναδευτήρα Mi. Τα αιτήματα για την παροχή του service πρέπει να δίνουν την πληροφορία για τον απαιτούμενο χρόνο ανάδευσης. Μόλις ο χρόνος ανάδευσης περάσει, το service αυτό δηλώνει την ολοκλήρωσή του. Αξίζει να σημειωθεί ότι το service αυτό δεν έχει καμία πληροφορία για το αν ήδη χρησιμοποιείται ο αναδευτήρας από κάποιο άλλο σιλό, καθώς ένας από τους ενεργειακούς περιορισμούς είναι το να μην χρησιμοποιείται ο αναδευτήρας ταυτόχρονα στις δύο διαδικασίες παραγωγής liqueur. Δηλαδή το Mixer service, αν λάβει εντολή για ανάδευση, ενώ ήδη κάποιο άλλο σιλό χρησιμοποιεί το δικό του αναδευτήρα, θα αρχίσει την ανάδευση αγνοώντας το και προκαλώντας προβλήματα καθώς δεν υπάρχει αρκετή ενέργεια για την τροφοδότηση και των δύο αναδευτήρων ταυτόχρονα. 3. Το Silo-Driver service. Προσφέρει το service ανίχνευσης της στάθμης του υγρού μέσα στο σιλό. Κάνει χρήση τόσο του αισθητήρα Fi όσο του αισθητήρα Ei που είναι υπεύθυνοι για την ανίχνευση της υψηλής και της χαμηλής στάθμης του υγρού αντίστοιχα. Μόλις η κατάσταση του αισθητήρα Fi ανιχνεύσει υψηλό επίπεδο στάθμης, το Silo-Driver service στέλνει το κατάλληλο σήμα, όπως και αντίστοιχα γίνεται και με τον αισθητήρα Ei και το χαμηλό επίπεδο στάθμης. Το service αυτό δεν παρέχεται on-demand. Δηλαδή, δεν μπορεί κάποιος να ζητήσει από το Silo-Driver service την ανάγνωση των τιμών των αισθητήρων αυτών. Αντίθετα, μόνο του το service αυτό παρέχει στους ενδιαφερόμενους τις τιμές των αισθητήρων αυτών, καθώς αλλάζει η κατάστασή τους (παρόμοια με το publish-subscribe messaging model). 4. Το In-Valve service. Προσφέρει το service ροής υγρού προς το εσωτερικό του σιλό. Αυτό το μέρος του συστήματος κάνει χρήση της βαλβίδας INi που είναι υπεύθυνη για την ροή του υγρού προς το εσωτερικό του σιλό. Τα αιτήματα για την παροχή του service, δηλαδή για την είσοδο υγρού στο σιλό, δεν χρειάζεται να παρέχουν καμία επιπρόσθετη πληροφορία, καθώς το service αυτό σταματάει αυτόματα μέσω έμμεσης χρήσης του SiloDriver service. Δηλαδή, όταν το υγρό φτάσει στην απαιτούμενη στάθμη, το Silo-Driver service θα το γνωστοποιήσει στα άλλα ενδιαφερόμενα services το In-Valve service θα φτάσει στην ολοκλήρωσή του. 5. Το Out-Valve service. Είναι το συμμετρικό service του In-Valve service. Δηλαδή, προσφέρει το service ροής υγρού προς το εξωτερικό του σιλό. Αυτό το μέρος του συστήματος κάνει χρήση της βαλβίδας OUTi που είναι υπεύθυνη για την ροή του υγρού προς το εξωτερικό του σιλό. Τα αιτήματα για την παροχή του service, δηλαδή για την έξοδο του υγρού από το σιλό, δεν χρειάζεται να περιλαμβάνουν κάποια επιπρόσθετη πληροφορία, καθώς το service αυτό σταματάει αυτόματα μέσω της έμμεσης χρήσης του Silo-Driver service. Μόλις το Silo-Driver service, γνωστοποιήσει στο Out-Valve service ότι το υγρό έχει φτάσει το χαμηλό επίπεδο στάθμης, το service αυτό θα φτάσει στην ολοκλήρωσή του. 6. Το Silo-Controller service. Το service αυτό είναι υπεύθυνο για τη μετατροπή του σιλό σε έξυπνο αντικείμενο. Δηλαδή, με το που όλα τα υπόλοιπα services είναι διαθέσιμα, το SiloController service είναι υπεύθυνο για την αρχικοποίηση του Silo smart-object και την 64 εγγραφή του στον αντίστοιχο LwM2M server. Επίσης, το Silo-Controller service λειτουργεί ως κανονικός LwM2M client, δηλαδή είναι υπεύθυνο για την ικανοποίηση των αιτημάτων του LwM2M server στον οποίο έχει κάνει εγγραφή. Μόλις το Silo-Controller service πάρει αίτημα από τον LwM2M server να παρέχει κάποια λειτουργία του smart silo, είναι υπεύθυνο έτσι ώστε τα υπόλοιπα 5 services του συστήματος να συνεργαστούν αρμονικά για την πραγματοποίηση του αιτήματος αυτού. Με άλλα λόγια το Silo-Controller service είναι το interface όλου του συστήματος του σιλό προς τον «έξω κόσμο». Στην υλοποίηση του συστήματος με αυτόν τον τρόπο, δεν μπορεί κάποιο εξωτερικό service να κάνει άμεσα αίτημα στο Heater service για παράδειγμα. Όλα τα αιτήματα θα πρέπει να περνάνε μέσα από το Silo-Controller service και να είναι συμβατά με το silo smart object interface. Αξίζει να αναφερθεί ότι η αρχιτεκτονική του κώδικα είναι αυτή του «monorepo». Ενώ το κάθε microservice ουσιαστικά έχει το δικό του κώδικα, ορίζει ένα διαφορετικό εκτελέσιμο και θα ενθυλακωθεί σε δικό του container, ο κώδικας μεταξύ των microservices διαμοιράζεται. Διαφορετικές αρχιτεκτονικές οργάνωσης του κώδικα του συστήματος θα ήταν: 1. Οργάνωση των διαφορετικών services και του κοινού κώδικα σε διαφορετικά git submodules. Κάθε microservice θα είχε το δικό του git repository όπως και ο κοινός κώδικας. Χρήση του κοινού κώδικα από κάθε microservice git repository θα γινόταν με τη χρήση των git submodules [125]. Επίσης το κάθε microservice git repository θα περιείχε το δικό του Dockerfile με περιγραφή του πώς πρέπει να χτιστεί το αντίστοιχο docker image. Η κατασκευή της αυτοματοποίησης της δημιουργίας και εκτέλεσης του συστήματος, η οποία πρέπει να έχει γνώση κατασκευής των επιμέρους τμημάτων του συστήματος, θα πρέπει να γίνει και αυτή σε ξεχωριστό git repository το οποίο θα δηλώνει ως submodules όλα τα υπόλοιπα modules του συστήματος. Φυσικά κάτι τέτοιο θα έχει ως αποτέλεσμα και την παρουσία του κοινού κώδικα 6 φορές, μία για το git repository του κάθε service. 2. Οργάνωση των διαφορετικών services και του κοινού κώδικα με χρήση node modules. Ως node module μπορεί να δηλωθεί ακόμα και ένα git repository, αρκεί να ακολουθεί τη σωστή δομή, δηλαδή να ορίζει ένα φάκελο αρχείων «dist» ο οποίος περιλαμβάνει τον κώδικα που θέλουμε να γίνει διαθέσιμο στους χρήστες του node module (ουσιαστικά, αν υπάρχουν build-artifacts, πρέπει να συμπεριληφθούν και αυτά στο repository). Κάθε service-module θα όριζε το module που ενθυλακώνει τον κοινό κώδικα ως εξάρτηση. Η κατασκευή της αυτοματοποίησης της δημιουργίας και εκτέλεσης του συστήματος μπορεί να γίνει σε ένα Node.js project το οποίο δηλώνει τα διαφορετικά services-modules ως εξαρτήσεις. Σε αυτήν την περίπτωση ο κοινός κώδικας δεν εμφανίζεται πολλαπλές φορές, καθώς όλα τα service-modules έχουν εξάρτηση στην ίδια έκδοσή του. Και οι δύο αυτοί τρόποι δίνουν ξεχωριστές δυνατότητες από άποψη απόζευξης των services μεταξύ τους. Αν τα services δεν έχουν κοινό repository δίνεται εύκολα η δυνατότητα της επιλογής διαφορετικής γλώσσας προγραμματισμού μεταξύ τους, ο ορισμός διαφορετικού built process καθώς και η εγγραφή διαφορετικών unit tests. Έτσι το κάθε service είναι πραγματικά διαχωρισμένο από το υπόλοιπο σύστημα. Παρ’ όλα αυτά τα πλεονεκτήματα, για την κατασκευή του σιλό επιλέχθηκε η λογική του «monorepo», δηλαδή όλος ο κώδικας στο ίδιο git repository, για λόγους απλότητας, καθώς ούτε διαφορετικές ομάδες υπάρχουν που να δουλεύουν στο ίδιο σύστημα ούτε ο κώδικας είναι μεγάλης έκτασης αλλά και ούτε και χρησιμοποιήθηκαν διαφορετικές γλώσσες προγραμματισμού. Επίσης υπάρχουν εργαλεία όπου πολύπλοκα συστήματα γραμμένα ακόμα και σε διαφορετικές γλώσσες προγραμματισμού μπορούν να συνυπάρχουν αρμονικά στο ίδιο repository. 65 6.2 Χρήση Node.js modules για επικοινωνία Η κατασκευή καθενός από τα microservices έγινε με χρήση της Node.js. Για την επικοινωνία μεταξύ των microservices έγινε έρευνα πάνω σε Node.js modules που είναι ειδικά σχεδιασμένα για τέτοιου είδους επικοινωνία. 6.2.1 Έρευνα για το Seneca module Ένα από τα πιο δημοφιλή modules για την επικοινωνία μεταξύ microservices στη Node.js είναι το Seneca [126]. Το Seneca είναι μία «εργαλειοθήκη» για microservices για τη Node.js. Παρέχει πρόσθετα που αναλαμβάνουν αυτά να διεκπεραιώσουν την κατάλληλη επικοινωνία μεταξύ των διαφορετικών τμημάτων του συστήματος. Έτσι ο προγραμματιστής μένει ελεύθερος στο να εστιάσει στο πιο σημαντικό κομμάτι του κώδικα που αποτελεί τους κανόνες της επιχείρησης και όχι στο πώς πρέπει να γίνει η επικοινωνία του συστήματος. Τα κύρια χαρακτηριστικά του Seneca module είναι τα εξής: • Ανεξαρτησία από το transport level. Αυτό πρακτικά σημαίνει ότι το Seneca μπορεί να λειτουργήσει με πολλά διαφορετικά πρωτόκολλα επικοινωνίας και να αλλάξει από το ένα στο άλλο χωρίς να αλλάξει καθόλου η λογική του κώδικα στα services που τη χρησιμοποιούν. Με αλλαγή του περιβάλλοντος, των συνθηκών ή των προδιαγραφών κάτω από τα οποία τρέχουν τα microservices μπορεί να γίνει εύκολή αλλαγή και σε κάποιο διαφορετικό πρωτόκολλο επικοινωνίας χωρίς αυτό να δημιουργήσει επιπρόσθετα προβλήματα. • Χρήση πολλών πρωτόκολλων ταυτόχρονα. Δεν χρειάζεται όλα τα διαφορετικά μέρη του το κατανεμημένου συστήματος να επικοινωνούν μεταξύ τους με χρήση του ίδιου πρωτόκολλου. Το Seneca επιτρέπει την ταυτόχρονη χρήση πολλών διαφορετικών πρωτόκολλων επικοινωνίας. Έτσι τα services μπορούν να εκτελούνται στο ίδιο host operating system και να χρησιμοποιούν κάποιο πρωτόκολλο επικοινωνίας βασισμένο σε Unix Domain Sockets και να επικοινωνούν με άλλα services κάνοντας χρήση του HTTP πρωτόκολλου. • Αντί για την έννοια των topics η οποία συνηθίζεται να συναντάται σε publish-subscribe messaging, εισάγει την έννοια των pins. Τα pins λειτουργούν ως φίλτρα των μηνυμάτων που στέλνονται και λαμβάνονται από κάθε microservice και έχουν παρόμοια λειτουργία με αυτή των topics, αλλά σε εξελιγμένη μορφή. Δηλαδή επιπρόσθετα εισάγουν και την έννοια του pattern-matching σε επίπεδο JSONencoded string / Javascript object. • Τα μηνύματα που στέλνονται και παραδίδονται έχουν JSON-encoded string μορφή. • Η χρήση της μπορεί να επεκταθεί με την χρήση πρόσθετων modules (plugins) [127] τα οποία μπορούν να χρησιμοποιηθούν για συνήθη προβλήματα που αντιμετωπίζουν οι προγραμματιστές που ασχολούνται με microservices, όπως αυθεντικοποίηση χρηστών, κατασκευή RESTful API, caching mechanisms, payment gateway integrations, database integrations κ.ά. • Είναι ένα ώριμο module το οποίο έχει δοκιμαστεί σε production περιβάλλοντα και χρησιμοποιείται από μεγάλες εταιρείες, όπως η Intel. 66 6.2.2 Έρευνα για το node-ipc module Παρόλο που οι δυνατότητες που δίνει στον προγραμματιστή το Seneca module, είναι παραπάνω απ’ ότι χρειάζεται το απλό σύστημα προς κατασκευή καθώς διαθέτει πολύ περισσότερες δυνατότητες από τις απαιτούμενες. Στη συνέχεια έγινε έρευνα για modules που από τη μία προσφέρουν δυνατότητα για εύκολη χρήση διαφορετικών επιλογών, όπως διαφορετικά πρωτόκολλα επικοινωνίας, αλλά χωρίς την περιπλοκότητα του pattern matching. Ιδιαίτερη προσοχή τράβηξε το node-ipc Node.js module [128], το οποίο παίρνει το όνομά του από τη κοινή χρήση των λέξεων «Node.js» και «Inter-Process Communication». Τα κύρια χαρακτηριστικά του είναι: 1. Υποστήριξη για sockets σε επίπεδο πυρήνα, δηλαδή Unix Domain Sockets σε συστήματα που χρησιμοποιούν Linux πυρήνα, και Windows Sockets σε συστήματα που χρησιμοποιούν το Windows operating system. 2. Υποστήριξη χαμηλού επιπέδου sockets τύπου TCP και UDP, δίνοντας τόσο την επιλογή χρήσης του πιο απλού πρωτόκολλου επικοινωνίας UDP για επικοινωνία στο δίκτυο αλλά και του πιο περίπλοκου TCP για μεγαλύτερη ασφάλεια των μηνυμάτων. 3. Υποστήριξη TLS sockets, τα οποία κάνουν χρήση κρυπτογραφίας, για ασφάλεια των δεδομένων κατά την μετάδοση. 4. Κοινό και απλό API για κάθε είδους socket το οποίο ενθυλακώνει τις περιπλοκότητες που περιλαμβάνουν από το σχεδιασμό τους και δίνει στον προγραμματιστή ευκολία στη χρήση. Ουσιαστικά αυτό σημαίνει ότι το node-ipc μπορεί να προσφέρει απλότητα στη χρήση και την ευελιξία διαφορετικών πρωτόκολλων επικοινωνίας αν αυτό χρειαστεί, πλεονεκτήματα που θεωρήθηκαν πολύ βασικά και για τη χρήση του Seneca. Εν τέλει το node-ipc module επιλέχθηκε για την ανάπτυξη του συστήματος του σιλό καθώς κρίθηκε ότι είναι πιο απλό στη χρήση και παραπάνω από αρκετό για τις ανάγκες του συστήματος. 6.3 Ενθυλάκωση του συστήματος σε containers Για κάθε μέρος του συστήματος έγινε χρήση και ενός διαφορετικού docker container. Τα αντίστοιχα container images δημιουργήθηκαν κάνοντας χρήση των επίσημων docker images της Node.js [129], χρησιμοποιώντας την κατάλληλη έκδοση όπως απαιτεί το σύστημα. Για την κατασκευή των images αυτών έγινε χρήση Dockerfile configuration αρχείων. Το κάθε Dockerfile ορίζει με μία συγκεκριμένη σύνταξη σε ποιο docker image θα στηρίζεται (απόγονος) το docker image που θα κτιστεί με αυτό το Dockerfile, ποιες εντολές πρέπει να τρέξουν στη συνέχεια για να διαμορφωθεί το image κατάλληλα, καθώς και ποια εντολή πρέπει να εκτελεστεί κατά το ξεκίνημα του container και που θα ορίζει το lifecycle του. Όταν αυτή η εντολή τερματιστεί, ο container σταματάει και αυτός την εκτέλεσή του. Το χτίσιμο του κάθε image μπορεί να γίνει με χρήση του command line εργαλείου «docker» [130], όπως και το ξεκίνημα του κάθε αντίστοιχου container. Συνοπτικά, τα βήματα που ακολουθήθηκαν για το χτίσιμο του κάθε docker image είναι τα εξής: 1. Χρήση του Node.js docker image ως «βάση» για το χτίσιμο κάθε νέου image. Το image αυτό περιέχει το runtime της Node.js προ-εγκατεστημένο καθώς και περιλαμβάνει και τους πιο γνωστούς package managers της Node.js, όπως το npm [131] και το yarn [132]. Επίσης, περιλαμβάνει συνηθισμένα Linux εργαλεία και προσφέρει το τερματικό της bash. Από τη μία, αυτό αυξάνει το μέγεθος του docker image, αλλά από την άλλη βοηθάει στο λύσιμο προβλημάτων, καθώς κάποιος μπορεί να έχει πρόσβαση στο εσωτερικό του container και να χρησιμοποιήσει τις διαθέσιμες εντολές. 67 2. Αντιγραφή των αρχείων της εφαρμογής μέσα στο docker image. Κάθε container του κάθε microservice περιλαμβάνει μέσα του όλα τα αρχεία του «monorepo», χωρίς να χρησιμοποιούνται όλα κατά τη διάρκεια της εκτέλεσης του καθενός από αυτά. Αυτό συμβαίνει για λόγους απλότητας. 3. Εγκατάσταση των εξαρτήσεων. Το Node.js project του κάθε microservice, επειδή ανήκει σε «monorepo», έχει και κοινές εξαρτήσεις με τα υπόλοιπα. Αυτό έχει ως αποτέλεσμα την εγκατάσταση εξαρτήσεων, όπως για παράδειγμα του coap-node module, σε όλα τα συστήματα. Στην πραγματικότητα, αυτή η εξάρτηση χρειάζεται μόνο στο Silo Controller service, ο οποίος είναι υπεύθυνος για την εγγραφή του σιλό LwM2M object στον LwM2M server. Το μόνο πρόβλημα που προκαλεί αυτό είναι μικρή καθυστέρηση κατά τη διάρκεια χτισίματος του image καθώς και μικρή αύξηση του μεγέθους του. Ενώ όλα τα συστήματα έχουν κοινές εξαρτήσεις και χρησιμοποιούν την ίδια βάση ως docker image, δεν μοιράζονται τις εξαρτήσεις τους. Χρήση ενός docker volume μπορεί να γίνει έτσι ώστε να γίνει η εγκατάσταση των εξαρτήσεων μία μόνο φορά, να επιταχυνθεί η διαδικασία χτισίματος των images και να μειωθεί το μέγεθός τους, αλλά αυτό δημιουργεί επιπλέον πολυπλοκότητα και πιο σφιχτή σύνδεση μεταξύ των containers και γι’ αυτό δεν επιλέχθηκε ως λύση. 4. Εκτέλεση με χρήση του Node.js runtime του αντίστοιχου microservice. Τα microservices προσπαθούν να συνδεθούν μεταξύ τους και να αρχίζουν να ανταλλάσσουν πληροφορίες. Τα εκτελέσιμα διατηρούν τη σύνδεσή τους και προσπαθούν ανά τακτά χρονικά διαστήματα να πραγματοποιήσουν τις κατάλληλες συνδέσεις αν δεν έχουν ξεκινήσει ταυτόχρονα. Με αυτόν τον τρόπο, η εκτέλεσή τους δεν σταματάει χωρίς χειροκίνητη εντολή από το χρήστη και έτσι τα αντίστοιχα docker containers συνεχίζουν την εκτέλεσή τους. Παρ’ όλα αυτά, κατά τη διάρκεια ανάπτυξης του συστήματος, δημιουργήθηκε η ανάγκη της εύκολης και γρήγορης δημιουργίας όλου του συστήματος, γι’ αυτό και χρησιμοποιήθηκε το εργαλείο αυτοματοποίησης docker-compose [133]. Το docker-compose χρησιμοποιεί ως configuration ένα αρχείο με όνομα docker-compose.yml που ακολουθεί τη σύνταξη YAML [134]. Σε αυτό το αρχείο όλα τα διαφορετικά services μπορούν να περιγραφούν με τρόπο δηλωτικό, δίνοντάς τους τα ονόματά τους, το πώς θα χτιστεί το καθένα από αυτά (δηλαδή τα αντίστοιχα Dockerfile τους), επιλογές κατά τη διάρκεια του χτισίματός τους, μεταξύ τους εξαρτήσεις, έλεγχοι ότι τα services έχουν ξεκινήσει σωστά (healthchecks) κ.ά.. Με χρήση της εντολής «docker-compose up -d», τα docker images αρχίζουν και χτίζονται σειριακά, και στη συνέχεια εκτελούνται, κάνοντας τις δοκιμές του συστήματος πιο γρήγορες και με λιγότερα λάθη. Η επικοινωνία μεταξύ των services επιλέχθηκε να γίνει με χρήση Unix Domain Sockets για δύο βασικούς λόγους. Πρώτον, επειδή τα έξι διαφορετικά services θα τρέχουν μέσα σε containers κάτω από το ίδιο host operating system και προσφέρουν την καλύτερη δυνατή απόδοση σύμφωνα με τις μετρήσεις, και δεύτερον για να επιβεβαιωθεί η δυνατότητα χρήσης Unix Domain Sockets για επικοινωνία μεταξύ διαφορετικών containers σύμφωνα με την λογική ότι τα containers μοιράζονται τον ίδιο πυρήνα (kernel) και ο πυρήνας είναι αυτός που κάνει διαχείριση των Unix Domain Sockets. Η μόνη προϋπόθεση που χρειάζεται έτσι ώστε οι containers να μπορούν να επικοινωνούν μέσω Unix Domain Sockets είναι να έχουν πρόσβαση όλοι σε αυτά τα sockets. Αυτό αποτελεί πρόβλημα καθώς τα docker containers δεν χρησιμοποιούν κοινό σύστημα αρχείων λόγω χρήσης διαφορετικού mount namespace. Παρ’ όλα αυτά, στο πρόβλημα αυτό μπορεί να δοθεί λύση μέσω της χρήσης volumes του docker [70]. Ένας συγκεκριμένος φάκελος του host operating system μπορεί να γίνει διαθέσιμος με read/write πρόσβαση, μέσα από έναν docker container. Η κύρια λειτουργία αυτού του συστήματος αφορά το data persistency, δηλαδή τα δεδομένα να μην χάνονται όταν σταματάει τη λειτουργία του ένας docker container και διαγράφεται. Για παράδειγμα, αυτό μπορεί να φανεί χρήσιμο σε μία βάση δεδομένων, όπου υπάρχει η ανάγκη αναβάθμισής της. Τα δεδομένα της βάσης δεδομένων μπορούν να αποθηκεύονται σε ένα volume, και έτσι ο αντίστοιχος container μπορεί να σταματήσει την εκτέλεσή του, το αντίστοιχο image του να αναβαθμιστεί και να ξεκινήσει ο αναβαθμισμένος container χρησιμοποιώντας τα δεδομένα της παλιάς βάσης, χωρίς να 68 χρειαστεί η αποθήκευση και η επαναφορά των δεδομένων αυτών (δεδομένου της συμβατότητας της μορφής δεδομένων μεταξύ των διαφορετικών εκδόσεων αυτής της βάσης δεδομένων). Όμως τα volumes μπορούν να χρησιμοποιηθούν, στην περίπτωση των microservices, για την αποθήκευση των Unix Domain Sockets. Πιο συγκεκριμένα, αν ένα volume γίνει διαθέσιμο σε έναν docker container, δεν υπάρχει περιορισμός χρήσης του μόνο από αυτόν τον container. Δηλαδή ένας δεύτερος container μπορεί να έχει πρόσβαση και αυτός σε αυτό το volume. Οι containers που έχουν την ανάγκη επικοινωνίας μεταξύ τους, μπορεί να μοιράζονται ανά δύο ένα docker volume. Παρ’ όλα αυτά, για λόγους απλότητας στην κατασκευή του σιλό με microservices, χρησιμοποιήθηκε μόνο ένα docker volume το οποίο μπορεί και χρησιμοποιείται από όλους τους containers. Εκεί μέσα έγινε η δημιουργία των Unix Domain Sockets και η επικοινωνία των containers μέσω αυτών και κατ’ επέκταση μέσω του κοινού πυρήνα που χρησιμοποιούν όλοι οι containers, δηλαδή το kernel του host operating system. 6.4 Τρόπος λειτουργίας του συστήματος Επειδή έγινε επιλογή χρήσης του node-ipc δημιουργήθηκε η UDSCOM (Unix Domain Socket Communication) class η οποία είναι υπεύθυνη για την επικοινωνία μεταξύ των microservices. Ποιο συγκεκριμένα, κάνει abstract το communication layer έτσι ώστε το κάθε microservice να μιλάει με κάθε άλλο με χρήση απλά ενός human-friendly ονόματος, όπως το «siloController». Κάθε microservice έχει ένα δικό του Unix Domain Socket το οποίο δημιουργεί και δρα ως server σε αυτό, δηλαδή κάποιο άλλο service μπορεί να συνδεθεί στο socket αυτό και να στείλει ή να λάβει δεδομένα. H UDSCOM class αναλαμβάνει τη δημιουργία των Unix Domain Sockets με βάση το human-friendly όνομα του κάθε service, την αποστολή μηνυμάτων σε άλλα services με χρήση JSON-encoded strings, την αναμονή για τη διαθεσιμότητα άλλων services και τον τερματισμό της επικοινωνίας (κλείσιμο ανοικτών συνδέσεων και τερματισμό του server). Τέλος, η UDSCOM κάνει extend την EventEmitter class της Node.js [135], προσφέροντας έτσι τη δυνατότητα ένα service να αναγνωρίσει πότε ένα μήνυμα στέλνεται σε αυτό και να αντιδράσει ανάλογα. 6.4.1 Silo Controller Microservice Η λειτουργία του Silo Controller Service μπορεί να παρασταθεί συνοπτικά και σε υψηλό επίπεδο με το παρακάτω διάγραμμα: 69 Εικόνα 34 Flow-chart για τη λειτουργία του Silo Controller Service Το Silo Controller Service (SCS) χρησιμοποιεί τη UDSCOM class δηλώνοντας το όνομά του ως «siloController». Στη συνέχεια, κάνοντας χρήση της ίδιας κλάσης, αναμένει έτσι ώστε τα In-Valve, Out-Valve, Heater και Mixer Services να γίνουν διαθέσιμα, δηλαδή να έχουν ξεκινήσει και να μπορούν να δεχθούν συνδέσεις στα αντίστοιχα Unix Domain Sockets τους. Μόλις τα services αυτά γίνουν διαθέσιμα, ο Silo Controller αρχικοποιεί το Silo Smart Object και τη σύνδεσή του στον αντίστοιχο LwM2M server. Το Silo Smart Object αναλαμβάνει τις απαντήσεις των αιτημάτων του LwM2M server, δηλαδή αιτήματα για την ανάγνωση διάφορων τιμών του αλλά και εκτελέσεις διαφόρων διεργασιών, όπως του «Heat» και του «Mix». Για το σκοπό αυτό, γίνεται χρήση του ενός finite state machine. Πιο συγκεκριμένα, για την αναγνώριση των διαφορετικών καταστάσεων στις οποίες μπορεί να βρίσκεται ένα σιλό, έγινε χρήση του javascript-state-machine module [136] από το Silo Controller Service. Σε αυτό το finite state machine ο προγραμματιστής μπορεί να περιγράψει δηλωτικά τις καταστάσεις στις οποίες βρίσκεται το σύστημα, καθώς και να ορίσει τις αντιδράσεις του συστήματος όταν αυτό εισέρχεται ή εξέρχεται από μία κατάσταση. Οι καταστάσεις καθώς και οι μεταβάσεις μεταξύ των καταστάσεων αυτών, όπως αυτές δηλώνονται με χρήση του javascript-state-machine module, περιγράφονται στον παρακάτω πίνακα: Κατάσταση του συστήματος Init Emptying Πίνακας 9 – οι καταστάσεις του finite state machine του SCS Επιτρεπόμενες Όνομα Περιγραφή κατάστασης προηγούμενες μετάβασης καταστάσεις Το σύστημα βρίσκεται στην αρχική του Καμία κατάσταση Το σιλό αδειάζει από το liqueur έχοντας τη βαλβίδα OUTVALVE ανοικτή και την Empty Init, Heated, Mixed, Full INVALVE κλειστή 70 Emptied Filling Full Mixing Mixed Heating Heated Το σιλό δεν περιέχει liqueur μέσα του, καθώς το σήμα «low level reached» έχει ενεργοποιηθεί Το σιλό γεμίζει με liqueur έχοντας τη βαλβίδα INVALVE ανοικτή και την OUTVALVE κλειστή Το σιλό έχει γεμίσει με liqueur μέσα του, καθώς το σήμα «high level reached» έχει ενεργοποιηθεί Ο αναδευτήρας του σιλό έχει ενεργοποιηθεί και βρίσκεται σε λειτουργία Η ανάδευση του liqueur μέσω του αναδευτήρα ήταν η τελευταία διεργασία που πραγματοποιήθηκε στο σιλό Η ηλεκτρική αντίσταση του σιλό έχει ενεργοποιηθεί και βρίσκεται σε λειτουργία Η αύξηση της θερμοκρασίας του liqueur μέσω της ηλεκτρικής αντίστασης ήταν η τελευταία διεργασία που πραγματοποιήθηκε στο σιλό Empty Emptying Fill Init, Emptied Fill Filling Mix Full, Heated Mix Mixing Heat Full, Mixed Heat Heating Το Silo Controller service, χρησιμοποιεί το finite state machine έτσι ώστε να συντονίζει, ανάλογα με την είσοδό του σε καταστάσεις, τα υπόλοιπα microservices: 1. Η εισαγωγή στην κατάσταση Filling έχει ως αποτέλεσμα την αποστολή του μηνύματος «Close» στο Out-Valve Service και του μηνύματος «Open» στο In-Valve Service. 2. Η εισαγωγή στην κατάσταση Full έχει ως αποτέλεσμα την αποστολή του μηνύματος «Close» στο In-Valve Service. 3. Η εισαγωγή στην κατάσταση Emptied έχει ως αποτέλεσμα την αποστολή του μηνύματος «Close” στο Out-Valve Service. 4. Η εισαγωγή στην κατάσταση Heating έχει ως αποτέλεσμα την αποστολή του μηνύματος «Heat» στο Heater Service. 5. Η εισαγωγή στην κατάσταση Mixing έχει ως αποτέλεσμα την αποστολή του μηνύματος «Mix» στο Mixer Service. 6. Η εισαγωγή στην κατάσταση Emptying έχει ως αποτέλεσμα την αποστολή του μηνύματος «Close» στο In-Valve Service και την αποστολή του μηνύματος «Open» στο Out-Valve Service. Όταν το Smart Silo λάβει αίτημα από τον LwM2M server για εκτέλεση κάποιας διεργασίας του, αυτό κάνει χρήση του state machine για να επιβεβαιώσει ότι η διεργασία αυτή είναι έγκυρη και ότι μπορεί να εκτελεστεί. Για παράδειγμα, δεν είναι έγκυρη η διεργασία «Heat» ενώ το σύστημα βρίσκεται στην κατάσταση «Filling», «Mixing» ή «Emptied». Μόλις επιβεβαιωθεί ότι η μετάβαση στην καινούργια κατάσταση, όπως αυτή ζητήθηκε από τον LwM2M server, είναι έγκυρη, τότε το state machine προχωράει σε αλλαγή του συστήματος. Αυτό έχει ως αποτέλεσμα την πυροδότηση των κατάλληλων μηνυμάτων στα υπόλοιπα microservices του συστήματος. Έτσι γίνεται η επικοινωνία μέσω LwM2M και μεταξύ των διαφορετικών microservices. 71 Εικόνα 35 Flowchart ροής LwM2M αιτημάτων Όμως το σύστημά μας πρέπει να ανανεώνει και μόνο του την κατάσταση του φυσικού σιλό και κατ’ επέκταση την κατάσταση του Silo Smart Object ως LwM2M client. Για παράδειγμα, αν λάβει την εντολή «fill» από τον LwM2M server, δεν θα πρέπει να γίνεται αναμονή για την εντολή «stop filling» από τον LwM2M server. Όταν το σιλό γεμίσει (σήμα «high level reached»), το Silo Controller Service πρέπει να πάρει την πρωτοβουλία να σταματήσει το γέμισμα του σιλό και να αποτρέψει την υπερχείλισή του. Για το λόγο αυτό, πρέπει να υπάρχει και η υποστήριξη της αντίθετης κατεύθυνσης της πληροφορίας μέσα στο Silo Controller Service, δηλαδή το Silo Controller Service να λαμβάνει μηνύματα από τα υπόλοιπα microservices και στη συνέχεια να τα προωθεί κατάλληλα στο Silo Smart Object αλλά και να αλλάζει η κατάσταση του finite state machine. Για παράδειγμα, όταν το Silo Controller Service, κάνοντας χρήση της ιδιότητας του Event Emmiter της UDSCOM class, λάβει το μήνυμα «high level reached» από το Silo Driver microservice, θα πρέπει να πραγματοποιήσει τρεις διεργασίες: 1. Ανανέωση της κατάστασης του Smart Silo Object 2. Αλλαγή της κατάστασης του finite state machine σε «filled» 3. Αποστολή του μηνύματος «Close» στο In-Valve Service 72 Αυτή η κατεύθυνση της ροής της πληροφορίας φαίνεται στο παρακάτω διάγραμμα: Εικόνα 36 Σύνδεση μεταξύ μηνυμάτων από microservices και ανανέωση του Silo Smart Object 6.4.2 Heater and Mixer Microservice Το Heater microservice, όπως και κάθε άλλο microservice το οποίο επικοινωνεί με τον Silo Simulator, κάνει χρήση του node-rpio Node.js module [137], το οποίο χρησιμοποιείται ώστε να πραγματοποιείται η ανάγνωση και η εγγραφή στα pins που είναι διαθέσιμα στο Raspberry Pi και χρησιμοποιούνται για το interfacing με το Silo Simulator. Η μόνη διεργασία που αναλαμβάνει το Heater Microservice είναι η ενεργοποίηση της ηλεκτρικής αντίστασης για συγκεκριμένο χρονικό διάστημα, η απενεργοποίησή της αμέσως μετά και η ειδοποίηση του Silo Controller για την ολοκλήρωση της διαδικασίας θέρμανσης του υγρού. Το Μixer Service δουλεύει με τρόπο κοινό με αυτό του heater με τη μόνη διαφορά ότι χρησιμοποιεί τα αντίστοιχα Pins για interfacing με τον αναδευτήρα του Silo Simulator και ότι φυσικά ειδοποιεί τον Silo Controller ότι η διαδικασία ανάδευσης ολοκληρώθηκε. 73 Εικόνα 37 Flowchart διεργασιών που πραγματοποιούνται από το Heater Service. Αντίστοιχες διεργασίες πραγματοποιούνται και στο Mixer Service 6.4.3 In-Valve και Out-Valve Microservice Τα In-Valve και Out-Valve microservices δουλεύουν ουσιαστικά ως drivers των βαλβίδων εισόδου και εξόδου liqueur, αντίστοιχα. Δηλαδή, η μόνη τους ευθύνη είναι το άνοιγμα και το κλείσιμο των αντίστοιχων βαλβίδων. Η διεργασία αυτή θεωρείται ότι είναι σχεδόν στιγμιαία και έτσι δεν υπάρχει η ανάγκη αποστολής μηνυμάτων από αυτά τα microservices προς τον Silo Controller. Τα services αυτά κάνουν χρήση των αντίστοιχων pins του Raspberry Pi για επικοινωνία με INVALVE και το OUTVALVE του SiloSimulator, αντίστοιχα. 74 Εικόνα 38 Flowchart διεργασιών του In-Valve και του Out-Valve Service 6.4.4 SiloDriver Microservice Το SiloDriver service αναλαμβάνει την ανάγνωση των αισθητήρων που φέρει το κάθε σιλό (ανεξάρτητα από τα πρόσθετα που μπορεί να έχει ένα σιλό, όπως ηλεκτρική αντίσταση και αναδευτήρα), γι’ αυτό και ονομάζεται έτσι. Οι αισθητήρες αυτοί χρησιμοποιούνται για την ανίχνευση της υψηλής και της χαμηλής στάθμης του liqueur, με τα αντίστοιχα σήματά τους, τα «high level reached» και «low level reached». Ευθύνη του SiloDriver Service είναι η ανάγνωση των αισθητήρων αυτών και, κατά την αλλαγή της κατάστασής τους, την αποστολή των αντίστοιχων μηνυμάτων στο Silo Controller Service. Επειδή η μοναδική του ευθύνη είναι να ειδοποιεί το Silo Controller Service με αυτά τα μηνύματα, το SiloDriver Service δεν ξεκινάει να διαβάζει την κατάσταση των αντίστοιχων Pins έως ότου το SiloDriver Service γίνει διαθέσιμο, λειτουργία που παρέχεται μέσω της UDSCOM class. 75 Εικόνα 39 Flowchart διεργασιών του SiloDriver Service Για την ανάγνωση της κατάστασης των Pins με χρήση Polling, έγινε χρήση της δυνατότητας του node-rpio module POLL_BOTH, όπου ειδοποιεί το πρόγραμμα όταν το αντίστοιχο Pin αλλάζει κατάσταση είτε από HIGH σε LOW είτε ανάποδα, χωρίς να πρέπει η επαναληπτική ανάγνωση των Pins να υλοποιηθεί από τον προγραμματιστή. 76 Κεφάλαιο 7 7. Συμπεράσματα 7.1 Συμπεράσματα Μέσω της έρευνας και των πειραμάτων που πραγματοποιήθηκαν για τη συγγραφή αυτής της διπλωματικής εργασίας εξάγονται τα παρακάτω συμπεράσματα: 1. Λόγω των λίγων επιπρόσθετων υπολογιστικών πόρων που απαιτούνται για την εκτέλεση containerized εφαρμογών, η εκτέλεσή τους σε υπολογιστικά συστήματα περιορισμένων πόρων είναι δυνατή. Αυτό φαίνεται από το πείραμα που πραγματοποιήθηκε, όπου το software ενός σιλό έτρεξε ως containerized εφαρμογή μέσα σε ένα raspberry pi, χρησιμοποιώντας μάλιστα Nodejs, μια γλώσσα προγραμματισμού που δεν φημίζεται για την ταχύτητα εκτέλεσής της ή για τη μικρή κατανάλωση μνήμης. 2. Η χρήση containers μπορεί να είναι ζωτικής σημασίας στο Industry 4.0, καθώς η συντήρηση και η ανάπτυξη ενός συστήματος λογισμικού που αποτελείται από containers γίνεται πολύ πιο εύκολη συγκριτικά με τη μη χρήση αυτών. Καθώς κύριο χαρακτηριστικό του Industry 4.0 αποτελούν τα συνεχώς μεταβαλλόμενες απαιτήσεις του συστήματος και η προσθήκη περισσότερων μεταβλητών που μπορούν να επηρεάσουν το τελικό προϊόν, είναι επόμενο ότι το λογισμικό το οποίο χρησιμοποιείται για αυτή τη διαδικασία να πρέπει να εξελίσσεται συνεχώς και να είναι ευέλικτο στην αλλαγή των προδιαγραφών. 3. Μεγάλη προσοχή πρέπει να δοθεί στο αν ένα σύστημα είναι κατάλληλο για να λειτουργήσει με την αρχιτεκτονική των microservices. Προσοχή πρέπει να δοθεί στις απαιτήσεις του συστήματος έτσι ώστε να γίνει σωστά η τμηματοποίησή του, στη σωστή επιλογή των κατάλληλων πρωτόκολλων επικοινωνίας, καθώς και στη συντήρηση ενός συστήματος από microservices. 4. Η χρήση microservices γίνεται πιο εύκολη όταν κάθε microservice τρέχει ως containerized εφαρμογή, καθώς κάθε εφαρμογή θα τρέχει σε ένα ενθυλακωμένο περιβάλλον ανεξάρτητο από τις υπόλοιπες εφαρμογές. Όμως, η χρήση τους απαιτεί έρευνα για τον τρόπο επικοινωνίας μεταξύ αυτών, τον τρόπο με τον οποίο καινούργιες εκδόσεις των microservices θα γίνονται deploy καθώς και ποιες θα είναι οι επιπτώσεις των καινούργιων εκδόσεων στις αλληλεξαρτήσεις τους. Ενώ τα microservices επιτρέπουν κάθε εφαρμογή του συστήματος να έχει συγκεκριμένη λειτουργία και έτσι μπορεί πιο εύκολα να πιστοποιηθεί η ορθή λειτουργία του συστήματος και γίνεται πιο εύκολη η ανάπτυξή του, η περιπλοκότητα αυτή μεταφέρεται εκτός του κώδικα της εφαρμογής, δηλαδή στο πώς ακριβώς θα γίνεται η ενορχήστρωση μεταξύ των διαφορετικών microservices. 5. Η χρήση containers μπορεί να κάνει εύκολη την ορθή κατανομή των πόρων του συστήματος στις διαφορετικές εφαρμογές που εκτελούνται σε αυτό. Αυτό κάνει την εκτέλεση κρίσιμων εφαρμογών πιο ασφαλή, καθώς μπορεί να πιστοποιηθεί ότι διαφορετικές, λιγότερο σημαντικές εφαρμογές, δεν θα καταλάβουν περισσότερους πόρους από ότι τους επιτρέπεται. 6. Η χρήση ανοιχτών πρωτοκόλλων του IoT όπως το LwM2M κάνει δυνατή τη συνεργασία εφαρμογών που είναι αναπτυγμένες με διαφορετικές τεχνολογίες (interoperability). Αυτό είναι πολύ σημαντικό για το Industry 4.0 καθώς έξυπνα αντικείμενα που είναι κατασκευασμένα με διαφορετικές τεχνολογίες και από διαφορετικούς κατασκευαστές θα μπορούν να επικοινωνούν μεταξύ τους και να συνεργάζονται για την παραγωγή των προϊόντων. 77 7.2 Προτάσεις για μελλοντική εργασία Πολλές εφαρμογές, ειδικά όσον αφορά την βιομηχανία, έχουν απαιτήσεις πραγματικού χρόνου. Στα πλαίσια αυτής της διπλωματικής δεν έγινε μελέτη σχετικά με τις εφαρμογές αυτού του τύπου και κατά πόσο ένα containerized περιβάλλον μέσα στο οποίο εκτελούνται microservices θα μπορούσε να ικανοποιήσει τις απαιτήσεις αυτές. Μία μελλοντική εργασία θα μπορούσε να είναι η μελέτη των διαφορετικών τρόπων με τους οποίους containerized εφαρμογές μπορούν να έχουν πιστοποιήσεις εκτέλεσης σε πραγματικό χρόνο (real time) καθώς και αν θα μπορούν να χρησιμοποιηθούν πρωτόκολλα επικοινωνίας μεταξύ των microservices τα οποία προσφέρουν τέτοιες πιστοποιήσεις. Επίσης, σε ένα βιομηχανικό περιβάλλον υπάρχει η απαίτηση της συντήρησης του λογισμικού και της ανάπτυξής του, αλλά δεν έγινε σχετική μελέτη για το πώς με τη χρήση containers μπορεί να γίνει αυτό. Όπως αναφέρθηκε, ένα σύστημα ενορχήστρωσης containers αποτελεί το Kubernetes [75] το οποίο όμως απαιτεί αρκετή γνώση για την αρχικοποίησή του, τη συντήρηση και την ανανέωσή του. Μελλοντική εργασία μπορεί να γίνει στο πώς μπορεί να γίνει αποτελεσματικά η ενορχήστρωση containers σε ένα βιομηχανικό περιβάλλον χωρίς να υπάρχουν σημαντικές επιπτώσεις στην ταχύτητα, αλλά και οι απαιτήσεις για τη συντήρηση του συστήματος ενορχήστρωσης (όπως το Kubernetes) να μην υπερβαίνουν τις απαιτήσεις που υπήρχαν για τη συντήρηση του συστήματος λογισμικού της βιομηχανίας εξαρχής. 78 Bibliography [1] [2] [3] [4] [5] [6] [7] [8] [9] [10] [11] [12] [13] [14] [15] [16] [17] [18] [19] “iot Explore Google Trends,” Google, [Online]. Available: https://trends.google.com/trends/explore?date=all&q=iot. [Accessed 15 6 2020]. “8 Surprising Facts About Real Docker Adoption | Datadog,” Datadog, 6 2018. [Online]. Available: https://www.datadoghq.com/docker-adoption/. [Accessed 15 6 2020]. J. Lewis and M. Fowler, “Microservices,” 25 3 2014. [Online]. Available: https://www.martinfowler.com/articles/microservices.html. [Accessed 15 6 2020]. “XDK Overview,” Robert Bosch GmbH, [Online]. Available: https://developer.bosch.com/web/xdk. [Accessed 26 10 2019]. A. T. S., “The Industrial Revolution (1760-1830),” 1964. [Online]. Available: https://web.archive.org/web/20170312195459/http://www.dli.ernet.in/handle/2015/237785. [Accessed 23 10 2019]. “The Second Industrial Revolution, 1870-1914,” 8 1998. [Online]. Available: https://cpb-use1.wpmucdn.com/sites.northwestern.edu/dist/3/1222/files/2016/06/The-Second-IndustrialRevolution-1870-1914-Aug-1998-1ubah7s.pdf. [Accessed 23 10 2019]. “Industry 4.0 and manufacturing ecosystems,” 25 6 2019. [Online]. Available: https://www2.deloitte.com/content/dam/insights/us/articles/manufacturing-ecosystemsexploring-world-connectedenterprises/DUP_2898_Industry4.0ManufacturingEcosystems.pdf. [Accessed 23 10 2019]. K. Thramboulidis, D. Vachtsevanou and A. Solanos, “Cyber-Physical Microservices: An IoTbased Framework for Manufacturing Systems,” in 1st IEEE International Conference on Industrial Cyber-Physical Systems (ICPS 2018), Saint Petersburg, Russia, May 15-18, 2018. “Who Needs the Internet of Things?,” Linux.com, 26 9 2016. [Online]. Available: www.linux.com/news/who-needs-internet-things. [Accessed 14 11 2018]. S. Salamone, “August 1995 / Core Technologies / Novell Builds a NEST,” byte.com, 1995. [Online]. Available: https://web.archive.org/web/20001002035211/http://www.byte.com:80/art/9508/sec13/art2. htm. [Accessed 15 11 2018]. E. Perratore, “Printer at Work,” byte.com, 5 1994. [Online]. Available: https://web.archive.org/web/20070930185433/http://www.byte.com/art/9405/sec8/art8.htm. [Accessed 15 11 2018]. “That 'Internet of Things' Thing,” rfidjournal.com, 22 6 2009. [Online]. Available: https://www.rfidjournal.com/articles/view?4986. [Accessed 15 11 2018]. “Fitness Band Google Trends,” 23 10 2019. [Online]. Available: https://trends.google.com/trends/explore?date=all&q=fitness%20band. [Accessed 23 10 2019]. “PostgreSQL: Documentation: 12: 13.3. Explicit Locking,” [Online]. Available: https://www.postgresql.org/docs/12/explicit-locking.html#LOCKING-ROWS. [Accessed 24 10 2019]. “PostgreSQL: Documentation: 12: SELECT,” [Online]. Available: https://www.postgresql.org/docs/12/sql-select.html. [Accessed 24 10 2019]. “PostgreSQL: Documentation: 12: NOTIFY,” [Online]. Available: https://www.postgresql.org/docs/12/sql-notify.html. [Accessed 24 10 2019]. “PostgreSQL: Documentation: 12: LISTEN,” [Online]. Available: https://www.postgresql.org/docs/12/sql-listen.html. [Accessed 24 10 2019]. “PostgreSQL: Documentation: 12: UNLISTEN,” [Online]. Available: https://www.postgresql.org/docs/12/sql-unlisten.html. [Accessed 24 10 2019]. “Information technology -- Message Queuing Telemetry Transport (MQTT) v3.1.1,” 06 2016. [Online]. Available: https://www.iso.org/standard/69466.html. [Accessed 29 11 2018]. 79 [20] [21] [22] [23] [24] [25] [26] [27] [28] [29] [30] [31] [32] [33] [34] [35] [36] “OMA LightweightM2M (LwM2M) Object and Resource Registry,” [Online]. Available: http://www.openmobilealliance.org/wp/OMNA/LwM2M/LwM2MRegistry.html. [Accessed 29 11 2018]. IETF, “Terminology for Constrained-Node Networks,” 05 2014. [Online]. Available: https://tools.ietf.org/html/rfc7228. [Accessed 29 11 2018]. Open Mobile Alliance, “LwM2M v1.1,” 2019. [Online]. Available: https://www.openmobilealliance.org/release/LightweightM2M/Lightweight_Machine_to_Mac hine-v1_1-OMASpecworks.pdf. [Accessed 7 7 2020]. K. Thramboulidis and F. Christoulakis, “UML4IoT—A UML-based approach to exploit IoT in cyber-physical manufacturing systems,” 22 06 2016. [Online]. Available: https://www.researchgate.net/publication/287250136_UML4IoT__A_UML_profile_to_exploit_IoT_in_cyber-physical_manufacturing_systems. [Accessed 21 03 2020]. P. C. Basile and D. Gerbasio, “On the Implementation of Industrial Automation Systems Based on PLC,” IEEE Trans. on automation science and engineering, vol. 10, no. 4, pp. 9901003, 10 2013. K. Thramboulidis, “A Cyber-Physical System-based Approach for Industrial Automation Systems,” Computers in Industry, vol. 72, pp. 92-102, 09 2015. F. Christoulakis and K. Thramboulidis, “IoT-based Integration of IEC 61131 Industrial Automation Systems,” in IEEE Inter. Symposium on Industrial Electronics, Santa Clara, CA, 2016. T. Foradis and K. Thramboulidis, “From Mechatronic Components to Industrial Automation Things: An IoT Model for Cyber-Physical Manufacturing Systems,” Journal of Software Engineering and Applications, vol. 10, pp. 734-753, 2017. K. Thramboulidis, P. Bochalis and J. Bouloumpasis, “A framework for MDE of IoTBasedManufacturing Cyber-Physical Systems,” in The 7th International Conference on the Internet of Things (IoT 2017), Linz, Austria, 2017. K. Thramboulidis, “Liqueur Plant case study,” [Online]. Available: https://sites.google.com/site/uml4iot/liqueur-plant-case-study. [Accessed 30 11 2018]. “GPIO - Raspberry Pi Documentation,” Raspberry Pi Foundation, 1 10 2019. [Online]. Available: https://www.raspberrypi.org/documentation/usage/gpio/README.md. [Accessed 26 10 2019]. “XDK_Node_110_combined_Datasheet.pdf,” Robert Bosch GmbH, [Online]. Available: https://developer.bosch.com/documents/422133/482930/XDK_Node_110_combined_Datas heet.pdf/ace7ca7c-fd0e-9741-acf6-e9d5dc10957d. [Accessed 26 10 2019]. K. Tramboulidis, “SiloSimulator,” [Online]. Available: https://sites.google.com/site/uml4iot/liqueur-plant-case-study/silosimulator. [Accessed 3 12 2018]. “GitHub - eclipse/leshan: Eclipse Leshan is an OMA Lightweight M2M (LWM2M) implementation in Java.,” [Online]. Available: https://github.com/eclipse/leshan. [Accessed 26 10 2019]. “GitHub - OpenMobileAlliance/lwm2m-registry: This is a public repository dedicated to store and register new LwM2M Objects,” [Online]. Available: https://github.com/OpenMobileAlliance/lwm2m-registry. [Accessed 26 10 2019]. K. A. &. O. Agesen, “A Comparison of Software and Hardware Techniques for x86 Virtualization,” [Online]. Available: https://www.vmware.com/pdf/asplos235_adams.pdf. [Accessed 28 10 2019]. “Take a Snapshot in the VMware Host Client,” VMware, Inc, 27 4 2018. [Online]. Available: https://docs.vmware.com/en/VMwarevSphere/6.0/com.vmware.vsphere.html.hostclient.doc/GUID-A0D8E8E7-629B-466D-A50C38705ACA7613.html. [Accessed 28 10 2019]. 80 [37] [38] [39] [40] [41] [42] [43] [44] [45] [46] [47] [48] [49] [50] [51] [52] [53] [54] [55] “Welcome to Remote Desktop Services in Windows Server 2016 | Microsoft Docs,” Microsoft, 22 2 2017. [Online]. Available: https://docs.microsoft.com/en-us/windowsserver/remote/remote-desktop-services/welcome-to-rds. [Accessed 28 10 2019]. “OpenSSH ArchWiki,” [Online]. Available: https://wiki.archlinux.org/index.php/OpenSSH#X11_forwarding. [Accessed 28 10 2019]. “3.5. Limiting your program's environment,” [Online]. Available: https://www.freebsd.org/doc/en/books/developers-handbook/secure-chroot.html. [Accessed 28 10 2019]. “Creating and Using Oracle Solaris Kernel Zones,” [Online]. Available: https://docs.oracle.com/cd/E36784_01/html/E37629/index.html. [Accessed 28 10 2019]. “Sandboxie - How It Works,” [Online]. Available: https://www.sandboxie.com/HowItWorks. [Accessed 28 10 2019]. “Wine Developer's Guide/Architecture Overview - WineHQ Wiki,” [Online]. Available: https://wiki.winehq.org/Wine_Developer%27s_Guide/Architecture_Overview. [Accessed 28 10 2019]. “VMware Virtual Networking Concepts - virtual_networking_concepts.pdf,” VMware, Inc, [Online]. Available: https://www.vmware.com/content/dam/digitalmarketing/vmware/en/pdf/techpaper/virtual_ne tworking_concepts.pdf. [Accessed 10 28 2019]. J. McCarthy, “REMINISCENCES ON THE HISTORY OF TIME SHARING,” stanford, Stanford University. [Online]. Available: https://web.archive.org/web/20071020032705/http://wwwformal.stanford.edu/jmc/history/timesharing/timesharing.html. [Accessed 16 11 2018]. S. E. Madnick, “Time-Sharing Systems: Virtual Machine Concept vs. Conventional Approach,” 1969. [Online]. Available: http://web.mit.edu/smadnick/www/papers/J004.pdf. “A performance analysis of Xen and KVM hypervisors for hosting the Xen Worlds Project viewcontent.cgi,” [Online]. Available: http://lib.dr.iastate.edu/cgi/viewcontent.cgi?article=3243&context=etd. [Accessed 27 10 2019]. “VMware Workstation Pro Documentation,” [Online]. Available: https://docs.vmware.com/en/VMware-Workstation-Pro/index.html. [Accessed 27 10 2019]. “Technical_documentation – Oracle VM VirtualBox,” [Online]. Available: https://www.virtualbox.org/wiki/Technical_documentation. [Accessed 27 10 2019]. “docker container Explore Google Trends,” [Online]. Available: https://trends.google.com/trends/explore?date=all&q=docker%20container. [Accessed 10 27 2019]. T. U. N. Archives, “A port bow view of the civilian container ship SS MARGARET LYKES underway near Hampton Roads, VA | PICRYL,” 02 08 1990. [Online]. Available: https://picryl.com/media/a-port-bow-view-of-the-civilian-container-ship-ss-margaret-lykesunderway-near-aa84df. [Accessed 25 11 2018]. “What is a Container? | App Containerization | Docker,” Docker Inc., [Online]. Available: https://www.docker.com/resources/what-container. [Accessed 27 10 2019]. “NVD - CVE-2014-9357,” [Online]. Available: https://nvd.nist.gov/vuln/detail/CVE-2014-9357. [Accessed 27 10 2019]. “Install Docker Desktop on Windows | Docker Documentation,” [Online]. Available: https://docs.docker.com/docker-for-windows/install/. [Accessed 27 10 2019]. “Introduction to Hyper-V on Windows 10 | Microsoft Docs,” [Online]. Available: https://docs.microsoft.com/en-us/virtualization/hyper-v-on-windows/about/. [Accessed 27 10 2019]. “Docker: A Favourite in the DevOps World,” 08 02 2017. [Online]. Available: http://opensourceforu.com/2017/02/docker-favourite-devops-world/. [Accessed 11 27 2018]. 81 [56] [57] [58] [59] [60] [61] [62] [63] [64] [65] [66] [67] [68] [69] [70] [71] [72] [73] [74] [75] [76] [77] [78] [79] [80] “chroot(2) - Linux manual page,” [Online]. Available: http://man7.org/linux/manpages/man2/chroot.2.html. [Accessed 28 10 2019]. “Enterprise Container Platform | Docker,” [Online]. Available: https://www.docker.com/. [Accessed 28 10 2019]. “Docker: Automated and Consistent Software Deployments,” InfoQ, 3 2013. [Online]. Available: https://www.infoq.com/news/2013/03/Docker/. [Accessed 28 10 2019]. “mount_namespaces(7) - Linux manual page,” [Online]. Available: http://man7.org/linux/manpages/man7/mount_namespaces.7.html. [Accessed 29 10 2019]. “pid_namespaces(7) - Linux manual page,” [Online]. Available: http://man7.org/linux/manpages/man7/pid_namespaces.7.html. [Accessed 29 10 2019]. “kill(1) - Linux manual page,” [Online]. Available: http://man7.org/linux/manpages/man1/kill.1.html. [Accessed 29 10 2019]. “network_namespaces(7) Linux manual page,” [Online]. Available: http://man7.org/linux/man-pages/man7/network_namespaces.7.html. [Accessed 29 10 2019]. “user_namespaces(7) - Linux manual page,” [Online]. Available: http://man7.org/linux/manpages/man7/user_namespaces.7.html. [Accessed 29 10 2019]. “uts_namespaces(7) - Linux manual page,” [Online]. Available: http://man7.org/linux/manpages/man7/uts_namespaces.7.html. [Accessed 29 10 2019]. “ipc_namespaces(7) - Linux manual page,” [Online]. Available: http://man7.org/linux/manpages/man7/ipc_namespaces.7.html. [Accessed 29 10 2019]. “cgroups(7) - Linux manual page,” [Online]. Available: http://man7.org/linux/manpages/man7/cgroups.7.html. [Accessed 29 10 2019]. “Linux Kernel Documentation :: cgroup-v1 : pids.txt,” [Online]. Available: https://www.mjmwired.net/kernel/Documentation/cgroup-v1/pids.txt. [Accessed 29 10 2019]. “Linux Kernel Documentation :: cgroup-v1 : freezer-subsystem.txt,” [Online]. Available: https://www.mjmwired.net/kernel/Documentation/cgroup-v1/freezer-subsystem.txt. [Accessed 29 10 2019]. “What is Continuous Integration? – Amazon Web Services,” [Online]. Available: https://aws.amazon.com/devops/continuous-integration/. [Accessed 30 10 2019]. “Use volumes | Docker Documentation,” Docker Inc, [Online]. Available: https://docs.docker.com/storage/volumes/. [Accessed 30 10 2019]. “Dockerfile Reference | Docker Documentatio,” Docker Inc, [Online]. Available: https://docs.docker.com/engine/reference/builder/. [Accessed 30 10 2019]. “Jenkins,” [Online]. Available: https://jenkins.io/. [Accessed 30 10 2019]. “Bitbucket Pipelines - Continuous Delivery | Bitbucket,” Atlassian, [Online]. Available: https://bitbucket.org/product/features/pipelines. [Accessed 15 6 2020]. “Features • GitHub Actions · GitHub,” Github, Inc., [Online]. Available: https://github.com/features/actions. [Accessed 15 6 2020]. “Production-Grade Container Orchestration - Kubernetes,” [Online]. Available: https://kubernetes.io/. [Accessed 30 10 2019]. “Managed Kubernetes on DigitalOcean,” [Online]. Available: https://www.digitalocean.com/products/kubernetes/. [Accessed 30 10 2019]. “Google Kubernetes Engine | Kubernetes Engine | Google Cloud,” [Online]. Available: https://cloud.google.com/kubernetes-engine/. [Accessed 30 10 2019]. “Introducing AKS (managed Kubernetes) and Azure Container Registry improvements | Blog | Microsoft Azure,” [Online]. Available: https://azure.microsoft.com/en-us/blog/introducingazure-container-service-aks-managed-kubernetes-and-azure-container-registry-georeplication/. [Accessed 30 10 2013]. S. Newman, “Building Microservices,” in Building Microservices, Ο'REILLY, 2015, p. 2. A. Cockcroft, "State of the Art in Microservices" at DockerCon Europe 14, Amsterdam, 2014. 82 [81] [82] [83] [84] [85] [86] [87] [88] [89] [90] [91] [92] [93] [94] [95] [96] [97] [98] [99] [100] [101] [102] [103] “Basics of the Unix Philosophy,” 23 9 2003. [Online]. Available: http://www.catb.org/~esr/writings/taoup/html/ch01s06.html#id2877537. [Accessed 30 10 2019]. R. C. Martin, “Clean Coder Blog,” 8 5 2014. [Online]. Available: https://blog.cleancoder.com/uncle-bob/2014/05/08/SingleReponsibilityPrinciple.html. [Accessed 30 10 2019]. “document.title,” Microsoft, 9 11 2006. [Online]. Available: https://docs.microsoft.com/enus/previous-versions/office/developer/servertechnologies/aa480455(v=msdn.10)?redirectedfrom=MSDN. [Accessed 30 10 2019]. B. McCloskey, “Multiprocess Firefox | Bill McCloskey's Blog,” 5 12 2013. [Online]. Available: https://billmccloskey.wordpress.com/2013/12/05/multiprocess-firefox/#work. [Accessed 31 10 2019]. “Multi-process Architecture - The Chromium Projects,” [Online]. Available: https://www.chromium.org/developers/design-documents/multi-process-architecture. [Accessed 31 10 2019]. “gRPC – About gRPC,” [Online]. Available: https://grpc.io/about/. [Accessed 15 6 2020]. “Service | Kubernetes,” The Linux Foundation, [Online]. Available: https://kubernetes.io/docs/concepts/services-networking/service/. [Accessed 29 06 2020]. “Node.js,” [Online]. Available: https://nodejs.org. [Accessed 1 11 2019]. “Ruby Programming Language,” [Online]. Available: https://www.ruby-lang.org. [Accessed 1 11 2019]. “Ruby on Rails | A web-application framework that includes everything needed to create database-backed web applications according to the Model-View-Controller (MVC) pattern,” [Online]. Available: https://rubyonrails.org/. [Accessed 1 11 2019]. “Reference - C++ Reference,” [Online]. Available: http://www.cplusplus.com/reference/. [Accessed 1 11 2019]. “https://golang.org/doc/,” [Online]. Available: https://golang.org/doc/. [Accessed 1 11 2019]. “Redis,” [Online]. Available: https://redis.io/. [Accessed 1 11 2019]. J. Frederick P. Brooks, “The mythical man-month,” Boston, Addison-Wesley Longman Publishing Co., Inc., 1995, pp. 18-19. M. F. James Lewis, “Microservices,” 2014 05 2014. [Online]. Available: https://martinfowler.com/articles/microservices.html. [Accessed 28 11 2018]. “Squash Docs,” [Online]. Available: https://squash.solo.io/. [Accessed 1 11 2019]. “Home - Telepresence,” [Online]. Available: https://www.telepresence.io/. [Accessed 1 11 2019]. “The OpenTracing project,” [Online]. Available: https://opentracing.io/. [Accessed 1 11 2019]. “Ib (integration Broker,” Gartner, Inc, [Online]. Available: https://www.gartner.com/en/information-technology/glossary/ib-integration-broker. [Accessed 2 11 2019]. “Home | AMQP,” OASIS, [Online]. Available: http://www.amqp.org/. [Accessed 2 11 2019]. “IEEE-Advanced_Message_Queuing_Protocol.pdf,” 12 2006. [Online]. Available: http://steve.vinoski.net/pdf/IEEE-Advanced_Message_Queuing_Protocol.pdf. [Accessed 2 11 2019]. “OASIS Advanced Message Queuing Protocol (AMQP) Version 1.0, Part 1: Types,” OASIS, 29 10 2012. [Online]. Available: http://docs.oasis-open.org/amqp/core/v1.0/os/amqp-coretypes-v1.0-os.html#section-types. [Accessed 2 11 2019]. “OASIS Advanced Message Queuing Protocol (AMQP) Version 1.0, Part 2: Transport,” OASIS, 29 10 2012. [Online]. Available: docs.oasis-open.org/amqp/core/v1.0/os/amqp-coretransport-v1.0-os.html. [Accessed 2. 11 2019]. 83 [104] “OASIS Advanced Message Queuing Protocol (AMQP) Version 1.0, Part 3: Messaging,” OASIS, 29 10 2012. [Online]. Available: http://docs.oasis-open.org/amqp/core/v1.0/os/amqpcore-messaging-v1.0-os.html. [Accessed 2 11 2019]. [105] “OASIS Advanced Message Queuing Protocol (AMQP) Version 1.0, Part 4: Transactions,” OASIS, 29 10 2012. [Online]. Available: docs.oasis-open.org/amqp/core/v1.0/os/amqp-coretransactions-v1.0-os.html. [Accessed 2 10 2019]. [106] “OASIS Advanced Message Queuing Protocol (AMQP) Version 1.0, Part 5: Security,” OASIS, 29 10 2012. [Online]. Available: docs.oasis-open.org/amqp/core/v1.0/os/amqp-core-securityv1.0-os.html. [Accessed 2 11 2019]. [107] “Messaging that just works — RabbitMQ,” Pivotal Software, Inc, [Online]. Available: https://www.rabbitmq.com/. [Accessed 2 11 2019]. [108] “ZeroMQ,” [Online]. Available: https://zeromq.org/. [Accessed 2 11 2019]. [109] “Broker vs. Brokerless zeromq,” 9 6 2017. [Online]. Available: http://wiki.zeromq.org/whitepapers:brokerless. [Accessed 3 11 2019]. [110] P. Hintjens, “ZeroMQ Message Transport Protocol,” [Online]. Available: https://rfc.zeromq.org/spec:23/ZMTP/. [Accessed 29 11 2018]. [111] “Stream | Node.js v13.0.1 Documentation,” [Online]. Available: https://nodejs.org/api/stream.html#stream_event_pipe. [Accessed 4 11 2019]. [112] “Readline | Node.js v13.0.1 Documentation,” [Online]. Available: https://nodejs.org/api/readline.html. [Accessed 4 11 2019]. [113] “unix - sockets for local interprocess communication,” [Online]. Available: http://man7.org/linux/man-pages/man7/unix.7.html. [114] “Net | Node.js v13.0.1 Documentation,” [Online]. Available: https://nodejs.org/api/net.html#net_ipc_support. [Accessed 4 11 2019]. [115] “socket — Low-level networking interface — Python 3.8.0 documentation,” [Online]. Available: https://docs.python.org/3/library/socket.html. [Accessed 4 11 2019]. [116] “Semaphore Objects - Win32 apps | Microsoft Docs,” Microsoft, 31 5 2018. [Online]. Available: https://docs.microsoft.com/en-us/windows/win32/sync/semaphore-objects. [Accessed 5 11 2019]. [117] “GitHub - avz/node-posix-clock: POSIX clock_gettime(2), clock_getres(2) and clock_nanosleep(2) for NodeJS,” [Online]. Available: https://github.com/avz/node-posixclock. [Accessed 7 11 2019]. [118] “clock_getres(2) - Linux manual page,” [Online]. Available: http://man7.org/linux/manpages/man3/clock_gettime.3.html. [Accessed 7 11 2019]. [119] “UDP/Datagram Sockets | Node.js v13.1.0 Documentation,” [Online]. Available: https://nodejs.org/api/dgram.html. [Accessed 7 11 2019]. [120] “GitHub - PeterEB/coap-node: Client node of lightweight M2M (LWM2M).,” [Online]. Available: https://github.com/PeterEB/coap-node. [Accessed 5 11 2019]. [121] “GitHub - lwmqn/smartobject: A Smart Object Class that helps you with creating IPSO Smart Objects in your JavaScript applications.,” [Online]. Available: https://github.com/lwmqn/smartobject. [Accessed 5 11 2019]. [122] “GitHub - PeterEB/coap-shepherd: Network server and manager for lightweight M2M (LWM2M).,” [Online]. Available: https://github.com/PeterEB/coap-shepherd. [Accessed 5 11 2019]. [123] “GitHub - squaremo/amqp.node: AMQP 0-9-1 library and client for Node.JS,” [Online]. Available: https://github.com/squaremo/amqp.node. [Accessed 7 11 2019]. [124] “GitHub - zeromq/zeromq.js: Node.js bindings to the ØMQ library,” [Online]. Available: https://github.com/zeromq/zeromq.js. [Accessed 8 11 2019]. [125] “Git - Submodules,” [Online]. Available: https://git-scm.com/book/en/v2/Git-ToolsSubmodules. [Accessed 10 11 2019]. [126] “Seneca, a microservices toolkit for Node.js,” [Online]. Available: http://senecajs.org/docs/. [Accessed 9 11 2019]. 84 [127] “Seneca, a microservices toolkit for Node.js,” [Online]. Available: http://senecajs.org/plugins/. [Accessed 9 11 2019]. [128] “GitHub - RIAEvangelist/node-ipc: Inter Process Communication Module for node supporting Unix sockets, TCP, TLS, and UDP. Giving lightning speed on Linux, Mac, and Windows. Neural Networking in Node.JS,” [Online]. Available: https://github.com/RIAEvangelist/nodeipc. [Accessed 9 11 2019]. [129] “node - Docker Hub,” [Online]. Available: https://hub.docker.com/_/node/. [Accessed 9 11 2019]. [130] “Use the Docker command line | Docker Documentation,” [Online]. Available: https://docs.docker.com/engine/reference/commandline/cli/. [Accessed 10 11 2019]. [131] “npm | build amazing things,” [Online]. Available: https://www.npmjs.com/. [Accessed 10 11 2019]. [132] “Yarn,” [Online]. Available: https://yarnpkg.com/lang/en/. [Accessed 10 11 2019]. [133] “Overview of Docker Compose | Docker Documentation,” [Online]. Available: https://docs.docker.com/compose/. [Accessed 10 11 2019]. [134] “The Official YAML Web Site,” [Online]. Available: https://yaml.org/. [Accessed 10 11 2019]. [135] “Events | Node.js v13.1.0 Documentation,” [Online]. Available: https://nodejs.org/api/events.html. [Accessed 13 11 2019]. [136] “GitHub - jakesgordon/javascript-state-machine: A javascript finite state machine library,” [Online]. Available: https://github.com/jakesgordon/javascript-state-machine. [Accessed 13 11 2019]. [137] “GitHub - jperkin/node-rpio: Raspberry Pi GPIO library for node.js,” [Online]. Available: https://github.com/jperkin/node-rpio. [Accessed 13 11 2019]. [138] “Why IoT, big data & smart farming are the future of agriculture,” 20 12 2016. [Online]. Available: https://www.businessinsider.com/internet-of-things-smart-agriculture-2016-10. [Accessed 28 11 2018]. [139] M. L. R. R. Michael Chui, “The Internet of Things,” 5 2010. [Online]. Available: https://www.mckinsey.com/industries/high-tech/our-insights/the-internet-of-things. [Accessed 11 28 2018]. [140] “23/ZMTP · ZeroMQ RFC,” [Online]. Available: https://rfc.zeromq.org/spec:23/ZMTP/. [Accessed 4 11 2019].