
Bonjour ! Récemment, de nombreux outils d'automatisation intéressants ont été lancés, tant pour la création d'images Docker que pour le déploiement dans Kubernetes. J'ai donc décidé d'explorer GitLab, d'étudier ses fonctionnalités et bien sûr, de configurer un pipeline.
L'inspiration pour ce travail a été le site , qui est généré à partir de automatiquement, et pour chaque demande de tirage envoyée, le robot génÚre automatiquement une version d'aperçu du site avec vos modifications et fournit un lien pour sa consultation.
J'ai tenté d'établir un processus similaire depuis le début, mais entiÚrement construit sur GitLab CI et des outils gratuits que j'ai l'habitude d'utiliser pour déployer des applications dans Kubernetes. Aujourd'hui, je vais enfin vous en parler plus en détail.
Cet article abordera des outils tels que :
Hugo, qbec, kaniko, git-crypt et GitLab CI pour la création d'environnements dynamiques.
Table des matiĂšres
1. Découverte de Hugo
Pour l'exemple de notre projet, nous allons essayer de créer un site pour la publication de documentation, construit sur Hugo. Hugo est un générateur de contenu statique.
Pour ceux qui ne connaissent pas les gĂ©nĂ©rateurs statiques, je vais en dire un peu plus. Contrairement aux moteurs de sites classiques avec une base de donnĂ©es et quelque chose comme du php, qui, Ă la demande de l'utilisateur, gĂ©nĂšrent des pages Ă la volĂ©e, les gĂ©nĂ©rateurs statiques fonctionnent un peu diffĂ©remment. Ils permettent de prendre des sources, gĂ©nĂ©ralement un ensemble de fichiers en Markdown et des modĂšles de thĂšmes, puis de les compiler en un site complĂštement prĂȘt.
Cela signifie qu'à la sortie, vous obtiendrez une structure de répertoires et un ensemble de fichiers html générés, que vous pourrez simplement télécharger sur n'importe quel hébergement bon marché et obtenir un site opérationnel.
Hugo peut ĂȘtre installĂ© localement et essayĂ© :
Initialisons un nouveau site :
hugo new site docs.example.orgEt en mĂȘme temps, un dĂ©pĂŽt git :
cd docs.example.org
git initPour l'instant, notre site est vierge et pour y voir quelque chose, nous devons d'abord connecter un thÚme, le thÚme n'est rien d'autre qu'un ensemble de modÚles et de rÚgles définies selon lesquelles notre site est généré.
Nous allons utiliser comme thĂšme , qui, Ă mon avis, convient parfaitement pour un site de documentation.
Une attention particuliĂšre doit ĂȘtre accordĂ©e au fait que nous n'avons pas besoin de conserver les fichiers du thĂšme dans le dĂ©pĂŽt de notre projet, Ă la place, nous pouvons simplement lâajouter en utilisant git submodule:
git submodule add https://github.com/matcornic/hugo-theme-learn themes/learnAinsi, notre dĂ©pĂŽt contiendra uniquement les fichiers directement liĂ©s Ă notre projet, tandis que le thĂšme connectĂ© restera sous forme de lien vers un dĂ©pĂŽt spĂ©cifique et un commit dedans, ce qui signifie qu'il pourra toujours ĂȘtre rĂ©cupĂ©rĂ© Ă partir de la source originale sans craindre d'incompatibilitĂ©s.
Corrigeons le config config.toml:
baseURL = "http://docs.example.org/"
languageCode = "en-us"
title = "Mon site de documentation"
theme = "learn"à ce stade, nous pouvons déjà lancer :
hugo serverEt à l'adresse vérifier notre site nouvellement créé, toutes les modifications apportées dans le répertoire mettent automatiquement à jour la page ouverte dans le navigateur, trÚs pratique !
Essayons de créer une page d'accueil dans content/_index.md:
# My docs site
## Welcome to the docs!
You will be very smart :-)Capture d'écran de la page nouvellement créée

Pour générer le site, il suffit de lancer :
hugoLe contenu du répertoire public/ sera votre site.
Au fait, ajoutons-le directement dans .gitignore:
echo /public > .gitignoreNâoublions pas de valider nos modifications :
git add .
git commit -m "Nouveau site créé"2. Préparation du Dockerfile
Il est temps de définir la structure de notre dépÎt. En général, j'utilise quelque chose comme :
.
âââ deploy
â âââ app1
â âââ app2
âââ dockerfiles
âââ image1
âââ image2- dockerfiles/ contiennent des rĂ©pertoires avec des Dockerfiles et tout le nĂ©cessaire pour construire nos images docker.
- deploy/ contient des répertoires pour déployer nos applications dans Kubernetes
Ainsi, notre premier Dockerfile sera créé dans dockerfiles/website/Dockerfile
FROM alpine:3.11 as builder
ARG HUGO_VERSION=0.62.0
RUN wget -O- https://github.com/gohugoio/hugo/releases/download/v${HUGO_VERSION}/hugo_${HUGO_VERSION}_linux-64bit.tar.gz | tar -xz -C /usr/local/bin
ADD . /src
RUN hugo -s /src
FROM alpine:3.11
RUN apk add --no-cache darkhttpd
COPY --from=builder /src/public /var/www
ENTRYPOINT [ "/usr/bin/darkhttpd" ]
CMD [ "/var/www" ]Comme vous pouvez le constater, le Dockerfile contient deux DE, cette fonctionnalité s'appelle et permet d'exclure tout ce qui est inutile du docker-image final.
Ainsi, l'image finale contiendra uniquement darkhttpd (un serveur HTTP lĂ©ger) et public/ â le contenu de notre site statiquement gĂ©nĂ©rĂ©.
Nâoublions pas de valider nos modifications :
git add dockerfiles/website
git commit -m "Ajouter Dockerfile pour le site"3. Introduction Ă kaniko
En tant qu'assembleur d'images docker, j'ai choisi d'utiliser , car son fonctionnement ne nĂ©cessite pas de dĂ©mon docker, et la construction peut ĂȘtre effectuĂ©e sur n'importe quelle machine, tout en stockant le cache directement dans le registre, ce qui Ă©limine la nĂ©cessitĂ© d'avoir un stockage persistant complet.
Pour construire l'image, il suffit de lancer un conteneur avec kaniko executor et de lui transmettre le contexte de construction actuel, cela peut ĂȘtre fait localement, via docker :
docker run -ti --rm
-v $PWD: /workspace
-v ~/ .docker / config.json: /kaniko / .docker / config.json: ro
gcr.io / kaniko-project / executor:v0.15.0
--cache
--dockerfile=dockerfiles / website / Dockerfile
--destination=registry.gitlab.com / kvaps / docs.example.org / website:v0.0.1OĂč registry.gitlab.com / kvaps / docs.example.org / website â le nom de votre image docker, aprĂšs la construction, elle sera automatiquement poussĂ©e dans le registre docker.
ParamĂštre âcache permet de mettre en cache les couches dans le registre docker. Dans l'exemple donnĂ©, elles seront sauvegardĂ©es dans registry.gitlab.com / kvaps / docs.example.org / website / cache, mais vous pouvez Ă©galement spĂ©cifier un autre chemin Ă l'aide du paramĂštre âcache-repo.
Capture d'écran du docker-registry

4. Introduction Ă qbec
est un outil de déploiement qui permet de décrire de maniÚre déclarative les manifestes de votre application et de les déployer dans Kubernetes. L'utilisation de Jsonnet comme syntaxe principale permet de simplifier considérablement la description des différences pour plusieurs environnements, tout en éliminant presque entiÚrement la répétabilité du code.
Cela peut ĂȘtre particuliĂšrement pertinent dans les cas oĂč vous devez dĂ©ployer une application sur plusieurs clusters avec des paramĂštres diffĂ©rents et souhaiter les dĂ©crire de maniĂšre dĂ©clarative dans Git.
Qbec permet Ă©galement de rendre les graphiques Helm en leur transmettant les paramĂštres nĂ©cessaires, et ensuite de les manipuler comme des manifestes ordinaires, y compris la possibilitĂ© d'appliquer diverses mutations, ce qui permet d'Ă©viter d'utiliser ChartMuseum. Ainsi, il est possible de stocker et de rendre des graphiques directement depuis git, oĂč ils ont leur place.
Comme je l'ai dit précédemment, tous les déploiements seront stockés dans le répertoire deploy/:
mkdir deploy
cd deployInitialisons notre premiĂšre application :
qbec init website
cd websiteActuellement, la structure de notre application ressemble Ă cela :
.
âââ components
âââ environments
â âââ base.libsonnet
â âââ default.libsonnet
âââ params.libsonnet
âââ qbec.yamljetons un Ćil au fichier qbec.yaml:
apiVersion: qbec.io / v1alpha1
kind: App
metadata:
name: website
spec:
environments:
default:
defaultNamespace: docs
server: https: / / kubernetes.example.org:8443
vars: {}Ici, nous nous intéressons principalement à spec.environments, qbec a déjà créé pour nous un environnement par défaut et a pris l'adresse du serveur ainsi que le namespace de notre kubeconfig actuel.
Maintenant, lors du déploiement dans default l'environnement, qbec déploiera toujours uniquement dans le cluster Kubernetes spécifié et dans le namespace spécifié, ce qui signifie que vous n'aurez plus besoin de basculer entre les contextes et les namespaces pour effectuer un déploiement.
En cas de besoin, vous pouvez toujours mettre Ă jour les paramĂštres dans ce fichier.
Tous vos environnements sont dĂ©crits dans qbec.yaml, et dans le fichier params.libsonnet, oĂč il est indiquĂ© d'oĂč prendre les paramĂštres pour eux.
Ensuite, nous voyons deux répertoires :
- components/ â tous les manifests pour notre application seront stockĂ©s ici, ils peuvent ĂȘtre dĂ©crits soit en jsonnet soit dans des fichiers yaml ordinaires
- environments/ â ici, nous dĂ©crirons toutes les variables (paramĂštres) pour nos environnements.
Par défaut, nous avons deux fichiers :
- environments/base.libsonnet â il contiendra des paramĂštres communs pour tous les environnements
- environments/default.libsonnet â contient des paramĂštres redĂ©finis pour l'environnement default
Ouvrons environments/base.libsonnet et ajoutons les paramĂštres pour notre premier composant :
{
components: {
website: {
name: 'example-docs',
image: 'registry.gitlab.com/kvaps/docs.example.org/website:v0.0.1',
replicas: 1,
containerPort: 80,
servicePort: 80,
nodeSelector: {},
tolerations: [],
ingressClass: 'nginx',
domain: 'docs.example.org',
},
},
}Créons également notre premier composant components/website.jsonnet:
local env = {
name: std.extVar('qbec.io/env'),
namespace: std.extVar('qbec.io/defaultNs'),
};
local p = import '../params.libsonnet';
local params = p.components.website;
[
{
apiVersion: 'apps/v1',
kind: 'Deployment',
metadata: {
labels: { app: params.name },
name: params.name,
},
spec: {
replicas: params.replicas,
selector: {
matchLabels: {
app: params.name,
},
},
template: {
metadata: {
labels: { app: params.name },
},
spec: {
containers: [
{
name: 'darkhttpd',
image: params.image,
ports: [
{
containerPort: params.containerPort,
},
],
},
],
nodeSelector: params.nodeSelector,
tolerations: params.tolerations,
imagePullSecrets: [{ name: 'regsecret' }],
},
},
},
},
{
apiVersion: 'v1',
kind: 'Service',
metadata: {
labels: { app: params.name },
name: params.name,
},
spec: {
selector: {
app: params.name,
},
ports: [
{
port: params.servicePort,
targetPort: params.containerPort,
},
],
},
},
{
apiVersion: 'extensions/v1beta1',
kind: 'Ingress',
metadata: {
annotations: {
'kubernetes.io/ingress.class': params.ingressClass,
},
labels: { app: params.name },
name: params.name,
},
spec: {
rules: [
{
host: params.domain,
http: {
paths: [
{
backend: {
serviceName: params.name,
servicePort: params.servicePort,
},
},
],
},
},
],
},
},
]Dans ce fichier, nous avons décrit trois entités Kubernetes : Déploiement, Service et Ingress. Si nous le souhaitions, nous pourrions les séparer en différents composants, mais à ce stade, un seul nous suffit.
Syntaxe jsonnet ressemble beaucoup Ă du json normal, en fait, du json normal est dĂ©jĂ un jsonnet valide, donc au dĂ©but, il vous sera peut-ĂȘtre plus facile d'utiliser des services en ligne comme yaml2json pour convertir votre yaml habituel en json, ou, si vos composants ne contiennent aucune variable, vous pouvez tout Ă fait les dĂ©crire sous forme de yaml normal.
Lors de l'utilisation de jsonnet je vous recommande vivement d'installer un plugin pour votre éditeur.
Par exemple, pour vim, il existe un plugin vim-jsonnet, qui ajoute la coloration syntaxique et exécute automatiquement jsonnet fmt à chaque sauvegarde (nécessite l'installation de jsonnet).
Tout est prĂȘt, nous pouvons maintenant commencer le dĂ©ploiement :
Pour voir ce que nous avons obtenu, exécutons :
qbec show defaultEn sortie, vous verrez les manifestes yaml rendus qui seront appliqués dans le cluster par défaut.
Super, maintenant appliquons :
qbec apply defaultEn sortie, vous verrez toujours ce qui sera fait dans votre cluster, qbec vous demandera de confirmer les changements en tapant y vous pourrez confirmer vos intentions.
C'est fait, notre application est déployée !
En cas de modifications, vous pourrez toujours exécuter :
qbec diff defaultpour voir comment ces changements vont affecter le déploiement actuel.
Nâoublions pas de valider nos modifications :
cd ..\/..\ngit add deploy\/website\ngit commit -m "Ajout du déploiement pour le site web"5. Essayons Gitlab-runner avec Kubernetes-executor.
Jusqu'à récemment, j'utilisais uniquement des exécutants conventionnels. gitlab-runner sur une machine préparée à l'avance (un conteneur LXC) avec un shell- ou docker-executor. Au départ, nous avions plusieurs de ces exécutants définis globalement dans notre GitLab. Ils construisaient des images Docker pour tous les projets.
Mais comme l'a montré la pratique, cette option n'est pas la plus optimale, que ce soit en termes de praticité ou de sécurité. Il est de loin préférable et idéologiquement plus juste d'avoir des exécutants déployés pour chaque projet, voire pour chaque environnement.
Heureusement, ce n'est pas un problÚme, car maintenant nous allons déployer gitlab-runner directement comme partie de notre projet dans Kubernetes.
Gitlab fournit un chart helm prĂȘt Ă l'emploi pour dĂ©ployer gitlab-runner dans Kubernetes. Donc, tout ce dont vous avez besoin, c'est de connaĂźtre le registration token de notre projet dans ParamĂštres â> CI \/ CD â> ExĂ©cutants et de le transmettre Ă helm :
helm repo add gitlab https:\/\/charts.gitlab.io\n\nhelm install gitlab-runner \n --set gitlabUrl=https:\/\/gitlab.com \n --set runnerRegistrationToken=yga8y-jdCusVDn_t4Wxc \n --set rbac.create=true \n gitlab\/gitlab-runnerOĂč :
- â l'adresse de votre serveur Gitlab.
- yga8y-jdCusVDn_t4Wxc â registration token pour votre projet.
- rbac.create=true â donne Ă l'exĂ©cutant les privilĂšges nĂ©cessaires pour pouvoir crĂ©er des pods afin d'exĂ©cuter nos tĂąches avec l'exĂ©cuteur Kubernetes.
Si tout a été fait correctement, vous devriez voir l'exécutant enregistré dans la section Exécutants, dans les paramÚtres de votre projet.
Capture d'écran de l'exécutant ajouté.

C'est aussi simple que ça ? â oui, aussi simple ! Plus de tracas avec l'enregistrement manuel des exĂ©cutants, Ă partir de maintenant, les exĂ©cutants seront créés et dĂ©truits automatiquement.
6. Déploiement des charts Helm avec QBEC
Puisque nous avons décidé de considérer gitlab-runner comme faisant partie de notre projet, il est temps de le décrire dans notre dépÎt Git.
Nous pourrions le décrire comme un composant séparé site web, mais à l'avenir, nous prévoyons de déployer différentes copies site web trÚs souvent, contrairement gitlab-runner, qui ne sera déployé qu'une seule fois sur chaque cluster Kubernetes. Alors, initialisons une application distincte pour cela :
cd deploy\nqbec init gitlab-runner\ncd gitlab-runnerCette fois, nous n'allons pas dĂ©crire les entitĂ©s Kubernetes manuellement, mais nous allons utiliser un chart Helm prĂȘt Ă l'emploi. Un des avantages de qbec est la possibilitĂ© de rendre les charts Helm directement depuis un dĂ©pĂŽt Git.
Connectons-le en utilisant git submodule :
git submodule add https://gitlab.com/gitlab-org/charts/gitlab-runner vendor/gitlab-runnerLe répertoire vendor/gitlab-runner contient le dépÎt avec le chart pour gitlab-runner.
De la mĂȘme maniĂšre, vous pouvez connecter d'autres dĂ©pĂŽts, par exemple, le dĂ©pĂŽt entier avec les charts officiels.
Décrivons le composant components/gitlab-runner.jsonnet:
local env = {
name: std.extVar('qbec.io/env'),
namespace: std.extVar('qbec.io/defaultNs'),
};
local p = import '../params.libsonnet';
local params = p.components.gitlabRunner;
std.native('expandHelmTemplate')(
'../vendor/gitlab-runner',
params.values,
{
nameTemplate: params.name,
namespace: env.namespace,
thisFile: std.thisFile,
verbose: true,
}
)Le premier argument pour expandHelmTemplate est le chemin vers le chart, puis params.values, que nous allons prendre Ă partir des paramĂštres d'environnement, suivi d'un objet contenant
- nameTemplate â le nom de la release
- namespace â le namespace transmis Ă Helm
- thisFile â un paramĂštre obligatoire, transmettant le chemin vers le fichier actuel
- verbose â montre la commande helm template avec tous les arguments lors du rendu du chart.
Décrivons maintenant les paramÚtres pour notre composant dans environments/base.libsonnet:
local secrets = import '../secrets/base.libsonnet';
{
components: {
gitlabRunner: {
name: 'gitlab-runner',
values: {
gitlabUrl: 'https://gitlab.com/',
rbac: {
create: true,
},
runnerRegistrationToken: secrets.runnerRegistrationToken,
},
},
},
}Veuillez noter runnerRegistrationToken nous le prenons du fichier externe secrets/base.libsonnet, créons-le :
{
runnerRegistrationToken: 'yga8y-jdCusVDn_t4Wxc',
}Vérifions si tout fonctionne :
qbec show defaultsi tout est en ordre, nous pouvons supprimer notre release précédemment déployée via Helm :
helm uninstall gitlab-runneret le redéployer, mais déjà via qbec :
qbec apply default7. Introduction Ă git-crypt
est un outil qui permet de configurer un chiffrement transparent pour votre dépÎt.
Actuellement, la structure de notre répertoire pour gitlab-runner ressemble à cela :
.
âââ components
â âââ gitlab-runner.jsonnet
âââ environments
â âââ base.libsonnet
â âââ default.libsonnet
âââ params.libsonnet
âââ qbec.yaml
âââ secrets
â âââ base.libsonnet
âââ vendor
âââ gitlab-runner (submodule)Mais stocker des secrets dans Git n'est pas sĂ»r, n'est-ce pas ? Il nous faut donc les chiffrer correctement.
En général, pour une seule variable, ce n'est pas toujours logique. Vous pouvez transmettre des secrets dans qbec et via des variables d'environnement dans votre systÚme CI.
Il convient de noter qu'il existe des projets plus complexes qui peuvent contenir beaucoup plus de secrets, et les transmettre tous via des variables d'environnement serait extrĂȘmement difficile.De plus, dans ce cas, je n'aurais pas pu vous parler d'un outil aussi formidable que git-crypt.
git-crypt il est également pratique car il permet de conserver tout l'historique des secrets, ainsi que de comparer, fusionner et résoudre les conflits comme nous avons l'habitude de le faire avec Git.
La premiÚre chose à faire aprÚs l'installation git-crypt est de générer des clés pour notre dépÎt :
git crypt initSi vous avez une clé PGP, vous pouvez immédiatement vous ajouter en tant que collaborateur à ce projet :
git-crypt add-gpg-user kvapss@gmail.comAinsi, vous pourrez toujours déchiffrer ce dépÎt en utilisant votre clé privée.
Si vous n'avez pas de clé PGP et qu'aucune n'est prévue, vous pouvez suivre une autre voie et exporter la clé du projet :
git crypt export-key /path/to/keyfileDe cette façon, quiconque possÚde le keyfile pourra déchiffrer votre dépÎt.
Il est temps de configurer notre premier secret.
Rappelons que nous sommes toujours dans le rĂ©pertoire deploy/gitlab-runner/, oĂč nous avons un rĂ©pertoire secrets/, crĂ©ons donc un fichier pour chiffrer tous les fichiers qui s'y trouvent secrets/.gitattributes avec ce contenu :
* filter=git-crypt diff=git-crypt
.gitattributes !filter !diffComme on peut le voir dans le contenu, tous les fichiers correspondant au modÚle * seront traités par git-crypt, à l'exception de .gitattributes
Nous pouvons vérifier cela en exécutant :
git crypt status -eEn sortie, nous obtiendrons la liste de tous les fichiers dans le dépÎt pour lesquels le chiffrement est activé.
VoilĂ , maintenant nous pouvons commettre nos changements :
cd ../..
git add .
git commit -m "Add deploy for gitlab-runner"Pour verrouiller le dépÎt, il suffit d'exécuter :
git crypt locket tous les fichiers chiffrés se transformeront en quelque chose de binaire, il sera impossible de les lire.
Pour déchiffrer le dépÎt, exécutez :
git crypt unlock8. Créons une image toolbox
L'image Toolbox est une image avec tous les outils que nous allons utiliser pour déployer notre projet. Elle sera utilisée par GitLab Runner pour exécuter des tùches de déploiement standard.
C'est simple, créons un nouveau dockerfiles/toolbox/Dockerfile avec ce contenu :
DE l'image alpine:3.11
RUN apk add --no-cache git git-crypt
RUN QBEC_VER=0.10.3
&& wget -O- https://github.com/splunk/qbec/releases/download/v${QBEC_VER}/qbec-linux-amd64.tar.gz
| tar -C /tmp -xzf -
&& mv /tmp/qbec /tmp/jsonnet-qbec /usr/local/bin/
RUN KUBECTL_VER=1.17.0
&& wget -O /usr/local/bin/kubectl
https://storage.googleapis.com/kubernetes-release/release/v${KUBECTL_VER}/bin/linux/amd64/kubectl
&& chmod +x /usr/local/bin/kubectl
RUN HELM_VER=3.0.2
&& wget -O- https://get.helm.sh/helm-v${HELM_VER}-linux-amd64.tar.gz
| tar -C /tmp -zxf -
&& mv /tmp/linux-amd64/helm /usr/local/bin/helmComme vous pouvez le constater, dans cette image, nous installons tous les outils que nous avons utilisés pour déployer notre application. à part cela, nous n'en avons pas besoin ici kubectl, mais vous pourriez vouloir l'expérimenter lors de la configuration du pipeline.
De plus, pour pouvoir communiquer avec Kubernetes et y déployer, nous devons configurer un rÎle pour les pods générés par gitlab-runner.
Pour cela, allons dans le répertoire du gitlab-runner :
cd deploy/gitlab-runneret ajoutons un nouveau composant components/rbac.jsonnet:
local env = {
name: std.extVar('qbec.io/env'),
namespace: std.extVar('qbec.io/defaultNs'),
};
local p = import '../params.libsonnet';
local params = p.components.rbac;
[
{
apiVersion: 'v1',
kind: 'ServiceAccount',
metadata: {
labels: {
app: params.name,
},
name: params.name,
},
},
{
apiVersion: 'rbac.authorization.k8s.io/v1',
kind: 'Role',
metadata: {
labels: {
app: params.name,
},
name: params.name,
},
rules: [
{
apiGroups: [
'*',
],
resources: [
'*',
],
verbs: [
'*',
],
},
],
},
{
apiVersion: 'rbac.authorization.k8s.io/v1',
kind: 'RoleBinding',
metadata: {
labels: {
app: params.name,
},
name: params.name,
},
roleRef: {
apiGroup: 'rbac.authorization.k8s.io',
kind: 'Role',
name: params.name,
},
subjects: [
{
kind: 'ServiceAccount',
name: params.name,
namespace: env.namespace,
},
],
},
]Nous allons également décrire les nouveaux paramÚtres dans environments/base.libsonnet, qui ressemble maintenant à :
local secrets = import '../secrets/base.libsonnet';
{
components: {
gitlabRunner: {
name: 'gitlab-runner',
values: {
gitlabUrl: 'https://gitlab.com/',
rbac: {
create: true,
},
runnerRegistrationToken: secrets.runnerRegistrationToken,
runners: {
serviceAccountName: $.components.rbac.name,
image: 'registry.gitlab.com/kvaps/docs.example.org/toolbox:v0.0.1',
},
},
},
rbac: {
name: 'gitlab-runner-deploy',
},
},
}Veuillez noter $.components.rbac.name référence à nom pour le composant rbac
Vérifions ce qui a changé :
qbec diff defaultet appliquons nos modifications dans Kubernetes :
qbec apply defaultN'oublions pas de valider nos modifications dans git :
cd ../../
git add dockerfiles/toolbox
git commit -m "Add Dockerfile for toolbox"
git add deploy/gitlab-runner
git commit -m "Configure gitlab-runner to use toolbox"9. Notre premier pipeline et la construction d'images par étiquettes
à la racine du projet, nous allons créer .gitlab-ci.yml avec ce contenu :
.build_docker_image:
stage: build
image:
name: gcr.io/kaniko-project/executor:debug-v0.15.0
entrypoint: [""]
before_script:
- echo "{"auths":{"$CI_REGISTRY":{"username":"$CI_REGISTRY_USER","password":"$CI_REGISTRY_PASSWORD"}}}" > /kaniko/.docker/config.json
build_toolbox:
extends: .build_docker_image
script:
- /kaniko/executor --cache --context $CI_PROJECT_DIR/dockerfiles/toolbox --dockerfile $CI_PROJECT_DIR/dockerfiles/toolbox/Dockerfile --destination $CI_REGISTRY_IMAGE/toolbox:$CI_COMMIT_TAG
only:
refs:
- tags
build_website:
extends: .build_docker_image
variables:
GIT_SUBMODULE_STRATEGY: normal
script:
- /kaniko/executor --cache --context $CI_PROJECT_DIR --dockerfile $CI_PROJECT_DIR/dockerfiles/website/Dockerfile --destination $CI_REGISTRY_IMAGE/website:$CI_COMMIT_TAG
only:
refs:
- tagsVeuillez noter que nous utilisons GIT_SUBMODULE_STRATEGY: normal pour les travaux oĂč il est nĂ©cessaire d'initialiser explicitement les sous-modules avant l'exĂ©cution.
Nâoublions pas de valider nos modifications :
git add .gitlab-ci.yml
git commit -m "Automatiser la construction de docker"Je pense qu'on peut l'appeler version v0.0.1 et ajouter la balise :
git tag v0.0.1Nous allons ajouter des balises chaque fois que nous devrons publier une nouvelle version. Les balises dans les images Docker seront liées aux balises Git. Chaque push avec une nouvelle balise lancera la construction d'images avec cette balise.
Nous exĂ©cuterons git push âtags, et regardons notre premier pipeline :
Capture d'écran du premier pipeline

Notez que la construction par balises convient pour la crĂ©ation d'images Docker, mais ne convient pas pour le dĂ©ploiement d'applications dans Kubernetes. Comme de nouvelles balises peuvent ĂȘtre attribuĂ©es Ă d'anciens commits, l'initialisation du pipeline pour celles-ci entraĂźnera le dĂ©ploiement d'une ancienne version.
Pour résoudre ce problÚme, la construction des images Docker est généralement liée à des balises, tandis que le déploiement de l'application se fait avec une branche master, qui contient les versions codées en dur des images construites. Dans ce cas, vous pourrez initier un rollback simplement avec un revert master-de la branche.
10. Automatisation du déploiement
Pour que Gitlab-runner puisse déchiffrer nos secrets, nous devons exporter la clé du dépÎt et l'ajouter aux variables d'environnement de notre CI :
git crypt export-key /tmp/docs-repo.key
base64 -w0 /tmp/docs-repo.key; echonous enregistrerons la chaĂźne obtenue dans Gitlab, pour cela, allons dans les paramĂštres de notre projet :
ParamĂštres â> CI / CD â> Variables
Et créons une nouvelle variable :
Type
Clé
Valeur
Protégé
Masqué
Portée
Fichier
GITCRYPT_KEY
<your string>
true (pour la durée de l'apprentissage, vous pouvez aussi faux)
true
Tous les environnements
Capture d'écran de la variable ajoutée

Maintenant, mettons Ă jour notre .gitlab-ci.yml en ajoutant :
.deploy_qbec_app:
stage: deploy
only:
refs:
- master
deploy_gitlab_runner:
extends: .deploy_qbec_app
variables:
GIT_SUBMODULE_STRATEGY: normal
before_script:
- base64 -d "$GITCRYPT_KEY" | git-crypt unlock -
script:
- qbec apply default --root deploy/gitlab-runner --force:k8s-context __incluster__ --wait --yes
deploy_website:
extends: .deploy_qbec_app
script:
- qbec apply default --root deploy/website --force:k8s-context __incluster__ --wait --yesIci, nous avons utilisé plusieurs nouvelles options pour qbec :
- âroot some/app â permet de dĂ©finir le rĂ©pertoire de l'application spĂ©cifique
- âforce:k8s-context __incluster__ â c'est une variable magique qui indique que le dĂ©ploiement se fera dans le mĂȘme cluster que celui oĂč est exĂ©cutĂ© gitlab-runner. Il est nĂ©cessaire de le faire, car sinon qbec essaiera de trouver un serveur Kubernetes adaptĂ© dans votre kubeconfig
- âwait â force qbec Ă attendre que les ressources qu'il crĂ©e passent Ă l'Ă©tat PrĂȘt avant de se terminer avec un code de sortie rĂ©ussi.
- âyes â dĂ©sactive simplement le shell interactif Ătes-vous sĂ»r ? lors du dĂ©ploiement.
Nâoublions pas de valider nos modifications :
git add .gitlab-ci.yml
git commit -m "Automatiser le déploiement"Et aprÚs git push nous verrons comment nos applications ont été déployées :
Capture d'écran du deuxiÚme pipeline

11. Artéfacts et construction lors du push dans master
Les étapes décrites ci-dessus sont généralement suffisantes pour construire et livrer presque n'importe quel microservice, mais nous ne voulons pas ajouter de tag chaque fois que nous avons besoin de mettre à jour le site. Par conséquent, nous allons prendre une approche plus dynamique et configurer le déploiement par digest dans la branche master.
L'idée est simple : désormais, l'image de notre site web sera reconstruite à chaque push dans master, puis déployée automatiquement dans Kubernetes.
Mettons Ă jour ces deux jobs dans notre .gitlab-ci.yml:
build_website:
extends: .build_docker_image
variables:
GIT_SUBMODULE_STRATEGY: normal
script:
- mkdir -p $CI_PROJECT_DIR/artifacts
- /kaniko/executor --cache --context $CI_PROJECT_DIR --dockerfile $CI_PROJECT_DIR/dockerfiles/website/Dockerfile --destination $CI_REGISTRY_IMAGE/website:$CI_COMMIT_REF_NAME --digest-file $CI_PROJECT_DIR/artifacts/website.digest
artifacts:
paths:
- artifacts/
only:
refs:
- master
- tags
deploy_website:
extends: .deploy_qbec_app
script:
- DIGEST="$(cat artifacts/website.digest)"
- qbec apply default --root deploy/website --force:k8s-context __incluster__ --wait --yes --vm:ext-str digest="$DIGEST"Notez que nous avons ajouté la branche master sur refs pour le job build_website et nous utilisons désormais $CI_COMMIT_REF_NAME au lieu de $CI_COMMIT_TAG, c'est-à -dire que nous nous dégageons des tags dans Git et que nous allons maintenant pousser une image avec le nom de la branche du commit qui a initié le pipeline. Il convient de noter que cela fonctionnera également avec les tags, ce qui nous permettra de conserver des snapshots du site avec une version spécifique dans le docker-registry.
Lorsque le nom de la balise docker pour la nouvelle version du site peut rester inchangĂ©, nous devons tout de mĂȘme dĂ©crire les modifications pour Kubernetes, sinon il ne redĂ©ploiera simplement pas l'application Ă partir de la nouvelle image, car il ne remarquera aucun changement dans le manifeste de dĂ©ploiement.
L'option âvm:ext-str digest="$DIGEST" pour qbec â cela permet de passer une variable externe dans jsonnet. Nous voulons que chaque version de notre application soit redĂ©ployĂ©e dans le cluster. Utiliser un nom de balise qui peut maintenant rester inchangĂ© n'est plus possible ici, car nous devons nous lier Ă une version spĂ©cifique de l'image et dĂ©clencher le dĂ©ploiement lors de son changement.
Ici, la possibilitĂ© de Kaniko de sauvegarder le digest de l'image dans un fichier nous aidera (option âdigest-file)
Ensuite, nous transmettrons ce fichier et le lirons au moment du déploiement.
Mettons Ă jour les paramĂštres pour notre deploy/website/environments/base.libsonnet qui aura maintenant l'apparence suivante :
{
components: {
website: {
name: 'example-docs',
image: 'registry.gitlab.com/kvaps/docs.example.org/website@' + std.extVar('digest'),
replicas: 1,
containerPort: 80,
servicePort: 80,
nodeSelector: {},
tolerations: [],
ingressClass: 'nginx',
domain: 'docs.example.org',
},
},
}C'est fait, maintenant chaque commit dans master initialisera la construction de l'image docker pour site web, puis son déploiement dans Kubernetes.
Nâoublions pas de valider nos modifications :
git add .
git commit -m "Configurer la construction dynamique"Vérifions, aprÚs git push nous devrions voir quelque chose de similaire :
Capture d'écran du pipeline pour master

En principe, nous n'avons pas besoin de redéployer gitlab-runner à chaque push, sauf si, bien sûr, rien n'a changé dans sa configuration, corrigeons cela dans .gitlab-ci.yml:
deploy_gitlab_runner:
extends: .deploy_qbec_app
variables:
GIT_SUBMODULE_STRATEGY: normal
before_script:
- base64 -d "$GITCRYPT_KEY" | git-crypt unlock -
script:
- qbec apply default --root deploy/gitlab-runner --force:k8s-context __incluster__ --wait --yes
only:
changes:
- deploy/gitlab-runner/**/*changes permettra de suivre les modifications dans deploy/gitlab-runner/ et déclenchera notre tùche uniquement en cas d'éventuels changements.
Nâoublions pas de valider nos modifications :
git add .gitlab-ci.yml
git commit -m "Réduire le déploiement de gitlab-runner"git push, c'est mieux ainsi :
Capture d'écran du pipeline mis à jour

12. Environnements dynamiques
Il est temps de diversifier notre pipeline avec des environnements dynamiques.
Pour commencer, mettons à jour la tùche build_website dans notre .gitlab-ci.yml, en supprimant le bloc only, ce qui fera en sorte que Gitlab la déclenche à chaque commit dans n'importe quelle branche :
build_website:
extends: .build_docker_image
variables:
GIT_SUBMODULE_STRATEGY: normal
script:
- mkdir -p $CI_PROJECT_DIR/artifacts
- /kaniko/executor --cache --context $CI_PROJECT_DIR --dockerfile $CI_PROJECT_DIR/dockerfiles/website/Dockerfile --destination $CI_REGISTRY_IMAGE/website:$CI_COMMIT_REF_NAME --digest-file $CI_PROJECT_DIR/artifacts/website.digest
artifacts:
paths:
- artifacts/Ensuite, mettons Ă jour la tĂąche deploy_website, ajoutons un bloc lĂ -bas environment:
deploy_website:
extends: .deploy_qbec_app
environment:
name: prod
url: https://docs.example.org
script:
- DIGEST="$(cat artifacts/website.digest)"
- qbec apply default --root deploy/website --force:k8s-context __incluster__ --wait --yes --vm:ext-str digest="$DIGEST"Cela permettra Ă Gitlab d'associer le job avec prod l'environnement et d'afficher le bon lien vers celui-ci.
Maintenant, ajoutons deux autres jobs :
deploy_website:
extends: .deploy_qbec_app
environment:
name: prod
url: https://docs.example.org
script:
- DIGEST="$(cat artifacts/website.digest)"
- qbec apply default --root deploy/website --force:k8s-context __incluster__ --wait --yes --vm:ext-str digest="$DIGEST"
deploy_review:
extends: .deploy_qbec_app
environment:
name: review/$CI_COMMIT_REF_NAME
url: http://$CI_ENVIRONMENT_SLUG.docs.example.org
on_stop: stop_review
script:
- DIGEST="$(cat artifacts/website.digest)"
- qbec apply review --root deploy/website --force:k8s-context __incluster__ --wait --yes --vm:ext-str digest="$DIGEST" --vm:ext-str subdomain="$CI_ENVIRONMENT_SLUG" --app-tag "$CI_ENVIRONMENT_SLUG"
only:
refs:
- branches
except:
refs:
- master
stop_review:
extends: .deploy_qbec_app
environment:
name: review/$CI_COMMIT_REF_NAME
action: stop
stage: deploy
before_script:
- git clone "$CI_REPOSITORY_URL" master
- cd master
script:
- qbec delete review --root deploy/website --force:k8s-context __incluster__ --yes --vm:ext-str digest="$DIGEST" --vm:ext-str subdomain="$CI_ENVIRONMENT_SLUG" --app-tag "$CI_ENVIRONMENT_SLUG"
variables:
GIT_STRATEGY: none
only:
refs:
- branches
except:
refs:
- master
when: manualIls seront déclenchés lors d'un push sur n'importe quelle branche sauf master et déploieront la version preview du site.
Nous voyons une nouvelle option pour qbec : âapp-tag â elle permet de taguer les versions dĂ©ployĂ©es de l'application et de ne travailler qu'Ă l'intĂ©rieur de ce tag, lors de la crĂ©ation et de la destruction des ressources dans Kubernetes qbec n'opĂ©rera qu'avec celles-ci.
Ainsi, nous n'avons pas besoin de crĂ©er un environnement sĂ©parĂ© pour chaque review, mais simplement de rĂ©utiliser le mĂȘme.
Ici, nous utilisons Ă©galement qbec apply review, Ă la place de qbec apply default â c'est justement Ă ce moment-lĂ que nous allons essayer de dĂ©crire les diffĂ©rences pour nos environnements (review et default) :
Ajoutons l'environnement dans deploy/website/qbec.yaml spec: environments: review: defaultNamespace: docs server: https://kubernetes.example.org:8443
Ensuite, déclarons-le dansdeploy/website/params.libsonnet local env = std.extVar('qbec.io/env'); local paramsMap = { _: import './environments/base.libsonnet', default: import './environments/default.libsonnet', review: import './environments/review.libsonnet', };if std.objectHas(paramsMap, env) then paramsMap[env] else error 'environment ' + env + ' not defined in ' + std.thisFile:
Et nous écrirons des paramÚtres personnalisés pour celui-ci dansdeploy/website/environments/review.libsonnet Prenons également un moment pour examiner le job:
// this file has the param overrides for the default environment
local base = import './base.libsonnet';
local slug = std.extVar('qbec.io/tag');
local subdomain = std.extVar('subdomain');
base {
components+: {
website+: {
name: 'example-docs-' + slug,
domain: subdomain + '.docs.example.org',
},
},
}stop_review , il sera déclenché lors de la suppression d'une branche et pour que gitlab n'essaie pas d'effectuer un checkout dessus, nous utilisonsGIT_STRATEGY: none , plus tard nous clonons-la branche et supprimons la review à travers elle. master-branche et supprimons la critique par ce biais.
C'est un peu compliqué, mais je n'ai pas trouvé de méthode plus élégante pour l'instant.
Une alternative pourrait ĂȘtre de dĂ©ployer chaque revue dans un espace de noms sĂ©parĂ©, que l'on peut toujours supprimer entiĂšrement.
Nâoublions pas de valider nos modifications :
git add .
git commit -m "Activer la révision automatique"git push, git checkout -b test, git push origin test, vérifions :
Capture d'écran des environnements créés dans Gitlab

Tout fonctionne ? â parfait, supprimons notre branche de test : git checkout master, git push origin :test, vĂ©rifions que les tĂąches de suppression de l'environnement se sont exĂ©cutĂ©es sans erreurs.
Il convient de préciser ici que n'importe quel développeur du projet peut créer des branches, il peut également modifier .gitlab-ci.yml le fichier et accéder aux variables secrÚtes.
C'est pourquoi il est fortement recommandé de n'autoriser leur utilisation que pour les branches protégées, par exemple dans master, ou de créer un ensemble de variables distinct pour chaque environnement.
13. Applications de révision
c'est une fonctionnalité de Gitlab qui permet d'ajouter un bouton pour chaque fichier dans le dépÎt, permettant de le prévisualiser rapidement dans l'environnement déployé.
Pour que ces boutons apparaissent, il faut créer un fichier .gitlab/route-map.yml et décrire toutes les transformations de chemins, dans notre cas ce sera trÚs simple :
# Indices
- source: /content/(.+?)_index.(md|html)/
public: '1'
# Pages
- source: /content/(.+?).(md|html)/
public: '1/'Nâoublions pas de valider nos modifications :
git add .gitlab/
git commit -m "Activer les applications de révision"git push, et vérifions :
Capture d'écran du bouton Application de révision

Le travail est terminé !
Sources du projet :
- sur Gitlab :
- sur GitHub :
Merci de votre attention, j'espĂšre que cela vous a plu ![]()
Source : habr.com
