Pods
Que es un Pod?
Son la unidad mas pequenia que podemos tener (un runtime), los pods internamente estan compuestos por contenedores Docker o cualquier otro runtime que elijamos.
Ciclo de vidas de una app contenizada

Cuando ya tenemos nuestra app en una imagen contenerizada, tenemos que hacer el deploy, para esto existen dos maneras, una es la manera imperativa donde por linea de comandos desplegamos la app en un cluster.
Por otro lado tenemos de declarativa que es atravez de un manitest yaml_ donde tendremos los pasos para ejecutar la app en un cluster. Este metodo nos sirve ademas para tener un control automatico del estado deseado, ya que los diferentes controladores de kubernetes estaran pendientes de que tengamos online todos los detellaes y características que hayamos puesto en el manifest, por ejemplo si declaramos que queremos tener siempre 5 replicas , y se detecta que tenemos 3, se crearan nuevamente 2 instancias.
Introduccion a los Pods
En Docker el objeto minimo es el contenedor, en kubernetes eso mismo se lo denomina Pod .
Un pod no es nada mas que un contenedor que da una serie de caracteristicas y funcionalidades a los contenedores de Docker.
Podemos tener varios contenedores dentro de un Pod pero lo mejor es tener uno solo por Pod.
Que características le otorga un Pod a un cotenedor?
- Direcciones.
- Puertos.
- Hostnames.
- Sockets.
- Memoria.
- Volumenes.
- Etc.
- Ip.
Este Pod se despliega dentro del nodo de un cluster.(worker)
Los Pod son stateless no tienen estado , por lo tanto no deberiamos guardar informacion en ellos, tal como lo son los contenedores docker.

Pods con varios contenedores.
El Tema aca es pensar en microservicios, por ejemplo cada aplicacion deberia vivir y no estar fuertemente enlazada dentro de un pod con otra applicacion.
Por ejemplo si tenemos una imagen del front y una del la DB , podes pensar que lo optimo seria tener un pod con ambos container. Pero si necesitases hacer un backup de la DB tendrias que detenerla , lo que probocaria que el front funcionace mal.
Creando el primer pod de manera imperativa.
El orden indica kubectl run nombre_del_pod --image=imagen-para-que-se-base
kubectl run nginx1 --image=nginx
pod/nginx1 created
| NAME | READY | STATUS | RESTARTS | AGE |
|--------|-------|---------|----------|-----|
| nginx1 | 1/1 | Running | 0 | 68s |
Podemos usar -o wide
| NAME | READY | STATUS | RESTARTS | AGE | IP | NODE | NOMINATED NODE | READINESS GATES |
|--------|-------|---------|----------|------|-------------|----------------|----------------|-----------------|
| nginx1 | 1/1 | Running | 0 | 22m | 10.244.1.4 | micluster-m02 | none | none |
Ver las propiedades de un Pod con DESCRIBE
Si hacemos kubectl get pods
podremos ver los NAME de los pods.
Si luego hacemos kubectl describe pod/nginx1
veremos muchos mas detalles:
Name: nginx1
Namespace: default
Priority: 0
Service Account: default
Node: micluster-m02/192.168.49.3
Start Time: Sat, 28 Sep 2024 14:50:52 -0300
Labels: run=nginx1
Annotations: none
Status: Running
IP: 10.244.1.4
IPs:
IP: 10.244.1.4
Containers:
nginx1:
Container ID: docker://ae1de1ff7b424e8d4e05995418ec304a68b29575734eb586e4a70f3505d12326
Image: nginx
Image ID: docker-pullable://nginx@sha256:b5d3f3e104699f0768e5ca8626914c16e52647943c65274d8a9e63072bd015bb
Port: none
Host Port: none
State: Running
Started: Sat, 28 Sep 2024 14:51:09 -0300
Ready: True
Restart Count: 0
Environment: none
Mounts:
/var/run/secrets/kubernetes.io/serviceaccount from kube-api-access-29qbj (ro)
Conditions:
Type Status
PodReadyToStartContainers True
Initialized True
Ready True
ContainersReady True
PodScheduled True
Volumes:
kube-api-access-29qbj:
Type: Projected (a volume that contains injected data from multiple sources)
TokenExpirationSeconds: 3607
ConfigMapName: kube-root-ca.crt
ConfigMapOptional: nil
DownwardAPI: true
QoS Class: BestEffort
Node-Selectors: none
Tolerations: node.kubernetes.io/not-ready:NoExecute op=Exists for 300s
node.kubernetes.io/unreachable:NoExecute op=Exists for 300s
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal Scheduled 45m default-scheduler Successfully assigned default/nginx1 to micluster-m02
Normal Pulling 45m kubelet Pulling image "nginx"
Normal Pulled 44m kubelet Successfully pulled image "nginx" in 17.021s (17.021s including waiting). Image size: 187706909 bytes.
Normal Created 44m kubelet Created container nginx1
Normal Started 44m kubelet Started container nginx1
Es importante anteponer pod/ ya que sino, dira que no reconoce el recurso.
Ejecutar comandos contra un POD. Comando EXEC
kubectl exec nombre_del_pod comando
kubectl exec nginx1 -- ls
Para el modo interactivo seria :
kubectl exec nginx1 -it -- sh
Ver los logs de un Pod
kubectl logs nombre_del_pod
kubectl logs nginx1
Si queremos ir viendo los logs en vivo hacemos
kubectl logs -f nginx1
Si queremos ver las ultimas lineas :
kubectl logs -f nginx1 --tail=30
Probar pod con kubectl proxy - (no me funciono)
Hasta aca no podemos acceder al pod de una manera directa desde fuera. Por ejemplo si nuestro pod tiene un apache con una web, no podriamos verlo.
Pero podemos usar una herramienta: kubectl proxy
Primero cremos un server apache en un pod.
kubectl run apache --image=httpd --port=80
kubectl get pods -o wide
Hacemos kubectl proxy
veremos :
kubectl proxy
Starting to serve on 127.0.0.1:8001
Esto nos permite acceder a los recursos mediante el navegador web:
Algunas url son:
Lo que haremos para saber si apache esta funcionando, es usar la api que tenemos disponible.
Pero vayamos de a poco.Naveguemos:
http://127.0.0.1:8001/api/v1/ veremos una lista de los recursos.
Ahora agregamos namespace a la url
http://127.0.0.1:8001/api/v1/namespaces
Entre los namespaces veremos default elejimos ese y lo ponemos en la url.
http://127.0.0.1:8001/api/v1/namespaces/default
Si decimos que dentro de default queremos ver los pods ponemos:
http://127.0.0.1:8001/api/v1/namespaces/default/pods
Alli en la lista encontraremos apache y lo agregamos al url:
[http://127.0.0.1:8001/api/v1/namespaces/default/pods/apache] (http://127.0.0.1:8001/api/v1/namespaces/default/pods/apache)
Por ultimo si agregamos proxy al final , veremos la pagina web del apache:
http://127.0.0.1:8001/api/v1/namespaces/default/pods/apache/proxy
Es probable que el ejemplo anterior no te funcione. Cuando sepa lo acomodo.
Mientras tanto podemos hacer otro ejercicio que si funciona.
Otra manera de acceder al recuso del pod como servicio.
# Si ya tenes un cluster podes omitirlo
minikube start -p newCluster --nodes 2 --driver=docker
# Creamos un pod apache httpd
kubectl run httpd --image=httpd --port=80
# Vemos si se creo el pod
kubectl get pods
httpd 1/1 Running 0 22s
# Exponemos el servicio
kubectl expose pod httpd --type=NodePort --port=80 --target-port=80 --name=httpd-node-port
service/httpd-node-port exposed
# Vemos los services expuestos
kubectl get services
httpd-node-port NodePort 10.103.237.31 none 80:30995/TCP 69s
# Obtenemos la ip del nodo
kubectl get nodes -o wide
Veras algo como:
NAME | STATUS | ROLES | AGE | VERSION | INTERNAL-IP | EXTERNAL-IP |
---|---|---|---|---|---|---|
newCluster | Ready | control-plane,master | 15m | v1.31.0 | 192.168.49.2 | <none> |
newCluster-m02 | Ready | worker | 15m | v1.31.0 | 192.168.49.3 | <none> |
Elegimos una de las ips, en teoria cualquiera de las dos funcionaria, pero esto no siempre es asi, asi que elegi una y proba, sino es la otra, Anda al navegador y ponemos la IP:PUERTO donde el puerto es el que nos dio httpd-node-port NodePort 10.103.237.31 none 80:30995/TCP 69s quedaria http://192.168.49.3:30995. Deberias poder ver la web de bienvenida de httpd.
LoadBalancer vs NodePort
La principal diferencia entre un servicio NodePort y un servicio LoadBalancer en Kubernetes es cómo exponen las aplicaciones y cómo gestionan el acceso a estas aplicaciones desde fuera del clúster.
1. NodePort
- Función: Un servicio de tipo
NodePort
expone un puerto específico en cada nodo del clúster, permitiendo el acceso externo a tu aplicación desde cualquier nodo, a través de ese puerto. - Cómo funciona:
- Kubernetes asigna un puerto dentro del rango 30000–32767 (o uno personalizado que definas) en cada nodo del clúster.
- Puedes acceder a la aplicación utilizando la IP de cualquier nodo del clúster y el puerto asignado.
- Ejemplo:
- Si tienes un pod que corre en el puerto 80 y defines un servicio
NodePort
, Kubernetes asignará un puerto como30226
. - Podrás acceder a la aplicación en cualquier nodo en
http://<Node-IP>:30226
.
- Si tienes un pod que corre en el puerto 80 y defines un servicio
- Ventajas:
- Funciona en cualquier clúster de Kubernetes, ya sea en la nube o local.
- No depende de un balanceador de carga externo.
- Limitaciones:
- El balanceo de carga no es tan eficiente como con un servicio
LoadBalancer
. - Depende de que el cliente conozca la IP de al menos uno de los nodos.
- No es ideal para entornos de producción a gran escala, ya que no ofrece un balanceo de carga integrado.
- El balanceo de carga no es tan eficiente como con un servicio
2. LoadBalancer
- Función: Un servicio de tipo
LoadBalancer
expone tu aplicación utilizando un balanceador de carga externo. Es ideal para entornos de producción en la nube, donde se puede usar un balanceador de carga para distribuir el tráfico entre los nodos y los pods de manera más eficiente. - Cómo funciona:
- Kubernetes solicita al proveedor de la nube (AWS, GCP, Azure) que cree un balanceador de carga externo para tu servicio.
- Este balanceador de carga dirige el tráfico a los nodos del clúster en el puerto apropiado.
- Asigna una External IP al balanceador, lo que permite el acceso desde fuera del clúster.
- Ejemplo:
- Si defines un servicio
LoadBalancer
, Kubernetes le pide a tu proveedor de nube que cree un balanceador de carga que reciba tráfico enhttp://<External-IP>:80
y lo distribuya a los nodos del clúster.
- Si defines un servicio
- Ventajas:
- Proporciona balanceo de carga real entre los nodos y es ideal para entornos de producción.
- Simplifica el acceso externo porque proporciona una dirección IP pública directamente accesible.
- Limitaciones:
- Solo está disponible en proveedores de nube que soportan la creación automática de balanceadores de carga (AWS, GCP, Azure).
- No es una opción en clústeres locales sin herramientas adicionales (como MetalLB).
Resumen de Diferencias
Característica | NodePort | LoadBalancer |
---|---|---|
Acceso | A través de la IP de los nodos y un puerto específico. | A través de una IP pública asignada por el balanceador de carga. |
Tipo de Exposición | Exposición limitada, solo a través de los nodos. | Exposición pública a través de un balanceador de carga. |
Uso en la nube | Funciona, pero no optimizado para grandes cargas. | Ideal para entornos de nube y producción. |
Uso en local | Funciona en cualquier entorno. | Requiere MetalLB u otra herramienta en entornos locales. |
Balanceo de carga | No tiene balanceo de carga externo. | Balanceo de carga real entre los nodos y pods. |
¿Cuál usar?
- NodePort: Es útil para pruebas locales o cuando solo necesitas exponer tu aplicación rápidamente. No es ideal para entornos de producción debido a la falta de un balanceo de carga real.
- LoadBalancer: Ideal para entornos de producción en la nube, ya que proporciona una IP pública y un balanceo de carga eficaz.
Port-Forwarding
Otra manera de acceder a un recurso web que esta dentro de un pod es:
kubectl port-forward httpd 9999:80
Vamos a localhost:9999 y deberiamos poder ver la web de apache.
Esto es util en entornos de prueba no produccion.
Crear un POD mediante un MANIFEST
Lo que vamos a hacer es lo siguiente para simular un flujo real:
- Creamos la imagen
- Subimos la imagen a docker hub
- Usamos la imagen del docker hub en el archivo .yml
Primero creamos la imagen de Docker dockerfile
:
##Descargamos UBUNTU
FROM ubuntu
##Actualizamos el sistema
RUN apt-get update
##En algunas versiones de Linux es necesario configurar una variable para el TIMEZONE
ENV TZ=Europe/Madrid
##Luego creamos un fichero llamado /etc/timezone para configurar
RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone
##Instalamos NGINX
RUN apt-get install -y nginx
##Creamos un fichero index.html en el directorio por defecto de nginx
RUN echo 'Ejemplo de POD con KUBERNETES y YAML' > /var/www/html/index.html
##Arrancamos NGINX a través de ENTRYPOINT para que no pueda ser modificado en la creación del contenedor
ENTRYPOINT ["/usr/sbin/nginx", "-g", "daemon off;"]
##Exponemos el Puerto 80
EXPOSE 80
Luego creamos el MANIFEST , nginx.yml
:
apiVersion: v1
kind: Pod
metadata:
name: nginx
labels:
zone: prod
version: v1
spec:
containers:
- name: nginx
image: nahueljj/nginx:v1
Es importante que pongamos nuestra cuenta personal de dockerhub en image: nahueljj/nginx:v1
en mi caso nahueljj.
Ahora hacemos docker build -t nahueljj/nginx:v1 .
Una vez generada :
- Docker login -u mi-user
- Docker push nahueljj/nginx:v1
- Probamos la imagen subida
docker run -d -p 80:80 --name nginx nahueljj/nginx:v1
- Vamos al navegador http://localhost:80 -> Vemos la web.
5,
Docker stop nginx
Veamos en detalle el nginx.yml
apiVersion: v1
kind: Pod
metadata:
name: nginx
labels:
zone: prod
version: v1
spec:
containers:
- name: nginx
image: nahueljj/nginx:v1
- kind : Le dice a kubernetes que tipo de componente que quiero crear.
- metadata:
- name: nombre del pod.
- labels: permite poner etiquetas.
- spec: Estas características desebles se guardan en etcd.
- containers: Lista de contenedores que quiero correr en un pod.
- name: nombre del container.
- image: que imagen voy a usar para levantar el contenedor.
- containers: Lista de contenedores que quiero correr en un pod.
Una vez tengamos nuetro fichero yml hacemos:
kubectl create -f nginx.yml
El comando create
nos permite generar un recurso basado en un yml.
Ahora podemos ver si se creo correctamente con:
kubectl get pods
Podemos validar que esta funcionando con kubectl describe pod nginx
o con kubectl logs pod nginx
.
Pero tambiem podemos ver la web exponiendolo como servicio:
#exponemos el servicio
kubectl expose pod nginx --name=nginx-service --port=80 --type=LoadBalancer
#vemos el puerto del servicio
kubectl get svc nginx-service
Por ejemplo podemos ver el contenido del servicio en http://192.168.58.3:32457/
Sino podes hacer minikube ip
para ver la ip del master plane
Obtener la configuracion de un pod en JSON o YAML
Podemos obtener la config de un POD con:
kubectl get pod nginx -o yaml > config.yaml
kubectl get pod nginx -o yaml > config.json
Comando APPLY
Anteriormente creamos un POD de manera declartiva con un yml,
el comando fue kubectl create -f nginx.yml
, pero una vez que
hayamos corrido ese comando no podremos hacer modificaciones
y que estas se reflejen en tiempo de ejecucion.
Para lograr cambiar a un nuevo estado deseado tenemos que usar APPLY
de la siguiente manera:
kubectl apply -f nginx.yml
Lo que hace es , si no existe un pod lo crea , y si ya existe lo modifica.
Borrar PODS
# Eliminamos un POD o Varios
# Normmalmente hace un delete controlado siguiendo
# un orden y espera a los subprocesos a que se cierren.
kubectl delete pod pod1,pod2 ...
# Eliminamos un pod con delay en segundos.
kubectl delete pod apache --grace-period=5
# Elimina un pod automaticamente sin esperar a ningun otro proceso
delete pod apache --now
# Elimina todos los pods sin peguntar nada.
kubectl delete pods --all
# Eliminamos todo lo existente, pods ,servicios, etc.
kubectl delte all --all
PODS Multicontenedores
Vamos a hacer el siguiente ejercicio:
Tendremos un pod con un servidor nginx para mostrar una web y otro servidor a su lado que lo monitorice, que le haga un ping cada 5 segundos. Todo en el mismo POD.
manitest.yaml
apiVersion: v1
kind: Pod
metadata:
name: multi
spec:
containers:
- name: web
image: nginx
ports:
- containerPort: 80
- name: frontal
image: alpine
command: ["watch","-n5","ping","localhost"]
Cada pod tiene una ip unica que se comparte entre los containers que viven dentro de el, es por eso que podemos usar localhost en el comando.
Ejecutamos kubectl apply -f manifest.yaml
Vemos el pod generado:
kubectl get pods
multi 2/2 ContainerCreating 0 13s
Esta vez veremos 2/2 antes si habia un solo container veiamos 1/1.
Verificamos que todo funcine con kubectl describe pod multi
.
Podemos intenta ver los logs con:
kubectl logs multi
Pero esto nos devolvera solo el log del primer container, ya que lo toma por orden del yaml.
Si queremos ver el log del que esta tirando ping, usamos su nombre :
kubectl logs multi -c frontal
Mantenemos escuchado con -f
kubectl logs -f multi -c frontal
Como debe reiniciarse un pod?
Cuando creamos un pod con containers podemos declarar
restartPolicy
en los siguientes valores : always
,
onFailure
o Never
-
Always: Esta es la opción por defecto si no se especifica una restartPolicy. Kubernetes siempre reiniciará el contenedor si este termina, independientemente de si fue exitoso o fallido. Es ideal para servicios que deben ejecutarse continuamente.
-
OnFailure:
Kubernetes reiniciará el contenedor solo si este termina con un código de error distinto a 0. Útil para procesos que deben volver a intentarse en caso de fallos, como trabajos de tipo “batch” que deben completarse con éxito.
-
Never:
Kubernetes no reiniciará el contenedor sin importar si finaliza correctamente o con errores. Se utiliza en situaciones donde el contenedor debe ejecutarse una sola vez y no debe reiniciarse, como en tareas que se completan y no requieren repetición.
apiVersion: v1
kind: Pod
metadata:
name: ejemplo-pod
spec:
containers:
- name: ejemplo-container
image: nginx
restartPolicy: OnFailure
Consideraciones sobre escalado?
Cuando escalamos apps sin etado no tendremos mayor problema, el tema es cuando queremos replicar bases de datos, por ejemplo, si estamos usando postgres, o mariadb podriamos pensar en tener 3 replicas y que esas tres apunten a un mismo volumen. Lo cual es incorrecto. , compartir el mismo volumen de datos entre ambas instancias no es recomendable ni soportado ya que puede llevar a corrupción de datos y otros problemas graves. En lugar de eso, se deben utilizar estrategias de replicación y sincronización adecuadas.
Hay alternativas de MYSQL como MYSQL CLUSTER o MYSQL GALERA cada una tiene su propia estrategia para manajar la replica de las DB.