deploy: Add warm-cache CronJob

- Introduced a new `warm-cache` CronJob to periodically preload the cache.
- Added supporting scripts (`warm-cache.sh`, `warm-cache-init.sh`) to manage the cache warm-up process.
- Configured `ServiceAccount` for the frontend deployment to enhance security.
- Adjusted HPA minimum replicas from 1 to 2 for better availability.
- Set frontend deployment to use debug log level.

Signed-off-by: Stefan Prodan <stefan.prodan@gmail.com>
This commit is contained in:
Stefan Prodan
2026-06-18 11:44:39 +03:00
parent 7a9ce5c9bb
commit 72c51ed43b
7 changed files with 166 additions and 3 deletions
@@ -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