Jobs und CronJobs
Ein Container, der eine Aufgabe erledigt und sich beendet, gehört nicht in ein Deployment. Ein Deployment startet ihn endlos neu, und genau daher kommen die meisten CrashLoopBackOff-Meldungen über einen völlig gesunden Container.
Eine einmalige Aufgabe
apiVersion: batch/v1
kind: Job
metadata:
name: migrate
spec:
backoffLimit: 3
template:
spec:
restartPolicy: OnFailure
containers:
- name: migrate
image: registry.itsh.dev/<dein-slug>/myapp:1.0.0
command: ["./migrate"]
securityContext:
allowPrivilegeEscalation: false
runAsNonRoot: true
seccompProfile:
type: RuntimeDefault
capabilities:
drop: ["ALL"]
resources:
requests:
cpu: 100m
memory: 128Mi
limits:
cpu: 500m
memory: 256MirestartPolicy muss OnFailure oder Never sein, Always ist für einen Job nicht zulässig. backoffLimit begrenzt die Wiederholungen, bevor der Job als fehlgeschlagen gilt.
Der securityContext ist derselbe Block wie bei jedem anderen Pod, und er steht aus demselben Grund da: Ohne ihn gibt Kubernetes beim Apply vier Warnungen aus. Siehe Versionen und Standardwerte.
Geplante Arbeit
apiVersion: batch/v1
kind: CronJob
metadata:
name: nightly-report
spec:
schedule: "0 3 * * *"
timeZone: "Europe/Berlin"
concurrencyPolicy: Forbid
successfulJobsHistoryLimit: 3
failedJobsHistoryLimit: 3
jobTemplate:
spec:
template:
spec:
restartPolicy: OnFailure
containers:
- name: report
image: registry.itsh.dev/<dein-slug>/myapp:1.0.0
command: ["./report"]
securityContext:
allowPrivilegeEscalation: false
runAsNonRoot: true
seccompProfile:
type: RuntimeDefault
capabilities:
drop: ["ALL"]Normales fünfstelliges Cron, anders als die sechsstellige Form, die der Datenbank-Operator für geplante Backups verwendet.
Drei Felder solltest du bewusst setzen:
timeZone. Ohne die Angabe läuft der Zeitplan in UTC, und das war nicht gemeint, wenn du0 3 * * *in Ortszeit gedacht hast.concurrencyPolicy: Forbidlässt einen Lauf aus, wenn der vorherige noch läuft. Der Standard lässt sie sich überlappen, was ein Report oder ein Aufräumjob selten will.- Die History-Limits. Fertige Pods bleiben liegen, damit du ihre Logs lesen kannst. Behalte ein paar; viele machen den Namespace unübersichtlich.
Was sie kosten
Ein Job wird nach den Requests seines Pods abgerechnet, und zwar nur so lange, wie dieser Pod tatsächlich läuft, minutengenau. Ein Job mit fünf Minuten Laufzeit kostet fünf Minuten, keine Stunde.
Fertige Pods, die als History liegen bleiben, werden nicht abgerechnet. Sie laufen nicht, kosten also nichts, und ein paar für die Logs aufzuheben ist gratis.
Damit ist geplante Arbeit das Günstigste auf der Plattform, und deshalb ist ein CronJob die bessere Antwort als ein Service, der schläft: Ein CronJob kostet zwischen den Läufen nichts, ganz ohne die Vorbehalte von Scale-to-Zero. Siehe Scale-to-Zero.
Wenn ein Job scheitert
kubectl get jobs zeigt die Abschlüsse. Die Pods bleiben liegen, also:
kubectl get pods --selector=job-name=migrate
kubectl logs job/migrateEin CronJob, der nie zu laufen scheint, hat meistens einen Zeitplan in der falschen Zeitzone, oder concurrencyPolicy: Forbid lässt Läufe aus, weil der vorherige nie fertig wurde.
Wie es weitergeht
- Secrets und Konfiguration, um Zugangsdaten zu übergeben
- Abrechnung und Kontingente, wie Laufzeit gezählt wird