
Bonjour Ă tous !Â
Je m'appelle Nikita, je suis le chef d'équipe des ingénieurs chez Cian. L'une de mes responsabilités dans l'entreprise est de réduire le nombre d'incidents liés à l'infrastructure en production à zéro.
Ce dont nous allons parler par la suite nous a causĂ© beaucoup de douleurs, et l'objectif de cet article est d'Ă©viter Ă d'autres de rĂ©pĂ©ter nos erreurs ou tout au moins de minimiser leur impact.Â
Préambule
Il Ă©tait une fois, quand Cian n'Ă©tait constituĂ© que de monolithes, et qu'il n'y avait aucun indice sur les microservices, nous mesurions la disponibilitĂ© des ressources en vĂ©rifiant 3 Ă 5 pages.Â
S'ils rĂ©pondent â tout va bien, s'ils ne rĂ©pondent pas pendant un long moment â alerte. Combien de temps ils doivent ĂȘtre hors service pour que cela soit considĂ©rĂ© comme un incident, cela Ă©tait dĂ©cidĂ© par des personnes lors de rĂ©unions. L'Ă©quipe d'ingĂ©nieurs participait toujours Ă l'enquĂȘte sur l'incident. Lorsque l'enquĂȘte Ă©tait terminĂ©e, un post-mortem Ă©tait rĂ©digĂ© â un rapport adressĂ© par email sous la forme : ce qui s'est passĂ©, combien de temps cela a durĂ©, ce que nous avons fait sur le moment, ce que nous ferons Ă l'avenir.Â
Les pages principales du site ou comment nous savons que nous avons atteint un point critique
Â
Pour pouvoir comprendre la prioritĂ© des erreurs, nous avons identifiĂ© les pages du site les plus critiques pour la fonctionnalitĂ© commerciale. Nous comptons le nombre de requĂȘtes rĂ©ussies/Ă©chouĂ©es et de timeouts sur ces pages. Ainsi, nous mesurons le temps de disponibilitĂ©.Â
Supposons que nous avons dĂ©terminĂ© qu'il y a plusieurs sections super importantes du site qui sont responsables du service principal â la recherche et la soumission d'annonces. Si le nombre de requĂȘtes qui se terminent par une erreur dĂ©passe 1 %, c'est un incident critique. Si pendant 15 minutes durant les heures de pointe, le pourcentage d'erreurs dĂ©passe 0,1 %, cela est Ă©galement considĂ©rĂ© comme un incident critique. Ces critĂšres couvrent la plus grande partie des incidents, les autres dĂ©passent le cadre de cet article.

Top des incidents les plus marquants de Cian
Donc, nous avons certainement appris Ă dĂ©finir le fait qu'un incident s'est produit.Â
Maintenant, chaque incident est dĂ©taillĂ© et reflĂ©tĂ© dans une Ă©popĂ©e Jira. Ă propos : pour cela, nous avons créé un projet sĂ©parĂ©, que nous avons appelĂ© FAIL â oĂč l'on peut uniquement crĂ©er des Ă©popĂ©es.Â
Si nous rassemblons tous les Ă©checs des derniĂšres annĂ©es, nous constatons que les principaux sont :Â
- incidents liés à mssql ;
- incidents causés par des facteurs externes ;
- erreurs d'administration.
Concentrons-nous plus en détail sur les erreurs des administrateurs, ainsi que sur quelques autres échecs intéressants.
CinquiĂšme place â « Mettre de l'ordre dans le DNS »
C'Ă©tait un mardi pluvieux. Nous avons dĂ©cidĂ© de faire le mĂ©nage dans le cluster DNS.Â
Nous avons voulu migrer nos serveurs DNS de BIND vers PowerDNS, en allouant des serveurs entiĂšrement dĂ©diĂ©s Ă cela, oĂč il n'y a rien d'autre que des DNS.Â
Nous avons installĂ© un serveur DNS dans chaque emplacement de nos centres de donnĂ©es, et le moment est venu de migrer les zones de BIND vers PowerDNS et de passer notre infrastructure vers les nouveaux serveurs.Â
Au beau milieu de la migration, tous serveurs, qui étaient indiqués dans les caches locaux de BIND sur tous les serveurs, il ne restait qu'un seul serveur, situé dans le centre de données de Saint-Pétersbourg. Ce DC avait initialement été déclaré non critique pour nous, mais il est soudainement devenu un point de défaillance unique.
Juste Ă ce moment-lĂ , le lien entre Moscou et Saint-PĂ©tersbourg est tombĂ©. Nous avons Ă©tĂ© sans DNS pendant cinq minutes, et nous avons rĂ©cupĂ©rĂ© quand l'hĂ©bergeur les problĂšmes ont Ă©tĂ© rĂ©solus.Â
Conclusions :
Alors qu'auparavant, nous ne tenions pas compte des facteurs externes lors de la préparation des travaux, nous avons désormais inclus ces éléments dans notre liste de préparation. Nous visons désormais à avoir tous les composants redondés en n-2, et pendant la durée des travaux, nous pouvons réduire ce niveau à n-1.
- Lors de l'Ă©laboration du plan d'actions, notez les points oĂč le service pourrait tomber, et planifiez des scĂ©narios oĂč tout pourrait aller "de mal en pis", Ă l'avance.
- Répartissez les serveurs DNS internes dans différentes géolocalisations / centres de données / racks / commutateurs / entrées.
- Sur chaque serveur, installez un serveur DNS de cache local qui redirige les requĂȘtes vers les serveurs DNS principaux, et en cas d'indisponibilitĂ©, rĂ©pondra Ă partir du cache.Â
QuatriĂšme point â «Mise en ordre dans Nginx»
Un beau jour, notre Ă©quipe a dĂ©cidĂ© que "ça suffit" et le processus de refactoring des configurations Nginx a commencĂ©. L'objectif principal Ă©tait de rendre les configurations intuitivement comprĂ©hensibles. Auparavant, tout Ă©tait "historique" et n'avait aucune logique. Maintenant, chaque server_name a Ă©tĂ© dĂ©placĂ© dans un fichier homonyme et toutes les configurations ont Ă©tĂ© rĂ©parties dans des dossiers. Ă propos â la configuration contient 253949 lignes ou 7836520 caractĂšres et fait presque 7 mĂ©gaoctets. Le niveau supĂ©rieur de la structure:Â
Structure Nginx
âââ accĂšs
â Â âââ allow.list
...
â Â âââ whitelist.conf
âââ geobase
â Â âââ exclude.conf
...
â Â âââ geo_ip_to_region_id.conf
âââ geodb
â Â âââ GeoIP.dat
â Â âââ GeoIP2-Country.mmdb
â Â âââ GeoLiteCity.dat
âââ inc
â Â âââ error.inc
...
â Â âââ proxy.inc
âââ lists.d
â Â âââ bot.conf
...
â Â âââ dynamique
â Â âââ geo.conf
âââ lua
â Â âââ cookie.lua
â Â âââ log
â Â â Â âââ log.lua
â Â âââ logics
â Â â Â âââ include.lua
â Â â Â âââ ...
â Â â Â âââ utils.lua
â Â âââ prom
â Â Â Â âââ stats.lua
â Â Â Â âââ stats_prometheus.lua
âââ map.d
â Â âââ access.conf
â Â âââ ..Â
â Â âââ zones.conf
âââ nginx.conf
âââ robots.txt
âââ server.d
â Â âââ cian.ru
â Â â Â âââ cian.ru.conf
â Â â Â âââ ...
â Â â Â âââ my.cian.ru.conf
âââ service.d
â Â âââ ...
â Â âââ status.conf
âââ upstream.d
    âââ cian-mcs.conf
    âââ ...
    âââ wafserver.confC'est devenu beaucoup mieux, mais au cours du renommage et de la rĂ©organisation des configurations, certaines d'entre elles avaient une mauvaise extension et n'Ă©taient pas incluses dans la directive include *.conf. En consĂ©quence, certaines hĂŽtes sont devenus inaccessibles et ont renvoyĂ© 301 vers l'accueil. Comme le code de rĂ©ponse n'Ă©tait pas 5xx/4xx, cela n'a pas Ă©tĂ© remarquĂ© immĂ©diatement, seulement au matin. AprĂšs cela, nous avons commencĂ© Ă Ă©crire des tests pour vĂ©rifier les composants d'infrastructure.
Conclusions :Â
- Structurez correctement les configurations (pas seulement nginx) et pensez à la structure dÚs le début du projet. Cela les rendra plus compréhensibles pour l'équipe, ce qui à son tour réduira le TTM.
- Pour certains composants d'infrastructure, Ă©crivez des tests. Par exemple : vĂ©rifiez que tous les server_name clĂ©s renvoient le bon statut, + le corps de la rĂ©ponse. Avoir simplement quelques scripts sous la main qui vĂ©rifient les fonctions principales du composant suffira, afin de ne pas se rappeler frĂ©nĂ©tiquement Ă 3 heures du matin ce qu'il faut encore vĂ©rifier.Â
TroisiĂšme place â «Soudain, plus d'espace dans Cassandra»
Les donnĂ©es ont progressivement augmentĂ©, et tout allait bien jusqu'au moment oĂč les rĂ©parations de grands keyspaces dans le cluster Cassandra ont commencĂ© Ă Ă©chouer, car la compaction ne pouvait pas ĂȘtre effectuĂ©e.Â
Un jour de tempĂȘte, le cluster s'est presque transformĂ© en citrouille, Ă savoir :
- il restait environ 20 % en total dans le cluster ;
- il n'est pas possible d'ajouter des nĆuds pleinement, car le nettoyage ne se passe pas aprĂšs l'ajout d'un nĆud en raison du manque d'espace sur les partitions ;
- la performance diminue lentement, car la compaction ne fonctionne pas ;Â
- le cluster fonctionne en mode d'urgence.

La sortie â nous avons ajoutĂ© cinq nĆuds sans nettoyage, puis nous avons commencĂ© Ă les retirer progressivement du cluster et Ă les rĂ©introduire en tant que nĆuds vides, sur lesquels lâespace Ă©tait Ă©puisĂ©. Le temps consacrĂ© a Ă©tĂ© beaucoup plus long que prĂ©vu. Il y avait un risque d'indisponibilitĂ© partielle ou complĂšte du cluster.Â
Conclusions :
- Sur tous les serveurs Cassandra, l'espace occupĂ© ne doit pas dĂ©passer 60 % sur chaque partition.Â
- Ils ne doivent pas ĂȘtre chargĂ©s Ă plus de 50 % en CPU.
- Ne négligez pas la planification de capacité et réfléchissez-y pour chaque composant, en fonction de ses spécificités.
- Plus il y a de nĆuds dans le cluster, mieux c'est. Les serveurs contenant un petit volume de donnĂ©es se redĂ©marrent plus rapidement, et un tel cluster est plus facile Ă rĂ©animer.Â
DeuxiĂšme place â «Des donnĂ©es ont disparu du stockage clĂ©-valeur de Consul»
Pour la dĂ©couverte de services, nous utilisons, comme beaucoup, Consul. Mais notre clĂ©-valeur est Ă©galement utilisĂ©e pour la mise en production continue de notre monolithe. Elle stocke des informations sur les upstreams actifs et inactifs, qui changent de place lors du dĂ©ploiement. Un service de dĂ©ploiement a Ă©tĂ© Ă©crit pour interagir avec le KV. Ă un moment donnĂ©, les donnĂ©es du KV ont disparu. Nous les avons rĂ©cupĂ©rĂ©es de mĂ©moire, mais avec quelques erreurs. En consĂ©quence, lors de la mise en production, la charge sur les upstreams s'est rĂ©partie de maniĂšre inĂ©gale, et nous avons rencontrĂ© de nombreuses erreurs 502 en raison de la surcharge des backends en CPU. Au final, nous avons migrĂ© de Consul KV vers Postgres, d'oĂč il n'est pas si facile de les supprimer. Â
Conclusions :
- Les services sans aucune autorisation ne doivent pas contenir de donnĂ©es critiques pour le fonctionnement du site. Par exemple, si vous n'avez pas d'autorisation dans ES, il serait prĂ©fĂ©rable d'interdire l'accĂšs au niveau rĂ©seau de partout oĂč il n'est pas nĂ©cessaire, de ne laisser que les accĂšs nĂ©cessaires, et de rendre action.destructive_requires_name: true.
- Testez le mécanisme de sauvegarde et de restauration à l'avance. Par exemple, créez à l'avance un script (par exemple, en Python) qui peut à la fois sauvegarder et restaurer.
PremiĂšre place â «Capitaine Ăvidence»Â
Ă un moment donnĂ©, nous avons remarquĂ© une rĂ©partition inĂ©gale de la charge sur les upstreams nginx lorsque le backend comptait plus de 10 serveurs. Ătant donnĂ© que le round-robin envoyait les demandes du premier au dernier upstream dans l'ordre, et que chaque rechargement de nginx recommençait, les premiers upstreams recevaient toujours plus de requĂȘtes que les autres. En consĂ©quence, ils fonctionnaient plus lentement et l'ensemble du site en souffrait. Cela devenait de plus en plus perceptible Ă mesure que le trafic augmentait. Il n'a pas Ă©tĂ© suffisant de mettre Ă jour nginx pour activer random â il a fallu réécrire une quantitĂ© consĂ©quente de code lua, qui ne fonctionnait pas sur la version 1.15 (Ă ce moment-lĂ ). Nous avons dĂ» patcher notre nginx 1.14.2 pour y intĂ©grer le support du random. Cela a rĂ©solu le problĂšme. Ce bug mĂ©rite le prix du « capitaine de l'Ă©vident ».
Conclusions :
C'Ă©tait trĂšs intĂ©ressant et captivant d'explorer ce bug.)Â
- Mettez en place un suivi afin qu'il aide Ă dĂ©tecter rapidement de telles fluctuations. Par exemple, vous pouvez utiliser ELK pour surveiller le RPS sur chaque backend de chaque upstream et suivre leur temps de rĂ©ponse du point de vue de nginx. Dans ce cas, cela nous a aidĂ©s Ă identifier le problĂšme.Â
Une grande partie des Ă©checs aurait pu ĂȘtre Ă©vitĂ©e avec une approche plus scrupuleuse de ce que vous faites. Il faut toujours garder Ă l'esprit la loi de Murphy : Tout ce qui peut mal tourner, tournera mal, et construire des composants en s'en inspirant.Â
Source : habr.com
