Il tuo deployment funziona in locale e si rompe in produzione. Oppure funziona in produzione e si rompe quando cambi una password. Il problema non è il codice: è la configurazione cucita dentro l'immagine. Ogni volta che modifichi una variabile d'ambiente, sei costretto a ricostruire l'immagine, pushatta sul registry e riavvii i pod. Un incubo. E se qualcuno committa una chiave API nel repository, la lezione è amara.
Noi, di Meteora Web, lavoriamo con Kubernetes in produzione da anni. E la prima cosa che impariamo quando accompagniamo un cliente verso l'orchestrazione è questa: l'immagine del container deve essere immutabile. Le configurazioni si iniettano dall'esterno. Il codice non si tocca, le credenziali non si bruciano, e i pod si aggiornano senza ricostruire nulla.
In questa guida vediamo come usare ConfigMap e Secret per gestire configurazioni e credenziali in Kubernetes, con esempi funzionanti e errori da evitare. Parliamo da ingegneri a ingegneri: niente teoria accademica, solo pratica che tiene in piedi i servizi.
Perché le ConfigMap e i Secret sono separati in Kubernetes?
Kubernetes separa le configurazioni non sensibili dalle credenziali per un motivo preciso: il ciclo di vita e il livello di protezione sono diversi. Una ConfigMap contiene valori come URL, flag di feature, timeout. Un Secret contiene password, token, chiavi API. Se li mescoli, chi ha accesso al cluster vede tutto. E se un Secret finisce in una ConfigMap, hai perso il vantaggio della separazione.
La differenza pratica? Un Secret viene codificato in base64 (non cifrato, attenzione) e può essere montato come file o variabile d'ambiente. Una ConfigMap è pensata per essere letta da più pod e aggiornata senza riavvii traumatici. Ma la vera separazione logica sta nel tuo flusso di lavoro: se un valore è una credenziale, deve stare in un Secret, non in una ConfigMap.
I Secret in Kubernetes non sono cifrati di default. Sono solo codificati in base64. Per una sicurezza reale, devi abilitare la cifratura a riposo con etcd. Kubernetes supporta la cifratura dei Secret a riposo dal 2017, e chi non la abilita sta correndo un rischio che prima o poi paga.
Sponsored Protocol
Come creare una ConfigMap e usarla nei pod?
Il modo più semplice per creare una ConfigMap è da un file YAML. Ecco un esempio concreto:
apiVersion: v1
kind: ConfigMap
metadata:
name: app-config
data:
APP_ENV: production
APP_DEBUG: "false"
DB_HOST: postgres.default.svc.cluster.local
DB_PORT: "5432"
CACHE_DRIVER: redis
TIMEOUT: "30"Per applicarla: kubectl apply -f configmap.yaml. Poi la usi in un deployment come variabili d'ambiente:
apiVersion: apps/v1
kind: Deployment
metadata:
name: app
spec:
replicas: 3
selector:
matchLabels:
app: app
template:
metadata:
labels:
app: app
spec:
containers:
- name: app
image: nginx:alpine
envFrom:
- configMapRef:
name: app-configCon envFrom carichi tutte le chiavi della ConfigMap come variabili d'ambiente. Se vuoi solo alcune, usa env con valueFrom:
env:
- name: DB_HOST
valueFrom:
configMapKeyRef:
name: app-config
key: DB_HOSTUn dettaglio che molti dimenticano: se una ConfigMap non esiste, il pod non parte. Se esiste ma una chiave manca, il pod fallisce con un errore di risoluzione. L'ordine di deploy conta: prima la ConfigMap, poi il deployment. In produzione consigliamo di usare kubectl apply su tutta la cartella con i manifest, ma se usi Helm, questa roba è gestita automaticamente dalle chart.
Quando modifichi una ConfigMap, i pod in esecuzione non ricevono la modifica automaticamente, a meno che tu non usi un meccanismo di ricaricamento come Stakater Reloader o kubectl rollout restart deployment/app. Questa è una delle prime cose che gli sviluppatori sbagliano: aggiornano la ConfigMap e si aspettano che il pod sia aggiornato all'istante. Non lo è.
Come montare una ConfigMap come file di configurazione?
Alcune applicazioni non leggono variabili d'ambiente ma vogliono un file di configurazione. Con Kubernetes puoi montare una ConfigMap come volume. Ecco un esempio con application.properties:
Sponsored Protocol
apiVersion: v1
kind: ConfigMap
metadata:
name: app-properties
data:
application.properties: |
spring.datasource.url=jdbc:postgresql://postgres:5432/app
spring.datasource.username=app
spring.datasource.password=${DB_PASSWORD}
server.port=8080E nel deployment:
volumeMounts:
- name: config
mountPath: /app/config
volumes:
- name: config
configMap:
name: app-propertiesCosì il file application.properties appare in /app/config/application.properties. In questo modo puoi cambiare la configurazione senza ricostruire l'immagine. Attenzione: se monti una ConfigMap come volume, la modifica della ConfigMap viene propagata ai file con un piccolo ritardo (può richiedere fino a 10 secondi), ma il pod non viene riavviato. Il processo nell'applicazione deve essere capace di ricaricare il file. Qui entra in gioco la differenza tra configurazione statica e dinamica: per una vera ricarica a caldo, usa strumenti come spring-boot-devtools o implementa un watcher nel tuo codice.
Come creare un Secret e proteggere le credenziali?
Un Secret si crea in modo simile, ma con dati codificati in base64. Non devi codificarli a mano: kubectl lo fa per te. Il modo più sicuro e riproducibile è crearlo da file:
echo -n 'S3cretP@ssw0rd' | base64
# SegRETo in base64: UzNjcmV0UEBzc3cwcmQ=Oppure, meglio ancora, usa il comando imperativo:
kubectl create secret generic db-secret \
--from-literal=DB_PASSWORD='S3cretP@ssw0rd' \
--from-literal=DB_USER='admin'In un file YAML, un Secret appare così:
apiVersion: v1
kind: Secret
metadata:
name: db-secret
type: Opaque
data:
DB_PASSWORD: UzNjcmV0UEBzc3cwcmQ=
DB_USER: YWRtaW4=Per usare un Secret come variabile d'ambiente:
env:
- name: DB_PASSWORD
valueFrom:
secretKeyRef:
name: db-secret
key: DB_PASSWORDE come volume:
Sponsored Protocol
volumeMounts:
- name: secrets
mountPath: /etc/secrets
readOnly: true
volumes:
- name: secrets
secret:
secretName: db-secretIl volume Secrets monta ogni chiave come un file separato nella directory specificata. Se hai un'applicazione che vuole leggere la password da un file, questo è il modo.
Quali sono gli errori comuni con ConfigMap e Secret?
Errori che vediamo continuamente nei progetti che ci arrivano:
1. Committare i Secret nel repository Git
Il più grave. Un Secret YAML con dati in base64 è semplicemente testo. Se lo committi, hai esposto la credenziale. La codifica base64 non è cifratura. Usa sealed secrets o un vault esterno (HashiCorp Vault, AWS Secrets Manager, Google Secret Manager) per gestire i segreti in modo sicuro. Se usi Git, aggiungi file *.secret.yaml al .gitignore.
2. Usare i Secret come ConfigMap
Mettere valori non sensibili (come il nome dell'ambiente) in un Secret è uno spreco e aumenta la superficie di esposizione. I log di Kubernetes non mostrano i valori dei Secret, ma i metadati sì. Tieni i Secret solo per dati sensibili.
3. Aggiornare ConfigMap senza riavviare i pod
Come detto, il pod non vede le modifiche a meno che non venga riavviato o non monti la ConfigMap come volume. Per applicazioni che non supportano il reload a caldo, aggiungi un'annotazione di versione nel deployment:
spec:
template:
metadata:
annotations:
configmap.reload: "2026-01-15-v2"Così ogni modifica alla ConfigMap richiede un aggiornamento dell'annotazione e un kubectl rollout restart. È un pattern semplice e funziona.
4. Non impostare la cifratura dei Secret a riposo
Se un attaccante accede all'API server o a etcd, può leggere i Secret. Abilita la cifratura a riposo come previsto dalla documentazione ufficiale di Kubernetes. In un cluster self-managed, questo si configura nell'API server con il file di encryption config.
Come gestire l'aggiornamento dei Secret senza downtime?
Quando devi cambiare una credenziale, non devi buttare giù il servizio. La strategia è semplice: aggiorna il Secret come oggetto Kubernetes, poi esegui un rollout rolling update. Il deployment ricreerà i pod uno alla volta, acquisendo il nuovo valore. Per farlo, usa:
Sponsored Protocol
kubectl apply -f secret.yaml
kubectl rollout restart deployment/appSe l'applicazione supporta la ricarica a caldo dei file montati, puoi evitare il reboot semplicemente sostituendo il file. Ad esempio, con un volume Secrets montato, Kubernetes aggiorna il contenuto del file, ma l'applicazione deve rileggerlo. Nginx con la direttiva reload funziona, ma non tutte le app lo fanno.
Un'altra tecnica avanzata è usare Secret rotation controller che automatizza il processo. Ma per la maggior parte dei casi, un rollout restart è sufficiente e semplice da capire.
Come cifrare i Secret a riposo nel cluster?
La configurazione si fa modificando l'API server. Ecco un esempio di encryption config:
apiVersion: apiserver.config.k8s.io/v1
kind: EncryptionConfiguration
resources:
- resources:
- secrets
providers:
- aescbc:
keys:
- name: key1
secret: s3cretK3y...
- identity: {}Poi devi passare il percorso a kube-apiserver tramite il flag --encryption-provider-config. Questo è un argomento avanzato, ma nel mondo reale è il minimo per chi gestisce cluster in produzione. Senza questo, i tuoi Secret sono solo base64 in etcd.
Per la gestione del ciclo di vita completo, valuta l'uso di Sealed Secrets. Con sealed-secrets, puoi committare in Git una versione cifrata del Secret che solo il controller nel cluster può decifrare. È un ottimo compromesso tra GitOps e sicurezza. Il progetto ufficiale è su GitHub e la documentazione è chiara.
Qual è la differenza tra ConfigMap e Secret quando vengono montati come volumi?
Quando monti una ConfigMap come volume, i file sono leggibili e aggiornabili da chiunque abbia accesso al container. Quando monti un Secret come volume, Kubernetes crea file con permessi 0644 di default, ma puoi renderli più restrittivi con defaultMode:
Sponsored Protocol
volumes:
- name: secrets
secret:
secretName: db-secret
defaultMode: 0400Con 0400, solo il proprietario (root) può leggere. Questo è importante se hai un'applicazione che gira con un utente non root: l'utente deve avere i permessi per leggere il file, altrimenti avrai errori inaspettati. Un pattern comune è usare un'immagine che parte con un utente dedicato e montare i Secret con permessi 0440. La regola generale: i file Secret devono essere leggibili solo dall'utente del container, non da tutti. Tieni presente che Kubernetes monta i Secret come bind mount tmpfs, quindi non scrivono su disco; ma la sicurezza dei file system è comunque una responsabilità tua.
In sintesi
Ecco le azioni immediate da implementare oggi sui tuoi cluster:
- Separa subito ConfigMap e Secret: niente credenziali nelle ConfigMap. Una password in chiaro è una violazione che aspetta di accadere.
- Imposta la cifratura dei Secret a riposo: se il tuo cluster è self-managed, attiva
--encryption-provider-configcon AES-CBC. Se usi un provider cloud, abilita la cifratura gestita. - Adotta Sealed Secrets o un vault esterno: niente Secret in chiaro nel repository. Il base64 non è cifratura.
- Usa il pattern
kubectl rollout restartdopo ogni modifica: per le app che non supportano il reload a caldo, è il metodo più semplice e affidabile. - Monta i Secret come volumi con defaultMode 0400: riduci la superficie di esposizione se un container viene compromesso.
Kubernetes ti dà gli strumenti. Sta a te usarli come si deve. Se vuoi approfondire l'intero percorso sull'orchestrazione, parte dalla nostra guida pillar su Kubernetes e orchestrazione container. Per la gestione del ciclo di vita delle configurazioni, consulta la documentazione ufficiale sui Secret e il task di cifratura dei dati in etcd.