summaryrefslogtreecommitdiff
path: root/f3s/shuriken/helm-chart/templates/cronjob.yaml
blob: 538255ee18c888900b41856527fa67613b5505aa (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
# shuriken: nightly regeneration of the irregular.ninja and alt.irregular.ninja
# static photo albums. The container entrypoint runs `shuriken --generate
# --config <conf>` once per /configs/*.conf (alphabetical), writing each site
# into /data/shuriken.sh/<site>/dist on the shared NFS export.
#
# Design:
#   - concurrencyPolicy: Forbid -- a long generate run (large photo library,
#     single image job) must not stack a second worker on top of it; shuriken's
#     staging directories would race and the NFS write load would double.
#   - backoffLimit: 0 -- a failed nightly run surfaces in the Job history and
#     gets retried by the next night's schedule; spamming retries would just
#     re-burn ImageMagick CPU on a persistent failure (e.g. NFS down).
#   - activeDeadlineSeconds: 6h -- caps a runaway first-run backfill of a large
#     library; steady state is far shorter.
#   - runAsUser: 0 -- the syncthing source tree is mode 750 root:wheel on the
#     NFS server, and the dist output is written as root to match the existing
#     irregular.ninja layout.
#   - Single image job by default (entrypoint --image-jobs 1); raise via the
#     SHURIKEN_IMAGE_JOBS env if a faster one-off run is needed.
apiVersion: batch/v1
kind: CronJob
metadata:
  name: shuriken
  namespace: services
spec:
  # 04:00 local daily -- off-peak, and doesn't collide with the beets-art
  # noon sweep. timeZone is GA in k8s 1.27+; k3s 1.32 supports it.
  schedule: "0 4 * * *"
  timeZone: Europe/Sofia
  concurrencyPolicy: Forbid
  startingDeadlineSeconds: 300
  successfulJobsHistoryLimit: 3
  failedJobsHistoryLimit: 3
  jobTemplate:
    spec:
      backoffLimit: 0
      activeDeadlineSeconds: 21600
      template:
        spec:
          restartPolicy: Never
          initContainers:
            - name: nfs-check
              image: busybox:stable
              command:
                - sh
                - -c
                - |
                  test -f /mnt/shuriken.sh/.nfs-sentinel || (
                    echo "ERROR: NFS sentinel missing at /mnt/shuriken.sh/.nfs-sentinel"
                    echo "refusing to start; node likely has NFS unmounted"
                    echo "pod would otherwise write into a stale local-XFS shadow"
                    exit 1
                  )
              volumeMounts:
                - name: data
                  mountPath: /mnt
                  readOnly: true
          containers:
            - name: shuriken
              image: registry.lan.buetow.org:30001/shuriken:0.14.0
              imagePullPolicy: Always
              command: ["/bin/sh", "-c"]
              args:
                - |
                  set -e
                  # Hold the generation/sync mutex for the WHOLE multi-site run
                  # so the shuriken-sync CronJob can't publish mid-generation
                  # (and vice versa). flock auto-releases if the pod dies, so a
                  # crash never leaves a stale lock. Blocking acquire: if a sync
                  # is mid-publish, wait for it (it's short) then generate.
                  exec flock /data/shuriken.sh/.lock shuriken-entrypoint
              env:
                # Default 1 image job (single-threaded); override here if a
                # faster one-off run is acceptable. The entrypoint passes this
                # to shuriken as --image-jobs.
                - name: SHURIKEN_IMAGE_JOBS
                  value: "1"
                # ImageMagick 7 is Q16-HDRI and built with OpenMP. Pin OpenMP
                # to a single thread: the pod is cpu-limited to 1 core anyway,
                # and extra resample threads would only multiply the HDRI pixel
                # buffer's memory pressure (the OOM trigger for large PNGs).
                - name: OMP_NUM_THREADS
                  value: "1"
              resources:
                requests:
                  cpu: 100m
                  memory: 256Mi
                limits:
                  cpu: "1"
                  # 2 Gi: the ImageMagick policy (baked into the image) caps
                  # the in-RAM pixel cache at 512 MiB and spills oversized
                  # decodes to the /tmp disk cache, so this headroom covers the
                  # working set + bash + the HDRI buffers of one photo without
                  # the pod being OOM-killed on a multi-megapixel source image.
                  memory: 2Gi
              securityContext:
                allowPrivilegeEscalation: false
                runAsUser: 0
                runAsGroup: 0
              volumeMounts:
                - name: data
                  mountPath: /data
                - name: configs
                  mountPath: /configs
                  readOnly: true
                - name: tmp
                  mountPath: /tmp
          volumes:
            - name: data
              persistentVolumeClaim:
                claimName: shuriken-data-pvc
            - name: configs
              configMap:
                name: shuriken-config
            # Disk-backed scratch for ImageMagick's disk pixel cache (the
            # policy spills oversized HDRI decodes here instead of OOMing).
            - name: tmp
              emptyDir:
                sizeLimit: 8Gi