Kubernetes od základov
Ak vieš pracovať s Linuxom a niečo ti hovorí sieťovanie, toto je sprievodca pre teba. Vysvetľuje prečo veci fungujú tak ako fungujú — nie len príkazy.
🚀 Úvod do Kubernetes
Čo je Kubernetes?
Kubernetes (skrátene K8s) je open-source platforma na orchestráciu kontajnerov. Zjednodušene povedané: hovoríš mu "chcem bežať 3 kópie tejto aplikácie" a K8s sa postará o to, aby vždy bežali — aj keď server spadne, aj keď treba škálovať.
Prečo práve Kubernetes?
Kľúčové pojmy (slovník)
| Pojem | Čo to je | Linux analógia |
|---|---|---|
| Cluster | Sada serverov (nodes) riadených K8s | Celý server park |
| Node | Jeden fyzický alebo VM server | Jeden server |
| Pod | Najmenšia jednotka — 1+ kontajnerov | Proces (PID) |
| Deployment | Definuje, koľko podov chceme | systemd service |
| Service | Stabilná IP/DNS pre skupinu podov | load balancer / iptables |
| Namespace | Logické oddelenie zdrojov v clustri | Linux namespaces |
| Ingress | HTTP(S) router z internetu do clusterom | nginx reverse proxy |
| PVC | Žiadosť o perzistentné úložisko | LVM logical volume |
🏗️ Architektúra Kubernetes
Kubernetes cluster sa skladá z dvoch druhov serverov: Control Plane (mozog)
a Worker Nodes (svaly). Ty komunikuješ vždy len s Control Plane cez kubectl.
Čo robí každá komponenta?
kubectl hovorí práve s ním cez REST API. Overuje autentifikáciu, autorizáciu (RBAC) a validuje YAML manifesty pred ich uložením do etcd.⚙️ Inštalácia prostredia
1. Docker (container runtime)
# Ubuntu / Debian
sudo apt-get update
sudo apt-get install -y ca-certificates curl gnupg
sudo install -m 0755 -d /etc/apt/keyrings
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | \
sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg
echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] \
https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable" | \
sudo tee /etc/apt/sources.list.d/docker.list
sudo apt-get update
sudo apt-get install -y docker-ce docker-ce-cli containerd.io
# Pridaj seba do skupiny docker (bez sudo)
sudo usermod -aG docker $USER
newgrp docker
# Overenie
docker run hello-world2. kubectl
# Stiahni najnovšiu stabilnú verziu
curl -LO "https://dl.k8s.io/release/$(curl -sL https://dl.k8s.io/release/stable.txt)/bin/linux/amd64/kubectl"
# Nainštaluj
sudo install -o root -g root -m 0755 kubectl /usr/local/bin/kubectl
# Overenie
kubectl version --client
# Voliteľné: bash autocomplete
echo 'source <(kubectl completion bash)' >> ~/.bashrc
echo 'alias k=kubectl' >> ~/.bashrc
source ~/.bashrc3. Minikube
# Stiahni a nainštaluj
curl -LO https://storage.googleapis.com/minikube/releases/latest/minikube-linux-amd64
sudo install minikube-linux-amd64 /usr/local/bin/minikube
# Spusti cluster (s Docker driverom)
minikube start --driver=docker --cpus=2 --memory=4096
# Skontroluj stav
minikube status
kubectl get nodesNAME STATUS ROLES AGE VERSION
minikube Ready control-plane 1m v1.32.0minikube stop — zastav clusterminikube delete — zmaž cluster (clean slate)minikube dashboard — otvor web UIminikube addons enable ingress — zapni nginx ingress🐳 Docker & Docker Compose
Kontajner vs. virtuálny stroj
Dockerfile — recept na image
# Základný PHP-FPM image
FROM php:8.2-fpm-alpine
# Inštaluj systémové závislosti
RUN apk add --no-cache git curl
# Nastav pracovný adresár
WORKDIR /var/www/html
# Skopíruj composer.json (oddelene kvôli Docker cache vrstiev!)
COPY composer.json composer.lock ./
RUN composer install --no-dev --optimize-autoloader
# Skopíruj zvyšok kódu
COPY . .
# Použi non-root používateľa (bezpečnosť)
RUN chown -R www-data:www-data /var/www/html
USER www-data
EXPOSE 9000
CMD ["php-fpm"]composer.json, vrstvy s composer install sa znovu nezostavujú — šetrí čas. Preto COPY závislostí vždy pred COPY kódu.Základné Docker príkazy
# Zostavenie image
docker build -t moja-app:1.0 .
docker build -t moja-app:latest -f Dockerfile.prod .
# Spustenie kontajnera
docker run -d -p 8080:80 --name web nginx:alpine
docker run -it --rm alpine sh # interaktívny, automaticky zmaže po ukončení
# Správa
docker ps # bežiace kontajnery
docker ps -a # všetky vrátane zastavených
docker logs -f web # log streaming
docker exec -it web sh # shell do bežiaceho kontajnera
docker stop web && docker rm web
# Images
docker images
docker pull nginx:alpine
docker push registry.example.com/moja-app:1.0
docker rmi moja-app:1.0
# Čistenie
docker system prune -a # POZOR: zmaže všetko nepoužívanéDocker Compose — lokálne vývojové prostredie
Compose je nástroj pre spúšťanie viacerých kontajnerov naraz. V Kubernetes ho nepoužívaš priamo, ale je dôležitý na lokálny vývoj a pochopenie multi-container architektúry.
# docker-compose.yml
version: '3.9'
services:
app:
build: .
ports:
- "8080:80"
environment:
DATABASE_URL: mysql://user:pass@db:3306/myapp
depends_on:
db:
condition: service_healthy
volumes:
- .:/var/www/html # live reload pri vývoji
db:
image: mysql:8.0
environment:
MYSQL_ROOT_PASSWORD: rootpass
MYSQL_DATABASE: myapp
MYSQL_USER: user
MYSQL_PASSWORD: pass
volumes:
- db_data:/var/lib/mysql # perzistentné dáta
healthcheck:
test: ["CMD", "mysqladmin", "ping", "-h", "localhost"]
interval: 5s
timeout: 3s
retries: 5
volumes:
db_data:docker compose up -d # spusti na pozadí
docker compose logs -f app # sleduj logy
docker compose down # zastav a zmaž kontajnery (dáta ostanú vo volume)
docker compose down -v # zastav aj zmaž volumes💻 kubectl — ovládanie clustra
kubectl je CLI nástroj, ktorý komunikuje s kube-apiserver cez HTTPS. Všetko čo urobíš v Kubernetes, prejde cez neho.
Základná štruktúra príkazu
Najdôležitejšie príkazy
# ── GET: čo beží? ──────────────────────────────────────
kubectl get pods # pody v default namespace
kubectl get pods -n kube-system # pody v konkrétnom namespace
kubectl get pods -A # pody vo VŠETKÝCH namespacoch
kubectl get pods -o wide # + IP, node
kubectl get pods -o yaml # kompletný YAML
kubectl get all -n moj-namespace # pody + services + deploymenty
kubectl get nodes # zoznam nodes
kubectl get namespaces # zoznam namespacov
kubectl get services # services
kubectl get deployments # deploymenty
kubectl get pvc # persistent volume claims
# ── DESCRIBE: detailné info ────────────────────────────
kubectl describe pod # udalosti, stav, logy kontajnerov
kubectl describe node minikube
kubectl describe deployment nginx
# ── APPLY / CREATE: nasadenie ──────────────────────────
kubectl apply -f deployment.yaml # vytvor/aktualizuj z YAML (ODPORÚČANÉ)
kubectl apply -f ./k8s/ # aplikuj celý adresár
kubectl create namespace prod # rýchle vytvorenie bez YAML
# ── DELETE: mazanie ───────────────────────────────────
kubectl delete -f deployment.yaml # zmaž čo definuje YAML
kubectl delete pod <meno> # zmaž konkrétny pod (deployment ho znovu vytvorí!)
kubectl delete deployment nginx
kubectl delete namespace test # zmaže VŠETKO v namespace
# ── LOGS: logy ────────────────────────────────────────
kubectl logs <pod> # logy
kubectl logs <pod> -f # streaming
kubectl logs <pod> -c <container> # konkrétny kontajner v pode
kubectl logs <pod> --previous # logy po crashi (predchádzajúci beh)
# ── EXEC: príkazy v kontajneri ────────────────────────
kubectl exec -it <pod> -- sh # shell do kontajnera (alpine)
kubectl exec -it <pod> -- bash # bash (ubuntu/debian)
kubectl exec <pod> -- ls /var/www # jednorázový príkaz
# ── PORT-FORWARD: lokálny prístup ─────────────────────
kubectl port-forward pod/<meno> 8080:80 # localhost:8080 → pod:80
kubectl port-forward svc/nginx 8080:80 # cez service - Spusti minikube:
minikube start - Pozri sa na nodes:
kubectl get nodes - Pozri systémové pody:
kubectl get pods -n kube-system - Spusti testovací pod:
kubectl run test --image=nginx:alpine - Skontroluj stav:
kubectl get pods - Pozri detaily:
kubectl describe pod test - Otvor shell:
kubectl exec -it test -- sh - Vyčisti:
kubectl delete pod test
📦 Pody (Pods)
Pod je najmenšia nasaditeľná jednotka v Kubernetes. Obsahuje jeden alebo viac kontajnerov, ktoré zdieľajú sieťový namespace (rovnaká IP, môžu si písať cez localhost) a volumes.
YAML definícia podu
apiVersion: v1
kind: Pod
metadata:
name: moj-pod
namespace: default
labels:
app: web # labely — kľúčové pre Selektory a Services
verzia: "1.0"
spec:
containers:
- name: nginx
image: nginx:alpine
ports:
- containerPort: 80
resources:
requests:
cpu: "100m" # 0.1 CPU jadra (minimum garantované)
memory: "64Mi"
limits:
cpu: "200m" # maximum (pri prekročení: throttling pre CPU, kill pre RAM)
memory: "128Mi"
env:
- name: APP_ENV
value: "production"
livenessProbe: # K8s reštartuje kontajner ak toto zlyhá
httpGet:
path: /healthz
port: 80
initialDelaySeconds: 10
periodSeconds: 15
readinessProbe: # K8s posiela traffic len ak toto prejde
httpGet:
path: /ready
port: 80
initialDelaySeconds: 5
periodSeconds: 5Životný cyklus podu
🔄 Deploymenty & ReplicaSety
Deployment je to čo naozaj používaš v praxi. Hovorí K8s: "Chcem 3 kópie tejto aplikácie, vždy." ReplicaSet (vytvorený automaticky) udržuje presný počet podov.
apiVersion: apps/v1
kind: Deployment
metadata:
name: web-app
namespace: moj-projekt
spec:
replicas: 3 # chceme 3 pody
selector:
matchLabels:
app: web-app # riadi pody s týmto labelom
strategy:
type: RollingUpdate
rollingUpdate:
maxSurge: 1 # max +1 pod počas updatu
maxUnavailable: 0 # vždy 3 pody k dispozícii (zero downtime)
template: # šablóna pre pody
metadata:
labels:
app: web-app # musí sa zhodovať so selector.matchLabels
spec:
containers:
- name: app
image: nginx:1.25 # zmenou tagu sa spustí rolling update
ports:
- containerPort: 80
resources:
requests:
cpu: "100m"
memory: "128Mi"
limits:
cpu: "500m"
memory: "256Mi"Správa deploymentu
# Nasadenie
kubectl apply -f deployment.yaml
# Škálovanie
kubectl scale deployment web-app --replicas=5
# Update image (spustí rolling update)
kubectl set image deployment/web-app app=nginx:1.27
# Sleduj priebeh updatu
kubectl rollout status deployment/web-app
# História
kubectl rollout history deployment/web-app
# Rollback na predchádzajúcu verziu
kubectl rollout undo deployment/web-app
# Rollback na konkrétnu revíziu
kubectl rollout undo deployment/web-app --to-revision=2
# Reštart podov (napr. po zmene ConfigMap)
kubectl rollout restart deployment/web-app- Ulož deployment YAML vyššie ako
deployment.yaml - Vytvor namespace:
kubectl create namespace moj-projekt - Nasaď:
kubectl apply -f deployment.yaml - Sleduj pody:
kubectl get pods -n moj-projekt -w - V druhom termináli zmeň image:
kubectl set image deployment/web-app app=nginx:alpine -n moj-projekt - Pozoruj rolling update — pody sa postupne nahrádzajú
- Vyskúšaj rollback:
kubectl rollout undo deployment/web-app -n moj-projekt
🌐 Services & Networking
Pody majú dočasné IP adresy. Keď pod reštartuje, dostane novú IP. Service dáva stabilné DNS meno a IP, za ktorou môže byť X podov. Funguje ako load balancer.
http://web-app, CoreDNS preloží na ClusterIP, a kube-proxy prepošle request na jeden z bežiacich podov.Typy Services
NODE_IP:PORT. Používaná v Minikube na testovanie.minikube tunnel.# ClusterIP — interná komunikácia
apiVersion: v1
kind: Service
metadata:
name: web-app
namespace: moj-projekt
spec:
type: ClusterIP # default, možno vynechať
selector:
app: web-app # vyberie pody s týmto labelom
ports:
- port: 80 # port SERVICE
targetPort: 80 # port KONTAJNERA
---
# NodePort — prístup zvonku (Minikube)
apiVersion: v1
kind: Service
metadata:
name: web-app-ext
spec:
type: NodePort
selector:
app: web-app
ports:
- port: 80
targetPort: 80
nodePort: 30080 # voliteľné, inak K8s priradí náhodnýDNS v Kubernetes
CoreDNS automaticky vytvára DNS záznamy pre každú Service. Z akéhokoľvek podu v clustri môžeš volať:
- Nasaď deployment z Lab 02
- Vytvor NodePort service:
kubectl expose deployment web-app --type=NodePort --port=80 -n moj-projekt - Zisti URL:
minikube service web-app -n moj-projekt --url - Otvor URL v prehliadači alebo:
curl $(minikube service web-app -n moj-projekt --url) - Testuj DNS zvnútra:
kubectl run -it --rm dns-test --image=alpine --restart=Never -n moj-projekt -- sh
potom vnútri:wget -O- http://web-app
🔧 ConfigMaps & Secrets
Konfigurácia patrí mimo Docker image. ConfigMap ukladá necitlivé dáta (URL, feature flags), Secret citlivé (heslá, tokeny, certifikáty — base64 encoded).
# ConfigMap
apiVersion: v1
kind: ConfigMap
metadata:
name: app-config
namespace: moj-projekt
data:
APP_ENV: "production"
APP_URL: "https://moja-app.sk"
nginx.conf: | # môže obsahovať aj celé súbory
server {
listen 80;
root /var/www/html/public;
}
---
# Secret (hodnoty musia byť base64: echo -n 'heslo' | base64)
apiVersion: v1
kind: Secret
metadata:
name: app-secrets
namespace: moj-projekt
type: Opaque
data:
DB_PASSWORD: aGVzbG8xMjM= # "heslo123" v base64
API_KEY: c2VjcmV0a2V5 # "secretkey" v base64Použitie v Pode — ako env premenné
spec:
containers:
- name: app
image: moja-app:latest
env:
# Jednotlivé hodnoty z ConfigMap/Secret
- name: APP_ENV
valueFrom:
configMapKeyRef:
name: app-config
key: APP_ENV
- name: DB_PASSWORD
valueFrom:
secretKeyRef:
name: app-secrets
key: DB_PASSWORD
envFrom:
# VŠETKY hodnoty z ConfigMap naraz (prefix voliteľný)
- configMapRef:
name: app-config
- secretRef:
name: app-secretsPoužitie ako volume (súbory)
spec:
volumes:
- name: config-vol
configMap:
name: app-config
containers:
- name: nginx
volumeMounts:
- name: config-vol
mountPath: /etc/nginx/conf.d # každý kľúč = jeden súbor# Rýchle vytvorenie Secret z príkazového riadku (bez YAML)
kubectl create secret generic db-creds \
--from-literal=DB_PASSWORD=moje-heslo \
--from-literal=DB_USER=admin \
-n moj-projekt
# Secret z súboru
kubectl create secret generic tls-cert \
--from-file=tls.crt=./cert.pem \
--from-file=tls.key=./key.pem
# Prečítaj Secret (dekóduj base64)
kubectl get secret db-creds -o jsonpath='{.data.DB_PASSWORD}' | base64 -d💾 Perzistentné dáta
Kontajnery sú dočasné — keď pod zmažeš, dáta sú preč. Pre databázy, upload súbory a iné trvalé dáta potrebuješ PersistentVolume (PV) a PersistentVolumeClaim (PVC).
# PersistentVolumeClaim — "žiadosť o úložisko"
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: mysql-data
namespace: moj-projekt
spec:
accessModes:
- ReadWriteOnce # RWO: jeden node môže čítať+písať
# ReadOnlyMany (ROX): viac nodes môže čítať
# ReadWriteMany (RWX): viac nodes môže čítať+písať (napr. NFS)
storageClassName: standard # Minikube default StorageClass
resources:
requests:
storage: 5Gi
---
# Deployment s PVC
apiVersion: apps/v1
kind: Deployment
metadata:
name: mysql
namespace: moj-projekt
spec:
replicas: 1 # Databázy typicky 1 replika (bez clustering riešenia)
selector:
matchLabels:
app: mysql
template:
metadata:
labels:
app: mysql
spec:
containers:
- name: mysql
image: mysql:8.0
env:
- name: MYSQL_ROOT_PASSWORD
valueFrom:
secretKeyRef:
name: mysql-secret
key: root-password
- name: MYSQL_DATABASE
value: "myapp"
ports:
- containerPort: 3306
volumeMounts:
- name: mysql-storage
mountPath: /var/lib/mysql # kde MySQL ukladá dáta
volumes:
- name: mysql-storage
persistentVolumeClaim:
claimName: mysql-data # napojenie na PVC vyššie- Vytvor Secret:
kubectl create secret generic mysql-secret --from-literal=root-password=Heslo123 -n moj-projekt - Ulož PVC + Deployment YAML a aplikuj:
kubectl apply -f mysql.yaml - Počkaj na Running:
kubectl get pods -n moj-projekt -w - Pripoj sa k MySQL:
kubectl exec -it deploy/mysql -n moj-projekt -- mysql -uroot -pHeslo123 - Vytvor tabuľku, vlož dáta
- Zmaž pod:
kubectl delete pod -l app=mysql -n moj-projekt - Po reštarte skontroluj — dáta musia ostať!
⚡ Resource Limits
Bez limitov môže jeden pod spotrebovať všetky zdroje a "vyhladovieť" ostatné. Kubernetes pracuje s requests (garantované minimum) a limits (maximum).
CPU jednotky
LimitRange — defaults pre namespace
apiVersion: v1
kind: LimitRange
metadata:
name: default-limits
namespace: moj-projekt
spec:
limits:
- type: Container
default: # limits ak kontajner nešpecifikuje
cpu: "500m"
memory: "256Mi"
defaultRequest: # requests ak kontajner nešpecifikuje
cpu: "100m"
memory: "64Mi"
max: # nikto v namespace nemôže požiadať viac
cpu: "2"
memory: "2Gi"ResourceQuota — limity pre celý namespace
apiVersion: v1
kind: ResourceQuota
metadata:
name: namespace-quota
namespace: moj-projekt
spec:
hard:
requests.cpu: "4" # celý namespace môže requestovať max 4 CPU
requests.memory: 8Gi
limits.cpu: "8"
limits.memory: 16Gi
pods: "20" # max 20 podov v namespace
persistentvolumeclaims: "5"# Aktuálna spotreba zdrojov
kubectl top pods -n moj-projekt # potrebuje metrics-server
kubectl top nodes
# Zapni metrics-server v Minikube
minikube addons enable metrics-server⛵ Helm — package manager pre K8s
Helm je ako apt alebo pip pre Kubernetes. Namiesto manuálneho písania desiatich YAML súborov nainštaluješ celú aplikáciu jedným príkazom. Balíčky sa volajú charts.
helm install wordpress bitnami/wordpress a cez values.yaml si nastavíš čo potrebuješ.Inštalácia Helm
curl https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3 | bash
helm versionZákladné Helm príkazy
# Repozitáre (zdroje chartov)
helm repo add bitnami https://charts.bitnami.com/bitnami
helm repo add ingress-nginx https://kubernetes.github.io/ingress-nginx
helm repo update # aktualizuj zoznam
# Hľadanie
helm search repo wordpress
helm search hub nginx # hľadaj na Artifact Hub
# Inštalácia
helm install mojwordpress bitnami/wordpress \
--namespace moj-projekt \
--create-namespace \
--set wordpressUsername=admin \
--set wordpressPassword=SuperHeslo \
--set mariadb.auth.rootPassword=RootHeslo
# Inštalácia s values súborom (odporúčané)
helm install mojwordpress bitnami/wordpress \
-f values.yaml -n moj-projekt
# Zoznam nainštalovaných releasov
helm list -n moj-projekt
helm list -A # všetky namespacey
# Update (nová verzia chartu alebo zmena values)
helm upgrade mojwordpress bitnami/wordpress -f values.yaml -n moj-projekt
# Rollback
helm rollback mojwordpress 1 -n moj-projekt # číslo = revízia
# Odinštalovanie
helm uninstall mojwordpress -n moj-projekt
# Pozri čo Helm vygeneruje (bez nasadenia)
helm template mojwordpress bitnami/wordpress -f values.yamlVlastný Helm chart
helm create moja-app
# Vytvorí štruktúru:
# moja-app/
# Chart.yaml - metadata chartu
# values.yaml - default hodnoty (prepisovateľné pri inštalácii)
# templates/ - YAML šablóny s Go template syntaxou
# deployment.yaml
# service.yaml
# ingress.yaml
# _helpers.tpl - pomocné funkcie# values.yaml
replicaCount: 2
image:
repository: nginx
tag: "alpine"
pullPolicy: IfNotPresent
service:
type: ClusterIP
port: 80
ingress:
enabled: false
resources:
requests:
cpu: 100m
memory: 64Mi
limits:
cpu: 500m
memory: 128Mi# templates/deployment.yaml (Go template)
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ include "moja-app.fullname" . }}
spec:
replicas: {{ .Values.replicaCount }}
template:
spec:
containers:
- name: {{ .Chart.Name }}
image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}"
resources: {{ toYaml .Values.resources | nindent 12 }}helm repo add bitnami https://charts.bitnami.com/bitnami && helm repo updatehelm install wp bitnami/wordpress --set wordpressPassword=Heslo123 -n wordpress --create-namespacekubectl get pods -n wordpress -w(čakaj kým Running)minikube service wp-wordpress -n wordpress --url- Otvor URL a prihlásy sa (admin / Heslo123)
- Vyčisti:
helm uninstall wp -n wordpress
🔒 Ingress & Certifikáty
Ingress je HTTP/HTTPS router. Namiesto samostatného LoadBalancera pre každú aplikáciu, jeden Ingress Controller prijíma všetok HTTP traffic a podľa hostname/path ho smeruje na správnu Service.
Ingress Controller v Minikube
minikube addons enable ingress
kubectl get pods -n ingress-nginx # počkaj kým RunningIngress Resource
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: app-ingress
namespace: moj-projekt
annotations:
nginx.ingress.kubernetes.io/rewrite-target: /
spec:
ingressClassName: nginx
tls:
- hosts:
- app.local
secretName: app-tls-secret # Secret s certifikátom
rules:
- host: app.local
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: web-app
port:
number: 80Self-signed certifikát (lokálny vývoj)
# Generuj self-signed cert pre app.local
openssl req -x509 -nodes -days 365 -newkey rsa:2048 \
-keyout tls.key \
-out tls.crt \
-subj "/CN=app.local/O=Dev" \
-addext "subjectAltName=DNS:app.local"
# Vytvor TLS Secret v K8s
kubectl create secret tls app-tls-secret \
--cert=tls.crt \
--key=tls.key \
-n moj-projekt
# Pridaj do /etc/hosts (na hostiteli)
echo "$(minikube ip) app.local" | sudo tee -a /etc/hostsLet's Encrypt s cert-manager
Na produkčnom clustri s reálnou doménou cert-manager automaticky získava a obnovuje Let's Encrypt certifikáty.
# Inštalácia cert-manager cez Helm
helm repo add jetstack https://charts.jetstack.io
helm repo update
helm install cert-manager jetstack/cert-manager \
--namespace cert-manager \
--create-namespace \
--set installCRDs=true# ClusterIssuer — Let's Encrypt produkcia
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
name: letsencrypt-prod
spec:
acme:
server: https://acme-v02.api.letsencrypt.org/directory
email: admin@tvoja-domena.sk # tvoj email
privateKeySecretRef:
name: letsencrypt-prod-key
solvers:
- http01:
ingress:
class: nginx # ACME HTTP-01 challenge cez Ingress
---
# Ingress s automatickým cert-manager certifikátom
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: app-ingress
annotations:
cert-manager.io/cluster-issuer: "letsencrypt-prod" # magická annotation!
spec:
ingressClassName: nginx
tls:
- hosts:
- moja-app.sk
secretName: moja-app-tls # cert-manager sem uloží certifikát
rules:
- host: moja-app.sk
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: web-app
port:
number: 80🌿 Reálny projekt: Drupal + MySQL
Celý postup nasadenia Drupal CMS s MySQL databázou. Obsahuje: PVC pre databázu, Secrets pre heslá, Services pre komunikáciu, Ingress pre HTTP prístup.
Architektúra
# drupal-all.yaml — nasadenie v jednom súbore (oddeľ s ---)
# 1. Namespace
apiVersion: v1
kind: Namespace
metadata:
name: drupal
---
# 2. Secret pre MySQL heslá
apiVersion: v1
kind: Secret
metadata:
name: mysql-creds
namespace: drupal
type: Opaque
stringData: # stringData = automatický base64
MYSQL_ROOT_PASSWORD: "RootHeslo123"
MYSQL_DATABASE: "drupal"
MYSQL_USER: "drupal"
MYSQL_PASSWORD: "DrupalHeslo456"
---
# 3. PVC pre MySQL dáta
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: mysql-data
namespace: drupal
spec:
accessModes: [ReadWriteOnce]
resources:
requests:
storage: 10Gi
---
# 4. PVC pre Drupal súbory (upload, public files)
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: drupal-files
namespace: drupal
spec:
accessModes: [ReadWriteOnce]
resources:
requests:
storage: 5Gi
---
# 5. MySQL Deployment
apiVersion: apps/v1
kind: Deployment
metadata:
name: mysql
namespace: drupal
spec:
replicas: 1
selector:
matchLabels:
app: mysql
template:
metadata:
labels:
app: mysql
spec:
containers:
- name: mysql
image: mysql:8.0
envFrom:
- secretRef:
name: mysql-creds
ports:
- containerPort: 3306
volumeMounts:
- name: data
mountPath: /var/lib/mysql
resources:
requests:
cpu: 250m
memory: 512Mi
limits:
cpu: 1000m
memory: 1Gi
livenessProbe:
exec:
command: ["mysqladmin", "ping", "-h", "localhost"]
initialDelaySeconds: 30
periodSeconds: 10
volumes:
- name: data
persistentVolumeClaim:
claimName: mysql-data
---
# 6. MySQL Service (ClusterIP — len interné)
apiVersion: v1
kind: Service
metadata:
name: mysql
namespace: drupal
spec:
selector:
app: mysql
ports:
- port: 3306
targetPort: 3306
---
# 7. Drupal Deployment
apiVersion: apps/v1
kind: Deployment
metadata:
name: drupal
namespace: drupal
spec:
replicas: 1
selector:
matchLabels:
app: drupal
template:
metadata:
labels:
app: drupal
spec:
initContainers:
# Počkaj kým MySQL je ready
- name: wait-for-mysql
image: busybox
command: ['sh', '-c', 'until nc -z mysql 3306; do echo waiting; sleep 2; done']
containers:
- name: drupal
image: drupal:10-apache
ports:
- containerPort: 80
env:
- name: DRUPAL_DATABASE_HOST
value: "mysql" # DNS meno MySQL service
- name: DRUPAL_DATABASE_NAME
value: "drupal"
- name: DRUPAL_DATABASE_USER
value: "drupal"
- name: DRUPAL_DATABASE_PASSWORD
valueFrom:
secretKeyRef:
name: mysql-creds
key: MYSQL_PASSWORD
volumeMounts:
- name: drupal-files
mountPath: /var/www/html/sites/default/files
resources:
requests:
cpu: 200m
memory: 256Mi
limits:
cpu: 1000m
memory: 512Mi
readinessProbe:
httpGet:
path: /
port: 80
initialDelaySeconds: 30
periodSeconds: 10
volumes:
- name: drupal-files
persistentVolumeClaim:
claimName: drupal-files
---
# 8. Drupal Service
apiVersion: v1
kind: Service
metadata:
name: drupal
namespace: drupal
spec:
type: NodePort
selector:
app: drupal
ports:
- port: 80
targetPort: 80
nodePort: 30080# Nasadenie
kubectl apply -f drupal-all.yaml
# Sleduj stav
kubectl get pods -n drupal -w
# Zisti URL (Minikube)
minikube service drupal -n drupal --url
# Logy Drupal podu
kubectl logs -f deploy/drupal -n drupalmysql (meno Service). Drupal sa pripojí cez ClusterIP Service automaticky.🎵 Reálny projekt: Symfony + PostgreSQL
Symfony aplikácia vyžaduje špecifické kroky: database migrations pri nasadení, správne environment premenné a voliteľne queue worker (Messenger). Ukážeme aj Job pre jednorazové príkazy.
# symfony-all.yaml
# 1. Namespace
apiVersion: v1
kind: Namespace
metadata:
name: symfony-app
---
# 2. Secrets
apiVersion: v1
kind: Secret
metadata:
name: symfony-secrets
namespace: symfony-app
type: Opaque
stringData:
DATABASE_URL: "postgresql://symfony:heslo@postgres:5432/symfony_db?serverVersion=15"
APP_SECRET: "vygeneruj-nahodny-32-znakovy-retazec"
MAILER_DSN: "smtp://localhost:25"
---
# 3. ConfigMap (necitlivé nastavenia)
apiVersion: v1
kind: ConfigMap
metadata:
name: symfony-config
namespace: symfony-app
data:
APP_ENV: "prod"
APP_DEBUG: "0"
TRUSTED_PROXIES: "127.0.0.0/8,10.0.0.0/8,172.16.0.0/12,192.168.0.0/16"
---
# 4. PostgreSQL PVC
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: postgres-data
namespace: symfony-app
spec:
accessModes: [ReadWriteOnce]
resources:
requests:
storage: 5Gi
---
# 5. PostgreSQL Deployment
apiVersion: apps/v1
kind: Deployment
metadata:
name: postgres
namespace: symfony-app
spec:
replicas: 1
selector:
matchLabels:
app: postgres
template:
metadata:
labels:
app: postgres
spec:
containers:
- name: postgres
image: postgres:15-alpine
env:
- name: POSTGRES_DB
value: "symfony_db"
- name: POSTGRES_USER
value: "symfony"
- name: POSTGRES_PASSWORD
value: "heslo"
volumeMounts:
- name: data
mountPath: /var/lib/postgresql/data
subPath: postgres # subPath aby sa nested dir nevytváral
resources:
requests:
cpu: 100m
memory: 256Mi
limits:
cpu: 500m
memory: 512Mi
readinessProbe:
exec:
command: ["pg_isready", "-U", "symfony", "-d", "symfony_db"]
initialDelaySeconds: 10
periodSeconds: 5
volumes:
- name: data
persistentVolumeClaim:
claimName: postgres-data
---
# 6. PostgreSQL Service
apiVersion: v1
kind: Service
metadata:
name: postgres
namespace: symfony-app
spec:
selector:
app: postgres
ports:
- port: 5432
targetPort: 5432
---
# 7. Migrations Job (spustí sa raz pri každom nasadení)
apiVersion: batch/v1
kind: Job
metadata:
name: symfony-migrations-v1 # zmeň verziu pri každom nasadení
namespace: symfony-app
spec:
template:
spec:
restartPolicy: OnFailure
containers:
- name: migrations
image: moja-symfony-app:latest
command: ["php", "bin/console", "doctrine:migrations:migrate", "--no-interaction"]
envFrom:
- configMapRef:
name: symfony-config
- secretRef:
name: symfony-secrets
---
# 8. Symfony App Deployment
apiVersion: apps/v1
kind: Deployment
metadata:
name: symfony-app
namespace: symfony-app
spec:
replicas: 2
selector:
matchLabels:
app: symfony-app
template:
metadata:
labels:
app: symfony-app
spec:
containers:
- name: app
image: moja-symfony-app:latest # tvoj image z registry
ports:
- containerPort: 80
envFrom:
- configMapRef:
name: symfony-config
- secretRef:
name: symfony-secrets
resources:
requests:
cpu: 200m
memory: 128Mi
limits:
cpu: 1000m
memory: 512Mi
readinessProbe:
httpGet:
path: /health
port: 80
initialDelaySeconds: 10
periodSeconds: 5
---
# 9. Symfony Queue Worker (Messenger)
apiVersion: apps/v1
kind: Deployment
metadata:
name: symfony-worker
namespace: symfony-app
spec:
replicas: 1
selector:
matchLabels:
app: symfony-worker
template:
metadata:
labels:
app: symfony-worker
spec:
containers:
- name: worker
image: moja-symfony-app:latest
command: ["php", "bin/console", "messenger:consume", "async", "--time-limit=3600"]
envFrom:
- configMapRef:
name: symfony-config
- secretRef:
name: symfony-secrets
resources:
requests:
cpu: 100m
memory: 128Mi
limits:
cpu: 500m
memory: 256Mi
---
# 10. Symfony Service + Ingress
apiVersion: v1
kind: Service
metadata:
name: symfony-app
namespace: symfony-app
spec:
selector:
app: symfony-app
ports:
- port: 80
targetPort: 80
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: symfony-ingress
namespace: symfony-app
annotations:
cert-manager.io/cluster-issuer: "letsencrypt-prod"
spec:
ingressClassName: nginx
tls:
- hosts:
- moja-symfony-app.sk
secretName: symfony-tls
rules:
- host: moja-symfony-app.sk
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: symfony-app
port:
number: 80composer install v build stage, skopíruj len výsledok do runtime image. Image bude 2–3× menší.docker build -t moja-symfony-app:latest . && minikube image load moja-symfony-app:latest
Postup nasadenia
# 1. Nasaď infraštruktúru
kubectl apply -f symfony-all.yaml
# 2. Počkaj na PostgreSQL
kubectl wait --for=condition=ready pod -l app=postgres -n symfony-app --timeout=60s
# 3. Spusti migrácie
kubectl apply -f symfony-all.yaml # Job sa spustí automaticky
# 4. Sleduj priebeh migrácií
kubectl logs job/symfony-migrations-v1 -n symfony-app -f
# 5. Skontroluj všetko
kubectl get all -n symfony-app
# 6. Port-forward na lokálne testovanie
kubectl port-forward svc/symfony-app 8080:80 -n symfony-app
# Otvor: http://localhost:8080🎉 Gratulujeme!
Prešiel si celým sprievodcom. Tu je quick reference na každodenné použitie: