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! Récemment, de nombreux outils d'automatisation intéressants ont été publiés à la fois pour créer des images Docker et pour le déploiement sur Kubernetes. À cet égard, j'ai décidé de jouer avec GitLab, d'étudier en profondeur ses capacités et, bien sûr, de mettre en place le pipeline.

Ce travail a été inspiré par le site Web kubernetes.io, qui est généré à partir de codes sources automatiquement, et pour chaque demande de pool envoyée, le robot génère automatiquement une version aperçu du site avec vos modifications et fournit un lien de 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'ai l'habitude d'utiliser pour déployer des applications sur Kubernetes. Aujourd'hui, je vais enfin vous en dire plus à leur sujet.

L'article abordera des outils tels que :
Hugo, Québec, Kaniko, git-crypte и CI GitLab avec la création d’environnements dynamiques.

contenu

  1. Rencontrez Hugo
  2. Préparation du fichier Docker
  3. Apprendre à connaître Kaniko
  4. Apprendre à connaître le Québec
  5. Essayer Gitlab-runner avec Kubernetes-executor
  6. Déployer des cartes Helm avec qbec
  7. Présentation de git-crypt
  8. Création d'une image de boîte à outils
  9. Notre premier pipeline et 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. Faire connaissance avec Hugo

A titre d'exemple de notre projet, nous allons essayer de créer un site de publication de documentation construit sur Hugo. Hugo est un générateur de contenu statique.

Pour ceux qui ne connaissent pas les générateurs statiques, je vais vous en parler un peu plus. Contrairement aux moteurs de sites Web classiques dotés d'une base de données et d'un peu de PHP, qui, à la demande d'un utilisateur, génèrent des pages à la volée, les générateurs statiques sont conçus un peu différemment. Ils vous permettent de prendre des sources, généralement un ensemble de fichiers dans des modèles de balisage et de thème Markdown, puis de les compiler dans un site Web entièrement terminé.

Autrement dit, vous recevrez une structure de répertoires et un ensemble de fichiers HTML générés, que vous pourrez simplement télécharger sur n'importe quel hébergement bon marché et obtenir un site Web fonctionnel.

Vous pouvez installer Hugo localement et l'essayer :

Initialisation d'un 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, nous devons d'abord connecter un thème ; un thème est simplement un ensemble de modèles et de règles spécifiées par lesquels notre site est généré.

Pour le thème nous utiliserons Ressources, ce qui, à mon sens, convient parfaitement à un site de documentation.

Je voudrais accorder une attention particulière au fait que nous n'avons pas besoin de sauvegarder les fichiers de thème dans le référentiel de notre projet ; nous pouvons simplement le connecter en utilisant sous-module git:

git submodule add https://github.com/matcornic/hugo-theme-learn themes/learn

Ainsi, notre référentiel ne contiendra que les fichiers directement liés à notre projet, et le thème connecté restera comme un lien vers un référentiel spécifique et un commit dans celui-ci, c'est-à-dire qu'il pourra toujours être extrait de la source d'origine sans avoir peur de changements 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 exécuter :

hugo server

Et à l'adresse http://localhost:1313/ consultez notre site Web nouvellement créé, toutes les modifications apportées dans l'annuaire mettent automatiquement à jour la page ouverte dans le navigateur, très pratique !

Essayons de créer une page de garde 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, exécutez simplement :

hugo

Contenu du répertoire Publique/ et sera votre site Web.
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 fichier Docker

Il est temps de définir la structure de notre référentiel. J'utilise habituellement quelque chose comme :

.
├── deploy
│   ├── app1
│   └── app2
└── dockerfiles
    ├── image1
    └── image2

  • fichiers docker/ — contiennent des répertoires avec des Dockerfiles et tout le nécessaire pour créer nos images Docker.
  • déployer/ — contient des répertoires pour déployer nos applications sur Kubernetes

Ainsi, nous créerons 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 constater, le Dockerfile contient deux De, cette fonctionnalité est appelée construction en plusieurs étapes et vous permet d'exclure tout ce qui est inutile de l'image Docker finale.
Ainsi, l'image finale ne contiendra que sombrehttpd (serveur HTTP léger) et Publique/ — le contenu de notre site Web 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 son fonctionnement ne nécessite pas de démon Docker, 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 de disposer d'un stockage persistant à part entière.

Pour créer l'image, exécutez simplement le conteneur avec exécuteur testamentaire Kaniko et transmettez-lui le contexte de build actuel ; cela peut également ê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

registre.gitlab.com/kvaps/docs.example.org/website — le nom de votre image docker ; après construction, elle sera automatiquement lancée dans le registre docker.

Paramètre --cache permet de mettre en cache les couches dans le registre Docker ; pour l'exemple donné, elles seront enregistrées dans registre.gitlab.com/kvaps/docs.example.org/website/cache, mais vous pouvez spécifier un autre chemin à l'aide du paramètre --cache-dépôt.

Capture d'écran du registre Docker

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

4. Apprendre à connaître le Québec

Québec est un outil de déploiement qui vous permet de décrire de manière déclarative vos manifestes d'application et de les déployer sur Kubernetes. L'utilisation de Jsonnet comme syntaxe principale vous permet de simplifier considérablement la description des différences entre plusieurs environnements et d'éliminer également presque complètement la répétition du code.

Cela peut être particulièrement vrai dans les cas où vous devez déployer une application sur plusieurs clusters avec des paramètres différents et souhaitez les décrire de manière déclarative dans Git.

Qbec vous permet également de restituer les graphiques Helm en leur transmettant les paramètres nécessaires, puis de les exploiter de la même manière que les manifestes réguliers, vous pouvez notamment leur appliquer diverses mutations, ce qui, à son tour, vous permet de vous débarrasser de la nécessité de utilisez ChartMuseum. Autrement dit, vous pouvez stocker et afficher des graphiques directement depuis git, à leur place.

Comme je l'ai dit plus tôt, 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: {}

Ici, nous nous intéressons principalement à spec.environnements, 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 sur défaut environnement, qbec déploiera toujours uniquement 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 est indiqué où obtenir les paramètres correspondants.

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 des fichiers jsonnet et yaml normaux
  • 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 communs à tous les environnements
  • environnements/default.libsonnet — contient des paramètres remplacés pour l'environnement défaut

ouvrons environnements/base.libsonnet et ajoutez-y des paramètres pour notre premier composant :

{
  components: {
    website: {
      name: 'example-docs',
      image: 'registry.gitlab.com/kvaps/docs.example.org/website:v0.0.1',
      replicas: 1,
      containerPort: 80,
      servicePort: 80,
      nodeSelector: {},
      tolerations: [],
      ingressClass: 'nginx',
      domain: 'docs.example.org',
    },
  },
}

Créons également notre premier composant composants/site web.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, Service и Entrée. Si nous le voulions, nous pourrions les regrouper en différents composants, mais à ce stade, un seul nous suffira.

syntaxe jsonnet est très similaire au json normal, en principe, le json régulier est déjà un jsonnet valide, donc au début, il peut ê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, alors ils peuvent être décrits sous la forme d'un yaml normal.

Lorsque vous travaillez avec jsonnet Je recommande fortement d'installer un plugin pour votre éditeur

Par exemple, il existe un plugin pour vim vim-jsonnet, qui active la coloration syntaxique et exécute automatiquement jsonnet fmt chaque fois que vous enregistrez (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

En sortie, vous verrez les manifestes yaml rendus qui seront appliqués au cluster par défaut.

Super, postulez maintenant :

qbec apply default

En sortie vous verrez toujours ce qui sera fait dans votre cluster, qbec vous demandera d'accepter les changements en tapant y vous pourrez confirmer vos intentions.

Notre application est prête et 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 des gitlab-runner sur une machine pré-préparée (conteneur LXC) avec shell ou docker-exécuteur. Au départ, nous avions plusieurs coureurs de ce type définis globalement dans notre gitlab. Ils ont collecté des images 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é que de sécurité. Il est bien préférable et idéologiquement plus correct de déployer des coureurs distincts pour chaque projet, voire pour chaque environnement.

Heureusement, ce n'est pas du tout un problème, puisque nous allons maintenant déployer gitlab-runner directement dans le cadre de notre projet directement dans Kubernetes.

Gitlab fournit un graphique de barre prêt à l'emploi pour déployer gitlab-runner sur Kubernetes. Il ne vous reste plus qu'à découvrir jeton d'enregistrement pour notre projet en Paramètres -> CI / CD -> Coureurs et passez-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 — fournit au coureur le nombre de privilèges nécessaire pour pouvoir créer des pods pour effectuer nos tâches à l'aide de kubernetes-executor.

Si tout est fait correctement, vous devriez voir un 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

Est-ce si simple ? - oui, c'est aussi simple que ça ! Plus de soucis pour l'inscription manuelle des coureurs, désormais les coureurs seront créés et détruits automatiquement.

6. Déployez des cartes Helm avec QBEC

Depuis que nous avons décidé d'envisager gitlab-runner fait 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 distinct site de NDN Collective, mais à l'avenir, nous prévoyons de déployer différentes copies site de NDN Collective 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 les entités Kubernetes manuellement, mais prendrons un graphique Helm prêt à l'emploi. L'un des avantages de qbec est la possibilité de restituer des graphiques Helm directement à partir d'un référentiel Git.

Connectons-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 Nous avons un 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 on passe le chemin vers le graphique, puis paramètres.valeurs, que nous prenons des paramètres d'environnement, vient ensuite l'objet avec

  • nomModèle — nom de la version
  • namespace — espace de noms transféré à la barre
  • ce fichier — un paramètre obligatoire qui transmet le chemin d'accès au fichier actuel
  • verbeux - affiche 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 runnerRegistrationToken nous prenons à partir d'un fichier externe secrets/base.libsonnet, créons-le :

{
  runnerRegistrationToken: 'yga8y-jdCusVDn_t4Wxc',
}

Vérifions si tout fonctionne :

qbec show default

si tout est en ordre, alors nous pouvons supprimer notre version précédemment déployée via Helm :

helm uninstall gitlab-runner

et déployez-le de la même manière, mais via qbec :

qbec apply default

7. Introduction à git-crypt

Git-crypte est un outil qui vous permet de mettre en place un cryptage transparent pour votre référentiel.

Pour le moment, notre structure de répertoires 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 ? Nous devons donc les chiffrer correctement.

Habituellement, pour le bien d’une variable, cela n’a pas toujours de sens. Vous pouvez transférer des secrets vers 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 transférer 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 dans la mesure où il 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 dans le cas de 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, alors vous pouvez immédiatement vous ajouter en tant que collaborateur pour ce projet :

git-crypt add-gpg-user [email protected]

De cette façon, vous pouvez toujours déchiffrer ce référentiel en utilisant votre clé privée.

Si vous n’avez pas de clé PGP et que vous ne l’attendez pas, vous pouvez procéder dans l’autre sens et exporter la clé du projet :

git crypt export-key /path/to/keyfile

Ainsi, quiconque possède un produit exporté fichier clé pourra décrypter votre référentiel.

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 le montre le contenu, tous les fichiers sont masqués * sera traversé git-crypte, sauf pour la plupart .gitattributes

Nous pouvons vérifier cela en exécutant :

git crypt status -e

Le résultat sera une liste de tous les fichiers du référentiel pour lesquels le cryptage est activé

C'est tout, 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éez une image de boîte à outils

Une image boîte à outils est une 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.

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 que nous avons utilisés pour déployer notre application. Nous n'en avons pas besoin ici à moins que kubectl, mais vous souhaiterez peut-être jouer avec pendant la phase de configuration du pipeline.

De plus, afin de 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, allons dans le répertoire avec gitlab-runner :

cd deploy/gitlab-runner

et ajoutez 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 à prénom pour composant rbac

Vérifions ce qui a changé :

qbec diff default

et appliquez nos modifications à Kubernetes :

qbec apply default

N’oubliez pas non plus de valider nos modifications sur 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

A 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 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 appeler cela une version en toute sécurité v0.0.1 et ajoutez la balise :

git tag v0.0.1

Nous ajouterons des 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 push avec une nouvelle balise initialisera la construction des images avec cette balise.

Effectuer 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 convient d'attirer votre attention sur le fait que l'assemblage par balises convient à la création d'images Docker, mais ne convient pas au déploiement d'une application sur Kubernetes. Étant donné que de nouvelles balises peuvent être attribuées à d’anciens commits, dans ce cas, l’initialisation du pipeline pour celles-ci entraînera le déploiement de l’ancienne version.

Pour résoudre ce problème, la création d'images Docker est généralement liée à des balises et le déploiement de l'application sur une branche maître, dans lesquelles les versions des images collectées sont codées en dur. C'est ici que vous pouvez initialiser la restauration avec un simple retour en arrière maître-branches.

10. Automatisation du déploiement

Pour que Gitlab-runner déchiffre 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 ligne résultante dans Gitlab ; pour cela, allons 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 formation vous pouvez 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 notre .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 :

  • --root certains/app — vous permet de déterminer 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 dans lequel gtilab-runner est exécuté. Ceci est nécessaire car sinon qbec tentera de trouver un serveur Kubernetes adapté dans votre kubeconfig
  • --attendez - oblige qbec à attendre que les ressources qu'il crée passent à l'état Prêt et ensuite seulement à quitter avec un code de sortie réussi.
  • -Oui - désactive simplement le shell interactif Es-tu sûr? une fois déployé.

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 maître

En règle générale, les étapes décrites ci-dessus sont suffisantes pour créer et fournir presque n'importe quel microservice, mais nous ne souhaitons pas ajouter de balise à chaque fois que nous devons mettre à jour le site. Par conséquent, nous allons emprunter une voie plus dynamique et mettre en place un déploiement digest dans la branche master.

L'idée est simple : désormais l'image de notre site de NDN Collective sera reconstruit à chaque fois que vous entrerez maître, puis déployez 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é un fil de discussion maître к réfs pour les emplois build_siteweb et nous utilisons maintenant $CI_COMMIT_REF_NAME au lieu de $CI_COMMIT_TAG, c'est-à-dire que nous sommes déliés des balises dans Git et que nous allons maintenant pousser une image avec le nom de la branche de validation qui a initialisé le pipeline. Il convient de noter que cela fonctionnera également avec les balises, qui nous permettront de sauvegarder des instantanés d'un site avec une version spécifique dans le registre Docker.

Lorsque le nom de la balise docker d'une nouvelle version du site peut rester inchangé, nous devons encore 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 permet de passer une variable externe à jsonnet. Nous souhaitons qu'il soit redéployé dans le cluster à chaque release de notre application. Nous ne pouvons plus utiliser le nom de la balise, qui peut désormais être immuable, puisqu'il faut être lié à 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 --digest-fichier)
Ensuite, nous transférerons 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',
    },
  },
}

C'est fait, maintenant n'importe quel commit maître initialise la construction de l'image Docker pour site de NDN Collective, 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-le 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 suivre les changements dans déployer/gitlab-runner/ et déclenchera notre travail seulement s'il y en a

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.

Tout d'abord, mettons à jour le travail build_siteweb dans notre .gitlab-ci.yml, en supprimant le bloc uniquement, ce qui forcera Gitlab à le déclencher lors de n'importe quel commit dans n'importe quelle branche :

build_website:
  extends: .build_docker_image
  variables:
    GIT_SUBMODULE_STRATEGY: normal
  script:
    - mkdir -p $CI_PROJECT_DIR/artifacts
    - /kaniko/executor --cache --context $CI_PROJECT_DIR --dockerfile $CI_PROJECT_DIR/dockerfiles/website/Dockerfile --destination $CI_REGISTRY_IMAGE/website:$CI_COMMIT_REF_NAME --digest-file $CI_PROJECT_DIR/artifacts/website.digest
  artifacts:
    paths:
      - artifacts/

Puis mettez à jour le travail déployer_siteweb, ajoutez-y un bloc sûr, heureux et sain:

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 lors du push vers toutes les branches sauf master et déploieront la version préliminaire du site.

On voit une nouvelle option pour le Québec : --app-tag — il permet de baliser 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 ne pouvons pas créer un environnement séparé pour chaque avis, mais simplement réutiliser le même.

Ici, nous utilisons également qbec postuler examen, au lieu de qbec appliquer par défaut - c'est justement le moment où nous allons tenter de décrire les différences pour nos environnements (revue et défaut) :

Ajouter Avis environnement dans déployer/site web/qbec.yaml

spec:
  environments:
    review:
      defaultNamespace: docs
      server: https://kubernetes.example.org:8443

Ensuite, nous le déclarerons dans déployer/site web/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 notez les paramètres personnalisés correspondants dans déployer/site Web/environnements/review.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',
    },
  },
}

Regardons également de plus près jobu stop_review, il sera déclenché lorsque la branche sera supprimée et pour que gitlab n'essaye pas de l'extraire, il est utilisé GIT_STRATEGY : aucun, plus tard on clone maître-branchez et supprimez l'avis à travers celui-ci.
C’est un peu déroutant, mais je n’ai pas encore trouvé de plus belle façon.
Une option alternative consisterait à déployer chaque avis dans un espace de noms d'hôtel, qui peut toujours être entièrement démoli.

N'oubliez pas de valider nos modifications :

git add .
git commit -m "Enable automatic review"

git push, test git checkout -b, test d'origine git push, vérifier:

Capture d'écran des environnements créés dans Gitlab

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

Tout fonctionne ? - super, supprime notre branche test : maître de caisse git, git push origine : test, nous vérifions que les tâches de suppression de l'environnement ont fonctionné sans erreur.

Ici, je voudrais immédiatement préciser que tout développeur d'un projet peut créer des branches, il peut également changer .gitlab-ci.yml fichier et accéder aux variables secrètes.
Il est donc fortement recommandé de n'autoriser leur utilisation que pour des 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 un environnement déployé.

Pour que ces boutons apparaissent, vous devez créer un fichier .gitlab/route-map.yml et décrivez toutes les transformations de chemin qu'il contient ; 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 vérifie:

Capture d'écran du bouton Review App

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

La tâche est finie!

Sources du projet :

Merci pour votre attention, j'espère que cela vous a plu Essayer de nouveaux outils pour créer et automatiser le déploiement dans Kubernetes

Source: habr.com

Ajouter un commentaire