Automatyzacja nie jest czymś obcym
Ostatnim razem wyciągnąłem bolesną lekcję dzięki 10 minutom przestoju i raportowi z incydentu. Wdrożenie nie jest magią, lecz musi być ciągiem surowych zabezpieczeń: „Git, zapis – Test, weryfikacja – Build, pakowanie – Deploy, dostarczenie”.
Teraz cel stał się jasny. Nie mogę za każdym razem własnymi rękami sprawdzać tych skomplikowanych i uciążliwych zabezpieczeń. Potrzebuję „sumiennego robota”, który będzie pracował za mnie nawet wtedy, gdy wrócę już do domu albo będę na urlopie.
„Automatyzacja? Czy to nie brzmi trochę zbyt górnolotnie?”
Na początku trochę mnie to przestraszyło, ale gdy się nad tym zastanowiłem, uświadomiłem sobie, że już czegoś bardzo podobnego doświadczyłem. Chodzi o „Docker”, o którym mówiliśmy w poprzedniej serii.
Jeśli zapiszemy w Dockerfile „zainstaluj Ubuntu, zainstaluj Javę, skopiuj kod”, Docker to odczyta i sam zbuduje środowisko serwera. Dzięki temu uwolniliśmy się od mozolnego ręcznego wpisywania poleceń.
„Pipeline CI/CD” działa dokładnie tak samo. Jeśli Docker zautomatyzował „konfigurację serwera”, to pipeline automatyzuje „cały proces wdrożenia”. Zmieniła się tylko skala sceny: z mojego komputera na większy świat zwany „GitLabem i serwerem produkcyjnym”.

CI/CD: zautomatyzowana taśma produkcyjna
Zainstalujmy teraz „linię automatyzacji” w naszym „cyfrowym centrum logistycznym”. Brzmi to poważnie, ale zasada jest prosta.
1. CI (Continuous Integration, ciągła integracja)
- Znaczenie: „Kod programistów jest nieustannie scalany w jedną całość i sprawdzany.”
- Rola: [build + test]. To etap, w którym zaraz po wrzuceniu przeze mnie kodu maszyna sprawdza: „Czy składnia jest poprawna?”, „Czy nie zepsuło się nic z dotychczasowych funkcji?”. To tutaj odsiewane są wadliwe produkty.
2. CD (Continuous Delivery/Deployment, ciągłe dostarczanie/wdrażanie)
- Znaczenie: „Sprawdzony kod jest dostarczany klientowi.”
- Rola: [tworzenie obrazu + wdrożenie na serwer]. To etap, w którym pełnowartościowy produkt (Docker Image), który przeszedł kontrolę, trafia na ciężarówkę, jedzie na serwer produkcyjny i zostaje tam uruchomiony.
Plan pipeline’u: .gitlab-ci.yml
„Czyli jak właściwie zlecić temu robotowi pracę?” W GitLabie wystarczy utworzyć w głównym katalogu projektu jeden plik o nazwie .gitlab-ci.yml. To właśnie „instrukcja pracy”, którą wręcza się robotowi.
Pamiętasz ból ręcznego wdrożenia, którego doświadczyłem ostatnim razem? Przepiszmy ten sam proces wprost do kodu. Tak jak przy pisaniu Dockerfile, wystarczy zapisywać go linia po linii.
# .gitlab-ci.yml (instrukcja pracy)
stages: # 1. Zdefiniuj kolejnosc calego zadania
- test # (Etap 1: kontrola)
- build # (Etap 2: pakowanie)
- deploy # (Etap 3: wysylka)
# [Etap 1] Test: maszyno, sprawdz, czy moj kod jest w porzadku (CI)
unit_test_job:
stage: test
image: openjdk:17-alpine # Uruchom w srodowisku z zainstalowana Java
script:
- ./gradlew test # "Odpal testy!" (zapobiega poprzedniemu bledowi)
# [Etap 2] Build: jesli testy przejda, zbuduj obraz (CI)
build_image_job:
stage: build
image: docker:latest # Uruchom w srodowisku z zainstalowanym Dockerem
script:
- docker build -t my-app:v1 . # "Zbuduj obraz!"
- docker push my-regisry/my-app:v1 # "Wgraj do registry!" (wymagany tag rollback)
# [Etap 3] Deploy: teraz umiesc to na serwerze produkcyjnym (CD)
deploy_job:
stage: deploy
script:
- ssh user@server "docker service update my-app" # "Zaktualizuj serwer!"
Ten jeden plik wystarczy. W chwili, gdy wykonuję git push, czyli w momencie triggera, GitLab odczytuje tę instrukcję i budzi robota roboczego zwanego „GitLab Runner”, aby wykonał zadanie.
[Check] Zabezpieczenia, które musimy uwzględnić
W tej pipeline’ie zawarte są wszystkie trzy zabezpieczenia, których boleśnie nauczyłem się ostatnim razem.
- Git (Trigger):
- Sam fakt, że lokalnie robię
pushkodu, staje się „przyciskiem start” pipeline’u. Jeśli nie zostanie zapis (Commit), wdrożenie też się nie rozpocznie.
- Sam fakt, że lokalnie robię
- Unit Test (Quality Gate):
- Co, jeśli test w
unit_test_jobsię nie powiedzie? Pipeline natychmiast „staje (Fail)”. Wadliwy produkt nie ma żadnej możliwości przejścia do następnego etapu (build/deploy). (Koniec z pomijaniem testów pod pretekstem braku czasu!)
- Co, jeśli test w
- Docker Image (Artifact):
- Obraz utworzony na etapie build jest bezpiecznie przechowywany w Docker Hub. Nawet jeśli serwer kiedyś padnie, wystarczy ten obraz, żeby przywrócić wszystko w 1 sekundę.

Praktyczna rada: pipeline to żywy organizm
Nie próbuj od początku pisać idealnej pipeline’y. Ja sam na początku stworzyłem tylko dwa etapy, build i deploy, a dopiero po spowodowaniu incydentu dodałem przed nimi etap test.
- Początek: build -> deploy (najważniejsze, że automatyzacja w ogóle jest)
- Etap pośredni: test -> build -> deploy (dodane zabezpieczenie)
- Wersja rozbudowana: test -> analiza statyczna (SonarQube) -> build -> wdrożenie na serwer deweloperski -> (zatwierdzenie) -> wdrożenie na serwer produkcyjny
Pipeline to „żywy system”, który stale ewoluuje wraz z tempem rozwoju naszego zespołu.
Na zakończenie: instrukcja jest napisana, ale kto właściwie wykonuje pracę?
Teraz doskonały plan, .gitlab-ci.yml, jest już gotowy. Ale instrukcja zapisana na papierze sama się nie porusza. Tak samo jak Dockerfile, mimo że istnieje, potrzebuje zainstalowanego Docker Daemon, aby dało się go uruchomić, tak i instrukcja pipeline’u potrzebuje „robota terenowego”, który naprawdę spoci się, budując i uruchamiając kod.
Ten robot nazywa się „GitLab Runner”. Kierownik fabryki, czyli GitLab Server, wydaje jedynie polecenia. Faktyczną pracę wykonuje właśnie ten runner.
Następnym razem zainstalujmy tego runnera na moim komputerze albo na osobnym serwerze, połączmy go z GitLabem i uruchommy pipeline naprawdę. To wreszcie moment, w którym zapina się pierwszy guzik automatyzacji.


