Feature #393

Refactoring του business logic σε services

Added by Giorgos Gousios about 11 years ago. Updated about 11 years ago.

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

Πρέπει να γίνουν τα παρακάτω:

  1. Να αποφασιστεί αν μας κάνει το 0mq και αν όχι με τι να το αντικαταστήσουμε
  2. Να οργανώσουμε το σύστημα ώστε να μπορούν τα διάφορα μέρη του να τρέχουν σε πολλά μηχανήματα με ανοχή σε βλάβες

Associated revisions

Revision a7d2ae87
Added by Georgios Gousios about 11 years ago

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

Revision 5d081749
Added by Georgios Gousios about 11 years ago

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

Revision ff55193e
Added by Vangelis Koukis about 11 years ago

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 about 11 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 about 11 years ago

  • Category set to logic

#3 Updated by Giorgos Gousios about 11 years ago

  • Status changed from New to Assigned
  • % Done changed from 0 to 10

Έχει ξεκινήσει σχετική συζήτηση στη σελίδα On queues and architecture

#4 Updated by Giorgos Gousios about 11 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 about 11 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)

Also available in: Atom PDF