61 votes

architecture optimale pour une application multitenant sur django

J'ai réfléchi à la manière correcte/optimale de créer une application de multilocation basée sur Django. sur Django.

Une explication :

  • L'application peut être utilisée par plusieurs locataires (locataire1, locataire2, ...,).

  • Toutes les données individuelles des locataires doivent être protégées contre l'accès d'autres locataires (et de leurs utilisateurs).

  • En option, les locataires peuvent créer des champs personnalisés supplémentaires pour les objets d'application.

  • Bien sûr, le matériel sous-jacent limite le nombre de locataires sur un "système".

1) Séparation de chaque locataire, par exemple par sous-domaine, et utilisation de bases de données spécifiques aux locataires dans la couche sous-jacente.

2) Utilisation d'un identifiant de locataire dans le modèle pour séparer les données des locataires dans la base de données.

Je pense aux processus de déploiement, aux performances des parties du système (serveur(s) web, serveur(s) de base de données, nœud(s) de travail,...).

Quelle serait la meilleure configuration ? Quels sont les avantages et les inconvénients ?

Qu'en pensez-vous ?

56voto

Reto Aebersold Points 6144

Nous avons construit un système de multilocation plateforme en utilisant l'architecture suivante. J'espère que vous y trouverez des conseils utiles.

  • Chaque locataire obtient un sous-domaine (t1.example.com).
  • En utilisant la réécriture d'url, les requêtes pour l'application Django sont réécrites en quelque chose comme exemple.com/t1.
  • Toutes les définitions d'url sont préfixées par quelque chose comme (r'^(?P<tenant_id>[\w\-]+)
  • A intergiciel traite et consomme le tenant_id et l'ajoute à la demande (par exemple, request.tenant = 't1')
  • Maintenant vous avez le locataire actuel disponible dans chaque vue sans spécifier l'argument tenant_id dans chaque vue.
  • Dans certains cas, vous ne disposez pas de la demande. J'ai résolu ce problème en liant le tenant_id au thread en cours (de manière similaire à la fonction langue actuelle en utilisant threading.local )
  • Créer des décorateurs (par exemple un locataire conscient) login_required ), des middlewares ou des factories pour protéger les vues et sélectionner les bons modèles
  • En ce qui concerne les bases de données, j'ai utilisé deux scénarios différents :
    • Installer plusieurs bases de données et configurer un routage selon le locataire actuel. J'ai d'abord utilisé ce système, mais je suis passé à une seule base de données après environ un an. Les raisons étaient les suivantes :
      • Nous n'avions pas besoin d'une solution hautement sécurisée pour séparer les données.
      • Les différents locataires ont utilisé presque tous les mêmes modèles
      • Nous devions gérer un grand nombre de bases de données (et n'avions pas mis en place un processus de mise à jour/migration facile).
    • Utiliser une base de données avec quelques tables de correspondance simples, par exemple pour les utilisateurs et les différents modèles. Pour ajouter des champs supplémentaires et spécifiques au modèle du locataire, nous utilisons la méthode suivante l'héritage du modèle .

En ce qui concerne l'environnement, nous utilisons la configuration suivante :

De mon point de vue, cette configuration présente les avantages et les inconvénients suivants :

Pro :

  • Une instance d'application connaissant le locataire actuel
  • La plupart des parties du projet ne doivent pas s'occuper de questions spécifiques aux locataires.
  • Solution facile pour partager des entités entre tous les locataires (par exemple, des messages)

Contra :

  • Une base de données assez importante
  • Certains tableaux très similaires en raison de l'héritage du modèle
  • Non sécurisé au niveau de la couche de base de données

Bien sûr, la meilleure architecture dépend fortement de vos besoins, comme le nombre de locataires, le delta de vos modèles, les exigences de sécurité, etc.

Mise à jour : Comme nous avons revu notre architecture, je suggère de no réécrire l'URL comme indiqué au point 2-3. Je pense qu'une meilleure solution est de mettre le tenant_id en tant qu'en-tête de la demande et extraire (point 4) l'en-tête de la demande. tenant_id de la demande avec quelque chose comme request.META.get('TENANT_ID', None) . De cette façon, vous obtenez des URLs neutres et il est beaucoup plus facile d'utiliser les fonctions intégrées de Django (par exemple {% url ...%} o reverse() ) ou des applications externes.

4voto

akaihola Points 10007

Voici quelques pointeurs vers des discussions connexes :

1voto

Clash Points 1091

Je vous recommande de jeter un coup d'œil à https://github.com/bcarneiro/django-tenant-schemas . Il résoudra vos problèmes un peu comme Reto l'a mentionné, sauf qu'il utilise les schémas postgresql.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X