Revisión
La idea aquí es solo abordar una revisión de los diferentes recursos de Kubernetes.
Pods
Son la menor unidad de Kubernetes. Un pod no debe ejecutar más de una instancia del mismo contenedor. Es posible que un pod tenga más de un contenedor, pero generalmente son sidecars que apoyan al contenedor principal. No se escala una aplicación creando más de un contenedor de la aplicación dentro del mismo pod.
Estos contenedores pueden comunicarse directamente (localhost) ya que están compartiendo el mismo espacio de red. Los contenedores en un pod también comparten el mismo espacio de almacenamiento.
kubectl run nginx --image nginx
pod/nginx created
kubectl get pods
NAME READY STATUS RESTARTS AGE
nginx 1/1 Running 0 41s
kubectl describe pod nginx
Name: nginx
Namespace: default
Priority: 0
Service Account: default
Node: k3d-k3d-cluster-agent-1/172.18.0.4
Start Time: Sat, 23 Dec 2023 10:21:05 -0300
Labels: run=nginx
Annotations: <none>
Status: Running
IP: 10.42.1.25
IPs:
IP: 10.42.1.25
Containers:
nginx:
Container ID: containerd://70ebdd2cc5ebf9ce43d69d323a17c8db53022221d2bc813b4b2f3838d30b6d9d
Image: nginx
Image ID: docker.io/library/nginx@sha256:2bdc49f2f8ae8d8dc50ed00f2ee56d00385c6f8bc8a8b320d0a294d9e3b49026
Port: <none>
Host Port: <none>
State: Running
Started: Sat, 23 Dec 2023 10:21:13 -0300
Ready: True
Restart Count: 0
Environment: <none>
Mounts:
/var/run/secrets/kubernetes.io/serviceaccount from kube-api-access-rswr7 (ro)
Conditions:
Type Status
Initialized True
Ready True
ContainersReady True
PodScheduled True
Volumes:
kube-api-access-rswr7:
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 75s default-scheduler Successfully assigned default/nginx to k3d-k3d-cluster-agent-1
Normal Pulling 76s kubelet Pulling image "nginx"
Normal Pulled 68s kubelet Successfully pulled image "nginx" in 7.355673083s (7.355680823s including waiting)
Normal Created 68s kubelet Created container nginx
Normal Started 68s kubelet Started container nginx
Creando con un archivo podemos ejecutar con un apply.
kubectl apply -f pod.yaml
# O
kubectl create -f pod.yaml
Cualquier manifiesto yaml en Kubernetes posee las claves abajo.
apiVersion:
kind:
metadata:
spec:
Para pod el kind es Pod.
Cree un pod con este archivo. kubectl apply -f pod.yaml.
apiVersion: v1
kind: Pod
metadata:
name: nginx
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx
- name: redis
image: redis
Algunos comandos iniciales esenciales.
kubectl get nodes
kubectl get pods
kubectl get pods -o wide
kubectl get pods -n default
kubectl describe pods nginx -n default
kubectl edit pods pod_name
kubectl get pods -o wide
kubectl run nginx-1 --image nginx
kubectl run nginx-2 --image nginx --dry-run=client -o yaml
kubectl delete pods nginx nginx-1
Replica Set
Este es un controlador especial que asegura que una cantidad idéntica del mismo pod esté en ejecución en el cluster, garantizando alta disponibilidad. Supervisa para garantizar que, si un pod se detiene, sea automáticamente reiniciado para atender la cantidad deseada. Lanzar pods aislados, sin un ReplicaSet, no garantiza que serán reiniciados automáticamente en caso de fallo.
Los pods pueden extenderse por múltiples nodos para balancear la carga entre los nodos.
ReplicationController es el kind antiguo, el nuevo a ser usado es el ReplicaSet



Observe que necesitamos del selector para que el ReplicaSet sepa qué pod controlará y monitoreará. El selector es necesario pues es posible crear un pod anteriormente al ReplicaSet y después crear un ReplicaSet para ese pod sin definirlo en el template.
apiVersion: apps/v1
kind: ReplicaSet
metadata:
name: myapp-rs
labels:
app: myapp
spec:
template:
metadata:
name: nginx
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx
selector:
matchLabels:
app: nginx
replicas: 3
kubectl get replicaset
NAME DESIRED CURRENT READY AGE
myapp-rs 3 3 3 29s
# rs = replicaset
kubectl get rs
NAME DESIRED CURRENT READY AGE
myapp-rs 3 3 3 39s
kubectl describe rs myapp-rs
NAME READY STATUS RESTARTS AGE
myapp-rs-prnh7 1/1 Running 0 4h12m
myapp-rs-7l8z6 1/1 Running 0 4h12m
myapp-rs-z8vzw 1/1 Running 0 4h12m
Name: myapp-rs
Namespace: default
Selector: app=nginx
Labels: app=myapp
Annotations: <none>
Replicas: 3 current / 3 desired
Pods Status: 3 Running / 0 Waiting / 0 Succeeded / 0 Failed
Pod Template:
Labels: app=nginx
Containers:
nginx:
Image: nginx
Port: <none>
Host Port: <none>
Environment: <none>
Mounts: <none>
Volumes: <none>
Events: <none>
Observe que el ReplicaSet creó los pods y añadió unos números y algunos caracteres después.
Un detalle importante es que el template siempre es necesario. Si los pods definidos en el selector ya existieran no los creará, pero si alguno de ellos falla y el ReplicaSet necesita lanzar uno nuevo es en su propio template que verificará los requisitos para lanzar un nuevo pod para sustituir el que falta.
Para escalar más réplicas tenemos algunos métodos.
kubectl scale --replicas 6 rs myapp-rs
kubectl get pods
NAME READY STATUS RESTARTS AGE
myapp-rs-prnh7 1/1 Running 0 4h25m
myapp-rs-7l8z6 1/1 Running 0 4h25m
myapp-rs-z8vzw 1/1 Running 0 4h25m
myapp-rs-jwxf5 1/1 Running 0 47s
myapp-rs-79hps 1/1 Running 0 47s
myapp-rs-8qp6p 1/1 Running 0 47s
# otro método usando el archivo
kubectl scale --replicas 6 -f ./files/replicaset.yaml
replicaset.apps/myapp-rs scaled
# en este método si hubiéramos editado el archivo replicaset.yaml con 6 lo sustituiría, pero terminó volviendo pues estaba con 3
kubectl replace -f ./files/replicaset.yaml
replicaset.apps/myapp-rs replaced
kubectl get pods
NAME READY STATUS RESTARTS AGE
myapp-rs-prnh7 1/1 Running 0 4h27m
myapp-rs-7l8z6 1/1 Running 0 4h27m
myapp-rs-z8vzw 1/1 Running 0 4h27m
# elimine todos los pods y vea qué sucede
kubectl delete pods --selector app=nginx
pod "myapp-rs-w7fwh" deleted
pod "myapp-rs-thfbw" deleted
pod "myapp-rs-szr6m" deleted
# vea que recreó nuevamente con 4s
kubectl get pods
NAME READY STATUS RESTARTS AGE
myapp-rs-5j77g 1/1 Running 0 4s
myapp-rs-hm9nz 1/1 Running 0 4s
myapp-rs-dmh29 1/1 Running 0 4s
# o usando el comando edit... edité de 3 para 6 nuevamente
kubectl edit replicasets myapp-rs
replicaset.apps/myapp-rs edited
kubectl get pods
NAME READY STATUS RESTARTS AGE
myapp-rs-prnh7 1/1 Running 0 4h30m
myapp-rs-7l8z6 1/1 Running 0 4h30m
myapp-rs-z8vzw 1/1 Running 0 4h30m
myapp-rs-b9bzj 1/1 Running 0 8s
myapp-rs-2x426 1/1 Running 0 8s
myapp-rs-l2jvq 1/1 Running 0 8s
kubectl delete rs myapp-rs
replicaset.apps "myapp-rs" deleted
kubectl get pods
No resources found in default namespace.
kubectl create -f ./files/replicaset.yaml
replicaset.apps/myapp-rs created
kubectl get pods
NAME READY STATUS RESTARTS AGE
myapp-rs-crfns 1/1 Running 0 12s
myapp-rs-9pqhh 1/1 Running 0 12s
myapp-rs-p4bks 1/1 Running 0 12s
kubectl delete -f ./files/replicaset.yaml
replicaset.apps "myapp-rs" deleted
kubectl get pods
No resources found in default namespace.
Un comando para verificar sobre los manifiestos es el kubectl explain replicaset.
Aplique un replicaset para un pod creado anteriormente como estudio. No existe un comando kubectl create replicaset.
DaemonSet
Son idénticos al ReplicaSet, pero no es necesario pasar el número de réplicas pues es un único pod por nodo. Esto es un tipo especial muy usado para recolección de logs, monitoreo del nodo y red.
Por ejemplo, el Kube-Proxy sería un excelente ejemplo.
apiVersion: apps/v1
kind: DaemonSet
metadata:
name: myapp-ds
labels:
app: myapp
spec:
template:
metadata:
name: nginx
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx
selector:
matchLabels:
app: nginx
Deployments
Son capaces de ejecutar rolling updates, donde los pods son sustituidos uno por uno en vez de ser todos destruidos y sustituidos de una vez. Esto garantiza que su aplicación permanezca operacional durante la actualización para una nueva imagen, por ejemplo. Si la actualización falla, es posible revertir a la versión anterior mediante un rollback. Además, los Deployments gestionan diferentes versiones del ReplicaSet, proporcionando una manera eficiente de controlar las alteraciones en la infraestructura.
Observe que solamente cambiando el kind ReplicaSet por Deployment es lo necesario para que todo funcione pudiendo mantener la misma estructura.

deployment.apps/myapp created
kubectl get all
NAME READY STATUS RESTARTS AGE
pod/myapp-98d49bcf5-s6kmm 1/1 Running 0 4s
pod/myapp-98d49bcf5-tjm4j 1/1 Running 0 4s
pod/myapp-98d49bcf5-pzjsm 1/1 Running 0 4s
NAME READY UP-TO-DATE AVAILABLE AGE
deployment.apps/myapp 3/3 3 3 4s
NAME DESIRED CURRENT READY AGE
replicaset.apps/myapp-98d49bcf5 3 3 3 4s
kubectl describe deployment myapp
Name: myapp
Namespace: default
CreationTimestamp: Sun, 24 Dec 2023 16:39:40 -0300
Labels: app=myapp
Annotations: deployment.kubernetes.io/revision: 1
Selector: app=nginx
Replicas: 3 desired | 3 updated | 3 total | 3 available | 0 unavailable
StrategyType: RollingUpdate
MinReadySeconds: 0
RollingUpdateStrategy: 25% max unavailable, 25% max surge # VEA ESTO
Pod Template:
Labels: app=nginx
Containers:
nginx:
Image: nginx
Port: <none>
Host Port: <none>
Environment: <none>
Mounts: <none>
Volumes: <none>
Conditions:
Type Status Reason
---- ------ ------
Available True MinimumReplicasAvailable
Progressing True NewReplicaSetAvailable
OldReplicaSets: <none>
NewReplicaSet: myapp-98d49bcf5 (3/3 replicas created)
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal ScalingReplicaSet 2m39s deployment-controller Scaled up replica set myapp-98d49bcf5 to 3
Es importante observar que el Deployment crea tanto el ReplicaSet como los pods. Además, el objeto Deployment incluye una clave que define la tolerancia a fallos de los pods durante una actualización, así como la estrategia utilizada para realizar esa actualización.
Consejo: El comando abajo crea un manifiesto en la consola y si prefiere podrá redirigir a un archivo.
kubectl create deployment --image=nginx nginx --dry-run=client -o yaml
apiVersion: apps/v1
kind: Deployment
metadata:
creationTimestamp: null
labels:
app: nginx
name: nginx
spec:
replicas: 1
selector:
matchLabels:
app: nginx
strategy: {}
template:
metadata:
creationTimestamp: null
labels:
app: nginx
spec:
containers:
- image: nginx
name: nginx
resources: {}
status: {}
kubectl create deployment --image=nginx nginx --replicas 5 --dry-run=client -o yaml > deployment-dry-run.yaml
cat deployment-dry-run.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
creationTimestamp: null
labels:
app: nginx
name: nginx
spec:
replicas: 5
selector:
matchLabels:
app: nginx
strategy: {}
template:
metadata:
creationTimestamp: null
labels:
app: nginx
spec:
containers:
- image: nginx
name: nginx
resources: {}
status: {}
Un Deployment no puede ser modelado como un DaemonSet, es decir, siempre necesita tener un número de réplicas, aunque sea 1.
Services
Los services ayudan a conectar aplicaciones con otras aplicaciones o usuarios.

Existen algunas formas diferentes de hacer esto y es mediante el Kube-Proxy que manipula la tabla de enrutamiento dentro de los nodos para garantizar la conectividad.
- Cada nodo posee una IP.
- Cada pod también posee una IP y generalmente están en una red diferente del nodo.
- Los pods dentro de cada nodo poseen una interfaz virtual de red que usan la misma interfaz de red del nodo, pero están en otra red.
- El Kube-Proxy crea una conexión entre la red en que el nodo está y la red del pod, por eso si intentamos hacer ping a un pod mediante el shell del nodo conseguimos.
NodePort
La función es avisar a todos los nodos para escuchar en un puerto entre 30000 y 32767 y reenviar la solicitud de este puerto a un pod que está ofreciendo el servicio en otro puerto. Para eso necesita que todos los nodos tengan el reenvío de este puerto específico para el grupo de pods específico, después de todo ese pod puede estar ejecutándose en cualquier nodo.



apiVersion: v1
kind: Service
metadata:
name: myapp-service
spec:
type: NodePort
ports:
- targetPort: 80
port: 80
nodePort: 30008
# Necesita filtrar las labels del pod y no las labels del Deployment o del ReplicaSet
selector:
app: nginx
kubectl apply -f ./files/nodeport.yaml
service/myapp-service created
kubectl get services
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
myapp-service NodePort 10.43.164.162 <none> 80:30008/TCP 77s
# usando un alias. Observe también que posee un clusterIP del que hablaremos adelante
kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
myapp-service NodePort 10.43.164.162 <none> 80:30008/TCP 3m15s
kubectl describe service myapp-service
Name: myapp-service
Namespace: default
Labels: <none>
Annotations: <none>
Selector: app=nginx
Type: NodePort
IP Family Policy: SingleStack
IP Families: IPv4
IP: 10.43.164.162
IPs: 10.43.164.162
Port: <unset> 80/TCP
TargetPort: 80/TCP
NodePort: <unset> 30008/TCP
# VEA LOS ENDPOINTS
Endpoints: 10.42.0.11:80,10.42.2.39:80,10.42.3.251:80
Session Affinity: None
External Traffic Policy: Cluster
Events: <none>
# Verificando
kubectl get pods -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
myapp-98d49bcf5-s6kmm 1/1 Running 0 17h 10.42.2.39 k3d-k3d-cluster-agent-2 <none> <none>
myapp-98d49bcf5-tjm4j 1/1 Running 0 17h 10.42.0.11 k3d-k3d-cluster-agent-0 <none> <none>
myapp-98d49bcf5-pzjsm 1/1 Running 0 17h 10.42.3.251 k3d-k3d-cluster-server-0 <none> <none>
# Un comando un poco más avanzado para imprimir solamente ip de los pods
kubectl get pods -o custom-columns=IP:.status.podIP
IP
10.42.2.39
10.42.0.11
10.42.3.251
# Un comando un poco más avanzado para imprimir solamente ip de cada pod
kubectl get pods -o custom-columns=IP:.status.hostIP
IP
172.18.0.5
172.18.0.2
172.18.0.3
Vea ahora que en cualquier nodo que haga ping en el puerto 30008 responderá.
curl http://172.18.0.5:30008
# <!DOCTYPE html>
# <html>
# <head>
# <title>Welcome to nginx!</title>
# <style>
# html { color-scheme: light dark; }
# body { width: 35em; margin: 0 auto;
# font-family: Tahoma, Verdana, Arial, sans-serif; }
# </style>
# </head>
# <body>
# <h1>Welcome to nginx!</h1>
# <p>If you see this page, the nginx web server is successfully installed and
# working. Further configuration is required.</p>
# <p>For online documentation and support please refer to
# <a href="http://nginx.org/">nginx.org</a>.<br/>
# Commercial support is available at
# <a href="http://nginx.com/">nginx.com</a>.</p>
# <p><em>Thank you for using nginx.</em></p>
# </body>
# </html>
curl http://172.18.0.2:30008
# <!DOCTYPE html>
# <html>
# <head>
# <title>Welcome to nginx!</title>
# <style>
...
curl http://172.18.0.3:30008
# <!DOCTYPE html>
# <html>
# <head>
# <title>Welcome to nginx!</title>
# <style>
...
ClusterIP
Cualquiera que sea el service creado, poseerá el ClusterIP. Este definirá una IP que representará un grupo de pods. Un ClusterIP posee varios endpoints que pueden responder a la solicitud. Se utiliza un algoritmo aleatorio para elegir el nodo.
ClusterIP solo funciona dentro del cluster y sirve para comunicación entre las propias aplicaciones.
Escalar un Deployment, por ejemplo, hace que nuevos endpoints aparezcan.
apiVersion: v1
kind: Service
metadata:
name: myapp-service
spec:
type: ClusterIP
ports:
- targetPort: 80 # Puerto del pod
port: 80 # Puerto del service
# no es necesario el NodePort
selector:
app: nginx
Como visto anteriormente, el NodePort crea automáticamente un ClusterIP.
LoadBalancer
Este tipo crea un balanceador de carga para la aplicación en proveedores de nube para que su aplicación sea ofrecida mediante una URL.
Veremos sobre esto más adelante, pero la única diferencia es cambiar el type de NodePort a LoadBalancer, pero aún mantenemos la clave nodePort:
apiVersion: v1
kind: Service
metadata:
name: myapp-service
spec:
type: LoadBalancer # <<<<<
ports:
- targetPort: 80
port: 80
nodePort: 30008 # mantiene
selector:
app: nginx

Namespaces
Es cómo Kubernetes divide las cosas y aísla los recursos en el cluster. Dos Deployments pueden tener el mismo nombre, pero necesitan estar en namespaces diferentes.
Algunos namespaces son estándar:
- default:
- kube-system: Usado para desplegar objetos que mantienen Kubernetes funcionando. Está aislado para evitar que el usuario elimine cosas importantes.
- kube-public: Usado para desplegar recursos que son usados por todos los usuarios.
Por ejemplo, un uso de namespace es tener uno de ambiente de producción y otro de desarrollo en el mismo cluster. Si fuera un cluster solamente de producción podemos separar diferentes aplicaciones o herramientas.
Podemos aplicar políticas diferentes en cada namespace así como reglas de red o incluso de cuotas.
Para que un pod converse con otro en el mismo namespace podemos apenas usar el nombre del service que apunta al pod en el caso de muchos pods, o en el caso de un único pod, podemos usar el propio nombre del pod.
Para referenciar un service en otro namespace usamos
service_name.namespace_name.svc.cluster.local

Cuando un service es creado, el CoreDNS ya crea ese sistema de resolución de nombres.
Para crear un namespace:
apiVersion: v1
kind: Namespace
metadata:
name: dev
kubectl get namespaces
NAME STATUS AGE
default Active 68d
kube-system Active 68d
kube-public Active 68d
kube-node-lease Active 68d
crossplane-system Active 62d
# Estamos pidiendo filtrar los pods de ese namespace específico, si no es pasado tomará el namespace corriente que sería el default
kubectl get pods --namespace kube-system
NAME READY STATUS RESTARTS AGE
helm-install-traefik-crd-mntff 0/1 Completed 0 68d
helm-install-traefik-hknb2 0/1 Completed 1 68d
svclb-traefik-fe102d0a-pdrkf 2/2 Running 92 (2d11h ago) 68d
svclb-traefik-fe102d0a-5h7hh 2/2 Running 92 (2d11h ago) 68d
metrics-server-648b5df564-n8xp7 1/1 Running 92 (2d11h ago) 68d
local-path-provisioner-957fdf8bc-r9t55 1/1 Running 78 (2d11h ago) 68d
svclb-traefik-fe102d0a-85546 2/2 Running 92 (2d11h ago) 68d
svclb-traefik-fe102d0a-qhgk9 2/2 Running 92 (2d11h ago) 68d
traefik-64f55bb67d-q9wv9 1/1 Running 46 (2d11h ago) 68d
coredns-77ccd57875-v49sl 1/1 Running 46 (2d11h ago) 68d
kubectl describe ns dev
Name: dev
Labels: kubernetes.io/metadata.name=dev
Annotations: <none>
Status: Active
No resource quota.
No LimitRange resource.
kubectl create ns prod
namespace/prod created
kubectl get ns
NAME STATUS AGE
default Active 68d
kube-system Active 68d
kube-public Active 68d
kube-node-lease Active 68d
crossplane-system Active 62d
cert-manager Active 37d
controller-plane Active 21d
dev Active 2m55s
prod Active 3s
kubectl delete ns dev prod
namespace "dev" deleted
namespace "prod" deleted
En metadata podemos definir que un recurso sea siempre creado pasando el nombre del namespace que queremos.
apiVersion: apps/v1
kind: Deployment
metadata:
namespace: dev ## <<<<<
labels:
app: nginx
name: nginx
...
Existen recursos que son a nivel de namespace y otros a nivel de cluster. En los recursos a nivel de cluster no conseguimos definir un namespace específico como demostrado arriba.
kubectl config set-context --current --namespace=dev
Context "k3d-k3d-cluster" modified.
kubectl get pods
No resources found in dev namespace.
# Para ver los pods en todos los namespaces
kubectl get pods --all-namespace
kubectl get pod -A
kubectl get pods -A --selector name=pod_name
Para crear cuotas podemos usar el recurso ResourceQuota.
