Mise à jour du processus CI / CD: teamcity

image
Il s'agit du deuxième article d'une série sur la mise à jour des processus CI / CD. Jusque-là, des préparatifs étaient en cours pour la mise en place de nouveaux outils, à savoir: la planification, les rassemblements quotidiens, la résolution des désaccords, en général, sans lesquels il ne serait pas possible de construire avec compétence un processus de travail. Et maintenant, tous les problèmes ont été résolus, nous sommes convaincus que nous pouvons poursuivre le développement, et notre code ne s'enfoncera pas dans le trou noir avec le début de l'été. 

Permettez-moi de vous rappeler la table des matières de la liste des articles, ainsi que les brefs résultats de la partie précédente.

Partie 1: ce qui est, pourquoi ce n'est pas comme, la planification, un petit coup bash. J'appellerais cette partie presque technique.
Partie 2: teamcity.
Partie 3: déploiement de poulpe.
Partie 4: dans les coulisses. Moments désagréables, plans pour l'avenir, éventuellement FAQ. Très probablement, il peut également être qualifié de quasi-technique.

Nous avons fourni au client une preuve de concept d'un nouveau système de livraison de mise à jour, identifié les lacunes du système existant, sélectionné la base du nouveau, élaboré un plan de transition et converti notre référentiel mercurial en git. De plus, dans la partie précédente, nous avons décrit les caractéristiques du projet qui affecteront l'ensemble du processus ultérieur. 

Comme mentionné précédemment, la solution existante ne répondait pas aux exigences modernes. Initialement, très probablement, une version a été configurée dans ccnet. La toute première construction avec laquelle tout a commencé (comme un big bang, ouais). Ensuite, apparemment, la personne qui a tout configuré, a appuyé sur ctrl + c, ctrl + v, a corrigé quelques lignes et a reçu une nouvelle configuration. Et pour tout le temps suivant, cela a été fait un nombre suffisant de fois pour que le code source sur le serveur de build prenne plus de 60G. Et ce ne sont que la source! Et il y a des journaux, des artefacts de construction, des fichiers temporaires, etc. Seulement cela m'a fait réfléchir. En regardant l'ancien système, on pouvait voir ce qui suit: une étape de construction a été effectuée pour chaque module. En fait, le même noyau a été assemblé. Et cet assemblage a été stocké pour chaque module (ainsi que la source) sur le serveur.Les résultats de la construction (identiques à 90%) ont été transférés dans des référentiels locaux (déploiement git), et ont bien sûr également pris leur place. Il était possible et nécessaire de laisser cela. 

Réglages généraux 


À ce stade, il est supposé qu'il existe déjà une équipe et une pieuvre configurées. Au stade de l'installation et de la configuration, nous ne nous arrêtons pas en détail. Pour plus de commodité, nous écrivons l'url et le jeton api de la pieuvre dans env teamcity, qui, en passant, pour le projet racine ressemble à ceci:

image

env.apiUserName et env.apiUserPassword sont des accès api à l'utilisateur teamcity (seront nécessaires plus tard), env.buildPath et env .sourcePath - le chemin par défaut vers les fichiers de génération et source, respectivement, env.octoApiKey et env.octoUrl - le jeton et l'url de la pieuvre. Ils ont toujours env.teamcityUrl ajouté. Eh bien, env.tt.exe- chemin vers l'utilitaire de transformation de texte. Cet utilitaire génère toutes les configurations.

Parmi les plugins tiers, seule l'intégration d'Octopus Deploy est installée. 

Afin de ne pas surcharger la quantité déjà énorme de texte, seules les étapes dans lesquelles il y a quelque chose qui mérite l'attention seront examinées en détail. On suppose que tout le reste est compréhensible sur la base des données présentées et des connaissances du lecteur. En tout cas, si vous avez des questions, je me ferai un plaisir d'y répondre.

Répartition du projet, versioning, noms


Le résultat était un tel schéma de projet (comme une image, car il y a un problème avec l'affichage d'une liste à plusieurs niveaux):
image

Nous allons versionner les packages comme suit: major.minor.patch, où major est toujours 1, mineur - compteur de build de la configuration de construction, patch - compteur de build pour la configuration Déployer (pas nécessaire pour la configuration de Build, donc toujours 0).

Remarque: l' ancienne version est décrite ici. Nous le repensons pour qu'il soit basé sur la balise git.

image

Par exemple, ici 1 est majeur, 95 est mineur et 1 est patch.

Dans le nouveau processus pour les modules, il y aura une build qui sera ensuite déployée sur chaque module. Autrement dit, le code source est stocké dans une seule copie pour chaque environnement, le résultat de la construction l'est également, car il est commun à tous les modules. Les configurations et bibliothèques uniques sont regroupées dans un package individuel au stade du déploiement. Cela utilisera efficacement l'espace disque et réduira le délai de livraison de chaque paquet.

Au stade de la préparation, il y a eu une brève réunion séparée sur la convention de dénomination des fichiers de configuration. C'était (c'était juste), mais pas toujours pareil. De plus, le nom de la configuration ne contenait pas toujours suffisamment d'informations. Par conséquent, tous les noms de configuration étaient les suivants:
ConfigurationType.Environment.ModName.configoù ConfigurationType peut être AppSettings, Web, etc. Environnement - développement, test, mise en scène, production. ModName est le nom unique du module.

Modules


Tous les projets de modules fonctionnent sur le même principe. 

Chaque environnement a une configuration Build qui:

  1. Effectue la restauration de nuget (programme d'installation NuGet, restauration de nuget)
  2. Génère des configurations (ligne de commande, transformation de texte)
  3. Solution Buildit (Visual Studio SLN)
  4. Récupère les fichiers de env.buildPath dans zip et les pousse dans octopus (Octopus deploy: push packages)
  5. Création de version (pas de déploiement) (Octopus deploy create release)
  6. Réinitialise la version du correctif (PowerShell)

Il existe également des configurations de déploiement qui fonctionnent sur la base du modèle et effectuent les opérations suivantes:

  1. Récupère les configurations générées au stade de la construction (étape 2) (Octopus déploie des packages push)
  2. Déployer la version principale (qui a été créée à l'étape 6 de la build) (Octopus deploy: deploy release)
  3. Version individuelle de déploiement (créée à l'étape 1 de la configuration actuelle) (Octopus deploy: deploy release)
  4. Affiche une liste de tous les domaines pour ce module dans le journal (étape pour la commodité du testeur et du développeur). (Powershell).

Pour chaque configuration de déploiement, la configuration de génération est ajoutée à la dépendance. Cela permet de lancer automatiquement la build lorsqu'elle est déployée si elle n'est pas pertinente, ainsi que la possibilité de réinitialiser la version du patch.

Configuration de Deploy_all. En fait, il s'agit d'une chaîne de génération qui décrit que vous devez d'abord exécuter la génération si elle n'est pas pertinente, puis installer simultanément tous les modes.

Cela ressemble à ceci:

image

Regardons certaines des étapes un peu plus en détail.

Build.General settings


image

Du format de numéro de build inhabituel uniquement. Sera expliqué plus tard.

Build.env


image

Voici un intérêt:
env.coreProjectName - défini pour l'ensemble du projet. Nécessaire pour identifier le projet dans la pieuvre.
env.Environment - défini pour le sous-projet Modules. Il est nécessaire de sélectionner les bonnes configurations en fonction de l'environnement.
env.octoChannel - le canal de la pieuvre dans lequel le paquet tombe. Identique à l' environnement .
env.serviceName est le nom de la solution. Fondamentalement, la variable est ajoutée pour faciliter la visualisation.
env.tenantRoleTag - balise de locataire dans la pieuvre. Plus de détails dans la section poulpe.

Build.4


image

Ajoutez à l'archive avec le nom % env.serviceName%.% Build.number% .zip fichiers du répertoire avec le résultat de la construction, à l'exclusion de toutes les configurations, et dll du dossier bin / native. Les derniers ont été ajoutés manuellement au serveur Web une fois et personne ne les touchera à nouveau. Si nécessaire, ils seront également mis à jour manuellement (pour cela, il est prévu d'écrire un script séparé, mais nous serons honnêtes). Cela est dû au fait que ces bibliothèques sont «détenues» par le pool IIS, et pour un déploiement réussi, il doit être arrêté puis lancé, ce qui prend un certain temps. En excluant ces bibliothèques du déploiement, nous pouvons ignorer l'étape d'arrêt du pool, ce qui réduira le temps de déploiement et, par conséquent, les temps d'arrêt.

Build.5


image

Je pense que tout est clair ici, sauf pourquoi le champ Environnement est vide. Dans ce que le paquet obtient, la pieuvre prend une décision basée sur la balise de pré-version (configurée dans les canaux). Cette balise est contenue dans % build.number% (la même mise en scène à l'écran dans Build.General). Dans ce cas, cela s'est avéré plus pratique. Il convient également de prêter attention à la case «Afficher le processus de déploiement» . S'il n'est pas installé, timsity considère la construction réussie dès que la pieuvre a commencé (mais pas terminé!) Le travail. Autrement dit, si la case à cocher n'est pas installée et que quelque chose s'est mal passé pendant la phase de déploiement - sans regarder l'interface octopus, nous ne le saurons pas.

Build.6


Rien d'inhabituel, juste un script PowerShell et des informations de la documentation 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
}

Ici, nous avons besoin d'un utilisateur api dans teamcity (env est créé pour lui). Nous nous connectons, prenons toutes les configurations qui dépendent de la configuration de Build et remettons leur compteur de build à 1. C'est-à-dire, en fait, pour chaque version de patch, c'est un indicateur du nombre de fois où la même build a été déployée pour un module particulier. S'il n'y en a pas 1, cela signifie que quelque chose s'est mal passé avec le processus de déploiement ou de travail du module sur la version actuelle, et nécessite une attention.

Déployer.Général


image

Format de version: 1.% dep.Module_Build.build.counter%.% Build.counter% -staging , où
% dep.Module_Build.build.counter% est la variable système teamcity dont la valeur correspond au compteur de build de la configuration de build correspondante (doit être ajoutée à dépendances). En fait, ici 1 est la version principale, % dep.Module_Build.build.counter% est mineur et % build.counter% est patch. staging est une balise de pré-version.

Deploy.Env


image

Lors de l'ajout d'un nouveau module, seule la configuration de son déploiement est ajoutée et la valeur de la variable env.modName est indiquée . Toutes les autres variables sont héritées des projets parents. La variable env.tenantModTag pour une pieuvre est formée à partir de env.modName .

Deploy.2


image

Déployer le package principal. 

Numéro de version le plus récent: utilisez la dernière version disponible pour cet environnement.
Tag locataire - tag client / organisation. Plus de détails dans la section poulpe.
Paramètres cli supplémentaires. Ici, nous indiquons le canal dans lequel notre package tombera, ainsi que des paramètres supplémentaires. Cela sera également discuté séparément dans la section poulpe.

Déployer.3


Comme Deploy.2, seul un package de module individuel est déployé.

Déployer.4


Cette étape sera également décrite en détail ci-dessous, car elle reçoit des données de la pieuvre.

Les configurations pour le site Web de la société et le module spécial sont construites sur le même principe, et les principaux points y sont les mêmes, donc je ne vois pas l'intérêt de les décrire avec le même détail. Ils ne font qu'un, ils n'ont pas besoin d'être déployés plusieurs fois, et en fait il y a restauration de nuget, transformation de texte, msbuild, octopus push, octopus create release + deploy.

En savoir plus sur les configurations uniques. Pour certains mods, il peut ne pas y avoir d'environnements (par exemple, de transfert), ils doivent être déployés sur d'autres serveurs, ou vous devez définir manuellement l'identifiant client (prenez la configuration de base et modifiez certains champs à l'aide de PowerShell). Autrement dit, ils fonctionnent sur le même principe que les modules principaux. Leur caractère unique réside dans le fait que des étapes sont ajoutées / modifiées pour eux, ou que le serveur de déploiement est modifié et qu'ils ne rentrent pas dans le modèle, il faut donc un peu plus de temps pour les créer. Heureusement, il y en a peu.

Teamcity: Résumé


L'implémentation de teamcity a permis une approche plus optimale du processus d'assemblage des applications. Maintenant, cela prend beaucoup moins de temps: au lieu de construire le même code à chaque fois, nous le construisons une fois. Nous utilisons également mieux l'espace disque, le trafic réseau et le temps de calcul. Auparavant, le nombre de packages à déployer (référentiels avec artefacts) était égal au nombre de modules. Chaque colis pesait environ 100M sous forme compressée. Nous avons maintenant le nombre de packages un de plus que le nombre de modules, avec un package pesant environ les mêmes 100M, et le reste environ 500K. De plus, une interface plus conviviale, des journaux pratiques et, surtout, réduisant le temps de maintenance du système de construction et l'ajout de configurations. L'ajout d'un nouveau module prend maintenant de 15 minutes à une demi-heure.

Séparément, il convient de dire à propos des modèles, car c'est la normalisation qui nous permet de gagner du temps sur la création de nouvelles configurations et leur maintenance ultérieure. Toutes les configurations d'interaction avec Octopus (déploiement) sont construites sur la base d'un modèle unique. Tout d'abord, il permet d'unifier, et donc de simplifier le processus. Deuxièmement, comme déjà mentionné, cela fait gagner du temps sur l'ajout de nouvelles configurations: nous avons connecté un modèle, changé une variable, et c'est tout. Et surtout, les modèles simplifient la maintenance. Toutes les modifications apportées au modèle sont héritées par des configurations basées sur celui-ci. Donc, si nous devons ajouter une étape à toutes les configurations à la fois, nous n'avons pas besoin de toutes les cliquer, ajoutez simplement cette étape au modèle.

Naturellement, le processus de configuration de teamcity allait de pair avec la configuration de poulpe. Il est tout simplement impossible de décrire en parallèle dans le texte, car il existe un grand risque de confusion. Par conséquent, la description a été partagée et dans cette partie, seules les étapes de fourniture de CI sont décrites. Le processus de configuration de la pieuvre sera décrit ci-dessous.

All Articles