Jak skonfigurować Gitlab CI dla projektu Python Django

 In Python, Technicznie

Artykuł opisuje proces konfiguracji i uruchomienia własnego środowiska do ciągłej integracji z wykorzystaniem Gitlab CI na potrzeby automatycznego testowania i wdrożenia aplikacji stworzonej w Python i Django.

Gitlab CI jest środowiskiem do ciągłej integracji (ang. continuous integration).  Pozwala na automatyczne uruchamianie testów oraz wdrożenie na serwer po każdym commicie. Alternatywami dla Gitlab CI mogą być takie platformy jak CircleCi oraz TravisCI. Przewagą Gitlab’a jest posiadanie repozytorium oraz środowiska uruchomieniowego w jednym miejscu. W darmowej wersji możemy skorzystać z środowiska uruchomieniowego (runnerów) dostarczanego przez Gitlab, które posiada ograniczenie co do czasu wykonywania skryptów, lub skonfigurować runner na swoim serwerze.

GitLab Runner jest projektem open source, który umożliwia uruchamianie zadań z poziomu GitLab i przesyłanie ich rezultatów z powrotem do GitLaba. Dzięki temu, możemy zautomatyzować proces ciągłej integracji (CI). Zadania są grupowane i kolejno wykonywane, a każda następna faza rozpoczyna się tylko w przypadku powodzenia wykonania fazy poprzedzającej, np. Testy -> Wdrożenie na serwerze testowym -> Wdrożenie na serwerze produkcyjnym.

Wymagania

Konfiguracja przedstawiona w tym artykule będzie przeprowadzana na maszynie z zainstalowanym systemem Linux. Z racji na to, że sam Runner będzie działał w kontenerze Dockerowym rozbieżności wynikające z wykorzystywanego systemu będą niewielkie. Potrzebować będziemy Docker i Docker-Compose.

Instalacja Runnera na własnym serwerze

Gitlab Runner jest narzędziem, które możemy zainstalować na wielu różnych środowiskach (m. in. Linux, macOS, Windows, Docker, Kubernetes). W naszym przykładzie będziemy pracować z konfiguracją opartą o Docker. Pierwszym krokiem jest przygotowanie Dockera na maszynie, na której chcemy uruchomić naszego Runnera.

docker run -d --name gitlab-runner --restart always \
  -v /var/run/docker.sock:/var/run/docker.sock \
  -v /srv/gitlab-runner/config:/etc/gitlab-runner \
  gitlab/gitlab-runner:latest

Po wykonaniu powyższego polecenia powinien się uruchomić kontener z Gitlab Runnerem.

uruchomienie kontenera - wydruk

Konfiguracja w Gitlab w celu nawiązania połączenia z Runnerem

Do uzyskania połączenia pomiędzy Gitlabem, a Runnerem zainstalowanym na własnym serwerze należy przejść przez proces rejestracji Runnera. Dokumentacja procesu dla poszczególnych środowisk znajduje się pod adresem https://docs.gitlab.com/runner/register/.

W naszym przypadku pierwszym krokiem będzie uruchomienie kontenera w trybie interaktywnym:

docker exec -it gitlab-runner gitlab-runner register

Następnie należy uzupełnić wszystkie niezbędne dane. Proces ten jest bardzo prosty z racji tego, że “Rejestrator” będzie pytał o kolejne pola. Pierwszą wymaganą informacją jest adres koordynatora CI. Jest to nic innego jak adres naszej instancji Gitlaba (czyli adres pod którym znajduje się nasze repozytorium). W kolejnym kroku należy wygenerować i podać klucz do rejestracji. Znaleźć go można poprzez interfejs Gitlab w sekcji ustawień CI/CD repozytorium:

Kolejne pola do uzupełnienia to opis runnera, powiązane tagi, czy runner powinien podejmować działanie dla zadań bez tagów, czy zablokować runnera dla konkretnego projektu. Po wprowadzeniu tych danych będzie trzeba zdecydować w jaki sposób Runner ma wykonywać zadania. W naszym przypadku wybraliśmy opcję docker. W ten sposób dla każdego zadania runner będzie tworzył nowy kontener dockerowy i w nim wykonywał poszczególne kwerendy. Jeżeli na ostatnie z wymienionych pytań jako odpowiedź padło ‘docker’ będzie trzeba podać przy użyciu jakiego obrazu ma powstać domyślny kontener. Jeżeli w pliku .gitlab.ci-yml (więcej o nim w kolejnym dziale) nie zdefiniujemy żadnego obrazu to zostanie wybrany właśnie ten domyślny.

Jeżeli nie chcemy korzystać już z udostępnianych publicznie Runnerów należy na Gitlabie w sekcji ustawień CI/CD repozytorium kliknąć w przycisk Disable shared Runners. Jeżeli nie wykonamy tej akcji to do uruchomienia pipeline’a będzie wybierany wolny kontener spośród wszystkich dostępnych. Natomiast gdy czynność tą wykonamy to zawsze będzie wykorzystywany nasz zestaw Runner’ów (Specific Runners).

Przygotowanie .gitlab-ci.yml.

Plik .gitlab-ci.yml jest swego rodzaju “przepisem” na przebieg naszego pipeline’u CI w Runnerze. To właśnie tu definiujemy etapy działania naszego pipeline’u (testy, wdrożenie na serwer testowy), w jaki sposób zostaną one zrealizowane oraz jakie są niezbędne wymagania, aby krok mógł zostać wykonany – możemy np. zdecydować, aby etap wdrożenia był wykonywany tylko dla gałęzi master po poprawnie ukończonym etapie testów.

Co powinien zawierać .gitlab.ci?

Plik .gitlab-ci.yml jest plikiem YAML, powinien zawierać w sobie sekcje:

image: – tu podajemy obraz dockerowego kontenera, na bazie którego zostanie zbudowane środowisko do uruchomienia pipeline’u, jeśli zdecydujemy się na pominięcie tego parametru, zostanie użyty domyślny kontroler z sekcji ustawień CI/CD na GitLabie.

variables: – jest to sekcja opcjonalna, pozwala nam na skonfigurowanie zmiennych środowiskowych dla uruchamianego pipeline’a.

services: – sekcja, w której możemy dołączyć dodatkowe kontenery dockerowe potrzebne do wykonania dalszych zadań (może to być np. baza danych MongoDB, która jest potrzebna do działania naszej aplikacji)

before_script: – jest to sekcja, w której definiujemy listę poleceń, które powinny zostać wykonane dla każdego z etapów pipeline’a (przed dalszymi poleceniami, jednak po pobraniu kodu źródłowego z GitLaba oraz innych artefaktów projektu),

after_script: – analogicznie, jednak polecenia są wykonywane po zakończeniu etapu (także w przypadku jego niepowodzenia)

Opcje dodatkowe dla .gitlab.ci

Kolejne sekcje definiujemy sami – to w nich znajdują się właściwe opisy postępowania w poszczególnych etapach pipeline’a. Każda z tych sekcji zawiera w sobie zdefiniowane parametry:

script: – lista poleceń wykonywanych na danym etapie

stage: – poziom zadania (w przypadku braku tego parametru domyślnie przyjmowane jest test, ponadto GitLab w domyślnej konfiguracji udostępnia także etapy build oraz deploy, z których pierwszy jest wykonywany w całości przed rozpoczęciem etapu testów, natomiast ostatni wykonuje się dopiero, gdy wszystkie testy zostaną poprawnie wykonane), zadania znajdujące się na tym samym poziomie są wykonywane równolegle (możemy np. zdefiniować testy jednostkowe oraz integracyjne w dwóch różnych zadaniach, a następnie, o ile będzie możliwe jednoczesne przydzielenie ich do wolnych GitLab runnerów, zostaną one wykonane w tym samym czasie).

only: – pozwala nam wskazać, na których gałęziach ma zostać wykonana dana sekcja (np. wdrożenie na serwer testowy tylko z gałęzi develop)

except: – analogiczne do only, jednak pozwala nam wskazać, na których gałęziach zadanie ma nie być wykonywane

environment: – w przypadku etapu deploy definiuje nazwę oraz adres środowiska, na które zostaje wykonane wdrożenie, możliwe jest późniejsze sprawdzenie statusu wdrożenia oraz całej historii wdrożeń bezpośrednio z poziomu sekcji Environments w zakładce CI/CD naszego projektu na GitLabie.

services: – analogicznie do globalnie skonfigurowanej sekcji services, w tym przypadku pozwala nam skonfigurować dodatkowe kontenery potrzebne tylko do realizacji konkretnego etapu pipeline’a.

image: docker:latest
services:
  - docker:dind

variables:
  DOCKER_DRIVER: overlay

stages:
  - test
  - deploy

all-tests:
  stage: test
  image: python:3.6
  script:
    - bash ci/install.sh
    - coverage run --source="gitlab_ci_example,helloworld" manage.py test --noinput -k
    - coverage report -m
  coverage: '/TOTAL.+ ([0-9]{1,3}%)/'

deploy-dev:
  stage: deploy
  image: kroniak/ssh-client
  script:
    - 'echo "TODO: Here you can run your deployment process for staging environment."'
  only:
    - dev
  environment:
    name: staging
    url: http://staging.example.com

deploy-prod:
  stage: deploy
  image: kroniak/ssh-client
  script:
    - 'echo "TODO: Here you can run your deployment process for production environment."'
  only:
    - master
  environment:
    name: production
    url: http://example.com
  when: manual

Instalacja zależności w kontenerze

Kluczem do szybkiego i bezproblemowego działania naszego zautomatyzowanego pipeline’a jest odpowiednie przygotowanie zależności, które muszą zostać spełnione do jego działania. Możemy ten problem rozwiązać, przygotowując Dockerfile, w którym przygotujemy obraz testowy dla naszego środowiska, wskazując obraz bazowy, który jest najbardziej zbliżony naszym oczekiwaniom, a następnie doinstalowując do niego brakujące funkcjonalności – np. w projekcie Pythonowym jednym z pierwszych kroków będzie doinstalowanie polecenia pip, a następnie pobranie zależności naszego projektu. Podejście z użyciem wcześniej przygotowanego obrazu pozwala nam na skrócenie czasu potrzebnego na przygotowanie środowiska testowego – zależności pobierane są tylko podczas przygotowywania naszego obrazu oraz ewentualnej jego aktualizacji, a następnie wykorzystujemy już wcześniej przygotowany obraz, który zawiera w sobie przygotowane wszystkie zależności. Do przechowywania obrazu testowego możemy wykorzystać obecne w GitLabie Registry, które pozwala na przechowywanie obrazów powiązanych z projektem.

Uruchamianie pipeline’a

Jeżeli wszystko zostanie poprawnie skonfigurowane to po każdy commit’ie powinien uruchomić się pipeline. Wykona on wszystkie zadania, które będą przewidziane w jego ramach. Jeżeli wszystkie Runnery będą zajęte to nowy pipeline otrzyma status pending i będzie oczekiwał w kolejce

Code coverage

Każdy lubi widzieć jak wzrasta Code Coverage. Możemy w prosty sposób skonfigurować pobieranie danych z wyjścia konsoli  i wyświetlanie ich w odpowiednich miejscach.

W pierwszym kroku należy udać się do sekcji ustawień CI/CD i rozwinąć sekcję General pipelines settings. Znajdziemy w niej między innymi Test coverage parsing wraz z kilkoma przykładowymi regex’ami.

Wystarczy uzupełnić to pole, a przy każdym uruchomieniu pipeline’a z wydruku konsoli zostanie pobrana wartość dotycząca Code Coverage. Poniżej znajdują się też dostępne kontrolki z statusem pipeline’a oraz raportem Code Coverage. Możemy je umieścić gdzie tylko chcemy (np. README.md, informacja na stronie internetowej).

Istnieje także możliwość zdefiniowania regex’a dla code coverage bezpośrednio w pliku .gitlab-ci.yml podając parametr dla odpowiedniego zadania (zawarte w przykładowym .gitlab-ci.yml): coverage: ‘/TOTAL.+ ([0-9]{1,3}%)/’

Informacja o pokryciu kodu testami jest wyświetlana automatycznie na ekranie CI/CD Jobs.

Należy pamiętać, że aby wyświetlić Code Coverage trzeba w pipeline wykonać komendę, która przeanalizuje i wyświetli raport pokrycia kodu testami (w Python’ie może być to pytest-cov, dla tej komendy mamy też przygotowany przykładowy regex w ustawieniach CI/CD).

Więcej runnerów

W projekcie dość szybko możemy dojść do sytuacji, gdzie czas przebiegu pojedynczego pipeline’a dość szybko zacznie rosnąć (zwłaszcza, gdy w etapie testów będziemy uwzględniać code coverage). W takiej sytuacji przydaje się możliwość posiadania równoległych runnerów, dzięki którym kilka commitów wysłanych w niedużym odstępie czasu nie zablokuje nam automatycznych testów na np. godzinę. Skutecznym rozwiązaniem tego problemu jest przygotowanie większej ilości runnerów i skonfigurowanie GitLab Runnera do pracy z równolegle działającymi runnerami.

Pierwszym krokiem jest edycja pliku config.toml. W przypadku Runnera zainstalowanego jako Dockerowy kontener znajduje się on w katalogu /etc/gitlab-runner/ wewnątrz samego kontenera.

docker exec -it gitlab-runner vim /etc/gitlab-runner/config.toml

W pierwszej linii tego pliku zmieniamy domyślną wartość parametru concurrent z 1 na wyższą wartość (wg uznania), a następnie rejestrujemy kolejne instancje runnera dokładnie w taki sam sposób, w jaki robiliśmy to w punkcie 2. Od teraz, w przypadku większej ilości oczekujących pipeline’ów zostaną one rozdysponowane pomiędzy działające instancje runnera.

Podsumowanie

Jako podsumowanie naszego wpisu przygotowaliśmy projekt wykorzystujący Gitlab Runner’a. Zawiera on aplikację w stylu Hello World! wykonaną w Python’ie z wykorzystaniem frameworka Django.

https://gitlab.com/ermlab-public/gitlab-ci-demohttps://gitlab.com/ermlab-public/gitlab-ci-demo

Źródła

Autorzy

 

 

Recommended Posts