bentopdf: new chart for the PDF toolbox (1.0.0+up2.8.7)
BentoPDF computes entirely in the visitor's browser; the container only serves static files through nginx. The chart is therefore close to cyberchef - same nginx-unprivileged base, uid 101, port 8080, probes on "/" - with four differences that shaped the design. Runtime config.json. The one setting reachable at runtime is the tool list. It is a static file in the middle of the web root, so it is mounted by subPath: a directory mount would hide the whole application. subPath mounts are never refreshed by the kubelet, which makes the checksum/config annotation (#82) load-bearing rather than decorative. bentopdf.config is passed through verbatim, like searxng's settings, so a key upstream adds tomorrow is a values change and not a chart change. Three env switches that would kill the pod. PORT, DISABLE_IPV6 and ROBOTS_NOINDEX are implemented as "sed -i" against files in the image, run under "set -e" in /docker-entrypoint.d/, and the base image's entrypoint aborts when one of them fails. With readOnlyRootFilesystem: true the pod therefore never starts instead of ignoring the switch - measured in k3d as CrashLoopBackOff, exit 1, "sed: can't create temp file '/etc/nginx/nginx.confXXXXXX': Read-only file system". None of the three is offered as a first-class value; values.yaml names the failure picture and the escape hatch (env block plus readOnlyRootFilesystem: false). Writable paths were measured, not copied from the upstream chart. /etc/nginx/tmp holds the pid file and /var/cache/nginx is where nginx creates its temp directories at startup - without it the master aborts with 'mkdir() "/var/cache/nginx/client_temp" failed'. /var/run is deliberately not mounted: the running pod proves it is unnecessary. Everything else about BentoPDF is a build-time argument and out of reach for any chart; values.yaml says so explicitly rather than leaving it to be discovered. Two accepted consequences are named there too: some tools fetch WASM modules from a public CDN at runtime, and the served pages carry a canonical link to the vendor's site. Fixes #174
This commit is contained in:
14 files changed
+1049
No files matched your search
@@ -0,0 +1,120 @@
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: {{ .Release.Name }}-{{ .Chart.Name }}-deployment
|
||||
labels:
|
||||
app: {{ .Release.Name }}-{{ .Chart.Name }}-deployment
|
||||
spec:
|
||||
replicas: {{ include "bentopdf.replicas" (dict "scaleDown" .Values.scaleDown "replicas" .Values.bentopdf.replicas) }}
|
||||
selector:
|
||||
matchLabels:
|
||||
app: {{ .Release.Name }}-{{ .Chart.Name }}-deployment
|
||||
template:
|
||||
metadata:
|
||||
name: {{ .Release.Name }}-{{ .Chart.Name }}-deployment
|
||||
labels:
|
||||
app: {{ .Release.Name }}-{{ .Chart.Name }}-deployment
|
||||
annotations:
|
||||
# Hash of the rendered ConfigMap: a changed config.json changes the pod
|
||||
# template and thus rolls the pods. Mandatory here rather than nice to
|
||||
# have - config.json is a subPath mount, which the kubelet never
|
||||
# refreshes, so without this annotation a changed tool list would stay
|
||||
# invisible until someone restarted the pods for an unrelated reason.
|
||||
checksum/config: {{ include (print $.Template.BasePath "/bentopdf.configmap.yaml") . | sha256sum }}
|
||||
spec:
|
||||
automountServiceAccountToken: false
|
||||
securityContext:
|
||||
{{- include "bentopdf.podSecurityContext" .Values.bentopdf.securityContext | nindent 8 }}
|
||||
containers:
|
||||
- name: {{ .Release.Name }}-{{ .Chart.Name }}-deployment
|
||||
# The "-simple" variant on purpose: it is built from the same
|
||||
# Dockerfile as ghcr.io/alam00000/bentopdf, the difference being the
|
||||
# SIMPLE_MODE build argument, which strips the marketing surface of
|
||||
# the commercial site. That has no place on an internal instance.
|
||||
image: "ghcr.io/alam00000/bentopdf-simple:{{ .Chart.AppVersion }}"
|
||||
imagePullPolicy: IfNotPresent
|
||||
{{- if .Values.bentopdf.env }}
|
||||
env:
|
||||
{{- range $value := .Values.bentopdf.env }}
|
||||
{{- if $value.value }}
|
||||
- name: {{ $value.name | quote }}
|
||||
value: {{ tpl $value.value $ | quote }}
|
||||
{{- else if $value.valueFrom }}
|
||||
{{- if $value.valueFrom.secretKeyRef }}
|
||||
- name: {{ $value.name | quote }}
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: {{ tpl $value.valueFrom.secretKeyRef.name $ | quote }}
|
||||
key: {{ tpl $value.valueFrom.secretKeyRef.key $ | quote }}
|
||||
{{- else if $value.valueFrom.configMapKeyRef }}
|
||||
- name: {{ $value.name | quote }}
|
||||
valueFrom:
|
||||
configMapKeyRef:
|
||||
name: {{ tpl $value.valueFrom.configMapKeyRef.name $ | quote }}
|
||||
key: {{ tpl $value.valueFrom.configMapKeyRef.key $ | quote }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
securityContext:
|
||||
{{- include "bentopdf.containerSecurityContext" .Values.bentopdf.securityContext | nindent 12 }}
|
||||
volumeMounts:
|
||||
- name: config
|
||||
mountPath: /usr/share/nginx/html/config.json
|
||||
subPath: config.json
|
||||
readOnly: true
|
||||
# The two writable paths below are what keeps
|
||||
# readOnlyRootFilesystem: true possible. Both are image facts, not
|
||||
# user configuration, so neither is a value - and both are here
|
||||
# because a run without them failed, not because the upstream
|
||||
# chart mounts them.
|
||||
- name: nginx-tmp
|
||||
# The nginx.conf puts its pid file at /etc/nginx/tmp/nginx.pid.
|
||||
mountPath: /etc/nginx/tmp
|
||||
- name: nginx-cache
|
||||
# NOT optional: nginx creates its temp directories at startup and
|
||||
# the image ships /var/cache/nginx without them. Missing this
|
||||
# mount, the master process aborts before serving anything, with
|
||||
# [emerg] mkdir() "/var/cache/nginx/client_temp" failed
|
||||
# (30: Read-only file system)
|
||||
mountPath: /var/cache/nginx
|
||||
# /var/run is deliberately NOT mounted. The upstream chart mounts
|
||||
# it, but this image keeps its pid file under /etc/nginx/tmp, and a
|
||||
# run with only the two mounts above serves / and /config.json with
|
||||
# 200. An emptyDir nobody writes to is a claim the chart cannot
|
||||
# back up.
|
||||
ports:
|
||||
# 8080 is the image's EXPOSE and the port its nginx.conf listens
|
||||
# on. It is not a value, because a value here would lie: the image
|
||||
# switch that moves the port (PORT) rewrites nginx.conf with sed
|
||||
# and therefore cannot run with a read-only root filesystem at all
|
||||
# - see the comment in values.yaml. Moving the port means changing
|
||||
# the chart, on purpose.
|
||||
#
|
||||
# The port NAME is load-bearing and must not be renamed: the
|
||||
# Service selects it by name and so do all three probes.
|
||||
- containerPort: 8080
|
||||
protocol: TCP
|
||||
name: http
|
||||
# Probes, resources and both security contexts go through the
|
||||
# null-safe helpers (#99): an erased block means "chart defaults
|
||||
# apply" instead of a vanished probe or an unhardened container.
|
||||
# Never wrap these in an "if" - a guard hides the erased case
|
||||
# completely instead of rendering a visible null.
|
||||
livenessProbe:
|
||||
{{- include "bentopdf.livenessProbe" .Values.bentopdf.livenessProbe | nindent 12 }}
|
||||
readinessProbe:
|
||||
{{- include "bentopdf.readinessProbe" .Values.bentopdf.readinessProbe | nindent 12 }}
|
||||
startupProbe:
|
||||
{{- include "bentopdf.startupProbe" .Values.bentopdf.startupProbe | nindent 12 }}
|
||||
resources:
|
||||
{{- include "bentopdf.effectiveResources" .Values.bentopdf.resources | nindent 12 }}
|
||||
volumes:
|
||||
- name: config
|
||||
configMap:
|
||||
name: "{{ .Release.Name }}-{{ .Chart.Name }}-configmap"
|
||||
- name: nginx-tmp
|
||||
emptyDir: {}
|
||||
- name: nginx-cache
|
||||
emptyDir: {}
|
||||
restartPolicy: Always
|
||||
Reference in new issue
Block a user