Essayer de nouveaux outils pour créer et automatiser le déploiement dans Kubernetes

Essayer de nouveaux outils pour créer et automatiser le déploiement dans Kubernetes

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 kubernetes.io, qui est généré à partir de codes sources 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
  2. Préparation du Dockerfile
  3. Apprendre Ă  connaĂźtre Kaniko
  4. Apprendre Ă  connaĂźtre qbec
  5. Essai de Gitlab-runner avec Kubernetes-executor
  6. Déploiement de graphiques Helm avec qbec
  7. Découvrir Git-Crypt
  8. Créer une image de boßte à outils
  9. Notre premier pipeline et l'assemblage d'images par tags
  10. Automatisation du déploiement
  11. Artefacts et assemblage lors de la poussée vers le maßtre
  12. Environnements dynamiques
  13. Examiner les applications

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.org

Et aussi un dépÎt git :

cd docs.example.org
git init

Pour 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 Apprendre, 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/learn

De 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 server

Et à l'adresse http://localhost:1313/ 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

Essayer de nouveaux outils pour créer et automatiser le déploiement dans Kubernetes

Pour générer un site Web, exécutez simplement :

hugo

Contenu du répertoire Publique/ et sera votre site Web.
Oui, au fait, ajoutons-le tout de suite .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 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 construction en plusieurs étapes 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 Kaniko, 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.1

OĂč 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

Essayer de nouveaux outils pour créer et automatiser le déploiement dans Kubernetes

4. Apprendre Ă  connaĂźtre qbec

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 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

regardons 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 default

La sortie vous montrera les manifestes yaml rendus qui seront appliqués au cluster par défaut.

Super, maintenant appliquons-le :

qbec apply default

En 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 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 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-runner

OĂč:

  • https://gitlab.com — 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é

Essayer de nouveaux outils pour créer et automatiser le déploiement dans Kubernetes

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-runner

Cette 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-runner

Maintenant 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 https://github.com/helm/charts

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 default

si tout va bien, nous pouvons alors supprimer notre version prĂ©cĂ©demment dĂ©ployĂ©e via Helm :

helm uninstall gitlab-runner

et le déployer, mais via qbec :

qbec apply default

7. Introduction Ă  git-crypt

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 init

Si 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.com

De 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/keyfile

Ainsi, 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 !diff

Comme 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 -e

La 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 lock

et 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 unlock

8. 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/helm

Comme 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-runner

et 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 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 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:
      - tags

Veuillez 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.1

Nous 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

Essayer de nouveaux outils pour créer et automatiser le déploiement dans Kubernetes

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; echo

Nous 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

Essayer de nouveaux outils pour créer et automatiser le déploiement dans Kubernetes

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 --yes

Ici, 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

Essayer de nouveaux outils pour créer et automatiser le déploiement dans Kubernetes

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

Essayer de nouveaux outils pour créer et automatiser le déploiement dans Kubernetes

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

Essayer de nouveaux outils pour créer et automatiser le déploiement dans Kubernetes

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: manual

Ils 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:8443

Ensuite, 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.thisFile

Et 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

Essayer de nouveaux outils pour créer et automatiser le déploiement dans Kubernetes

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

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 Â»

Essayer de nouveaux outils pour créer et automatiser le déploiement dans Kubernetes

Travail terminé !

Sources du projet :

Merci pour votre attention, j'espÚre que vous avez apprécié Essayer de nouveaux outils pour créer et automatiser le déploiement dans Kubernetes

Source: habr.com

Achetez un hĂ©bergement fiable pour les sites avec protection DDoS, serveurs VPS VDS đŸ”„ Achetez un hĂ©bergement web fiable avec protection DDoS, serveurs VPS et VDS | ProHoster