---
# System prepended metadata

title: volums-a-k8s

---

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