# Infrastructure de la plateforme data

> Mise en place de l'infrastructure de la plateforme data

- URL : https://blog.xreveillon.eu/fr-FR/blog/data-platform-infra
- Date : 2025-08-24T08:25:18.000Z
- Langue : fr-FR

---

Pour ce premier article dans la série sur la plateforme data, on va d'abord installer quelques outils, démarrer le cluster Kubernetes, puis lancer la partie infrastructure de la plateforme.

## Prérequis

Je le rappelle, cette plateforme de donnée fonctionne mieux sous un OS de type Linux. Tous les outils peuvent fonctionner sous Windows (avec WSL) ou sur Mac, mais cela nécessite quelques configurations supplémentaires pour pouvoir accéder aux différentes interfaces web et API.

De plus [Docker doit être installé](https://docs.docker.com/engine/install/), ainsi que la commande `jq` (avec `sudo apt install jq` ou `sudo dnf install jq`).

## Présentation et installation des outils

Tout est indiqué dans le [répertoire git](https://github.com/xreveillon/data-platform-demo/tree/main/01-infra) dont on va récupérer les sources, je vais ajouter ici quelques détails supplémentaires.

### Kind

[Kind](https://kind.sigs.k8s.io/) (initialement "Kubernetes IN Docker") est un logiciel pour déployer un cluster Kubernetes sur base de conteneurs. Chaque nœud est représenté par un conteneur, avec un réseau virtuel dédié au cluster. Kind était d'abord développé pour tester les développements du projet [Kubernetes](https://github.com/kubernetes/) lui-même, mais peut maintenant servir à tester ses propres applications dans un environnement Kubernetes local.

Pour l'installer, il y a deux façons. A la mode de go, il suffit de lancer la commande suivante :

```bash
go install sigs.k8s.io/kind@latest
```

Ou bien vous pouvez télécharger le binaire et le placer dans un dossier compris dans le `$PATH`.
```bash
ARCH=$(uname -m | sed 's/x86_64/amd64/;s/aarch64/arm64/')
curl -Lo ./kind https://kind.sigs.k8s.io/dl/latest/kind-linux-${ARCH}
chmod +x ./kind
mv ./kind $HOME/.local/bin/kind
```

Le cluster de démo est composé d'un nœud pour le controle-plane et de deux nœuds worker, dont l'un sera utilisé pour recevoir les connexions externes, grâce au load-balancer de MetalLB.

```yaml
kind: Cluster
apiVersion: kind.x-k8s.io/v1alpha4
name: data-platform
networking:
  ipFamily: ipv4
nodes:
  - role: control-plane
    image: kindest/node:v1.33.1
  - role: worker
    image: kindest/node:v1.33.1
    labels:
      net: metallb-gateway
  - role: worker
    image: kindest/node:v1.33.1
```

### Kubectl

[Kubectl](https://kubernetes.io/docs/tasks/tools/#kubectl) est l'outil de ligne de commande utilisé pour interagir avec les clusters Kubernetes. Il permet de déployer des applications, de gérer les ressources du cluster et d'obtenir des informations sur l'état des ressources.

Pour l'installer, il faut télécharger le binaire. Des [repositories](https://kubernetes.io/docs/tasks/tools/install-kubectl-linux/#install-using-native-package-management) apt et rpm existent aussi.

```bash
ARCH=$(uname -m | sed 's/x86_64/amd64/;s/aarch64/arm64/')
VERSION=$(curl -L -s https://dl.k8s.io/release/stable.txt)
curl -LO "https://dl.k8s.io/release/${VERSION}/bin/linux/${ARCH}/kubectl"
chmod +x ./kubectl
mv ./kubectl $HOME/.local/bin/kubectl
```

### Helm

Déployer des applications dans un cluster Kubernetes peut être laborieux. Il faut définir les différents composants nécessaires (pods, services, pv ...), le type (Deployments/DaemonSets, ClusterIP/LoadBalancer, ...), les accès réseaux (Ingress, HTTPRoute, ...). C'est là qu'intervient [Helm](https://helm.sh/), un gestionnaire de packages pour Kubernetes qui simplifie le déploiement et la gestion des applications. Les développeurs de l'application, ou la communauté, peuvent créer des "charts" Helm, qui sont l'équivalent des paquets APT/RPM pour Kubernetes. A ceci près que toute la configuration des applications Helm peut (doit) être définie avant l'installation, via les "values".

Un repository Helm est mis à disposition, chaque repository peut contenir des charts de plusieurs applications. Lorsqu'on installe une application dans le cluster, cela devient une "release".

Helm stocke la configuration des applications dans le cluster Kubernetes lui-même, en utilisant des objets Kubernetes tels que ConfigMaps et Secrets.

Pour l'installer, [plusieurs méthodes sont disponibles](https://helm.sh/docs/intro/install/). J'en reprends ici deux :

```bash
# Pour les développeurs golang
go install helm.sh/helm/v3/cmd/helm@latest

# Ou télécharger un binaire compilé
ARCH=$(uname -m | sed 's/x86_64/amd64/;s/aarch64/arm64/')
VERSION=$(curl -s https://api.github.com/repos/helm/helm/releases/latest | jq -r '.tag_name')
curl -L "https://get.helm.sh/helm-${VERSION}-linux-${ARCH}.tar.gz" | tar -xz --strip-components=1 linux-${ARCH}/helm
mv ./helm $HOME/.local/bin/helm
```

### Helmfile

Une plateforme est généralement composée de plusieurs applications, et donc de plusieurs release Helm. Pour simplifier et centraliser les values et configurations, on peut utiliser [Helmfile](https://github.com/roboll/helmfile). Cet outil permet de définir l'ensemble des releases Helm dans un fichier YAML, et de les gérer de manière cohérente.

On a le choix entre un fichier helmfile.yaml par plateforme, ou de découper avec un fichier par application. En allant dans les détails de la configuration helmfile, on pourrait même gérer plusieurs environnements différents. Tout comme Helm, on peut utiliser des templates Golang, ce qui démultiplie encore les possibilités.

Pour l'installer, encore et toujours la [page de documentation](https://helmfile.readthedocs.io/en/latest/#installation), et deux possibilités reprises ci-dessous :

```bash
# Pour les développeurs golang
go install github.com/helmfile/helmfile@latest

# Ou télécharger un binaire
ARCH=$(uname -m | sed 's/x86_64/amd64/;s/aarch64/arm64/')
VERSION=$(curl -s https://api.github.com/repos/helmfile/helmfile/releases/latest | jq -r '.tag_name')
VERSION=${VERSION#v}
curl -L "https://github.com/helmfile/helmfile/releases/download/v${VERSION}/helmfile_${VERSION}_linux_${ARCH}.tar.gz" | tar -xz -C $HOME/.local/bin helmfile
```

> **Tip:** 👇 Après l'installation, il faut télécharger tous les plugins helm dont helmfile a besoin.

```bash
helmfile init
```


## Installation de la plateforme

En premier lieu, il faut cloner le [repository git](https://github.com/xreveillon/data-platform-demo) et se placer à l'intérieur du dossier `01-infra`.

```bash
git clone https://github.com/xreveillon/data-platform-demo.git
cd data-platform-demo/01-infra
```

### Démarrage du cluster Kind

**Démarrons le cluster Kind:**

```bash
kind create cluster --config kind-config.yaml
```

On peut vérifier que le cluster est bien démarré avec la commande `kubectl get nodes`.

### Personnalisation de la plateforme

Pendant que le cluster démarre, on va personnaliser un peu la plateforme. Dans le fichier `.env`, je propose de définir quelques paramètres via des variables d'environnement.

Le plus important est le nom de domaine principal, qui sera utilisé pour accéder aux différentes applications. Chaque application, avec une interface web ou avec un certificat SSL, aura son propre sous-domaine, par exemple `minio.GLOBAL_DOMAIN` pour Minio, ou `airbyte.GLOBAL_DOMAIN` pour Airbyte.

**Fichier .env:**

```ini
# Domaine global, sous lequel chaque application prendra un sous-domaine
# Exemple, minio serait accessible sous minio.GLOBAL_DOMAIN,
# soit minio.data-platform.xreveillon.eu
GLOBAL_DOMAIN=data-platform.xreveillon.eu

MINIO_ACCESS_KEY=adminuser
MINIO_SECRET_KEY=adminpassword

AIRBYTE_S3_ACCESS_KEY=airbyte
AIRBYTE_S3_SECRET_KEY=airbytepassword

REGISTRY_USER=registry
REGISTRY_PASSWORD=registrypassword
REGISTRY_S3_ACCESS_KEY=registry
REGISTRY_S3_SECRET_KEY=registrypassword
```

Une fois modifiées, chargeons la configuration en mémoire.

```bash
set -a ; . .env ; set +a
```

### Récupération d'informations réseau

Maintenant que le cluster est démarré, on va récupérer quelques informations réseau, qui serviront à configurer le réseau pour MetalLB (cf. plus bas le paragraphe sur MetalLB).

```bash
# Extraction du CIDR du réseau IPv4 du cluster kind
export KIND_IPV4_CIDR=$(docker network inspect kind | jq -r '.[0].IPAM.Config[] | select(.Subnet | test(":") | not) | .Subnet')

# Calcul de la plage d'adresses IP pour MetalLB
export METALLB_IP_RANGE=$(./last10range.sh $KIND_IPV4_CIDR)
```

### Installation de l'infrastructure

On commence par installer les ressources de la [Gateway API](https://gateway-api.sigs.k8s.io/), et enfin on installe toute l'infra en une seule commande.

```bash
# Installer les CRD de la Gateway API depuis la branche expérimentale.
kubectl apply -f https://github.com/kubernetes-sigs/gateway-api/releases/download/v1.3.0/experimental-install.yaml

# Installer les applications
helmfile apply
```

Cela va durer plusieurs minutes, le temps de récupérer les images Docker et démarrer les applications. Si un timeout survient, il suffit de relancer la commande `helmfile apply`.

En attendant, lisons le contenu de ce que l'on déploie 👇

## Contenu de l'infrastructure

### MetalLB

<details>
<summary><code>helmfile.yaml</code></summary>

```yaml
releases:
  - name: metallb
    namespace: metallb-system
    createNamespace: true
    chart: metallb/metallb
    wait: true
  - name: kubedeploy-metallb
    namespace: metallb-system
    chart: sysbee/kubedeploy
    disableValidationOnInstall: true
    needs:
      - metallb-system/metallb
    values:
      - values/metallb-extraobjects.yaml.gotmpl
```

</details>

Comme son nom l'indique, [MetalLB](https://metallb.universe.tf/) est un load-balancer pour Kubernetes. Il existe [4 types de services](https://kubernetes.io/docs/concepts/services-networking/service/#publishing-services-service-types) pour gérer le réseau dans Kubernetes. Le service de type LoadBalancer est le seul à permettre des connections réseau depuis l'extérieur du cluster vers un port "habituel" (80, 443, 5432, ...), au moyen d'un load-balancer situé à l'extérieur. Dans un environnement cloud, le LoadBalancer  externe est automatiquement approvisionné, configuré et géré par le fournisseur cloud (AWS ELB, GCP LB, Azure LB, ...). Dans un environnement on-premise ou local, il n'y a pas de load-balancer natif. MetalLB comble cette lacune en fournissant une implémentation logicielle de load-balancer, par exemple au moyen de l'allocation d'une adresse IP virtuelle sur un des noeuds.

<figure>
  <a href="/images/blog/data-platform-infra/MetalLB.drawio.svg"><img src="/images/blog/data-platform-infra/MetalLB.drawio.svg" alt="Schéma de fonctionnement de MetalLB" /></a>
  <figcaption>Les adresses IP présentées sont propres à ma configuration. J'ai choisi le mode <a href="https://metallb.io/concepts/layer2/">Layer 2.</a></figcaption>
</figure>

### Cert-manager

<details>
<summary><code>helmfile.yaml</code></summary>

```yaml
  - name: cert-manager
    namespace: cert-manager
    createNamespace: true
    chart: jetstack/cert-manager
    wait: true
    disableValidationOnInstall: true
    values:
      - crds:
          enabled: true
        prometheus:
          enabled: false
        config:
          apiVersion: controller.config.cert-manager.io/v1alpha1
          kind: ControllerConfiguration
          enableGatewayAPI: true
  - name: cert-manager-extra-objects
    namespace: cert-manager
    chart: sysbee/kubedeploy
    disableValidationOnInstall: true
    wait: true
    needs:
      - cert-manager/cert-manager
    values:
      - values/cert-manager-extraObjects.yaml.gotmpl
```

</details>

[Cert-manager](https://cert-manager.io/) est un gestionnaire de certificats SSL pour Kubernetes. Il automatise la gestion et le renouvellement des certificats TLS, en s'intégrant avec des autorités de certification comme Let's Encrypt ou même privées. Cert-manager surveille les ressources de certificat dans le cluster et s'assure que les certificats sont valides et à jour.

Quand je ferai évoluer la plateforme vers un mode "production-ready", Cert-manager sera très utile pour chiffrer toutes les communications internes du cluster. Là maintenant, Cert-Manager est utilisé pour sécuriser votre accès aux interfaces web.

> **Note:** Expliquer le pourquoi et le comment d'une PKI (public Key Infrastucture) fait partie de mes futurs articles de blog.

> **Tip:** Si vous en avez marre de voir des avertissements de certificats dans votre navigateur en visitant les pages de la plateforme, vous pouvez installer le certificat racine de la PKI dans vos autorités de confiance. Mais je le DECONSEILLE FORTEMENT.


### Trust-manager

<details>
<summary><code>helmfile.yaml</code></summary>

```yaml
  - name: trust-manager
    namespace: cert-manager
    chart: jetstack/trust-manager
    disableValidationOnInstall: true
    wait: true
    needs:
      - cert-manager/cert-manager-extra-objects
    values:
      - crds:
          enabled: true
        secretTargets:
          enabled: false
  - name: trust-manager-extra-objects
    namespace: cert-manager
    chart: sysbee/kubedeploy
    disableValidationOnInstall: true
    wait: true
    needs:
      - cert-manager/trust-manager
    values:
      - values/trust-manager-extraObjects.yaml.gotmpl
```

</details>

Lorsque Cert-manager gère lui-même la PKI, [Trust-manager](https://cert-manager.io/docs/trust/trust-manager/) est très pratique pour distribuer le certificat public, pour que les différentes applications puissent faire confiance aux certificats émis par Cert-manager.

### Traefik

<details>
<summary><code>helmfile.yaml</code></summary>

```yaml
  - name: traefik
    namespace: traefik
    createNamespace: true
    chart: traefik/traefik
    disableValidationOnInstall: true
    values:
      - values/traefik-values.yaml.gotmpl
    needs:
      - cert-manager/cert-manager
```

</details>

Quand des applications web sont hébergées à l'intérieur d'un cluster Kubernetes, il faut pouvoir y accéder depuis l'extérieur. On l'a déjà vu avec MetalLb, des services de type LoadBalancer le permettent, quel que soit le protocole d'échange utilisé. Mais cela donne un accès direct aux applications, sans gestion des noms de domaines, ni des certificats SSL, ni de politiques de sécurité.

Pour le protocole HTTP(S), la meilleure méthode pour le moment est d'utiliser les [Ingress](https://kubernetes.io/docs/concepts/services-networking/ingress/) avec un [Ingress Controller](https://kubernetes.io/docs/concepts/services-networking/ingress-controllers/) (pour simplifier, c'est le reverse-proxy de Kubernetes). Dans ma plateforme, j'utilise [Traefik](https://traefik.io/traefik) comme Ingress Controller.

Traefik est aussi compatible avec la Gateway API, qui complètera les Ingress pour les applications nécessitant des configurations plus complexes.

### Minio

<details>
<summary><code>helmfile.yaml</code></summary>

```yaml
  - name: minio
    namespace: minio
    createNamespace: true
    chart: minio/minio
    disableValidationOnInstall: true
    wait: true
    needs:
      - minio/minio-extra-objects-pre
    values:
      - values/minio-values.yaml.gotmpl
```

</details>

[Minio](https://min.io/) est une solution de stockage objet compatible avec l'API S3 d'Amazon. Elle est conçue pour être hautement disponible, évolutive et performante, et peut être déployée sur site ou dans le cloud. Minio est souvent utilisé pour stocker des données non structurées telles que des images, des vidéos, des sauvegardes et des fichiers journaux.

Le stockage objet est devenu prédominant dans le monde de la data, avec des solutions comme les Lakehouse.

Les lakehouse reposent sur le principe de la séparation du stockage et du calcul. Le stockage est assuré par un système de fichiers distribué ou un stockage objet, accessibles par plusieurs clients et query engines de manière concurrentielle.

### Registre d'images Docker

<details>
<summary><code>helmfile.yaml</code></summary>

```yaml
  - name: registry
    namespace: registry
    createNamespace: true
    chart: twuni/docker-registry
    needs:
      - minio/minio
    values:
      - values/docker-registry-values.yaml.gotmpl
```

</details>

Parmi les outils installés, il y a au moins Dagster qui nécessitera de créer ses propres images Docker. Et pour inclure le certificat public de l'autorité racine de la PKI privée, il faudra peut-être aussi procéder à la création d'images.


### Instance PostgreSQL avec des données source

<details>
<summary><code>helmfile.yaml</code></summary>

```yaml
  - name: pg-source
    namespace: postgresql
    createNamespace: true
    chart: bitnami/postgresql
    needs:
      - traefik/traefik
    values:
      - values/pg-source-values.yaml.gotmpl
```

</details>

Il s'agit de l'instance PostgreSQL qui fournira certaines données sources à [Airbyte](https://airbyte.com/), [Dagster](https://dagster.io/) ou [Olake](https://olake.io/).

<details>
<summary>Structure de la database source</summary>

<figure>
  <a href="/images/blog/data-platform-infra/source-database---chargy.svg"><img src="/images/blog/data-platform-infra/source-database---chargy.svg" alt="Structure de la base de données source" /></a>
  <figcaption>La base de données source contient 5 tables, dont 3 pouvant servir de dimension, et 2 de fait, nécessitant quelques transformations. Credits à  <a href="https://drawdb.app/">drawdb.app et lilith pour le logiciel de diagramme ER</a></figcaption>
</figure>

</details>

## Finalisation


Vérifions déjà que tout est démarré correctement.

**Commande de vérification:**

```shell
kubectl get daemonsets.apps,deployments.apps -A

## Résultat attendu
NAMESPACE        NAME                             DESIRED   CURRENT   READY   UP-TO-DATE   AVAILABLE   NODE SELECTOR            AGE
kube-system      daemonset.apps/kindnet           3         3         3       3            3           kubernetes.io/os=linux   6h49m
kube-system      daemonset.apps/kube-proxy        3         3         3       3            3           kubernetes.io/os=linux   6h49m
metallb-system   daemonset.apps/metallb-speaker   3         3         3       3            3           kubernetes.io/os=linux   6h46m
traefik          daemonset.apps/traefik           2         2         2       2            2           <none>                   6h45m

NAMESPACE            NAME                                       READY   UP-TO-DATE   AVAILABLE   AGE
cert-manager         deployment.apps/cert-manager               1/1     1            1           6h46m
cert-manager         deployment.apps/cert-manager-cainjector    1/1     1            1           6h46m
cert-manager         deployment.apps/cert-manager-webhook       1/1     1            1           6h46m
cert-manager         deployment.apps/trust-manager              1/1     1            1           6h45m
kube-system          deployment.apps/coredns                    2/2     2            2           6h49m
local-path-storage   deployment.apps/local-path-provisioner     1/1     1            1           6h49m
metallb-system       deployment.apps/metallb-controller         1/1     1            1           6h46m
minio                deployment.apps/minio                      1/1     1            1           6h45m
registry             deployment.apps/registry-docker-registry   1/1     1            1           6h44m
```

Ensuite, si l'on veut pouvoir accéder à nos services via des noms de domaine plutôt que par adresse IP, il faut ajouter une entrée dans le fichier `/etc/hosts` de votre machine, avec l'adresse IP du nœud du LoadBalancer créé par MetalLB.

**Obtenir l'adresse IP du LoadBalancer, colonne EXTERNAL-IP:**

```shell
# Pour Traefik et toutes les interfaces web
kubectl get services -n traefik -o wide

# Pour PostgreSQL
kubectl get services -n postgresql -o wide pg-source-postgresql
```

Il faut relever l'adresse IP. En théorie, elle restera toujours la même, on peut donc la mettre dans le fichier `/etc/hosts` de la machine hôte.

Dans la ligne suivante, remplacez `data-platform.xreveillon.eu` par la valeur `GLOBAL_DOMAIN` que vous avez mise dans le fichier `.env`.

**Ligne à ajouter dans `/etc/hosts`:**

```ini
<EXTERNAL-IP> traefik.data-platform.xreveillon.eu airbyte.data-platform.xreveillon.eu minio-console.data-platform.xreveillon.eu s3.data-platform.xreveillon.eu dagster.data-platform.xreveillon.eu registry.data-platform.xreveillon.eu pg-source.data-platform.xreveillon.eu
```

Une fois que c'est fait, on peut vérifier que tout fonctionne en navigant vers différentes URL pour voir les différentes interfaces web.

<details>
<summary>https://traefik.data-platform.xreveillon.eu/dashboard/</summary>

<figure>
  <a href="/images/blog/data-platform-infra/traefik-dashboard.png"><img src="/images/blog/data-platform-infra/traefik-dashboard.png" alt="Dashboard de Traefik" /></a>
  <figcaption>Le dashboard de Traefik permet de voir les routes configurées, les services, les middlewares, les certificats TLS, et plus encore.</figcaption>
</figure>

</details>


<details>
<summary>https://minio-console.data-platform.xreveillon.eu/</summary>

<figure>
  <a href="/images/blog/data-platform-infra/minio-console.png"><img src="/images/blog/data-platform-infra/minio-console.png" alt="Console de Minio" /></a>
  <figcaption>La console de Minio ne permet plus que de parcourir le contenu des buckets. Auparavant on pouvait gérer aussi la configuration et la sécurité.</figcaption>
</figure>

</details>
