web-dev-qa-db-fra.com

Atteindre un déploiement sans interruption de service

J'essaie de réaliser des déploiements sans temps d'arrêt afin de pouvoir déployer moins pendant les heures creuses et plus pendant les heures "plus lentes" - ou à tout moment, en théorie.

Ma configuration actuelle, quelque peu simplifiée:

  • Serveur Web A (application .NET)
  • Serveur Web B (application .NET)
  • Serveur de base de données (SQL Server)

Mon processus de déploiement actuel:

  1. "Arrêtez" les sites sur le serveur Web A et B
  2. Mettre à niveau le schéma de base de données pour la version de l'application en cours de déploiement
  3. Mettre à jour le serveur Web A
  4. Mettre à jour le serveur Web B
  5. Remettez tout en ligne

Problème actuel

Cela conduit à une petite durée d'indisponibilité chaque mois - environ 30 minutes. Je le fais pendant les heures creuses, donc ce n'est pas un gros problème - mais c'est quelque chose que j'aimerais éviter.

De plus, il n'y a aucun moyen de vraiment revenir en arrière. Je ne fais généralement pas de restauration de scripts DB - seulement des scripts de mise à niveau.

Tirer parti de l'équilibreur de charge

J'adorerais pouvoir mettre à niveau un serveur Web à la fois. Retirez le serveur Web A de l'équilibreur de charge, mettez-le à niveau, remettez-le en ligne, puis répétez l'opération pour le serveur Web B.

Le problème est la base de données. Chaque version de mon logiciel devra s'exécuter sur une version différente de la base de données - je suis donc en quelque sorte "bloqué".

Solution possible

Une solution actuelle que j'envisage est d'adopter les règles suivantes:

  • Ne supprimez jamais une table de base de données.
  • Ne supprimez jamais une colonne de base de données.
  • Ne renommez jamais une colonne de base de données.
  • Ne réorganisez jamais une colonne.
  • Chaque procédure stockée doit être versionnée.
    • Signification - 'spFindAllThings' deviendra 'spFindAllThings_2' lors de sa modification.
    • Il devient alors "spFindAllThings_3" lorsqu'il est à nouveau modifié.
    • La même règle s'applique aux vues.

Bien que cela semble un peu extrême - je pense que cela résout le problème. Chaque version de l'application atteindra la base de données sans interruption. Le code attend certains résultats des vues/procédures stockées - et cela maintient ce "contrat" valide. Le problème est - il suinte simplement bâclé. Je sais que je peux nettoyer les anciennes procédures stockées après le déploiement de l'application pendant un certain temps, mais cela semble juste sale. En outre - cela dépend de tous les développeurs qui suivent ces règles, ce qui se produira principalement, mais j'imagine que quelqu'un fera une erreur.

Enfin - ma question

  • Est-ce bâclé ou hacky?
  • Quelqu'un d'autre le fait-il de cette façon?
  • Comment les autres résolvent-ils ce problème?
40
MattW

Il s'agit d'une approche très pragmatique des mises à niveau logicielles basées sur une base de données. Il a été décrit par Martin Fowler et Pramod Sadalage en 2003 et rédigé par la suite dans Refactoring Databases: Evolutionary Database Design .

Je peux voir ce que vous voulez dire lorsque vous dites que cela semble bâclé, mais lorsque cela est fait intentionnellement et avec prévoyance, et en prenant le temps de refactoriser les structures inutilisées de la base de code et de la base de données lorsqu'elles ne sont manifestement plus utilisées, c'est beaucoup plus robuste que des solutions plus simples basées sur des scripts de mise à niveau et de restauration.

14
Mike Partridge

Le "temps d'arrêt zéro" n'est qu'une des nombreuses raisons possibles de ce type d'approche. Garder un modèle de données rétrocompatible de cette façon vous aide à faire face à de nombreux problèmes différents:

  • si vous avez beaucoup de progiciels accédant à votre base de données, vous n'aurez pas à tous les vérifier si un changement de schéma les affecte (dans les grandes organisations avec plusieurs équipes écrivant des programmes accédant tous à la même base de données, les changements de schéma peuvent devenir très difficiles)

  • si vous le devez, vous pouvez consulter une ancienne version de l'un de vos programmes et elle s'exécutera probablement contre une base de données plus récente (tant que vous ne vous attendez pas à ce que l'ancien programme gère correctement les nouvelles colonnes)

  • l'importation/exportation de données archivées dans la version actuelle de la base de données est beaucoup plus facile

Voici une règle supplémentaire pour votre liste

  • chaque nouvelle colonne doit être NULLable ou fournir une valeur par défaut significative

(cela garantit que même les programmes plus anciens qui ne connaissent pas les nouvelles colonnes ne casseront rien lorsqu'ils créent de nouveaux enregistrements dans votre base de données).

Bien sûr, cette approche a un réel inconvénient: la qualité de votre modèle de données peut se dégrader avec le temps. Et si vous avez un contrôle total sur toutes les applications accédant à votre base de données, et que vous pouvez facilement refactoriser toutes ces applications lorsque vous, par exemple, allez renommer une colonne, alors vous pourriez envisager de refactoriser les choses de manière plus propre.

5
Doc Brown

Cela varie d'un déploiement à l'autre.

Bien sûr, vous ne pouvez jamais supprimer un tableau ou une colonne. Vous ne pourriez jamais rien changer à la compatibilité de l'interface. Vous pouvez toujours ajouter une couche d'abstraction. Mais alors vous devez versionner cette abstraction et la version le versioning.

La question que vous devez vous poser est la suivante: chaque version modifie-t-elle le schéma de manière à ce qu'il ne soit pas rétrocompatible?

Si très peu de versions modifient le schéma de cette manière, le problème de base de données est alors muet. Effectuez simplement un déploiement continu des serveurs d'applications.

Les deux choses que j'ai vues aider le plus avec un déploiement à temps d'arrêt minimal sont:

  1. Efforcez-vous de la compatibilité descendante - au moins dans une seule version. Vous ne l'atteindrez pas toujours, mais je parie que vous pouvez l'atteindre sur 90% ou plus de vos versions, surtout si chaque version est petite.
  2. Avoir un script de base de données avant et après la sortie. Cela vous permet de gérer les changements de nom ou d'interface en créant votre nouvel objet avant le déploiement du code de votre application, puis en supprimant l'ancien après le déploiement du code de l'application. Si vous ajoutez une nouvelle colonne non nullable, vous pouvez l'ajouter comme nullable dans votre script de pré-version avec un déclencheur qui remplit une valeur par défaut. Ensuite, dans votre post-version, vous pouvez supprimer le déclencheur.

Espérons que le reste de vos déploiements puisse être enregistré pour les fenêtres de maintenance.

Autres idées pouvant aider à gérer les quelques déploiements qui nécessitent des temps d'arrêt:

  • Pouvez-vous créer une compatibilité descendante dans votre code? Par exemple, existe-t-il un moyen pour votre code de prendre en charge plusieurs types d'ensembles de résultats? Si vous devez changer une colonne d'un int en un double, le code de votre application peut la lire sous forme de chaîne et l'analyser. Un peu hacky, mais si c'est du code temporaire pour passer au travers du processus de sortie, ce n'est peut-être pas la fin du monde.
  • Les procédures stockées peuvent aider à isoler le code de votre application des modifications de schéma. Cela ne peut aller que si loin, mais cela aide un peu.
4
Brandon

Vous pourriez potentiellement le faire comme ça pour un peu d'effort supplémentaire.

  1. Sauvegarder la base de données en effectuant une exportation
  2. Importez la sauvegarde mais renommez-la avec une version finale, par exemple myDb_2_1
  3. Exécuter la version de la base de données sur myDB_2_1
  4. "Arrêtez" le pool d'applications sur le serveur Web A ou retirez-le de l'équilibreur de charge
  5. Mettre à jour le serveur Web A, exécuter des tests de post-implémentation et restaurer si nécessaire
  6. Session purge le serveur Web B et remet le serveur Web A en boucle
  7. Mettez à niveau le serveur Web B, puis réinstallez l'équilibreur de charge

Naturellement, les mises à jour Web auraient besoin de nouvelles entrées de configuration pour pointer vers le nouveau schéma Db. Le problème est que si vous publiez une fois par mois et que c'est une petite équipe, combien de modifications de base de données effectuez-vous qui ne sont pas rétrocompatibles? Si vous pouvez contrôler cela en testant, vous pouvez opter pour un déploiement automatisé sans temps d'arrêt ou, au pire, seulement 5 minutes de temps d'arrêt.

2
LeoLambrettra