Atteindre le déploiement zéro temps d'arrêt

40

J'essaie de réaliser des déploiements sans temps d'arrêt afin de pouvoir en déployer moins pendant les heures creuses et davantage 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 les serveurs 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. Ramener tout en ligne

Problème actuel

Cela conduit à une petite quantité de temps d'arrêt chaque mois - environ 30 minutes. Je le fais en dehors des heures de travail, donc ce n’est pas un problème énorme, mais c’est quelque chose que j’aimerais abandonner.

En outre, il n'y a aucun moyen de vraiment revenir en arrière. En général, je ne fais pas de scripts d'annulation de base de données, mais uniquement des scripts de mise à niveau.

Tirer parti de l’équilibreur de charge

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

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

Solution possible

Une solution actuelle que je considère envisage 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 jamais renommer 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 la modification.
    • Il devient ensuite 'spFindAllThings_3' lors de sa prochaine édition.
    • 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 utilisera la base de données de manière ininterrompue. Le code attend certains résultats des vues / procédures stockées, ce qui permet de conserver la validité de ce "contrat". Le problème est - ça suinte mouillée. 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 tout simplement sale. En outre, cela dépend du fait que tous les développeurs suivent cette règle, ce qui arrivera surtout, mais j'imagine que quelqu'un va se tromper.

Enfin - ma question

  • Est-ce bâclé ou hacky?
  • Est-ce que quelqu'un d'autre le fait de cette façon?
  • Comment d'autres personnes résolvent-elles ce problème?
MattW
la source
2
Où est votre plan de backout? Comment testez-vous que tout fonctionne et qu'il n'y a pas de régression?
Deer Hunter
3
Vous n'avez pas besoin de "jamais": vous "seulement" devez vous assurer que toutes les deux versions adjacentes peuvent être exécutées simultanément. Cela limite vos chemins de mise à niveau, mais pas autant que jamais car vous ne pouvez jamais modifier le schéma de base de données de manière significative.
Joachim Sauer
Merci Joachim ... J'aime parler en absolu pour que l'idée de base soit claire - mais vous avez raison, nous pourrions avoir une politique de compatibilité ascendante pour N versions, à quel point nous pouvons supprimer les objets de base de données inutiles.
MattW
2
Vous voudrez avoir un plan de restauration en place. Un jour, vous en aurez besoin.
Thorbjørn Ravn Andersen
1
D'après mon expérience, pour la plupart des sites Web, votre solution possible est pire que le problème qu'elle résout. La complexité que cela ajoutera sera plus coûteuse que vous ne pouvez l’attendre maintenant. Peut-être beaucoup plus de temps / d'efforts pour apporter des modifications et ajouter des fonctionnalités. Je ne le considère pour les sites web qui ne peut absolument pas tout avoir des temps d' arrêt, jamais .
MGOwen

Réponses:

14

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

Je peux comprendre ce que vous voulez dire quand vous dites que cela semble négligé, mais lorsque cela est fait intentionnellement et avec prévoyance, et que vous prenez le temps de modifier les structures inutilisées de la base de code et de la base de données quand elles ne sont plus utilisées, il est beaucoup plus robuste que des solutions plus simples basées sur des scripts de mise à niveau et d'annulation.

Mike Partridge
la source
5

Le "zéro temps d'arrêt" n'est qu'une des nombreuses raisons possibles de ce type d'approche. Garder un modèle de données compatible avec les versions antérieures de cette manière vous aide à faire face à de nombreux problèmes différents:

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

  • si vous le devez, vous pouvez extraire une ancienne version de l’un de vos programmes et il exécutera probablement une nouvelle base de données (tant que vous ne vous attendez pas à ce que l’ancien programme gère correctement les colonnes les plus récentes).

  • 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 implicite significative

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

Bien entendu, cette approche présente un inconvénient majeur: 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 les refactoriser facilement lorsque vous allez, par exemple, renommer une colonne, vous pouvez envisager de refactoriser les éléments de manière plus propre.

Doc Brown
la source
4

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

Bien sûr, vous ne pouvez jamais supprimer une table ou une colonne. Vous ne pouvez jamais changer quoi que ce soit qui a brisé la compatibilité de l'interface. Vous pouvez toujours ajouter une couche d'abstraction. Mais alors vous devez version 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 changent le schéma de cette manière, le problème de la base de données reste muet. Il suffit de faire un déploiement continu des serveurs d'applications.

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

  1. Recherchez une compatibilité ascendante - au moins dans une seule version. Vous ne le réaliserez pas toujours, mais je peux parier que vous pouvez le faire avec 90% ou plus de vos publications, en particulier si chaque publication est petite.
  2. Avoir un script de base de données pré-release et post-release. 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 en tant que Nullable dans votre script préliminaire avec un déclencheur qui remplit une valeur par défaut. Ensuite, dans votre post-release, vous pouvez laisser tomber la gâchette.

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

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

  • Pouvez-vous intégrer une compatibilité ascendante dans votre code? Par exemple, votre code peut-il prendre en charge plusieurs types d’ensembles de résultats? Si vous devez modifier une colonne d'un int à un double, le code de votre application peut la lire comme une chaîne et l'analyser. Un peu comme un hacky, mais s'il s'agit d'un code temporaire pour vous aider à traverser le processus de publication, il se peut que ce ne soit pas la fin du monde.
  • Les procédures stockées peuvent aider à isoler votre code d'application des modifications de schéma. Cela ne peut aller aussi loin, mais aide un peu.
Brandon
la source
2

Vous pourriez potentiellement le faire comme ceci 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, par exemple myDb_2_1.
  3. Exécuter la publication 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 post-implémentation et effectuer une restauration si nécessaire
  6. Session saignez le serveur Web B et mettez le serveur Web A en boucle
  7. Mettez à niveau le serveur Web B, puis remettez-le dans l'équilibreur de charge.

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

Leo Lambrettra
la source
1
Que se passe-t-il si (sur) l'application sur le serveur A écrit dans sa base de données après avoir stocké la sauvegarde, mais avant d'avoir arrêté le serveur A? Il y a toujours une "fenêtre de vulnérabilité". Ces écritures seront perdues, ce qui pourrait ne pas être acceptable.
Sleske