
Bonjour ! DerniÚrement, de nombreux outils d'automatisation performants ont été lancés pour la création d'images Docker et le déploiement sur Kubernetes. C'est pourquoi j'ai décidé de tester Gitlab, d'étudier ses fonctionnalités en profondeur et, bien sûr, de mettre en place un pipeline.
L'inspiration pour ce travail était le site Web , qui est généré à partir de automatiquement, et pour chaque pull request envoyée, le robot génÚre automatiquement une version d'aperçu du site avec vos modifications et fournit un lien pour la visualisation.
J'ai essayé de créer un processus similaire de A à Z, mais entiÚrement basé sur Gitlab CI et les outils gratuits que j'utilisais pour déployer des applications sur Kubernetes. Aujourd'hui, je vais enfin vous en dire plus.
L'article couvrira des outils tels que :
Hugo, Québec, Kaniko, git-crypte О CI GitLab avec la création d'environnements dynamiques.
contenu
1. Apprendre Ă connaĂźtre Hugo
à titre d'exemple de notre projet, nous allons créer un site de publication de documentation basé sur Hugo. Hugo est un générateur de contenu statique.
Pour ceux qui ne connaissent pas les générateurs statiques, je vais vous les présenter plus en détail. Contrairement aux moteurs de site web classiques, dotés d'une base de données et de PHP, qui génÚrent des pages à la volée à la demande de l'utilisateur, les générateurs statiques sont organisés différemment. Ils permettent de récupérer le code source, généralement un ensemble de fichiers au format Markdown et des modÚles de thÚme, puis de le compiler pour obtenir un site web complet.
Autrement dit, à la sortie, vous recevrez une structure de répertoire 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 fonctionnel.
Hugo peut ĂȘtre installĂ© localement et testĂ© :
Initialiser un nouveau site :
hugo new site docs.example.orgEt aussi un dépÎt git :
cd docs.example.org
git initPour l'instant, notre site est vierge et pour que quelque chose y apparaisse, nous devons d'abord connecter un thÚme, un thÚme est juste un ensemble de modÚles et de rÚgles spécifiées par lesquels notre site est généré.
Comme sujet, nous utiliserons , qui, à mon avis, est le plus adapté à un site de documentation.
Je voudrais accorder une attention particuliÚre au fait que nous n'avons pas besoin d'enregistrer les fichiers de thÚme dans notre référentiel de projet, mais nous pouvons simplement le connecter en utilisant sous-module git:
git submodule add https://github.com/matcornic/hugo-theme-learn themes/learnDe cette façon, notre rĂ©fĂ©rentiel ne contiendra que des fichiers directement liĂ©s Ă notre projet, et le thĂšme connectĂ© restera sous forme de lien vers un rĂ©fĂ©rentiel spĂ©cifique et un commit dans celui-ci, ce qui signifie qu'il peut toujours ĂȘtre extrait de la source d'origine sans crainte de modifications incompatibles.
Corrigeons la configuration config.toml:
baseURL = "http://docs.example.org/"
languageCode = "en-us"
title = "My Docs Site"
theme = "learn"Déjà à ce stade vous pouvez lancer :
hugo serverEt à l'adresse consultez notre site nouvellement créé, toutes les modifications apportées dans l'annuaire sont automatiquement mises à jour et la page ouverte dans le navigateur, trÚs pratique !
Essayons de créer une page de titre dans contenu/_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 un site Web, exécutez simplement :
hugoContenu du répertoire Publique/ et sera votre site Web.
Oui, au fait, ajoutons-le tout de suite .gitignore:
echo /public > .gitignoreN'oubliez pas de valider nos modifications :
git add .
git commit -m "New site created"2. Préparation du Dockerfile
Il est maintenant temps de définir la structure de notre dépÎt. J'utilise généralement quelque chose comme :
.
âââ deploy
â âââ app1
â âââ app2
âââ dockerfiles
âââ image1
âââ image2- dockerfiles/ â contient des rĂ©pertoires avec des Dockerfiles et tout ce qui est nĂ©cessaire Ă la construction de nos images Docker.
- dĂ©ployer/ â contient des rĂ©pertoires pour dĂ©ployer nos applications sur Kubernetes
Nous allons donc créer notre premier Dockerfile le long du chemin dockerfiles/site web/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 voir, le Dockerfile contient deux De, cette opportunité s'appelle et vous permet d'exclure tout ce qui est inutile de l'image Docker finale.
Ainsi, notre image finale ne contiendra que darkhttpd (serveur HTTP lĂ©ger) et Publique/ â le contenu de notre site gĂ©nĂ©rĂ© statiquement.
N'oubliez pas de valider nos modifications :
git add dockerfiles/website
git commit -m "Add Dockerfile for website"3. Apprendre Ă connaĂźtre Kaniko
En tant que constructeur d'images Docker, j'ai dĂ©cidĂ© d'utiliser , car il ne nĂ©cessite pas de dĂ©mon Docker pour fonctionner, et la construction elle-mĂȘme peut ĂȘtre effectuĂ©e sur n'importe quelle machine et le cache peut ĂȘtre stockĂ© directement dans le registre, Ă©liminant ainsi le besoin d'avoir un stockage persistant Ă part entiĂšre.
Pour construire une image, il suffit de lancer un conteneur avec Kaniko Executive et lui passer 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 sa construction, elle sera automatiquement lancĂ©e dans le registre Docker.
ParamĂštre âcache vous permet de mettre en cache des couches dans le registre Docker, pour l'exemple donnĂ©, elles seront enregistrĂ©es dans registry.gitlab.com/kvaps/docs.example.org/website/cache, mais vous pouvez spĂ©cifier un autre chemin en utilisant le paramĂštre âcache-repo.
Capture d'écran de docker-registry

4. Apprendre Ă connaĂźtre qbec
â est un outil de dĂ©ploiement qui vous permet de dĂ©crire de maniĂšre dĂ©clarative les manifestes de vos applications et de les dĂ©ployer sur Kubernetes. L'utilisation de Jsonnet comme syntaxe sous-jacente simplifie grandement la description des diffĂ©rences entre environnements et Ă©limine presque entiĂšrement la rĂ©pĂ©tition de 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 que vous souhaitez les dĂ©crire de maniĂšre dĂ©clarative dans Git.
Qbec permet Ă©galement de gĂ©nĂ©rer des graphiques Helm en leur transmettant les paramĂštres nĂ©cessaires, puis de les exploiter comme des manifestes classiques, y compris en leur imposant diverses mutations. Cela Ă©limine ainsi le recours Ă ChartMuseum. Autrement dit, vous pouvez stocker et gĂ©nĂ©rer des graphiques directement depuis Git, lĂ oĂč ils se trouvent.
Comme je lâai dit prĂ©cĂ©demment, nous stockerons tous les dĂ©ploiements dans le rĂ©pertoire dĂ©ployer/:
mkdir deploy
cd deployInitialisons notre premiĂšre application :
qbec init website
cd websiteMaintenant, la structure de notre application ressemble Ă ceci :
.
âââ components
âââ environments
â âââ base.libsonnet
â âââ default.libsonnet
âââ params.libsonnet
âââ qbec.yamlregardons le fichier qbec.yaml:
apiVersion: qbec.io/v1alpha1
kind: App
metadata:
name: website
spec:
environments:
default:
defaultNamespace: docs
server: https://kubernetes.example.org:8443
vars: {}Ce qui nous intéresse ici est principalement environnements spécifiques, qbec a déjà créé un environnement par défaut pour nous et a pris l'adresse du serveur et l'espace de noms de notre kubeconfig actuel.
Maintenant, lors du déploiement sur défaut environnement, qbec se déploiera toujours uniquement sur le cluster Kubernetes spécifié et sur l'espace de noms spécifié, ce qui signifie que vous n'avez plus besoin de basculer entre les contextes et les espaces de noms pour effectuer un déploiement.
Si nécessaire, 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.libson.net, oĂč il est indiquĂ© oĂč obtenir les paramĂštres pour eux.
Ensuite, nous voyons deux répertoires :
- composants / â tous les manifestes de notre application seront stockĂ©s ici, ils peuvent ĂȘtre dĂ©crits Ă la fois dans jsonnet et dans des fichiers yaml classiques
- environnements/ â ici nous allons dĂ©crire toutes les variables (paramĂštres) de nos environnements.
Par défaut, nous avons deux fichiers :
- environnements/base.libsonnet - il contiendra des paramĂštres communs Ă tous les environnements
- environnements/default.libsonnet â contient des paramĂštres redĂ©finis pour l'environnement dĂ©faut
Ouvrons-le environnements/base.libsonnet et ajoutez-y les paramĂštres de 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 composants/siteweb.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 à la fois, à savoir : Déploiement, Services О EntréeSi nous le voulions, nous pourrions les déplacer dans différents composants, mais à ce stade, un seul nous suffira.
syntaxe jsonnet trĂšs similaire au json normal, en principe le json normal est dĂ©jĂ un jsonnet valide, donc au dĂ©but il pourrait ĂȘtre plus facile pour vous d'utiliser des services en ligne comme yaml2json pour convertir votre yaml habituel en json, ou, si vos composants ne contiennent aucune variable, ils peuvent alors ĂȘtre dĂ©crits comme du yaml normal.
Lorsque vous travaillez avec jsonnet Je vous recommande fortement d'installer un plugin pour votre éditeur
Par exemple, il existe un plugin pour vim vim-jsonnet, qui permet 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 defaultLa sortie vous montrera les manifestes yaml rendus qui seront appliqués au cluster par défaut.
Super, maintenant appliquons-le :
qbec apply defaultEn sortie vous verrez toujours ce qui sera fait dans votre cluster, qbec vous demandera d'accepter les modifications en tapant y vous pourrez confirmer vos intentions.
C'est fait, maintenant notre application est déployée !
Si vous apportez des modifications, vous pouvez toujours :
qbec diff defaultpour voir comment ces changements affecteront le déploiement actuel
N'oubliez pas de valider nos modifications :
cd ../..
git add deploy/website
git commit -m "Add deploy for website"5. Essayer Gitlab-runner avec Kubernetes-executor
Jusqu'à récemment, je n'utilisais que le modÚle normal gitlab-runner Sur une machine pré-préparée (conteneur LXC) avec un shell ou un exécuteur Docker. Initialement, nous disposions de plusieurs exécuteurs de ce type définis globalement dans notre Gitlab. Ils créaient des images Docker pour tous les projets.
Mais comme l'a montré la pratique, cette option n'est pas idéale, tant en termes de praticité que de sécurité. Il est bien plus judicieux, et idéologiquement plus juste, de déployer des exécuteurs distincts pour chaque projet, voire pour chaque environnement.
Heureusement, ce nâest pas du tout un problĂšme, car nous allons maintenant dĂ©ployer gitlab-runner directement dans le cadre de notre projet directement dans Kubernetes.
Gitlab fournit un diagramme Helm prĂȘt Ă l'emploi pour le dĂ©ploiement de gitlab-runner sur Kubernetes. Il vous suffit donc de savoir : jeton d'enregistrement pour notre projet en ParamĂštres -> CI / CD -> Runners et passe-le Ă la barre :
helm repo add gitlab https://charts.gitlab.io
helm install gitlab-runner
--set gitlabUrl=https://gitlab.com
--set runnerRegistrationToken=yga8y-jdCusVDn_t4Wxc
--set rbac.create=true
gitlab/gitlab-runnerOĂč:
- â l'adresse de votre serveur Gitlab.
- yga8y-jdCusVDn_t4Wxc â jeton d'enregistrement pour votre projet.
- rbac.create=true â accorde au coureur la quantitĂ© nĂ©cessaire de privilĂšges pour pouvoir crĂ©er des pods pour exĂ©cuter nos tĂąches Ă l'aide de kubernetes-executor.
Si tout est fait correctement, vous devriez voir le coureur inscrit dans la section Runners, dans les paramĂštres de votre projet.
Capture d'écran du coureur ajouté

C'est aussi simple que ça ? Oui, c'est aussi simple que ça ! Plus besoin d'enregistrer manuellement les coureurs : désormais, ils seront créés et supprimés automatiquement.
6. Déploiement de cartes Helm avec QBEC
Depuis que nous avons décidé de compter gitlab-runner une 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, mais à l'avenir, nous prévoyons de déployer différentes copies site trÚs souvent, contrairement à gitlab-runner, qui ne sera déployé qu'une seule fois par cluster Kubernetes. Initialisons donc une application distincte pour cela :
cd deploy
qbec init gitlab-runner
cd gitlab-runnerCette fois, nous ne dĂ©crirons pas manuellement les entitĂ©s Kubernetes, mais utiliserons un graphique Helm prĂȘt Ă l'emploi. L'un des avantages de qbec est la possibilitĂ© de gĂ©nĂ©rer des graphiques Helm directement depuis un dĂ©pĂŽt Git.
Activons-le en utilisant le sous-module git :
git submodule add https://gitlab.com/gitlab-org/charts/gitlab-runner vendor/gitlab-runnerMaintenant le répertoire fournisseur/gitlab-runner contient notre référentiel avec un graphique pour gitlab-runner.
De la mĂȘme maniĂšre, vous pouvez connecter d'autres rĂ©fĂ©rentiels, par exemple l'intĂ©gralitĂ© du rĂ©fĂ©rentiel avec les graphiques officiels
Décrivons le composant composants/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 à développerHelmTemplate nous passons le chemin vers le graphique, puis paramÚtres.valeurs, que nous prenons à partir des paramÚtres d'environnement, puis vient l'objet avec
- nomModĂšle â titre de la sortie
- namespace - espace de noms transmis Ă helm
- ce fichier â un paramĂštre obligatoire qui transmet le chemin d'accĂšs au fichier actuel
- verbeux - montre la commande ModĂšle de barre avec tous les arguments lors du rendu du graphique
Décrivons maintenant les paramÚtres de notre composant dans environnements/base.libsonnet:
local secrets = import '../secrets/base.libsonnet';
{
components: {
gitlabRunner: {
name: 'gitlab-runner',
values: {
gitlabUrl: 'https://gitlab.com/',
rbac: {
create: true,
},
runnerRegistrationToken: secrets.runnerRegistrationToken,
},
},
},
}Noter jeton d'enregistrement du coureur nous prenons à partir d'un fichier externe secrets/base.libson.net, créons-le :
{
runnerRegistrationToken: 'yga8y-jdCusVDn_t4Wxc',
}Vérifions si tout fonctionne :
qbec show defaultsi tout va bien, nous pouvons alors supprimer notre version précédemment déployée via Helm :
helm uninstall gitlab-runneret le déployer, mais via qbec :
qbec apply default7. Introduction Ă git-crypt
â est un outil qui vous permet de configurer un cryptage transparent pour votre rĂ©fĂ©rentiel.
Pour le moment, notre structure de répertoire pour gitlab-runner ressemble à ceci :
.
âââ 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 faut donc les chiffrer correctement.
Généralement, pour une seule variable, cela n'a pas toujours de sens. Vous pouvez transmettre des secrets. Québec et via les variables d'environnement de votre systÚme CI.
Mais il convient de noter qu'il existe Ă©galement des projets plus complexes qui peuvent contenir beaucoup plus de secrets ; les transmettre tous via des variables d'environnement sera extrĂȘmement difficile.De plus, dans ce cas, je ne pourrais pas vous parler dâun outil aussi merveilleux que git-crypte.
git-crypte C'est Ă©galement pratique car cela vous permet de sauvegarder l'intĂ©gralitĂ© de l'historique des secrets, ainsi que de comparer, fusionner et rĂ©soudre les conflits de la mĂȘme maniĂšre que nous avons l'habitude de le faire avec Git.
PremiÚre chose aprÚs l'installation git-crypte nous devons générer des clés pour notre référentiel :
git crypt initSi vous disposez d'une clé PGP, vous pouvez immédiatement vous ajouter en tant que collaborateur pour ce projet :
git-crypt add-gpg-user kvapss@gmail.comDe cette façon, vous pourrez toujours décrypter ce référentiel à l'aide de votre clé privée.
Si vous nâavez pas de clĂ© PGP et ne prĂ©voyez pas dâen avoir une, vous pouvez procĂ©der dans lâautre sens et exporter la clĂ© du projet :
git crypt export-key /path/to/keyfileAinsi, quiconque a exporté fichier clé sera capable de décrypter votre dépÎt.
Il est temps de mettre en place notre premier secret.
Je vous rappelle que nous sommes toujours dans l'annuaire. dĂ©ployer/gitlab-runner/, oĂč nous avons un rĂ©pertoire secrets/, chiffrons tous les fichiers qu'il contient, pour cela nous allons crĂ©er un fichier secrets/.gitattributes avec le contenu suivant :
* filter=git-crypt diff=git-crypt
.gitattributes !filter !diffComme vous pouvez le voir dans le contenu, tous les fichiers sont classĂ©s par masque * sera exĂ©cutĂ© Ă travers git-crypte, Ă l'exception de celui-lĂ mĂȘme .gitattributes
Nous pouvons vérifier cela en exécutant :
git crypt status -eLa sortie sera une liste de tous les fichiers du référentiel pour lesquels le chiffrement est activé.
Voilà , nous pouvons maintenant valider nos modifications en toute sécurité :
cd ../..
git add .
git commit -m "Add deploy for gitlab-runner"Pour bloquer un dépÎt, exécutez simplement :
git crypt locket immédiatement tous les fichiers cryptés se transformeront en quelque chose de binaire, il sera impossible de les lire.
Pour décrypter le référentiel, exécutez :
git crypt unlock8. Créer une image de boßte à outils
L'image Toolbox contient tous les outils nécessaires au déploiement de notre projet. GitLabRunner l'utilisera pour effectuer les tùches de déploiement courantes.
Tout est simple ici, créons-en un nouveau dockerfiles/boßte à outils/Dockerfile avec le contenu suivant :
FROM 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 voir, dans cette image, nous installons tous les utilitaires utilisĂ©s pour dĂ©ployer notre application. Nous n'en avons besoin que de kubectl, mais vous souhaiterez peut-ĂȘtre jouer avec pendant l'Ă©tape de 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 ce faire, allez dans le répertoire contenant gitlab-runner :
cd deploy/gitlab-runneret ajouter un nouveau composant composants/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 décrirons également les nouveaux paramÚtres dans environnements/base.libsonnet, qui ressemble maintenant à ceci :
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',
},
},
}Noter $.components.rbac.name fait référence à Le nom pour le composant rbac
Voyons ce qui a changé :
qbec diff defaultet appliquer nos modifications Ă Kubernetes :
qbec apply defaultN'oubliez pas non plus 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 l'assemblage d'images par balises
A la racine du projet nous allons créer .gitlab-ci.yml avec le contenu suivant :
.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 tĂąches oĂč vous devez initialiser explicitement les sous-modules avant l'exĂ©cution.
N'oubliez pas de valider nos modifications :
git add .gitlab-ci.yml
git commit -m "Automate docker build"Je pense que nous pouvons sans risque appeler cela une version v0.0.1 et accrochez l'étiquette :
git tag v0.0.1Nous ajouterons des balises à chaque nouvelle version. Les balises des images Docker seront liées aux balises Git. Chaque push avec une nouvelle balise initialisera la construction des images portant cette balise.
Nous le ferons git push --tags, et regardons notre premier pipeline :
Capture d'écran du premier pipeline

Il est important de noter que les builds basĂ©s sur des balises conviennent Ă la crĂ©ation d'images Docker, mais pas au dĂ©ploiement d'une application sur Kubernetes. Puisque de nouvelles balises peuvent ĂȘtre attribuĂ©es Ă d'anciens commits, l'initialisation du pipeline pour ces derniers entraĂźnera le dĂ©ploiement de l'ancienne version.
Pour rĂ©soudre ce problĂšme, les builds dâimages Docker sont gĂ©nĂ©ralement liĂ©es Ă des balises et le dĂ©ploiement dâapplications est liĂ© Ă une branche. maĂźtre, oĂč les versions des images collectĂ©es sont codĂ©es en dur. Dans ce cas, vous pouvez initialiser la restauration par un simple retour en arriĂšre. maĂźtre-branches.
10. Automatisation du déploiement
Pour que Gitlab-runner puisse décrypter nos secrets, nous devrons exporter la clé du référentiel et l'ajouter à nos variables d'environnement CI :
git crypt export-key /tmp/docs-repo.key
base64 -w0 /tmp/docs-repo.key; echoNous allons enregistrer la chaßne résultante dans Gitlab, pour ce faire nous irons dans les paramÚtres de notre projet :
ParamĂštres -> CI / CD -> Variables
Et créons une nouvelle variable :
Type
ACTIVITES
Valeur
Protégé
Masqué
Domaine
File
GITCRYPT_KEY
<your string>
true (pendant la période de formation, il est possible et false)
true
All environments
Capture d'écran de la variable ajoutée

Maintenant, mettons Ă jour le nĂŽtre .gitlab-ci.yml en y 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 activé plusieurs nouvelles options pour qbec :
- ârooter certaines/applications â permet de dĂ©finir le rĂ©pertoire d'une application spĂ©cifique
- --force:k8s-context __incluster__ â Il s'agit d'une variable magique qui indique que le dĂ©ploiement aura lieu dans le mĂȘme cluster que celui oĂč s'exĂ©cute gtilab-runner. Cette opĂ©ration est indispensable, car sinon qbec tentera de trouver un serveur Kubernetes appropriĂ© dans votre kubeconfig.
- -attendez â fait attendre qbec jusqu'Ă ce que les ressources qu'il crĂ©e passent Ă l'Ă©tat PrĂȘt et ne sortent qu'ensuite avec un code de sortie rĂ©ussi.
- -Oui - désactive simplement le shell interactif Es-tu sûr? lors du déploiement.
N'oubliez pas de valider nos modifications :
git add .gitlab-ci.yml
git commit -m "Automate deploy"Et aprÚs git push nous verrons comment nos applications ont été déployées :
Capture d'écran du deuxiÚme pipeline

11. Artefacts et assemblage lors de la poussée vers le master
En général, les étapes ci-dessus suffisent à créer et à livrer presque n'importe quel microservice, mais nous ne souhaitons pas ajouter une balise à chaque mise à jour du site. Par conséquent, nous allons adopter une approche plus dynamique et configurer le déploiement par digest dans la branche principale.
L'idée est simple : maintenant l'image de notre site sera reconstruit à chaque fois que vous enfoncerez maßtre, puis déployé automatiquement sur Kubernetes.
Mettons Ă jour ces deux emplois 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"Veuillez noter que nous avons ajoutĂ© une branche maĂźtre Đș rĂ©fs pour le travail construire_site_web et maintenant nous utilisons $CI_COMMIT_REF_NAME au lieu de $CI_COMMIT_TAG, c'est-Ă -dire que nous nous dĂ©tachons des balises dans Git et que nous allons maintenant pousser une image portant le nom de la branche du commit ayant initialisĂ© le pipeline. Il est Ă noter que cela fonctionnera Ă©galement avec les balises, ce qui nous permettra d'enregistrer des instantanĂ©s du site avec une certaine version dans le registre Docker.
Bien que le nom de la balise Docker pour une nouvelle version d'un site puisse rester inchangé, nous devons toujours décrire les modifications apportées à Kubernetes, sinon il ne redéployera tout simplement pas l'application à partir de la nouvelle image, car il ne remarquera aucun changement dans le manifeste de déploiement.
Option âvm:ext-str digest=â$DIGESTâ Pour qbec, vous pouvez transmettre une variable externe Ă Jsonnet. Nous souhaitons que notre application soit redĂ©ployĂ©e dans le cluster Ă chaque nouvelle version. Nous ne pouvons plus utiliser le nom de la balise, dĂ©sormais immuable, car nous devons l'associer Ă une version spĂ©cifique de l'image et dĂ©clencher le dĂ©ploiement lorsqu'elle change.
Ici, nous serons aidĂ©s par la capacitĂ© de Kaniko Ă enregistrer une image de rĂ©sumĂ© dans un fichier (option âfichier de rĂ©sumĂ©)
Nous transférerons ensuite ce fichier et le lirons au moment du déploiement.
Mettons à jour les paramÚtres de notre déployer/site web/environnements/base.libsonnet qui ressemblera désormais à ceci :
{
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',
},
},
}Terminé, maintenant tout commit dans maßtre initialise la construction de l'image docker pour site, puis déployez-le sur Kubernetes.
N'oubliez pas de valider nos modifications :
git add .
git commit -m "Configure dynamic build"Nous vérifierons plus tard git push nous devrions voir quelque chose comme ceci :
Capture d'écran du pipeline pour le maßtre

En principe, nous n'avons pas besoin de redéployer gitlab-runner à chaque push, à moins, bien sûr, que rien n'ait 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/**/*change vous permettra de surveiller les changements dans déployer/gitlab-runner/ et ne déclenchera notre travail que s'il y a de telles
N'oubliez pas de valider nos modifications :
git add .gitlab-ci.yml
git commit -m "Reduce gitlab-runner deploy"git push, c'est mieux :
Capture d'écran du pipeline mis à jour

12. Environnements dynamiques
Il est temps de diversifier notre pipeline avec des environnements dynamiques.
Commençons par mettre à jour le travail. construire_site_web dans notre .gitlab-ci.yml, en retirant le bloc uniquement, ce qui fera que Gitlab le déclenchera sur n'importe quel commit sur 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, nous mettrons à jour le travail déployer_site_web, ajoutons un bloc ici convivial:
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 travail à poussée environnement et afficher le lien correct vers celui-ci.
Ajoutons maintenant deux autres tĂąches :
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 lancés lorsqu'ils seront poussés vers toutes les branches sauf la branche principale et déploieront une version préliminaire du site.
Nous voyons une nouvelle option pour qbec : âĂ©tiquette d'application â il vous permet de marquer les versions dĂ©ployĂ©es de l'application et de travailler uniquement dans cette balise ; lors de la crĂ©ation et de la destruction de ressources dans Kubernetes, qbec fonctionnera uniquement avec elles.
De cette façon, nous pouvons Ă©viter de crĂ©er un environnement distinct pour chaque rĂ©vision, mais simplement rĂ©utiliser le mĂȘme.
Ici, nous utilisons Ă©galement examen de la candidature qbec, au lieu de qbec applique par dĂ©faut â c'est exactement le moment oĂč nous allons essayer de dĂ©crire les diffĂ©rences pour nos environnements (rĂ©vision et par dĂ©faut) :
Ajouter évaluation environnement dans déployer/site web/qbec.yaml
spec:
environments:
review:
defaultNamespace: docs
server: https://kubernetes.example.org:8443Ensuite, nous l'annoncerons dans deploy/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.thisFileEt nous écrirons des paramÚtres personnalisés pour cela dans déployer/site web/environnements/révision.libsonnet:
// 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',
},
},
}Examinons également de plus prÚs le joba stop_review, il sera déclenché lorsque la branche est supprimée et afin que gitlab n'essaie pas de l'extraire, il est utilisé GIT_STRATEGY : aucune, plus tard nous clonons maßtre-branchez et supprimez l'avis via celui-ci.
C'est un peu compliqué, mais je n'ai pas encore trouvé de moyen plus beau.
Une autre option serait de dĂ©ployer chaque avis dans un espace de noms distinct, qui peut toujours ĂȘtre complĂštement supprimĂ©.
N'oubliez pas de valider nos modifications :
git add .
git commit -m "Enable automatic review"git push, git checkout -b test, test d'origine git push, nous vérifions :
Capture d'écran des environnements créés dans Gitlab

Est-ce que tout fonctionne ? - Super, supprimons notre branche de test : maßtre de caisse git, git push origin :test, nous vérifions que les tùches de suppression de l'environnement ont fonctionné sans erreur.
Ici, je voudrais préciser immédiatement que tout développeur du projet peut créer des branches, il peut également modifier .gitlab-ci.yml fichier et accéder aux variables secrÚtes.
Pour cette raison, il est fortement recommandé de n'autoriser leur utilisation que pour les branches protégées, par exemple dans maßtre, ou créez un ensemble distinct de variables pour chaque environnement.
13. Examiner les applications
Il s'agit d'une fonctionnalité GitLab qui vous permet d'ajouter un bouton pour chaque fichier du référentiel afin de le visualiser rapidement dans l'environnement déployé.
Pour que ces boutons apparaissent, vous devez créer un fichier .gitlab/route-map.yml et décrivez-y toutes les transformations des 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'oubliez pas de valider nos modifications :
git add .gitlab/
git commit -m "Enable review apps"git push, et nous vérifions :
Capture d'écran du bouton « Review App »

Travail terminé !
Sources du projet :
- sur Gitlab :
- sur GitHub :
Merci pour votre attention, j'espÚre que vous avez apprécié ![]()
Source: habr.com
