diff --git a/gotenberg/Chart.yaml b/gotenberg/Chart.yaml index cec062d..2be02a3 100644 --- a/gotenberg/Chart.yaml +++ b/gotenberg/Chart.yaml @@ -5,7 +5,7 @@ icon: https://gotenberg.dev/img/logo.png type: application -version: 6.0.0+up8.36.0 +version: 7.0.0+up8.36.0 appVersion: "8.36.0" maintainers: diff --git a/gotenberg/templates/NOTES.txt b/gotenberg/templates/NOTES.txt index 7e77437..5532a23 100644 --- a/gotenberg/templates/NOTES.txt +++ b/gotenberg/templates/NOTES.txt @@ -38,28 +38,45 @@ NO AUTHENTICATION, AND THIS CHART CANNOT ADD ANY and the URL routes make the server fetch whatever URL they are handed. Keep it cluster-internal; do not put an Ingress in front of it without auth. -MEMORY IS THE LIMIT YOU HIT FIRST - Chromium and LibreOffice hold the whole document in memory. With this - release's limit of {{ index $limits "memory" | default "(none set)" }}, a 20-page HTML converts in well under a second, - while a 25 MB HTML is enough to get the container OOM-killed. - How you notice: the caller gets no HTTP error — the connection is dropped - mid-request — and the pod's RESTARTS counter goes up. Confirm with +BIG CONVERSIONS RUN OUT OF TIME BEFORE THEY RUN OUT OF MEMORY + Chromium and LibreOffice hold the whole document in memory, so a conversion + is a short, memory-hungry burst. This release limits the container to - kubectl -n {{ .Release.Namespace }} get pod -l app={{ .Release.Name }}-{{ .Chart.Name }}-deployment -o jsonpath='{.items[*].status.containerStatuses[*].lastState.terminated.reason}' + memory: {{ index $limits "memory" | default "(none set)" }} + ephemeral-storage: {{ index $limits "ephemeral-storage" | default "(none set)" }} - which then prints OOMKilled. Raise gotenberg.resources.requests.memory (the - limit follows at limitFactor x request) or set an explicit limit. + against deliberately smaller requests: the pod reserves little while it + idles and grows into the limits only while converting. -TEMP SPACE IS TIGHT, AND RUNNING OUT LOOKS DIFFERENT - /tmp and /home/gotenberg are emptyDirs, so they count against the container's - ephemeral-storage limit of {{ index $limits "ephemeral-storage" | default "(none set)" }}. Chromium's profile alone takes about 4 MB, - and the uploaded file plus the rendered PDF come on top. - How you notice: the pod does not restart, it disappears — a fresh one takes - its place and the old one is left in status Evicted, with the message naming - ephemeral-storage. Raise gotenberg.resources.requests.ephemeralStorage. + Those limits are set so the 30-second request timeout below is what a + too-large document hits first. An 18 MB HTML converts in about 22 seconds + and stays well inside them; a document heavy enough to exhaust the memory + limit would have run past the 30 seconds anyway, so raising memory further + does not turn a failing conversion into a successful one — splitting the + document does. + + If you do need to change them, set gotenberg.resources.limits.memory and + .limits.ephemeralStorage. Both are set explicitly now, so raising the + matching request no longer drags the limit along at limitFactor x request. + + Should a conversion still hit a limit, the two failures look different and + neither of them reports an HTTP error to the caller: + + - Out of memory: the connection is dropped mid-request and the pod's + RESTARTS counter goes up. Confirm with + + kubectl -n {{ .Release.Namespace }} get pod -l app={{ .Release.Name }}-{{ .Chart.Name }}-deployment -o jsonpath='{.items[*].status.containerStatuses[*].lastState.terminated.reason}' + + which then prints OOMKilled. + + - Out of ephemeral storage: /tmp and /home/gotenberg are emptyDirs and count + against the ephemeral-storage limit — Chromium's profile alone takes about + 4 MB, and the uploaded file plus the rendered PDF come on top. The pod does + not restart, it disappears: a fresh one takes its place and the old one is + left in status Evicted, with the message naming ephemeral-storage. REQUESTS TIME OUT AFTER 30 SECONDS That is Gotenberg's own default, and since the chart passes no flags it - cannot be changed here. Large conversions get close: the 25 MB document - above still needed 19 seconds once it had memory enough to finish. Callers - that need longer must split the document instead. + cannot be changed here. This is the ceiling large conversions actually meet: + an 18 MB HTML needs about 22 seconds, and a 25 MB one does not finish inside + the 30. Callers that need longer must split the document instead. diff --git a/gotenberg/templates/_helpers.tpl b/gotenberg/templates/_helpers.tpl index 6b62fe9..6aedb55 100644 --- a/gotenberg/templates/_helpers.tpl +++ b/gotenberg/templates/_helpers.tpl @@ -263,7 +263,8 @@ instead of a container without any resource accounting. {{- define "gotenberg.effectiveResources" -}} {{- $defaults := dict "limitFactor" 3 - "requests" (dict "cpu" 0.1 "memory" "512Mi" "ephemeralStorage" "10Mi") -}} + "requests" (dict "cpu" 0.1 "memory" "512Mi" "ephemeralStorage" "10Mi") + "limits" (dict "memory" "3Gi" "ephemeralStorage" "256Mi") -}} {{- $merged := fromYaml (include "gotenberg.defaultedDict" (dict "defaults" $defaults "given" . "path" "gotenberg.resources")) -}} {{- include "gotenberg.resources" $merged -}} {{- end -}} diff --git a/gotenberg/values.yaml b/gotenberg/values.yaml index c24d95b..b39b5cb 100644 --- a/gotenberg/values.yaml +++ b/gotenberg/values.yaml @@ -54,7 +54,27 @@ gotenberg: # limitFactor x the request, an explicitly set limit always wins, and a # CPU limit is only set when configured explicitly under "limits". limitFactor: 3 + # The requests stay small on purpose: gotenberg idles at a few dozen MB, + # so reserving conversion-sized capacity on the node would waste it + # around the clock. Conversions are short bursts, and the limits below + # cover the burst instead (issue #106). requests: cpu: 0.1 memory: 512Mi ephemeralStorage: 10Mi + # Set explicitly rather than left to limitFactor x request, because the + # computed limits (1536Mi / 30Mi) were too small for real documents. + # Measured with the official image, HTML converted via Chromium: + # - 18 MB HTML: OOMKilled at 2Gi and 2560Mi, converts at 3Gi in ~22s + # (21 MB PDF). 12 MB HTML already OOM-kills at the old 1536Mi. + # - Raising memory alone only moves the failure: with 3Gi memory and + # the old 30Mi ephemeral limit, /tmp peaked at 40 MB on a single + # conversion and at ~125 MB over a short series, and the pod was + # evicted ("ephemeral local storage usage exceeds the total limit of + # containers 30Mi") instead of OOM-killed. + # Memory beyond 3Gi buys nothing: gotenberg's own request timeout is 30s + # and the chart passes no flags to change it, so a document that needs + # more memory than this has already run out of time. + limits: + memory: 3Gi + ephemeralStorage: 256Mi