kubernetes#
Il plugin kubernetes rilascia manifest o chart Helm su uno o più cluster Kubernetes. Usa il server-side apply, attende che i workload completino il rollout e conserva nel cluster lo storico delle release, da cui torna alla precedente. Parla direttamente con l'API del cluster: sul worker non serve kubectl, e helm serve solo per renderizzare i chart.
L'artefatto sono i manifest#
L'artefatto è un file di manifest (YAML o JSON, anche con più documenti separati da ---) oppure un archivio .tar.gz di file .yaml, .yml e .json, letti in ordine di nome. È l'artefatto verificato con lo sha256 e promosso tra gli ambienti: lo stesso file va in test e in produzione.
artifact:
type: kubernetes
name: shop
version: "1.4.0"
source: https://nexus.example.com/repository/manifests/shop-1.4.0.yaml
sha256: 8c1f…
Per un chart Helm vedi Chart Helm. Per le configurazioni Kustomize si rilascia l'output già generato in CI (kustomize build): l'artefatto resta immutabile e verificabile.
Variabili#
Ciò che cambia tra gli ambienti arriva dalla configurazione di ambiente, non dai manifest. Nei manifest si possono usare queste variabili:
| Variabile | Valore |
|---|---|
${MANENT_APPLICATION}, ${MANENT_ENVIRONMENT}, ${MANENT_VERSION} |
Applicazione, ambiente e versione del deploy. |
${MANENT_NAMESPACE} |
Il parametro namespace. |
${MANENT_DEPLOYMENT} |
ID del deploy. |
${NOME} |
Il parametro var.NOME, ad esempio var.REPLICAS: "3" per ${REPLICAS}. |
Le altre ${...} restano invariate, perché possono far parte di script contenuti nei manifest.
Configurazione#
plugins:
kubernetes:
# ca_file: /var/run/secrets/kubernetes.io/serviceaccount/ca.crt # manent dentro il cluster
insecure_skip_tls_verify: false # true solo in sviluppo
artifact_types: [kubernetes] # default
helm: # solo per i chart
command: helm # default; nell'immagine di manent-server è incluso
# sha256: "<sha256 del binario>" # verificato a ogni rendering
timeout: 2m # default
Chart Helm#
L'artefatto può essere un chart impacchettato con helm package (un .tgz con <nome>/Chart.yaml), riconosciuto in automatico. Il pacchetto è l'artefatto verificato con lo sha256 e promosso tra gli ambienti; le dipendenze vanno incluse (helm package --dependency-update).
artifact:
type: kubernetes
name: shop
version: "1.4.0"
source: https://nexus.example.com/repository/charts/shop-1.4.0.tgz
sha256: 8c1f…
Il chart viene solo renderizzato con helm template, per ogni cluster, come fa Argo CD: gli oggetti generati seguono lo stesso percorso dei manifest (etichette, server-side apply, storico, attesa del rollout, prune, dry-run). Helm non installa nulla e non tiene uno stato nel cluster: niente secret sh.helm.release.v1, e helm list non mostra la release. Il rollback riapplica gli oggetti salvati nello storico e non richiede Helm.
- Capabilities del cluster.
--kube-versione--api-versionsvengono dalla discovery del cluster di destinazione, quindi.Capabilities.KubeVersione.Capabilities.APIVersions.Hasrispondono come in unhelm install. - Values, in ordine di precedenza crescente:
values.yamldel chart, i file dihelm_values_files(contenuti nel chart, ad esempiovalues-prod.yaml),helm_values(YAML), i singolihelm_value.<percorso>. Come gli altri parametri, arrivano dalla configurazione di ambiente. - Hook. Gli hook (
helm.sh/hook) non hanno un equivalente nel server-side apply: di default non vengono applicati e sono elencati nel risultato (helm_hooks_skipped) e nel log. Conhelm_hooks: applysono applicati come oggetti normali, insieme agli altri; un Job di migrazione va allora reso idempotente o rinominato a ogni versione (ad esempio con{{ .Chart.AppVersion }}nel nome). I test (helm.sh/hook: test) non vengono mai applicati. - CRD della cartella
crds/incluse e applicate per prime (helm_skip_crds: trueper escluderle). lookupnon legge il cluster durante il rendering e restituisce sempre un valore vuoto.- Isolamento. Helm gira con un ambiente vuoto e una home temporanea: non eredita le variabili del worker né legge
KUBECONFIG, repository o credenziali locali. Conhelm.sha256il binario viene verificato prima di ogni uso. - Le variabili
${...}dei manifest non si applicano ai chart: si usano i values.
plugin: kubernetes
targets:
- {host: k8s-eu1.example.com, labels: {region: eu-1}}
params:
namespace: shop-prod
token_secret: kube-token
helm_values_files: values-prod.yaml
helm_value.replicaCount: "4"
helm_values: |
ingress:
enabled: true
host: shop.example.com
Un'applicazione già installata con helm install può passare a manent: gli oggetti vengono adottati dal server-side apply. Poi va eliminato il solo stato di Helm (i secret sh.helm.release.v1.<release>.* nel namespace), non la release con helm uninstall, che cancellerebbe gli oggetti.
Target e credenziali#
Ogni target è il server API di un cluster: host (o un URL https://…) e port (default 6443). Le etichette region e az permettono di rilasciare un cluster alla volta con i rilasci a ondate.
| Parametro | Descrizione |
|---|---|
token_secret |
Segreto con il bearer token, ad esempio quello di un ServiceAccount. |
client_cert_secret, client_key_secret |
In alternativa al token: certificato client e chiave PEM. |
ca_secret |
Segreto con la CA del cluster (PEM). Default: ca_file del plugin o le CA di sistema. |
Con manent in esecuzione nel cluster, il token del service account si legge con il secret provider file::
plugin: kubernetes
targets:
- {host: kubernetes.default.svc, port: 443, labels: {region: eu-1}}
params:
namespace: shop-prod
token_secret: kube-token
var.REPLICAS: "4"
secrets:
- {name: kube-token, ref: "file:/var/run/secrets/kubernetes.io/serviceaccount/token"}
I permessi del token decidono cosa manent può modificare: un Role limitato al namespace dell'applicazione è la scelta tipica. Oltre agli oggetti dei manifest, servono i permessi su ConfigMap nel namespace per lo storico delle release.
Parametri del deploy#
| Parametro | Default | Descrizione |
|---|---|---|
namespace |
default |
Namespace degli oggetti che non ne indicano uno, e dello storico. |
wait_timeout |
5m |
Attesa del rollout dei workload. |
prune |
false |
true: dopo il deploy elimina gli oggetti della release precedente che i nuovi manifest non contengono più. I Namespace non vengono mai eliminati. |
keep_releases |
5 |
Release conservate nello storico (minimo 2). |
require_image_digest |
false |
true: rifiuta i workload con immagini non fissate al digest (image@sha256:…). |
var.NOME |
Valore di ${NOME} nei manifest. |
|
helm_release |
nome dell'artefatto | Nome della release Helm (.Release.Name): due chart della stessa applicazione generano oggetti con nomi diversi. |
helm_values_files |
File di values contenuti nel chart, separati da virgola, applicati in ordine. | |
helm_values |
Values in YAML. | |
helm_value.<percorso> |
Un singolo value, interpretato come YAML: helm_value.image.tag: "1.4.0", helm_value.replicaCount: "3". |
|
helm_hooks |
skip |
apply: applica gli hook del chart come oggetti normali. |
helm_skip_crds |
false |
true: non applica le CRD della cartella crds/. |
Funzionamento#
Per ogni cluster (uno dopo l'altro, o parallel alla volta: ogni cluster ha la propria connessione e il proprio rendering Helm, e la maggior parte del tempo è l'attesa del rollout):
- legge i manifest e sostituisce le variabili, oppure renderizza il chart con le capabilities del cluster; poi verifica ogni oggetto con la discovery del cluster (apiVersion e kind esistenti). Agli oggetti senza namespace assegna
namespace; - aggiunge le etichette
app.kubernetes.io/managed-by: manent,manent.io/application,manent.io/artifact,manent.io/tenant,manent.io/environmente le annotazionimanent.io/versionemanent.io/deployment; - applica prima i Namespace, poi salva la release nello storico, poi applica gli altri oggetti in ordine di dipendenza (CRD, permessi, ConfigMap e Secret, Service, workload). Il server-side apply usa il field manager
manented è idempotente; - attende il rollout di Deployment, StatefulSet e DaemonSet. Un Deployment oltre il suo
progressDeadlineSecondsfallisce subito, senza attenderewait_timeoute senza retry (errore permanente); - con
prune, elimina gli oggetti rimossi dai manifest; - aggiorna la release in esercizio ed elimina dallo storico le release oltre
keep_releases.
Per bloccare un deploy che non parte senza aspettare il timeout conviene impostare progressDeadlineSeconds nei Deployment.
Storico e rollback#
Lo storico è fatto di ConfigMap nel namespace:
manent-<tenant>-<app>-<artefatto>-<env>-<hash>: la release in esercizio (per esempiomanent-untenanted-shop-shop-api-prod-1a2b3c4d;untenantedper le applicazioni che nessun tenant elenca);- lo stesso nome seguito da
-<versione>: gli oggetti applicati da ogni release, compressi (al più circa 900 KiB per release).
Ogni release (tenant, applicazione, artefatto, ambiente) ha uno storico proprio. Le ConfigMap hanno le etichette della release, e la pulizia oltre keep_releases tocca solo lo storico della release che si sta rilasciando.
Il rollback riapplica su ogni cluster gli oggetti salvati della release precedente, ne attende il rollout e, con prune, elimina gli oggetti creati solo dalla release fallita. Non serve riscaricare l'artefatto precedente. La release in esercizio (locator plugin) si legge dallo storico; cluster con release diverse fermano il deploy nel checkpoint.
Il tipo di artefatto kubernetes ammette il rollback (rollback.artifact_types.kubernetes.allowed: true nei default).
Rilasci per percentuale#
Con un rollout strategy: traffic (Rilasci per percentuale) il plugin tiene due versioni affiancate in due slot, a e b, alternati a ogni rilascio, e sposta il traffico con i pesi di una HTTPRoute (o GRPCRoute) della Gateway API.
- Oggetti dello slot. Deployment, StatefulSet, DaemonSet, Service, HorizontalPodAutoscaler e PodDisruptionBudget dei manifest ricevono il suffisso
-<slot>(shop→shop-b) e l'etichettamanent.io/track: <slot>, anche nei selettori e nei pod;serviceNamedegli StatefulSet escaleTargetRefdegli HPA seguono il nuovo nome. Gli altri oggetti (ConfigMap, Secret, …) sono comuni ai due slot: una modifica vale subito anche per la stabile. - La route è dell'infrastruttura: manent cambia solo i pesi dei
backendRefs<traffic_service>-ae<traffic_service>-b(deve averli entrambi) con una merge patch condizionata alresourceVersionletto. La regola con un match sull'header dei tester (rollout.traffic.testers.header) porta solo alla candidata. - Storico. La ConfigMap della release in esercizio registra anche lo slot e, dopo una promozione, la release sostituita. Il ritorno ridimensiona a zero la candidata; dopo
scaleDownDelayil ritiro porta a zero i workload dello slot sostituito ed elimina i suoi HPA.
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata: {name: shop, namespace: shop}
spec:
parentRefs: [{name: public}]
rules:
- matches: [{headers: [{name: x-canary, value: always}]}]
backendRefs: [{name: shop-a, port: 80}]
- backendRefs:
- {name: shop-a, port: 80, weight: 100}
- {name: shop-b, port: 80, weight: 0}
| Parametro | Default | Descrizione |
|---|---|---|
traffic_route |
Nome della route che divide il traffico. | |
traffic_service |
Nome base dei Service degli slot (<nome>-a, <nome>-b). |
|
traffic_route_kind |
HTTPRoute |
HTTPRoute o GRPCRoute. |
traffic_route_namespace |
namespace |
Namespace della route. |
traffic_router |
gateway-api |
gateway-api o istio. |
traffic_rename |
true |
false se il chart dà già i nomi per slot: manent aggiunge solo l'etichetta manent.io/track. |
Istio. Con traffic_router: istio la route è un VirtualService (networking.istio.io/v1): manent cambia i pesi delle route verso i subset a e b, definiti da una DestinationRule sull'etichetta manent.io/track, e la voce http con un match sull'header dei tester porta solo alla candidata. I Service non hanno suffisso e restano comuni ai due slot; il traffico tra servizi della mesh segue gli stessi pesi.
Repliche proporzionali. Con rollout.traffic.replicas: proportional la candidata parte con una replica (le repliche dei manifest restano nell'annotazione manent.io/replicas) e prima di ogni gradino cresce a ceil(repliche × percentuale / 100), attendendo che i pod siano disponibili. I workload gestiti da un HorizontalPodAutoscaler non vengono toccati.
Chart Helm. Il chart riceve lo slot in .Values.manent.slot. Se lo usa già nei nomi degli oggetti, traffic_rename: "false" evita il doppio suffisso.
Un'applicazione già rilasciata senza slot (oggetti senza suffisso) passa agli slot con un'operazione esplicita: il primo rilascio per percentuale crea shop-a accanto a shop e riceve subito tutto il traffico (non c'è una versione stabile negli slot); la route va preparata con i backend shop-a e shop-b e gli oggetti senza suffisso vanno rimossi a mano dopo la verifica.
Dry-run#
In dry-run, per ogni cluster: versione e credenziali, poi ogni oggetto viene applicato con dryRun=All. Il cluster lo valida con schema, admission webhook e RBAC, ma non salva nulla. Gli oggetti in un namespace che i manifest stessi creerebbero non sono verificabili e vengono contati in objects_skipped.