4. Deploy su Kubernetes#
In questo tutorial
Rilasci un manifest su un cluster Kubernetes con una service account limitata a un namespace. manent applica gli oggetti con server-side apply, attende il rollout, conserva lo storico delle release nel cluster e lo usa per riportare indietro un'immagine difettosa in pochi secondi, senza scaricare nulla.
Tempo: 30 minuti · Serve: un cluster Kubernetes che puoi amministrare (kind, k3d, k3s, Docker Desktop o uno reale), kubectl, i binari del tutorial 1
flowchart LR
M["worker manent"] -->|"server-side apply<br/>token bearer"| API["API di Kubernetes"]
API --> NS["namespace hello<br/>Deployment · Service"]
API --> H["ConfigMap manent-hello-test-*<br/>storico delle release"]
1. Una service account per manent#
manent parla direttamente con l'API del cluster: al worker non servono kubectl né un kubeconfig. Serve un token, i cui permessi decidono esattamente cosa può cambiare. Un Role limitato al namespace dell'applicazione è la scelta abituale:
apiVersion: v1
kind: Namespace
metadata:
name: hello
---
apiVersion: v1
kind: ServiceAccount
metadata:
name: manent
namespace: hello
---
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: manent-deployer
namespace: hello
rules:
- apiGroups: [""]
resources: [configmaps, services] # configmaps: anche lo storico delle release
verbs: [get, list, watch, create, update, patch, delete]
- apiGroups: [apps]
resources: [deployments, replicasets]
verbs: [get, list, watch, create, update, patch, delete]
- apiGroups: [""]
resources: [pods]
verbs: [get, list, watch]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: manent-deployer
namespace: hello
subjects:
- {kind: ServiceAccount, name: manent, namespace: hello}
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: Role
name: manent-deployer
---
apiVersion: v1
kind: Secret
metadata:
name: manent-token
namespace: hello
annotations:
kubernetes.io/service-account.name: manent
type: kubernetes.io/service-account-token
mkdir -p ~/manent-tutorial/k8s && cd ~/manent-tutorial/k8s
kubectl apply -f rbac.yaml
kubectl -n hello get secret manent-token -o jsonpath='{.data.token}' | base64 -d > token
kubectl -n hello get secret manent-token -o jsonpath='{.data.ca\.crt}' | base64 -d > ca.crt
kubectl config view --minify -o jsonpath='{.clusters[0].cluster.server}' # l'indirizzo dell'API
Aggiungi al Role le risorse che contengono i tuoi manifest (Ingress, HorizontalPodAutoscaler, …). Con manent dentro il cluster si legge invece il token del pod con file:/var/run/secrets/kubernetes.io/serviceaccount/token.
2. L'artefatto: un manifest#
L'artefatto è il file dei manifest (o un .tar.gz di manifest, o un chart Helm). Viene verificato con lo sha256 e promosso senza modifiche tra gli ambienti: ciò che cambia tra un ambiente e l'altro arriva dalle variabili.
apiVersion: apps/v1
kind: Deployment
metadata:
name: hello
spec:
replicas: ${REPLICAS}
progressDeadlineSeconds: 120
selector:
matchLabels: {app: hello}
template:
metadata:
labels: {app: hello}
spec:
containers:
- name: hello
image: hashicorp/http-echo:1.0
args: ["-listen=:8080", "-text=hello from ${MANENT_VERSION} in ${MANENT_ENVIRONMENT}"]
ports: [{containerPort: 8080}]
readinessProbe:
httpGet: {path: /, port: 8080}
---
apiVersion: v1
kind: Service
metadata:
name: hello
spec:
selector: {app: hello}
ports: [{port: 80, targetPort: 8080}]
${MANENT_VERSION} e ${MANENT_ENVIRONMENT} li imposta manent; ${REPLICAS} viene dal parametro var.REPLICAS dell'ambiente. progressDeadlineSeconds fa fallire in fretta un rollout bloccato invece di attendere tutto il wait_timeout.
Immagini fissate per digest
In produzione riferisci le immagini come image@sha256:… e imposta il parametro require_image_digest: "true": un manifest con un tag modificabile viene rifiutato, e l'artefatto è davvero immutabile.
3. Configurazione di ambiente#
Sostituisci /home/you/manent-tutorial con il tuo percorso nei riferimenti ai segreti e nel descrittore.
plugin: kubernetes
targets:
- {host: <host-api>, port: <porta-api>} # dall'indirizzo del server, es. 127.0.0.1 e 6443
params:
namespace: hello
token_secret: kube-token
ca_secret: kube-ca
var.REPLICAS: "2"
wait_timeout: 3m
secrets:
- {name: kube-token, ref: "file:/home/you/manent-tutorial/k8s/token"}
- {name: kube-ca, ref: "file:/home/you/manent-tutorial/k8s/ca.crt"}
rollback:
enabled: true
Ogni target è un cluster. Con più cluster aggiungi un target per ciascuno con le etichette region e az, e un rilascio a ondate li aggiorna uno alla volta; senza ondate vengono aggiornati uno dopo l'altro, o parallel alla volta.
dbos:
database_url: sqlite:manent.db
workdir: ./work
plugins:
kubernetes: {}
envconfig:
required: true
providers:
- name: default
type: file
file:
path: "./envconfig/{tenant}/{application}/{artifact}/{environment}.yml"
apiVersion: manent/v1
application: hello
environment: test
artifact:
type: kubernetes
name: hello
version: 1.0.0
source: /home/you/manent-tutorial/k8s/dist/hello-1.0.0.yaml
Avvia il server (manent-server -c manent.yml serve) in un altro terminale.
4. Dry-run sul cluster#
manent deploy -f hello.yml --dry-run | jq '.steps[] | select(.name=="deploy") | .output.result | fromjson'
{"plugin": "kubernetes", "targets": [{"host": "127.0.0.1", "status": "ready",
"details": {"cluster_version": "v1.31.4+k3s1", "namespace": "hello", "objects_checked": "2", "objects_skipped": "0"}}]}
In dry-run ogni oggetto viene applicato con dryRun=All: il cluster verifica schema, admission webhook e RBAC della service account, e non salva nulla. Un permesso mancante emerge qui, non durante il rilascio reale.
5. Rilascia#
NAME READY UP-TO-DATE AVAILABLE
deployment.apps/hello 2/2 2 2
NAME DATA
configmap/manent-hello-test 3
configmap/manent-hello-test-1-0-0-8bf0f7f2 5
NAME TYPE CLUSTER-IP PORT(S)
service/hello ClusterIP 10.43.225.169 80/TCP
Lo step deploy è terminato solo a rollout del Deployment completato. Gli oggetti hanno le etichette app.kubernetes.io/managed-by: manent, manent.io/application, manent.io/artifact, manent.io/tenant e manent.io/environment, e l'annotazione manent.io/version. Le due ConfigMap sono lo storico: manent-hello-test punta alla release in esercizio, manent-hello-test-1-0-0-… contiene gli oggetti applicati dalla 1.0.0.
6. Un'immagine difettosa, riportata indietro#
Rilascia una 1.1.0 la cui immagine non esiste:
sed -e 's#http-echo:1.0#http-echo:does-not-exist#' -e 's/progressDeadlineSeconds: 120/progressDeadlineSeconds: 30/' \
dist/hello-1.0.0.yaml > dist/hello-1.1.0.yaml
manent deploy -f hello.yml --version 1.1.0 --source "$PWD/dist/hello-1.1.0.yaml"
cluster 127.0.0.1: Deployment/hello/hello: rollout stuck: ReplicaSet "hello-8544968f49" has timed out progressing.; rollback completed
kubectl -n hello get deploy hello -o jsonpath='{.spec.template.spec.containers[0].image} {.metadata.annotations.manent\.io/version}{"\n"}'
# hashicorp/http-echo:1.0 1.0.0
Passata la scadenza, il plugin ha dichiarato il rollout bloccato e il rollback ha riapplicato gli oggetti salvati per la 1.0.0 nello storico: non è stato necessario scaricare nulla. Un rollout bloccato è un errore permanente, quindi manent non perde tempo a ritentarlo.
7. Chart Helm#
Lo stesso plugin rilascia un chart impacchettato con helm package (un .tgz con <nome>/Chart.yaml, riconosciuto in automatico). manent lo renderizza soltanto, con helm template e le capability del cluster di destinazione, e ne applica il risultato come un manifest: Helm non tiene stato nel cluster, e storico e rollback funzionano come sopra.
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 # dentro il chart
helm_value.replicaCount: "4"
helm_values: |
ingress:
enabled: true
host: shop.example.com
Il worker ha bisogno del binario helm (è nell'immagine manent-server). Gli hook sono saltati di default; vedi Chart Helm per hook, CRD e il passaggio a manent di una release Helm esistente.
Cosa hai imparato#
- manent usa l'API del cluster con un token limitato al namespace; i permessi sono il confine.
- L'artefatto è il manifest o il chart; gli ambienti differiscono per variabili e values, non per file.
- Un deploy termina quando il rollout è completo, e un rollout bloccato fallisce in fretta.
- Lo storico nel cluster rende il rollback immediato.
Prossimo passo#
- Rilasci per percentuale su Kubernetes con la Gateway API o Istio: Rilasci per percentuale e il plugin kubernetes.
- Tutorial 5: canary e ondate con health gate.