Jak rozwijać aplikację Django w lokalnym środowisku Kubernetes z użyciem Minikube

Styczeń 1, 2024 | #django , #localhost , #kubernetes , #python

000000
010001
000010
110001

Kubernetes jest obecnie chyba najbardziej popularnym systemem do automatyzacji wdrażania, skalowania i zarządzania aplikacjami kontenerowymi. Jest duże prawdopodobieństwo, że aplikacja nad którą pracujesz - niezależnie od technologii - zostanie wdrożona właśnie z użyciem K8s dlatego warto poświęcić mu trochę czasu. Nie pełniąc roli DevOps-a być może nie będziesz bezpośrednio odpowiedzialny za wdrażanie serwisów na serwerze produkcyjnym ale twoje oprogramowanie musi być na to gotowe, a opanowanie tej technologi z pewnością ułatwi debugowanie i optymalizację i da Ci większą swobodę w poruszaniu się po środowiskach developerskich i stagingowych. Dlatego dzisiaj pokarzę jak wdrożyć aplikację Django na komputerze lokalnym z użyciem Minikube i co można z tym więcej zrobić.

Przygotowanie Kubernetes do pracy na localhost

Instalacja minikube i kubectl

Klaster Kubernetes jest też wbudowany w Docker Desktop (także Docker Desktop for Linux), ja jednak pracuję na Linuksie z czystym Docker Engine więc zdecydowałem się na użycie Minikube, który zresztą ma większe możliwości od wersji wbudowanej w Docker Desktop.

Instalacja jest różna w zależności od systemu operacyjnego, a poza tym dość prosta dlatego odsyłam do oficjalnej dokumentacji minikube. Podobnie z narzędziem kubectl, które na Ubuntu można zainstalować choćby z pakietów snap

sudo snap inastall kubectl

W dalszej części zakładam, że zainstalowano Minikube i kubectl oraz, że Minikube jest wystartowane.

Dodanie modułu Ingress

Ingress to taki mechanizm w Kubernetes zapewniający zewnętrzny dostęp do usług w klastrze - routing. Ponieważ może on być realizowany za pośrednictwem różnorodnych kontrolerów Ingress - w tym NGINX Ingress Controller, Traefik, HAProxy Ingress, Istio Gateway itp. - użytkownicy mogą wybrać kontroler, który najlepiej odpowiada ich wymaganiom. W różnych środowiskach różnie się instaluje kontroler Ingress na szczęście w Minikube wystarczy go aktywować

minikube addons enable ingress

i upewnić się, że jest włączony

minikube addons list

Praca Kubernetes z lokalnymi obrazami Docker-a

Przygotowując aplikacje do publikacji na produkcji najpierw buduje się obrazy Docker-a a następnie wysyła do rejestru skąd są pobierane przez Kubernetes i wdrażane. Na localhost chcemy pominąć ten krok i używać obrazu wygenerowanego lokalnie. Aby Kubernetes był w stanie odnaleźć ten obraz a Pod, w definicji którego odwołano się do lokalnie zbudowanego obrazu nie przyjął po uruchomieniu statusu ImagePullBackOff musimy zrobić dwie rzeczy.

Docker w Minikube

Gdy używasz Minikube - Kubernetes i Docker - w zależności od użytego sterownika - działają wewnątrz wirtualnej maszyny lub kontenera (minikube start --driver=docker). Aby korzystać z lokalnych obrazów Docker-a bez ich wypychania do zdalnego rejestru, możesz ustawić swoje środowisko Docker tak, aby wskazywało na Docker w Minikube:

eval $(minikube docker-env)

Następnie, gdy zbudujesz obraz za pomocą docker build, będzie on dostępny dla klastra Kubernetes w Minikube.


Uwaga!

Jeśli w środowisku Minikube uruchomisz kontener Docker-a z podmontowanym katalogiem hosta to takie bindowanie nie zadziała ponieważ kontener ten jest uruchomiony w maszynie wirtualnej lub wewnątrz innego kontenera.

Nie ma to natomiast wpływu na kopiowanie plików lub katalogów w trakcie budowania obrazu.


Aby cofnąć to ustawienie wystarczy zamknąć terminal albo wywołać

eval $(minikube docker-env -u)

SameNode Policy

Jeśli aplikacja działa w środowisku testowym lub deweloperskim i jest tylko jeden węzeł, można skorzystać z opcji instruującej Kubernetes aby szukał obrazu tylko na lokalnym węźle. W tym celu w definicji Pod lub Deployment - jak w tym przypadku trzeba dodać instrukcję imagePullPolicy: IfNotPresent (patrz niżej), co spowoduje, że Kubernetes użyje lokalnego obrazu, jeśli jest on dostępny, i nie będzie próbował go pobrać z rejestru, chyba że go nie znajdzie.

Budowanie obrazu z projektem Django

Przykłady najlepiej obrazują przedstawiane zagadnienie zatem przejdźmy do konkretów.

W tym celu utwórz nowy projekt Django wraz z bazą danych Postgresql. Posłuży za przykład aplikacji, która zostanie wdrożona w klastrze Kubernetes.

Po utworzeniu nowego projektu Django według podanej wyżej instrukcji wraz ze zmianami, które wprowadzimy w tym artykule docelowo struktura projektu będzie wyglądać tak:

project_django/
├── app/
│   ├── manage.py
│   └── project_name/
├── assets/
│   ├── static/
│   └── media/
├── manifests/
│   ├── django-deployment.yml
│   ├── django-svc.yml
│   ├── ingress.yml
│   ├── postgres-deployment.yml
│   ├── postgres-pvc.yml
│   ├── postgres-svc.yml
│   └── media/
├── .env
├── docker-compose.override.yml
├── docker-compose.yml
├── Dockerfile
└── requirements.txt

Zanim zbudujemy obraz aplikacji Django na podstawie pliku docker-compose.yml uzupełnimy go o nazwę obrazu jaką chcemy mu nadać. W tym celu utworzymy plik docker-compose.override.yml

touch docker-compose.override.yml
version: '3.8'
services:
  django:
    image: projectdjango:1.0.0

Budujemy obraz

# nie zapomnij o wcześniejszym wykonaniu `eval $(minikube docker-env)`
docker compose build django

Polecenie to odczyta zarówno docker-compose.yml, jak i docker-compose.override.yml, łącząc ich konfiguracje.

Na liście obrazów oprócz obrazów k8s powinniśmy zobaczyć też nasz własny.

docker images

REPOSITORY             TAG       IMAGE ID       CREATED         SIZE
projectdjango          1.0.0     f54c2a958c4a   9 seconds ago   782MB

Teraz możemy przejść do Kubernetesa

Wrażanie projektu Django na lokalnym klastrze Kubernetes-a

Konwersja pliku .env na sekrety Kubernetes

Z racji tego, że klaster Kubernetes może obejmować kilka maszyn fizycznych lub wirtualnych, czytanie zmiennych środowiskowych z pliku .env mija się z celem. Dlatego też dane konfiguracyjne trzyma się w ConfigMap a dane wrażliwe w Secret. O ile zarówno jedne jak i drugie typy obiektów można utworzyć ręcznie, o tyle w przypadku sekretów tworzenie manifestu z wrażliwymi danymi i zapisanie go w repozytorium byłoby ewidentnym naruszeniem bezpieczeństwa. Dlatego najprostszym sposobem konwersji .env bezpośrednio na Kubernetes Secret jest użycie komendy

kubectl create secret generic projectdjango-secret --from-env-file=.env

i sprawdzenie czy udało się wygenerować Secret

kubectl get secret projectdjango-secret -o yaml

Manifesty Kubernetes wdrażające Django i Postgresql

mkdir ./manifests && \
touch ./manifests/postgres-pvc.yml \
      ./manifests/postgres-deployment.yml \
      ./manifests/postgres-svc.yml \
      ./manifests/django-deployment.yml \
      ./manifests/django-svc.yml \
      ./manifests/ingress.yml

Plik ./manifests/postgres-pvc.yml

Dane w Kubernetes - podobnie jak w Dockerze - przechowywane są w woluminach (Volumens) nie mniej jest to mechanizm nieco bardziej skomplikowany niż w Dockerze. Poniżej zdefiniowano żądanie o magazyn danych (PersistentVolumeClaim, PVC) - o pojemności 244Mi (mebibajtów) co daje w zaokrągleniu około 256MB (megabajtów). Żądanie PVC jest zwykle powiązane z magazynem ( PersistentVolume, PV). Rodzaj tego magazynu zależy od parametru storageClassName, którego nie ma w przykładzie poniżej ponieważ korzysta on po prostu ze standardowego ustawienia.

W środowisku Minikube, dynamiczne tworzenie wolumenów PersistentVolume, jest zazwyczaj skonfigurowane domyślnie. Oznacza to, że wystarczy utworzyć żądanie PersistentVolumeClaim, a Minikube automatycznie stworzy odpowiedni PersistentVolume dla tego PVC, jeśli nie istnieje już dostępny PV, który spełniałby wymagania PVC.

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: projectdjango-pg-pvc
spec:
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 244Mi

Plik ./manifests/postgres-deployment.yml

Etykieta postgres połączy Pod-a i ReplicaSet, które zostaną automatycznie utworzone po zastosowaniu Deployment-u, a także serwis przedstawiony niżej. Wartości zmiennych środowiskowych przechowujących dane wrażliwe są pobierane z wcześniej wygenerowanego obiektu Secret. Na końcu został użyty zadeklarowany wyżej wolumin na dane bazy Postgresql.

apiVersion: apps/v1
kind: Deployment
metadata:
  name: projectdjango-pg-deployment
spec:
  selector:
    matchLabels:
      app: postgres
  template:
    metadata:
      labels:
        app: postgres
    spec:
      containers:
        - name: postgres
          image: postgres:16.1-alpine3.19
          env:
            - name: POSTGRES_DB
              valueFrom:
                secretKeyRef:
                  name: projectdjango-secret
                  key: POSTGRES_DB
            - name: POSTGRES_USER
              valueFrom:
                secretKeyRef:
                  name: projectdjango-secret
                  key: POSTGRES_USER
            - name: POSTGRES_PASSWORD
              valueFrom:
                secretKeyRef:
                  name: projectdjango-secret
                  key: POSTGRES_PASSWORD
          ports:
            - containerPort: 5432
          volumeMounts:
            - name: postgres-storage
              mountPath: /var/lib/postgresql/data
      volumes:
        - name: postgres-storage
          persistentVolumeClaim:
            claimName: projectdjango-pg-pvc

Powyżej

Plik ./manifests/postgres-svc.yml

Usługa (Service) umożliwia dostęp do Pod-a poprzez sieć. Dzięki niemu aplikacja Django będzie miała połączenie z bazą danych.

apiVersion: v1
kind: Service
metadata:
  name: projectdjango-pg-service
spec:
  selector:
    app: postgres
  ports:
    - protocol: TCP
      port: 5432
      targetPort: 5432

Plik ./manifests/django-deployment.yml

W tym pliku należy przede wszystkim zwrócić uwagę na to, że kontener zostanie utworzony na podstawie lokalnego obrazu projectdjango:1.0.0 przez co koniecznym było dodanie też instrukcji imagePullPolicy: IfNotPresent żeby Kubernetes był wstanie odnaleźć ścieżkę do tego obrazu. Jako wartość zmiennej środowiskowej POSTGRES_HOST została podana nazwa serwisu dającego dostęp do Postgres-a. W chwili wystartowania Pod-a wywoływana jest komenda uruchamiająca serwer developerski Django. Na produkcji Django serwowane jest za pośrednictwem Gunicorn-a, uWSGI albo inny serwr wsgi, ale na potrzeby dewelopmentu to wystarczy.

apiVersion: apps/v1
kind: Deployment
metadata:
  name: projectdjango-dj-deployment
spec:
  selector:
    matchLabels:
      app: django
  template:
    metadata:
      labels:
        app: django
    spec:
      containers:
        - name: django
          image: projectdjango:1.0.0
          imagePullPolicy: IfNotPresent
          command: ["/bin/sh"]
          args: ["-c", "python manage.py runserver 0.0.0.0:8000"]
          ports:
            - containerPort: 8000
          env:
            - name: DJANGO_SECRET_KEY
              valueFrom:
                secretKeyRef:
                  name: projectdjango-secret
                  key: DJANGO_SECRET_KEY
            - name: POSTGRES_HOST
              value: projectdjango-pg-service
            - name: POSTGRES_PORT
              value: "5432"
            - name: POSTGRES_DB
              valueFrom:
                secretKeyRef:
                  name: projectdjango-secret
                  key: POSTGRES_DB
            - name: POSTGRES_USER
              valueFrom:
                secretKeyRef:
                  name: projectdjango-secret
                  key: POSTGRES_USER
            - name: POSTGRES_PASSWORD
              valueFrom:
                secretKeyRef:
                  name: projectdjango-secret
                  key: POSTGRES_PASSWORD

Plik ./manifests/django-svc.yml

Podobnie jak Postgres także Django jest wyeksponowane w sieci. Service z Deployment "połączony" jest etykietą django.

apiVersion: v1
kind: Service
metadata:
  name: projectdjango-dj-service
spec:
  selector:
    app: django
  ports:
    - protocol: TCP
      port: 8000
      targetPort: 8000

Plik ./manifests/ingress.yml

W Kubernetes, Ingress to mechanizm, który umożliwia zarządzanie dostępem do usług (Services) w klastrze poprzez definiowanie reguł routingu na poziomie warstwy aplikacji. Ingress działa jako kontroler, który zarządza ruchem HTTP i HTTPS do aplikacji wewnątrz klastra na podstawie reguł zdefiniowanych w zasobach Ingress.

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: projectdjango-ingress
spec:
  rules:
    - http:
        paths:
          - path: /
            pathType: Prefix
            backend:
              service:
                name: projectdjango-dj-service
                port:
                  number: 8000

Aplikacja manifestów Kubernetes

Aplikujemy wszystkie manifesty znajdujące się w katalogu ./manifests

kubectl apply -f ./manifests/

I sprawdzamy co zostało utworzone

kubectl get all

NAME                                               READY   STATUS    RESTARTS   AGE
pod/projectdjango-dj-deployment-675b5bdfc8-bv4z5   1/1     Running   0          10s
pod/projectdjango-pg-deployment-6f5f94df79-zs7xg   1/1     Running   0          10s

NAME                               TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)    AGE
service/kubernetes                 ClusterIP   10.96.0.1        <none>        443/TCP    24d
service/projectdjango-dj-service   ClusterIP   10.103.145.97    <none>        8000/TCP   10s
service/projectdjango-pg-service   ClusterIP   10.106.200.113   <none>        5432/TCP   10s

NAME                                          READY   UP-TO-DATE   AVAILABLE   AGE
deployment.apps/projectdjango-dj-deployment   1/1     1            1           10s
deployment.apps/projectdjango-pg-deployment   1/1     1            1           10s

NAME                                                     DESIRED   CURRENT   READY   AGE
replicaset.apps/projectdjango-dj-deployment-675b5bdfc8   1         1         1       10s
replicaset.apps/projectdjango-pg-deployment-6f5f94df79   1         1         1       10s

Sprawdź czy został utworzony PVC i PV

kubectl get pvc

NAME                   STATUS   VOLUME                                     CAPACITY   ACCESS MODES   STORAGECLASS   AGE
projectdjango-pg-pvc   Bound    pvc-de72bf3c-bcff-43cf-80b2-bdb1dc3c9eb1   1Gi        RWO            standard       24s


kubectl get pv

NAME                                       CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS   CLAIM                          STORAGECLASS   REASON   AGE
pvc-de72bf3c-bcff-43cf-80b2-bdb1dc3c9eb1   1Gi        RWO            Delete           Bound    default/projectdjango-pg-pvc   standard                117s

Sprawdzamy czy Ingress otrzymał już adres IP

kubectl get ingress projectdjango-ingress

NAME                    CLASS   HOSTS   ADDRESS        PORTS   AGE
projectdjango-ingress   nginx   *       192.168.49.2   80      53s

Jeśli w polu ADDRESS jest pusta wartość należy poczekać i ponowić sprawdzenie gdyż nadanie IP chwilę trwa.

Otwórz przeglądarkę i pod adresem http://192.168.49.2/ (w Twoim przypadku to oczywiście może być inny adres IP) powinieneś zobaczyć odpalony projekt Django.

Praca z Django w Kubernetes

Jeśli korzystałeś z mojego przykładowego projektu to przywitał Cię błąd konfiguracji Django Invalid HTTP_HOST header. Jest to łatwe do naprawienia ale pracochłonne ponieważ każda zmiana w plikach wiąże się z koniecznością wykonania paru dodatkowych czynności.

Wprowadzanie zmian w kodzie aplikacji

Po zmianie w pliku ./app/project_name/settings.py obejmującej modyfikację zmiennej ALLOWED_HOSTS = ["*"]

Powinniśmy jeszcze

Wdrażanie zmian w kodzie

  1. Zmienić numer wersji obrazu w pliku docker-compose.override.yml oraz w pliku ./manifests/django.yml na 1.0.1

  2. Przebudować obraz docker compose build django

  3. Zaaplikować zmiany kubectl apply -f ./manifests/django.yml

Taka procedura jest jak najbardziej właściwa. Na koniec jeszcze warto śledzić postęp aktualizacji.

kubectl rollout status deployment/projectdjango-dj-deployment

Nie mniej w trakcie developmentu nie jest to najwygodniejsze rozwiązanie.

Aktualizacja podów poprzez ich usunięcie

Alternatywnie możemy pominąć modyfikowania tagu obrazu i wykonać następujące kroki

  1. Przebudować obraz docker compose build django

  2. Sprawdzić nazwy pod-ów

    kubectl get pods
    NAME                                           READY   STATUS    RESTARTS   AGE
    projectdjango-dj-deployment-77f896db7d-swcnk   1/1     Running   0          19s
    projectdjango-pg-deployment-6f5f94df79-mxsp7   1/1     Running   0          135m
    
  3. Usunąć pod-a z Django

    kubectl delete pod projectdjango-dj-deployment-77f896db7d-swcnk
    

Na miejsce starego poda automatycznie zostanie utworzony nowy ale będzie on bazował już na nowo wygenerowanym obrazie nawet jak nie zmienimy mu tag-a

To wciąż nie jest wygodne rozwiązanie.

Montowanie plików hosta

W Docker Compose jest proste rozwiązanie na tego typu okoliczność polegające na utworzeniu woluminu typu bind czyli inaczej mówiąc podmontowaniu katalogu z kodem pod katalog wewnątrz kontenera. W ten sposób każda zmiana w kodzie jest natychmiast odzwierciedlona w kontenerze. W Minikube też mamy taką możliwość ale wymaga ona nieco pracy poniważ, jak już wiemy kontenery Kubernetesa są zamknięte w maszynie wirtualnej lub w kontenerze Dockera.

Użyjemy do tego hostPath (lokalne woluminy), które pozwalają na montowanie katalogów bezpośrednio z hosta VM. Jest to prostsze niż konfiguracja NFS czy Cloud Storage (patrz niżej), ale działa tylko z Minikube i nie jest zalecane dla środowisk produkcyjnych. Na komputerze lokalnym jest to jednak rozwiązanie wygodne i elastyczne i użyjemy go zarówno do podmontowania kodu aplikacji, ale też jako sposób na współdzielenie plików statycznych i mediów.


Uwaga!

W Kubernetes, do przechowywania plików wspólnych dla wszystkich podów, takich jak np. media w projekcie Django, zazwyczaj korzysta się z wolumenów. Istnieje kilka podejść, które można wykorzystać w zależności od potrzeb i środowiska. Zazwyczaj definiuje się Persistent Volume (PV), który wskazuje na fizyczne miejsce przechowywania danych (np. Network File Systems (NFS), cloud storage jak AWS S3, Google Cloud Storage, Azure Blob Storage) oraz Persistent Volume Claim (PVC) czyli żądanie dostępu do tych zasobów.


Mamy zatem do wykonania dwie akcje:

  1. Podmontowanie katalogu hosta do katalogu wewnątrz Minikube
  2. Podmontowanie katalogu Minikube do kontenera wewnątrz Pod-a

Montowanie katalogu hosta do katalogu wewnątrz Minikube

Najpierw musimy wejść do VM Minikube

minikube ssh

Wewnątrz Minikube tworzymy katalogi i ustawiamy im prawa do zapisu i odczytu. W środowisku lokalnym możemy sobie pozwolić na nadanie uprawnień 777.

mkdir -p /home/docker/project_django/app \
    && mkdir -p /home/docker/project_django/assets/static \
    && mkdir -p /home/docker/project_django/assets/collected_static \
    && mkdir -p /home/docker/project_django/assets/media

sudo chmod -R 777 /home/docker/project_django

W nowym oknie terminala montujemy katalog w którym znajduje się kod naszej aplikacji Django

minikube mount ./app:/home/docker/project_django/app

Jeśli pojawi się błąd z połączeniem może to być spowodowane ustawieniami zapory sieciowej (firewall) na hoście, która blokuje połączenie.

  Exiting due to GUEST_MOUNT_COULD_NOT_CONNECT: mount could not connect: /bin/bash -c "sudo mount -t 9p -o dfltgid=$(grep ^docker: /etc/group | cut -d: -f3),dfltuid=$(id -u docker),msize=262144,port=34869,trans=tcp,version=9p2000.L 192.168.49.1 /home/docker/project_django/app": Process exited with status 32

W moim przypadku był to port 34869, który Minikube używa do montowania.

sudo ufw allow 34869
sudo ufw status

Otworzyłem ten port i ponowiłem próbę zamontowania katalogu.

minikube mount --port=34869 ./app:/home/docker/project_django/app
📁  Mounting host path ./app into VM as /home/docker/project_django/app ...
     Mount type:   9p
     User ID:      docker
     Group ID:     docker
     Version:      9p2000.L
     Message Size: 262144
     Options:      map[]
     Bind Address: 192.168.49.1:34869
🚀  Userspace file server: ufs starting
✅  Successfully mounted ./app to /home/docker/project_django/app

📌  NOTE: This process must stay alive for the mount to be accessible ...

Proces montowania jest tak długo podtrzymywany, jak długo terminal w którym to wykonaliśmy jest otwarty i nie wciśniemy kombinacji klawiszy Ctl + C.

Możemy ponownie wejść do Minikube i sprawdzić czy montowanie się powiodło

ls /home/docker/project_django/app

Uwaga!

Po utworzeniu katalogu /home/docker/project_django/app w Minikube dobrym pomysłem - jeszcze przed podmontowaniem katalogu ./app z hosta - jest po prostu skopiowanie tam plików z tego katalogu.

scp -r -i ~/.minikube/machines/minikube/id_rsa \
    ./app docker@$(minikube ip):/home/docker/project_django

Dzięki czemu będziemy mogli wystartować projekt w Kubernetes nawet jak nie podmontujemy katalogu hosta. Inaczej pod z aplikacją Django wysypie się i zobaczymy nic nie mówiący Error. Będziemy musieli domyślić się, że nie ma plików projektu i serwer Django nie może wystartować. Dostaniemy projekt przed zmianami ale to jest lepsze niż nic nie mówiący błąd.


Montowanie katalogu Minikube do kontenera wewnątrz Pod-a

Należy zmodyfikować manifest Deployment-u kontenera z aplikacją Django ./manifests/django-deployment.yml.

Znaczną część kodu została wykropkowana aby lepiej było widać co zostało dodane do manifestu

apiVersion: apps/v1
kind: Deployment
metadata:
  name: projectdjango-dj-deployment
spec:
  selector:
    ...
  template:
    ...
    spec:
      containers:
        - name: django
          ...
          volumeMounts:
            - name: code-volume
              mountPath: /src
            - name: media-storage
              mountPath: /assets/media
            - name: static-storage
              mountPath: /assets/static
            - name: collected-static-storage
              mountPath: /assets/collected_static
      volumes:
        - name: code-volume
          hostPath:
            path: /home/docker/project_django/app
            type: Directory
        - name: media-storage
          hostPath:
            path: /home/docker/project_django/assets/media
            type: Directory
        - name: static-storage
          hostPath:
            path: /home/docker/project_django/assets/static
            type: Directory
        - name: collected-static-storage
          hostPath:
            path: /home/docker/project_django/assets/collected_static
            type: Directory

Po rozszerzeniu manifestu Deployment-u i modyfikacji ustawień projektu Django koniecznym jest jeszcze zastosowanie zmian.

kubectl apply -f ./manifests

Teraz po usunięciu gwiazdki z ustawienia ALLOWED_HOSTS = ["*"] i przeładowaniu widoku w przeglądarce (http://192.168.49.2/) powinniśmy natychmiast ponownie zobaczyć błąd zgłaszany przez Django tak jak i każdą inną zmianę.

Co do plików statycznych i mediów to wrócimy do nich za chwilę.

Wykonywanie operacji bezpośrednio w kontenerze

Do kontenerów wewnątrz pod-ów Kubernetesa można wejść. Należy jedynie sprawdzić nazwę poda, który nas interesuje.

kubectl exec -it projectdjango-dj-deployment-77f896db7d-qbb7p -- sh

Będąc w kontenerze możemy w nim wykonać migracje Django python manage.py migrate ale też przekopiować do jednego katalogu wszystkie pliki statyczne Django, co pozwoli nam sprawdzić czy pliki trafiły do podmontowanego katalogu.

python manage.py collectstatic

128 static files copied to '/collected_static'.

Po wyjściu z kontenera wchodzimy ponownie do Minikube minikube ssh i sprawdzamy czy pliki znajdują się tam, gdzie się ich spodziewamy.

ls /home/docker/project_django/assets/collected_static/

Po skopiowaniu jednego z obrazków do katalogu z mediami

cp /home/docker/project_django/assets/collected_static/admin/img/icon-alert.svg \ 
   /home/docker/project_django/assets/media

Mamy też możliwość sprawdzenia czy plik ten zobaczymy w przeglądarce http://192.168.49.2/media/icon-alert.svg.

Podsumowanie

Rozwój aplikacji w Kubernetes na komputerze lokalnym nie jest może najwygodniejszym sposobem pracy nie mniej pozwala:

  • lepiej poznać środowisko K8s i oswoić się z nim,
  • dostosować projekt do wymagań Kubernetes,
  • radzić sobie z problemami, z którymi można się później spotkać na produkcji.

Przedstawiony w tym artykule proof of concept nie wyczerpuje tematu. Jeśli chcesz wdrożyć w Minikube bardziej rozbudowany projekt Django z Celery i frontendem w Vue.js, być może pomocny okaże się artykuł "Setting up a Django project in Kubernetes with minikube" Briana Caffey.

Z kolei Alex Garnett na łamach DigitalOcean w artykule How To Use minikube for Local Kubernetes Development and Testing uczy jak monitorować i testować aplikacje.

Reset

Na koniec krótko jeszcze o czyszczeniu lub resetowaniu klastra Minikube

Możemy zdecydować się na usunięcie po prostu całego klastra

minikube delete

Jeśli jednak nie chcemy tego robić mamy kilka kroków do wykonania

kubectl delete -f ./manifests/
kubectl get all
kubectl get pvc
kubectl get ingress
kubectl get secret

Ostanie z poleceń ujawniło że sekrety nie zostały usunięte. Dlatego musimy to zrobić ręcznie.

kubectl delete projectdjango-secret

Pozostają jeszcze obiekty Docker-a do usunięcia

docker compose down -v
docker rmi projectdjango:1.0.0

Akceptuję Ta strona zapisuje niewielkie pliki tekstowe, nazywane ciasteczkami (ang. cookies) na Twoim urządzeniu w celu lepszego dostosowania treści oraz dla celów statystycznych. Możesz wyłączyć możliwość ich zapisu, zmieniając ustawienia Twojej przeglądarki. Korzystanie z tej strony bez zmiany ustawień oznacza zgodę na przechowywanie cookies w Twoim urządzeniu.