Skip to main content

FORGE colima sizing — kad CI pada na OOM u docker buildu (MC #107134/#107139)

FORGE colima sizing — kad CI pada na OOM u docker buildu

Status: AKTIVNO · Nastalo: 2026-08-14, MC #107134/#107139 · Vrijedi za: self-hosted azdo agent bilko-forge-1 na FORGE ([email protected])

Simptom

Build na main pada u fazi Build (linux/amd64 → ACR):

npm error signal SIGKILL
ResourceExhausted: cannot allocate memory

Sve prethodne faze prolaze — Kotlin compile, integrationTest, Kotest su zeleni. Pada isključivo korak koji gradi bilko-web docker image.

Nije isto što i MC #104886 („Reached heap limit"). Tamo je JS heap dosegao svoj strop; ovdje host/VM nema RAM-a, pa dizanje --max-old-space-size pogoršava stvar.

Uzrok

colima VM na FORGE-u je imao 6 GiB / 4 CPU na mašini od 256 GB. Uz to se bilko-web gradi kao docker buildx --platform linux/amd64, dakle QEMU emulacija na ARM-u, što dodatno diže potrošnju.

Popravka

ssh [email protected]
export PATH=/opt/homebrew/bin:$PATH
# backup postojeceg configa prije izmjene
cp ~/.colima/default/colima.yaml ~/.colima/default/colima.yaml.bak-$(date +%Y%m%d)
colima stop && colima start --cpu 8 --memory 24

Provjera na oba mjesta — ne vjerovati jednom:

colima list                        # -> aarch64, 8 CPU, 24GiB
docker info | grep -i "total memory"   # -> Total Memory: 23.42GiB

Kako znati da je stvarno riješeno

Ne po tome što VM ima više RAM-a, nego po kontrolisanom prije/poslije na istoj grani:

Build sha colima Ishod
1065, 1066, 1067, 1070 4f9224af 6 GiB svi pali na fazi 2, ~30 min
1072 e6f3a526 24 GiB faza 2 prošla — nekeširano, "Compiled successfully in 4.2min", bez SIGKILL
1073 e6f3a526 24 GiB faza 2 „prošla" za 16 s — korak je bio CACHED, nije izvršen

Primarni dokaz je build 1072, ne 1073. Ovo je ispravka nakon nezavisne peer-verifikacije (2026-08-15): 1073 je bio prvi citiran, ali njegov OOM-osjetljiv korak (npm run build --workspace=@bilko/web) je bio registry-cache pogodak (#12 CACHED) — cijeli job 16 sekundi. Sam po sebi ne dokazuje ništa o memoriji. Keširan je bio zato što je 1072 (isti sha, u redu 8 minuta ranije, ništa za ponovnu upotrebu) taj isti korak već odradio nekeširano i uspješno pod 24 GiB.

Pouka koja se ponavlja: „faza prošla" nije isto što i „faza se izvršila". Provjeri je li korak stvarno radio ili je uzet iz keša prije nego ga navedeš kao dokaz.

Dokaz: ~/system/evidence/107134/colima-fix-proven-build1073-2026-08-14.md + peer-verifikacija ~/system/evidence/107139/peer-verify-transcript-2026-08-15.md

Kontrola confounda — RIJEŠENA EKSPERIMENTOM (2026-08-15)

1072 je na sha e6f3a526, a padovi su na 4f9224af — različit sha je bio stvaran confound.

Prvo sam ga pokušao odbraniti argumentom: git diff 4f9224af..e6f3a526 dira 113 fajlova, nijedan CI/build config, samo aplikativni kod, 8103 dodane linije — dakle posao je teži, ne lakši. Cross-vendor recenzent (gpt-5.6-sol) je to odbio, s pravom: to je rezonovanje, nije kontrola.

Kontrola je onda izvedena — isti sha, mijenja se samo memorija:

commit 4f9224af   (tačno onaj koji je 4× pao pod 6 GiB)
colima: 8 CPU / 24GiB   (docker info: Total Memory 23.42GiB)
docker buildx build --platform linux/amd64 -f apps/web/Dockerfile ...
#27 DONE 372.1s          exit=0          real 6m43.219s
grep -ci "sigkill|cannot allocate memory|ResourceExhausted|killed"  →  0

Izvedeno van CI-ja, direktno na FORGE-u u scratch worktreeu. Namjerno se nije puštalo kroz main pipeline: to bi deployalo stari aplikativni kod u živo okruženje, a PR/branch putanja uopšte ne gradi ovaj image pa ne može poslužiti kao test.

Dokaz: ~/system/evidence/107139/same-sha-control-build-2026-08-15.log + same-sha-control-preflight-2026-08-15.txt

Ni to nije bila prava kontrola — druga runda recenzije

Cross-vendor recenzent je i ovo odbio, opet s pravom:

„The same-SHA run is not a memory-only control because CPU doubled and the restart removed orphan containers."

Tačno. Promjena je bila 6 GiB / 4 CPU → 24 GiB / 8 CPU, a restart je uz to pomeo 3 orphan Testcontainers postgres kontejnera i lumiscare-redis. Tri varijable, ne jedna. Prvi „kontrolni" run je dokazao da novo okruženje radi — ne i šta je od toga bilo presudno.

Izolacija — treći run, i ovaj je konačan

Isti sha, CPU ostavljen na novoj vrijednosti (8), orphan kontejneri već pometeni, spuštena samo memorija na 6 GiB:

colima: aarch64, 8 CPU, 6GiB
#27 307.3  npm error signal SIGKILL
ERROR: ... "npm run build --workspace=@bilko/web" did not complete successfully: cannot allocate memory
ResourceExhausted
real 5m38.358s   exit=102

Dokaz: ~/system/evidence/107139/mem6gib-cpu8-isolation-build-2026-08-15.log

Konačna tabela — dvije tačke koje se razlikuju SAMO u memoriji

sha CPU RAM orphani ishod
4f9224af 4 6 GiB prisutni pao ×4 (CI 1065/1066/1067/1070), ~30 min
4f9224af 8 6 GiB pometeni pao — SIGKILL, cannot allocate memory, 5m38s
4f9224af 8 24 GiB pometeni prošao — 6m43s, bez SIGKILL

Srednji red isključuje obje konkurentske hipoteze odjednom: CPU je na novoj, višoj vrijednosti i ne spašava; orphani su počišćeni i ne spašavaju. Ostaje memorija.

Zaključak: memorija je bila vezujuće ograničenje. Dokazano izolacijom, ne argumentom.

FORGE je poslije eksperimenta vraćen na 8 CPU / 24 GiB i to je provjereno (colima list + docker infoTotal Memory: 23.42GiB).

Pouka o metodi — tri kruga, tri različite greške

  1. Prvo sam kao dokaz naveo run čiji je korak bio keširan (#12 CACHED, 16 s) — „faza prošla" nije „faza se izvršila".
  2. Zatim sam confound branio argumentom („113 fajlova čini posao težim") umjesto kontrolom.
  3. Zatim sam kontrolu nazvao „samo memorija" iako su se promijenile tri stvari.

Moj vlastiti peer-verifikator je dao 12/12 PASS i propustio sve troje. Model drugog vendora je oborio dva kruga zaredom i oba puta bio u pravu. Kad se više varijabli promijeni zajedno, jedini izlaz je pustiti isti ulaz kroz postavke koje se razlikuju u jednoj stvari.

Dvije zamke koje su nas koštale

  1. PR build ne dokazuje ništa o OOM-u. Build 1071 (PR 338) je bio zelen poslije popravke i djelovao kao dokaz — ali PR putanja uopšte ne gradi web image. Prethodni pad istog PR-a (1069) bio je na integrationTest baseline check, ne na memoriji. Dokaz je isključivo main build.
  2. Jedan paralelni slot org-wide. Ako ručno pokreneš build dok drugi već radi na istoj grani, drugi čeka — build 1073 je izgubio 21 minutu čekajući 1072. Prije ručnog pokretanja provjeri šta već teče. azdo-pr-requeue.sh kancelira build koji je running.

Trajni pravac (nije urađeno)

Web image se gradi emulirano. Organizacija već ima plaćen Microsoft-hosted paralelni slot (isHosted:true, PurchasedCount:1, totalMinutes:1800) koji stoji prazan — svih 11 jobova je zakucano na bilko-selfhosted. Premještanje samo image-build joba na hosted agenta daje nativni amd64 (bez QEMU) i skida kontenciju, bez ijednog dolara.

Ograničenje: bilko-demo-pg je IP-ograničen na FORGE, pa jobovi koji diraju bazu (Flyway, E2E) ne mogu na hosted agenta bez izmjene firewalla.

Detalji i cijene: ~/system/evidence/107134/ci-capacity-options-2026-08-14.md

Metodološka napomena

Prije ove popravke napravljene su tri pogrešne atribucije uzastopno (Sentry source mape, dupli typecheck pod QEMU, pa „nešto u aplikaciji"). Sve tri izmjene su bile korisne same po sebi, ali nijedna nije bila uzrok. Uzrok je nađen tek kad je neko pogledao koliko RAM-a VM uopšte ima — a to je stajalo neprovjereno jer je ranije prijavljeno „nemam SSH pristup FORGE-u", što nije bilo tačno: makinja@ radi.