# Activitats de Volums en Kubernetes **Durada estimada:** 3-4 hores **Nivell:** Intermedi **Prerequisits:** Coneixements bàsics de Pods, Deployments i Services **Entorn:** Minikube amb driver Docker --- ## 📚 Taula de continguts 1. [Activitat 1: emptyDir - Volums temporals compartits](#activitat-1-emptydir---volums-temporals-compartits) 2. [Activitat 2: hostPath - Persistència bàsica amb el node](#activitat-2-hostpath---persistència-bàsica-amb-el-node) 3. [Activitat 3: PersistentVolume i PersistentVolumeClaim - WordPress complet](#activitat-3-persistentvolume-i-persistentvolumeclaim---wordpress-complet) 4. [Recursos addicionals](#recursos-addicionals) --- ## Activitat 1: emptyDir - Volums temporals compartits ### 🎯 Objectius - Comprendre el funcionament dels volums `emptyDir` - Aprendre a compartir dades entre containers d'un mateix Pod - Entendre el cicle de vida dels volums temporals ### 📖 Teoria Un volum `emptyDir` es crea quan s'assigna un Pod a un node i existeix mentre el Pod s'executa en aquest node. Com el seu nom indica, inicialment està buit. Tots els containers del Pod poden llegir i escriure fitxers en el volum `emptyDir`. **Característiques clau:** - ✅ Ideal per compartir dades entre containers d'un mateix Pod - ✅ Útil per emmagatzematge temporal (cache, scratch space) - ⚠️ Les dades es perden quan el Pod s'elimina o es reinicia - ⚠️ Les dades NO sobreviuen a fallades del container **Arquitectura:** ``` ┌─────────────────────────────────────┐ │ POD │ │ ┌──────────────┐ ┌─────────────┐ │ │ │ Container 1 │ │ Container 2 │ │ │ │ (Writer) │ │ (Reader) │ │ │ └──────┬───────┘ └──────┬──────┘ │ │ │ │ │ │ └────────┬────────┘ │ │ ┌───▼────┐ │ │ │emptyDir│ │ │ └────────┘ │ └─────────────────────────────────────┘ ``` ### 💻 Pràctica #### Pas 1: Crear el namespace de treball ```bash kubectl create namespace volums-lab kubectl config set-context --current --namespace=volums-lab ``` #### Pas 2: Crear el fitxer YAML del Pod Crea un fitxer anomenat `emptydir-pod.yaml`: ```yaml apiVersion: v1 kind: Pod metadata: name: shared-data-pod labels: app: emptydir-demo spec: containers: # Container que escriu dades - name: writer image: busybox:1.36 command: ["/bin/sh"] args: - -c - | while true; do echo "$(date): Message from writer" >> /data/shared.log sleep 5 done volumeMounts: - name: shared-storage mountPath: /data # Container que llegeix dades - name: reader image: busybox:1.36 command: ["/bin/sh"] args: - -c - | while true; do echo "=== Latest log entries ===" tail -n 3 /data/shared.log 2>/dev/null || echo "Waiting for data..." sleep 10 done volumeMounts: - name: shared-storage mountPath: /data readOnly: true # El reader només necessita llegir # Definició del volum emptyDir volumes: - name: shared-storage emptyDir: {} # Volum temporal buit ``` #### Pas 3: Desplegar el Pod ```bash kubectl apply -f emptydir-pod.yaml ``` #### Pas 4: Verificar el desplegament ```bash # Comprovar que el Pod està running kubectl get pods # Veure els logs del container writer kubectl logs shared-data-pod -c writer # Veure els logs del container reader kubectl logs shared-data-pod -c reader -f ``` ### ✅ Verificació i experimentació **Experiment 1: Compartició de dades** ```bash # Executar una shell interactiva al container writer kubectl exec -it shared-data-pod -c writer -- sh # Dins del container, crear un fitxer echo "Hello from writer!" > /data/test.txt cat /data/shared.log exit # Ara accedir al container reader kubectl exec -it shared-data-pod -c reader -- sh # Dins del container, llegir el fitxer cat /data/test.txt cat /data/shared.log exit ``` **Experiment 2: Permanència de dades** ```bash # Eliminar un dels containers (forçant restart) kubectl exec -it shared-data-pod -c writer -- kill 1 # Esperar que el container es reiniciï kubectl get pods -w # Comprovar si les dades segueixen existint kubectl exec -it shared-data-pod -c reader -- cat /data/test.txt # ✅ Les dades sobreviuen al restart del container ``` **Experiment 3: Eliminació del Pod** ```bash # Eliminar el Pod complet kubectl delete pod shared-data-pod # Tornar a crear-lo kubectl apply -f emptydir-pod.yaml # Intentar llegir les dades anteriors kubectl exec -it shared-data-pod -c reader -- cat /data/test.txt # ❌ Les dades NO sobreviuen a l'eliminació del Pod ``` ### 🤔 Preguntes de reflexió 1. Per què les dades sobreviuen al restart d'un container però no a l'eliminació del Pod? 2. En quins casos seria útil utilitzar un `emptyDir` en lloc d'un volum persistent? 3. Què passaria si el node on s'executa el Pod falla? ### 🧹 Neteja ```bash kubectl delete pod shared-data-pod --force ``` --- ## Activitat 2: hostPath - Persistència bàsica amb el node ### 🎯 Objectius - Comprendre el funcionament dels volums `hostPath` - Aprendre a persistir dades més enllà del cicle de vida d'un Pod - Entendre les limitacions i riscos dels volums `hostPath` ### 📖 Teoria Un volum `hostPath` munta un fitxer o directori del sistema de fitxers del node host dins del Pod. Això permet que les dades persisteixin més enllà del cicle de vida del Pod. **Característiques clau:** - ✅ Les dades sobreviuen a l'eliminació i recreació de Pods - ✅ Útil per desenvolupament i proves locals - ⚠️ Només funciona si el Pod es crea al mateix node - ⚠️ Risc de seguretat: accés directe al filesystem del node - ❌ NO recomanat per producció en clusters multi-node - ❌ Fa els Pods dependents d'un node específic **Arquitectura:** ``` ┌─────────────────────────────────┐ │ NODE (Minikube) │ │ │ │ /mnt/data/ (directori host) │ │ │ │ │ ▼ │ │ ┌──────────────┐ │ │ │ POD │ │ │ │ ┌────────┐ │ │ │ │ │Container│ │ │ │ │ │ /data ──┼─┼──► hostPath │ │ │ └────────┘ │ │ │ └──────────────┘ │ └─────────────────────────────────┘ ``` ### 💻 Pràctica #### Pas 1: Preparar el directori al node de Minikube ```bash # Accedir al node de Minikube minikube ssh # Crear el directori i afegir dades de prova sudo mkdir -p /mnt/data echo "Persistent data from host" | sudo tee /mnt/data/host-file.txt sudo chmod -R 777 /mnt/data exit ``` #### Pas 2: Crear el fitxer YAML del Deployment Crea un fitxer anomenat `hostpath-deployment.yaml`: ```yaml apiVersion: apps/v1 kind: Deployment metadata: name: nginx-hostpath labels: app: nginx-persistent spec: replicas: 1 # IMPORTANT: només 1 rèplica amb hostPath selector: matchLabels: app: nginx-persistent template: metadata: labels: app: nginx-persistent spec: containers: - name: nginx image: nginx:1.25-alpine ports: - containerPort: 80 volumeMounts: - name: html-storage mountPath: /usr/share/nginx/html - name: logs-storage mountPath: /var/log/nginx volumes: - name: html-storage hostPath: path: /mnt/data/html # Directori al host per HTML type: DirectoryOrCreate # Crea el directori si no existeix - name: logs-storage hostPath: path: /mnt/data/logs # Directori al host per logs type: DirectoryOrCreate ``` **Tipus de hostPath disponibles:** - `DirectoryOrCreate`: Crea el directori si no existeix - `Directory`: El directori ha d'existir - `FileOrCreate`: Crea el fitxer si no existeix - `File`: El fitxer ha d'existir - `Socket`, `CharDevice`, `BlockDevice`: Per recursos específics #### Pas 3: Desplegar l'aplicació ```bash kubectl apply -f hostpath-deployment.yaml # Esperar que el Pod estigui ready kubectl wait --for=condition=ready pod -l app=nginx-persistent --timeout=60s # Obtenir el nom del Pod kubectl get pods -l app=nginx-persistent ``` #### Pas 4: Afegir contingut HTML personalitzat ```bash # Obtenir el nom del Pod (substitueix <pod-name> pel nom real) export POD_NAME=$(kubectl get pods -l app=nginx-persistent -o jsonpath='{.items[0].metadata.name}') # Crear una pàgina HTML personalitzada kubectl exec $POD_NAME -- sh -c 'echo "<h1>Hello from Kubernetes with hostPath!</h1><p>This data persists across Pod restarts.</p>" > /usr/share/nginx/html/index.html' # Generar alguns logs accedint al nginx kubectl exec $POD_NAME -- wget -O /dev/null http://localhost/ ``` #### Pas 5: Crear un Service per accedir al nginx Crea un fitxer anomenat `nginx-service.yaml`: ```yaml apiVersion: v1 kind: Service metadata: name: nginx-service spec: type: NodePort selector: app: nginx-persistent ports: - protocol: TCP port: 80 targetPort: 80 nodePort: 30080 # Port accessible des del host ``` ```bash kubectl apply -f nginx-service.yaml # Obtenir la URL per accedir al servei minikube service nginx-service --url -n volums-lab ``` Obre la URL al navegador o utilitza curl: ```bash curl $(minikube service nginx-service --url -n volums-lab) ``` ### ✅ Verificació i experimentació **Experiment 1: Persistència després d'eliminar el Pod** ```bash # Eliminar el Pod (el Deployment en crearà un de nou) kubectl delete pod $POD_NAME # Esperar que el nou Pod estigui ready kubectl wait --for=condition=ready pod -l app=nginx-persistent --timeout=60s # Obtenir el nom del nou Pod export POD_NAME=$(kubectl get pods -l app=nginx-persistent -o jsonpath='{.items[0].metadata.name}') # Comprovar que el contingut HTML encara existeix kubectl exec $POD_NAME -- cat /usr/share/nginx/html/index.html # ✅ Les dades persisteixen! ``` **Experiment 2: Accedir directament a les dades al node** ```bash # Accedir al node de Minikube minikube ssh # Veure els fitxers que ha creat el Pod ls -la /mnt/data/html/ cat /mnt/data/html/index.html # Veure els logs del nginx ls -la /mnt/data/logs/ cat /mnt/data/logs/access.log # Modificar directament des del host echo "<h1>Modified from the host node</h1>" | sudo tee /mnt/data/html/index.html exit # Comprovar des del Pod kubectl exec $POD_NAME -- cat /usr/share/nginx/html/index.html # ✅ Els canvis al host es reflecteixen al Pod immediatament ``` **Experiment 3: Limitació amb múltiples rèpliques** ```bash # Intentar escalar a 2 rèpliques kubectl scale deployment nginx-hostpath --replicas=2 # Veure on s'han desplegat els Pods kubectl get pods -l app=nginx-persistent -o wide # En un cluster multi-node, els Pods podrien anar a nodes diferents # En Minikube (single-node), tots van al mateix node, però això # il·lustra per què hostPath no és adequat per producció ``` ### 🤔 Preguntes de reflexió 1. Per què `hostPath` no és recomanable en entorns de producció? 2. Què passaria si escalem a múltiples rèpliques en un cluster amb 3 nodes? 3. Quins riscos de seguretat comporta donar accés al filesystem del host? ### 🧹 Neteja ```bash kubectl delete deployment nginx-hostpath kubectl delete service nginx-service # Opcional: Netejar dades del node minikube ssh sudo rm -rf /mnt/data exit ``` --- ## Activitat 3: PersistentVolume i PersistentVolumeClaim - WordPress complet ### 🎯 Objectius - Comprendre el model de persistència professional amb PV i PVC - Desplegar una aplicació multi-tier amb persistència - Implementar un WordPress complet amb MySQL persistent - Verificar la recuperació de dades després de fallades ### 📖 Teoria El model **PersistentVolume (PV)** i **PersistentVolumeClaim (PVC)** proporciona una abstracció entre l'emmagatzematge i el seu ús: - **PersistentVolume (PV)**: Recurs d'emmagatzematge al cluster (provisionat per l'administrador o dinàmicament) - **PersistentVolumeClaim (PVC)**: Sol·licitud d'emmagatzematge per part d'un usuari - **StorageClass**: Defineix diferents "classes" d'emmagatzematge amb diferents propietats **Característiques clau:** - ✅ Abstracció entre emmagatzematge i consum - ✅ Provisionament dinàmic amb StorageClass - ✅ Persistència independent del cicle de vida dels Pods - ✅ Adequat per entorns de producció - ✅ Suport per diferents backends (NFS, iSCSI, Cloud providers, etc.) **Arquitectura de la solució:** ``` ┌─────────────────────────────────────────────────────┐ │ KUBERNETES CLUSTER │ │ │ │ ┌──────────────────┐ ┌─────────────────┐ │ │ │ WordPress Pod │────────▶│ MySQL Pod │ │ │ │ ┌────────────┐ │ │ ┌───────────┐ │ │ │ │ │ WordPress │ │ 3306 │ │ MySQL │ │ │ │ │ │ Container │ │ │ │ Container │ │ │ │ │ └─────┬──────┘ │ │ └─────┬─────┘ │ │ │ │ │ │ │ │ │ │ │ │ ┌───▼────┐ │ │ ┌───▼────┐ │ │ │ │ │PVC: WP │ │ │ │PVC: DB │ │ │ │ │ └───┬────┘ │ │ └───┬────┘ │ │ │ └────────┼─────────┘ └────────┼────────┘ │ │ │ │ │ │ ┌───▼────┐ ┌───▼────┐ │ │ │PV: WP │ │PV: DB │ │ │ └───┬────┘ └───┬────┘ │ │ │ │ │ │ ┌────▼────────────────────────────▼─────┐ │ │ │ StorageClass (standard) │ │ │ └──────────────────────────────────────┘ │ │ │ │ ┌─────────────────────────────────────────────┐ │ │ │ Secret: mysql-credentials │ │ │ │ - MYSQL_ROOT_PASSWORD │ │ │ │ - MYSQL_DATABASE │ │ │ │ - MYSQL_USER │ │ │ │ - MYSQL_PASSWORD │ │ │ └─────────────────────────────────────────────┘ │ └─────────────────────────────────────────────────────┘ ``` **Flux de treball:** 1. El desenvolupador crea un PVC sol·licitant 1GB d'emmagatzematge 2. El StorageClass provisiona automàticament un PV 3. Kubernetes fa el binding entre PVC i PV 4. El Pod munta el PVC com un volum 5. Les dades escrites persisteixen independentment del Pod ### 💻 Pràctica #### Pas 1: Verificar el StorageClass disponible ```bash # Llistar StorageClasses disponibles kubectl get storageclass # Veure detalls del StorageClass per defecte de Minikube kubectl describe storageclass standard ``` A Minikube, el StorageClass `standard` utilitza el provisionador `k8s.io/minikube-hostpath` que crea volums persistents al node. #### Pas 2: Crear el Secret per les credencials de MySQL Crea un fitxer anomenat `mysql-secret.yaml`: ```yaml apiVersion: v1 kind: Secret metadata: name: mysql-credentials type: Opaque stringData: # Credencials de MySQL (en producció, utilitzar secrets externs com Vault) MYSQL_ROOT_PASSWORD: rootpassword123 MYSQL_DATABASE: wordpress MYSQL_USER: wpuser MYSQL_PASSWORD: wppassword123 ``` ```bash kubectl apply -f mysql-secret.yaml ``` #### Pas 3: Crear el PersistentVolumeClaim per MySQL Crea un fitxer anomenat `mysql-pvc.yaml`: ```yaml apiVersion: v1 kind: PersistentVolumeClaim metadata: name: mysql-pvc labels: app: wordpress tier: database spec: accessModes: - ReadWriteOnce # El volum pot ser muntat com read-write per UN sol node resources: requests: storage: 2Gi # Sol·licitem 2GB per la base de dades storageClassName: standard # Utilitzem el StorageClass per defecte ``` **Access Modes disponibles:** - `ReadWriteOnce (RWO)`: Muntat com read-write per un sol node - `ReadOnlyMany (ROX)`: Muntat com read-only per múltiples nodes - `ReadWriteMany (RWX)`: Muntat com read-write per múltiples nodes ```bash kubectl apply -f mysql-pvc.yaml # Comprovar l'estat del PVC kubectl get pvc mysql-pvc # Veure detalls del PVC kubectl describe pvc mysql-pvc ``` #### Pas 4: Desplegar MySQL amb persistència Crea un fitxer anomenat `mysql-deployment.yaml`: ```yaml apiVersion: v1 kind: Service metadata: name: mysql labels: app: wordpress tier: database spec: ports: - port: 3306 targetPort: 3306 selector: app: wordpress tier: database clusterIP: None # Headless service per comunicació interna --- apiVersion: apps/v1 kind: Deployment metadata: name: mysql labels: app: wordpress tier: database spec: replicas: 1 selector: matchLabels: app: wordpress tier: database strategy: type: Recreate # Important per volums RWO template: metadata: labels: app: wordpress tier: database spec: containers: - name: mysql image: mysql:8.0 env: # Variables d'entorn obtingudes del Secret - name: MYSQL_ROOT_PASSWORD valueFrom: secretKeyRef: name: mysql-credentials key: MYSQL_ROOT_PASSWORD - name: MYSQL_DATABASE valueFrom: secretKeyRef: name: mysql-credentials key: MYSQL_DATABASE - name: MYSQL_USER valueFrom: secretKeyRef: name: mysql-credentials key: MYSQL_USER - name: MYSQL_PASSWORD valueFrom: secretKeyRef: name: mysql-credentials key: MYSQL_PASSWORD ports: - containerPort: 3306 name: mysql volumeMounts: - name: mysql-persistent-storage mountPath: /var/lib/mysql # Directori de dades de MySQL livenessProbe: exec: command: - mysqladmin - ping - -h - localhost initialDelaySeconds: 30 periodSeconds: 10 readinessProbe: exec: command: - mysqladmin - ping - -h - localhost initialDelaySeconds: 5 periodSeconds: 5 volumes: - name: mysql-persistent-storage persistentVolumeClaim: claimName: mysql-pvc # Referència al PVC creat anteriorment ``` ```bash kubectl apply -f mysql-deployment.yaml # Esperar que MySQL estigui ready kubectl wait --for=condition=ready pod -l tier=database --timeout=120s # Comprovar els logs de MySQL kubectl logs -l tier=database ``` #### Pas 5: Crear el PersistentVolumeClaim per WordPress Crea un fitxer anomenat `wordpress-pvc.yaml`: ```yaml apiVersion: v1 kind: PersistentVolumeClaim metadata: name: wordpress-pvc labels: app: wordpress tier: frontend spec: accessModes: - ReadWriteOnce resources: requests: storage: 3Gi # Sol·licitem 3GB per fitxers de WordPress storageClassName: standard ``` ```bash kubectl apply -f wordpress-pvc.yaml # Comprovar que ambdós PVCs estan bound kubectl get pvc ``` #### Pas 6: Desplegar WordPress amb persistència Crea un fitxer anomenat `wordpress-deployment.yaml`: ```yaml apiVersion: v1 kind: Service metadata: name: wordpress labels: app: wordpress tier: frontend spec: type: NodePort ports: - port: 80 targetPort: 80 nodePort: 30081 # Port accessible des del host selector: app: wordpress tier: frontend --- apiVersion: apps/v1 kind: Deployment metadata: name: wordpress labels: app: wordpress tier: frontend spec: replicas: 1 selector: matchLabels: app: wordpress tier: frontend strategy: type: Recreate template: metadata: labels: app: wordpress tier: frontend spec: containers: - name: wordpress image: wordpress:6.4-apache env: # Configuració de connexió a MySQL - name: WORDPRESS_DB_HOST value: mysql:3306 # Nom del servei de MySQL - name: WORDPRESS_DB_NAME valueFrom: secretKeyRef: name: mysql-credentials key: MYSQL_DATABASE - name: WORDPRESS_DB_USER valueFrom: secretKeyRef: name: mysql-credentials key: MYSQL_USER - name: WORDPRESS_DB_PASSWORD valueFrom: secretKeyRef: name: mysql-credentials key: MYSQL_PASSWORD ports: - containerPort: 80 name: wordpress volumeMounts: - name: wordpress-persistent-storage mountPath: /var/www/html # Directori arrel de WordPress livenessProbe: tcpSocket: port: 80 initialDelaySeconds: 30 periodSeconds: 10 readinessProbe: httpGet: path: /wp-login.php port: 80 initialDelaySeconds: 5 periodSeconds: 5 volumes: - name: wordpress-persistent-storage persistentVolumeClaim: claimName: wordpress-pvc ``` ```bash kubectl apply -f wordpress-deployment.yaml # Esperar que WordPress estigui ready kubectl wait --for=condition=ready pod -l tier=frontend --timeout=120s # Obtenir la URL per accedir a WordPress minikube service wordpress --url -n volums-lab ``` #### Pas 7: Configurar WordPress Obre la URL al navegador i completa la instal·lació de WordPress: 1. Selecciona l'idioma 2. Configura el lloc: - Títol del lloc: "Kubernetes WordPress Lab" - Nom d'usuari: admin - Contrasenya: (genera una contrasenya forta) - Correu electrònic: el teu email 3. Fes clic a "Install WordPress" 4. Inicia sessió amb les credencials creades ### ✅ Verificació i proves de persistència #### Prova 1: Crear contingut i verificar persistència de WordPress ```bash # Des de WordPress: # 1. Crea un nou article amb contingut # 2. Puja una imatge destacada # 3. Publica l'article # 4. Instal·la un plugin (per exemple, "Akismet Anti-Spam") # Eliminar el Pod de WordPress kubectl delete pod -l tier=frontend # Esperar que es creï un nou Pod kubectl wait --for=condition=ready pod -l tier=frontend --timeout=60s # Accedir de nou a WordPress minikube service wordpress --url -n volums-lab # ✅ Verifica que: # - L'article creat encara existeix # - Les imatges pujades es mostren correctament # - El plugin instal·lat segueix actiu ``` #### Prova 2: Verificar persistència de la base de dades MySQL ```bash # Connectar-se a MySQL i crear dades de prova export MYSQL_POD=$(kubectl get pods -l tier=database -o jsonpath='{.items[0].metadata.name}') kubectl exec -it $MYSQL_POD -- mysql -u root -prootpassword123 -e " USE wordpress; CREATE TABLE test_persistence ( id INT AUTO_INCREMENT PRIMARY KEY, message VARCHAR(255), created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ); INSERT INTO test_persistence (message) VALUES ('Data created before Pod deletion'); SELECT * FROM test_persistence; " # Eliminar el Pod de MySQL (simular fallada) kubectl delete pod $MYSQL_POD # Esperar que es creï un nou Pod kubectl wait --for=condition=ready pod -l tier=database --timeout=120s # Obtenir el nom del nou Pod export MYSQL_POD=$(kubectl get pods -l tier=database -o jsonpath='{.items[0].metadata.name}') # Verificar que les dades persisteixen kubectl exec -it $MYSQL_POD -- mysql -u root -prootpassword123 -e " USE wordpress; SELECT * FROM test_persistence; " # ✅ Les dades haurien d'existir després de la recreació del Pod ``` #### Prova 3: Verificar els PersistentVolumes ```bash # Llistar tots els PVs kubectl get pv # Veure detalls dels PVs (hauríem de veure 2 PVs en estat Bound) kubectl describe pv # Comprovar l'ús d'espai dels PVCs kubectl get pvc # Veure on s'emmagatzemen realment les dades (al node de Minikube) minikube ssh sudo ls -la /tmp/hostpath-provisioner/ # Hauríeu de veure els directoris corresponents als PVs exit ``` #### Prova 4: Simulació de fallada total (eliminar tots els Pods) ```bash # Crear més contingut a WordPress abans de la prova # - Afegeix 2-3 articles més # - Puja més imatges # - Configura un tema diferent # Eliminar TOTS els Pods (WordPress i MySQL) kubectl delete pods --all # Esperar que ambdós es recreïn kubectl wait --for=condition=ready pod -l app=wordpress --timeout=120s # Accedir de nou a WordPress minikube service wordpress --url -n volums-lab # ✅ Verifica que: # - Tot el contingut està intacte # - Les configuracions es mantenen # - Els plugins segueixen actius # - El tema personalitzat continua aplicat ``` #### Prova 5: Escalar WordPress (múltiples rèpliques) ```bash # Nota: Amb ReadWriteOnce, múltiples rèpliques al mateix node poden # causar problemes. Aquesta prova il·lustra les limitacions. kubectl scale deployment wordpress --replicas=2 # Observar el comportament kubectl get pods -l tier=frontend -w # Possibles escenaris: # - En Minikube (single-node): Ambdós Pods poden arrencar però compartir # el mateix volum pot causar problemes de concurrència # - En multi-node: Un Pod quedarà en Pending perquè RWO només permet # muntatge en un node # Tornar a 1 rèplica kubectl scale deployment wordpress --replicas=1 ``` ### 🔍 Inspecció i debugging ```bash # Veure events del namespace kubectl get events --sort-by='.lastTimestamp' # Logs de MySQL kubectl logs -l tier=database --tail=50 # Logs de WordPress kubectl logs -l tier=frontend --tail=50 # Descripció completa del deployment de MySQL kubectl describe deployment mysql # Descripció completa del deployment de WordPress kubectl describe deployment wordpress # Veure variables d'entorn del Pod de WordPress (sense mostrar secrets) kubectl exec -it $(kubectl get pods -l tier=frontend -o jsonpath='{.items[0].metadata.name}') -- env | grep WORDPRESS # Accedir a la shell de WordPress per debugging kubectl exec -it $(kubectl get pods -l tier=frontend -o jsonpath='{.items[0].metadata.name}') -- bash # Dins del container: ls -la /var/www/html/wp-content/ df -h exit ``` ### 📊 Diagrama de components desplegats ``` ┌─────────────────────────────────────────────────────────────┐ │ Namespace: volums-lab │ ├─────────────────────────────────────────────────────────────┤ │ │ │ Secrets: │ │ └─ mysql-credentials (credencials BD) │ │ │ │ PersistentVolumeClaims: │ │ ├─ mysql-pvc (2Gi, RWO) ──────┐ │ │ └─ wordpress-pvc (3Gi, RWO) ──┼───┐ │ │ │ │ │ │ Services: │ │ │ │ ├─ mysql (ClusterIP: None) │ │ │ │ └─ wordpress (NodePort: 30081) │ │ │ │ │ │ │ │ Deployments: │ │ │ │ ├─ mysql │ │ │ │ │ └─ Pod ────────────────────┘ │ │ │ │ └─ Container: mysql:8.0 │ │ │ │ └─ Volume: mysql-pvc │ │ │ │ Mount: /var/lib/mysql │ │ │ │ │ │ │ └─ wordpress │ │ │ └─ Pod ────────────────────────┘ │ │ └─ Container: wordpress:6.4-apache │ │ └─ Volume: wordpress-pvc │ │ Mount: /var/www/html │ │ │ └─────────────────────────────────────────────────────────────┘ ``` ### 🤔 Preguntes de reflexió 1. **Quina és la diferència entre un PV i un PVC?** 2. **Per què utilitzem `strategy: Recreate` en lloc de `RollingUpdate`?** 3. **Què passaria si intentem escalar MySQL a 3 rèpliques amb el volum actual?** 4. **Com podríem fer backups de les dades del volum persistent?** 5. **Què passaria si eliminem el PVC mentre el Pod encara l'està utilitzant?** ### 🎯 Exercicis addicionals (opcionals) #### Exercici A: Afegir phpMyAdmin Crea un deployment de phpMyAdmin per gestionar la base de dades visualment: ```yaml apiVersion: apps/v1 kind: Deployment metadata: name: phpmyadmin spec: replicas: 1 selector: matchLabels: app: phpmyadmin template: metadata: labels: app: phpmyadmin spec: containers: - name: phpmyadmin image: phpmyadmin:5.2 env: - name: PMA_HOST value: mysql - name: PMA_PORT value: "3306" ports: - containerPort: 80 --- apiVersion: v1 kind: Service metadata: name: phpmyadmin spec: type: NodePort ports: - port: 80 targetPort: 80 nodePort: 30082 selector: app: phpmyadmin ``` #### Exercici B: Implementar backups automàtics Crea un CronJob que faci backups periòdics de la base de dades: ```yaml apiVersion: batch/v1 kind: CronJob metadata: name: mysql-backup spec: schedule: "0 2 * * *" # Cada dia a les 2 AM jobTemplate: spec: template: spec: containers: - name: backup image: mysql:8.0 command: - /bin/sh - -c - | mysqldump -h mysql -u root -p${MYSQL_ROOT_PASSWORD} --all-databases > /backup/backup-$(date +%Y%m%d-%H%M%S).sql env: - name: MYSQL_ROOT_PASSWORD valueFrom: secretKeyRef: name: mysql-credentials key: MYSQL_ROOT_PASSWORD volumeMounts: - name: backup-storage mountPath: /backup restartPolicy: OnFailure volumes: - name: backup-storage persistentVolumeClaim: claimName: backup-pvc # Cal crear aquest PVC ``` #### Exercici C: Configurar límits de recursos Afegeix `requests` i `limits` de CPU i memòria als deployments: ```yaml resources: requests: memory: "256Mi" cpu: "250m" limits: memory: "512Mi" cpu: "500m" ``` ### 🧹 Neteja final ```bash # Eliminar tots els recursos del namespace kubectl delete namespace volums-lab # Nota: Els PVs poden quedar en estat "Released" després d'eliminar els PVCs # Per eliminar-los completament: kubectl get pv kubectl delete pv <pv-name> # Si cal # Verificar que tot s'ha eliminat kubectl get all -n volums-lab kubectl get pvc -n volums-lab kubectl get pv ``` --- ## 📚 Recursos addicionals ### Documentació oficial - [Kubernetes Volumes](https://kubernetes.io/docs/concepts/storage/volumes/) - [Persistent Volumes](https://kubernetes.io/docs/concepts/storage/persistent-volumes/) - [Storage Classes](https://kubernetes.io/docs/concepts/storage/storage-classes/) - [Dynamic Volume Provisioning](https://kubernetes.io/docs/concepts/storage/dynamic-provisioning/) ### Comparativa de tipus de volums | Tipus de volum | Persistència | Multi-node | Cas d'ús | Recomanat per producció | |----------------|--------------|------------|----------|-------------------------| | `emptyDir` | ❌ Temporal | ❌ No | Cache, scratch space | ❌ No | | `hostPath` | ✅ Persist al node | ❌ No | Dev/test local | ❌ No | | `PV/PVC` | ✅ Persistent | ✅ Depèn del backend | Dades d'aplicació | ✅ Sí | | `configMap` | N/A | ✅ Sí | Configuració | ✅ Sí | | `secret` | N/A | ✅ Sí | Credencials | ✅ Sí | ### Backends d'emmagatzematge per producció - **Cloud providers:** - AWS: EBS, EFS - GCP: Persistent Disk, Filestore - Azure: Disk, Files - **On-premise:** - NFS - Ceph / Rook - GlusterFS - Longhorn - **Altres:** - Local Persistent Volumes - iSCSI ### Bones pràctiques 1. **Utilitza sempre PV/PVC en producció**, no `hostPath` 2. **Defineix StorageClasses** per diferents tipus d'emmagatzematge (SSD, HDD, etc.) 3. **Estableix límits de quota** per namespace per controlar l'ús d'emmagatzematge 4. **Implementa backups automàtics** de volums crítics 5. **Utilitza `Recreate` strategy** per deployments amb volums RWO 6. **Monitoritza l'ús d'espai** dels volums persistents 7. **Utilitza Secrets** per credencials, no variables d'entorn en text pla 8. **Documenta les polítiques de retenció** (`Retain`, `Delete`, `Recycle`) 9. **Testa els procediments de recuperació** regularment ### Solució de problemes comuns **Problema: PVC en estat "Pending"** ```bash # Comprovar events kubectl describe pvc <pvc-name> # Possibles causes: # - No hi ha StorageClass disponible # - StorageClass no pot provisionar el volum # - No hi ha espai disponible ``` **Problema: Pod no pot muntar el volum** ```bash # Comprovar events del Pod kubectl describe pod <pod-name> # Possibles causes: # - PVC no està en estat Bound # - Access mode incompatible # - Volum ja muntat en un altre node (amb RWO) ``` **Problema: Dades corrompudes després de fallada** ```bash # Comprovar l'estat del filesystem kubectl exec <pod-name> -- df -h kubectl exec <pod-name> -- ls -la <mount-path> # Per MySQL, comprovar integritat: kubectl exec <mysql-pod> -- mysqlcheck -u root -p<password> --all-databases ``` --- ## 🎓 Resum i conclusions ### Què hem après 1. **emptyDir**: Volums temporals ideals per compartir dades entre containers del mateix Pod 2. **hostPath**: Persistència bàsica lligada al node (només per dev/test) 3. **PV/PVC**: Model professional de persistència, adequat per producció ### Evolució del coneixement ``` emptyDir (Temporal) ↓ └─► Dades compartides dins del Pod hostPath (Persistent al node) ↓ └─► Dades que sobreviuen al Pod però lligades al node PV/PVC (Persistent i portable) ↓ └─► Abstracció professional, independència d'infraestructura ``` ### Pròxims passos - Explorar **StatefulSets** per aplicacions amb identitat de xarxa persistent - Implementar **backup i restore** amb eines com Velero - Estudiar **CSI (Container Storage Interface)** per backends avançats - Aprendre sobre **Volume Snapshots** per crear còpies instantànies - Investigar **Storage Quotas** i **Resource Quotas** per gestió d'espai --- **Autor:** Activitats per aprendre Kubernetes amb Minikube **Data:** Gener 2026 **Versió:** 1.0 **Llicència:** Creative Commons BY-SA 4.0 --- **Nota final:** Aquestes activitats estan dissenyades per executar-se en un entorn de Minikube local. Per utilitzar-les en un cluster de producció, caldrà adaptar els StorageClasses, els backends d'emmagatzematge i les configuracions de seguretat segons l'entorn específic.