Mike Heeren
Voor DevOps engineers die met Kubernetes (k8s) werken is het waarschijnlijk een bekend probleem. Op het cluster staan een aantal Secrets die moeten worden bijgewerkt. Nadat je dit hebt gedaan zie je de logs ineens volstaan met authenticatie-fouten. Waarschijnlijk zijn er een aantal Pods die de wijzigingen nog niet hebben opgepakt, en daardoor nog de oude waarden gebruiken.
Reproduceren situatie
Voordat we naar oplossingen gaan kijken, gaan we de situatie eerst reproduceren. Dit is eenvoudig te doen met het volgende bestand:
reproduction.yaml
---
apiVersion: v1
kind: Secret
metadata:
name: dummy-secret
data:
password: V2VsY29tZTE= # Base64-hash van Welcome1
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx
spec:
replicas: 2
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx
envFrom:
- secretRef:
name: dummy-secret
Met het volgende commando worden de Secret en Deployment op het cluster toegevoegd:
$ kubectl apply -f reproduction.yaml
secret/dummy-secret created
deployment.apps/nginx created
We kunnen nu op één van de Pods die door de Deployment is aangemaakt het wachtwoord opvragen:
$ kubectl get pods
NAME READY STATUS RESTARTS AGE
nginx-7c588d7c4b-dtcqt 1/1 Running 0 76s
nginx-7c588d7c4b-j6dmq 1/1 Running 0 76s
$ kubectl exec nginx-7c588d7c4b-dtcqt -- printenv password
Welcome1
Nu passen we het wachtwoord aan naar “NewP@ssword” door met het onderstaande commando de waarde van password te veranderen naar “TmV3UEBzc3dvcmQ=”:
$ kubectl edit secret dummy-secret
secret/dummy-secret edited
Als we nu nogmaals het wachtwoord in de Pod bekijken, is dit nog steeds ongewijzigd:
$ kubectl exec nginx-7c588d7c4b-dtcqt -- printenv password
Welcome1
Standaardoplossingen
Een van de standaardoplossingen die gebruikt kan worden om dit probleem op te lossen is stakater/Reloader. Dit is echter een oplossing die op het cluster geïnstalleerd moet worden. Bij ons had het echter de voorkeur om niks op het cluster te hoeven installeren, maar het in te bouwen in onze bestaande CI/CD-pipelines.
In deze pipelines hebben we naast kubectl ook jq tot onze beschikking.
Script voor herstarten pods
De stappen die we uit willen voeren zijn relatief eenvoudig:
- Bepalen hoe oud het Secret is;
- Bepalen welke Deployments gebruikmaken van het Secret;
- Bepalen of de Deployment Pods bevat die ouder zijn dan het Secret;
- En zo ja, de Deployment opnieuw uitrollen.
Bepalen hoe oud een Secret is
We gebruiken het tijdstip van het laatste managedFields element om te bepalen wanneer een Secret voor het laatst is aangepast:
$ kubectl get secret dummy-secret --show-managed-fields --output jsonpath='{.metadata.managedFields[-1].time}'
2026-01-28T07:08:35Z
Bepalen welke Deployments gebruikmaken van een Secret
Om te bepalen welke Deployments er zijn die gebruikmaken van het Secret, moeten we alle plekken waarin een Secret toegepast kan worden in een Deployment meenemen:
- Een Secret kan als volume worden gekoppeld aan een Pod.
- Er kunnen losse waarden uit het Secret gekoppeld worden als omgevingsvariabele.
- Alle waarden uit een Secret kunnen gekoppeld worden als omgevingsvariabelen.
Met JQ kunnen we alle Deployments selecteren die aan minimaal een van de bovenstaande voorwaarden voldoen:
$ kubectl get deployment --output json \
| jq --arg secret dummy-secret --raw-output '
[
.items[]
| select(.spec.template.spec.volumes[]?.secret.secretName == $secret),
select(.spec.template.spec.containers[].env[]?.valueFrom.secretKeyRef.name == $secret),
select(.spec.template.spec.containers[].envFrom[]?.secretRef.name == $secret)
| .metadata.name
]
| unique
| join(" ")
'
nginx
Bepalen of de Deployment Pods bevat die ouder zijn dan het Secret
Om de Pods op te halen die bij een Deployment horen, hebben we eerst de “selector” nodig:
$ kubectl get deployment nginx --output json \
| jq --raw-output '.spec.selector.matchLabels
| to_entries
| map("\(.key)=\(.value)")
| join(",")
'
app=nginx
Met deze “selector” kunnen we vervolgens de bijbehorende Pods opvragen. Hier zijn we enkel geïnteresseerd in de starttijd van de oudste Pod:
$ kubectl get pods --selector app=nginx --output jsonpath='{.items[*].status.startTime}' \
| tr ' ' '\n' \
| sort \
| head -n1
2026-01-28T07:03:55Z
Deployment opnieuw uitrollen
Als we deze datums vergelijken, zien we dat het Secret (07:08u) inderdaad is aangepast nadat de oudste Pod (07:03u) van de Deployment is gestart.
Met het volgende commando kan de Deployment opnieuw worden uitgerold, waarmee alle Pods zullen worden herstart:
$ kubectl rollout restart deployment nginx
Alle stappen samenvoegen tot een script
Alle bovenstaande stappen kunnen worden samengevoegd in één script:
update.sh
#!/bin/bash
secret=dummy-secret
echo "Getting last update time of $secret..."
last_update_secret=$(kubectl get secret "$secret" --show-managed-fields --output jsonpath='{.metadata.managedFields[-1].time}')
last_update_secret_epoch=$(date --utc --date="$last_update_secret" +%s)
echo "Last update time: $last_update_secret"
echo
echo "Getting deployments using $secret..."
deployments_using_secret=$(\
kubectl get deployment --output json \
| jq --arg secret "$secret" --raw-output '
[
.items[]
| select(.spec.template.spec.volumes[]?.secret.secretName == $secret),
select(.spec.template.spec.containers[].env[]?.valueFrom.secretKeyRef.name == $secret),
select(.spec.template.spec.containers[].envFrom[]?.secretRef.name == $secret)
| .metadata.name
]
| unique
| join(" ")
' \
)
echo "Deployments using $secret: $deployments_using_secret"
echo
echo "Determining deployments that require restart..."
deployments_to_restart=()
for deployment in $deployments_using_secret; do
echo " - $deployment"
selector=$(\
kubectl get deployment "$deployment" --output json \
| jq --raw-output '.spec.selector.matchLabels
| to_entries
| map("\(.key)=\(.value)")
| join(",")
'\
)
echo " - Selector: $selector"
last_start_time=$(\
kubectl get pods --selector "$selector" --output jsonpath='{.items[*].status.startTime}' \
| tr ' ' '\n' \
| sort \
| head -n1 \
)
last_start_time_epoch=$(date --utc --date="$last_start_time" +%s)
echo " - Oldest pod start time: $last_start_time"
if (( last_start_time_epoch < last_update_secret_epoch )); then
deployments_to_restart+=("$deployment")
echo " - Restart required"
else
echo " - Will not be restarted"
fi
done
echo "Deployments that require restart: ${deployments_to_restart[*]}"
echo
for deployment in "${deployments_to_restart[@]}"; do
echo "Rollout restart of $deployment..."
kubectl rollout restart deployment "$deployment"
Wanneer we dit script uitvoeren, zien we dat de Deployment herstart wordt:
$ . update.sh
Getting last update time of dummy-secret...
Last update time: 2026-01-28T07:08:35Z
Getting deployments using dummy-secret...
Deployments using dummy-secret: nginx
Determining deployments that require restart...
- nginx
- Selector: app=nginx
- Oldest pod start time: 2026-01-28T07:03:55Z
- Restart required
Deployments that require restart: nginx
Rollout restart of nginx...
deployment.apps/nginx restarted
Als we nu opnieuw de waarde van het wachtwoord in de Pod opvragen, zien we de nieuwe waarde terugkomen:
$ kubectl get pods
NAME READY STATUS RESTARTS AGE
nginx-54558995c5-99td7 1/1 Running 0 118s
nginx-54558995c5-c65qc 1/1 Running 0 2m
$ kubectl exec nginx-54558995c5-c65qc -- printenv password
NewP@ssword
Conclusie
Er zijn verschillende manieren waarop dit probleem opgelost kan worden. Wanneer het geen probleem is om zaken op het Kubernetes cluster te installeren, zou je bijv. gebruik kunnen maken van stakater/Reloader.
Wanneer je echter niks op het cluster wilt installeren of het liever oplost in een script wat bijv. vanuit een CI/CD-pipeline kan worden aangeroepen, is ook dit eenvoudig te realiseren met kubectl in combinatie met jq.



