feat(docker): prekompresja gzip plików statycznych - #730
Merged
Conversation
Nginx w bpp-deploy ma `gzip_static on` wlaczone od dawna, ale ta dyrektywa
nie znaczy "kompresuj statyki" — znaczy "wyslij x.js.gz, JESLI istnieje".
W obrazie nie bylo ani jednego .gz (3429 plikow, 0 sztuk), wiec nginx cicho
spadal na `gzip on` i kompresowal plotly.min.js (4,4 MB) od nowa przy kazdym
cache-missie. Brak objawow: strona dziala, Content-Encoding jest, rozni sie
tylko CPU brzegu i poziom kompresji (w locie 5, offline 9).
Dwa kroki, bo jeden nie wystarczal:
1. R16 w bpp_base/Dockerfile — gzip -9 na staticroot.baked zaraz po
collectstatic. Zmierzone: 1813 plikow, 2 s buildu, +12,5 MB do obrazu.
2. entrypoint-appserver.sh — gzip na $STATIC_ROOT/CACHE po `manage.py
compress`. Ten krok jest konieczny, bo bare.html pakuje bundle.js (786 KB)
w {% compress js %}, czyli przegladarka pobiera CACHE/js/output.<hash>.js
— plik, ktory POWSTAJE DOPIERO W RUNTIME i ktorego build-time nie widzi.
Trzy najwieksze assety (plotly 4,4 MB, three 1,9 MB, cytoscape 886 KB) sa
odwrotnie: ladowane przez {% static %} POZA blokami compress, czyli wprost
z .baked.
Dlaczego build-time, a nie krok runtime po stronie wdrozenia: nginx serwujac
x.js.gz NIE porownuje mtime z x.js. Nieaktualny .gz = wieczne serwowanie
starego JS-a wszystkim przegladarkom, bez sladu w logach. Generowanie .gz
w tym samym kroku co plik zrodlowy usuwa to z definicji; w CACHE/ dodatkowo
chroni content-hash w nazwie.
Zabezpieczenia: -size +1000c odpowiada gzip_min_length 1000 z konfiguracji
nginksa; kompresujemy wylacznie formaty tekstowe (woff/png/webp sa juz
skompresowane); drugi find kasuje .gz, ktory wyszedl nie mniejszy od
oryginalu. Blad w entrypoincie NIE eskaluje (skrypt leci z `sh -e`) —
degraduje sie miekko do gzip w locie, ale loguje glosno.
Zweryfikowane na prawdziwym drzewie z opublikowanego obrazu: exit 0 pod
set -e, 1813 plikow, integralnosc `gzip -dc | cmp` na probce OK, guard
rozmiaru sprawdzony podlozonym plikiem niescisliwym, entrypoint przetestowany
w trzech sciezkach (CACHE z plikami / brak CACHE / find z bledem).
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01J35fM3toviqNDsGxRg6P1c
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
Nginx w
bpp-deploymagzip_static onwłączone od dawna. Ta dyrektywa nie znaczy „kompresuj statyki" — znaczy „wyślijx.js.gz, jeśli istnieje".W obrazie nie było ani jednego
.gz(zmierzone na202608.1399rc5: 3429 plików, 0 sztuk). Nginx cicho spadał nagzip oni kompresowałplotly.min.js(4,4 MB) od nowa przy każdym cache-missie.Objawu nie ma żadnego: strona działa,
Content-Encodingjest, różni się tylko CPU brzegu i poziom kompresji (w locie 5, offline 9).Rozwiązanie — dwa kroki, bo jeden nie wystarczał
1.
R16wdocker/bpp_base/Dockerfile—gzip -9nastaticroot.bakedzaraz pocollectstatic.Zmierzone: 1813 plików, 2 s buildu, +12,5 MB do obrazu.
2.
docker/appserver/entrypoint-appserver.sh— gzip na$STATIC_ROOT/CACHEpomanage.py compress.Drugi krok jest konieczny, bo
bare.htmlpakujebundle.js(786 KB) w{% compress js %}— przeglądarka pobieraCACHE/js/output.<hash>.js, plik, który powstaje dopiero w runtime i którego build-time nie widzi. Trzy największe assety są odwrotnie:plotly.min.js(4,4 MB),three-bundle.js(1,9 MB) icytoscape-bundle.js(886 KB) są ładowane przez{% static %}poza blokami{% compress %}, czyli wprost z.baked.Dlaczego build-time, a nie krok po stronie wdrożenia
Nginx serwując
x.js.gznie porównuje mtime zx.js. Nieaktualny.gz= wieczne serwowanie starego JS-a wszystkim przeglądarkom, bez śladu w logach; operator zgłasza to jako „zaktualizowałem i nic się nie zmieniło".Generowanie
.gzw tym samym kroku co plik źródłowy usuwa problem z definicji — nie ma okna, w którym mogłyby się rozjechać. WCACHE/dodatkowo chroni content-hash w nazwie (zmiana treści = nowa nazwa, nie nadpisanie).Zabezpieczenia
-size +1000codpowiadagzip_min_length 1000z konfiguracji nginksa.woff/png/webpsą już skompresowane,.gztylko zajmowałby miejsce.findkasuje.gz, który wyszedł nie mniejszy od oryginału (nginx serwowałby większy plik klientom deklarującym gzip). Na obecnym drzewie takich przypadków jest 0 — to zabezpieczenie na przyszłe assety.sh -e) — degraduje się miękko do gzipu w locie, ale loguje głośno, żeby regresja nie była niewidoczna.Weryfikacja
Fragmenty wykonane na prawdziwym drzewie z opublikowanego obrazu, dokładnie w postaci, w jakiej wykona je Docker:
exit 0podset -e, 1813 plików.gz;gzip -dc | cmpna losowej próbce — zgodne bajt w bajt;bundle.js.gznietknięty) — żeby nie był martwym kodem;findz błędem — nigdzie nie wywraca startu.Kompresja kluczowych assetów:
plotly.min.js4558732 → 1331125 B,three-bundle.js1942818 → 424346 B,bundle.js805055 → 226887 B.Nie budowałem pełnego obrazu (kilkuminutowy build z yarn/uv) — pierwszy build w CI potwierdzi resztę.
Stage
testserver(dev) celowo pominięty:gzip_staticto funkcja nginksa, a dev serwuje statyki Django.Powiązane
Sparowany z iplweb/bpp-deploy#31, który zapisuje dowody i werdykt w sprawie brotli/zstd (odrzucone — trzy blokady po stronie obrazu CRS). Tamten PR nie zmienia zachowania; ten włącza
gzip_staticrealnie.🤖 Generated with Claude Code
https://claude.ai/code/session_01J35fM3toviqNDsGxRg6P1c