複数の似た CronJob をなるべく DRY に書く (外部ツール無し)


背景

Kubernetes の CronJob は 1 リソースにつき 1 つのスケジュールを記述するようになっている。同じような spec でたくさんのスケジュールを組みたいときはスケジュール分の CronJob リソースが必要になる。

が、同じような spec を何度も書きたくない。

対策

YAML のエイリアス機能と Kubernetes の kind: List を使ってなるべく DRY に書く。

---
_too_long_cronjob_base_spec: &cronjob_base_spec
  concurrencyPolicy: Forbid
  successfulJobsHistoryLimit: 3
  jobTemplate:
    spec: &job_base_spec
      parallelism: 1
      completions: 1
      backoffLimit: 1
      template:
        spec: &pod_base_spec
          initContainers:
          - name: init-hogehoge
            imagePullPolicy: IfNotPresent
            image: busybox
            command:
            - sh
            - -c
            - |
              echo hogehoge
            volumeMounts:
            - name: fugafuga
              mountPath: /a/b/c/d
          containers:
          - &main_container_base_spec
            name: main-hogehoge
            image: myimage:mytag
            imagePullPolicy: IfNotPresent
            command: ['/bin/bash', '-c']
            args: ["echo thisisdefaultcommand"]
            env:
            - name: MY_POD_NAME
              valueFrom:
                fieldRef:
                  fieldPath: metadata.name
            - name: MY_SECRET
              valueFrom:
                secretKeyRef:
                  name: foo
                  key: bar
            volumeMounts:
            - name: fugafuga
              mountPath: /a/b/c/d
            lifecycle:
              postStart:
                exec:
                  command:
                  - sh
                  - -c
                  - |
                    my_logging_start
              preStop:
                exec:
                  command:
                  - sh
                  - -c
                  - |
                    my_logging_end
          restartPolicy: Never
          volumes:
          - name: fugafuga
            emptyDir: {}

apiVersion: v1
kind: List
items:
- apiVersion: batch/v1beta1
  kind: CronJob
  metadata:
    name: my-job-example-1
  spec:
    <<: *cronjob_base_spec
    schedule: "15 */3 * * *"
    jobTemplate:
      spec:
        <<: *job_base_spec
        template:
          spec:
            <<: *pod_base_spec
            containers:
            - <<: *main_container_base_spec
              args: ["my-command-1.sh"]
- apiVersion: batch/v1beta1
  kind: CronJob
  metadata:
    name: my-job-example-2
  spec:
    <<: *cronjob_base_spec
    schedule: "30 */2 * * *"
    successfulJobsHistoryLimit: 1
    jobTemplate:
      spec:
        <<: *job_base_spec
        template:
          spec:
            <<: *pod_base_spec
            containers:
            - <<: *main_container_base_spec
              args: ["my-command-2.sh args1 args2"]
- apiVersion: batch/v1beta1
  kind: CronJob
  metadata:
    name: my-job-example-3
  spec:
    <<: *cronjob_base_spec
    concurrencyPolicy: Allow
    schedule: "* * * * *"
    jobTemplate:
      spec:
        <<: *job_base_spec
        template:
          spec:
            <<: *pod_base_spec
            containers:
            - <<: *main_container_base_spec
              args: ["my-command-3.sh"]

コマンドとスケジュールくらいしか違わない場合はまぁまぁ DRY に書ける。

制限

YAML 的にはキーごとに後勝ちでマージしているだけなので、例えばここに書いたようなエイリアスの付け方だと、env 配列に要素を追加したいといった場合は配列の全要素を書かねばならず、追加分だけ書くようなことはできない。