Feature #393
Refactoring του business logic σε services
Status: | Closed | Start date: | 04/06/2011 | ||
---|---|---|---|---|---|
Priority: | Medium | Due date: | 04/15/2011 | ||
Assignee: | Giorgos Gousios | % Done: | 90% |
||
Category: | logic | Spent time: | - | ||
Target version: | v0.3 |
Description
Πρέπει να γίνουν τα παρακάτω:
- Να αποφασιστεί αν μας κάνει το 0mq και αν όχι με τι να το αντικαταστήσουμε
- Να οργανώσουμε το σύστημα ώστε να μπορούν τα διάφορα μέρη του να τρέχουν σε πολλά μηχανήματα με ανοχή σε βλάβες
Associated revisions
Include .. in PYTHON path in manage.py
We have to do this in order to fix namespace inconsistencies (files in
synnefo use imports starting with synnefo)
refs: #393
Dynamic configuration of queues from settings.py
This commit enables test users to bind arbitrary functions in response
to queued messages, using the topic queue pattern
http://www.rabbitmq.com/tutorials/tutorial-five-python.html
refs: #393
Add initial version of Synnefo Ganeti hook
Add initial version of a Synnefo-specific Ganeti hook, which will
eventually use the redesigned messaging architecture to publish
notifications to the rest of the infrastructure, refs #393.
- Add generic hook handler ganeti/snf-ganeti-hook.py
- Add {pre, post} {start, stop} hooks in ganeti/hooks.py
- Implement a post-start hook to send notifications detailing
the NIC configuration of a Ganeti instance, refs #397 - Add simple unit test for said hook
History
#1 Updated by Vangelis Koukis over 12 years ago
Και να προσθέσω, ως αποτέλεσμα των (1),(2):
α) τι θα γίνει με τον db_controller
, που σήμερα αποτελεί ουσιαστικά το μοναδικό context εκτέλεσης του business logic.
β) τι θα γίνει με τον ganeti-0mqd
: ίσως έχει νόημα να αντικατασταθεί από ένα σύνολο από synnefo-provided scripts, τα οποία θα εκτελούνται μέσα στο Ganeti στο πλαίσιο συγκεκριμένων hooks του και θα προκαλούν ενεργοποίηση πραγμάτων στο business logic.
Για το (β) έχει γίνει μια αρχική συζήτηση και με [faidon], [apollon].
#2 Updated by Vangelis Koukis over 12 years ago
- Category set to logic
#3 Updated by Giorgos Gousios over 12 years ago
- Status changed from New to Assigned
- % Done changed from 0 to 10
Έχει ξεκινήσει σχετική συζήτηση στη σελίδα On queues and architecture
#4 Updated by Giorgos Gousios over 12 years ago
- % Done changed from 10 to 90
Αντιγράφω εδώ το email που έστειλα στη λίστα για μελλοντική αναφορά
Κυρίως για τους παρακάτω λόγους, αποφασίστηκε ότι πρέπει να αλλάξουμε την ουρά που χρησιμοποιεί το σύστημα:
-Το 0mq δεν είναι many to many αλλά one to many. Αυτό δημιουργεί επιπλέον πολυπλοκότητα στο deployment (και ίσως στη ανάπτυξη), σε περίπτωση που θα είχαμε πάνω από μια πηγές μηνυμάτων (που με τα hooks που έχει γράψει ο Β ήδη έχουμε)
-Το 0mq δεν υποστηρίζει κανενός είδους resilience, και λόγω του ότι τρέχει κατευθείαν ως μέρος του producer, η ουρά μηνυμάτων είναι ακόμη ποιο εκθετιμένη. Αν πέσει για οποιαδήποτε λόγο ο producer, χάνουμε όλα τα μηνύματα, ενώ αν πέσει ο οποιοσδήποτε consumer χάνουμε το μήνυμα που επεξεργάζεται εκείνη τη στιγμή.
Αφού εξέτασα αρκετές διαθέσιμες λύσεις, τόσο ampq-based (openampq, rabbitmq) όσο και ανεξάρτητες (beanstalk, kestrel), καταλήξαμε στο RabbitMQ, το ίδιο σύστημα που προτείνει και το OpenStack. Λόγω του RabbitMQ, η αρχιτεκτονική του συστήματος αλλάζει ως εξής:
-Η ουρά τρέχει κεντρικά σε passive/active mode με αυτόματο failover. Γενικά, τι θεωρούμε 99% αξιόπιστη, εκτός αν υπάρχει κάποιο bug στον κώδικα του RabbitMQ ή στην ενδιάμεση υποδομή.
-Όλοι οι producers γράφουν σε κάποιο από τα 3 διαθέσιμα exchnages:
--API: για μηνύματα που προέρχονται από το API (πχ emails, vm requests που δεν επιστρέφουν)
--Ganeti: για μηνύματα που προέρχονται από το Ganeti (κυρίως VM lifecycle events)
--Cron: Περιοδικά εκτελούμενες διεργασίες (πχ charging)
-Όλα τα μηνύματα, εκτός του περιεχομένου τους (που πρέπει να είναι JSON-encoded), έχουν ένα routing key που καθορίζει τον τύπο του μηνύματος. Το routing key μοιάζει κάπως έτσι: api.email.activation.ok, cron.credits.gousiosg.charge, ganeti.snf.event.reboot. Μελλοντικά, καλό είναι να τεκμηριώσουμε τα μηνύματα κάπου κεντρικά
-Οι ουρές ορίζονται με βάση εκφράσεις που "επιλέγουν" μηνύματα από κάποιο exchnage με βάση το routing key. Για παράδειγμα, η ουρά emails (api:"*.email.*", cron:"*.email.*") περιλαμβάνει όλα τα μηνύματα που έχουν το keyword email στο routing key τους ενώ η ουρά debug (api:"#", ganeti:"#") περιλαμβάνει όλα τα μηνύματα από τα exchnages api, ganeti. Φυσικά, ένα μηνύμα μπορεί να πάει σε πάνω από 1 ουρές, πχ το μήνυμα με routing key cron.email.creditsexpired θα εμφανιστεί τόσο στην ουρά debug όσο και στην emails.
-O consumer συνδέει ένα handler σε κάθε ουρά, και ειδοποιείται όταν κάποιο μήνυμα φτάσει. Ένα μήνυμα μπορεί να πάει μόνο σε ένα consumer και χρειάζεται επιβεβαίωση για να βγει τελειώς από την ουρά. Σε περίπτωση που αυτή δεν παραληφθεί λόγω κάποιου λάθους στο σύστημα, το RabbitMQ το επαναπροωθεί.
-Όλη η επικοινωνία μεταξύ producer και consumer είναι υποχρεωτικά ασύγχρονη.
-Τα ganeti-0mqd και /db/db_controller έχουν μετονομαστεί σε ganeti-eventd και /logic/dispatcher, ενώ callbacks για τον dispatcher βρίσκονται στο /logic/dispatcher_callbacks. Ο ορισμός των ουρών και των exchanges γίνεται στην πλευρά του client (δλδ στο dispatcher) ενώ το configuration είναι τελείως δυναμικό (δείτε το settings.py.dist)
Λόγω των παραπάνω αλλαγών η διαδικασία του development έχει κάποιες μικρές αλλαγές, τις εξής:
-Η ουρά είναι πλεον unicast και όχι multicast. Αυτό σημαίνει ότι αν ένας client επεξεργαστεί ένα μήνυμα κανένας άλλος client δεν θα το δει. Για να λαμβάνει μόνο τα μηνύματα από το ganeti που αφορούν τα VMs που έχει ορίσει ο ίδιος (και όχι μηνύματα που αφορούν άλλα VMs), κάθε developer θα πρέπει να φτιάξει μια ουρά που διαβάζει από ganeti exchange τα μηνήματα που περιλαμβάνουν το routing key που αφορά το instance που τρέχει. Καθώς τα instances ονομάζονται πάντα με βάση το BACKEND_PREFIX_ID (πχ gousios-1, gousiosg-04), επιλέχθηκε τα μηνύματα από το ganeti να έχουν την εξής μορφή:
ganeti.prefix.*.* όπου prefix το BACKEND_PREFIX_ID χωρίς την παύλα.
για παράδειγμα,
ganeti.gousios.event.shutdown
ganeti.vkoukis.email.slavedriver (:-))
Για να φτιάξει κάποιος μια τέτοια ουρά, θα πρέπει να κάνει append στο τοπικό settings.py κάτι σαν τα παρακάτω:
DISPATCHER_LOG_FILE = "synnefo.log"
BACKEND_PREFIX_ID = "gousiosg-"
DB_HANDLER_KEY = 'ganeti.%s.#' % BACKEND_PREFIX_ID.split('-')[0]
BINDINGS0 = ("events-%s" % BACKEND_PREFIX_ID.split('-')[0], EXCHANGE_GANETI, DB_HANDLER_KEY, 'update_db')
Μένει ακόμη κάποια δουλειά πριν πούμε ότι το σύστημα είναι 100% λειτουργικό:
-Καλύτερος/ποιο λεπτομερής χειρισμός προβλημάτων στο dispatcher
-Το dispatcher πρέπει να μετατραπεί για να υποστηρίζει multiple processes
-Καλύτερο configuration και logging
-Αναλυτικότερη τεκμηρίωση
PS: Δείτε μια σύντομη περιγραφή του AMPQ (με τον τρόπο που το χρησιμοποιούμε) στο παρακάτω link
http://www.rabbitmq.com/tutorials/tutorial-five-python.html
Επίσης, δείτε και αυτό
http://code.grnet.gr/projects/synnefo/wiki/On_queues_and_architecture
#5 Updated by Vangelis Koukis over 12 years ago
- Status changed from Assigned to Closed
Ολοκληρώθηκε η μετάβαση του συστήματος messaging σε AMQP, με RabbitMQ, δυνητικά σε HA configuration με shared (δυνητικά DRBD) storage.
Το logic layer τρέχει όλο στο context του logic/dispatcher.py, δυνητικά σε περισσότερες της μίας διεργασίες, σε περισσότερους του ενός κόμβους.
Ο ganeti-0mqd αντικαταστάθηκε από τον ganeti-eventd που εκδίδει ενημερώσεις σε ουρές AMQP.
Έχει ενσωματωθεί γενικό Ganeti hook [αυτή τη στιγμή τρέχει στη φάση post start ενός instance], για την έκδοση ενημερώσεων. (#397)