Merge pull request #499 from stefanprodan/deploy-traffic-gen

deploy: Add warm-cache CronJob
This commit is contained in:
Stefan Prodan
2026-06-18 11:50:13 +03:00
committed by GitHub
8 changed files with 190 additions and 19 deletions
+24 -16
View File
@@ -49,24 +49,32 @@ test-container:
version-set:
@next="$(TAG)" && \
current="$(VERSION)" && \
/usr/bin/sed -i '' "s/$$current/$$next/g" pkg/version/version.go && \
/usr/bin/sed -i '' "s/tag: $$current/tag: $$next/g" charts/podinfo/values.yaml && \
/usr/bin/sed -i '' "s/tag: $$current/tag: $$next/g" charts/podinfo/values-prod.yaml && \
/usr/bin/sed -i '' "s/appVersion: $$current/appVersion: $$next/g" charts/podinfo/Chart.yaml && \
/usr/bin/sed -i '' "s/version: $$current/version: $$next/g" charts/podinfo/Chart.yaml && \
/usr/bin/sed -i '' "s/podinfo:$$current/podinfo:$$next/g" kustomize/deployment.yaml && \
/usr/bin/sed -i '' "s/podinfo:$$current/podinfo:$$next/g" deploy/webapp/frontend/deployment.yaml && \
/usr/bin/sed -i '' "s/podinfo:$$current/podinfo:$$next/g" deploy/webapp/backend/deployment.yaml && \
/usr/bin/sed -i '' "s/podinfo:$$current/podinfo:$$next/g" deploy/bases/frontend/deployment.yaml && \
/usr/bin/sed -i '' "s/podinfo:$$current/podinfo:$$next/g" deploy/bases/backend/deployment.yaml && \
/usr/bin/sed -i '' "s/podinfo:$$current/podinfo:$$next/g" deploy/bases/database/statefulset-primary.yaml && \
/usr/bin/sed -i '' "s/podinfo:$$current/podinfo:$$next/g" deploy/bases/database/deployment-replica.yaml && \
/usr/bin/sed -i '' "s/podinfo:$$current/podinfo:$$next/g" deploy/bases/database/cronjob-rollup-daily.yaml && \
/usr/bin/sed -i '' "s/podinfo:$$current/podinfo:$$next/g" deploy/bases/database/cronjob-rollup-weekly.yaml && \
/usr/bin/sed -i '' "s/podinfo:$$current/podinfo:$$next/g" deploy/bases/database/cronjob-backup-daily.yaml && \
/usr/bin/sed -i '' "s/$$current/$$next/g" timoni/podinfo/values.cue && \
perl -i -pe "s/\Q$$current\E/$$next/g" pkg/version/version.go && \
perl -i -pe "s/tag: \Q$$current\E/tag: $$next/g" charts/podinfo/values.yaml && \
perl -i -pe "s/tag: \Q$$current\E/tag: $$next/g" charts/podinfo/values-prod.yaml && \
perl -i -pe "s/appVersion: \Q$$current\E/appVersion: $$next/g" charts/podinfo/Chart.yaml && \
perl -i -pe "s/version: \Q$$current\E/version: $$next/g" charts/podinfo/Chart.yaml && \
perl -i -pe "s/\Q$$current\E/$$next/g" timoni/podinfo/values.cue && \
grep -rl "podinfo:$$current" deploy kustomize | xargs perl -i -pe "s/\Qpodinfo:$$current\E/podinfo:$$next/g" && \
echo "Version $$next set in code, deployment, module, chart and kustomize"
prep-release:
@branch="$$(git rev-parse --abbrev-ref HEAD)" && \
if [ "$$branch" != "master" ]; then \
echo "Error: prep-release must be run from the master branch (current: $$branch)"; \
exit 1; \
fi && \
git pull origin master && \
next="$(TAG)" && \
if [ "$$next" = "latest" ]; then \
next="$$(echo $(VERSION) | awk -F. '{ printf "%d.%d.%d", $$1, $$2+1, 0 }')"; \
fi && \
git checkout -b release-$$next && \
$(MAKE) version-set TAG=$$next && \
git commit -am "Release $$next" && \
git push origin release-$$next && \
gh pr create --title "Release $$next" --body "Prepare for $$next release" --base master --head release-$$next
release:
git tag -s -m $(VERSION) $(VERSION)
git push origin $(VERSION)
@@ -0,0 +1,68 @@
apiVersion: batch/v1
kind: CronJob
metadata:
name: warm-cache
spec:
# Runs every 5 minutes
schedule: "*/5 * * * *"
concurrencyPolicy: Forbid
successfulJobsHistoryLimit: 1
failedJobsHistoryLimit: 1
jobTemplate:
spec:
# Cleanup after 1 hour
ttlSecondsAfterFinished: 3600
backoffLimit: 3
template:
metadata:
labels:
app.kubernetes.io/name: warm-cache
app.kubernetes.io/part-of: frontend
spec:
serviceAccountName: frontend
restartPolicy: OnFailure
initContainers:
- name: warm-cache-init
image: ghcr.io/stefanprodan/podinfo:6.13.0
imagePullPolicy: IfNotPresent
command:
- /bin/sh
- /scripts/warm-cache-init.sh
env:
- name: FRONTEND_URL
value: "http://frontend"
resources:
limits:
cpu: 100m
memory: 32Mi
requests:
cpu: 10m
memory: 16Mi
volumeMounts:
- name: scripts
mountPath: /scripts
containers:
- name: warm-cache
image: ghcr.io/stefanprodan/podinfo:6.13.0
imagePullPolicy: IfNotPresent
command:
- /bin/sh
- /scripts/warm-cache.sh
env:
- name: FRONTEND_URL
value: "http://frontend"
resources:
limits:
cpu: 100m
memory: 32Mi
requests:
cpu: 10m
memory: 16Mi
volumeMounts:
- name: scripts
mountPath: /scripts
volumes:
- name: scripts
configMap:
name: warm-cache-script
defaultMode: 0755
+2 -1
View File
@@ -21,6 +21,7 @@ spec:
labels:
app.kubernetes.io/name: frontend
spec:
serviceAccountName: frontend
containers:
- name: frontend
image: ghcr.io/stefanprodan/podinfo:6.13.0
@@ -39,7 +40,7 @@ spec:
- ./podinfo
- --port=9898
- --port-metrics=9797
- --level=info
- --level=debug
- --backend-url=http://backend:9898/echo
- --cache-server=tcp://cache:6379
env:
+1 -1
View File
@@ -7,7 +7,7 @@ spec:
apiVersion: apps/v1
kind: Deployment
name: frontend
minReplicas: 1
minReplicas: 2
maxReplicas: 4
metrics:
- type: Resource
+9 -1
View File
@@ -1,7 +1,15 @@
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- serviceaccount.yaml
- service.yaml
- deployment.yaml
- hpa.yaml
- cronjob-warm-cache.yaml
configMapGenerator:
- name: warm-cache-script
files:
- scripts/warm-cache.sh
- scripts/warm-cache-init.sh
options:
disableNameSuffixHash: true
+13
View File
@@ -0,0 +1,13 @@
#!/bin/sh
set -e
# Init step for the warm-cache job.
# Calls the frontend in verbose mode so the full connection, request and
# response debug data (DNS, TCP, headers, timings) is dumped to the logs.
FRONTEND="${FRONTEND_URL:-http://frontend}"
echo "Dumping debug data for ${FRONTEND}/api/info"
# -v writes the verbose debug data to stderr, redirect it to stdout so it
# lands in the container logs together with the response body.
curl -v "${FRONTEND}/api/info" 2>&1
+69
View File
@@ -0,0 +1,69 @@
#!/bin/sh
set -e
# This is a simulation of a cache warming process.
# It fetches the frontend info, stores it in the cache under the job pod
# hostname and reads it back to verify the round-trip.
#
# Log lines mimic the Log4j 2 default PatternLayout:
# %d{yyyy-MM-dd HH:mm:ss,SSS} [%thread] %-5level %logger{36} - %msg
LOGGER="com.stefanprodan.podinfo.WarmCache"
THREAD="main"
log() {
level="$1"
shift
# millisecond precision timestamp; fall back gracefully if %N is unsupported
ts="$(date '+%Y-%m-%d %H:%M:%S,%N' 2>/dev/null)"
case "$ts" in
*%N|*,) ts="$(date '+%Y-%m-%d %H:%M:%S'),000" ;;
*) ts="$(echo "$ts" | cut -c1-23)" ;;
esac
printf '%s [%s] %-5s %s - %s\n' "$ts" "$THREAD" "$level" "$LOGGER" "$*"
}
FRONTEND="${FRONTEND_URL:-http://frontend}"
KEY="$(hostname)"
START="$(date +%s)"
log INFO "Fetching info from ${FRONTEND}/api/info"
# Compact the (pretty-printed) JSON onto a single line so each log event
# stays on one line, as Log4j renders them.
INFO="$(curl -fsS "${FRONTEND}/api/info" | tr -d '\n' | sed 's/ */ /g')"
log INFO "Info: ${INFO}"
# Log each field of the info response on its own line. The /api/info payload
# is a flat JSON object of string values, so we can split it without jq.
echo "$INFO" | sed 's/^{//; s/}$//' | tr ',' '\n' | while IFS= read -r field; do
field="$(echo "$field" | sed 's/^[[:space:]]*//; s/[[:space:]]*$//; s/"//g')"
if [ -n "$field" ]; then
log DEBUG "info ${field}"
fi
done
log INFO "Writing info to cache key ${KEY}"
curl -fsS -X POST -H "Content-Type: application/json" -d "${INFO}" "${FRONTEND}/cache/${KEY}"
# Verify the cached value for 1 minute, polling every 10 seconds.
attempt=1
while [ "$attempt" -le 6 ]; do
log INFO "Reading cache key ${KEY} (attempt ${attempt}/6)"
CACHED="$(curl -fsS "${FRONTEND}/cache/${KEY}")"
if [ "${INFO}" = "${CACHED}" ]; then
log INFO "Cache warm verified: cached value matches info"
else
log ERROR "Cache warm failed: cached value does not match info"
log ERROR "Expected: ${INFO}"
log ERROR "Got: ${CACHED}"
exit 1
fi
attempt=$((attempt + 1))
if [ "$attempt" -le 6 ]; then
sleep 10
fi
done
log INFO "Cache warm finished in $(( $(date +%s) - START ))s"
@@ -0,0 +1,4 @@
apiVersion: v1
kind: ServiceAccount
metadata:
name: frontend