Konfiguracja GitLab Runner: tchnijmy życie w nasz pipeline

„Wysłałem zlecenie, ale dlaczego nikt nie reaguje?”

Ostatnim razem z entuzjazmem napisałem plik .gitlab-ci.yml i zrobiłem push. „No to teraz wdrożenie pójdzie samo, prawda?” — z taką nadzieją kliknąłem w menu 'CI/CD > Pipelines’ w GitLabie.

Ale na ekranie zamiast zielonego znaczka sukcesu zobaczyłem tylko bez końca kręcący się pasek ładowania i komunikat o statusie 'Pending (Oczekuje)’. Po kliknięciu w komunikat zobaczyłem takie ostrzeżenie:

This job is stuck because the project doesn't have any runners online assigned to it.

W tłumaczeniu: „Robota się pojawiła, ale pracownik (Runner), który ma ją wykonać, nie zjawił się dziś w pracy”. Auć — rzuciłem zlecenie kierownikowi fabryki (serwerowi GitLab), ale zapomniałem zatrudnić pracownika (Runnera), który miał faktycznie pracować w fabryce.

Nawet najlepszy projekt nic nie da, jeśli nie ma 'robotnika’, który go zmontuje — fabryka stoi.

GitLab Runner: ręce i nogi pipeline’a

GitLab dzieli się zasadniczo na dwa elementy.

  1. GitLab Server (centrala/kierownik fabryki): przechowuje kod, zarządza stanem pipeline’u i wydaje polecenia.
  2. GitLab Runner (oddział/pracownik na miejscu): jest zainstalowany na faktycznym serwerze, odbiera polecenia z centrali, pobiera kod, buduje go i wykonuje polecenia wdrożenia.

„A czy GitLab nie mógłby po prostu sam zalogować się na mój serwer i zrobić wdrożenie?” Jak się chwilę zastanowić, byłoby to skrajnie niebezpieczne. Gdyby strona z internetu (GitLab) za jednym kliknięciem mogła wejść do komputera (serwera) w moim pokoju i wykonywać dowolne komendy, to byłby właściwie 'hacking’. Wystarczyłoby, że ktoś przejmie moje konto GitLab, i mógłby zamienić mój serwer w koparkę bitcoinów.

Dlatego GitLab nie loguje się bezpośrednio na mój serwer. Zamiast tego osadza wewnątrz mojego serwera 'pełnomocnika o sprawdzonej tożsamości (Runnera)’ i tylko za jego pośrednictwem zleca pracę. Cały skomplikowany proces instalacji i rejestracji, który zaraz przejdziemy, to właśnie 'procedura bezpieczeństwa’ uwierzytelniająca tego pełnomocnika.

Krok 1: sprawdzamy ogłoszenie o pracę (wygenerowanie tokenu)

Zanim zainstalujemy runnera, w centrali (GitLab) trzeba najpierw wystawić 'ogłoszenie o pracę’ — „do naszego projektu potrzebny jest pracownik” — i odebrać 'legitymację rejestracyjną (Token)’.

W najnowszych wersjach GitLaba, ze względu na bezpieczeństwo, sposób na podejrzenie tokenu jest trochę ukryty.

  1. Wejdź do projektu w GitLabie
  2. Menu po lewej: Settings > CI/CD
  3. W sekcji Runners kliknij Expand
  4. W prawym górnym rogu sekcji Project runners kliknij przycisk z trzema kropkami (⋮)

Po kliknięciu tego przycisku otworzy się menu pop-up i to właśnie tu znajdują się szukane informacje. Sprawdź i skopiuj URL oraz Registration token.

(Uwaga: może pojawić się straszne ostrzeżenie 'Support for registration tokens is deprecated’. Oznacza to, że ten sposób wkrótce zniknie, ale na razie jest jeszcze wspierany ze względu na kompatybilność, więc bez paniki — po prostu skopiuj token.)

[Tip] Masz wiele projektów? (Group Runner) Jeśli, tak jak ja, zarządzasz kilkoma projektami (frontend, backend itp.) zgrupowanymi w jednej grupie, rejestrowanie runnera osobno dla każdego projektu jest nieefektywne. W takim wypadku zarejestruj runnera nie w ustawieniach projektu, a w menu Group Settings > CI/CD. Tak utworzony 'Group Runner’ może sam obsługiwać zadania ze wszystkich projektów należących do grupy. (Mam dużo projektów, więc wybrałem to podejście.)

Aby zobaczyć token, trzeba otworzyć ukryte menu (⋮). Ten token to 'tajny klucz’ łączący runnera z serwerem, więc nigdy nie wolno go ujawniać.

Krok 2: zatrudniamy pracownika (instalacja Runnera)

Teraz zainstalujmy runnera na serwerze linuksowym (lub własnym PC). Najczystszym sposobem jest oczywiście wykorzystanie naszego ukochanego 'Dockera’.

# 1. Uruchom kontener GitLab Runner
docker run -d --name gitlab-runner --restart always \
  -v /var/run/docker.sock:/var/run/docker.sock \
  -v gitlab-runner-config:/etc/gitlab-runner \
  gitlab/gitlab-runner:latest

  • -v /var/run/docker.sock:/var/run/docker.sock: to jest kluczowe. Runner musi uruchamiać kolejne kontenery Dockera (do budowania), dlatego udostępniamy mu gniazdo (socket) Dockera z hosta. (Metoda Docker in Docker)

Krok 3: spisujemy umowę o pracę (rejestracja Runnera)

Sama instalacja runnera to nie koniec. Potrzebny jest jeszcze krok, w którym ten runner zarejestruje się (Register) na serwerze GitLab z deklaracją „jestem pracownikiem projektu A”.

Wejdźmy do kontenera z runnerem, którego dopiero co uruchomiliśmy, i wykonajmy polecenie rejestracji.

# Uruchom polecenie rejestracji wewnatrz kontenera runnera
docker exec -it gitlab-runner gitlab-runner register

W terminalu zaczyna się teraz seria pytań niczym w 'gorącym krześle’. Wystarczy wpisać informacje, które widzieliśmy w kroku 1.

  1. Enter the GitLab instance URL:
    • https://gitlab.com/ (albo adres własnej instancji GitLaba)
  2. Enter the registration token:
    • (wklej skopiowany wcześniej token)
  3. Enter a description for the runner:
    • my-runner-01 (dowolna, byle łatwa do rozpoznania nazwa)
  4. Enter tags for the runner:
    • (można pominąć, naciskając Enter. Przydaje się, gdy runner ma wykonywać tylko zadania z określonymi tagami.)
  5. Enter an executor:
    • docker (będziemy budować na Dockerze, więc wpisujemy docker.)
  6. Enter the default Docker image:
    • docker:latest (obraz używany domyślnie)

„Runner registered successfully.” Kiedy zobaczysz ten komunikat — zatrudnienie zakończone! Wystarczy teraz odświeżyć ekran GitLaba, a zobaczysz runnera z zieloną kontrolką, który krzyczy „Jestem gotowy (Online)”.

Po udanej integracji kontrolka statusu runnera na ekranie GitLaba zmienia się na zieloną.

Krok 4: pipeline w ruch!

W momencie zarejestrowania runnera, stojący do tej pory pipeline (Pending) automatycznie rusza. Running… i po chwili Passed!

Niczego nie zrobiłem, a maszyna sama uruchomiła testy, zbudowała projekt, stworzyła obraz i wgrała go do repozytorium. Otworzyłem terminal, sprawdziłem logi i okazało się, że wszystko zostało zrobione znacznie szybciej i dokładniej niż wtedy, gdy klepałem to ręcznie.

„Łaaał… naprawdę działa?”

Wdrożenie ręczne, które zajmowało 10 minut, teraz kończy się jednym git push. Mogę odpalić deploy i pójść po kawę.

Praktyczna rada: problem uprawnień Dockera (Permission Denied)

Jest taki błąd, na który przy pierwszej konfiguracji runnera natrafia się w 99% przypadków: dial unix /var/run/docker.sock: connect: permission denied

To problem wynikający z tego, że runner próbuje używać poleceń Dockera, ale nie ma do tego uprawnień. W takim wypadku trzeba otworzyć plik konfiguracyjny gitlab-runner (config.toml) i włączyć opcję privileged = true.

# /etc/gitlab-runner/config.toml (plik konfiguracyjny runnera)
[[runners]]
  name = "my-runner-01"
  executor = "docker"

[runners.docker]

privileged = true # Tę linijkę trzeba zmienić na true, żeby Docker-in-Docker działał poprawnie. # …

Na koniec: można już iść do domu

Wreszcie mam swojego własnego 'robota od wdrożeń’. Teraz mogę skupić się tylko na pisaniu kodu. Nudne i powtarzalne czynności weryfikacji i wdrożeń wykona za mnie mój wierny robot (Runner).

Brakuje jednak jeszcze 2%. Na razie pipeline uruchamia się bezwarunkowo przy każdym pushu na branch master. „A gdybym chciał automatycznie wdrażać na serwer deweloperski (dev), ale na produkcję (prod) tylko po naciśnięciu przeze mnie 'przycisku akceptacji’?”

Następnym razem przyjrzymy się 'strategii wdrożeń per branch’ i 'procedurze ręcznego zatwierdzania (Manual)’, dzięki którym sterowanie pipeline’em stanie się jeszcze sprytniejsze.

Dodaj komentarz