por 12f-2
A finales de abril, mientras los Caminantes Blancos asediaban Winterfell, nos pasó algo más interesante; hicimos un lanzamiento inusual; En principio, constantemente implementamos nuevas funciones en producción (como todos los demás). Pero éste era diferente. La magnitud del problema era tal que cualquier error potencial que pudiéramos cometer afectaría a todos nuestros servicios y usuarios. Como resultado, implementamos todo según lo planeado, dentro del período de inactividad planificado y anunciado, sin consecuencias para las ventas. El artículo trata sobre cómo lo logramos y cómo cualquiera puede repetirlo en casa.
No describiré ahora las decisiones arquitectónicas y técnicas que tomamos ni contaré cómo funciona todo. Más bien son notas al margen sobre cómo se desarrolló uno de los lanzamientos más difíciles, que observé y en el que participé directamente. No pretendo que esté completo ni que los detalles técnicos aparezcan en otro artículo.
Antecedentes + ¿qué tipo de funcionalidad es esta?
Estamos construyendo una plataforma en la nube. (MCS), donde trabajo como director técnico. Y ahora es el momento de agregar IAM (Administración de identidad y acceso) a nuestra plataforma, que brinda administración unificada de todas las cuentas de usuario, usuarios, contraseñas, roles, servicios y más. Por qué es necesario en la nube es una pregunta obvia: toda la información del usuario se almacena en ella.
Por lo general, estas cosas comienzan a construirse desde el comienzo de cualquier proyecto. Pero históricamente las cosas han sido un poco diferentes en MCS. MCS se construyó en dos partes:
- Openstack con su propio módulo de autorización Keystone,
- Hotbox (almacenamiento S3) basado en el proyecto Mail.ru Cloud,
en torno al cual aparecieron entonces nuevos servicios.
Básicamente, se trataba de dos tipos diferentes de autorización. Además, utilizamos algunos desarrollos separados de Mail.ru, por ejemplo, un almacenamiento de contraseñas general de Mail.ru, así como un conector openid escrito por nosotros mismos, gracias al cual se proporcionó SSO (autorización de un extremo a otro) en el panel Horizon. de máquinas virtuales (UI nativa de OpenStack).
Hacer IAM para nosotros significó conectarlo todo en un único sistema, completamente nuestro. Al mismo tiempo, no perderemos ninguna funcionalidad en el camino, sino que crearemos una base para el futuro que nos permitirá refinarla de forma transparente sin refactorizarla y escalarla en términos de funcionalidad. También al principio los usuarios tenían un modelo a seguir para el acceso a los servicios (RBAC central, control de acceso basado en roles) y algunas otras pequeñas cosas.
La tarea resultó no ser trivial: Python y Perl, varios backends, servicios escritos de forma independiente, varios equipos de desarrollo y administradores. Y lo más importante, hay miles de usuarios activos en el sistema de producción de combate. Todo esto tenía que escribirse y, lo más importante, implementarse sin víctimas.
¿Qué vamos a desplegar?
Para decirlo de forma muy aproximada, en unos 4 meses preparamos lo siguiente:
- Creamos varios demonios nuevos que agregaron funciones que anteriormente funcionaban en diferentes partes de la infraestructura. Al resto de los servicios se les prescribió un nuevo backend en forma de estos demonios.
- Escribimos nuestro propio almacenamiento central de contraseñas y claves, disponible para todos nuestros servicios, que puede modificarse libremente según lo necesitemos.
- Escribimos 4 nuevos backends para Keystone desde cero (usuarios, proyectos, roles, asignaciones de roles), que, de hecho, reemplazaron su base de datos y ahora actúan como un repositorio único para nuestras contraseñas de usuario.
- Enseñamos a todos nuestros servicios de Openstack a acudir a un servicio de políticas de terceros para sus políticas en lugar de leer estas políticas localmente desde cada servidor (sí, ¡así es como funciona Openstack de forma predeterminada!)
Una reelaboración tan importante requiere cambios grandes, complejos y, lo más importante, sincrónicos en varios sistemas escritos por diferentes equipos de desarrollo. Una vez ensamblado, todo el sistema debería funcionar.
¿Cómo implementar esos cambios y no arruinarlo? Primero decidimos mirar un poco hacia el futuro.
Estrategia de implementación
- Sería posible lanzar el producto en varias etapas, pero esto triplicaría el tiempo de desarrollo. Además, desde hace algún tiempo tendríamos una desincronización completa de los datos en las bases de datos. Tendría que escribir sus propias herramientas de sincronización y vivir con múltiples almacenes de datos durante mucho tiempo. Y esto crea una amplia variedad de riesgos.
- Todo lo que se podía preparar de forma transparente para el usuario se hizo de antemano. Tardaron 2 meses.
- Nos permitimos un tiempo de inactividad durante varias horas, solo para que las operaciones de los usuarios crearan y cambiaran recursos.
- Para el funcionamiento de todos los recursos ya creados, el tiempo de inactividad era inaceptable. Planeamos que durante la implementación, los recursos deberían funcionar sin tiempo de inactividad y sin afectar a los clientes.
- Para reducir el impacto en nuestros clientes si algo sale mal, decidimos implementarlo el domingo por la noche. Menos clientes administran máquinas virtuales por la noche.
- Advertimos a todos nuestros clientes que durante el período seleccionado para la implementación, la gestión de servicios no estará disponible.
Digresión: ¿qué es un lanzamiento?
<precaución, filosofía>
Todo especialista en TI puede responder fácilmente qué es una implementación. Instala CI/CD y todo se entrega automáticamente a la tienda. 🙂
Por supuesto esto es verdad. Pero la dificultad es que con las herramientas modernas de automatización de entrega de código, se pierde la comprensión del despliegue en sí. Cómo te olvidas de lo épico de la invención de la rueda cuando miras el transporte moderno. Todo está tan automatizado que a menudo la implementación se lleva a cabo sin comprender el panorama completo.
Y el panorama completo es así. La implementación consta de cuatro aspectos principales:
- Entrega de código, incluida la modificación de datos. Por ejemplo, sus migraciones.
- La reversión del código es la capacidad de retroceder si algo sale mal. Por ejemplo, mediante la creación de copias de seguridad.
- Hora de cada operación de implementación/reversión. Es necesario comprender el momento de cualquier operación de los dos primeros puntos.
- Funcionalidad afectada. Es necesario evaluar tanto los efectos positivos esperados como los posibles efectos negativos.
Todos estos aspectos deben tenerse en cuenta para una implementación exitosa. Por lo general, solo se evalúa el primer punto, o en el mejor de los casos el segundo, y luego la implementación se considera exitosa. Pero el tercero y el cuarto son aún más importantes. ¿A qué usuario le gustaría que el lanzamiento tardara 3 horas en lugar de un minuto? ¿O si algo innecesario se ve afectado durante el lanzamiento? ¿O el tiempo de inactividad de un servicio tendrá consecuencias impredecibles?
Acto 1..n, preparación para la liberación.
Al principio pensé en describir brevemente nuestras reuniones: todo el equipo, sus partes, montones de discusiones en los cafés, discusiones, pruebas, lluvias de ideas. Entonces pensé que sería innecesario. Cuatro meses de desarrollo siempre consisten en esto, especialmente cuando no estás escribiendo algo que pueda entregarse constantemente, sino una característica importante para un sistema en vivo. Lo cual afecta a todos los servicios, pero nada debería cambiar para los usuarios excepto “un botón en la interfaz web”.
Nuestra comprensión de cómo implementar la implementación cambió con cada nueva reunión, y de manera bastante significativa. Por ejemplo, íbamos a actualizar toda nuestra base de datos de facturación. Pero calculamos el tiempo y nos dimos cuenta de que era imposible hacer esto en un tiempo de implementación razonable. Nos llevó casi una semana más fragmentar y archivar la base de datos de facturación. Y cuando la velocidad de implementación esperada aún no fue satisfactoria, pedimos hardware adicional y más potente, donde se arrastró toda la base. No es que no quisiéramos hacer esto antes, pero la necesidad actual de implementarlo nos dejó sin opciones.
Cuando uno de nosotros tuvo dudas de que el lanzamiento podría afectar la disponibilidad de nuestras máquinas virtuales, pasamos una semana realizando pruebas, experimentos, análisis de código y recibimos un entendimiento claro de que esto no sucedería en nuestra producción, e incluso las personas más escépticas estuvieron de acuerdo. con este.
Mientras tanto, los chicos del soporte técnico realizaron sus propios experimentos independientes para escribir instrucciones para los clientes sobre los métodos de conexión, que se suponía que cambiarían después del lanzamiento. Trabajaron en la UX del usuario, prepararon instrucciones y brindaron consultas personales.
Automatizamos todas las operaciones de implementación que fueron posibles. Cada operación estaba programada, incluso las más simples, y se ejecutaban pruebas constantemente. Discutieron sobre la mejor manera de desactivar el servicio: omitir el demonio o bloquear el acceso al servicio con un firewall. Creamos una lista de verificación de equipos para cada etapa de implementación y la actualizamos constantemente. Dibujamos y actualizamos constantemente un diagrama de Gantt para todo el trabajo de implementación, con tiempos.
Y entonces ...
El acto final, antes del lanzamiento.
...es hora de implementarlo.
Como dicen, una obra de arte no se puede terminar, sólo terminar de trabajar en ella. Debe hacer un esfuerzo de voluntad, entendiendo que no encontrará todo, pero creyendo que ha hecho todas las suposiciones razonables, previsto todos los casos posibles, solucionado todos los errores críticos y todos los participantes hicieron todo lo que pudieron. Cuanto más código implementes, más difícil será convencerte de ello (además, todo el mundo entiende que es imposible preverlo todo).
Decidimos que estábamos listos para implementarlo cuando estábamos convencidos de que habíamos hecho todo lo posible para cubrir todos los riesgos para nuestros usuarios asociados con efectos inesperados y tiempos de inactividad. Es decir, cualquier cosa puede salir mal excepto:
- Afectar (sagrado para nosotros, más preciado) la infraestructura del usuario,
- Funcionalidad: el uso de nuestro servicio después del lanzamiento debe ser el mismo que antes.
Despliegue
Dos rollos, 8 no interfieren
Tomamos un tiempo de inactividad para todas las solicitudes de los usuarios durante 7 horas. En este momento, tenemos un plan de implementación y un plan de reversión.
- El lanzamiento en sí dura aproximadamente 3 horas.
- 2 horas para la prueba.
- 2 horas: reserve para una posible reversión de cambios.
Se ha elaborado un diagrama de Gantt para cada acción, cuánto tiempo lleva, qué sucede de forma secuencial, qué se hace en paralelo.
Una parte de un diagrama de Gantt desplegable, una de las primeras versiones (sin ejecución paralela). La herramienta de sincronización más valiosa
Todos los participantes tienen determinado su papel en la implementación, qué tareas realizan y de qué son responsables. Intentamos llevar cada etapa al automatismo, implementarla, revertirla, recopilar comentarios y implementarla nuevamente.
Crónica de eventos
Entonces, 15 personas vinieron a trabajar el domingo 29 de abril a las 10 horas. Además de los participantes clave, algunos vinieron simplemente para apoyar al equipo, por lo que se les agradece especialmente.
También vale la pena mencionar que nuestro evaluador clave está de vacaciones. Es imposible implementarlo sin realizar pruebas, estamos explorando opciones. Una colega accede a ponernos a prueba durante las vacaciones, por lo que recibe un inmenso agradecimiento de todo el equipo.
00:00. Detener
Detenemos las solicitudes de los usuarios, colgamos un cartel que dice trabajo técnico. El seguimiento grita, pero todo es normal. Comprobamos que no cayó nada más de lo que debía caer. Y comenzamos a trabajar en materia de migración.
Todo el mundo tiene un plan de implementación impreso punto por punto, todo el mundo sabe quién está haciendo qué y en qué momento. Después de cada acción, comprobamos los tiempos para asegurarnos de no excederlos y que todo salga según lo previsto. Aquellos que no participan directamente en el lanzamiento en la etapa actual se están preparando lanzando un juguete en línea (Xonotic, tipo 3 charlatanes) para no molestar a sus colegas. 🙂
02:00. desplegado
Una agradable sorpresa: finalizamos la implementación una hora antes gracias a la optimización de nuestras bases de datos y scripts de migración. El grito de todos: "¡Desplegado!" Todas las funciones nuevas están en producción, pero hasta ahora solo nosotros podemos verlas en la interfaz. Todos entran en modo de prueba, los clasifican en grupos y comienzan a ver qué sucedió al final.
No salió muy bien, nos damos cuenta después de 10 minutos, cuando no hay nada conectado ni funcionando en los proyectos de los miembros del equipo. Sincronización rápida, expresamos nuestros problemas, establecemos prioridades, nos dividimos en equipos y comenzamos a depurar.
02:30. Dos grandes problemas versus cuatro ojos
Nos encontramos con dos grandes problemas. Nos dimos cuenta de que los clientes no verían algunos servicios conectados y surgirían problemas con las cuentas de los socios. Ambos se deben a secuencias de comandos de migración imperfectas para algunos casos extremos. Necesitamos arreglarlo ahora.
Escribimos consultas que dejan constancia de esto, con al menos 4 ojos. Los probamos durante la preproducción para asegurarnos de que funcionan y no rompen nada. Puedes seguir adelante. Al mismo tiempo, realizamos nuestras pruebas de integración periódicas, que revelan algunos problemas más. Todos son pequeños, pero también necesitan ser reparados.
03:00. -2 problemas +2 problemas
Los dos grandes problemas anteriores han sido solucionados, y casi todos los menores también. Todos aquellos desocupados en arreglos están trabajando activamente en sus cuentas e informando lo que encuentran. Priorizamos, distribuimos entre equipos y dejamos los elementos no críticos para la mañana.
Volvemos a realizar las pruebas y descubren dos nuevos grandes problemas. No todas las políticas de servicio llegaron correctamente, por lo que algunas solicitudes de usuarios no pasan la autorización. Además de un nuevo problema con las cuentas de socios. Corramos a mirar.
03:20. Sincronización de emergencia
Un nuevo problema solucionado. Para el segundo, estamos organizando una sincronización de emergencia. Entendemos lo que está sucediendo: la solución anterior solucionó un problema, pero creó otro. Nos tomamos un descanso para descubrir cómo hacerlo correctamente y sin consecuencias.
03:30. Seis ojos
Entendemos cuál debe ser el estado final de la base para que todo salga bien para todos los socios. Escribimos una solicitud con 6 ojos, la implementamos en preproducción, la probamos y la implementamos para producción.
04:00. todo esta funcionando
Todas las pruebas pasaron, no se ven problemas críticos. De vez en cuando, algo en el equipo no funciona para alguien, reaccionamos rápidamente. La mayoría de las veces la alarma es falsa. Pero a veces algo no llega o una página aparte no funciona. Nos sentamos, arreglamos, arreglamos, arreglamos. Un equipo independiente está lanzando la última gran característica: la facturación.
04:30. Punto sin retorno
Se acerca el punto de no retorno, es decir, el momento en el que, si empezamos a retroceder, no cumpliremos con el tiempo de inactividad que se nos ha asignado. Hay problemas con la facturación, que sabe y registra todo, pero se niega obstinadamente a cancelar el dinero de los clientes. Hay varios errores en páginas, acciones y estados individuales. La funcionalidad principal funciona, todas las pruebas se realizan con éxito. Decidimos que el lanzamiento se ha realizado y no retrocederemos.
06:00. Abierto para todos en la interfaz de usuario
Errores corregidos. Algunas que no atraen a los usuarios se dejan para más adelante. Abrimos la interfaz a todos. Seguimos trabajando en la facturación, esperando comentarios de los usuarios y monitoreando los resultados.
07:00. Problemas con la carga de API
Queda claro que planificamos ligeramente mal la carga en nuestra API y probamos esta carga, lo que no pudo identificar el problema. Como resultado, ≈5% de las solicitudes fallan. Movilicémonos y busquemos la razón.
Billing es testarudo y tampoco quiere trabajar. Decidimos posponerlo para más tarde para poder realizar los cambios con tranquilidad. Es decir, todos los recursos se acumulan en él, pero las cancelaciones de los clientes no se realizan. Por supuesto, esto es un problema, pero en comparación con el lanzamiento general parece sin importancia.
08:00. Reparar API
Implementamos una solución para la carga y las fallas desaparecieron. Empezamos a ir a casa.
10:00. Todo
Todo está arreglado. Es silencioso en el seguimiento y en casa de los clientes, el equipo poco a poco se va a dormir. La facturación permanece, la restableceremos mañana.
Luego, durante el día, hubo implementaciones que arreglaron registros, notificaciones, códigos de retorno y personalizaciones para algunos de nuestros clientes.
Entonces, ¡el lanzamiento fue exitoso! Por supuesto, podría ser mejor, pero sacamos conclusiones sobre lo que no era suficiente para alcanzar la perfección.
En total
Durante 2 meses de preparación activa para la implementación, se completaron 43 tareas, que duraron desde un par de horas hasta varios días.
Durante el lanzamiento:
- demonios nuevos y modificados: 5 piezas, reemplazando 2 monolitos;
- cambios dentro de las bases de datos: las 6 bases de datos nuestras con datos de usuario se han visto afectadas, se han realizado descargas de tres bases de datos antiguas a una nueva;
- interfaz completamente rediseñada;
- cantidad de código descargado: 33 mil líneas de código nuevo, ≈ 3 mil líneas de código en pruebas, ≈ 5 mil líneas de código de migración;
- Todos los datos están intactos y ni una sola máquina virtual de un cliente resultó dañada. 🙂
Buenas prácticas para un buen despliegue
Ellos nos guiaron en esta difícil situación. Pero, en términos generales, es útil seguirlos durante cualquier implementación. Pero cuanto más complejo sea el despliegue, mayor será el papel que desempeñan.
- Lo primero que debe hacer es comprender cómo la implementación puede afectar o afectará a los usuarios. ¿Habrá tiempo de inactividad? Si es así, ¿cuál es el tiempo de inactividad? ¿Cómo afectará esto a los usuarios? ¿Cuáles son los posibles mejores y peores escenarios? Y cubrir los riesgos.
- Planifica todo. En cada etapa, es necesario comprender todos los aspectos de la implementación:
- entrega de código;
- reversión de código;
- tiempo de cada operación;
- funcionalidad afectada.
- Juega a través de los escenarios hasta que todas las etapas del lanzamiento, así como los riesgos en cada una de ellas, se vuelvan evidentes. Si tiene alguna duda, puede tomar un descanso y examinar la etapa cuestionable por separado.
- Cada etapa puede y debe mejorarse si ayuda a nuestros usuarios. Por ejemplo, reducirá el tiempo de inactividad o eliminará algunos riesgos.
- Las pruebas de reversión son mucho más importantes que las pruebas de entrega de código. Es necesario comprobar que, como resultado de la reversión, el sistema volverá a su estado original y confirmarlo mediante pruebas.
- Todo lo que pueda automatizarse debería automatizarse. Todo lo que no se pueda automatizar debe escribirse de antemano en una hoja de referencia.
- Registre el criterio de éxito. ¿Qué funcionalidad debería estar disponible y en qué momento? Si esto no sucede, ejecute un plan de reversión.
- Y lo más importante: la gente. Todos deben ser conscientes de lo que están haciendo, por qué y de qué depende sus acciones en el proceso de implementación.
Y en una frase, con una buena planificación y elaboración podrás desplegar lo que quieras sin consecuencias para las ventas. Incluso algo que afectará a todos tus servicios en producción.
Fuente: habr.com
