ConfigMap et Secret dans Kubernetes : Séparer la config de l'image
1. Vue d’ensemble
Quand vous packagisez une application en image container, vous ne devriez pas « durcir » les valeurs comme l’URL de la base de données ou les clés API directement dans l’image. Cet article vous aide à comprendre ConfigMap et Secret dans Kubernetes — deux objets permettant de séparer la configuration de l’image, comment les créer, comment les charger dans un Pod via des variables d’environnement ou des volumes, et comment les intégrer avec une application Spring Boot. À la fin, vous serez en confiance pour configurer un Deployment qui lit la configuration dynamiquement sans avoir besoin de reconstruire l’image à chaque fois que vous changez une valeur.
2. Qu’est-ce que ConfigMap
ConfigMap est un objet qui stocke des données de configuration en format clé-valeur, non chiffrées, destinées aux données non sensibles : noms de service, feature flags, fichiers properties…
Les données d’une ConfigMap existent indépendamment du Pod — vous pouvez modifier la ConfigMap sans reconstruire l’image, il suffit de redémarrer le Pod (ou d’utiliser un mécanisme de reload si l’application le supporte).
3. Créer une ConfigMap
3.1. À partir d’un fichier YAML
apiVersion: v1
kind: ConfigMap
metadata:
name: order-service-config
data:
APP_ENV: "production"
LOG_LEVEL: "INFO"
application.properties: |
server.port=8080
spring.jpa.hibernate.ddl-auto=validate
Ici, data contient deux types : des clés simples (APP_ENV, LOG_LEVEL) et une clé contenant le contenu complet du fichier (application.properties). Appliquez-le ainsi :
kubectl apply -f order-service-config.yaml
3.2. À partir de littéraux ou de fichiers existants
kubectl create configmap order-service-config \
--from-literal=APP_ENV=production \
--from-file=application.properties=./config/application.properties
Cette commande crée une ConfigMap directement depuis la ligne de commande sans avoir besoin d’écrire du YAML, pratique quand on fait du scripting CI/CD.
4. Utiliser ConfigMap dans un Pod
4.1. Charger comme variables d’environnement
apiVersion: v1
kind: Pod
metadata:
name: order-service
spec:
containers:
- name: order-service
image: nonladev/order-service:1.0
envFrom:
- configMapRef:
name: order-service-config
envFrom charge toutes les clés de la ConfigMap en tant que variables d’environnement du même nom. Le container lit APP_ENV, LOG_LEVEL comme des variables d’environnement normales.
Si vous voulez sélectionner seulement une clé spécifique :
env:
- name: APP_ENVIRONMENT
valueFrom:
configMapKeyRef:
name: order-service-config
key: APP_ENV
Cette approche vous permet de renommer la variable d’environnement dans le container (APP_ENVIRONMENT) différemment de la clé d’origine dans la ConfigMap (APP_ENV).
4.2. Charger comme volume
volumeMounts:
- name: config-volume
mountPath: /app/config
volumes:
- name: config-volume
configMap:
name: order-service-config
Chaque clé dans la ConfigMap devient un fichier dans /app/config (par exemple /app/config/application.properties). Cette approche est appropriée quand l’application a besoin de lire des fichiers de configuration entiers plutôt que des variables d’environnement individuelles.
Note : Quand vous montez sous forme de volume, Kubernetes met automatiquement à jour les fichiers quand la ConfigMap change (avec un délai de quelques dizaines de secondes à quelques minutes selon la période de synchronisation de kubelet). Avec les variables d’environnement (
env/envFrom), le Pod ne se recharge pas automatiquement — vous devez redémarrer le Pod.
5. Qu’est-ce que Secret et la différence avec ConfigMap
Secret a une structure similaire à ConfigMap mais est destiné aux données sensibles : mots de passe, tokens, clés privées. Les principales différences :
| Critère | ConfigMap | Secret |
|---|---|---|
| Chiffrement | Non | Base64-encodé (pas du vrai chiffrement) |
| Stockage dans etcd | Texte en clair | Peut activer le chiffrement at rest |
Affiché via kubectl get |
Montre les valeurs | Masque les valeurs (affiche seulement base64) |
| Objectif | Configuration classique | Données sensibles |
Note : Secret n’est que du base64-encodé, pas chiffré par défaut. Quiconque ayant la permission
get secretdans le namespace peut décoder. Pour une véritable sécurité, vous devez activer le chiffrement [at rest] dans etcd ou utiliser un gestionnaire de secrets externe (Vault, AWS Secrets Manager) intégré via External Secrets Operator.
6. Créer un Secret
6.1. À partir d’un YAML (il faut encoder en base64 soi-même)
echo -n "P@ssw0rd123" | base64
# UEBzc3cwcmQxMjM=
apiVersion: v1
kind: Secret
metadata:
name: order-service-secret
type: Opaque
data:
DB_PASSWORD: UEBzc3cwcmQxMjM=
Le champ data nécessite des valeurs déjà encodées en base64. Si vous ne voulez pas encoder vous-même, utilisez stringData :
stringData:
DB_PASSWORD: "P@ssw0rd123"
Kubernetes encodera automatiquement lors de la sauvegarde. Utilisez stringData pour une meilleure lisibilité quand vous écrivez des manifests manuellement.
6.2. À partir de la ligne de commande
kubectl create secret generic order-service-secret \
--from-literal=DB_PASSWORD=P@ssw0rd123 \
--from-literal=DB_USERNAME=order_app
7. Utiliser Secret dans un Pod
Le chargement est exactement similaire à ConfigMap :
envFrom:
- secretRef:
name: order-service-secret
ou monter un volume :
volumeMounts:
- name: secret-volume
mountPath: /app/secret
readOnly: true
volumes:
- name: secret-volume
secret:
secretName: order-service-secret
Monter un volume pour un Secret devrait toujours avoir readOnly: true et vous pouvez restreindre les permissions d’accès aux fichiers avec defaultMode si nécessaire :
secret:
secretName: order-service-secret
defaultMode: 0400
8. Intégration avec Spring Boot
Spring Boot lit les variables d’environnement selon la norme de relaxed binding, donc vous n’avez pas besoin de code spécial — il suffit de nommer les variables correctement selon la convention.
envFrom:
- configMapRef:
name: order-service-config
- secretRef:
name: order-service-secret
env:
- name: SPRING_DATASOURCE_USERNAME
valueFrom:
secretKeyRef:
name: order-service-secret
key: DB_USERNAME
- name: SPRING_DATASOURCE_PASSWORD
valueFrom:
secretKeyRef:
name: order-service-secret
key: DB_PASSWORD
Spring Boot mappe automatiquement SPRING_DATASOURCE_USERNAME → spring.datasource.username. Dans le code, vous avez juste besoin de @ConfigurationProperties normale :
@ConfigurationProperties(prefix = "spring.datasource")
public record DataSourceProperties(String url, String username, String password) {
}
@Service
public class PaymentService {
private final DataSourceProperties dataSourceProperties;
public PaymentService(DataSourceProperties dataSourceProperties) {
this.dataSourceProperties = dataSourceProperties;
}
public String describeConnection() {
return "Connexion en tant que %s".formatted(dataSourceProperties.username());
}
}
PaymentService ne sait rien de Kubernetes — il reçoit simplement des valeurs déjà liées par Spring Boot à partir de variables d’environnement fournies par ConfigMap/Secret.
8.1. Tests indépendants du cluster
Vous testez la logique en définissant des variables d’environnement simulées via @TestPropertySource, sans avoir besoin d’un vrai Kubernetes :
@SpringBootTest
@TestPropertySource(properties = {
"spring.datasource.username=order_app",
"spring.datasource.password=test-pass"
})
class PaymentServiceTest {
@Autowired
private PaymentService paymentService;
@Test
void shouldDescribeConnectionUsingBoundProperties() {
assertThat(paymentService.describeConnection())
.isEqualTo("Connexion en tant que order_app");
}
}
Ce test confirme que DataSourceProperties est correctement liée et que PaymentService utilise les bonnes valeurs — complètement découplé de la question de savoir d’où proviennent réellement les valeurs (ConfigMap ou Secret sur le cluster).
PaymentServiceTest > shouldDescribeConnectionUsingBoundProperties() PASSED
9. Mise à jour dynamique sans redémarrage du Pod
Si vous montez ConfigMap/Secret sous forme de volume, les fichiers se mettront à jour automatiquement mais l’application Spring Boot ne relira pas automatiquement sauf si :
- Utilisez Spring Cloud Kubernetes (
spring-cloud-starter-kubernetes-client-config) pour faire du polling sur la ConfigMap et rafraîchir les beans@RefreshScope. - Ou implémentez vous-même un file-watcher qui relit les fichiers montés.
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-kubernetes-client-config</artifactId>
</dependency>
spring.cloud.kubernetes.config.enabled=true
spring.cloud.kubernetes.reload.enabled=true
spring.cloud.kubernetes.reload.mode=polling
Cette configuration active la fonctionnalité Spring Cloud Kubernetes qui interroge automatiquement la ConfigMap selon un cycle régulier et rafraîchit les beans marqués @RefreshScope, sans avoir besoin de redémarrer manuellement le Pod.
Note : Un polling continu consomme des ressources d’API server. Pour les grands clusters, envisagez d’utiliser
spring.cloud.kubernetes.reload.mode=event(watch API) à la place du polling.
10. Erreurs courantes et quand ne pas utiliser
- Mettre des mots de passe dans ConfigMap : ConfigMap n’est pas chiffrée,
kubectl get configmap -o yamlaffiche les valeurs. Utilisez toujours Secret pour les données sensibles. - Oublier de base64-encoder quand vous écrivez
datadans un Secret YAML : cela mène à l’erreurillegal base64 data. UtilisezstringDatasi vous ne voulez pas encoder vous-même. - Changer la ConfigMap mais le Pod ne reçoit pas les nouvelles valeurs : parce que charger via
env/envFromne se recharge pas automatiquement — vous devez fairekubectl rollout restart deployment. - Monter un volume Secret mais oublier
readOnly: true: le container pourrait écraser les fichiers, causant de la confusion lors du débogage. - Utiliser Secret comme un stockage de secrets à long terme : Secret dans Kubernetes est approprié pour la configuration runtime, ne remplace pas un gestionnaire de secrets dédié (Vault, AWS Secrets Manager) quand vous avez besoin de logs d’audit, de rotation automatique, ou d’un versioning strict.
- ConfigMap/Secret trop volumineux : Kubernetes limite la taille des objets à environ 1 MiB (à cause des limites d’etcd). Si vous avez besoin de fichiers de configuration volumineux, envisagez de monter un PersistentVolume ou d’utiliser un init container qui charge depuis du stockage objet.
11. Conclusion
ConfigMap et Secret vous aident à séparer la configuration de l’image, à déployer la même image dans plusieurs environnements (dev, staging, production) en changeant simplement les objets de configuration. Utilisez ConfigMap pour les données publiques, Secret pour les données sensibles — mais n’oubliez pas que Secret n’est encodé que par défaut, pas vraiment chiffré. Avec Spring Boot, vous tirez parti de la relaxed binding pour charger les valeurs via des variables d’environnement sans changer le code métier, et vous pouvez activer Spring Cloud Kubernetes si vous avez besoin de recharger la configuration dynamiquement sans redémarrer le Pod.