ConfigMap und Secret in Kubernetes: Konfiguration aus dem Image auslagern
1. Übersicht
Beim Verpacken einer Anwendung in ein Container-Image sollten Sie keine harten Werte wie Datenbank-URLs oder API-Schlüssel ins Image einbauen. Dieser Artikel hilft Ihnen, ConfigMap und Secret in Kubernetes zu verstehen — zwei Objekte zum Auslagern von Konfigurationen aus dem Image, deren Erstellung und Einbindung in Pods via Umgebungsvariablen oder Volumes, sowie die Integration mit Spring-Boot-Anwendungen. Danach können Sie selbstbewusst ein Deployment konfigurieren, das dynamisch Config liest, ohne das Image jedes Mal neu zu bauen, wenn sich ein Wert ändert.
2. Was ist ConfigMap?
ConfigMap ist ein Objekt, das Konfigurationsdaten in Key-Value-Form speichert, ohne Verschlüsselung, für nicht vertrauliche Daten: Servicenamen, Feature-Flags, Properties-Dateien…
Die Daten in einer ConfigMap existieren unabhängig von den Pods — Sie können die ConfigMap ändern, ohne das Image neu zu bauen. Sie müssen nur den Pod neu starten (oder einen Reload-Mechanismus verwenden, wenn die Anwendung ihn unterstützt).
3. ConfigMap erstellen
3.1. Aus einer YAML-Datei
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
Hier enthält data zwei Arten: einfache Keys (APP_ENV, LOG_LEVEL) und einen Key mit gesamtem Dateiinhalt (application.properties). Anwenden mit:
kubectl apply -f order-service-config.yaml
3.2. Aus Literals oder vorhandenen Dateien
kubectl create configmap order-service-config \
--from-literal=APP_ENV=production \
--from-file=application.properties=./config/application.properties
Dieser Befehl erstellt die ConfigMap direkt über die Kommandozeile ohne YAML-Datei — praktisch für CI/CD-Skripte.
4. ConfigMap in Pods verwenden
4.1. Laden als Umgebungsvariablen
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 lädt alle Keys aus der ConfigMap als Umgebungsvariablen mit gleichem Namen. Der Container liest APP_ENV, LOG_LEVEL wie normale Umgebungsvariablen.
Wenn Sie nur einen bestimmten Key auswählen möchten:
env:
- name: APP_ENVIRONMENT
valueFrom:
configMapKeyRef:
name: order-service-config
key: APP_ENV
So können Sie die Umgebungsvariable im Container (APP_ENVIRONMENT) anders benennen als den ursprünglichen Key in der ConfigMap (APP_ENV).
4.2. Laden als Volume
volumeMounts:
- name: config-volume
mountPath: /app/config
volumes:
- name: config-volume
configMap:
name: order-service-config
Jeder Key in der ConfigMap wird zu einer Datei in /app/config (z.B. /app/config/application.properties). Diese Methode eignet sich, wenn die Anwendung ganze Konfigurationsdateien lesen muss, statt einzelner Umgebungsvariablen.
Wichtig: Wenn Sie ConfigMap als Volume mounten, wird Kubernetes automatisch die Dateien aktualisieren, wenn sich die ConfigMap ändert (mit einer Verzögerung von einigen Sekunden bis Minuten, abhängig vom kubelet sync period). Bei Umgebungsvariablen (
env/envFrom) lädt der Pod nicht automatisch neu — Sie müssen den Pod neu starten.
5. Secret: Was ist das und wie unterscheidet es sich von ConfigMap?
Secret hat eine ähnliche Struktur wie ConfigMap, ist aber für vertrauliche Daten gedacht: Passwörter, Tokens, Private Keys. Die Hauptunterschiede:
| Kriterium | ConfigMap | Secret |
|---|---|---|
| Verschlüsselung | Nein | Base64-kodiert (nicht echte Verschlüsselung) |
| Speicherung in etcd | Klartext | Kann Verschlüsselung at-rest aktiviert sein |
Anzeige via kubectl get |
Werte sichtbar | Werte verborgen (nur base64 sichtbar) |
| Zweck | Normale Konfiguration | Vertrauliche Daten |
Wichtig: Secret kodiert nur base64, verschlüsselt nicht standardmäßig. Jeder mit
get secret-Berechtigung im Namespace kann dekodieren. Für echte Sicherheit müssen Sie Verschlüsselung at rest im etcd aktivieren oder einen externen Secret-Manager (Vault, AWS Secrets Manager) über External Secrets Operator integrieren.
6. Secret erstellen
6.1. Aus YAML (Werte müssen selbst base64-kodiert sein)
echo -n "P@ssw0rd123" | base64
# UEBzc3cwcmQxMjM=
apiVersion: v1
kind: Secret
metadata:
name: order-service-secret
type: Opaque
data:
DB_PASSWORD: UEBzc3cwcmQxMjM=
Das Feld data erfordert base64-kodierte Werte. Wenn Sie nicht selbst kodieren möchten, verwenden Sie stringData:
stringData:
DB_PASSWORD: "P@ssw0rd123"
Kubernetes kodiert automatisch beim Speichern. stringData ist besser lesbar, wenn Sie Manifeste manuell schreiben.
6.2. Über Kommandozeile
kubectl create secret generic order-service-secret \
--from-literal=DB_PASSWORD=P@ssw0rd123 \
--from-literal=DB_USERNAME=order_app
7. Secret in Pods verwenden
Das Laden funktioniert genauso wie bei ConfigMap:
envFrom:
- secretRef:
name: order-service-secret
oder Volume mounten:
volumeMounts:
- name: secret-volume
mountPath: /app/secret
readOnly: true
volumes:
- name: secret-volume
secret:
secretName: order-service-secret
Das Mounten von Secrets als Volumes sollte immer mit readOnly: true gesetzt werden und Sie sollten Dateizugriffsrechte mit defaultMode einschränken, falls nötig:
secret:
secretName: order-service-secret
defaultMode: 0400
8. Integration mit Spring Boot
Spring Boot liest Umgebungsvariablen nach dem Relaxed-Binding-Standard, also brauchen Sie keinen speziellen Code — Sie müssen nur die Variablen nach der Konvention benennen.
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 mapped automatisch SPRING_DATASOURCE_USERNAME → spring.datasource.username. Im Code brauchen Sie nur normal @ConfigurationProperties:
@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 "Connecting as %s".formatted(dataSourceProperties.username());
}
}
PaymentService weiß nichts von Kubernetes — es empfängt einfach Werte, die Spring Boot aus Umgebungsvariablen gebunden hat, die von ConfigMap/Secret bereitgestellt wurden.
8.1. Tests unabhängig vom Cluster
Sie testen die Logik, indem Sie Umgebungsvariablen via @TestPropertySource simulieren, ohne echtes 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("Connecting as order_app");
}
}
Dieser Test bestätigt, dass DataSourceProperties richtig gebunden wird und PaymentService die richtigen Werte nutzt — vollständig entkoppelt davon, ob die Werte von echten ConfigMap oder Secret im Cluster kommen.
PaymentServiceTest > shouldDescribeConnectionUsingBoundProperties() PASSED
9. Dynamische Updates ohne Pod-Neustart
Wenn Sie ConfigMap/Secret als Volume mounten, werden die Dateien automatisch aktualisiert, aber Spring Boot liest nicht automatisch neu, es sei denn Sie:
- Verwenden Spring Cloud Kubernetes (
spring-cloud-starter-kubernetes-client-config), das ConfigMap pollt und@RefreshScope-Beans refreshed. - Oder implementieren selbst einen File-Watcher, der gemountete Dateien erneut liest.
<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
Diese Konfiguration aktiviert das Polling von ConfigMap-Änderungen durch Spring Cloud Kubernetes nach einem bestimmten Zyklus und refresht mit @RefreshScope gekennzeichnete Beans — kein manueller Pod-Neustart nötig.
Wichtig: Kontinuierliches Polling verbraucht API-Server-Ressourcen. Bei großen Clustern sollten Sie
spring.cloud.kubernetes.reload.mode=event(Watch-API) statt Polling erwägen.
10. Häufige Fehler und wann Sie ConfigMap/Secret nicht verwenden sollten
- Passwörter in ConfigMap speichern: ConfigMap wird nicht verschlüsselt,
kubectl get configmap -o yamlzeigt Werte offen. Verwenden Sie immer Secret für vertrauliche Daten. - Base64-Kodierung in
datavon Secret-YAML vergessen: führt zuillegal base64 data-Fehler. Verwenden SiestringData, wenn Sie nicht selbst kodieren möchten. - ConfigMap geändert, aber Pod erhält neuen Wert nicht: da
env/envFromnicht automatisch neu laden — Sie müssenkubectl rollout restart deploymentausführen. - Secret-Volume gemountet, aber
readOnly: truevergessen: Container könnte Dateien überschreiben, was zu Verwirrung beim Debugging führt. - Secrets als Langzeitgeheimnisspeicher verwenden: Secrets in Kubernetes eignen sich für Runtime-Konfiguration, können aber einen dedizierten Secret-Manager (Vault, AWS Secrets Manager) nicht ersetzen, wenn Sie Audit-Logs, automatische Rotation oder strenge Versionierung brauchen.
- ConfigMap/Secret zu groß: Kubernetes limitiert die Objektgröße auf etwa 1 MiB (wegen etcd-Limits). Für große Config-Dateien sollten Sie die Verwendung von Volume-Mounts von PersistentVolumes oder Init-Containern erwägen, die aus Object Storage laden.
11. Fazit
ConfigMap und Secret helfen Ihnen, Konfigurationen aus dem Image auszulagern und das gleiche Image für mehrere Umgebungen (dev, staging, production) bereitzustellen — indem Sie einfach die Config-Objekte wechseln. ConfigMap ist für öffentliche Daten, Secret für vertrauliche Daten — aber denken Sie daran, dass Secret standardmäßig nur kodiert, nicht echte Verschlüsselung bietet. Mit Spring Boot nutzen Sie Relaxed Binding, um Werte über Umgebungsvariablen zu laden, ohne den Business-Code zu ändern, und Sie können Spring Cloud Kubernetes aktivieren, um Konfigurationen dynamisch zu aktualisieren, ohne Pods neu zu starten.