Optymalizacja kosztów Kubernetes z GitLab CI: gdzie ucieka budżet
Pięć dźwigni, które realnie obniżają rachunek za CI/CD na Kubernetes: right-sizing zasobów, autoskalowanie runnerów do zera, węzły spot i cache.
KUBERNETES · GITLAB · CI-CD · FINOPS · DEVOPS
Pipeline'y działają, klaster się skaluje, deploye wchodzą same. A potem przychodzi faktura za chmurę i ktoś pyta, dlaczego samo CI kosztuje tyle co połowa produkcji.
Kubernetes nie jest drogi z natury. Drogie jest CI, którego nikt nie mierzy: runnery stojące na zawołanie 24/7, joby proszące o 8 GB RAM „na wszelki wypadek”, obrazy ważące po dwa giga i buildy bez cache. Poniżej pięć dźwigni, które stosujemy, żeby ten rachunek zbić — bez spowalniania zespołu.
TL;DR
- Right-sizing: requesty ustawione pod realne zużycie, nie pod „a nuż zabraknie” — najszybsza wygrana
- Autoskalowanie do zera: runnery na Kubernetes executor + cluster autoscaler, zero płacenia za bezczynność
- Węzły spot: efemeryczne CI świetnie pasuje do tanich maszyn spot/preemptible
- Cache i mniejsze obrazy: krótszy build to mniej zużytych minut — a więc mniej pieniędzy
- Pomiar per projekt: bez kosztu rozbitego na zespoły optymalizujesz po omacku
Gdzie naprawdę ucieka budżet
Zanim zaczniesz ciąć, warto wiedzieć, na co idą pieniądze. W środowiskach CI/CD, które przejmowaliśmy, koszt prawie zawsze rozkładał się tak samo:
| Źródło kosztu | Typowy objaw |
|---|---|
| Przewymiarowane requesty | Job rezerwuje 8 vCPU, zużywa 1,5 — reszta węzła leży odłogiem |
| Runnery 24/7 | Stała pula maszyn płaci za noce i weekendy, gdy nikt nie commituje |
| Brak cache | Każdy build ściąga zależności od zera — minuty, za które płacisz |
| Grube obrazy | Pull obrazu trwa dłużej niż sam build; mnożone przez setki jobów |
Klasyczny przypadek: 200 vCPU rozłożone na stałej puli hostów, z których w nocy realnie pracuje kilka procent. Płacisz za szczyt, korzystasz ze średniej.
Dźwignia 1: right-sizing zasobów
Najczęstsze marnotrawstwo to requesty ustawione „na oko, z zapasem”. Kubernetes rezerwuje dokładnie tyle, ile poprosisz — niezależnie od tego, ile job faktycznie zużyje. Przewymiarowany request to pojemność, za którą płacisz i której nikt nie używa.
Co robimy w praktyce:
- Mierzymy realne zużycie jobów (Prometheus / metrics-server) zamiast zgadywać
- Ustawiamy request blisko mediany zużycia, limit z rozsądnym zapasem
- Egzekwujemy to per projekt przez ResourceQuota i LimitRange — opisaliśmy ten mechanizm w osobnym wpisie: twarde limity zasobów per projekt w GitLab CI
To zwykle najszybsza wygrana: nie zmienia infrastruktury, a uwalnia pojemność, którą do tej pory blokowały rezerwacje na wyrost.
Dźwignia 2: autoskalowanie runnerów do zera
Docker runnery na stałych hostach płacą rachunek niezależnie od tego, czy ktoś commituje. GitLab na Kubernetes executor tworzy pody jobów na żądanie — a w połączeniu z cluster autoscalerem dokłada i zdejmuje węzły zależnie od kolejki.
- Brak jobów → pula schodzi do zera (albo do małego minimum na ciepłe godziny)
- Skok commitów → autoscaler dokłada węzły na czas pracy, potem je zwalnia
- Płacisz za realnie wykonane joby, nie za gotowość maszyn w nocy
Kosztem jest cold start — pierwszy job po ciszy czeka kilkadziesiąt sekund na węzeł. Dla zdecydowanej większości zespołów to opłacalna wymiana.
Dźwignia 3: węzły spot dla CI
CI to obciążenie efemeryczne: job albo się uda, albo zostanie ponowiony. To idealny kandydat na węzły spot / preemptible, które bywają kilkukrotnie tańsze od on-demand.
Warunek jest jeden — pipeline musi znosić wywłaszczenie węzła. W praktyce oznacza to idempotentne joby i włączone retry. Produkcji na spotach nie stawiamy, ale buildy i testy przenosimy tam bez obaw. Pula on-demand zostaje jako bufor na wypadek braku spotów.
Dźwignia 4: cache i mniejsze obrazy
Tu koszt i czas to ta sama rzecz: każda minuta builda to minuta, za którą płacisz. Dwie zmiany dają najwięcej:
- Cache zależności — Maven, npm, pip czy warstwy Dockera nie muszą lecieć od zera przy każdym buildzie
- Mniejsze obrazy — multi-stage build i chude bazy skracają pull i przyspieszają start joba
W jednej z naszych migracji multi-stage build i porządki w warstwach ścięły rozmiar obrazów o 87%, a czas builda spadł z około 6 minut do ~30 sekund. Krótszy build to nie tylko wygoda — to wprost mniej zużytych minut runnera. Opisaliśmy to w case study migracji 60 aplikacji na Kubernetes.
Dźwignia 5: mierz koszt per projekt
Nie zoptymalizujesz czegoś, czego nie widzisz. Dopóki rachunek za CI jest jedną liczbą, nie wiadomo, który zespół generuje gros kosztu i gdzie cięcie ma sens.
- Namespace i ResourceQuota per projekt dają naturalny punkt rozliczenia zużycia
- Prometheus + Grafana pokazują CPU/RAM i czas jobów w rozbiciu na zespoły
- Z tym obrazem optymalizujesz tam, gdzie boli — a nie tam, gdzie akurat patrzysz
Od czego zacząć
Kolejność, którą rekomendujemy: najpierw zmierz (dźwignia 5 i right-sizing), bo to nic nie kosztuje i od razu pokazuje skalę marnotrawstwa. Potem autoskalowanie i spot — tam są największe oszczędności strukturalne. Cache i obrazy domykają temat i przy okazji przyspieszają pracę zespołu.
Prowadzimy CI/CD na Kubernetes w produkcji i regularnie przejmujemy środowiska, w których rachunek wymknął się spod kontroli. Jeśli Twoje CI kosztuje więcej, niż powinno, zobacz, jak budujemy i optymalizujemy pipeline'y albo umów bezpłatną konsultację — zaczynamy od audytu i konkretnych liczb.