Pràctica 2: Desplegament amb Kubernetes
Ara que ja tenim una comprensió sòlida dels conceptes bàsics i tenim el nostre entorn configurat, és hora de posar les mans a l'obra i veure com funciona tot això en la pràctica. Anem a començar amb un exemple senzill però complet: desplegar un servidor web NGINX.
Primer exemple pràctic: desplegant un servidor web NGINX
L'objectiu d'aquest exercici és desplegar un servidor web NGINX que tingui alta disponibilitat, és a dir, que estigui funcionant en múltiples rèpliques perquè si una falla, les altres continuïn servint. També volem que sigui accessible des de fora del clúster i que tingui monitoratge de salut automàtic.
Creant el Deployment
El primer pas és crear el Deployment que gestionarà els nostres pods. Creeu un fitxer nou anomenat nginx-deployment.yaml i obriu-lo amb el vostre editor de text preferit. Anem a construir aquest fitxer pas a pas, entenent cada part.
apiVersion: apps/v1
kind: Deployment
metadata:
name: webserver-nginx
labels:
app: webserver
spec:
replicas: 3
selector:
matchLabels:
app: webserver
template:
metadata:
labels:
app: webserver
spec:
containers:
- name: nginx
image: nginx:1.27-alpine
ports:
- containerPort: 80
resources:
requests:
memory: "64Mi"
cpu: "100m"
limits:
memory: "128Mi"
cpu: "200m"
livenessProbe:
httpGet:
path: /
port: 80
initialDelaySeconds: 15
periodSeconds: 10
readinessProbe:
httpGet:
path: /
port: 80
initialDelaySeconds: 5
periodSeconds: 5
Analitzem aquest fitxer secció per secció. A dalt de tot indiquem que estem creant un recurs de tipus Deployment de la API apps/v1. Li donem el nom "webserver-nginx" i una etiqueta "app: webserver" que ens servirà per identificar-lo.
A la secció spec és on definim com volem que sigui aquest Deployment. Primer de tot, diem que volem 3 rèpliques. Això significa que Kubernetes crearà i mantindrà sempre 3 pods idèntics funcionant. Per què tres? És un bon nombre per començar: tens redundància (si un falla tens dos més), però no estàs gastant massa recursos.
El selector amb matchLabels és molt important. Diu: "aquest Deployment gestiona tots els pods que tinguin l'etiqueta app: webserver". Això és el que permet al Deployment saber quins pods ha de controlar.
La secció template defineix com seran els pods que el Deployment crearà. Primer els donem l'etiqueta "app: webserver" (que coincideix amb el selector que hem definit abans). Després especifiquem que cada pod tindrà un contenidor anomenat nginx que usa la imatge nginx:1.27-alpine. Hem triat la variant alpine perquè és més lleugera que la versió estàndard (ocupa menys espai i s'inicia més ràpid), però funciona exactament igual.
La secció de resources és crucial per a un entorn de producció. Aquí estem dient dues coses diferents: requests és la quantitat mínima de recursos que el pod necessita per funcionar (64Mi de memòria i 100m de CPU, on 100m és un dècim d'un core de CPU). Limits és el màxim que pot fer servir (128Mi de memòria i 200m de CPU). Kubernetes utilitza aquests valors per decidir on col·locar els pods i per assegurar que cap pod monopolitzi els recursos del node.
Les proves de salut són molt importants. livenessProbe comprova si el contenidor està viu. Si aquesta prova falla diverses vegades, Kubernetes reiniciarà el pod automàticament. En el nostre cas, estem dient: "fes una petició HTTP GET a la ruta / del port 80. Espera 15 segons després d'iniciar el contenidor abans de començar a comprovar, i després comprova cada 10 segons".
readinessProbe és similar, però serveix per saber si el pod està llest per rebre trànsit. Si aquesta prova falla, Kubernetes deixa d'enviar trànsit a aquest pod (però no el reinicia). Això és útil quan un pod necessita temps per inicialitzar-se o per carregar dades. En el nostre cas, començem a comprovar als 5 segons i ho fem cada 5 segons.
Podeu trobar més informació sobre com crear Deployments a https://kubernetes.io/docs/tasks/run-application/run-stateless-application-deployment/
Creant el Service per exposar l'aplicació
Ara que tenim el Deployment definit, necessitem una manera d'accedir als nostres pods. Aquí és on entra el Service. Creeu un segon fitxer anomenat nginx-service.yaml:
apiVersion: v1
kind: Service
metadata:
name: webserver-service
spec:
type: NodePort
selector:
app: webserver
ports:
- protocol: TCP
port: 80
targetPort: 80
nodePort: 30080
Aquest Service és més senzill que el Deployment. Li donem el nom "webserver-service" i especifiquem que serà de tipus NodePort, que com recordeu, obre un port en tots els nodes del clúster per permetre l'accés extern.
El selector "app: webserver" és la clau: aquest Service trobarà automàticament tots els pods que tinguin aquesta etiqueta i els enviarà trànsit. No hem hagut d'especificar IPs ni noms de pods, només l'etiqueta. Això és molt potent perquè significa que si escale el Deployment a 10 rèpliques, el Service automàticament començarà a enviar trànsit a totes les 10 sense que haguem de canviar res.
La secció de ports defineix tres valors diferents. El port 80 és el port en què el Service escolta dins del clúster. Altres aplicacions del clúster poden accedir al nostre servidor web fent una petició a "webserver-service:80". El targetPort 80 indica el port on els nostres pods escolten, que coincideix amb el containerPort que vam definir al Deployment. Finalment, nodePort 30080 és el port que s'obrirà en tots els nodes del clúster per permetre l'accés des de fora.
Desplegant tot al clúster
Ara que tenim els dos fitxers YAML preparats, és hora de desplegar-los al clúster. Obriu un terminal a la carpeta on heu guardat els fitxers i executeu aquestes comandes:
La comanda apply és molt versàtil: si el recurs no existeix, el crea, i si ja existeix, l'actualitza amb els canvis que hi hagueu fet. Hauríeu de veure missatges confirmant que el deployment i el service s'han creat.
Ara podem verificar que tot funciona correctament. Primer, mirem l'estat del Deployment:
Això us mostrarà una llista dels deployments que teniu. Hauríeu de veure "webserver-nginx" amb 3/3 en la columna READY, que indica que les tres rèpliques estan funcionant correctament.
Per veure els pods individuals que s'han creat, executeu:
L'opció -l app=webserver filtra els pods per etiqueta, així només veureu els pods del vostre servidor web. Hauríeu de veure tres pods amb noms com "webserver-nginx-xxxxxxxxxx-xxxxx". Els nombres del final són generats automàticament per Kubernetes.
Per veure el Service, executeu:
Això us mostrarà el vostre service "webserver-service" amb el tipus NodePort i els ports configurats.
Accedint al servidor web
Ara ve la part emocionant: accedir al nostre servidor web. Si esteu usant Minikube, la manera més senzilla és executar:
Aquesta comanda us donarà una URL que podeu obrir al navegador per veure la pàgina de bienvenida de NGINX. El que fa Minikube per sota és crear un túnel des del vostre ordinador fins al clúster perquè pugueu accedir fàcilment.
Alternativament, podeu obtenir l'IP de Minikube amb "minikube ip" i després accedir directament a http://[IP_DE_MINIKUBE]:30080
Provant l'escalabilitat i l'auto-recuperació
Un dels grans avantatges de Kubernetes és la facilitat per escalar. Provem d'augmentar el nombre de rèpliques del nostre servidor web:
Aquesta comanda li diu a Kubernetes que volem 5 rèpliques en lloc de 3. Si executeu immediatament "kubectl get pods -l app=webserver", veureu com Kubernetes està creant dos nous pods. Al cap de pocs segons, tindreu 5 pods funcionant, i el Service automàticament començarà a distribuir el trànsit entre els cinc.
També podem provar l'auto-recuperació. Seleccioneu el nom d'un dels pods (el podeu veure amb kubectl get pods) i elimine'l:
Si immediatament torneu a executar "kubectl get pods -l app=webserver", veureu alguna cosa interessant: un pod estarà en estat "Terminating" (s'està eliminant), però ja hi haurà un nou pod en estat "ContainerCreating" o "Running". Kubernetes ha detectat que un pod ha desaparegut i automàticament n'ha creat un de nou per mantenir les 5 rèpliques que hem demanat. Això és l'auto-recuperació en acció.
Segon exemple pràctic: configurant un proxy invers amb NGINX Ingress
Aquest exemple és especialment interessant perquè mostra com Kubernetes pot gestionar múltiples aplicacions web diferents accedint-hi totes pel mateix port però amb paths o dominis diferents. És exactament el que faríeu amb un servidor NGINX o Apache tradicional actuant com a proxy invers, però amb tota la potència i flexibilitat de Kubernetes.
Entenent què farem
L'objectiu és desplegar dues aplicacions web diferents al nostre clúster. Cada aplicació mostrarà una pàgina diferent per poder distingir-les fàcilment. Després configurarem un Ingress perquè puguin accedir a les dues aplicacions des del mateix domini però amb paths diferents. Per exemple, http://myapp.local/app1 mostrarà la primera aplicació i http://myapp.local/app2 mostrarà la segona.
Instal·lant l'NGINX Ingress Controller
Abans de poder utilitzar Ingress, necessitem instal·lar l'Ingress Controller. Recordeu que l'Ingress (el recurs que definirem després) és només la configuració, però necessitem el Controller que implementi aquesta configuració.
Si esteu usant Minikube, la instal·lació és extremadament senzilla:
Això instal·la i configura automàticament l'NGINX Ingress Controller versió 1.11 o superior. Podeu verificar que s'ha instal·lat correctament executant:
Hauríeu de veure diversos pods en el namespace "ingress-nginx", incloent el "ingress-nginx-controller". Quan aquest pod estigui en estat "Running", vol dir que l'Ingress Controller està llest per rebre trànsit.
Si no esteu usant Minikube, sinó un clúster real (baremet o al núvol), haureu d'instal·lar l'NGINX Ingress Controller manualment. La forma més directa és executar:
kubectl apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/controller-v1.11.2/deploy/static/provider/baremetal/deploy.yaml
Aquesta comanda descarrega i aplica tots els recursos necessaris per executar l'Ingress Controller. Trobareu tota la documentació oficial a https://kubernetes.github.io/ingress-nginx/
Desplegant la primera aplicació
Ara crearem la primera aplicació. Per fer-ho més interessant i visual, utilitzarem un ConfigMap per definir un HTML personalitzat. Creeu un fitxer anomenat app1-deployment.yaml:
apiVersion: apps/v1
kind: Deployment
metadata:
name: app1-deployment
spec:
replicas: 2
selector:
matchLabels:
app: app1
template:
metadata:
labels:
app: app1
spec:
containers:
- name: nginx
image: nginx:1.27-alpine
ports:
- containerPort: 80
volumeMounts:
- name: html
mountPath: /usr/share/nginx/html
volumes:
- name: html
configMap:
name: app1-html
---
apiVersion: v1
kind: ConfigMap
metadata:
name: app1-html
data:
index.html: |
<html>
<head>
<title>App 1</title>
<style>
body { font-family: Arial; text-align: center; padding: 50px; background-color: #e3f2fd; }
h1 { color: #1976d2; }
</style>
</head>
<body>
<h1>Benvingut a l'Aplicació 1</h1>
<p>Aquesta és la primera aplicació del nostre clúster Kubernetes</p>
</body>
</html>
---
apiVersion: v1
kind: Service
metadata:
name: app1-service
spec:
selector:
app: app1
ports:
- port: 80
targetPort: 80
Aquest fitxer defineix tres recursos alhora (separats per ---). Primer tenim el Deployment, que crea 2 rèpliques d'NGINX. La part interessant és el volumeMounts: estem muntant un volum anomenat "html" a la ruta /usr/share/nginx/html, que és on NGINX busca els fitxers HTML per defecte.
Aquest volum ve d'un ConfigMap anomenat "app1-html", que és el segon recurs que definim. Un ConfigMap és un recurs de Kubernetes que permet emmagatzemar dades de configuració, en aquest cas, el contingut del fitxer index.html. D'aquesta manera, podem canviar el contingut del nostre servidor web sense haver de crear una nova imatge de Docker.
Finalment, definim un Service de tipus ClusterIP (el tipus per defecte) que exposa els pods internament al clúster. Fixeu-vos que no especifiquem NodePort perquè no volem accedir directament a aquest servei des de fora, sinó a través de l'Ingress.
Desplegant la segona aplicació
Ara crearem la segona aplicació seguint el mateix patró. Creeu un fitxer app2-deployment.yaml:
apiVersion: apps/v1
kind: Deployment
metadata:
name: app2-deployment
spec:
replicas: 2
selector:
matchLabels:
app: app2
template:
metadata:
labels:
app: app2
spec:
containers:
- name: nginx
image: nginx:1.27-alpine
ports:
- containerPort: 80
volumeMounts:
- name: html
mountPath: /usr/share/nginx/html
volumes:
- name: html
configMap:
name: app2-html
---
apiVersion: v1
kind: ConfigMap
metadata:
name: app2-html
data:
index.html: |
<html>
<head>
<title>App 2</title>
<style>
body { font-family: Arial; text-align: center; padding: 50px; background-color: #f3e5f5; }
h1 { color: #7b1fa2; }
</style>
</head>
<body>
<h1>Benvingut a l'Aplicació 2</h1>
<p>Aquesta és la segona aplicació del nostre clúster Kubernetes</p>
</body>
</html>
---
apiVersion: v1
kind: Service
metadata:
name: app2-service
spec:
selector:
app: app2
ports:
- port: 80
targetPort: 80
És pràcticament idèntic a l'aplicació 1, però amb etiquetes diferents (app: app2) i un HTML diferent amb altres colors perquè puguem distingir fàcilment quina aplicació estem veient.
Configurant l'Ingress: el cor del proxy invers
Ara ve la part més interessant: configurar l'Ingress que actuarà com a proxy invers. L'Ingress és el que rebrà totes les peticions HTTP externes i les redirigirà a l'aplicació corresponent segons el path de la URL. Creeu un fitxer anomenat ingress.yaml:
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: multi-app-ingress
annotations:
nginx.ingress.kubernetes.io/rewrite-target: /
nginx.ingress.kubernetes.io/ssl-redirect: "false"
spec:
ingressClassName: nginx
rules:
- host: myapp.local
http:
paths:
- path: /app1
pathType: Prefix
backend:
service:
name: app1-service
port:
number: 80
- path: /app2
pathType: Prefix
backend:
service:
name: app2-service
port:
number: 80
Aquest Ingress és el cervell de la nostra configuració de proxy invers. Anem a entendre cada part detalladament.
A la secció de metadata, li donem el nom "multi-app-ingress" i afegim dues anotacions importants. L'anotació nginx.ingress.kubernetes.io/rewrite-target: / és crucial: diu a l'Ingress que quan redirigeix la petició al servei, ha de reescriure el path a /. Això significa que una petició a /app1/index.html es converteix en /index.html quan arriba al nostre servei. Sense això, NGINX buscaria el fitxer a /app1/index.html dins del contenidor, que no existeix.
L'anotació nginx.ingress.kubernetes.io/ssl-redirect: "false" desactiva la redirecció automàtica de HTTP a HTTPS. Per defecte, NGINX Ingress Controller intenta forçar HTTPS, però com que estem fent proves locals sense certificats SSL, desactivem aquesta funcionalitat.
L'ingressClassName: nginx especifica quin Ingress Controller ha de processar aquest Ingress. Això és important si teniu múltiples Ingress Controllers instal·lats al clúster.
La secció rules és on definim les regles d'enrutament. Primer especifiquem un host: "myapp.local". Això significa que l'Ingress només processarà peticions que tinguin aquest host en la capçalera HTTP. Després definim els paths: qualsevol petició a /app1 (i tot el que comenci per aquest path, gràcies a pathType: Prefix) s'enviarà al servei "app1-service" al port 80. Igualment, les peticions a /app2 s'enviaran a "app2-service".
Desplegant tots els components
Ara que tenim tots els fitxers preparats, és hora de desplegar-ho tot al clúster. Executeu aquestes comandes en ordre:
kubectl apply -f app1-deployment.yaml
kubectl apply -f app2-deployment.yaml
kubectl apply -f ingress.yaml
Cadascuna d'aquestes comandes crearà múltiples recursos (Deployment, ConfigMap i Service per a cada aplicació, i l'Ingress). Podeu verificar que tot s'ha creat correctament:
Quan executeu "kubectl get ingress", veureu el vostre Ingress "multi-app-ingress". A la columna ADDRESS hauria d'aparèixer una IP (pot trigar uns segons). Això vol dir que l'Ingress Controller ha processat el recurs i està llest per rebre trànsit.
Per veure més detalls sobre l'Ingress, executeu:
Això us mostrarà tota la configuració, incloent les regles d'enrutament que hem definit. És molt útil per depurar si alguna cosa no funciona com s'esperava.
Configurant l'accés local
Perquè puguem accedir a les nostres aplicacions usant el domini "myapp.local", hem de configurar el nostre ordinador perquè sàpiga que aquest domini apunta al nostre clúster Kubernetes. Això es fa editant el fitxer /etc/hosts (en Linux i Mac) o C:\Windows\System32\drivers\etc\hosts (en Windows).
Primer, necessitem saber quina IP té el nostre clúster. Si esteu usant Minikube, obteniu-la amb:
Això us donarà una IP com ara 192.168.49.2 (la vostra pot ser diferent). Ara heu d'editar el fitxer hosts. En Linux o Mac, obriu un terminal i executeu:
Afegiu una línia al final del fitxer:
Recordeu substituir la IP per la que us hagi donat "minikube ip". Guardeu el fitxer i sortiu de l'editor (a nano, Ctrl+O per guardar, Ctrl+X per sortir).
Provant el nostre proxy invers
Ara ve el moment de la veritat: comprovar que tot funciona correctament. Obriu un navegador web i aneu a:
Hauríeu de veure la pàgina amb fons blau que diu "Benvingut a l'Aplicació 1". Ara aneu a:
I hauríeu de veure una pàgina amb fons lila que diu "Benvingut a l'Aplicació 2".
El que està passant per sota és fascinant: el vostre navegador fa una petició HTTP al domini "myapp.local", que el vostre fitxer hosts redirigeix a la IP del clúster Minikube. L'Ingress Controller d'NGINX rep aquesta petició, mira el host i el path, i segons les regles que hem definit a l'Ingress, redirigeix la petició al servei corresponent (app1-service o app2-service). Aquest servei distribueix la petició entre les rèpliques disponibles (en tenim 2 de cada), i finalment un dels pods processa la petició i retorna el HTML que veieu al navegador.
També podeu provar les aplicacions des de la línia de comandes amb curl:
Hauríeu de veure el codi HTML de cada aplicació.
Podeu trobar tota la documentació sobre Ingress a https://kubernetes.io/docs/concepts/services-networking/ingress/ i sobre NGINX Ingress Controller a https://kubernetes.github.io/ingress-nginx/
Tercer exemple: enrutament basat en dominis (Virtual Hosts)
L'exemple anterior ens ha mostrat com enrutar segons el path de la URL, però hi ha una altra manera molt potent d'organitzar les vostres aplicacions: usant dominis diferents per a cada aplicació. Això és el que tradicionalment s'anomena virtual hosts en servidors web com Apache o NGINX.
La idea és senzilla: en lloc de tenir /app1 i /app2 al mateix domini, tindríem app1.local i app2.local, cada un apuntant a la seva aplicació. Això té avantatges: cada aplicació pot tenir la seva pròpia configuració SSL, les URLs són més netes (no cal el prefix), i és més fàcil de gestionar si teniu moltes aplicacions diferents.
Podem reutilitzar els mateixos deployments i services que vam crear a l'exemple anterior. L'únic que canviarà és la configuració de l'Ingress. Creeu un fitxer anomenat ingress-vhosts.yaml:
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: virtual-host-ingress
annotations:
nginx.ingress.kubernetes.io/ssl-redirect: "false"
spec:
ingressClassName: nginx
rules:
- host: app1.local
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: app1-service
port:
number: 80
- host: app2.local
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: app2-service
port:
number: 80
La diferència clau és que ara tenim dues regles diferents, una per a cada host. La primera regla diu: "quan rebi una petició per al host app1.local, independentment del path, envia-la a app1-service". La segona regla fa el mateix per a app2.local i app2-service.
Fixeu-vos que el path ara és simplement /, que vol dir "qualsevol path". També hem eliminat l'anotació de rewrite-target perquè ja no és necessària: les peticions arriben directament amb el path que han de tenir.
Per utilitzar aquesta configuració, primer elimineu l'Ingress anterior si encara el teniu:
I després despleg
ueu el nou:
Ara heu d'actualitzar el vostre fitxer /etc/hosts per incloure els dos nous dominis:
Podeu provar-ho ara visitant http://app1.local i http://app2.local al vostre navegador. Veureu que cada domini mostra la seva aplicació corresponent, i no cal especificar cap path.
Quart exemple: afegint HTTPS amb certificats SSL/TLS
Fins ara hem estat treballant només amb HTTP, però en un entorn de producció segurament voldreu HTTPS. L'Ingress de Kubernetes fa que afegir HTTPS sigui relativament senzill. El que farem és crear un certificat SSL (en aquest cas autosignat, només per a proves), emmagatzemar-lo en un Secret de Kubernetes, i configurar l'Ingress per utilitzar-lo.
Creant el certificat SSL
Primer, generarem un certificat autosignat usant OpenSSL. Aquest certificat només és vàlid per a proves locals, en producció hauríeu d'usar un certificat real d'una autoritat de certificació com Let's Encrypt. Executeu aquesta comanda:
openssl req -x509 -nodes -days 365 -newkey rsa:2048 \
-keyout tls.key -out tls.crt \
-subj "/CN=myapp.local/O=myapp"
Aquesta comanda crea dos fitxers: tls.key (la clau privada) i tls.crt (el certificat). Els paràmetres indiquen que volem un certificat X.509 autosignat (-x509), sense contrasenya (-nodes), vàlid durant 365 dies, amb una clau RSA de 2048 bits, i per al domini myapp.local.
Ara hem de crear un Secret de Kubernetes que contingui aquests fitxers. Un Secret és un recurs especial que emmagatzema dades sensibles de forma segura:
El tipus de secret tls és específic per a certificats SSL/TLS. Podeu verificar que s'ha creat correctament amb:
Configurant l'Ingress amb TLS
Ara crearem un nou Ingress que utilitzi aquest certificat. Creeu un fitxer ingress-tls.yaml:
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: tls-ingress
annotations:
nginx.ingress.kubernetes.io/force-ssl-redirect: "true"
spec:
ingressClassName: nginx
tls:
- hosts:
- myapp.local
secretName: myapp-tls
rules:
- host: myapp.local
http:
paths:
- path: /app1
pathType: Prefix
backend:
service:
name: app1-service
port:
number: 80
- path: /app2
pathType: Prefix
backend:
service:
name: app2-service
port:
number: 80
La secció tls és la novetat aquí. Indica que per als hosts llistats (en aquest cas myapp.local), l'Ingress ha d'utilitzar el certificat SSL emmagatzemat al Secret "myapp-tls". També hem afegit l'anotació force-ssl-redirect: "true", que fa que qualsevol petició HTTP es redirigeixi automàticament a HTTPS.
Desplegeu aquest Ingress:
kubectl delete ingress virtual-host-ingress # Si el teniu del pas anterior
kubectl apply -f ingress-tls.yaml
Ara podeu accedir a les vostres aplicacions via HTTPS:
L'opció -k indica a curl que ignori els errors de certificat (necessària perquè estem usant un certificat autosignat que el navegador no confia). Si obriu https://myapp.local/app1 al navegador, probablement veureu un avís de seguretat dient que el certificat no és de confiança. Això és normal amb certificats autosignats; en producció usaríeu un certificat real i aquest avís no apareixeria.
Podeu veure tota la documentació sobre TLS a Ingress a https://kubernetes.io/docs/concepts/services-networking/ingress/#tls
Exercicis proposats per a pràctiques
Exercici 1: Bàsic
- Desplega un servidor web NGINX amb 2 rèpliques
- Exposa'l mitjançant un Service de tipus NodePort
- Accedeix al servei des del navegador
- Escala el deployment a 4 rèpliques
Exercici 2: Intermedi
- Crea dues aplicacions web diferents
- Configura un Ingress per enrutar tràfic segons el path (/app1, /app2)
- Configura health checks per a ambdues aplicacions
- Simula una caiguda d'un pod i observa com Kubernetes el recupera
Exercici 3: Avançat
- Desplega una aplicació web amb base de dades
- Configura Persistent Volumes per a la base de dades
- Implementa Ingress amb TLS
- Configura límits de recursos i monitoritza l'ús
- Implementa una estrategia de rolling update