Kubernetes: Wdrażanie pierwszej aplikacji

Praktyczny przewodnik po wdrażaniu aplikacji w Kubernetes. Od instalacji minikube po Deployments, Services i ConfigMaps z konkretnymi przykładami.

Przewodnik Kubernetes do wdrożenia pierwszej aplikacji

Kubernetes (K8s) stał się de facto standardem orkiestracji kontenerów. Zaprojektowany przez Google i obecnie utrzymywany przez CNCF, Kubernetes automatyzuje wdrażanie, skalowanie i zarządzanie aplikacjami konteneryzowanymi. Niniejszy przewodnik prowadzi przez konfigurację lokalnego klastra i wdrożenie pierwszej aplikacji.

Wymagania wstępne

Zalecana jest podstawowa znajomość Dockera przed zagłębieniem się w Kubernetes. Kontenery to fundamentalne bloki, którymi zarządza Kubernetes. Wcześniejsze zapoznanie się z przewodnikiem po Dockerze znacznie ułatwia zrozumienie prezentowanych tutaj koncepcji.

Zrozumienie architektury Kubernetes

Kubernetes opiera się na architekturze master-worker. Control Plane podejmuje globalne decyzje dotyczące klastra, podczas gdy Nodes uruchamiają obciążenia.

text
# Simplified Kubernetes Architecture

┌─────────────────────────────────────────────────────────────┐
│                      CONTROL PLANE                          │
│  ┌─────────────┐  ┌─────────────┐  ┌─────────────────────┐ │
│  │ API Server  │  │ Scheduler   │  │ Controller Manager  │ │
│  └─────────────┘  └─────────────┘  └─────────────────────┘ │
│  ┌─────────────────────────────────────────────────────────┐│
│  │                        etcd                             ││
│  └─────────────────────────────────────────────────────────┘│
└─────────────────────────────────────────────────────────────┘
         ┌────────────────────┼────────────────────┐
         ▼                    ▼                    ▼
┌─────────────────┐  ┌─────────────────┐  ┌─────────────────┐
│     NODE 1      │  │     NODE 2      │  │     NODE 3      │
│  ┌───────────┐  │  │  ┌───────────┐  │  │  ┌───────────┐  │
│  │  kubelet  │  │  │  │  kubelet  │  │  │  │  kubelet  │  │
│  ├───────────┤  │  │  ├───────────┤  │  │  ├───────────┤  │
│  │ kube-proxy│  │  │  │ kube-proxy│  │  │  │ kube-proxy│  │
│  ├───────────┤  │  │  ├───────────┤  │  │  ├───────────┤  │
│  │   Pods    │  │  │  │   Pods    │  │  │  │   Pods    │  │
│  └───────────┘  │  │  └───────────┘  │  │  └───────────┘  │
└─────────────────┘  └─────────────────┘  └─────────────────┘

API Server jest punktem wejścia dla wszystkich poleceń. etcd przechowuje stan klastra. Scheduler przypisuje Pody do Nodes. Controllery utrzymują pożądany stan systemu.

Konfiguracja środowiska lokalnego

Istnieje kilka opcji eksperymentowania z Kubernetes lokalnie: minikube, kind, k3d lub Docker Desktop. Minikube pozostaje najpopularniejszym rozwiązaniem do nauki.

bash
# terminal
# Install kubectl (Kubernetes client)
curl -LO "https://dl.k8s.io/release/$(curl -L -s https://dl.k8s.io/release/stable.txt)/bin/linux/amd64/kubectl"
chmod +x kubectl
sudo mv kubectl /usr/local/bin/

# Verify installation
kubectl version --client
# Client Version: v1.31.0

# Install minikube
curl -LO https://storage.googleapis.com/minikube/releases/latest/minikube-linux-amd64
sudo install minikube-linux-amd64 /usr/local/bin/minikube

# Start the local cluster
minikube start --driver=docker --cpus=2 --memory=4096

# Check status
minikube status
# minikube: Running
# cluster: Running
# kubectl: Configured

Minikube tworzy jednowęzłowy klaster Kubernetes wewnątrz maszyny wirtualnej lub kontenera Docker. Przydzielone zasoby (CPU, pamięć) można dostosować do potrzeb.

bash
# terminal
# Access the Kubernetes dashboard (web interface)
minikube dashboard

# Check cluster nodes
kubectl get nodes
# NAME       STATUS   ROLES           AGE   VERSION
# minikube   Ready    control-plane   5m    v1.31.0

# Detailed cluster information
kubectl cluster-info
Alternatywy dla Minikube

Kind (Kubernetes in Docker) startuje szybciej i lepiej nadaje się do testów CI/CD. K3d wykorzystuje k3s, lekką dystrybucję Kubernetes. Docker Desktop integruje Kubernetes bezpośrednio, ale zużywa więcej zasobów.

Pody: jednostka podstawowa

Pod jest najmniejszą wdrażaną jednostką w Kubernetes. Pod hermetyzuje jeden lub więcej kontenerów, które współdzielą tę samą sieć i pamięć masową.

yaml
# pod-simple.yaml
apiVersion: v1
kind: Pod
metadata:
  # Unique Pod name within the namespace
  name: nginx-pod
  # Labels for organization and selection
  labels:
    app: nginx
    environment: development
spec:
  containers:
    # Main container definition
    - name: nginx
      # Docker image to use
      image: nginx:1.25-alpine
      # Ports exposed by the container
      ports:
        - containerPort: 80
      # Container resource allocation
      resources:
        requests:
          memory: "64Mi"
          cpu: "100m"
        limits:
          memory: "128Mi"
          cpu: "200m"

Ten manifest YAML deklaruje Poda zawierającego pojedynczy kontener nginx. Etykiety umożliwiają identyfikację i selekcję Podów. Zasoby określają minimalne gwarancje (requests) i maksymalne limity (limits).

bash
# terminal
# Create the Pod
kubectl apply -f pod-simple.yaml
# pod/nginx-pod created

# List Pods
kubectl get pods
# NAME        READY   STATUS    RESTARTS   AGE
# nginx-pod   1/1     Running   0          30s

# Full Pod details
kubectl describe pod nginx-pod

# Container logs
kubectl logs nginx-pod

# Execute a command inside the Pod
kubectl exec -it nginx-pod -- /bin/sh

# Delete the Pod
kubectl delete pod nginx-pod

Pody są z natury efemeryczne. W przypadku awarii lub usunięcia Kubernetes nie tworzy ich automatycznie ponownie. Deploymenty rozwiązują to ograniczenie.

Deploymenty: zarządzanie deklaratywne

Deployment definiuje pożądany stan zestawu identycznych Podów. Kubernetes automatycznie utrzymuje ten stan, tworząc, aktualizując lub usuwając Pody w razie potrzeby.

yaml
# deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  # Deployment name
  name: webapp-deployment
  labels:
    app: webapp
spec:
  # Desired number of replicas
  replicas: 3
  # Selector to identify managed Pods
  selector:
    matchLabels:
      app: webapp
  # Template for Pod creation
  template:
    metadata:
      labels:
        app: webapp
    spec:
      containers:
        - name: webapp
          image: nginx:1.25-alpine
          ports:
            - containerPort: 80
          resources:
            requests:
              memory: "64Mi"
              cpu: "100m"
            limits:
              memory: "128Mi"
              cpu: "200m"
          # Liveness probe: restarts container on failure
          livenessProbe:
            httpGet:
              path: /
              port: 80
            initialDelaySeconds: 10
            periodSeconds: 10
          # Readiness probe: removes Pod from Service on failure
          readinessProbe:
            httpGet:
              path: /
              port: 80
            initialDelaySeconds: 5
            periodSeconds: 5

Deployment tworzy ReplicaSet, który utrzymuje 3 identyczne Pody. Sondy sprawdzają stan kontenerów i pozwalają Kubernetes automatycznie reagować na problemy.

bash
# terminal
# Create the Deployment
kubectl apply -f deployment.yaml
# deployment.apps/webapp-deployment created

# Verify the Deployment
kubectl get deployments
# NAME                READY   UP-TO-DATE   AVAILABLE   AGE
# webapp-deployment   3/3     3            3           1m

# List Pods created by the Deployment
kubectl get pods -l app=webapp
# NAME                                READY   STATUS    RESTARTS   AGE
# webapp-deployment-7d9f8b6c4-abc12   1/1     Running   0          1m
# webapp-deployment-7d9f8b6c4-def34   1/1     Running   0          1m
# webapp-deployment-7d9f8b6c4-ghi56   1/1     Running   0          1m

# Manual scaling
kubectl scale deployment webapp-deployment --replicas=5

# Deployment history
kubectl rollout history deployment webapp-deployment

Usunięcie Poda automatycznie wyzwala utworzenie nowego Poda, aby utrzymać pożądaną liczbę replik.

Gotowy na rozmowy o DevOps?

Ćwicz z naszymi interaktywnymi symulatorami, flashcards i testami technicznymi.

Services: ekspozycja sieciowa

Pody mają efemeryczne adresy IP. Services zapewniają stabilny adres dostępu do zestawu Podów, z wbudowanym równoważeniem obciążenia.

yaml
# service.yaml
apiVersion: v1
kind: Service
metadata:
  name: webapp-service
spec:
  # Service type: ClusterIP (internal), NodePort, LoadBalancer
  type: ClusterIP
  # Selector to identify target Pods
  selector:
    app: webapp
  ports:
    # Port exposed by the Service
    - port: 80
      # Target container port
      targetPort: 80
      # Protocol (TCP by default)
      protocol: TCP

Ten Service ClusterIP jest dostępny tylko z wnętrza klastra. Żądania do webapp-service:80 są rozdzielane między Pody z etykietą app: webapp.

bash
# terminal
# Create the Service
kubectl apply -f service.yaml
# service/webapp-service created

# List Services
kubectl get services
# NAME             TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)   AGE
# webapp-service   ClusterIP   10.96.123.456   <none>        80/TCP    30s

# Test from a temporary Pod
kubectl run curl-test --rm -it --image=curlimages/curl -- curl webapp-service

# Detailed Service description
kubectl describe service webapp-service

Aby udostępnić aplikację poza klastrem, wymagany jest typ NodePort lub LoadBalancer.

yaml
# service-nodeport.yaml
apiVersion: v1
kind: Service
metadata:
  name: webapp-nodeport
spec:
  type: NodePort
  selector:
    app: webapp
  ports:
    - port: 80
      targetPort: 80
      # Port on each Node (30000-32767)
      nodePort: 30080

Z minikube polecenie minikube service webapp-nodeport automatycznie otwiera przeglądarkę pod właściwym adresem URL.

ConfigMaps: zewnętrzna konfiguracja

ConfigMaps oddzielają konfigurację od kodu. Wartości są wstrzykiwane jako zmienne środowiskowe lub montowane jako pliki.

yaml
# configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: webapp-config
data:
  # Simple key-value pairs
  APP_ENV: "production"
  LOG_LEVEL: "info"
  MAX_CONNECTIONS: "100"
  # Multiline configuration (complete file)
  nginx.conf: |
    server {
        listen 80;
        server_name localhost;

        location / {
            root /usr/share/nginx/html;
            index index.html;
        }

        location /health {
            return 200 'OK';
            add_header Content-Type text/plain;
        }
    }

ConfigMaps przechowują dane niewrażliwe. Dla sekretów (haseł, tokenów) bardziej odpowiednie są Secrets w Kubernetes.

yaml
# deployment-with-config.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: webapp-configured
spec:
  replicas: 2
  selector:
    matchLabels:
      app: webapp-configured
  template:
    metadata:
      labels:
        app: webapp-configured
    spec:
      containers:
        - name: webapp
          image: nginx:1.25-alpine
          ports:
            - containerPort: 80
          # Inject environment variables
          envFrom:
            - configMapRef:
                name: webapp-config
          # Or individual variables
          env:
            - name: SPECIFIC_VAR
              valueFrom:
                configMapKeyRef:
                  name: webapp-config
                  key: LOG_LEVEL
          # Mount configuration file
          volumeMounts:
            - name: nginx-config
              mountPath: /etc/nginx/conf.d/default.conf
              subPath: nginx.conf
      volumes:
        - name: nginx-config
          configMap:
            name: webapp-config

Ta konfiguracja wstrzykuje zmienne środowiskowe i montuje plik nginx.conf wewnątrz kontenera.

bash
# terminal
# Apply resources
kubectl apply -f configmap.yaml
kubectl apply -f deployment-with-config.yaml

# Verify environment variables
kubectl exec deployment/webapp-configured -- printenv | grep APP_ENV
# APP_ENV=production

# Verify mounted file
kubectl exec deployment/webapp-configured -- cat /etc/nginx/conf.d/default.conf
Aktualizacja ConfigMaps

Modyfikacja ConfigMap nie restartuje automatycznie Podów. Aby zastosować zmiany, wymagany jest ręczny restart: kubectl rollout restart deployment webapp-configured. Narzędzia takie jak Reloader automatyzują ten proces.

Secrets: wrażliwe dane

Secrets przechowują wrażliwe informacje, takie jak hasła, tokeny czy klucze SSH. Choć są zakodowane w base64, domyślnie nie są szyfrowane w spoczynku.

yaml
# secret.yaml
apiVersion: v1
kind: Secret
metadata:
  name: webapp-secrets
type: Opaque
# Values must be base64-encoded
data:
  # echo -n 'admin' | base64
  username: YWRtaW4=
  # echo -n 'supersecretpassword' | base64
  password: c3VwZXJzZWNyZXRwYXNzd29yZA==
---
# Alternative: stringData accepts plain text values
apiVersion: v1
kind: Secret
metadata:
  name: webapp-secrets-plain
type: Opaque
stringData:
  username: admin
  password: supersecretpassword

Secrets są wstrzykiwane w taki sam sposób jak ConfigMaps.

yaml
# deployment-with-secrets.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: webapp-secure
spec:
  replicas: 1
  selector:
    matchLabels:
      app: webapp-secure
  template:
    metadata:
      labels:
        app: webapp-secure
    spec:
      containers:
        - name: webapp
          image: nginx:1.25-alpine
          env:
            - name: DB_USERNAME
              valueFrom:
                secretKeyRef:
                  name: webapp-secrets
                  key: username
            - name: DB_PASSWORD
              valueFrom:
                secretKeyRef:
                  name: webapp-secrets
                  key: password
bash
# terminal
# Create the Secret
kubectl apply -f secret.yaml

# List Secrets (values are not displayed)
kubectl get secrets
# NAME              TYPE     DATA   AGE
# webapp-secrets    Opaque   2      10s

# Decode a value
kubectl get secret webapp-secrets -o jsonpath='{.data.password}' | base64 -d
# supersecretpassword

Namespaces: izolacja logiczna

Namespaces dzielą klaster na izolowane środowiska wirtualne. Ten podział pozwala zarządzać wieloma zespołami lub środowiskami w tym samym klastrze.

yaml
# namespace.yaml
apiVersion: v1
kind: Namespace
metadata:
  name: development
  labels:
    environment: development
---
apiVersion: v1
kind: Namespace
metadata:
  name: staging
  labels:
    environment: staging

Każdy zasób można utworzyć w określonym namespace.

bash
# terminal
# Create namespaces
kubectl apply -f namespace.yaml

# List namespaces
kubectl get namespaces
# NAME          STATUS   AGE
# default       Active   1d
# development   Active   10s
# staging       Active   10s

# Create a resource in a specific namespace
kubectl apply -f deployment.yaml -n development

# List Pods in a namespace
kubectl get pods -n development

# Change default namespace
kubectl config set-context --current --namespace=development

Zasoby z różnych namespace są domyślnie odizolowane. Komunikacja między namespace odbywa się poprzez wewnętrzny DNS: service-name.namespace.svc.cluster.local.

Kompletna aplikacja: złożenie zasobów

Poniżej kompletna aplikacja łącząca wszystkie przedstawione koncepcje.

yaml
# complete-app.yaml
---
# Dedicated Namespace
apiVersion: v1
kind: Namespace
metadata:
  name: myapp
---
# ConfigMap for configuration
apiVersion: v1
kind: ConfigMap
metadata:
  name: myapp-config
  namespace: myapp
data:
  APP_NAME: "MyApp"
  LOG_LEVEL: "info"
---
# Secret for sensitive data
apiVersion: v1
kind: Secret
metadata:
  name: myapp-secrets
  namespace: myapp
type: Opaque
stringData:
  api-key: "sk-1234567890abcdef"
---
# Deployment with 3 replicas
apiVersion: apps/v1
kind: Deployment
metadata:
  name: myapp
  namespace: myapp
spec:
  replicas: 3
  selector:
    matchLabels:
      app: myapp
  template:
    metadata:
      labels:
        app: myapp
    spec:
      containers:
        - name: myapp
          image: nginx:1.25-alpine
          ports:
            - containerPort: 80
          envFrom:
            - configMapRef:
                name: myapp-config
          env:
            - name: API_KEY
              valueFrom:
                secretKeyRef:
                  name: myapp-secrets
                  key: api-key
          resources:
            requests:
              memory: "64Mi"
              cpu: "100m"
            limits:
              memory: "128Mi"
              cpu: "200m"
          livenessProbe:
            httpGet:
              path: /
              port: 80
            initialDelaySeconds: 10
            periodSeconds: 10
          readinessProbe:
            httpGet:
              path: /
              port: 80
            initialDelaySeconds: 5
            periodSeconds: 5
---
# Service for internal exposure
apiVersion: v1
kind: Service
metadata:
  name: myapp-service
  namespace: myapp
spec:
  type: ClusterIP
  selector:
    app: myapp
  ports:
    - port: 80
      targetPort: 80
---
# NodePort Service for external access (development)
apiVersion: v1
kind: Service
metadata:
  name: myapp-nodeport
  namespace: myapp
spec:
  type: NodePort
  selector:
    app: myapp
  ports:
    - port: 80
      targetPort: 80
      nodePort: 30100

Ten jeden plik wdraża kompletną aplikację z zewnętrzną konfiguracją, sekretami, wysoką dostępnością i ekspozycją sieciową.

bash
# terminal
# Deploy the complete application
kubectl apply -f complete-app.yaml

# Verify all resources
kubectl get all -n myapp
# NAME                         READY   STATUS    RESTARTS   AGE
# pod/myapp-7d9f8b6c4-abc12    1/1     Running   0          30s
# pod/myapp-7d9f8b6c4-def34    1/1     Running   0          30s
# pod/myapp-7d9f8b6c4-ghi56    1/1     Running   0          30s
#
# NAME                    TYPE        CLUSTER-IP      PORT(S)        AGE
# service/myapp-service   ClusterIP   10.96.123.456   80/TCP         30s
# service/myapp-nodeport  NodePort    10.96.123.789   80:30100/TCP   30s
#
# NAME                    READY   UP-TO-DATE   AVAILABLE   AGE
# deployment.apps/myapp   3/3     3            3           30s

# Access the application with minikube
minikube service myapp-nodeport -n myapp

Aktualizacje i rollbacki

Kubernetes ułatwia stopniowe aktualizacje i rollbacki.

bash
# terminal
# Update the Deployment image
kubectl set image deployment/myapp myapp=nginx:1.26-alpine -n myapp

# Track deployment in real-time
kubectl rollout status deployment/myapp -n myapp
# Waiting for deployment "myapp" rollout to finish: 1 out of 3 new replicas updated
# Waiting for deployment "myapp" rollout to finish: 2 out of 3 new replicas updated
# deployment "myapp" successfully rolled out

# Revision history
kubectl rollout history deployment/myapp -n myapp
# REVISION  CHANGE-CAUSE
# 1         <none>
# 2         <none>

# Rollback to previous revision
kubectl rollout undo deployment/myapp -n myapp

# Rollback to a specific revision
kubectl rollout undo deployment/myapp --to-revision=1 -n myapp

Domyślna strategia aktualizacji (RollingUpdate) stopniowo zastępuje stare Pody nowymi, zapewniając ciągłą dostępność.

Kluczowe polecenia kubectl

bash
# terminal
# ========================================
# General Information
# ========================================
kubectl cluster-info                    # Cluster info
kubectl get nodes -o wide               # Nodes with details
kubectl api-resources                   # List resource types

# ========================================
# Resource Management
# ========================================
kubectl get all                         # All namespace resources
kubectl get pods -A                     # Pods from all namespaces
kubectl get pods -o wide                # Pods with IP and Node
kubectl get pods -w                     # Watch mode (real-time)

# ========================================
# Inspection and Debugging
# ========================================
kubectl describe pod <name>             # Full details
kubectl logs <pod> -f                   # Streaming logs
kubectl logs <pod> -c <container>       # Specific container logs
kubectl exec -it <pod> -- /bin/sh       # Interactive shell
kubectl port-forward <pod> 8080:80      # Local tunnel to Pod

# ========================================
# Editing and Deletion
# ========================================
kubectl edit deployment <name>          # Live editing (vi)
kubectl delete -f manifest.yaml         # Delete via file
kubectl delete pod <name> --force       # Force deletion

Podsumowanie

Kubernetes przekształca zarządzanie aplikacjami konteneryzowanymi, dostarczając deklaratywny, odporny i rozszerzalny framework. Przedstawione tu fundamentalne koncepcje stanowią podstawę wdrożeń gotowych do produkcji.

Lista kontrolna pierwszego wdrożenia Kubernetes

  • ✅ Działający lokalny klaster (minikube, kind lub k3d)
  • ✅ kubectl zainstalowany i skonfigurowany
  • ✅ Deployment z replikami i health probes
  • ✅ Service do ekspozycji sieciowej
  • ✅ ConfigMap dla zewnętrznej konfiguracji
  • ✅ Secret dla wrażliwych danych
  • ✅ Namespace dla izolacji
  • ✅ Zdefiniowane limity zasobów (requests/limits)
  • ✅ Opanowana strategia aktualizacji i rollbacku

Zacznij ćwiczyć!

Sprawdź swoją wiedzę z naszymi symulatorami rozmów i testami technicznymi.

Opanowanie Kubernetes otwiera drogę do skalowalnych architektur cloud-native. Kolejne kroki obejmują eksplorację Ingress Controllers do routingu HTTP, PersistentVolumes do przechowywania oraz Helm do zarządzania pakietami. Kubernetes staje się ważnym atutem podczas rozmów kwalifikacyjnych DevOps i SRE.

Tagi

#kubernetes
#k8s
#container orchestration
#devops
#deployment

Udostępnij

Powiązane artykuły