user-microservice
Mikroserwis REST do zarządzania kontami użytkowników, zbudowany w celu porównania narzędzi CI/CD do automatycznego budowania i wdrażania aplikacji skonteneryzowanych. Projekt powstał w ramach pracy magisterskiej.
To repozytorium zawiera kod aplikacji oraz potoki CI (budowanie, testy, publikacja
obrazu, aktualizacja repozytorium GitOps). Manifesty Kubernetes i konfiguracje narzędzi CD
znajdują się w drugim repozytorium: user-microservice-deploy.
Spis treści
- Cel projektu
- Architektura rozwiązania
- Porównywane narzędzia (5)
- Scenariusze pomiarowe
- Mapa branchy
- Aplikacja
- Potoki CI
- Infrastruktura
- Pomiar dostarczenia zmiany
- Struktura repozytorium
Cel projektu
Zaprojektowano jednolity proces CI/CD (build → test → publikacja obrazu → wdrożenie na Kubernetes) i zaimplementowano go w pięciu narzędziach, a następnie zmierzono czas dostarczenia zmiany (od commita do działającej aplikacji) dla różnych kombinacji narzędzi CI i CD. Celem była obiektywna, ilościowa i jakościowa charakterystyka tych narzędzi.
Proces jest identyczny funkcjonalnie w każdym narzędziu:
- Uruchomienie po zdarzeniu
pushdo repozytorium aplikacji. - Uruchomienie testów jednostkowych (
pytest). - Zbudowanie obrazu kontenera i opublikowanie go w rejestrze (Azure Container Registry), z tagiem równym SHA commita.
- Zaktualizowanie manifestu wdrożenia w repozytorium GitOps (
user-microservice-deploy) — podmiana taga obrazu na nowy SHA. - Wdrożenie nowej wersji na klaster Kubernetes (Azure AKS) przez narzędzie CD.
Architektura rozwiązania
┌──────────────────────────┐ push ┌───────────────────────────┐
│ user-microservice (Git) │ ────────────────────▶ │ Potok CI │
│ kod aplikacji + potoki │ │ Jenkins / Woodpecker / │
└──────────────────────────┘ │ Argo Workflows │
└────────────┬──────────────┘
│
┌──────────────────────────────────┬──────────┴───────┐
│ 1. pytest │ │
│ 2. docker build + push ▼ ▼
│ ┌────────────────────┐ ┌───────────────┐
│ │ Azure Container │ │ commit taga │
│ │ Registry (ACR) │ │ do repo │
│ └────────────────────┘ │ GitOps │
│ └───────┬───────┘
▼ │
▼
┌──────────────────────────┐ obserwuje / webhook ┌───────────────────────────┐
│ user-microservice-deploy │ ◀──────────────────────────────▶│ Potok CD │
│ manifesty K8s (GitOps) │ │ kubectl / ArgoCD / Flux │
└──────────────────────────┘ └────────────┬──────────────┘
│ apply / sync
▼
┌───────────────────────────┐
│ Azure Kubernetes (AKS) │
│ namespace: user-... │
└───────────────────────────┘
Porównywane narzędzia (5)
| Rola | Narzędzie | Model działania |
|---|---|---|
| CI (build/test/push) | Jenkins | Potok deklaratywny (Jenkinsfile), agent w Kubernetes (pod z wieloma kontenerami) |
| CI | Woodpecker CI | Potok YAML (.woodpecker/build.yaml), backend Kubernetes |
| CI | Argo Workflows | Sterowany zdarzeniami (Argo Events: EventSource + Sensor → Workflow) |
| CD (deploy) | ArgoCD | GitOps typu pull — synchronizuje stan klastra z repo deploymentowym |
| CD | FluxCD | GitOps typu pull — GitRepository + Kustomization + Receiver |
Dodatkowo każde narzędzie CI potrafi też pełnić rolę CD w wariancie „to samo narzędzie":
wdrożenie realizowane jest wtedy imperatywnie przez kubectl apply (potok wyzwalany
commitem do repo GitOps).
Scenariusze pomiarowe
Pełna macierz to 3 narzędzia CI × 3 warianty CD = 9 scenariuszy:
| # | CI | CD |
|---|---|---|
| Scenariusz 1 | Jenkins | Jenkins (kubectl apply) |
| Scenariusz 2 | Jenkins | ArgoCD |
| Scenariusz 3 | Jenkins | FluxCD |
| Scenariusz 4 | Woodpecker | Woodpecker (kubectl apply) |
| Scenariusz 5 | Woodpecker | ArgoCD |
| Scenariusz 6 | Woodpecker | FluxCD |
| Scenariusz 7 | Argo Workflows | Argo Workflows |
| Scenariusz 8 | Argo Workflows | ArgoCD |
| Scenariusz 9 | Argo Workflows | FluxCD |
Scenariusze 1–3 obejmują Jenkinsa, 4–6 Woodpeckera, a 7–9 Argo Workflows — w każdej trójce kolejno: to samo narzędzie do CD, ArgoCD oraz FluxCD.
Mapa branchy
Każda kombinacja narzędzi ma osobny branch — w obu repozytoriach. Poniżej odwzorowanie scenariuszy na branche.
Repozytorium aplikacji (user-microservice) — definicje CI:
| Branch | Zawartość |
|---|---|
main |
Kod aplikacji (bazowy), ten README, skrypty analizy |
dev |
Gałąź rozwojowa aplikacji + narzędzia pomiarowe (deployment_timer.sh, data_analysis.py) |
jenkins-pipeline |
CI w Jenkins (.jenkins/Jenkinsfile, podTemplate.yaml) |
woodpecker |
CI w Woodpecker (.woodpecker/build.yaml) |
argo-workflows |
CI w Argo Workflows / Argo Events (argo-workflows/*) |
jenkins-fluxcd, woodpecker-argocd, woodpecker-fluxcd, argoworkflow-argocd, argoworkflow-fluxcd |
Warianty CI dostrojone do konkretnego narzędzia CD (np. docelowy branch repo GitOps) |
Repozytorium deploymentowe (user-microservice-deploy) — manifesty i CD:
| Branch | Scenariusz |
|---|---|
master |
Baza manifestów |
jenkins-kubernetes |
Sc 1 — Jenkins wdraża przez kubectl |
jenkins-argocd-deploy |
Sc 2 — ArgoCD |
jenkins-fluxcd-deploy |
Sc 3 — FluxCD |
woodpecker-deploy |
Sc 4 — Woodpecker wdraża przez kubectl |
woodpecker-argocd-deploy |
Sc 5 — ArgoCD |
woodpecker-fluxcd-deploy |
Sc 6 — FluxCD |
argo-deploy |
Sc 7 — Argo Workflows |
argoworkflow-argocd |
Sc 8 — ArgoCD |
argoworkflow-fluxcd / fluxcd |
Sc 9 — FluxCD |
Uwaga: warianty FluxCD używają układu katalogów
apps/+clusters/prod/wymaganego przez Flux, pozostałe warianty trzymają manifesty w katalogu głównym repo GitOps.
Aplikacja
Mikroserwis udostępnia API REST do zarządzania kontami użytkowników wraz z uwierzytelnianiem JWT (tokeny w ciasteczkach), rolami oraz mechanizmem wylogowania (unieważnianie tokenów).
Stos technologiczny
- Python 3.11, Flask 3
- Flask-JWT-Extended — uwierzytelnianie JWT
- Flask-SQLAlchemy / SQLAlchemy 2 — ORM
- MySQL (produkcyjnie), SQLite in-memory (testy)
- waitress — produkcyjny serwer WSGI
- pytest — testy jednostkowe
- Docker — konteneryzacja (obraz bazowy
python:3.11.7-slim-bookworm)
API
| Metoda | Ścieżka | Opis | Autoryzacja |
|---|---|---|---|
POST |
/users |
Utworzenie użytkownika | brak (konto Administrator wymaga tokena admina) |
GET |
/users |
Lista wszystkich użytkowników | tylko Administrator |
GET |
/users/<id> |
Szczegóły użytkownika | właściciel lub Administrator |
PUT/PATCH |
/users/<id> |
Edycja użytkownika (PUT wymaga wszystkich pól) |
właściciel lub Administrator |
DELETE |
/users/<id> |
Usunięcie użytkownika | właściciel lub Administrator |
POST |
/login |
Logowanie, ustawia token JWT w ciasteczku | brak |
GET |
/logout |
Wylogowanie (unieważnienie tokena) | zalogowany |
GET |
/health |
Sprawdzenie stanu aplikacji i połączenia z bazą | brak (dostępne na branchach z potokami CI/CD) |
GET |
/version |
Zwraca wdrożoną wersję (APP_VERSION = SHA commita) i czas budowy |
brak (dostępne na branchach z potokami CI/CD) |
Przy pierwszym uruchomieniu, jeśli baza jest pusta, tworzone jest domyślne konto administratora
(zob. zmienne ADMIN_*).
Model danych
User—id,username(unikalny),email(unikalny),role(Administrator/User),password(hash).RevokedToken—jtiunieważnionych tokenów JWT (blocklist przy wylogowaniu).
Konfiguracja (zmienne środowiskowe)
| Zmienna | Opis | Domyślna |
|---|---|---|
SQLALCHEMY_DATABASE_URI |
Połączenie do bazy MySQL | — (wymagana poza testami) |
JWT_SECRET_KEY |
Klucz podpisujący tokeny JWT | changeme |
APP_PORT |
Port serwera | 80 |
ADMIN_USERNAME |
Login domyślnego admina | admin |
ADMIN_EMAIL |
E-mail domyślnego admina | [email protected] |
ADMIN_PASSWORD |
Hasło domyślnego admina | admin |
Produkcyjnie sekrety (SQLALCHEMY_DATABASE_URI, hasła MySQL) są dostarczane z Azure Key
Vault przez sterownik CSI secrets-store — zob. repozytorium deploymentowe.
Uruchomienie lokalne
Za pomocą Docker Compose (uruchamia API + MySQL):
# przygotuj pliki api/.env oraz db/.env ze zmiennymi środowiskowymi
docker compose up --build
Bez konteneryzacji:
cd api
python3 -m venv env && source env/bin/activate
pip install -r requirements.txt
python3 app.py
Aplikacja czeka na gotowość bazy danych przy starcie (wait_for_db, do 100 prób co 3 s).
Testy
cd api
python3 -m venv env && source env/bin/activate
pip install -r requirements.txt pytest
python3 -m pytest
Testy używają bazy SQLite w pamięci (konfiguracja testing) i pokrywają operacje CRUD oraz
uwierzytelnianie. Ten sam zestaw testów jest uruchamiany w każdym potoku CI.
Wcześniejsze wersje potoków korzystały dodatkowo z testów kontenera Goss — zostały one ostatecznie usunięte z potoków (por. historia commitów), a walidację przejęły testy
pytestoraz health check po wdrożeniu.
Potoki CI
Definicje potoków znajdują się na branchach poszczególnych narzędzi. Wszystkie realizują ten sam trzyetapowy proces: testy → build&push → commit do GitOps.
Jenkins
Branch jenkins-pipeline, plik .jenkins/Jenkinsfile. Potok deklaratywny uruchamiany na
agencie w Kubernetes — pod z osobnymi kontenerami do każdego etapu (podTemplate.yaml):
python— uruchomieniepytest(wyniki jako JUnit XML),docker—docker build+ logowanie do ACR (Azure managed identity) +docker push,git— sklonowanie repo GitOps, podmiana taga obrazu (awk) i commit na branchjenkins-kubernetes.
Build kontenera działa bez uprawnień roota dzięki sysbox (runtimeClassName: sysbox-runc).
Woodpecker CI
Branch woodpecker, plik .woodpecker/build.yaml. Trzy kroki (code-tests,
build-and-push, gitops-commit) na backendzie Kubernetes. Docker-in-Docker uruchamiany
lokalnie (dockerd &) w kontenerze z sysbox. Sekrety (klucz deploy do Gitea, known hosts)
pobierane z sekretów Woodpeckera.
Argo Workflows
Branch argo-workflows, katalog argo-workflows/. Podejście sterowane zdarzeniami przez
Argo Events:
source.yaml—EventSource(webhook, dwa endpointy: dla repo aplikacji i deploymentowego),sensor.yaml—Sensorreagujący na push i tworzącyWorkflowbudujący (etapy:checkout→tests→build-and-push-image→gitops-commit),eventbus-default.yaml— szyna zdarzeń (NATS),permissions.yaml—ServiceAccount+Role/RoleBinding,secret-store.yaml— dostęp do sekretów z Key Vault.
Wspólny schemat potoku CI
Wszystkie trzy narzędzia wykonują tę samą logikę, różniąc się jedynie składnią i modelem uruchamiania:
push → [pytest] → [docker build → az acr login → docker push (tag = SHA)] → [clone repo GitOps → awk: podmiana tagu → commit → push]
Aktualizacja manifestu w repo GitOps jest realizowana identycznym skryptem awk, który
znajduje kontener api i podmienia w nim tag obrazu na SHA nowego commita.
Infrastruktura
| Element | Zastosowanie |
|---|---|
| Azure Kubernetes Service (AKS) | Klaster docelowy; działają na nim także agenci CI oraz kontrolery CD |
Azure Container Registry (marcin00.azurecr.io) |
Rejestr obrazów |
Azure Key Vault + CSI secrets-store |
Dostarczanie sekretów do podów |
| Managed Identity | Bezhasłowe uwierzytelnianie do ACR / AKS / Key Vault |
Gitea (self-hosted, gitea.marcin00.pl) |
Repozytoria Git + webhooki wyzwalające potoki |
sysbox (sysbox-runc) |
Budowanie obrazów bez uprawnień roota (rootless DinD) |
| NGINX Ingress | Wystawienie aplikacji (user-microservice.marcin00.pl) |
Pomiar dostarczenia zmiany
Metryką porównawczą jest czas dostarczenia zmiany — od commita do momentu, w którym nowa
wersja aplikacji faktycznie działa na klastrze. Pomiar jest w pełni zautomatyzowany, a
narzędzia znajdują się na branchu dev:
-
deployment_timer.sh— automatyczny pomiar pojedynczego wdrożenia. Skrypt:- odczytuje aktualną wersję z endpointu
/version, - zapisuje znacznik czasu do pliku, commituje go (
Automatyczna zmiana: <timestamp>) i wykonujegit push— co wyzwala cały łańcuch CI/CD, - odpytuje
/versionco sekundę, aż zwrócona wersja się zmieni, - zapisuje wynik (
start,koniec,czas,stara_wersja,nowa_wersja) do plikudeployment_times.csv.
Wykrycie zmiany opiera się na tym, że potok CI buduje obraz z
APP_VERSIONrównym SHA commita, a/versionzwraca tę wartość. CommityAutomatyczna zmiana: <timestamp>widoczne w historii repozytorium pochodzą właśnie z kolejnych przebiegów tego skryptu. - odczytuje aktualną wersję z endpointu
-
data_analysis.py— agreguje zebrane pomiary (po 20 wdrożeń na scenariusz) i generuje wykresy słupkowe średnich czasów dostarczenia do kataloguplots/. Tworzy 6 porównań: trzy według narzędzia CI (dla każdego CD) i trzy według narzędzia CD (dla każdego CI), obejmując wszystkie 9 scenariuszy.pip install matplotlib python3 data_analysis.py # zapisuje plots/mean_times_0..5.png
Pliki wynikowe pomiarów (
deployment_times.csv, katalogplots/) są generowane przez powyższe skrypty i nie są śledzone w repozytorium.
Struktura repozytorium
user-microservice/
├── api/
│ ├── app.py # fabryka aplikacji Flask, konfiguracja, start serwera waitress
│ ├── views.py # endpointy API (użytkownicy, login/logout)
│ ├── models.py # modele SQLAlchemy: User, RevokedToken
│ ├── utils.py # autoryzacja, oczekiwanie na bazę, inicjalizacja admina
│ ├── requirements.txt # zależności aplikacji
│ └── tests/ # testy pytest (conftest.py, test_users.py)
├── Dockerfile # obraz aplikacji
└── docker-compose.yml # lokalne uruchomienie API + MySQL
Definicje potoków CI (
Jenkinsfile,.woodpecker/,argo-workflows/) znajdują się na odpowiednich branchach — zob. Mapa branchy.