Iteratively create Kubernetes CronJob by refactoring your Helm templates

Helm is a really powerful tool as it package your Kubernetes app and enables you to deploy and rollback it easily. However you could come to have many templates for deployment, cronjob, configMap, etc, and some of them are really similar. Here is a way to refactore your code.

My issue: Create 32 similar crons in a Kubernetes cluster

Last week, I found myself in front of a refactoring issue. I had a Kubernetes cluster managed with Helm and I had 32 crons to transform into CronJob resources and to create and deploy. All those crons are very similar as they are Symfony commands: they are all based on the app image which is launched with a specific command such as

bin/console my:first:cron

I did not want to write 32 Helm templates for two reasons:

  • it is hard to maintain. Let’s imagine I want to add a configMap that applies to all of them, I will need to do it 32 times without error or without forgetting one
  • it is really boring to write.

One Helm template to rule them all

Here is a way to do it with only one short file.

First write one cronjob.yaml using helm :

apiVersion: batch/v1beta1
kind: CronJob
metadata:
name: "my-first-cron"
labels: {{ include "kubernetes.labels" . | indent 4 }}
spec:
schedule: "*/10 * * * *"
successfulJobsHistoryLimit: 0
jobTemplate:
spec:
template:
spec:
restartPolicy: Never
containers:
- image: "{{ .Values.cronjob.repository }}:{{ .Values.image.tag }}"
name: "my-first-cron"
args:
- sh
- -c
- |
sleep 2s
"./bin/console --env=prod my:first:comand"
sleep 30
imagePullPolicy:{{ .Values.image.pullPolicy }}
ports:
- name: http
containerPort: 80
protocol: TCPn

And then use the `range` instruction for iterative resource creation:

{{- range $job, $val := .Values.cronjob.crons }}
apiVersion: batch/v1beta1
kind: CronJob
metadata:
name: {{ .name }}
labels: {{ include "kubernetes.labels" $ | indent 4 }}
spec:
schedule: {{ .schedule | quote}}
successfulJobsHistoryLimit: 0
jobTemplate:
spec:
template:
spec:
restartPolicy: Never
containers:
- image: "{{ $.Values.cronjob.repository }}:{{ $.Values.image.tag }}"
name: {{ .name }}
args:
- sh
- -c
- |
sleep 2s
"./bin/console --env=prod {{ .command}}"
sleep 30
imagePullPolicy: {{ $.Values.image.pullPolicy }}
ports:
- name: http
containerPort: 80
protocol: TCP
---
{{- end}}

Finally modify your values.yaml the following way:

>cronjob:
repository: XXXX
crons:
"0":
name: my-first-cron
command: my:first:cron
schedule: "*/10 * * * *"
"1":
name: my-second-cron
command: my:second:cron
schedule: « */3 * * * *"{{- end}}

Here are two things I wish to outline:

  • Usually to use values stored in your values.yaml file, you do it this way :
    .Values.XXX.XXX.
    However, inside the `range` instruction, you need to call it this way
    $.Values.XXX.XXX
  • The 3 dashes at the end are important otherwise, only the last cron of your list will be launched.

I hope this article helps you, don’t hesitate to leave us feedbacks!

If you want to know more about CronJob resource, here is the documentation link or about Helm, here is the Getting Started page.Nouveau call-to-action

Aurore Malherbes

Aurore Malherbes

Aurore is the CTO of Padok. Previously architect developer in the world of mobile, she decided to move to the ops world, with one mission: put the develpment team in the best conditions with a fast dev environment and a reliable production.

What do you think? Leave your comments here !