Kapsułki do kawy i GitLab CI — jak uprościłem pipeline dla całej organizacji
Jeśli kiedykolwiek używałeś ekspresu na kapsułki, wiesz jak to działa: wkładasz kapsułkę, naciskasz guzik i masz kawę. Nie musisz wiedzieć, jak dokładnie działa zaworek, jaka jest temperatura wody ani skąd bierze się ciśnienie. Ekspres robi swoje, kapsułka dostarcza składniki — ty dostajesz wynik. Dokładnie tak samo zacząłem myśleć o GitLab CI — i tak powstał wzorzec, który uprościł pipeline dla całej organizacji.
Jeśli masz kilkanaście projektów w organizacji, to prędzej czy później natrafisz na ten problem: każdy projekt ma swój .gitlab-ci.yml, każdy trochę inny, każdy z innymi błędami. Ktoś zapomniał dodać yamllint, ktoś inny ściągnął regułę shellcheka bo “przeszkadzała”, a jeszcze ktoś inny ma pipeline napisany dwa lata temu przez osobę, która już tu nie pracuje.
Utrzymanie tego to koszmar. Zmiana jednej reguły = ręczna aktualizacja w 15 projektach.
Tu dochodzimy do sedna. Każdy job w systemie to hermetyczna kapsułka — zbiór plików, który wie wszystko o sobie:
common/jobs/yamllint/├── main.yml # Definicja joba — kiedy się odpala, jakie zmienne├── script.sh.yml # Co robi├── validate.sh.yml # Czy ma wszystko, czego potrzebuje├── before_script.sh.yml├── after_script.sh.yml└── README.md
Ta kapsułka nie wie nic o projekcie, który ją “pije”. Nie wie, czy to Ansible, czy Docker, czy Terraform. Po prostu odpala yamllint i sprawdza pliki YAML.
Jeśli zmienię coś w kapsułce yamllint, zmiana wchodzi do wszystkich projektów automatycznie — przy następnym odpaleniu pipeline.
common/ to wspólna podstawa — joby, które każdy projekt dostaje gratis: sprawdzanie Conventional Commits, linting YAML-i, shellcheck dla skryptów, wersjonowanie przez semantic-release.
pipelines/ to szablony per technologia — tu żyje logika specyficzna dla Ansible, OpenTofu, image buildera i reszty.
Nie każdy projekt przechodzi przez wszystkie — joby mają reguły, kiedy się odpalają. yamllint chodzi tylko gdy zmieniły się pliki *.yml. Deployment tylko na tagach v*.*.*.
GitLab CI ma mechanizm o nazwie !reference, który pozwala “wstrzyknąć” listę poleceń z innego miejsca w YAML-u. Dzięki temu kapsułka nie duplikuje kodu — zamiast tego mówi:
Nowy projekt Ansible? Cała konfiguracja po stronie projektu wygląda tak:
.gitlab-ci.yml:
include: - project: pl.rachuna-net/flows/gitlab ref: main file: .gitlab-ci.yml
GitLab CI/CD Variables:
PROJECT_TYPE = ansible-playbook
To dosłownie wszystko. Pipeline sam ogarnie: wersjonowanie, linting, sprawdzanie commitów, deployment na środowiska, publikację wersji. Projekt nie musi wiedzieć jak to działa — tak jak nie musisz wiedzieć, jak działa ekspres.
Tu kapsułkowy model dalej działa. Technologia może “nadpisać” dowolny job przez własną kopię kapsułki z własnym skryptem — bez modyfikowania common/. Ekspres pozostaje ten sam, kapsułka dostaje inny środek.
# pipelines/ansible-playbook/jobs/build/main.yml.pipelines.ansible-playbook.build: extends: [.common.build] # bazuje na kapsułce common script: - !reference [.common.logo.script.sh] - !reference [.pipelines.ansible-playbook.build.script.sh] # własna logika
Artykuł bazuje na realnym wdrożeniu w środowisku homelab. Wzorzec Job Capsule to wynik praktyki, a nie teorii — kod dostępny w repozytorium pl.rachuna-net/flows/gitlab. Pytania i PR-y mile widziane.