Programowanie Full-Stack w Chmurze Obliczeniowej
dr inż. Sławomir Wojciech Przyłucki
Termin zajęć:
środa, godz. 11:30,
Imię i nazwisko:
Paweł Pieczykolan,
II rok studiów magisterskich, WOiSI 2.3.
Celem zadania było przygotowanie kompletnego środowiska Kubernetes w oparciu o Minikube, umożliwiającego wdrożenie aplikacji webowej w architekturze typu Full-Stack. Wdrożenie miało bazować na jednym z popularnych stacków wymienionych na stronie W3Schools, z pełną konfiguracją komponentów w klastrze oraz dostępem do aplikacji pod adresem http://brilliantapp.zad. W ramach realizacji wykorzystano stack LEMP (Linux, Nginx, MySQL, PHP), wdrożony z użyciem Helm w wersji 3.19.4 oraz dwóch chartów: wso2/mysql (wersja obrazu 1.6.9 z MySQL 5.7.30) oraz k8s-at-home/nginx-php (wersja obrazu 1.2.2 z aplikacją trafex/php-nginx:2.4.0).
Środowisko zostało uruchomione na maszynie wirtualnej z systemem Ubuntu 24.04.1 LTS. Klaster Minikube został uruchomiony z wykorzystaniem sterownika Docker oraz pluginu sieciowego CNI Calico. Włączono również dodatek Ingress, który pozwolił na wystawienie aplikacji na zewnątrz klastra.
Wdrożenie aplikacji odbyło się poprzez instalację chartów Helm oraz przygotowanie dodatkowych plików konfiguracyjnych dla wybranych Charów. Do wgrywania plików aplikacji wykorzystano obiekt ConfigMap o nazwie lemp-app, który ładował zawartość katalogu ./lemp-app. Aplikacja realizowała prostą funkcjonalność listy obecności z pełnym zakresem operacji CRUD na tabeli w bazie danych MySQL. Konfiguracja chartu nginx-php została zmodyfikowana tak, aby korzystała z Ingressa oraz z danych z ConfigMap.
Dostępność aplikacji została zweryfikowana poprzez utworzenie tunelu Minikube oraz testy z użyciem polecenia curl i wpisu do pliku /etc/hosts. W ramach części nieobowiązkowej wdrożono mechanizm aktualizacji aplikacji z zachowaniem ciągłości działania – poprzez aktualizację ConfigMap oraz zastosowanie strategii RollingUpdate. Dodano również zmienną środowiskową TZ=UTC, której obecność została potwierdzona w działającym kontenerze po aktualizacji.
W celu zapewnienia wysokiej dostępności i odporności aplikacji na błędy, skonfigurowano sondy liveness i readiness dla obu komponentów: nginx-php oraz my-mysql. Ich działanie zostało potwierdzone poprzez wymuszenie restartu kontenerów oraz analizę zdarzeń w Kubernetes. Dodatkowo wprowadzono konfigurację strategii aktualizacji dla bazy danych oraz ustawiono strefę czasową UTC. Wszystkie kroki zostały udokumentowane i zilustrowane w dalszej części sprawozdania.
W ramach przygotowania środowiska do wdrożenia aplikacji Full-Stack uruchomiono nowy klaster Kubernetes z wykorzystaniem Minikube. Konfiguracja została przeprowadzona na maszynie wirtualnej z systemem Ubuntu 24.04.1 LTS, działającej w środowisku Oracle VirtualBox. Do uruchomienia klastra wykorzystano sterownik Docker oraz plugin sieciowy CNI Calico. Polecenie użyte do startu klastra:
minikube start --driver=docker --network-plugin=cni --cni=calico
Proces inicjalizacji klastra objął pobranie obrazu bazowego, utworzenie kontenera z 2 CPU i 3900 MB RAM oraz przygotowanie komponentów Kubernetes w wersji v1.34.0. Minikube automatycznie skonfigurował kontekst kubectl do pracy z nowym klastrem, umożliwiając zarządzanie zasobami w przestrzeni nazw default.
Rys. 1. Uruchomienie klastra Minikube z CNI Calico
Kolejnym krokiem było włączenie dodatku Ingress, który umożliwia wystawienie aplikacji na zewnątrz klastra. W terminalu wykonano polecenie:
minikube addons enable ingress
Minikube pobrał obrazy kontrolera Ingress oraz komponentów certgen, a następnie utworzył zasoby w przestrzeni nazw ingress-nginx. Status działania kontrolera został zweryfikowany za pomocą polecenia:
kubectl get pods -n ingress-nginx
Rys. 2. Aktywacja dodatku Ingress i weryfikacja działania kontrolera
Wynik polecenia potwierdził poprawne utworzenie zasobów, w tym kontrolera ingress-nginx-controller, który rozpoczął proces inicjalizacji. Tym samym środowisko było gotowe do wdrażania aplikacji.
Pierwszym krokiem po uruchomieniu klastra było przygotowanie konfiguracji dla chartu k8s-at-home/nginx-php. Zamiast korzystać z domyślnych wartości, utworzono plik value-nginx.yaml, w którym zdefiniowano sposób działania serwisu, typ kontrolera oraz podstawową konfigurację serwera Nginx. Dzięki temu możliwe było precyzyjne dostosowanie wdrożenia do wymagań zadania.
value-nginx.yaml
service:
main:
enabled: true # włącza główny serwis
type: ClusterIP # ustawia jego typ
ports:
http:
enabled: true # aktywuje port HTTP
controller:
type: deployment
mainContainer:
ports:
http:
enabled: true # włączono port HTTP w kontenerzePo przygotowaniu pliku konfiguracyjnego wykonano instalację chartu za pomocą polecenia:
helm install nginx-php k8s-at-home/nginx-php -f value-nginx.yaml
Polecenie to utworzyło obiekt Pod o nazwie nginx-php w przestrzeni nazw default. W wyniku instalacji powstał obiekt Deployment oraz odpowiadający mu Service, co zostało potwierdzone komunikatem o statusie deployed. Od tego momentu frontend aplikacji był dostępny w klastrze jako serwis typu ClusterIP i mógł zostać powiązany z Ingressem.
Rys. 3. Instalacja Charta nginx-php
W warstwie backendowej, bazodanowej aplikacji wykorzystano bazę danych MySQL wdrożoną za pomocą chartu wso2/mysql w wersji 1.6.9, który zawiera obraz mysql:5.7.30. Przed instalacją przygotowano plik value-mysql.yaml, zawierający szczegółową konfigurację instancji bazy danych, w tym dane dostępowe, sondy zdrowia oraz typ serwisu. Dzięki temu możliwe było precyzyjne dostosowanie zachowania kontenera do wymagań aplikacji oraz zapewnienie odporności na błędy.
value-mysql.yaml
# definicja danych dostępowych do bazy danych
mysqlRootPassword: "rootpass"
mysqlUser: "app"
mysqlPassword: "apppass"
mysqlDatabase: "appdb"
# konfiguracja sondy, która sprawdza, czy kontener nie zawiesił się
livenessProbe:
initialDelaySeconds: 60
periodSeconds: 15
timeoutSeconds: 5
failureThreshold: 6
# konfiguracja sondy, która sprawdzy, czy kontener jest gotowy do obsługi zapytań
# parametry sond livenessProbe i readinessProbe zostały dobrane tak, aby dać kontenerowi czas na inicjalizację, a jednocześnie szybko wykrywać awarie.
readinessProbe:
initialDelaySeconds: 30
periodSeconds: 15
timeoutSeconds: 5
failureThreshold: 6
# serwis nie zostaje wystawiony na zewnątrz
service:
type: ClusterIPInstalacja chartu została wykonana poleceniem:
helm install my-mysql wso2/mysql --version 1.6.9 -f value-mysql.yaml
Rys. 4. Instalacja Charta mysql
W wyniku instalacji utworzony został obiekt Pod, Deployment oraz odpowiadający mu Service. Po zakończeniu wdrożenia baza danych była gotowa do obsługi zapytań z aplikacji webowej, a jej stabilność była monitorowana przez sondy zdrowia.
W ramach warstwy frontendowej przygotowano prostą aplikację webową w języku PHP, której celem jest zarządzanie listą obecności studentów. Aplikacja umożliwia dodawanie, edytowanie, usuwanie oraz przeglądanie wpisów w tabeli attendance znajdującej się w bazie danych MySQL. Całość została umieszczona w katalogu lemp-app, a następnie wgrana do klastra Kubernetes jako ConfigMap, co pozwala na jej automatyczne podłączenie do kontenera Nginx-PHP.
Aplikacja składa się z pięciu plików PHP:
db.php
<?php
$host = "my-mysql"; // nazwa serwisu w Kubernetes
$user = "app";
$password = "apppass";
$database = "appdb";
$conn = new mysqli($host, $user, $password, $database);
if ($conn->connect_error) {
die("Connection failed: " . $conn->connect_error);
}
?>Plik db.php odpowiada za połączenie z bazą danych MySQL. Wykorzystuje nazwę serwisu my-mysql zdefiniowaną w Kubernetes oraz dane dostępowe skonfigurowane w chartcie MySQL. W przypadku błędu połączenia aplikacja wyświetla komunikat diagnostyczny.
index.php
<?php
include 'db.php';
$result = $conn->query("SELECT * FROM attendance ORDER BY id DESC");
echo "<h1>Lista obecności</h1>";
echo "<a href='add.php'>Dodaj studenta</a><br><br>";
echo "<table border='1'>";
echo "<tr><th>ID</th><th>Imię i nazwisko</th><th>Index</th><th>Obecność</th><th>Akcje</th></tr>";
while($row = $result->fetch_assoc()) {
echo "<tr>";
echo "<td>".$row['id']."</td>";
echo "<td>".$row['student_name']."</td>";
echo "<td>".$row['student_index']."</td>";
echo "<td>".($row['present'] ? "✓" : "✗")."</td>";
echo "<td>
<a href='edit.php?id=".$row['id']."'>Edytuj</a> |
<a href='delete.php?id=".$row['id']."'>Usuń</a>
</td>";
echo "</tr>";
}
echo "</table>";
?>Plik index.php pełni rolę strony głównej aplikacji. Pobiera dane z tabeli attendance i wyświetla je w formie tabeli HTML. Umożliwia przejście do formularza dodawania nowego studenta oraz edycji lub usunięcia istniejących wpisów.
add.php
<?php
include 'db.php';
if($_SERVER['REQUEST_METHOD'] === 'POST') {
$name = $_POST['student_name'];
$index = $_POST['student_index'];
$present = isset($_POST['present']) ? 1 : 0;
$stmt = $conn->prepare("INSERT INTO attendance (student_name, student_index, present) VALUES (?, ?, ?)");
$stmt->bind_param("ssi", $name, $index, $present);
$stmt->execute();
header("Location: index.php");
exit;
}
?>
<h1>Dodaj studenta</h1>
<form method="post">
Imię i nazwisko: <input type="text" name="student_name" required><br>
Index: <input type="text" name="student_index" required><br>
Obecność: <input type="checkbox" name="present"><br>
<input type="submit" value="Dodaj">
</form>
<a href="index.php">Powrót do listy</a>Plik add.php zawiera formularz umożliwiający dodanie nowego wpisu do listy obecności. Po przesłaniu danych formularza następuje ich walidacja i zapis do bazy danych za pomocą przygotowanego zapytania SQL.
edit.php
<?php
include 'db.php';
$id = $_GET['id'];
if($_SERVER['REQUEST_METHOD'] === 'POST') {
$name = $_POST['student_name'];
$index = $_POST['student_index'];
$present = isset($_POST['present']) ? 1 : 0;
$stmt = $conn->prepare("UPDATE attendance SET student_name=?, student_index=?, present=? WHERE id=?");
$stmt->bind_param("ssii", $name, $index, $present, $id);
$stmt->execute();
header("Location: index.php");
exit;
}
$result = $conn->query("SELECT * FROM attendance WHERE id=$id");
$row = $result->fetch_assoc();
?>
<h1>Edytuj studenta</h1>
<form method="post">
Imię i nazwisko: <input type="text" name="student_name" value="<?= $row['student_name'] ?>" required><br>
Index: <input type="text" name="student_index" value="<?= $row['student_index'] ?>" required><br>
Obecność: <input type="checkbox" name="present" <?= $row['present'] ? 'checked' : '' ?>><br>
<input type="submit" value="Zapisz">
</form>
<a href="index.php">Powrót do listy</a>Plik edit.php umożliwia edycję istniejącego wpisu. Na podstawie parametru id pobierane są dane studenta, które następnie można zmodyfikować i zapisać do bazy danych.
delete.php
<?php
include 'db.php';
$id = $_GET['id'];
$conn->query("DELETE FROM attendance WHERE id=$id");
header("Location: index.php");
exit;
?>Plik delete.php realizuje funkcję usuwania wpisu z bazy danych. Po wykonaniu zapytania DELETE następuje przekierowanie na stronę główną.
Cała aplikacja została wgrana do klastra Kubernetes jako ConfigMap o nazwie lemp-app. W terminalu wykonano polecenie:
kubectl create configmap lemp-app --from-file=./lemp-app
Rys. 5. Utworzenie plików aplikacji i wgranie Configmapa do klastra Kubernetesa
Dzięki temu po podpięciu obiektu ConfigMap pod kontener nginx-php pliki aplikacji zostaną automatycznie zamontowane w kontenerze frontendowym, a aplikacja będzie gotowa do działania i komunikacji z bazą danych MySQL.
Po utworzeniu plików aplikacji w katalogu lemp-app oraz ich wgraniu do klastra jako ConfigMap, wykonano implementację funkcjonalną aplikacji.
W pierwszym kroku utworzono tabelę attendance bezpośrednio w bazie danych appdb, korzystając z sesji MySQL uruchomionej wewnątrz kontenera:
USE appdb;
CREATE TABLE attendance (
id INT AUTO_INCREMENT PRIMARY KEY,
student_name VARCHAR(100) NOT NULL,
student_index VARCHAR(20) NOT NULL,
present BOOLEAN NOT NULL DEFAULT 0,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);Struktura tabeli została dobrana tak, aby umożliwić przechowywanie podstawowych informacji o studentach: imienia i nazwiska, numeru indeksu, statusu obecności oraz znacznika czasowego utworzenia wpisu. Kolumna present została zdefiniowana jako BOOLEAN z domyślną wartością 0, co pozwala na prostą interpretację obecności w interfejsie użytkownika.
Rys. 6. Utworzenie tabeli "attendance" w kontenerze my-mysql
Aplikacja została zamontowana w kontenerze nginx-php poprzez konfigurację persistence typu configMap, wskazującą na lemp-app jako źródło plików. Pliki zostały umieszczone w katalogu /var/www/html, co pozwoliło Nginxowi na ich bezpośrednie serwowanie.
W celu wystawienia aplikacji na zewnątrz klastra skonfigurowano obiekt Ingress w pliku value-nginx-map.yaml będącym kopią pliku value-nginx.yaml. W sekcji ingress.main zdefiniowano klasę nginx, nazwę hosta brilliantapp.zad oraz ścieżkę / kierującą ruch do serwisu nginx-php na porcie 8080. Zmiany w konfiguracji:
value-nginx-map.yaml
persistence:
main:
enabled: true # włącza mechanizm podpinania wolumenu do kontenera
type: configMap # źródłem danych montowanych do kontenera jest ConfigMap, a nie PVC
name: lemp-app # nazwa ConfigMap, która zostanie zamontowana jako wolumen
mountPath: /var/www/html # określa ścieżkę w kontenerze, pod którą ConfigMap zostanie podmontowana
ingress:
main:
enabled: true # włącza tworzenie zasobu Ingress
ingressClassName: nginx # nazwa kontrolera, który będzie obsługiwać Ingress
hosts:
- host: brilliantapp.zad # domena, pod którą aplikacja będzie dostępna
paths:
- path: / # ścieżki zaczynające się od / będą kierowane do aplikacji
pathType: Prefix
service:
name: nginx-php # ruch HTTP zostanie przekierowany do serwisu nginx-php
port: 8080 # wystawia aplikację na porcie 8080Po dodaniu konfiguracji Ingressu i ConfigMap wykonano aktualizację wdrożenia za pomocą polecenia:
helm upgrade nginx-php k8s-at-home/nginx-php -f value-nginx-map.yaml
Dzięki temu aplikacja została wystawiona pod adresem http://brilliantapp.zad
Rys. 7. Aktualizacja kontenera nginx-php
Po wdrożeniu aplikacji oraz konfiguracji Ingressu wykonano testy dostępności, mające na celu potwierdzenie poprawnego działania wszystkich komponentów w klastrze. Pierwszym krokiem było uruchomienie tunelu Minikube za pomocą polecenia:
minikube tunnel
Tunel umożliwił przekierowanie ruchu z zewnątrz do serwisów typu ClusterIP poprzez kontroler Ingress. Status tunelu został potwierdzony jako aktywny, bez błędów po stronie routera ani samego Minikube.
Rys. 8. Utworzenie tunelu
Następnie wykonano polecenie:
kubectl get all
które potwierdziło, że wszystkie obiekty – Pods, Services, Deployments – są w stanie Running
Dostępność aplikacji została przetestowana za pomocą polecenia curl z nagłówkiem Host, wskazującym na skonfigurowaną nazwę domenową:
curl -H "Host: brilliantapp.zad" http://192.168.49.2
Polecenie zwróciło poprawną odpowiedź HTTP oraz zawartość strony, potwierdzając, że Ingress poprawnie przekierowuje ruch do serwisu nginx-php.
Rys. 9. Wynik wykonania polecenia Curl
W celu umożliwienia testów w przeglądarce, dodano wpis do pliku /etc/hosts:
192.168.49.2 brilliantapp.zad
Dzięki temu możliwe było otwarcie aplikacji w przeglądarce pod adresem http://brilliantapp.zad.
Rys. 10. Widok aplikacji – lista obecności
Rys. 11. Widok aplikacji – dodanie rekordu do listy obecności
Rys. 12. Widok aplikacji – lista obecności po dodaniu użytkownika
Rys. 13. Widok aplikacji – edycja rekordu listy obecności
Aplikacja działa stabilnie, a komunikacja z bazą danych MySQL przebiega bez zakłóceń. Tym samym potwierdzono, że wdrożenie aplikacji w klastrze Minikube zakończyło się sukcesem.
W ramach części nieobowiązkowej zadania przeprowadzono aktualizację aplikacji webowej bez przerywania jej działania. Celem było potwierdzenie, że użytkownik końcowy nie doświadcza żadnych przerw w dostępności aplikacji podczas procesu aktualizacji.
W pliku value-nginx-map.yaml wprowadzono następujące zmiany:
value-nginx-map.yaml
controller:
type: deployment # typ zasobu
strategy: RollingUpdate # aktualizacje będą wykonywane stopniowo
rollingUpdate:
maxUnavailable: 0 # żaden pod nie może być niedostępny
maxSurge: 1 # kubernetes może utworzyć jeden dodatkowy pod ponad pożądaną liczbę replik, aby bezpiecznie przeprowadzić aktualizację
env:
TZ: "UTC" # ustawia zmienną środowiskową TZ wewnątrz konteneraAktualizacja została wykonana poleceniem:
helm upgrade nginx-php k8s-at-home/nginx-php -f value-nginx-map.yaml
Po wykonaniu aktualizacji rozpoczął się proces wymiany replik w ramach Deploymentu nginx-php.
W kolejnych wywołaniach polecenia kubectl get all można było zaobserwować:
- pojawienie się nowej repliki
nginx-php-5b9b7c54d4-*w stanieRunning, - równoległe działanie starej repliki
nginx-php-76478f9889-*, - stopniowe przechodzenie starej repliki w stan
Terminating, - finalnie – pełne przejęcie ruchu przez nową replikę i usunięcie starej repliki.
Rys. 14. Przebieg aktualizacji – równoległe działanie replik
Rys. 15. Przebieg aktualizacji – stara replika w stanie Terminating
Rys. 16. Przebieg aktualizacji – usunięcie starej repliki
W celu potwierdzenia, że aplikacja nie przestaje odpowiadać podczas aktualizacji, uruchomiono test ciągłości za pomocą polecenia:
while true; do curl -s -o /dev/null -w "%{http_code} - $(date '+%H:%M:%S')\n" -H "Host: brilliantapp.zad" http://192.168.49.2:80; sleep 10; done
Polecenie wysyłało zapytania HTTP co 10 sekund, rejestrując kod odpowiedzi oraz znacznik czasu. Wyniki testu wykazały, że przez cały czas trwania aktualizacji aplikacja zwracała kod 200, co oznacza pełną dostępność.
Rys. 17. Test dostępności aplikacji podczas aktualizacji
Dodatkowo, po zakończeniu aktualizacji, wykonano polecenie:
kubectl exec pod/nginx-php-76478f9889-95n75 -- printenv TZ
które zwróciło wartość UTC, potwierdzając, że nowa wersja aplikacji została poprawnie wdrożona.
Rys. 18. Weryfikacja zmiennej środowiskowej TZ po aktualizacji
Wszystkie testy potwierdziły, że aktualizacja została przeprowadzona zgodnie z założeniami – bez przestojów, z zachowaniem dostępności i poprawnym wdrożeniem zmian konfiguracyjnych.
Warning
W przypadku używanego w projekcie chartu MySQL zastosowano strategię Recreate, ponieważ chart ten celowo nie korzysta ze StatefulSet i został zaprojektowany jako prosta, jednowęzłowa instancja bazy danych. MySQL w takim układzie działa jako pojedynczy pod z podłączonym PVC. Z tego powodu strategia RollingUpdate byłaby niebezpieczna — mogłaby doprowadzić do jednoczesnego uruchomienia dwóch podów korzystających z tego samego wolumenu, co grozi uszkodzeniem danych. Recreate gwarantuje, że stary pod zostanie całkowicie zatrzymany przed uruchomieniem nowego, choć oznacza to krótką przerwę w dostępności bazy danych podczas aktualizacji.
Aby uniknąć jakichkolwiek przerw w działaniu MySQL, administrator musiałby wdrożyć pełną architekturę wysokiej dostępności — co najmniej replikację master, mechanizm automatycznego failovera, a następnie użyć StatefulSet z odpowiednią polityką aktualizacji. Dopiero wtedy możliwe byłoby bezprzestojowe przełączanie ruchu między instancjami i wykonywanie aktualizacji bez zatrzymywania bazy danych. Zgodnie z dokumentacją, dla tego charta nie jest możliwe ustawienie StatefulSet.
W ramach dalszego rozwoju aplikacji webowej dokonano modyfikacji pliku index.php, dodając integrację z biblioteką Bootstrap 5. Celem było poprawienie estetyki interfejsu użytkownika oraz zwiększenie czytelności tabeli obecności. Zmieniono strukturę HTML, dodano klasy CSS oraz komponenty Bootstrap.
index.php
<?php
include 'db.php';
$result = $conn->query("SELECT * FROM attendance ORDER BY id DESC");
?>
<!DOCTYPE html>
<html lang="pl">
<head>
<meta charset="UTF-8">
<title>Lista obecności</title>
<!-- Bootstrap 5 -->
<link
href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css"
rel="stylesheet"
>
</head>
<body class="bg-light">
<div class="container mt-5">
<h1 class="mb-4">Lista obecności</h1>
<a href="add.php" class="btn btn-primary mb-3">Dodaj studenta</a>
<table class="table table-striped table-bordered">
<thead class="table-dark">
<tr>
<th>ID</th>
<th>Imię i nazwisko</th>
<th>Index</th>
<th>Obecność</th>
<th>Akcje</th>
</tr>
</thead>
<tbody>
<?php while($row = $result->fetch_assoc()): ?>
<tr>
<td><?= $row['id'] ?></td>
<td><?= $row['student_name'] ?></td>
<td><?= $row['student_index'] ?></td>
<td class="text-center">
<?= $row['present'] ? "✓" : "✗" ?>
</td>
<td>
<a href="edit.php?id=<?= $row['id'] ?>" class="btn btn-sm btn-warning">Edytuj</a>
<a href="delete.php?id=<?= $row['id'] ?>" class="btn btn-sm btn-danger">Usuń</a>
</td>
</tr>
<?php endwhile; ?>
</tbody>
</table>
</div>
</body>
</html>Po zapisaniu zmian w pliku, zaktualizowano obiekt ConfigMap w Kubernetes, korzystając z polecenia:
kubectl create configmap lemp-app --from-file=index.php=./lemp-app/index.php --dry-run=client -o yaml | kubectl apply -f
Dzięki temu nowa wersja pliku została natychmiast wstrzyknięta do kontenera nginx-php, bez potrzeby restartu aplikacji ani ponownego wdrażania chartu.
Rys. 19. Zaktualizowany interfejs aplikacji z użyciem Bootstrap
Dzięki wykorzystaniu ConfigMap jako źródła plików aplikacji, możliwe było szybkie i bezpieczne wprowadzanie zmian w kodzie bez przerywania działania kontenera.
W chartcie nginx-php zastosowano predefiniowane sondy healthcheck – liveness, readiness oraz startup – które są automatycznie generowane na podstawie obrazu trafex/php-nginx. Chart nie udostępnia możliwości modyfikacji parametrów tych sond (takich jak initialDelaySeconds, timeoutSeconds, failureThreshold) z poziomu pliku YAML, co ogranicza kontrolę nad ich zachowaniem i czasem reakcji.
Rys. 20. Predefiniowane sondy w kontenerze nginx-php
W celu przetestowania działania sond przeprowadzono kontrolowany eksperyment polegający na ręcznym zakończeniu procesu nginx wewnątrz kontenera nginx-php za pomocą polecenia:
kubectl exec nginx-php-76478f9889-95n75 -- pkill nginx
Rys. 21. Zabicie procesu nginx w kontenerze nginx-php
Po zakończeniu procesu sondy wykryły brak odpowiedzi na porcie TCP, co skutkowało restartem kontenera przez kubelet. W logach zdarzeń pojawiły się komunikaty.
Rys. 22. Ostrzeżenia od sond healthcheck
Sondy readiness zgłaszały błędy, co potwierdza ich aktywne działanie i skuteczność w monitorowaniu stanu aplikacji. Dzięki temu Kubernetes automatycznie wyłączył niedziałający kontener i uruchomił nowy, przywracając pełną dostępność aplikacji.
Mimo braku możliwości dostosowania parametrów sond, ich domyślna konfiguracja okazała się wystarczająca w kontekście prostego wdrożenia testowego.
W celu zwiększenia odporności bazy danych MySQL na błędy oraz zapewnienia kontrolowanego procesu aktualizacji, zmodyfikowano konfigurację chartu wso2/mysql. Zmiany objęły zarówno sondy liveness i readiness. Celem było lepsze monitorowanie stanu kontenera.
Rys. 23. Predefiniowane sondy kontenera my-mysql
W pliku value-mysql.yaml wprowadzono następujące parametry:
value-mysql.yaml
# sprawdza, czy kontener działa poprawnie; w przypadku braku odpowiedzi przez 6 cykli (czyli 60 sekund), kontener jest restartowany
livenessProbe:
initialDelaySeconds: 60
periodSeconds: 10 # skrócono interwał czasowy między kolejnymi próbami sprawdzenia zdrowia kontenera
timeoutSeconds: 5
failureThreshold: 6
# decyduje, czy kontener jest gotowy do obsługi zapytań; jeśli nie, ruch do niego jest wstrzymywany
readinessProbe:
initialDelaySeconds: 30
periodSeconds: 10 # skrócono interwał czasowy między kolejnymi próbami sprawdzenia zdrowia kontenera
timeoutSeconds: 5
failureThreshold: 6
# zapewnia, że stary pod zostanie całkowicie usunięty przed uruchomieniem nowego, co chroni przed kolizją na poziomie wolumenów PVC
strategy:
type: Recreate
# ujednolica strefę czasową w kontenerze
timezone: "UTC"Zastosowano konfigurację dla kontenera my-mysql poleceniem:
helm upgrade my-mysql wso2/mysql --version 1.6.9 -f value-mysql.yaml
Po wykonaniu helm upgrade dla chartu MySQL, rozpoczął się proces wymiany podów zgodnie ze strategią Recreate.
Rys. 24. Kolejne zmiany stanu i liczby Podów MySQL po wgraniu aktualizacji konfiguracji
Sondy liveness i readiness zostały aktywowane zgodnie z konfiguracją. W przypadku błędów (np. nieudane połączenie z mysqladmin), w logach zdarzeń pojawiały się komunikaty. Do wyświetlenia użyto polecenia:
kubectl get events --sort-by=.metadata.creationTimestamp
Rys. 25. Logi zdarzeń dla kontenera my-mysql
Komunikaty te potwierdzają, że sondy skutecznie wykrywają problemy z kontenerem i uruchamiają mechanizmy samonaprawy. Dodatkowo, dzięki strategii Recreate uniknięto ryzyka jednoczesnego dostępu do wolumenu przez dwa pody, co mogłoby prowadzić do uszkodzenia danych.
W ramach niniejszego zadania przeprowadzono pełne wdrożenie aplikacji webowej w architekturze LEMP (Linux + Nginx + MySQL + PHP) z wykorzystaniem klastra Kubernetes uruchomionego lokalnie w środowisku Minikube. Proces obejmował zarówno konfigurację infrastruktury, jak i implementację aplikacji oraz testy dostępności i odporności na błędy.
Na początku uruchomiono klaster Minikube z pluginem sieciowym Calico oraz aktywowano dodatek Ingress, umożliwiający wystawienie aplikacji na zewnątrz. Następnie wdrożono dwa główne komponenty: nginx-php jako frontend oraz my-mysql jako backend bazodanowy. Oba komponenty zostały zainstalowane za pomocą Helm chartów, z precyzyjnie przygotowanymi plikami konfiguracyjnymi values.yaml.
Aplikacja webowa została zaimplementowana w PHP i umożliwia zarządzanie listą obecności studentów. Składa się z pięciu plików: index.php, db.php, add.php, edit.php oraz delete.php. Pliki zostały wgrane do klastra jako ConfigMap i zamontowane w kontenerze frontendowym. Po utworzeniu tabeli attendance w bazie danych, aplikacja została przetestowana pod kątem dodawania, edytowania i usuwania rekordów.
W kolejnych etapach skonfigurowano obiekt Ingress oraz uruchomiono tunel Minikube, co umożliwiło dostęp do aplikacji pod adresem http://brilliantapp.zad. Przeprowadzono testy dostępności z użyciem curl oraz wpisu w /etc/hosts, które potwierdziły poprawne działanie aplikacji.
W ramach aktualizacji aplikacji zastosowano strategię RollingUpdate dla komponentu nginx-php, co pozwoliło na bezprzestojowe wdrożenie nowej wersji z dodatkiem Bootstrap. Testy ciągłości działania wykazały, że aplikacja nie przestaje odpowiadać podczas aktualizacji, a mechanizmy Kubernetes skutecznie zarządzają replikami.
Dla komponentu MySQL zastosowano strategię Recreate, zgodną z charakterystyką baz danych jako komponentów stanowych. Dodatkowo skonfigurowano sondy liveness i readiness, które skutecznie wykrywały błędy i uruchamiały mechanizmy samonaprawy. Testy wykazały, że kontener MySQL jest poprawnie monitorowany i restartowany w przypadku awarii.
Całość wdrożenia została przeprowadzona zgodnie z dobrymi praktykami Kubernetes, z uwzględnieniem strategii aktualizacji, sond zdrowia, separacji komponentów oraz automatyzacji konfiguracji. Projekt potwierdził, że nawet w lokalnym środowisku Minikube możliwe jest zbudowanie w pełni funkcjonalnej aplikacji Full-Stack opartej na kontenerach, z zachowaniem wysokiej dostępności i odporności na błędy.

























