Recetas TeamCity. Informe Yandex.Taxi

Mi nombre es Eduard Matsukov, estoy haciendo Taximeter, una aplicación para los conductores Yandex.Taxi. Estoy comprometido con la infraestructura y todo lo relacionado con ella. Hace algún tiempo hice un informe: hablé sobre la experiencia de la amistad de TeamCity con nuestro proyecto y con los desarrolladores en general. Una parte separada del informe está dedicada a lo que Kotlin tiene que ver con eso.


- Casi todos los días vienen a mí personalmente y a nuestros desarrolladores con preguntas. ¿Y dónde conseguir la asamblea? ¿Y dónde conseguir esa rama? ¿Por qué cayó algo? ¿Dónde está el problema en mi código? ¿Por qué algo no funciona correctamente? Para hacer esto, tenemos una gran cantidad de infraestructura auto-escrita en el proyecto, complementos, varios hacks y trucos que usamos. Por un lado, para facilitar la vida del desarrollador, por otro, para implementar tareas comerciales específicas.





Y en algún momento, por supuesto, también usamos CI y TeamCity. Nos confundimos: enseñamos a TeamCity a ser amigos de Kotlin y llevamos, podemos decir, todo el CI y toda la asamblea a un nivel completamente nuevo.

Pero primero, un poco de historia: para entender cómo llegamos a esto y por qué este nivel lo llamo un canon separado. TeamCity ha existido en Yandex durante muchos años. Tuvimos que vivir en este servidor compartido, donde se alojan todo el backend, toda la interfaz y, más recientemente, todas las aplicaciones móviles. Hace unos dos años, todos nos unimos. Y cada desarrollador configura cada proyecto, ni siquiera como lo desea, sino como puede o hasta donde comprende cuánto quiere entender el sistema. Y no hay una persona que sepa todo y sepa cómo. Pocas personas quieren molestarse, estudiar plantillas por separado, TeamCity wilds. Por lo tanto, todo el mundo está aserrando, quién es qué tanto.

Vivíamos en este único servidor, y el año pasado tuvimos un accidente en TeamCity. Aproximadamente una semana fue tiempo de inactividad completo. Las asambleas no fueron recolectadas, las pruebas se quejaban constantemente. Alguien ideado, recogido localmente.



Esto se debe al hecho de que nuestro servidor TeamCity fue, en términos generales, una solución que se convirtió en un gran servicio. Es utilizado por miles de desarrolladores en Yandex. Por supuesto, hubo algún tipo de tolerancia a fallas, pero también se negó. La próxima vez que se actualizó TeamCity después del reinicio, resultó que varios discos duros simplemente se habían derrumbado y no podíamos volver a subir. Tuve que salir.

Necesitamos sacar conclusiones de todo lo que sucedió. Y, por supuesto, llegamos a estas conclusiones: analizamos por qué sucedió y cómo asegurarnos de que esto no vuelva a suceder.

En primer lugar, es importante que subamos durante mucho tiempo y restauremos nuestro servicio. Por servicio, me refiero tanto a un proceso técnico como en parte a un proceso comercial para la entrega banal de lanzamientos, para el ensamblaje de solicitudes de grupo. Perdimos muchos artefactos, incluidas las versiones de lanzamiento, perdimos mucho tiempo en solicitudes de grupo, por el hecho de que las pruebas no podían hacer su trabajo correctamente. Y, por supuesto, pasamos bastante tiempo restaurando el proyecto desde cero, reconfigurando toda la estructura, todo el sistema de construcción. Y luego nos dimos cuenta de que era hora de cambiar algo y configurar nuestro propio servidor.

Fuimos a esto por mucho tiempo. No quiere decir que solo un accidente llevó a esta conclusión. En general, decidimos que era hora de ir a la montaña, hacer todo esto nosotros mismos. Comenzamos un lanzamiento de servicio. Esto se hace extremadamente rápido: un par de días y ya está. Cuando implementa todo esto usted mismo y puede profundizar en el interior, administrar un poco, entonces las características interesantes son sorprendentes. Uno de ellos: el nuevo TeamCity le permite configurar el control de versiones.



El control de versiones es muy primitivo, pero al mismo tiempo muy confiable, hermoso y genial. Todo lo que se almacena en TeamCity con respecto a tu o cualquier otro proyecto se puede cargar de forma segura a Git, y puedes vivir felizmente. Pero hay un par de problemas.

El primer problema es que todas las personas están acostumbradas a trabajar con TeamCity exclusivamente a través de la interfaz, y este hábito es difícil de erradicar. Aquí hay un pequeño truco de vida: simplemente puede prohibir cualquier cambio desde la interfaz y obligar a todas las personas a volver a aprender. Nuestro equipo tiene 2,000 desarrolladores. No es una buena manera, ¿verdad?

De hecho, los contras terminan ahí. El inconveniente más importante es que las personas tienen que volver a aprender algo nuevo. Por lo tanto, se les debe dar terreno para llegar a una conclusión personal sobre por qué esto es necesario. Y es necesario que TeamCity, gracias a las versiones, no permita aplicar cambios que de alguna manera rompan el sistema. TeamCity en sí recurre a la última revisión estable.



En TeamCity, puede iniciar cada proyecto para esta versión y configurarlo de manera bastante flexible.



Un poco de programa educativo. Todos los proyectos en TeamCity se organizan en un árbol. Hay algún tipo de raíz común, y más allá viene una estructura tan simple. Cada proyecto es la parte superior de este gráfico. Puede actuar como un cierto conjunto de configuraciones que construyen algo, y como padre para otros proyectos.



En Git, puedes enviar todo de una vez o una pieza específica. Por ejemplo, si los colegas del backend con el frontend no quieren usar el control de versiones, por favor, no puede contar con ellos y simplemente asegurar su proyecto personal.



Puede configurar un sistema jerárquico bastante complejo, al que finalmente llegó nuestro equipo. Tenemos una raíz grande común y algunas raíces pequeñas. Backend, desarrollo móvil, frontend, Yandex.Food: todos viven en su propio repositorio separado. Al mismo tiempo, la información sobre todos estos proyectos se almacena en un gran repositorio compartido, en la raíz.

Después de que finalmente conecte esta versión, instálela con todos sus colegas, dónde y quién vivirá, quién se encargará del soporte; después de todo esto, debe tomar una decisión difícil.



TeamCity solo admite dos formatos de configuraciones. Con XML, sospecho que nadie querrá trabajar, así que elegimos el segundo formato. Le permite realizar estas configuraciones en un script Kotlin.



eamCity crea un proyecto maven, algo parecido a cualquier proyecto ordinario. Puedes hacer una de dos cosas con él: subirlo a tu proyecto: Android, backend, no importa, o dejarlo como un proyecto independiente. Entonces tendrá un repositorio independiente con un proyecto independiente.



¿Cuál es la ventaja de este enfoque? Personalmente, yo y esos tipos que se ocupan de nuestra infraestructura en el backend y la interfaz nos sobornaron inmediatamente. E incluso aquellos que no están familiarizados con Kotlin, que se enteraron por primera vez, fueron y comenzaron a enseñarle.



Estas dos líneas crean todo el proyecto. Este es el dialecto de la API de TeamCity. La API cambia cada versión principal. Hay 2018-2, 2018-1, 2017, etc. Pronto, con suerte, se lanzará el 2019.

La segunda línea simplemente declara el proyecto.



Aquí está el proyecto en sí. Este es un código absolutamente real. Así es como se ve nuestro repositorio raíz ahora. Nada extra, nada complicado. El único trabajo manual que se requiere aquí es crear manualmente el UUID usted mismo. TeamCity requiere que cada objeto, cada proyecto tenga su propio identificador único. Puedes escribir cualquier cosa allí. Solo uso el apodo estándar del equipo uuidgen.

Aquí comienza la aventura en Kotlin DSL. Creo que este es un lenguaje completamente sencillo para dominar. Al subirlo a IDEA, Eclipse o cualquier otro IDE, puede obtener toda la documentación, resaltado, autocompletar, consejos. De hecho, muchos de ellos faltan en la interfaz. Por lo tanto, mi experiencia personal dice que trabajar con el código es mucho más conveniente, más simple y más intuitivo. Todavía somos desarrolladores.



Algo así se parece a una configuración real que ahora funciona al mismo tiempo, que admite las configuraciones de TeamCity. Es decir, TeamCity crea sus propias configuraciones en su propio entorno. Si todo está bien y todo salió mal, con calma lo envía a la memoria y replica los cambios en PostgreSQL. La base ya está conectada al servicio en sí. Y aquí será un pecado no usar todas las características de Kotlin.



En este caso, a diferencia del XML, estas configuraciones se pueden describir utilizando el polimorfismo, la herencia: se permiten todas las características del lenguaje Kotlin. El único punto importante es que todo esto eventualmente puede convertirse en el caos que existía con nosotros antes de que introdujáramos el control de versiones de las configuraciones en el script de Kotlin.



Pero, curiosamente, este caos se ha vuelto mucho menos. Porque antes no era tan obvio cómo hacer lo que quiero, cómo lograr esta o aquella característica. Desde el código, en mi práctica, es mucho más fácil entender cómo implementar cualquier función.

Las aventuras más interesantes comienzan aquí: ¿cómo implementamos algunas cosas y cómo, en principio, facilitamos la interacción del proyecto con TeamCity?

Todos los presentes aquí de una forma u otra están preparando un lanzamiento, participando en su montaje, en la publicación. Publicamos nuestros lanzamientos en varios canales en Google Play.





Tenemos beta, hay experimentos, hay estable. Utilizamos un complemento especial con un robot que publica comentarios con un informe sobre la versión de lanzamiento en el ticket de lanzamiento. Y todo esto está configurado con una ventana tan hermosa. Aparece tan pronto como intentas crear una versión. Estas preguntas no se pueden evitar.



Desde la interfaz de TeamCity, se ve más o menos así. Para comprender de inmediato qué, dónde, dónde y cómo, necesita leer cada parámetro, necesita experimentar. De la documentación, además de lo que está visible en la pantalla, no se puede obtener nada más.



En código, se ve así. Al menos hasta ahora, durante medio año, nadie ha venido y ha preguntado: ¿cómo hago alguna función? La mayoría de las veces del código es intuitivamente claro.



Al mismo tiempo, algunas cosas se hacen de manera bastante simple, pero se ocultan detrás de varias capas de la interfaz. Tenemos que caminar, ir y venir.



Aquí hay un ejemplo de cómo se implementa la seguridad en TeamCity. En mi práctica, para la mayoría de las personas, TeamCity parece ser un sistema frío bastante simple que no admite la integración, por ejemplo, con los servicios de seguridad. Por lo tanto, todos los tokens, todas las claves, todas las credenciales que tenemos con nosotros a menudo permanecen abiertas. Por que no

De hecho, TeamCity es seguro. Él sabe cómo crear su propio archivo especial en su servidor, que se llama - credential json, como se muestra. Y crea una clave para cada token, para cada credencial, que generamos especialmente a través de la interfaz. Ya puede ponerlo en el código y asegurarse de que esta credencial nunca aparezca en los registros de TeamCity o en la interfaz de TeamCity. El sistema puede cortar estas teclas literalmente desde cualquier lugar. Toda la interfaz es, en términos generales, una especie de decoración.



De acuerdo, configuramos algunos de nuestros parámetros, realizamos un reenvío de los parámetros requeridos, por ejemplo, para compilar la versión. Pero, ¿y si queremos ir más allá? Y queríamos ir más allá. Durante el montaje, se lanzan muchos pasos diferentes. Ejecutamos varias bibliotecas anidadas que se crean a partir de repositorios completamente diferentes. Y solo queríamos hacer nuevos cambios. Todo eso es ahora. No se moleste, por ejemplo, no recopile una biblioteca auxiliar para solicitudes de grupo, no la cargue en el repositorio de Maven, no agregue gestos adicionales a la solicitud de grupo.



Acabamos de configurar el conjunto de la cadena. Mostraré hasta el final lo discreto e inconveniente que es hacer esto desde la interfaz, en mi opinión personal. Y ya juzgas por ti mismo.

Así es como se ve el ensamblaje de la cadena en la interfaz.



Se ve algo así en el código. Simplemente indicamos exactamente qué configuración depende y qué hacer si alguna de las configuraciones no funcionó o fue cancelada por el usuario desde el exterior. En este caso, no quiero que el ensamblaje se inicie en absoluto. Porque, ¿de qué sirve si no hemos recopilado todas las bibliotecas dependientes?

Alrededor del mismo espíritu, se están haciendo todas las demás cosas. Y todo el proyecto en TeamCity toma literalmente 500 líneas de código.



Resulta que puede reenviar algunos parámetros interesantes a través de todas las dependencias. Mostré encadenado por una razón. Las cadenas son convenientes, pero difíciles de preparar en la interfaz. Y TeamCity no documenta una característica tan importante como el reenvío a través de parámetros. ¿Para qué es esto? Supongamos que, en nuestra compilación en Gradle o en otro lugar, queremos vincularnos a un campo específico, reenviar la misma dirección al ticket de lanzamiento. Y queremos hacer esto una vez, y no para cada conjunto anidado.



TeamCity tiene un parámetro no tan obvio y completamente no documentado: reverse.dep (dependencia inversa). Lanza todos los parámetros que vienen después del asterisco en todas las compilaciones anidadas.


En la salida, obtenemos una estructura tan simple. Puede complicarlo y hacer que la anidación sea tan profunda como su imaginación o sus necesidades. Y para estar seguros de que en todas estas dependencias, en todas estas configuraciones, se reenviarán todos nuestros parámetros que esperamos en cada paso del ensamblaje. Listo para responder sus preguntas. Gracias a todos!

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


All Articles