Actualización del proceso de CI / CD: teamcity

imagen
Este es el segundo artículo de una serie sobre la actualización de procesos de CI / CD. Hasta este punto, se estaban haciendo preparativos para establecer nuevas herramientas, a saber: planificación, reuniones diarias, resolución de desacuerdos, en general, todo sin lo cual no sería posible construir de manera competente un proceso de trabajo. Y ahora, todos los problemas se han resuelto, estamos seguros de que podemos continuar con el desarrollo, y nuestro código no se hundirá en el agujero negro con el comienzo del verano. 

Permítame recordarle la tabla de contenido de la lista de artículos, así como los breves resultados de la parte anterior.

Parte 1: qué es, por qué no es así, planificar, una pequeña fiesta. Yo llamaría a esta parte casi técnica.
Parte 2: teamcity.
Parte 3: despliegue de pulpo.
Parte 4: detrás de escena. Momentos desagradables, planes para el futuro, posiblemente preguntas frecuentes. Lo más probable es que también se le pueda llamar casi técnico.

Proporcionamos al cliente una prueba de concepto de un nuevo sistema de entrega de actualizaciones, identificamos las deficiencias del sistema existente, seleccionamos la base para el nuevo, hicimos un plan de transición y convertimos nuestro repositorio mercurial a git. Además, en la parte anterior, describimos las características del proyecto que afectarán todo el proceso posterior. 

Como se mencionó anteriormente, la solución existente no cumplía con los requisitos modernos. Inicialmente, lo más probable es que se haya configurado una compilación en ccnet. La primera compilación con la que todo comenzó (como una gran explosión, sí). Luego, aparentemente, la persona que lo configuró todo, presionó ctrl + c, ctrl + v, corrigió un par de líneas y recibió una nueva configuración. Y durante todo el tiempo posterior, esto se hizo un número suficiente de veces para que el código fuente en el servidor de compilación ocupara más de 60G. ¡Y estos son solo la fuente! Y hay registros, artefactos de compilación, archivos temporales, etc. Solo esto me hizo pensar. Al observar el sistema anterior, se podía ver lo siguiente: se realizó un paso de compilación para cada módulo. De hecho, se ensambló el mismo núcleo. Y este ensamblado se almacenó para cada módulo (junto con la fuente) en el servidor.Los resultados de la compilación (90% idénticos) se introdujeron en repositorios locales (implementación de git) y, por supuesto, también tomaron su lugar. Era posible y necesario dejar esto. 

Configuración general 


En esta etapa, se supone que ya hay una teamcity configurada y un pulpo. En la etapa de instalación y configuración, no nos detenemos en detalle. Por conveniencia, anotamos la url y la api-token del pulpo en env teamcity, que, por cierto, para el proyecto raíz se ve así:

imagen

env.apiUserName y env.apiUserPassword son accesos api para el usuario de teamcity (se necesitarán más adelante), env.buildPath y env .sourcePath : rutas predeterminadas para compilar archivos y archivos fuente, respectivamente, env.octoApiKey y env.octoUrl : URL de token y octopus. Todavía env.teamcityUrl añadido. Bueno, env.tt.exe- ruta a la utilidad de transformación de texto. Esta utilidad genera todas las configuraciones.

De los complementos de terceros, solo se instala la integración de Octopus Deploy. 

Para no sobrecargar la gran cantidad de texto, solo se examinarán en detalle aquellos pasos en los que haya algo que merezca la atención. Se supone que todo lo demás es comprensible en función de los datos presentados y el conocimiento del lector. En cualquier caso, si tiene alguna pregunta, me complacerá responderla.

Desglose del proyecto, versiones, nombres


El resultado fue un esquema de proyecto de este tipo (como una imagen, porque hay un problema al mostrar una lista multinivel):
imagen

versionaremos los paquetes de la siguiente manera: major.minor.patch, donde major es hasta ahora 1, menor es el contador de compilación de la configuración de Build, el parche es el contador de compilación para la configuración Implementar (no es necesario para la configuración de compilación, por lo tanto, siempre 0).

Nota: la versión anterior se describe aquí. Lo estamos rediseñando para que se base en la etiqueta git.

imagen

Por ejemplo, aquí 1 es mayor, 95 es menor y 1 es parche.

En el nuevo proceso para módulos habrá una compilación que luego se implementará en cada módulo. Es decir, el código fuente se almacena en una sola copia para cada entorno, el resultado de la compilación también lo es, ya que es común para todos los módulos. Las configuraciones y bibliotecas únicas se empaquetan en un paquete individual en la etapa de implementación. Esto utilizará efectivamente el espacio en disco y reducirá el tiempo de entrega de cada paquete.

En la etapa de preparación, hubo una breve reunión separada sobre la convención de nombrar archivos de configuración. Solía ​​ser (era justo), pero no siempre es lo mismo. Además, el nombre de la configuración no siempre contenía suficiente información. Como resultado, todos los nombres de configuración fueron los siguientes:
ConfigurationType.Environment.ModName.configdonde ConfigurationType puede ser AppSettings, Web, etc. Medio ambiente: desarrollo, prueba, puesta en escena, producción. ModName es el nombre único del módulo.

Módulos


Todos los proyectos de módulos funcionan con el mismo principio. 

Cada entorno tiene una configuración de compilación que:

  1. Realiza la restauración nuget (instalador NuGet, restauración nuget)
  2. Genera configuraciones (línea de comando, transformación de texto)
  3. Solución Buildit (Visual studio sln)
  4. Recoge archivos de env.buildPath en zip y lo empuja hacia octopus (implementación de Octopus: paquetes push)
  5. Creación de lanzamiento (sin implementación) (implementación de lanzamiento de Octopus)
  6. Restablece la versión del parche (powershell)

También hay configuraciones de implementación que funcionan en función de la plantilla y hacen lo siguiente:

  1. Recoge las configuraciones que se generaron en la etapa de compilación (paso 2) (paquetes de implementación de implementación de Octopus)
  2. Implementar la versión principal (que se creó en el paso 6 de la compilación) (implementación de Octopus: implementación de la versión)
  3. Implementación de la versión individual (que se creó en el paso 1 de la configuración actual) (implementación de Octopus: implementación de la versión)
  4. Muestra una lista de todos los dominios para este módulo en el registro (paso para la conveniencia del probador y desarrollador). (Potencia Shell).

Para cada configuración de despliegue, la configuración de compilación se agrega a la dependencia. Esto hace posible iniciar automáticamente la compilación cuando se implementa si no es relevante, así como la capacidad de restablecer la versión del parche.

Implementar_toda la configuración. De hecho, esta es una cadena de compilación que describe que primero debe ejecutar la compilación si no es relevante, y luego instalar simultáneamente todos los modos.

Se parece a esto: veamos

imagen

algunos de los pasos con un poco más de detalle.

Build.Configuración general


imagen

Desde un formato de número de compilación inusual Será explicado más tarde.

Build.env


imagen


De interés aquí son: env.coreProjectName : establecido para todo el proyecto. Necesario para identificar el proyecto en pulpo.
env.Environment : establecido para el subproyecto Módulos. Es necesario seleccionar las configuraciones correctas según el entorno.
env.octoChannel : el canal en el pulpo en el que cae el paquete. Igual que el medio ambiente .
env.serviceName es el nombre de la solución. Básicamente, la variable se agrega para facilitar la visualización.
env.tenantRoleTag : etiqueta de inquilino en el pulpo. Más detalles en la sección de pulpo.

Build.4


imagen

Agregue al archivo con el nombre % env.serviceName%.% Build.number% .zip archivos del directorio con el resultado de la compilación, excluyendo todas las configuraciones, y dll de la carpeta bin / native. Los últimos se agregaron manualmente al servidor web una vez y nadie los volverá a tocar. Si es necesario, también se actualizarán manualmente (para esto se planea escribir un script por separado, pero seremos honestos). Esto se debe al hecho de que estas bibliotecas están "retenidas" por el grupo de IIS, y para una implementación exitosa debe detenerse y luego iniciarse, lo que lleva algún tiempo. Al excluir estas bibliotecas de la implementación, podemos omitir el paso de detener el grupo, lo que reducirá el tiempo de implementación y, en consecuencia, el tiempo de inactividad.

Build.5


imagen

Creo que todo está claro aquí, excepto por qué el campo Entorno está vacío. En lo que se refiere al paquete, el pulpo toma una decisión basada en la etiqueta de prelanzamiento (configurada en los canales). Esta etiqueta está contenida en % build.number% (la misma etapa en la pantalla en Build.General). Entonces, en este caso resultó ser más conveniente. También vale la pena prestar atención a la casilla de verificación "Mostrar proceso de implementación" . Si no está instalado, timsity considera que la construcción es exitosa tan pronto como el pulpo ha comenzado (¡pero no ha terminado!) El trabajo. Es decir, si la casilla de verificación no está instalada y algo salió mal durante la fase de implementación, sin mirar la interfaz de Octopus, no lo sabremos.

Build.6


Nada inusual, solo un script de PowerShell e información de la documentación de Teamcity.

$pair = "%env.apiUserName%:%env.apiUserPassword%"
$encodedCreds = [System.Convert]::ToBase64String([System.Text.Encoding]::ASCII.GetBytes($pair))
$basicAuthValue = "Basic $encodedCreds"
$depUri="%env.teamcityUrl%/app/rest/buildTypes?locator=snapshotDependency:(from:(build:(id:%teamcity.build.id%)),includeInitial:false)"
# Get all dependencies of main build
$result = (Invoke-RestMethod -Headers @{'Origin'='http://localhost:8090'; "Authorization"="$basicAuthValue"} -Uri $depUri)
foreach ($i in $result.buildTypes.buildType.Count) { $buildId += $result.buildTypes.buildType.id }
$buildId = $buildId | Where-Object { $_ -ne 'Module_DeployAll' }
# Reset all child build counters to 1. This build counters are patch versions in every build. (1.build.patch)
for ($i=0; $i -lt $buildId.Count; $i++) {
  $buildCounterUri = "%env.teamcityUrl%/httpAuth/app/rest/buildTypes/"+$buildId[$i]+"/settings/buildNumberCounter"
  Invoke-RestMethod -Method Put -Headers @{'accept'='text/plain'; 'Origin'='http://localhost:8090'; "Authorization"="$basicAuthValue"} -Uri $buildCounterUri -ContentType "text/plain" -Body 1
}

Aquí necesitamos un usuario api en teamcity (se crea env para él). Iniciamos sesión, tomamos todas las configuraciones que dependen de la configuración de compilación y restablecemos su contador de compilación a 1. De hecho, para cada versión de parche, este es un indicador de cuántas veces se implementó la misma compilación para un módulo en particular. Si no hay 1, significa que algo salió mal con el proceso de implementación o funcionamiento del módulo en la versión actual, y requiere atención.

Implementar.General


imagen

Formato de versión: 1.% dep.Module_Build.build.counter%.% Build.counter% -staging , donde
% dep.Module_Build.build.counter% es la variable del sistema teamcity cuyo valor corresponde al contador de compilación de la configuración de construcción correspondiente (debe agregarse a dependencias) De hecho, aquí 1 es la versión principal, % dep.Module_Build.build.counter% es menor y % build.counter% es parche. la puesta en escena es una etiqueta de prelanzamiento.

Deploy.Env


imagen

Al agregar un nuevo módulo, solo se agrega la configuración para su implementación y se indica el valor de la variable env.modName . Todas las demás variables se heredan de los proyectos principales. La variable env.tenantModTag para un pulpo se forma a partir de env.modName .

Implementar.2


imagen

Implementar paquete principal. 

Número de versión más reciente: use la última versión disponible para este entorno.
Etiqueta de inquilino: etiqueta de cliente / organización. Más detalles en la sección de pulpo.
Parámetros adicionales de cli. Aquí indicamos el canal en el que caerá nuestro paquete, así como los parámetros adicionales. Esto también se discutirá por separado en la sección de pulpo.

Implementar.3


Al igual que Deploy.2, solo se implementa un paquete de módulo individual.

Implementar.4


Este paso también se describirá en detalle a continuación, ya que recibe datos del pulpo.

Las configuraciones para el sitio web de la empresa y el módulo especial se basan en el mismo principio, y los puntos principales en ellos son los mismos, por lo que no veo el punto de describirlos con el mismo detalle. Son uno, no necesitan implementarse muchas veces, y de hecho hay restauración nuget, transformación de texto, msbuild, push de pulpo, octopus create release + deploy.

Obtenga más información sobre configuraciones únicas. Para algunos mods, puede que no haya entornos (por ejemplo, etapas), deben implementarse en otros servidores o debe establecer manualmente el identificador del cliente (tome la configuración básica y cambie algunos campos en él usando powershell). Es decir, funcionan según el mismo principio que los módulos principales. Su singularidad radica en el hecho de que se agregan / cambian pasos para ellos, o se cambia el servidor de implementación y no se ajustan a la plantilla, por lo que lleva un poco más de tiempo crearlos. Afortunadamente, hay pocos de ellos.

Teamcity: resumen


La implementación de teamcity permitió un enfoque más óptimo para el proceso de ensamblaje de la aplicación. Ahora lleva mucho menos tiempo: en lugar de crear el mismo código cada vez, lo creamos una vez. También hacemos un mejor uso del espacio en disco, el tráfico de red y el tiempo de computación. Anteriormente, el número de paquetes para implementación (repositorios con artefactos) era igual al número de módulos. Cada paquete pesaba aproximadamente 100M en forma comprimida. Ahora tenemos el número de paquetes uno más que el número de módulos, con un paquete que pesa aproximadamente los mismos 100M y el resto unos 500K. Además de una interfaz más fácil de usar, registros convenientes y, lo que es más importante, reduce el tiempo de mantenimiento del sistema de compilación y agrega configuraciones. Ahora agregar un nuevo módulo lleva de 15 minutos a media hora.

Por separado, debe decirse sobre las plantillas, ya que es la estandarización lo que nos permite ahorrar tiempo en la creación de nuevas configuraciones y su posterior mantenimiento. Todas las configuraciones para interactuar con Octopus (desplegar) se basan en una sola plantilla. En primer lugar, permite unificar y, por lo tanto, simplificar el proceso. En segundo lugar, como ya se mencionó, ahorra tiempo al agregar nuevas configuraciones: hemos conectado una plantilla, cambiado una variable, y eso es todo. Y lo más importante, las plantillas simplifican el mantenimiento. Todos los cambios realizados en la plantilla son heredados por las configuraciones basadas en ella. Por lo tanto, si necesitamos agregar algún paso a todas las configuraciones a la vez, no necesitamos hacer clic en todas, simplemente agregue este paso a la plantilla.

Naturalmente, el proceso de configuración de teamcity fue de la mano con la configuración de Octopus. Es simplemente imposible describir en paralelo en el texto, ya que existe una gran posibilidad de confusión. Por lo tanto, la descripción se ha compartido y en esta parte solo se describen los pasos para proporcionar CI. El proceso de configuración del pulpo se describirá a continuación.

All Articles