Connecteur de bases de données (optionnel)
Le connecteur de bases de données est optionnel et désactivé par défaut. Avatar fonctionne entièrement sans lui : les jeux de données envoyés sous forme de fichiers (CSV, Parquet) sont anonymisés comme d'habitude. Activez-le uniquement si vos utilisateurs ont besoin des exécutions sur base de données.
Les exécutions sur base de données lisent une base de données et écrivent la copie anonymisée dans une autre base. Les workers qui exécutent les jobs n'ouvrent pas eux-mêmes ces bases. Un service séparé, le connecteur de bases de données, contient tous les pilotes de bases de données et ouvre les bases. L'API conserve les chaînes de connexion ; le connecteur n'en reçoit une que le temps d'un job ; les workers n'en voient jamais aucune.
Cette page explique comment activer le connecteur sur une installation Kubernetes. Le connecteur est désactivé par défaut. Sans lui, les exécutions sur base de données n'atteignent aucune base, PostgreSQL compris, et les bases de démonstration ne sont pas proposées.
L'outil de déploiement pour Docker Compose n'installe pas encore le connecteur. Pour utiliser les exécutions sur base de données sur une installation Docker Compose, contactez le support Octopize.
Fonctionnement
worker ── short-lived token ──▶ connector ──▶ your database
│
└── "what does this name mean?" ──▶ API (internal port)
- Un job porte le nom d'une connexion et un jeton signé par l'API. Le jeton est valable pour une connexion, pour un utilisateur, et pour une durée limitée.
- Le connecteur vérifie le jeton, puis demande à l'API la chaîne de connexion sur un port
interne. Ce port est servi par son propre Service (
avatar-api-internal), auquel aucun Ingress ne fait référence, et une network policy le réserve au connecteur. Le port public de l'API ne sert pas cette route. - Le connecteur garde la chaîne de connexion en mémoire pendant 30 secondes. Il n'écrit rien sur le disque.
- Les lignes vont de votre base de données vers le stockage objet du job. Elles ne passent pas par l'API.
- Le connecteur n'a pas de point d'accès de requête. Il liste la structure d'une base de données, lit des tables, écrit dans des tables qui existent déjà dans une cible, et supprime les lignes des tables qu'une exécution nomme. Il ne crée pas de tables : la cible doit déjà posséder le schéma. Les messages d'erreur donnent une catégorie (par exemple la base de données a refusé les identifiants), jamais un hôte ni un identifiant.
Moteurs pris en charge
| Moteur | Chaîne de connexion | Statut |
|---|---|---|
| PostgreSQL | postgresql://user:password@host:5432/database | pris en charge |
| MySQL | mysql://user:password@host:3306/database | pris en charge |
| MariaDB | mariadb://user:password@host:3306/database | pris en charge |
| Oracle | oracle://user:password@host:1521/SERVICE_NAME | pris en charge (pilote thin, sans client Oracle) |
| SQL Server | mssql://user:password@host:1433/database | pris en charge |
| ClickHouse | clickhouse://user:password@host:8123/database | pris en charge (ajoutez ?secure=true pour 8443) |
| MongoDB | mongodb://… ou mongodb+srv://… | pris en charge (collections lues comme des tables) |
| Snowflake | snowflake://user:password@account/database/schema?warehouse=…&role=… | aperçu |
| Google BigQuery | bigquery://project/dataset?credentials_base64=… | aperçu, lecture seule (source uniquement) |
| IBM Db2 | db2://user:password@host:50000/database | aperçu, absent de l'image publiée |
Un moteur en aperçu est proposé, mais il n'a pas encore été validé sur un vrai serveur. L'application web le marque comme aperçu. Le connecteur indique à l'API les moteurs dont il embarque le pilote, et l'API ne propose que ceux-là.
IBM Db2 a besoin d'un pilote qui ajoute 135 Mo et ne fonctionne que sur amd64 : l'image publiée
ne l'inclut donc pas. Pour proposer Db2, construisez vous-même l'image depuis les sources du
connecteur avec --build-arg WITH_DB2=1 --platform linux/amd64, ou contactez le
support Octopize.
L'image contient aussi deux petites bases de démonstration (Northwind et Chinook). Vos utilisateurs peuvent les ajouter depuis Compte > Connexions aux bases de données et essayer une exécution sur base de données avant de connecter leurs propres bases. Une copie d'une cible de démonstration est supprimée 24 heures après sa dernière écriture, et quand le connecteur redémarre.
Avant de commencer
- Version 0.0.100 du chart ou ultérieure. La version 0.0.99 a ajouté le connecteur, et la 0.0.100 a corrigé son secret de pull d'image.
- Version 3.18.0 de l'API ou ultérieure. C'est la première version avec le port interne auquel le connecteur demande les chaînes de connexion.
- Network policies. Le chart refuse de générer le connecteur tant que
networkPolicy.enabledne vaut pastrue, car seules les network policies empêchent le connecteur d'atteindre le reste du cluster. Votre cluster a besoin d'un CNI qui applique les NetworkPolicy, comme Calico ou Cilium. - Accès à l'image. L'image
quay.io/octopize/avatar-connectorest privée. Le chart la tire avec les mêmes identifiants de pull d'images que les autres images Avatar.
Activer le connecteur
1. Générer les secrets
Le connecteur a besoin de deux secrets :
connector.secret: partagé par l'API et le connecteur. Il signe les jetons que portent les jobs, et le connecteur s'authentifie auprès de l'API avec lui.databaseConnections.encryptionKeys: la clé Fernet qui chiffre les connexions que vos utilisateurs et vos organisations enregistrent. Sans elle, les utilisateurs ne peuvent pas enregistrer leurs propres connexions (l'API répond 503), mais les connexions de déploiement fonctionnent toujours.
scripts/generate-secrets.sh crée les deux, avec les autres secrets du chart. Voir
Générer les secrets. Pour les fabriquer vous-même :
# connector.secret
python -c "import secrets; print(secrets.token_hex(), end='')"
# databaseConnections.encryptionKeys
python -c "from cryptography.fernet import Fernet; print(Fernet.generate_key().decode(), end='')"
Utiliser un gestionnaire de clés au lieu d'une clé Fernet
L'API peut chiffrer les connexions enregistrées avec une clé dans un gestionnaire de clés
compatible KMS (AWS KMS, ou un service compatible comme Scaleway Key Manager) au lieu d'une clé
Fernet. L'API ne détient jamais cette clé : chaque connexion reçoit sa propre clé de données, que
seul le gestionnaire de clés peut déchiffrer, et chaque déchiffrement est soumis à la politique et
à la piste d'audit de la clé. Le chart n'a pas encore de valeurs pour cela ; réglez les variables
d'environnement de l'API via api.extraEnvVars :
| Variable | Valeur |
|---|---|
CONNECTION_CIPHER | aws_kms. Choisie automatiquement quand CONNECTION_KMS_KEY_ID est définie |
CONNECTION_KMS_KEY_ID | L'ID, l'ARN ou l'alias de la clé |
CONNECTION_KMS_REGION | La région de la clé |
CONNECTION_KMS_ENDPOINT_URL | Le point d'accès d'un service compatible KMS. Laissez-la non définie pour AWS |
CONNECTION_KMS_ACCESS_KEY_ID, CONNECTION_KMS_SECRET_ACCESS_KEY | Facultatives. Sans elles, l'API utilise la chaîne d'identifiants AWS standard |
Les connexions déjà enregistrées avec une clé Fernet restent lisibles tant que cette clé reste définie : vous pouvez donc passer de l'une à l'autre.
Fournir vos propres Secrets
Si vous fournissez vos propres Secrets Kubernetes, utilisez connector.existingSecret (clé
connector-secret) et databaseConnections.existingSecret (clés database-connections et
connection-encryption-keys). Changez les noms des clés avec connector.secretKeys.* et
databaseConnections.secretKeys.*.
2. Activer le connecteur
Ajoutez ceci à votre fichier de valeurs. Fixez le tag de l'image : le chart refuse de générer
quand connector.image.tag et connector.image.digest sont tous deux vides.
networkPolicy:
enabled: true
connector:
enabled: true
image:
tag: "0.4.0"
Puis mettez à jour la release :
helm upgrade avatar <chart> --values my-values.yaml --values avatar-secrets.yaml
Le chart lance alors le connecteur, ouvre le port interne de l'API et ajoute les network policies entre les deux.
3. Permettre au connecteur d'atteindre vos bases de données
Le connecteur a sa propre network policy. Par défaut, il peut atteindre :
- le DNS ;
- le port interne de l'API ;
- le SeaweedFS intégré au chart, quand il est activé ;
- l'internet public, sauf les plages privées, partagées, link-local et loopback
(
10.0.0.0/8,172.16.0.0/12,192.168.0.0/16,100.64.0.0/10,169.254.0.0/16,127.0.0.0/8).
Une base de données à une adresse publique fonctionne sans aucun changement. Une base de données
sur un réseau privé (derrière un VPN ou un peering) a besoin que son réseau figure dans
allowedDatabaseCIDRs :
connector:
networkPolicy:
allowedDatabaseCIDRs:
- 10.20.0.0/16
Un stockage objet externe à une adresse privée doit aussi y figurer, car le connecteur écrit les tables qu'il lit dans le stockage objet du job.
Si vos pods DNS ou vos pods de stockage objet ne portent pas les labels par défaut, réglez
connector.networkPolicy.dnsSelector (par défaut k8s-app: kube-dns) et
connector.networkPolicy.storageSelector (par défaut app.kubernetes.io/name: seaweedfs,
app.kubernetes.io/component: s3).
4. Vérifier que cela fonctionne
Connectez-vous à l'application web avec n'importe quel utilisateur, allez dans Compte > Connexions aux bases de données, cliquez sur Ajouter les connexions de démonstration, puis sur Tester à côté de la source de démonstration. Si le test indique que la base de données est joignable, l'API, le connecteur et les network policies fonctionnent ensemble. Créez ensuite une connexion de test vers l'une de vos propres bases de données.
Déclarer des connexions pour tous les utilisateurs (facultatif)
Les utilisateurs peuvent enregistrer leurs propres connexions depuis l'application web ou le client Python. Vous pouvez aussi déclarer des connexions pour tout le déploiement. Chaque utilisateur les voit, et personne ne peut les modifier depuis l'application :
databaseConnections:
connections:
- name: crm-prod
label: CRM (production)
dsn: "postgresql://avatar_reader:<password>@crm.internal:5432/crm"
- name: crm-test
label: CRM (test)
dsn: "postgresql://avatar_writer:<password>@crm-test.internal:5432/crm"
Chaque entrée a un name, un dsn au format de Moteurs pris en charge, et
un label facultatif, qui est ce que les utilisateurs voient dans les sélecteurs.
Ces valeurs contiennent des mots de passe. Conservez-les dans un Secret Kubernetes
(databaseConnections.existingSecret, clé database-connections, sous forme de liste JSON)
plutôt que dans un fichier de valeurs.
Les workers lisent les connexions de déploiement dans le Secret api, clé
database_connections. Un Secret nommé dans databaseConnections.existingSecret ne les atteint
pas à lui seul : faites aussi pointer l'entrée DATABASE_CONNECTIONS de dask.worker.env vers
lui.
Donnez à la source un rôle qui peut seulement lire. Donnez à la cible un rôle qui peut écrire et supprimer des lignes. La cible doit déjà posséder le schéma : l'exécution ne crée pas de tables.
Autres paramètres
| Valeur | Défaut | Description |
|---|---|---|
connector.replicaCount | 1 | Nombre de pods du connecteur. Le connecteur est sans état : vous pouvez le mettre à l'échelle librement. |
connector.tokenTtlSeconds | 21600 | Durée de validité du jeton d'un job. Choisissez une valeur plus longue que votre job le plus long. |
connector.storageEndpoints | [] | Adresses host:port vers lesquelles le connecteur peut écrire les tables extraites. Vide : toutes autorisées. Nous recommandons le host:port de votre stockage objet, tel que les workers le nomment. |
connector.internalPort | 8001 | Port interne de l'API auquel le connecteur demande les chaînes de connexion. Doit différer de api.containerPort. |
connector.containerPort | 8080 | Port sur lequel le connecteur écoute. Son Service est toujours ClusterIP. |
connector.resources | requests 250m / 512 MiB, limits 2 CPU / 2 GiB | Ressources de calcul du conteneur du connecteur. |
connector.extraEnvVars | [] | Variables d'environnement supplémentaires du conteneur du connecteur. |
connector.nodeSelector, tolerations, affinity, podAnnotations, podLabels | vide | Planification et métadonnées des pods du connecteur. |
Le connecteur s'exécute avec un utilisateur non root (UID 10001) et un système de fichiers racine
en lecture seule. La liste complète des valeurs connector.* et databaseConnections.* se
trouve dans le README.md du chart.
Faire la rotation des secrets
connector.secret: quand vous le changez, les jetons des jobs en cours deviennent invalides. Faites la rotation quand aucun job de base de données ne tourne.databaseConnections.encryptionKeys: une liste séparée par des virgules. La première clé chiffre, et toutes les clés déchiffrent. Placez la nouvelle clé en premier, attendez que chaque connexion enregistrée soit réécrite, puis retirez l'ancienne clé.