Skip to content

Latest commit

 

History

6 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

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.

Wstęp

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 livenessreadiness 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.

1. Uruchomienie klastra

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.

2. Konfiguracja i uruchomienie Chartu nginx-php

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 kontenerze

Po 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

3. Konfiguracja i uruchomienie Chartu MySQL

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: ClusterIP

Instalacja 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.

4. Utworzenie aplikacji

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.

5. Implementacja aplikacji

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 8080

Po 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

6. Test dostępności aplikacji

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.

7. Aktualizacja aplikacji nginx-php

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 kontenera

Aktualizacja 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 stanie Running,
  • 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.

8. Modyfikacja aplikacji internetowej i aktualizacja ConfigMap

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.

9. Test działania sond healthcheck dla nginx-php

W chartcie nginx-php zastosowano predefiniowane sondy healthcheckliveness, 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.

10. Test działania sond healthcheck dla MySQL

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 livenessreadiness. 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 livenessreadiness 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.

Podsumowanie

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 livenessreadiness, 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.

About

Implementacja fullstacka LEMP oraz jego pełne wdrożenie w klastrze Minikube z użyciem Helm Chartów, wraz z dokumentacją, konfiguracją i ilustracją działania.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages