Ajouter un service¶
L'ajout d'un nouveau service à AKKO implique de modifier plusieurs fichiers afin de garantir que le service est correctement routé, sécurisé, supervisé et visible dans le portail cockpit. Cette page fournit la checklist complète ainsi qu'un exemple concret.
Checklist¶
| Étape | Fichier | Objectif |
|---|---|---|
| 1 | helm/akko/charts/<sub-chart>/ |
Sous-chart Helm (templates, values, helpers) |
| 2 | keycloak/realm-akko.json |
Client OAuth2 (si le service nécessite le SSO) |
| 3 | scripts/generate-secrets.sh |
Ajouter les nouveaux secrets à la génération .env |
| 4 | branding/cockpit/index.html |
Ajouter une carte de service au portail |
| 5 | branding/cockpit/app.js |
Enregistrer le point de contrôle de santé |
| 6 | branding/cockpit/nginx.conf |
Ajouter une route de proxy pour le health check (évite les problèmes CORS) |
| 7 | scripts/start.sh |
Afficher l'URL du service au démarrage |
| 8 | Documentation | Mettre à jour la documentation d'architecture, le README, etc. |
Chaque étape est détaillée ci-dessous, suivie d'un exemple complet et concret.
Étape 1 -- Sous-chart Helm¶
Créez un sous-chart Helm dans helm/akko/charts/<nom-du-service>/ et ajoutez la dépendance dans helm/akko/Chart.yaml. Chaque service AKKO suit un modèle cohérent :
- Tag d'image épinglé (jamais
latest) - Nom de conteneur préfixé par
akko- - IngressRoute Traefik pour le routage HTTPS
- Healthcheck avec sondes de vivacité et de disponibilité
- Limites de ressources appropriées au service
- Contexte de sécurité (
runAsNonRoot,drop: [ALL])
Modèle de labels Traefik¶
Tous les services exposés via Traefik utilisent ces quatre labels :
labels:
traefik.enable: "true"
traefik.http.routers.<name>.rule: "Host(`<subdomain>.{{ .Values.global.domain }}`)"
traefik.http.routers.<name>.entrypoints: "websecure"
traefik.http.routers.<name>.tls: "true"
Si le service écoute sur un port non standard (autre que 80), ajoutez :
Pour protéger le service derrière le SSO Keycloak via oauth2-proxy, ajoutez le middleware :
traefik.http.routers.<name>.middlewares: "oauth2-auth-chain@file"
# Plus le routeur de callback oauth2 :
traefik.http.routers.<name>-oauth2.rule: "Host(`<subdomain>.{{ .Values.global.domain }}`) && PathPrefix(`/oauth2/`)"
traefik.http.routers.<name>-oauth2.entrypoints: "websecure"
traefik.http.routers.<name>-oauth2.tls: "true"
traefik.http.routers.<name>-oauth2.service: "oauth2-proxy"
Modèle de healthcheck¶
livenessProbe:
httpGet:
path: /<health-path>
port: <port>
initialDelaySeconds: 30
periodSeconds: 30
timeoutSeconds: 10
failureThreshold: 3
readinessProbe:
httpGet:
path: /<health-path>
port: <port>
initialDelaySeconds: 10
periodSeconds: 10
timeoutSeconds: 5
failureThreshold: 3
Conseils pour les healthchecks
- Utilisez les sondes
httpGetpour les endpoints de santé HTTP. - Utilisez les sondes
execavec la commande native de healthcheck du service lorsqu'elle est disponible (par ex.["traefik", "healthcheck"]ou["mc", "ready", "local"]). - Certaines images minimales (comme celle d'Ollama) ne contiennent ni
curlniwget. Utilisez les sondestcpSocketcomme solution de repli.
Étape 2 -- Client OAuth Keycloak¶
Si le service supporte OpenID Connect, ajoutez un client dans keycloak/realm-akko.json
dans le tableau clients. L'identifiant du client doit correspondre au nom du service.
Définissez publicClient: false pour les flux côté serveur (confidentiels).
Étape 3 -- Génération des secrets¶
Si le service a besoin d'identifiants, ajoutez-les dans scripts/generate-secrets.sh
à l'intérieur du heredoc qui écrit .env :
Après modification, supprimez .env et relancez le script pour intégrer les nouvelles variables :
Étape 4 -- Carte cockpit¶
Ajoutez une carte dans branding/cockpit/index.html à l'intérieur de la section <div class="grid">.
Chaque carte suit cette structure :
<a class="card"
data-service="<service-id>"
data-subdomain="<subdomain>"
data-health="<service-id>"
data-category="<category>"
href="#" target="_blank" rel="noopener">
<div class="card__header">
<div class="card__icon">
<svg viewBox="0 0 24 24" aria-hidden="true">
<!-- Chemins d'icône SVG -->
</svg>
</div>
<div class="card__status">
<svg class="uptime-ring" width="28" height="28" viewBox="0 0 28 28" aria-hidden="true">
<circle cx="14" cy="14" r="11"/>
<circle class="ring-fill" cx="14" cy="14" r="11"/>
</svg>
<span class="status-dot status-dot--checking"></span>
</div>
</div>
<h4 class="card__name">Service Name</h4>
<p class="card__desc">Short description</p>
<button class="fav-btn" title="Pin" aria-label="Pin Service Name">☆</button>
</a>
Attributs clés :
data-service-- identifiant unique du service (utilisé pour les favoris, le filtrage)data-subdomain-- le sous-domaine utilisé dans l'URL (par ex.jupyterpourlab.akko.local, configurable viaglobal.domain)data-health-- doit correspondre à la clé dans l'objetSERVICESdeapp.jsdata-category-- utilisé pour le filtrage par catégorie (ui,data,monitoring,infra)
Étape 5 -- Health check dans app.js¶
Ajoutez une entrée dans l'objet SERVICES de branding/cockpit/app.js :
const SERVICES = {
// ... services existants ...
myservice: { name: 'MyService', endpoint: '/api/health/myservice' },
};
Le cockpit interroge ces endpoints toutes les 15 secondes et met à jour le point d'état et l'anneau de disponibilité sur chaque carte.
Étape 6 -- Proxy de santé Nginx¶
Ajoutez un bloc de reverse proxy dans branding/cockpit/nginx.conf pour transférer la
requête de health check du cockpit vers le service réel (évite les problèmes CORS puisque
le cockpit s'exécute sur un sous-domaine différent) :
location /api/health/myservice {
set $upstream_myservice http://myservice:8080;
proxy_pass $upstream_myservice/health;
proxy_connect_timeout 3s;
proxy_read_timeout 3s;
}
Résolution dynamique
Utilisez toujours set $upstream_xxx avec une variable avant proxy_pass. Cela
exploite la directive resolver 127.0.0.11 pour que nginx démarre même si le
service cible est indisponible. Sans cela, nginx plante au démarrage lorsqu'un
service est hors ligne.
Étape 7 -- URL de démarrage¶
Ajoutez l'URL du service dans la bannière de démarrage dans scripts/start.sh :
Étape 8 -- Documentation¶
Mettez à jour la documentation d'architecture et tout guide pertinent pour refléter le nouveau service.
Exemple complet : ajout de « DataHub »¶
Voici un exemple complet et concret d'ajout d'un service hypothétique DataHub (catalogue de métadonnées) à AKKO.
1. Sous-chart Helm¶
Créez helm/akko/charts/akko-datahub/ avec la structure standard de sous-chart :
helm/akko/charts/akko-datahub/
├── Chart.yaml
├── values.yaml
├── templates/
│ ├── deployment.yaml
│ ├── service.yaml
│ ├── ingress.yaml
│ └── _helpers.tpl
Ajoutez la dépendance dans helm/akko/Chart.yaml :
Exemple de values.yaml :
enabled: true
image:
repository: acryldata/datahub-gms
tag: "v0.13.0"
pullPolicy: IfNotPresent
resources:
requests:
memory: 512Mi
cpu: 250m
limits:
memory: 1Gi
cpu: 500m
2. keycloak/realm-akko.json¶
Ajoutez un client confidentiel dans le tableau clients :
{
"clientId": "datahub",
"enabled": true,
"publicClient": false,
"secret": "${KC_CLIENT_SECRET_DATAHUB}",
"redirectUris": ["https://datahub.akko.local/*"],
"webOrigins": ["https://datahub.akko.local"],
"protocol": "openid-connect",
"standardFlowEnabled": true,
"directAccessGrantsEnabled": false
}
3. scripts/generate-secrets.sh¶
Ajoutez dans le heredoc :
4. branding/cockpit/index.html¶
<a class="card"
data-service="datahub"
data-subdomain="datahub"
data-health="datahub"
data-category="data"
href="#" target="_blank" rel="noopener">
<div class="card__header">
<div class="card__icon">
<svg viewBox="0 0 24 24" aria-hidden="true">
<ellipse cx="12" cy="5" rx="9" ry="3"/>
<path d="M21 12c0 1.66-4 3-9 3s-9-1.34-9-3"/>
<path d="M3 5v14c0 1.66 4 3 9 3s9-1.34 9-3V5"/>
</svg>
</div>
<div class="card__status">
<svg class="uptime-ring" width="28" height="28" viewBox="0 0 28 28" aria-hidden="true">
<circle cx="14" cy="14" r="11"/>
<circle class="ring-fill" cx="14" cy="14" r="11"/>
</svg>
<span class="status-dot status-dot--checking"></span>
</div>
</div>
<h4 class="card__name">DataHub</h4>
<p class="card__desc">Metadata Catalog</p>
<button class="fav-btn" title="Pin" aria-label="Pin DataHub">☆</button>
</a>
5. branding/cockpit/app.js¶
const SERVICES = {
// ... existants ...
datahub: { name: 'DataHub', endpoint: '/api/health/datahub' },
};
6. branding/cockpit/nginx.conf¶
location /api/health/datahub {
set $upstream_datahub http://datahub:8080;
proxy_pass $upstream_datahub/health;
proxy_connect_timeout 3s;
proxy_read_timeout 3s;
}
7. scripts/start.sh¶
Vérification¶
Après avoir complété toutes les étapes :
# Regénérer les secrets
rm .env && ./scripts/generate-secrets.sh
# Déployer avec Helm
helm upgrade akko helm/akko/ -n akko -f helm/examples/values-dev.yaml \
--set-file akko-keycloak.realm.data=helm/examples/realm-akko-k3d.json
# Vérifier que DataHub est en bonne santé
kubectl get pods -n akko | grep datahub
kubectl logs -f deploy/akko-datahub -n akko
# Tester le proxy de santé du cockpit
curl -sf https://cockpit.akko.local/api/health/datahub
# Ouvrir dans le navigateur
open https://datahub.akko.local