hoortrend.com

Documentation

Livraison

Delivery

Construire, scanner et déployer : les pipelines de chaque projet, GitOps avec Argo CD et les vrais fichiers de configuration.

Build, scan and deploy: each project's pipeline, GitOps with Argo CD and the real configuration files.

Construire et déployerBuild and deploy

Tout le code vit dans GitLab : le groupe hoortrend (landing, lab, demo-app, inside, sre-demo, infra, auth-lab) et, à part, le groupe ilyes-litim pour le portfolio. Seul demo-app est public ; les autres dépôts sont privés. Chaque git push sur main lance un pipeline, et les images sont publiées dans le registry de GitLab.

  1. validate : syntaxe du code, des manifestes et des fichiers référencés par les pages.
  2. build : l'image est construite avec Kaniko dans un pod, sans accès privilégié, puis publiée sous le tag du commit.
  3. scan : Trivy analyse l'image publiée ; une vulnérabilité HIGH ou CRITICAL corrigeable bloque la suite. Dans chaque projet qui construit une image.
  4. promote : seulement ensuite, l'image reçoit le tag :latest (pour l'image kubectl d'infra, aussi son numéro de version). Les manifestes désignent ce tag ; un contrôle de la CI refuse un tag figé.
  5. deploy : kubectl set image sur le Deployment, puis attente de la fin du déploiement progressif. Pour demo-app, le reste des manifestes est appliqué par Argo CD (voir GitOps avec Argo CD).
  6. verify : un test de fumée interroge le site public.

Sur une merge request, seules les étapes de validation et de build tournent, sans publier ni déployer, et la merge request ne peut être fusionnée que si ce pipeline a réussi (réglage actif sur tous les projets). Avec maxUnavailable: 0, un nouveau pod doit être prêt avant qu'un ancien soit arrêté : le site reste disponible pendant la mise à jour.

Les jobs s'exécutent dans un runner Kubernetes. Son compte de service ne peut changer que l'image d'un seul Deployment nommé par namespace : la CI n'a pas de droits d'administration du cluster. Les pods tirent leurs images avec un jeton en lecture seule.

All code lives in GitLab: the hoortrend group (landing, lab, demo-app, inside, sre-demo, infra, auth-lab) and, separately, the ilyes-litim group for the portfolio. Only demo-app is public; the other repositories are private. Every git push to main starts a pipeline, and images are published to the GitLab registry.

  1. validate: syntax of the code, manifests and the files pages reference.
  2. build: the image is built with Kaniko inside a pod, without privileged access, then published under the commit tag.
  3. scan: Trivy scans the published image; a fixable HIGH or CRITICAL vulnerability stops the rest. In every project that builds an image.
  4. promote: only then does the image get the :latest tag (infra's kubectl image also gets its version number). Manifests name that tag; a CI check refuses a frozen tag.
  5. deploy: kubectl set image on the Deployment, then wait for the rolling update to finish. For demo-app, the rest of the manifests is applied by Argo CD (see GitOps with Argo CD).
  6. verify: a smoke test queries the public site.

On a merge request, only validate and build run, without publishing or deploying, and the merge request can only be merged if that pipeline passed (setting on in every project). With maxUnavailable: 0, a new pod must be ready before an old one is stopped: the site stays up during the update.

Jobs run in a Kubernetes runner. Its service account can only change the image of one named Deployment per namespace: CI has no cluster-admin rights. Pods pull their images with a read-only token.

git push origin main                                  # lance le pipeline / starts the pipeline
kubectl -n landing rollout status deploy/landing-web
kubectl -n landing rollout undo deploy/landing-web   # retour arrière / rollback

GitOps avec Argo CDGitOps with Argo CD

Avant Argo CD, les manifestes de chaque application étaient appliqués à la main avec kubectl apply, puis le pipeline ne changeait plus que l'image. Rien ne garantissait que le cluster restait identique au dépôt : une modification faite à la main y restait. Désormais, Argo CD fait de Git la seule source de vérité pour demo-app : ce qui est dans k8s/ sur main est ce qui tourne.

Le déploiement

Argo CD est installé avec Helm (chart argo/argo-cd 10.10.5, Argo CD v3.5.5) dans le namespace argocd, à partir d'un fichier de valeurs versionné dans le dépôt infra. Choix faits pour le lab :

  • Accès administrateur seulement sur argocd.hoortrend.com : aucun accès anonyme, pas de SSO (Dex désactivé), le compte local admin. Son mot de passe est tiré au hasard ; le cluster n'en garde que l'empreinte bcrypt.
  • TLS à l'entrée : ingress-nginx termine le HTTPS avec un certificat Let's Encrypt de cert-manager, et parle en HTTP à Argo CD dans le cluster (server.insecure). Limites de débit sur l'Ingress.
  • Version allégée : ni notifications, ni contrôleur ApplicationSet, un exemplaire de chaque composant, des limites de ressources, sur worker1 (master1 manque de mémoire). Environ 300 Mio en tout.
helm upgrade --install argocd argo/argo-cd --version 10.10.5 \
  -n argocd --create-namespace -f argocd/values.yaml    # dépôt infra
kubectl apply -f argocd/demo-app.yaml                  # projet + application
kubectl -n argocd get applications                     # SYNC STATUS Synced, HEALTH Healthy

Comment il fonctionne

  1. repo-server clone le dépôt public de demo-app et produit les manifestes du dossier k8s/ à la dernière révision de main.
  2. application-controller compare cet état voulu à l'état réel du cluster, objet par objet. Il le fait toutes les deux à trois minutes (120 s plus un décalage aléatoire de 60 s au plus), et aussitôt qu'un objet suivi change dans le cluster.
  3. S'il y a un écart, il synchronise : il applique les manifestes (automated sync). Un objet retiré du dépôt est supprimé du cluster (prune) ; un objet modifié à la main est remis comme dans le dépôt (self-heal).
  4. Il évalue enfin la santé de chaque objet : un Deployment est « Healthy » quand tous ses pods sont à jour et prêts.
  5. server sert l'interface et l'API (réservées à l'administrateur) ; redis sert de cache aux autres composants.

L'application est rangée dans un projet Argo CD (AppProject) qui limite ce qu'elle peut faire : seulement le dépôt de demo-app, seulement le namespace demo-app, et seulement les types d'objets qu'il contient (Namespace, Deployment, Service, PodDisruptionBudget, Ingress, NetworkPolicy). Un manifeste qui viserait un autre namespace ou créerait un rôle serait refusé.

Argo CD et le pipeline : qui fait quoi

ÉtapeQuiComment
Tests, build, analyse de l'imagePipeline GitLabKaniko, puis Trivy, qui bloque sur une vulnérabilité HIGH ou CRITICAL corrigeable.
Choix de l'image en productionPipeline GitLabkubectl set image avec le tag du commit, après l'analyse. Argo CD ignore ce seul champ (ignoreDifferences, RespectIgnoreDifferences) : les deux ne se battent pas.
Tout le reste des manifestesArgo CDRéplicas, variables, sondes, Service, Ingress, NetworkPolicy… appliqués depuis k8s/ sur main en deux à trois minutes ; tout écart fait à la main est annulé.

Vérifié : passer demo-app à 3 réplicas à la main a été annulé en 5 secondes (retour à 2). Un vrai pipeline a ensuite déployé la version 226f67fb : l'application est restée « Synced » et « Healthy », avec cette image.

Pas encore : les autres applications. Leurs dépôts sont privés (Argo CD aurait besoin d'un accès en lecture à GitLab), et le bac à sable du lab est modifié en direct par les visiteurs : il faudrait dire à Argo CD de laisser ces changements.

Before Argo CD, each application's manifests were applied by hand with kubectl apply, and the pipeline then only changed the image. Nothing guaranteed that the cluster stayed identical to the repository: a change made by hand stayed there. Now Argo CD makes Git the single source of truth for demo-app: what is in k8s/ on main is what runs.

The deployment

Argo CD is installed with Helm (chart argo/argo-cd 10.10.5, Argo CD v3.5.5) in the argocd namespace, from a values file versioned in the infra repository. Choices made for the lab:

  • Admin access only on argocd.hoortrend.com: no anonymous access, no SSO (Dex off), the local admin account. Its password is random; the cluster only keeps its bcrypt hash.
  • TLS at the edge: ingress-nginx terminates HTTPS with a Let's Encrypt certificate from cert-manager, and talks plain HTTP to Argo CD inside the cluster (server.insecure). Rate limits on the Ingress.
  • Pared down: no notifications, no ApplicationSet controller, one copy of each component, resource limits, on worker1 (master1 is short of memory). About 300 MiB in all.
helm upgrade --install argocd argo/argo-cd --version 10.10.5 \
  -n argocd --create-namespace -f argocd/values.yaml    # infra repository
kubectl apply -f argocd/demo-app.yaml                  # project + application
kubectl -n argocd get applications                     # SYNC STATUS Synced, HEALTH Healthy

How it works

  1. repo-server clones demo-app's public repository and renders the manifests of the k8s/ folder at the latest revision of main.
  2. application-controller compares this desired state with the live state of the cluster, object by object. It does so every two to three minutes (120 s plus a random delay of up to 60 s), and as soon as a tracked object changes in the cluster.
  3. If they differ, it syncs: it applies the manifests (automated sync). An object removed from the repository is deleted from the cluster (prune); an object changed by hand is put back as in the repository (self-heal).
  4. Finally it assesses the health of each object: a Deployment is "Healthy" when all its pods are up to date and ready.
  5. server serves the UI and the API (admin only); redis is the other components' cache.

The application belongs to an Argo CD project (AppProject) that limits what it may do: only demo-app's repository, only the demo-app namespace, and only the kinds of objects it contains (Namespace, Deployment, Service, PodDisruptionBudget, Ingress, NetworkPolicy). A manifest aimed at another namespace, or creating a role, would be refused.

Argo CD and the pipeline: who does what

StepWhoHow
Tests, build, image scanGitLab pipelineKaniko, then Trivy, which blocks on a fixable HIGH or CRITICAL vulnerability.
Which image runs in productionGitLab pipelinekubectl set image with the commit tag, after the scan. Argo CD ignores that one field (ignoreDifferences, RespectIgnoreDifferences): the two never fight.
The rest of the manifestsArgo CDReplicas, variables, probes, Service, Ingress, NetworkPolicy… applied from k8s/ on main within two to three minutes; any change made by hand is reverted.

Checked: scaling demo-app to 3 replicas by hand was reverted within 5 seconds (back to 2). A real pipeline then deployed version 226f67fb: the application stayed "Synced" and "Healthy", with that image.

Not yet: the other applications. Their repositories are private (Argo CD would need read access to GitLab), and the lab's sandbox is changed live by visitors: Argo CD would have to be told to leave those changes alone.

Fichiers de configurationConfiguration files

Ces fichiers sont copiés tels quels dans l'image au moment du build : ce sont exactement ceux qui font tourner la page d'accueil.

These files are copied as-is into the image at build time: they are exactly what runs the home page.


    
  • cert-manager.io/cluster-issuer demande le certificat Let's Encrypt, émis et renouvelé automatiquement.
  • cert-manager.io/cluster-issuer requests the Let's Encrypt certificate, issued and renewed automatically.
  • ssl-redirect renvoie tout le trafic HTTP vers HTTPS.
  • ssl-redirect sends all HTTP traffic to HTTPS.
  • limit-rps limite le débit par client pour protéger la page.
  • limit-rps rate-limits each client to protect the page.
  • La règle envoie hoortrend.com vers le Service, sans exposer les pods.
  • The rule routes hoortrend.com to the Service without exposing the pods.

Workflow de chaque projetEach project's workflow

Tous les projets suivent le même chemin, du poste du développeur jusqu'au site public. Choisis un projet, puis rejoue son pipeline : les étapes, les jobs et les images sont les vrais, et le .gitlab-ci.yml exact est affiché dessous.

Every project follows the same path, from the developer's machine to the public site. Pick a project, then replay its pipeline: the stages, jobs and images are the real ones, and the exact .gitlab-ci.yml is shown below.

  1. Branche. Le changement est fait sur une branche, jamais directement sur main.
  2. Branch. The change is made on a branch, never directly on main.
  3. Merge request. Le pipeline valide et construit l'image sans la publier : on sait que ça passe avant de fusionner.
  4. Merge request. The pipeline validates and builds the image without publishing it: we know it passes before merging.
  5. Fusion dans main. Le pipeline complet démarre : validation, construction, publication, déploiement.
  6. Merge into main. The full pipeline starts: validate, build, publish, deploy.
  7. Déploiement progressif. Kubernetes remplace les pods un par un, et ne coupe un ancien pod qu'une fois le nouveau prêt.
  8. Rolling update. Kubernetes replaces pods one by one, and only stops an old pod once the new one is ready.
  9. Vérification. Un test de fumée interroge le site public. En cas de problème : rollout undo, retour à la version précédente en quelques secondes.
  10. Verification. A smoke test queries the public site. If something is wrong: rollout undo, back to the previous version in seconds.


  

Le code affiché est une copie du fichier du dépôt, mise à jour avec cette page. Les durées sont un ordre de grandeur mesuré (démarrage du pod du job compris).

The code shown is a copy of the repository file, updated along with this page. Durations are a measured order of magnitude (job pod start-up included).