Aller au contenu principal

Renovate aumtomatise les maj de conteneurs

· 8 minutes de lecture
Stéphane ROBERT
Consultant DevOps

MAJ : Remplacement de docker-dind par img.

Dans le billet précédent, je vous exposais comment j'utilise mon cluster kubernetes dans pour faire tourner mes runners gitlab. Je vous propose de voir comment utiliser ces runners pour builder, tester et valider des images de container.

Pour cela je vais utiliser quelques un des outils que je vous ai exposés :

  • renovate qui scrute les projets à la recherche de dépendances à mettre à jour
  • lastversion qui récupère la dernière version d'un produit
  • halodint pour valider la qualité d'écriture des Dockerfiles
  • container-structure-test pour valider le contenu d'une image
  • trivy pour valider que l'image produite ne contient des vulnérabilités.

Tous ces outils, je vais les mettre en oeuvre dans un pipeline gitlab-ci pour qu'à chaque nouvelle version détectée par renovate (run schédulé) un build d'une nouvelle image soit produite et validée. Restera plus qu'à accepter la merge Request créé par Renovate pour que l'image soit poussé dans votre registry Docker.

Paramétrage de Docker (ancienne version -> plus utilisé -> passage à img)

Pour que Docker soit accessible depuis les jobs Gitlab je dois l'exposer en mode TCP.

Il faut éditer le fichier /etc/docker/daemon.json pour ajouter dans les hosts le mode tcp sur le port 2375 (non sécurisé tls = false) :

{
  "hosts": ["unix:///var/run/docker.sock", "tcp://0.0.0.0:2375"],
  "tls": false,
  "insecure-registries": [
  ]
}

Pour surcharger les paramètres du service docker je crée un fichier override.conf dans le répertoire /etc/systemd/system/docker.service.d/ avec ce contenu :

[Service]
ExecStart=
ExecStart=/usr/bin/dockerd

On redémarre le service :

sudo systemctl restart docker

On teste qu'on arrive à se connecter en mode TCP :

export DOCKER_HOST=tcp://<ip de votre serveur docker>:2375
export DOCKER_TLS_CERTDIR=""

docker info
...

WARNING: API is accessible on http://0.0.0.0:2375 without encryption.
         Access to the remote API is equivalent to root access on the host. Refer
         to the 'Docker daemon attack surface' section in the documentation for
         more information: https://docs.docker.com/go/attack-surface/

Pour sécuriser le tout je devrais activer tls, mais je n'ai trouvé comment injecter le certificat dans le runner gitlab.

Création des images sans dind avec img

img est un projet qui est en fait une surcouche de buildkit, dont l'objectif est de permettre de construire des images sans privilège (sans root). Pour optimiser les temps de build, je vais l'utiliser avec l'option de sortie au format oci-tar. trivy et container-struct-test prennent en charge ce format.

Vous pouvez l'installer sur votre poste de travail :

sudo apt install libseccomp-dev uidmap
export IMG_SHA256= " cc9bf08794353ef57b400d32cd1065765253166b0a09fba360d927cfbd158088 "
curl -fSL " https://github.com/genuinetools/img/releases/download/v0.5.11/img-linux-amd64 " -o " /usr/local/bin/img " \
 && echo "${IMG_SHA256 } /usr/local/bin/img" | sha256sum -c - \
 && chmod a+x "/usr/local/bin/img"

Bonne nouvelle pour les images à base d'alpine le package est disponible.

apk add img

Les options sont les mêmes que celle de la cli docker : build, tag, push, pull, login, logout, save.

Pour optimiser les temps de build, il est possible d'utiliser l'option --cache-from.

Création du groupe dans Gitlab

Je vais créer un groupe docker pour stocker deux sous-groupes images et template ainsi qu'un projet renovate-bot.

Ma structure

Création de la registry Docker

La registry docker sera celle porté par le groupe images. J'ai déjà expliqué tout cela dans un précédent billet. N'oubliez de créer le PAT pour vous connecter à la registry (je lui ai donné tous les droits, mais je pense qu'on peut limiter).

docker login registry.gitlab.com -u <votre-compte> -p <votre-token>

Login Succeeded

Création des variables du groupe

Dans les settings CI/CD du groupe docker créer deux variables : DEPLOY_USER qui votre compte gitlab et DEPLOY_TOKEN qui est le PAT créé précédemment.

Création des images nécessaires au pipeline

Nous allons créer plusieurs qui seront utilisé dans le pipeline: hadolint, lastversion et container-structure-test. A la fin vous devriez obtenir ceci dans votre registry :

Création de l'image Lastversion

Cloner le projet lastversion.

git clone https://gitlab.com/dockerfiles6/images/lastversion.git
cd lastversion
rm -rf .git

Vous ferez ainsi pour tous les projets !

On peut lancer le build :

export REGISTRY=<votre-registry-docker>
./build.sh
 ---> 951262b8e206

Successfully built 951262b8e206
Successfully tagged registry.gitlab.com/dockerfiles6/images/lastversion:2.0.1

En sortie vous devriez avoir le nom de l'image. Il reste plus qu'à pousser dans votre registry docker.

docker login registry.gitlab.com -u <votre-compte> -p <votre-token>

docker push ${REGISTRY}/lastversion:<version>

Un conseil jetez un œil au Dockerfile, je me suis bien amusé à l'écrire celui-là. J'explique tout dans ce billet sur l'optimisation des images python.

Création de l'image Img

On reprend le même principe. Cloner le projet img.

Et faire comme précédemment.

Création de l'image Hadolint

On reprend le même principe. Cloner le projet hadolint.

Et faire comme précédemment.

Création de l'image container-structure-test

On reprend le même principe. Cloner le projet container-structure-test.

Et faire comme précédemment.

Ecriture du template de CI

Pour éviter de dupliquer du code, je vais utiliser un template de pipeline Gitlab.

On reprend le même principe. Cloner le projet ci-build.

Quelques explications

Tout se passe dans le fichier build.yml :

variables:
  REGISTRY: registry.gitlab.com/dockerfiles6/images


stages:
  - lint
  - version
  - build
  - tests
  - push

lint:
  tags:
    - myrunner
  stage: lint
  image: registry.gitlab.com/dockerfiles6/images/hadolint:2.9.2
  script:
    - hadolint --ignore DL3018 --ignore DL3008 --ignore SC2015 --ignore=DL3041 Dockerfile

version:
  tags:
    - myrunner
  stage: version
  image: registry.gitlab.com/dockerfiles6/images/lastversion:2.2.2
  script:
    - export VERSION=$(lastversion ${SOURCE})
    - envsubst < test.yml > unittest.yml
    - echo "VERSION=$VERSION" >> build.env
  artifacts:
    reports:
      dotenv: build.env
    paths:
      - unittest.yml

build:
  tags:
    - myrunner
  stage: build
  image: registry.gitlab.com/dockerfiles6/images/img:0.5.11
  dependencies:
    - version
  script:
    - img build --cache-from registry.gitlab.com/dockerfiles6/images/${PRODUCT}:latest -o type=oci,dest=image.tar .
  artifacts:
    paths:
      - image.tar

trivy:
  tags:
    - myrunner
  stage: tests
  image:
    name: aquasec/trivy:0.24.4
    entrypoint: [""]
  dependencies:
    - version
    - build
  script:
    - trivy image --input image.tar

test:
  tags:
    - myrunner
  stage: tests
  image:
    name: registry.gitlab.com/dockerfiles6/images/container-structure-test:1.11.0
    entrypoint: [""]
  dependencies:
    - version
    - build
  script:
    - container-structure-test test --driver tar --image image.tar --config unittest.yml

push:
  tags:
    - myrunner
  rules:
    - if: $CI_COMMIT_BRANCH == "master"
    - if: $CI_COMMIT_BRANCH == "main"
  stage: push
  image: ${REGISTRY}/img:0.5.11
  dependencies:
    - version
  script:
    - img build --cache-from ${REGISTRY}/${PRODUCT}:latest -t ${REGISTRY}/${PRODUCT}:${VERSION} -t ${REGISTRY}/${PRODUCT}:latest .
    - echo "${DEPLOY_TOKEN}" | img login --username ${DEPLOY_USER} --password-stdin registry.gitlab.com
    - img push ${REGISTRY}/${PRODUCT}:${VERSION}
    - img push ${REGISTRY}/${PRODUCT}:latest
...

Les parties importantes :

  • La registry Docker ou sont stockés les images
variables:
  REGISTRY: registry.gitlab.com/dockerfiles6/images
  • on met une petite règle sur le job push qu'il ne s'exécute que sur la branche master ou main

Configuration de renovate pour ce projet

Il faut indiquer à Renovate qu'il faut regarder dans les fichiers de type *.yml. Ca se passe dans le fichier renovete.json :

{
  "$schema": "https://docs.renovatebot.com/renovate-schema.json",
  "extends": ["config:base"],
  "gitlabci": {
    "fileMatch": ["^*.yml$"
    ]
  }
}

Pousser le tout dans votre groupe docker

  1. Créez un projet ci-build dans le sous-groupe template et pousser le contenu que vous aurez modifié.
  2. Créez des projets hadolint, lastversion et container-structure-test dans le sous-groupe images et poussez-y les projets. Normalement dans ces projets les pipelines vous se déclencher.

Et pour finir renovate

Renovate va scruter les projets qui sont déclarés dans sa configuration à la recherche de dépendances.

Création du projet

On reprend le même principe. Cloner le projet renovate-bot

git clone https://gitlab.com/dockerfiles6/renovate-bot.git
cd renovate-bot
rm -rf .git

Editez le fichier config.js pour modifier les urls des projets dans la section repositories:

module.exports = {
  hostRules: [
    {
      hostType: "docker",
      matchHost: "registry.gitlab.com/dockerfiles6",
      username: process.env.DEPLOY_USER,
      password: process.env.DEPLOY_TOKEN,
    },
  ],
  endpoint: "https://gitlab.com/api/v4/",
  platform: "gitlab",
  gitUrl: "https",
  onboardingConfig: {
    extends: ["config:base"],
  },
  vulnerabilityAlerts: {
    labels: ["security"],
    schedule: "at any time",
  },
  repositories: [
    "dockerfiles6/template/ci-build",
    "dockerfiles6/images/hadolint",
    "dockerfiles6/images/ansible-lint",
    "dockerfiles6/images/lastversion",
    "dockerfiles6/images/container-structure-test",
    "dockerfiles6/images/img",
    "b4288/infra-as-code-homelab",
    "dockerfiles6/build-python-wheel"
  ],
};

Création des variables

Créez un second PAT gitlab et un PAT GitHub qui sera utilisé par renovate. Créez deux variables :

  • GITHUB_COM_TOKEN contenant le PAT Github
  • RENOVATE_TOKEN contenant le second PAT gitlab

Maintenant vous pouvez pousser le projet dans gitlab. Rien ne se lance, car le pipeline ne se lance que celui-ci vient de la source schedule.

Mise en place du scheduling

Pour éviter que renovate ne consomme les ressources quand j'en ai besoin, j'ai fait le choix de les scheduler de nuit. Dans le menu CI/CD > Schedules > [New schedule]

On finit par [Save Pipeline]

Déclenchement d'un pipeline

Il suffit de cliquer que la petite icône [>]. Ensuite allez dans le menu CI/CD > Pipelines. Normalement ça tourne.

A la fin allez dans le groupe Docker et vérifier le compteur de Merge requests. S'il n'en y pas, c'est que renovate à rien retrouver à dire.

Pour tester, remplacer par exemple l'image source du Dockerfile de lastversion. Et relancez !

Cliquez sur une d'entre elles.

Si le pipeline s'est correctement déroulé alors vous pouvez [Merger]. Cela poussera la nouvelle version dans votre registry docker.

Reste à faire

Je dois trouver le moyen d'optimiser le temps de build en trouvant le moyen d'activer le cache dans les jobs de build.

J'aimerais trouver une solution pour me passer de lastversion et construire le numéro de version de l'image avec ce que retourne renovate.

Je pense qu'il y a moyen d'implémenter d'autres outils. Je le ferais si les idées me viennent comme l'outil kbld.

Plus loin

Si vous voulez modifier le template de build, créez une branche et modifier la référence dans le fichier .gitlab-ci.yml d'une image pour tester vos modifications.

Vous avez plus qu'à écrire plein de Dockerfile. C'est ce que je vous souhaite.