Citymobil - un manuel pour améliorer la disponibilité au milieu de la croissance des entreprises pour les startups. 3e partie



Ceci est le prochain article de la série décrivant comment nous augmentons la disponibilité de nos services dans Citymobil (vous pouvez lire les parties précédentes ici et ici ). Dans d'autres parties, je parlerai des accidents et des pannes en détail. Mais permettez-moi d'abord de souligner quelque chose dont j'aurais dû parler dans le premier article mais que je n'ai pas fait. Je l'ai découvert grâce aux commentaires de mes lecteurs. Cet article me donne la chance de corriger cette lacune gênante.

1. Prologue


Un lecteur m'a posé une question très juste: "Qu'est-ce qui complique le backend du service de grêle?" Voilà une bonne question. L'été dernier, je me suis posé cette question avant de commencer à travailler chez Citymobil. Je pensais: "c'est juste un service de taxi avec son application à trois boutons." Cela pourrait-il être difficile? C'est devenu un produit de très haute technologie. Pour clarifier un peu ce dont je parle et ce qu'est une énorme technologie, je vais vous parler de quelques directions de produits chez Citymobil:

  • Tarification. Notre équipe de tarification s'occupe du problème du meilleur prix de trajet à chaque instant et à chaque instant. Le prix est déterminé par la prévision de l'équilibre entre l'offre et la demande sur la base de statistiques et d'autres données. Tout cela est fait par un service complexe et en constante évolution basé sur l'apprentissage automatique. L'équipe de tarification s'occupe également de la mise en œuvre de diverses méthodes de paiement, des frais supplémentaires à la fin d'un voyage, des rétrofacturations, de la facturation, de l'interaction avec les partenaires et les chauffeurs.
  • Envoi des commandes. Quelle voiture complète la commande du client? Par exemple, une option de choisir le véhicule le plus proche n'est pas la meilleure en termes de maximisation d'un certain nombre de trajets. La meilleure option est de faire correspondre les voitures et les clients afin de maximiser le nombre de voyages en tenant compte d'une probabilité que ce client spécifique annule sa commande dans ces circonstances spécifiques (car l'attente est trop longue) et une probabilité que ce conducteur spécifique annule ou sabote la commande ( par exemple parce que la distance est trop grande ou le prix est trop petit).
  • Geo. Tout sur la recherche et la suggestion d'adresses, les points de ramassage, les ajustements de l'heure d'arrivée estimée (nos partenaires de fourniture de cartes ne nous fournissent pas toujours des informations ETA précises en tenant compte du trafic), l'augmentation de la précision du géocodage direct et inverse, l'augmentation de la précision du point d'arrivée de la voiture. Il y a beaucoup de données, beaucoup d'analyses, beaucoup de services basés sur l'apprentissage automatique.
  • Antifraud La différence de coût de voyage pour un passager et un chauffeur (par exemple, pour de courts trajets) crée une incitation économique pour les intrus qui tentent de voler notre argent. La lutte contre la fraude est quelque peu similaire à la gestion du courrier indésirable - la précision et le rappel sont très importants. Nous devons bloquer le nombre maximum de fraudes (rappel), mais en même temps, nous ne pouvons pas prendre de bons utilisateurs pour des fraudes (précision).
  • L'équipe des incitations au conducteur supervise le développement de tout ce qui peut augmenter l'utilisation de notre plate-forme par les conducteurs et la fidélité des conducteurs en raison de différents types d'incitations. Par exemple, effectuez X voyages et obtenez de l'argent Y supplémentaire. Ou achetez un quart de travail pour Z et conduisez sans commission.
  • Backend de l'application pilote. Liste des commandes, carte de la demande (elle montre à un chauffeur où aller pour maximiser ses profits), changements de statut, système de communication avec les chauffeurs et plein d'autres choses.
  • Backend de l'application client (c'est probablement la partie la plus évidente et c'est ce que les gens appellent généralement "taxi backend"): placement de commande, informations sur l'état de la commande, fournir le mouvement des petites voitures sur la carte, conseils backend, etc.

Ce n'est que la pointe de l'iceberg. Il y a beaucoup plus de fonctionnalités. Il y a une énorme partie sous-marine de l'iceberg derrière ce qui semble être une interface assez simple.

Et maintenant revenons aux accidents. L'enregistrement de l'historique des accidents pendant six mois a donné le classement suivant:

  • mauvaise version: 500 erreurs internes du serveur;
  • mauvaise version: surcharge de la base de données;
  • interaction malheureuse de fonctionnement manuel du système;
  • Oeufs de Pâques;
  • raisons externes;
  • mauvaise version: fonctionnalité cassée.

Ci-dessous, je vais détailler les conclusions que nous avons tirées concernant nos types d'accidents les plus courants.

2. Mauvaise version: 500 erreurs de serveur internes


Notre backend est principalement écrit en PHP - un langage interprété faiblement typé. Nous publierions un code qui s'est écrasé en raison de l'erreur dans le nom de classe ou de fonction. Et ce n'est qu'un exemple lorsque l'erreur 500 se produit. Cela peut également être dû à une erreur logique dans le code; la mauvaise branche a été libérée; le dossier contenant le code a été supprimé par erreur; les artefacts temporaires nécessaires aux tests ont été laissés dans le code; la structure des tables n'a pas été modifiée selon le code; les scripts cron nécessaires n'ont pas été redémarrés ou arrêtés.

Nous abordions progressivement cette question par étapes. Les trajets perdus à cause d'une mauvaise version sont évidemment proportionnels à son temps de production. Par conséquent, nous devons faire de notre mieux et nous assurer de minimiser le temps de mauvaise production en production. Tout changement dans le processus de développement qui réduit le temps moyen de mauvais fonctionnement de la version même d'une seconde est bon pour les affaires et doit être mis en œuvre.

Une mauvaise libération et, en fait, tout accident de production a deux états que nous avons nommés "une étape passive" et "une étape active". Pendant la phase passive, nous ne sommes pas encore au courant d'un accident. La phase active signifie que nous savons déjà. Un accident commence au stade passif; avec le temps, il entre dans la phase active - c'est là que nous le découvrons et que nous commençons à y remédier: nous le diagnostiquons d'abord, puis - le réparons.

Pour réduire la durée de toute interruption, nous devons réduire la durée des étapes actives et passives. La même chose vaut pour une mauvaise version car elle est considérée comme une sorte de panne.

Nous avons commencé à analyser l'historique du dépannage des pannes. Les mauvaises versions que nous avons connues lorsque nous avons commencé à analyser les accidents ont causé en moyenne 20 à 25 minutes d'arrêt (complet ou partiel). La phase passive prend généralement 15 minutes et la phase active - 10 minutes. Au cours de la phase passive, nous recevions les plaintes des utilisateurs qui étaient traitées par notre centre d'appels; et après un certain seuil, le centre d'appels se plaindrait dans un chat Slack. Parfois, l'un de nos collègues se plaignait de ne pas pouvoir prendre de taxi. La plainte du collègue signalerait un problème grave. Après qu'une mauvaise version soit entrée dans la phase active, nous avons commencé le diagnostic du problème, en analysant les versions récentes, divers graphiques et journaux afin de trouver la cause de l'accident. Après avoir déterminé les causes, nous annulions si la mauvaise version était la dernière ou nous effectuions un nouveau déploiement avec la validation annulée.

Il s'agit du mauvais processus de gestion des versions que nous étions censés améliorer.

Étape passive: 20 minutes.
Étape active: 10 minutes.

3. Réduction du stade passif


Tout d'abord, nous avons remarqué que si une mauvaise version s'accompagnait de 500 erreurs, nous pouvions dire qu'un problème s'était produit même sans que les utilisateurs se plaignent. Heureusement, les 500 erreurs ont été enregistrées dans New Relic (c'est l'un des systèmes de surveillance que nous utilisons) et tout ce que nous avions à faire était d'ajouter des notifications SMS et IVR sur le dépassement d'un nombre spécifique de 500 erreurs. Le seuil serait abaissé en continu au fil du temps.

Le processus en cas d'accident ressemblerait à ceci:

  1. Un ingénieur déploie une version.
  2. La libération entraîne un accident (quantité massive de 500s).
  3. Un message texte est reçu.
  4. Les ingénieurs et les devops commencent à s'y intéresser. Parfois pas tout de suite mais en 2-3 minutes: le message texte peut être retardé, les sons du téléphone peuvent être coupés; et bien sûr, l'habitude d'une réaction immédiate à la réception de ce texte ne peut pas se former du jour au lendemain.
  5. La phase active de l'accident commence et dure les mêmes 10 minutes qu'avant.

Par conséquent, l'étape active du type d'accident "Mauvaise version: 500 erreurs de serveur interne" commencerait 3 minutes après une version. Par conséquent, la phase passive a été réduite de 15 minutes à 3.

Résultat:

Étape passive: 3 minutes.
Étape active: 10 minutes.

4. Réduction supplémentaire d'une phase passive


Même si la phase passive avait été réduite à 3 minutes, cela nous dérangeait plus qu'actifs car pendant la phase active, nous faisions quelque chose pour résoudre le problème, et pendant la phase passive, le service était totalement ou partiellement en panne, et nous étaient absolument désemparés.

Pour réduire davantage la phase passive, nous avons décidé de sacrifier 3 minutes du temps de nos ingénieurs après chaque sortie. L'idée était très simple: nous déployions du code et pendant trois minutes après, nous recherchions 500 erreurs dans New Relic, Sentry et Kibana. Dès que nous avons vu un problème, nous supposions qu'il était lié au code et avons commencé le dépannage.

Nous avons choisi cette période de trois minutes sur la base de statistiques: parfois les problèmes sont apparus dans les graphiques en 1-2 minutes, mais jamais plus tard qu'en 3 minutes.

Cette règle a été ajoutée aux choses à faire et à ne pas faire. Au début, ce n'était pas toujours suivi, mais au fil du temps, nos ingénieurs se sont habitués à cette règle comme ils l'ont fait à l'hygiène de base: se brosser les dents le matin prend aussi un certain temps, mais c'est toujours nécessaire.

En conséquence, la phase passive a été réduite à 1 minute (les graphiques étaient encore parfois en retard). Il a également réduit la phase active comme un joli bonus. Parce que maintenant, un ingénieur ferait face au problème préparé et serait prêt à restaurer son code immédiatement. Même si cela n'a pas toujours aidé, car le problème aurait pu être causé par une version déployée simultanément par quelqu'un d'autre. Cela dit, la scène active en moyenne réduite à cinq minutes.

Résultat:

Étape passive: 1 minute.
Étape active: 5 minutes.

5. Réduction supplémentaire d'une phase active


Nous nous sommes plus ou moins satisfaits de l'étape passive d'une minute et avons commencé à réfléchir à la façon de réduire davantage une étape active. Tout d'abord, nous avons concentré notre attention sur l'histoire des pannes (il se trouve que c'est la pierre angulaire d'un bâtiment de notre disponibilité!) Et nous avons découvert que dans la plupart des cas, nous ne restituons pas immédiatement une version puisque nous ne savons pas quelle version choisir: il existe de nombreuses versions parallèles. Pour résoudre ce problème, nous avons introduit la règle suivante (et l'avons écrite dans les choses à faire et à ne pas faire): juste avant une version, il faut informer tout le monde dans une conversation Slack de ce que vous êtes sur le point de déployer et pourquoi; en cas d'accident, il faut écrire: "Accident, ne pas déployer!" Nous avons également commencé à informer ceux qui ne lisent pas le chat des versions via SMS.

Cette règle simple a considérablement réduit le nombre de rejets lors d'un accident en cours, réduit la durée du dépannage et réduit la phase active de 5 minutes à 3.

Résultat:

Étape passive: 1 minute.
Étape active: 3 minutes.

6. Réduction encore plus importante d'une phase active


Malgré le fait que nous ayons affiché des avertissements dans le chat concernant toutes les versions et les accidents, des conditions de course se produisaient parfois - quelqu'un a publié une version et un autre ingénieur se déployait à ce moment précis; ou un accident s'est produit, nous en avons parlé dans le chat, mais quelqu'un venait de déployer son code. De telles circonstances ont prolongé le dépannage. Afin de résoudre ce problème, nous avons implémenté l'interdiction automatique des versions parallèles. C'était une idée très simple: pendant 5 minutes après chaque version, le système CI / CD interdit un autre déploiement pour quiconque sauf l'auteur de la dernière version (afin qu'elle puisse annuler ou déployer le correctif si nécessaire) et plusieurs développeurs expérimentés (dans cas d'urgence). De plus, le système CI / CD empêche les déploiements en cas d'accident (c'est-à-dire à partir du moment où la notification du début de l'accident arrive et jusqu'à l'arrivée de la notification de sa fin).

Ainsi, notre processus a commencé à ressembler à ceci: un ingénieur déploie une version, surveille les graphiques pendant trois minutes, puis personne ne peut rien déployer pendant deux minutes supplémentaires. En cas de problème, l'ingénieur annule la version. Cette règle a considérablement simplifié le dépannage et la durée totale des étapes actives et passives est passée de 3 + 1 = 4 minutes à 1 + 1 = 2 minutes.

Mais même un accident de deux minutes, c'était trop. C'est pourquoi nous avons continué à travailler sur l'optimisation de nos processus.

Résultat:

Stade passif: 1 minute.
Stade actif: 1 minute.

7. Détermination automatique des accidents et restauration


Nous réfléchissions depuis un certain temps à la façon de réduire la durée des accidents causés par de mauvaises rejets. Nous avons même essayé de nous forcer à examiner tail -f error_log | grep 500 tail -f error_log | grep 500 . Mais au final, nous avons opté pour une solution automatique drastique.

En un mot, c'est une restauration automatique. Nous avons un serveur Web distinct et l'avons chargé via l'équilibreur 10 fois moins que le reste de nos serveurs Web. Chaque version serait automatiquement déployée par les systèmes CI / CD sur ce serveur séparé (nous l'avons appelé préprod, mais malgré son nom, il recevrait une charge réelle des vrais utilisateurs). Le script exécuterait alors tail -f error_log | grep 500 tail -f error_log | grep 500 . Si en moins d'une minute il n'y avait pas d'erreur 500, CI / CD déploierait la nouvelle version en production sur d'autres serveurs Web. En cas d'erreurs, le système a tout annulé. Au niveau de l'équilibreur, toutes les demandes entraînant 500 erreurs sur le préprod seraient renvoyées sur l'un des serveurs Web de production.

Cette mesure a réduit à zéro l'impact des 500 erreurs de publication. Cela dit, juste en cas de bogues dans les contrôles automatiques, nous n'avons pas aboli notre règle de surveillance graphique de trois minutes. C'est tout sur les mauvaises versions et 500 erreurs. Passons au prochain type d'accident.

Résultat:

Étape passive: 0 minute.
Stade actif: 0 minutes.



Dans d'autres parties, je vais parler d'autres types de pannes dans l'expérience Citymobil et entrer dans les détails de chaque type de panne; Je vais également vous parler des conclusions que nous avons tirées sur les pannes, comment nous avons modifié le processus de développement, quelle automatisation nous avons introduite. Restez à l'écoute!

Source: https://habr.com/ru/post/fr449708/


All Articles