
Salut Récemment, de nombreux outils d'automatisation intéressants ont été publiés à la fois pour la création d'images Docker et pour le déploiement sur Kubernetes. à cet égard, j'ai décidé de jouer avec le gitlab, comment étudier ses capacités et, bien sûr, configurer le pipeline.
L'inspiration pour ce travail a été le site kubernetes.io , qui est généré automatiquement à partir des codes sources , et pour chaque pool envoyé, 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 à partir de zéro, mais entiÚrement basé sur Gitlab CI et des outils gratuits que j'utilisais pour déployer des applications dans Kubernetes. Aujourd'hui, je vais enfin vous en dire plus à leur sujet.
L'article examinera des outils tels que:
Hugo , qbec , kaniko , git-crypt et GitLab CI avec la création d'environnements dynamiques.
Table des matiĂšres
- Présentation de Hugo
- Préparation de Dockerfile
- Connaissance de kaniko
- Présentation du qbec
- Essayer Gitlab-runner avec Kubernetes-executor
- Déploiement des cartes Helm avec qbec
- Présentation de git-crypt
- Créer une image de boßte à outils
- Notre premier pipeline et assemblage d'images par tags
- Automatisation du déploiement
- Artefacts et push build dans master
- Environnements dynamiques
- Examiner les applications
1. Présentation de Hugo
à titre d'exemple de notre projet, nous allons essayer de créer un site web pour publier de la documentation construite 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 en dire un peu plus à leur sujet. Contrairement aux moteurs de sites de bases de données classiques et à certains moteurs php qui, lorsqu'ils sont demandés par un utilisateur, génÚrent des pages à la volée, les générateurs statiques sont organisés un peu différemment. Ils vous permettent de prendre la source, en rÚgle générale, il s'agit d'un ensemble de fichiers dans les modÚles de balisage et de thÚme Markdown, puis de les compiler dans un site entiÚrement terminé.
Autrement dit, Ă la sortie, vous obtiendrez une structure de rĂ©pertoires et un ensemble de fichiers HTML gĂ©nĂ©rĂ©s, qui peuvent ĂȘtre simplement tĂ©lĂ©chargĂ©s sur n'importe quel hĂ©bergement bon marchĂ© et obtenir un site de travail.
Hugo peut ĂȘtre installĂ© localement et l'essayer:
Nous initialisons le nouveau site:
hugo new site docs.example.org
Et en mĂȘme temps le dĂ©pĂŽt git:
cd docs.example.org git init
Jusqu'à présent, notre site est vierge et pour que quelque chose y apparaisse d'abord, nous devons connecter un thÚme, un thÚme - c'est juste un ensemble de modÚles et de rÚgles prédéfinies par lesquelles notre site est généré.
Comme sujet, nous utiliserons Learn , qui, à mon avis, est le mieux adapté pour un site avec de la documentation.
Je voudrais porter une attention particuliÚre au fait que nous n'avons pas besoin d'enregistrer les fichiers de thÚme dans le référentiel de notre projet, nous pouvons simplement le connecter en utilisant le sous - module git :
git submodule add https://github.com/matcornic/hugo-theme-learn themes/learn
Ainsi, seuls les fichiers directement liĂ©s Ă notre projet seront dans notre rĂ©fĂ©rentiel, et le sujet connectĂ© restera sous la forme d'un lien vers un rĂ©fĂ©rentiel spĂ©cifique et s'y engagera, c'est-Ă -dire qu'il peut toujours ĂȘtre extrait de la source d'origine et ne pas avoir peur des modifications incompatibles.
Corrigez la configuration config.toml :
baseURL = "http://docs.example.org/" languageCode = "en-us" title = "My Docs Site" theme = "learn"
Déjà à ce stade, vous pouvez exécuter:
hugo server
Et sur http: // localhost: 1313 / consultez notre site nouvellement créé, toutes les modifications apportées au répertoire sont automatiquement mises à jour et la page ouverte dans le navigateur est trÚs pratique!
Essayons de créer une page de garde 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 un site, lancez simplement:
hugo
Le contenu du répertoire public / sera votre site.
Oui, au fait, ajoutons-le immédiatement à .gitignore :
echo /public > .gitignore
N'oubliez pas de valider nos modifications:
git add . git commit -m "New site created"
2. Préparation du Dockerfile
Il est temps de déterminer la structure de notre référentiel. Habituellement, j'utilise quelque chose comme:
. âââ deploy â âââ app1 â âââ app2 âââ dockerfiles âââ image1 âââ image2
- dockerfiles / - contient des répertoires avec Dockerfiles et tout ce dont vous avez besoin pour construire nos images de docker.
- deploy / - contient des répertoires pour déployer nos applications sur Kubernetes
Ainsi, nous allons créer notre premier Dockerfile le long du chemin 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 voir, le Dockerfile contient deux FROM, cette fonctionnalité est appelée construction en plusieurs étapes et vous permet d'exclure tout ce qui n'est pas nécessaire de l'image Docker finale.
Ainsi, l'image finale ne contiendra que darkhttpd (un serveur HTTP léger) et public / - 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. Connaissance de kaniko
En tant que collectionneur d'images docker, j'ai dĂ©cidĂ© d'utiliser kaniko , car il ne nĂ©cessite pas de dĂ©mon docker pour fonctionner, et l'assemblage lui-mĂȘme peut ĂȘtre effectuĂ© sur n'importe quelle machine et stocker le cache directement dans le registre, Ă©liminant le besoin d'avoir un stockage persistant complet .
Pour construire l'image, il suffit de démarrer le conteneur avec kaniko executor et de lui passer le contexte de construction actuel, vous pouvez le faire 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.1
Lorsque Registry.gitlab.com/kvaps/docs.example.org/website est le nom de votre image Docker, aprÚs l'assemblage, elle sera automatiquement insérée dans le registre Docker.
Le paramÚtre --cache vous permet de mettre en cache les 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 à l'aide du paramÚtre --cache- repo .
Screenshot docker-registry
4. Connaissance du qbec
Qbec est un outil de déploiement qui vous permet de décrire de maniÚre déclarative le manifeste de votre application et de le déployer sur Kubernetes. L'utilisation de Jsonnet comme syntaxe principale vous permet de simplifier considérablement la description des différences pour plusieurs environnements, et élimine également presque complÚtement la répétabilité du code.
Cela peut ĂȘtre particuliĂšrement vrai dans les cas oĂč vous devez dĂ©ployer une application dans plusieurs clusters avec diffĂ©rents paramĂštres et que vous souhaitez les dĂ©crire de maniĂšre dĂ©clarative dans Git.
Qbec vous permet Ă©galement de rendre des graphiques Helm en leur transmettant les paramĂštres nĂ©cessaires et en les faisant ensuite fonctionner ainsi que des manifestes rĂ©guliers, y compris diverses mutations qui peuvent leur ĂȘtre appliquĂ©es, ce qui, Ă son tour, Ă©limine la nĂ©cessitĂ© d'utiliser le ChartMuseum. Autrement dit, vous pouvez stocker et rendre des graphiques directement Ă partir de git, oĂč ils ont la place.
Comme je l'ai déjà dit, nous allons stocker tous les déploiements dans le répertoire deploy / :
mkdir deploy cd deploy
Initialisons notre premiĂšre application:
qbec init website cd website
Maintenant, la structure de notre application ressemble Ă ceci:
. âââ components âââ environments â âââ base.libsonnet â âââ default.libsonnet âââ params.libsonnet âââ qbec.yaml
regardez 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: {}
Ici, nous nous intéressons principalement aux environnements spécifiques , qbec a déjà créé un environnement par défaut pour nous et a pris l'adresse du serveur, ainsi que l'espace de noms de notre kubeconfig actuel.
Désormais, lors du déploiement dans l'environnement par défaut , qbec ne sera toujours déployé que sur le cluster Kubernetes spécifié et sur l'espace de noms spécifié, c'est-à -dire que vous n'aurez plus à 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 de ce fichier.
Tous vos environnements sont dĂ©crits dans qbec.yaml et dans le fichier params.libsonnet , oĂč il indique oĂč vous devez prendre les paramĂštres pour eux.
Ensuite, nous voyons deux répertoires:
- composants / - tous les manifestes pour notre application seront stockĂ©s ici, ils peuvent ĂȘtre dĂ©crits Ă la fois dans jsonnet et dans des fichiers yaml ordinaires
- environnements / - nous décrirons ici toutes les variables (paramÚtres) de nos environnements.
Par défaut, nous avons deux fichiers:
- environnements / base.libsonnet - il contiendra des paramÚtres généraux pour tous les environnements
- environnements / default.libsonnet - contient les paramÚtres remplacés pour l'environnement par défaut
Ouvrons environnement / base.libsonnet et ajoutons-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', }, }, }
Nous allons également créer notre premier composant / 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 immédiatement décrit trois entités Kubernetes, à savoir: Déploiement , Service et Ingress . Si vous le souhaitez, nous pourrions les transporter dans différents composants, mais à ce stade, un suffit pour nous.
La syntaxe jsonnet est trĂšs similaire Ă json standard, en principe json standard est dĂ©jĂ un jsonnet valide, donc au dĂ©but il pourrait ĂȘ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, alors ils peuvent ĂȘtre dĂ©crits sous forme de yaml ordinaire.
Lorsque vous travaillez avec jsonnet, je recommande fortement d'installer un plugin pour votre éditeur
Par exemple, pour vim, il existe un plugin vim-jsonnet qui active la coloration syntaxique et exécute automatiquement jsonnet fmt chaque fois qu'il est enregistré (nécessite l'installation de jsonnet).
Tout est prĂȘt, nous pouvons maintenant commencer le dĂ©ploiement:
Pour voir ce qui s'est passé, nous allons faire:
qbec show default
En sortie, vous verrez des manifestes yaml rendus qui seront appliqués au cluster par défaut.
Ok, appliquez maintenant:
qbec apply default
Ă la sortie, vous verrez toujours ce qui se fera dans votre grappe, qbec vous demandera d'accepter les modifications, en tapant y vous pourrez confirmer vos intentions.
Terminé, maintenant notre application est ancrée!
Si vous apportez des modifications, vous pouvez toujours:
qbec diff default
pour 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 gitlab-runner habituel sur une machine pré-préparée (conteneur LXC) avec un shell ou un docker-executor. Initialement, nous avions plusieurs de ces coureurs définis globalement dans notre hitlab. Ils ont collecté des images de docker pour tous les projets.
Mais comme la pratique l'a montrĂ©, cette option n'est pas la plus idĂ©ale, tant en termes de praticitĂ© qu'en termes de sĂ©curitĂ©. Il est beaucoup mieux et idĂ©ologiquement plus correct de dĂ©ployer des coureurs sĂ©parĂ©s pour chaque projet, et mĂȘme pour chaque environnement.
Heureusement, ce n'est pas un problÚme du tout, car nous allons maintenant déployer gitlab-runner directement dans le cadre de notre projet directement sur Kubernetes.
Gitlab fournit un graphique de barre prĂȘt Ă l'emploi pour le dĂ©ploiement de gitlab-runner dans Kubernetes. Ainsi, tout ce que vous avez Ă faire est de trouver le jeton d'enregistrement de notre projet dans ParamĂštres -> CI / CD -> Runners et de lui passer 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-runner
OĂč:
- https://gitlab.com est l'adresse de votre serveur Gitlab.
- yga8y-jdCusVDn_t4Wxc - jeton d'enregistrement pour votre projet.
- rbac.create = true - Donne au coureur le nombre de privilÚges requis pour pouvoir créer des pods pour effectuer nos tùches en utilisant l'exécuteur kubernetes.
Si tout est fait correctement, vous devriez voir le coureur enregistré dans la section Coureurs , dans les paramÚtres de votre projet.
Capture d'écran du coureur ajouté C'est aussi simple que ça? - oui, si simple! Plus de tracas avec l'enregistrement manuel des coureurs, désormais les coureurs seront créés et détruits automatiquement.
6. Déploiement des cartes 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 référentiel Git.
Nous pourrions le décrire comme un composant distinct du site Web , mais à l'avenir, nous prévoyons de déployer trÚs souvent différentes copies du site Web , contrairement à gitlab-runner , qui ne sera déployé qu'une seule fois pour chaque cluster Kubernetes. Alors initialisons une application distincte pour cela:
cd deploy qbec init gitlab-runner cd gitlab-runner
Cette fois, nous ne dĂ©crirons pas les entitĂ©s Kubernetes manuellement, mais prenons un graphique Helm prĂȘt Ă l'emploi. L'un des avantages de qbec est la possibilitĂ© de rendre les graphiques Helm directement Ă partir du rĂ©fĂ©rentiel Git.
Branchez-le Ă l'aide du sous-module git:
git submodule add https://gitlab.com/gitlab-org/charts/gitlab-runner vendor/gitlab-runner
Maintenant, le répertoire vendor / gitlab-runner contient notre référentiel avec un graphique pour gitlab-runner.
De mĂȘme, vous pouvez connecter d'autres rĂ©fĂ©rentiels, par exemple, l'ensemble du rĂ©fĂ©rentiel avec les graphiques officiels https://github.com/helm/charts
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 nous passons le chemin vers le graphique, puis params.values , que nous prenons des paramĂštres d'environnement, puis un objet avec
- nameTemplate - nom de la version
- namespace - namespace passé à la barre
- thisFile - paramĂštre obligatoire transmettant le chemin d'accĂšs au fichier actuel
- verbeux - affiche la commande de modĂšle de barre avec tous les arguments lors du rendu du graphique
Nous allons maintenant décrire 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, }, }, }, }
Faites attention à runnerRegistrationToken que nous prenons du fichier externe secrets / base.libsonnet , créons-le:
{ runnerRegistrationToken: 'yga8y-jdCusVDn_t4Wxc', }
Vérifiez si tout fonctionne:
qbec show default
si tout est en ordre, nous pouvons supprimer notre version antérieure via la version Helm:
helm uninstall gitlab-runner
et le déployer, mais déjà via qbec:
qbec apply default
7. Présentation de git-crypt
Git-crypt est un outil qui vous permet de configurer un cryptage transparent pour votre référentiel.
Pour le moment, la structure de notre 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 garder des secrets dans Git n'est pas sûr, n'est-ce pas? Nous devons donc les chiffrer correctement.
Habituellement, pour le bien d'une seule variable, cela n'a pas toujours de sens. Vous pouvez transmettre des secrets Ă qbec et via les variables d'environnement de votre systĂšme CI.
Mais il convient de noter qu'il existe des projets plus complexes qui peuvent contenir beaucoup plus de secrets; il sera extrĂȘmement difficile de les transfĂ©rer tous via des variables d'environnement.
De plus, dans ce cas, je ne serais pas en mesure de vous parler d'un outil aussi merveilleux que git-crypt .
git-crypt est Ă©galement pratique car il vous permet de sauvegarder l'historique complet des secrets, ainsi que de comparer, de fusionner et de rĂ©soudre les conflits de la mĂȘme maniĂšre que nous le faisions dans le cas de Git.
Tout d'abord, aprÚs avoir installé git-crypt, nous devons générer des clés pour notre référentiel:
git crypt init
Si vous avez une clé PGP, vous pouvez immédiatement vous ajouter en tant que collaborateur pour ce projet:
git-crypt add-gpg-user kvapss@gmail.com
Ainsi, vous pouvez toujours décrypter ce référentiel à l'aide de votre clé privée.
Si vous n'avez pas de clĂ© PGP et que vous n'ĂȘtes pas attendu, vous pouvez alors aller dans l'autre sens et exporter la clĂ© de projet:
git crypt export-key /path/to/keyfile
De cette façon, toute personne possédant un fichier de clés exporté pourra décrypter votre référentiel.
Il est temps de mettre en place notre premier secret.
Permettez-moi de vous rappeler que nous sommes toujours dans le rĂ©pertoire deploy / gitlab-runner / , oĂč nous avons le rĂ©pertoire secrets /, chiffrons tous les fichiers qu'il contient , pour cela nous crĂ©ons le fichier secrets / .gitattributes avec le contenu suivant:
* filter=git-crypt diff=git-crypt .gitattributes !filter !diff
Comme vous pouvez le voir dans le contenu, tous les fichiers par mask * seront exĂ©cutĂ©s via git-crypt , Ă l'exception de .gitattributes lui-mĂȘme
Nous pouvons le vérifier en exécutant:
git crypt status -e
à la sortie, nous obtenons une liste de tous les fichiers du référentiel pour lesquels le cryptage est activé
C'est tout, maintenant nous pouvons valider nos modifications en toute sécurité:
cd ../.. git add . git commit -m "Add deploy for gitlab-runner"
Pour bloquer le référentiel, faites simplement:
git crypt lock
et puis tous les fichiers cryptés se transforment en quelque chose de binaire, il sera impossible de les lire.
Pour décrypter un référentiel, procédez comme suit:
git crypt unlock
Une image de boßte à outils est une telle image avec tous les outils que nous utiliserons pour déployer notre projet. Il sera utilisé par le runner gitlab pour effectuer des tùches de déploiement typiques.
Ici, tout est simple, nous créons un nouveau dockerfiles / toolbox / 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/helm
Comme vous pouvez le voir, dans cette image, nous installons tous les utilitaires que nous avons utilisĂ©s pour dĂ©ployer notre application. Nous n'avons pas besoin de kubectl ici , mais vous voudrez peut-ĂȘtre jouer avec lui lors de la configuration du pipeline.
De plus, afin de pouvoir communiquer avec Kubernetes et y effectuer un déploiement, nous devons configurer le rÎle des pods générés par gitlab-runner.
Pour ce faire, allez dans le répertoire avec gitlab-runner:
cd deploy/gitlab-runner
et ajoutez 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 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', }, }, }
Remarque $ .components.rbac.name fait référence au nom du composant rbac
Vérifions ce qui a changé:
qbec diff default
et appliquer nos modifications Ă Kubernetes:
qbec apply default
N'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 assemblage d'images par tags
à la racine du projet, nous créerons .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: - tags
Veuillez noter que nous utilisons GIT_SUBMODULE_STRATEGY: normal pour les travaux 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 vous pouvez l'appeler en toute sécurité la version v0.0.1 et accrocher une balise:
git tag v0.0.1
Nous accrocherons les balises chaque fois que nous aurons besoin de publier une nouvelle version. Les balises dans les images Docker seront liées aux balises Git. Chaque poussée avec une nouvelle balise initialisera l'assemblage d'images avec cette balise.
Exécutez git push --tags et regardez notre premier pipeline:
Capture d'Ă©cran du premier pipeline Il convient de prĂȘter votre attention au fait que l'assemblage par balises convient Ă l'assemblage d'images Docker, mais ne convient pas au dĂ©ploiement d'une application dans Kubernetes. Ătant donnĂ© que de nouvelles balises peuvent Ă©galement ĂȘtre affectĂ©es Ă d'anciennes validations, dans ce cas, l'initialisation du pipeline pour elles entraĂźnera le dĂ©ploiement de l'ancienne version.
Pour résoudre ce problÚme, généralement l'assemblage d'images docker est attaché à des balises et le déploiement de l'application à la branche principale, dans laquelle les versions des images collectées sont codées en dur. C'est dans ce cas que vous pouvez initialiser la restauration avec une simple balise maßtre de restauration.
10. Automatisation du déploiement
Pour que Gitlab-runner déchiffre nos secrets, nous devons exporter la clé du référentiel et l'ajouter aux variables d'environnement de notre CI:
git crypt export-key /tmp/docs-repo.key base64 -w0 /tmp/docs-repo.key; echo
enregistrez la chaßne résultante dans Gitlab, pour cela nous allons aller dans les paramÚtres de notre projet:
ParamĂštres -> CI / CD -> Variables
:
.gitlab-ci.yml :
.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 --yes
qbec:
- --root some/app â
- --force:k8s-context __incluster__ â , gtilab-runner. , qbec Kubernetes- kubeconfig
- --wait â qbec , Ready exit-code.
- --yes â Are you sure? .
:
git add .gitlab-ci.yml git commit -m "Automate deploy"
git push :
11. push master
, . digest master-.
: website push master , Kubernetes.
.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"
, master refs build_website $CI_COMMIT_REF_NAME $CI_COMMIT_TAG , Git . , , docker-registry.
docker- , Kubernetes, , .
--vm:ext-str digest="$DIGEST" qbec â jsonnet. . , , , .
Kaniko digest ( --digest-file )
.
deploy/website/environments/base.libsonnet :
{ 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', }, }, }
, master docker- website , Kubernetes.
:
git add . git commit -m "Configure dynamic build"
, git push - :
gitlab-runner push, , , , .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 deploy/gitlab-runner/
:
git add .gitlab-ci.yml git commit -m "Reduce gitlab-runner deploy"
git push , - :
12. Dynamic environments
.
build_website .gitlab-ci.yml , only , Gitlab :
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/
deploy_website , 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"
Gitlab prod .
:
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: manual
push master preview .
qbec: --app-tag â , Kubernetes qbec .
review, .
qbec apply review , qbec apply default â (review default):
review deploy/website/qbec.yaml
spec: environments: review: defaultNamespace: docs server: https://kubernetes.example.org:8443
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.thisFile
deploy/website/environments/review.libsonnet :
stop_review , gitlab checkout GIT_STRATEGY: none , master - review .
, .
review , .
:
git add . git commit -m "Enable automatic review"
git push , git checkout -b test , git push origin test , :
? â , : git checkout master , git push origin :test , environment .
, , .gitlab-ci.yml .
protected-, master , .
13. Review Apps
Review Apps , .
, .gitlab/route-map.yml , :
# Indices - source: /content\/(.+?)_index\.(md|html)/ public: '\1' # Pages - source: /content\/(.+?)\.(md|html)/ public: '\1/'
:
git add .gitlab/ git commit -m "Enable review apps"
git push , :
Job is done!
:
, 