Red de Servicios
Raramente configuras tus pods para comunicarse directamente entre sí. Si quieres que un pod acceda a los servicios de otro pod, alojado o no en el mismo host, siempre debes usar los services.
Cuando un service es creado puede ser accedido por todos los pods en el cluster independientemente de en qué nodo estén los pods. Mientras que un pod está alojado en un nodo, un service está "alojado" en todo el cluster y no vinculado a un nodo específico.
Tenemos 3 tipos de services.
- ClusterIP: Es el predeterminado y solo conocido dentro del cluster. Un ejemplo es una base de datos dentro del cluster para las aplicaciones.
- NodePort: Es el tipo de service que expone el service fuera del cluster. Una aplicación web sería un excelente ejemplo para uso de este tipo. Este service crea en todos los nodos del cluster el MISMO puerto y apunta al service. Un NodePort también tiene un ClusterIP.
- LoadBalancer:
hablaremos más tarde

Pero ¿cómo funciona realmente?
Resumen rápido.
-
El kubelet observa las alteraciones en el kube-apiserver y cada vez que un pod está definido para ser creado en su nodo, interactúa con el container runtime y lo crea.
-
El container runtime hace su trabajo de crear los namespaces necesarios para el pod e invoca el plugin CNI para crear la red para ese pod.
En cada nodo ejecutamos el kube-proxy también, por eso es un DaemonSet:
kubectl get ds -n kube-system
NAME DESIRED CURRENT READY UP-TO-DATE AVAILABLE NODE SELECTOR AGE
kube-proxy 4 4 4 4 4 kubernetes.io/os=linux 30h
weave-net 4 4 4 4 4 <none> 30h
El kube-proxy también observa las alteraciones del cluster a través del kube-apiserver, así como el kubelet, y siempre que un nuevo service es creado entra en acción.
Los services son un concepto que abarca todo el cluster. No son un recurso creado en el nodo como un contenedor, pero existen en todos los nodos del cluster. De hecho no existen. No existen procesos, interfaces, namespaces, contenedores, IPs, nada. Los services en Kubernetes son solo objetos virtuales.
Entonces ¿cómo obtienen un IP?
Cuando creamos un service recibe un IP de un intervalo predefinido. Los componentes del kube-proxy en ejecución que corren en cada nodo toman esa dirección IP y crean reglas de reenvío en cada nodo del cluster diciendo que todo tráfico que llegue a ese IP del servicio debe ser dirigido al IP del pod.

Siempre que alguien intenta acceder al IP de un servicio será reenviado a la dirección IP del pod que puede ser accedida en cualquier nodo del cluster.
No se trata solo del IP sino de la combinación de IP:PUERTO
Cada vez que un service es creado el kube-proxy agrega nuevas reglas, y cada vez que es eliminado esas reglas son removidas.
¿Cómo son creadas esas reglas y dónde?
El kube-proxy ofrece soporte a diferentes modos, como:
- userspaces: El kube-proxy escucha en un puerto para cada servicio y los conecta con los pods.
- IPVS: el kube-proxy crea reglas IPVS.
- iptables: el kube-proxy crea reglas en iptables
En el kube-proxy el proxy-mode puede definirse usando el parámetro --proxy-mode [userspaces | iptables | ipvs]...
Si esto no está definido, el predeterminado es iptables.
Por lo tanto, veremos cómo los iptables son configurados por el kube-proxy y cómo puedes visualizarlos en los nodos.
Vamos a crear un deploy, que crea un replicaset, que crea un pod. Podría ser solo un pod, pero vamos por el método usual, nadie crea pod sin un controller.
kubectl create deployment nginx --image nginx
deployment.apps/nginx created
# PODEMOS HACER DE ESTA MANERA O DE LA SEGUNDA MANERA
kubectl expose deployment nginx --port=8080 --target-port=80 --type=ClusterIP
service/nginx exposed
kubectl delete svc nginx
# Y PODRÍAMOS HACER ASÍ
kubectl create service clusterip nginx --tcp=8080:80
¿Qué definimos aquí? Un service que recibirá en el puerto 8080 y enviará al nginx en el puerto 80.
El comando kubectl expose y el kubectl create service son dos maneras diferentes de alcanzar el mismo resultado: crear un servicio en Kubernetes. Sin embargo, hay algunas diferencias entre ellos:
-
kubectl expose:
- Requiere que especifiques un deployment preexistente para crear el servicio.
- Además de crear un servicio, el comando expose puede modificar o actualizar un servicio existente
- Permite que especifiques opciones adicionales, como tipo de servicio, puerto de servicio y otros detalles.
- Más complejo cuando usado con muchos parámetros.
-
kubectl create service:
- Permite crear un servicio sin necesariamente tener un deployment correspondiente.
- Crea un servicio con los puertos especificados y no ofrece muchas opciones adicionales.
- Menos complejo
k get pods -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
nginx-7854ff8877-wc4dr 1/1 Running 0 13m 10.34.0.1 cka-cluster-worker2 <none> <none>
k get svc -o wide
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE SELECTOR
kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 30h <none>
nginx ClusterIP 10.101.29.104 <none> 8080/TCP 3s app=nginx
El IP real del pod es 10.34.0.1 El IP del service es 10.109.205.172
¿Por qué el service obtuvo IP?
La definición está en el kube-apiserver, es uno de los parámetros pasados en la ejecución del pod.
root@cka-cluster-control-plane:/etc# cat /etc/kubernetes/manifests/kube-apiserver.yaml
apiVersion: v1
kind: Pod
metadata:
# Removido para disminuir la salida ...
name: kube-apiserver
namespace: kube-system
spec:
containers:
- command:
- kube-apiserver
- --advertise-address=172.18.0.5
- --allow-privileged=true
- --authorization-mode=Node,RBAC
- --client-ca-file=/etc/kubernetes/pki/ca.crt
- --enable-admission-plugins=NodeRestriction
- --enable-bootstrap-token-auth=true
- --etcd-cafile=/etc/kubernetes/pki/etcd/ca.crt
- --etcd-certfile=/etc/kubernetes/pki/apiserver-etcd-client.crt
- --etcd-keyfile=/etc/kubernetes/pki/apiserver-etcd-client.key
- --etcd-servers=https://127.0.0.1:2379
- --kubelet-client-certificate=/etc/kubernetes/pki/apiserver-kubelet-client.crt
- --kubelet-client-key=/etc/kubernetes/pki/apiserver-kubelet-client.key
- --kubelet-preferred-address-types=InternalIP,ExternalIP,Hostname
- --proxy-client-cert-file=/etc/kubernetes/pki/front-proxy-client.crt
- --proxy-client-key-file=/etc/kubernetes/pki/front-proxy-client.key
- --requestheader-allowed-names=front-proxy-client
- --requestheader-client-ca-file=/etc/kubernetes/pki/front-proxy-ca.crt
- --requestheader-extra-headers-prefix=X-Remote-Extra-
- --requestheader-group-headers=X-Remote-Group
- --requestheader-username-headers=X-Remote-User
- --runtime-config=
- --secure-port=6443
- --service-account-issuer=https://kubernetes.default.svc.cluster.local
- --service-account-key-file=/etc/kubernetes/pki/sa.pub
- --service-account-signing-key-file=/etc/kubernetes/pki/sa.key
- --service-cluster-ip-range=10.96.0.0/12 # <<<<< Aquí será el rango
- --tls-cert-file=/etc/kubernetes/pki/apiserver.crt
- --tls-private-key-file=/etc/kubernetes/pki/apiserver.key
image: registry.k8s.io/kube-apiserver:v1.29.1
imagePullPolicy: IfNotPresent
## Removido para disminuir la salida
## O
kubectl get pods -n kube-system kube-apiserver-cka-cluster-control-plane -o=jsonpath='{.spec.containers[*].command}' | jq
[
"kube-apiserver",
"--advertise-address=172.18.0.5",
"--allow-privileged=true",
"--authorization-mode=Node,RBAC",
"--client-ca-file=/etc/kubernetes/pki/ca.crt",
"--enable-admission-plugins=NodeRestriction",
"--enable-bootstrap-token-auth=true",
"--etcd-cafile=/etc/kubernetes/pki/etcd/ca.crt",
"--etcd-certfile=/etc/kubernetes/pki/apiserver-etcd-client.crt",
"--etcd-keyfile=/etc/kubernetes/pki/apiserver-etcd-client.key",
"--etcd-servers=https://127.0.0.1:2379",
"--kubelet-client-certificate=/etc/kubernetes/pki/apiserver-kubelet-client.crt",
"--kubelet-client-key=/etc/kubernetes/pki/apiserver-kubelet-client.key",
"--kubelet-preferred-address-types=InternalIP,ExternalIP,Hostname",
"--proxy-client-cert-file=/etc/kubernetes/pki/front-proxy-client.crt",
"--proxy-client-key-file=/etc/kubernetes/pki/front-proxy-client.key",
"--requestheader-allowed-names=front-proxy-client",
"--requestheader-client-ca-file=/etc/kubernetes/pki/front-proxy-ca.crt",
"--requestheader-extra-headers-prefix=X-Remote-Extra-",
"--requestheader-group-headers=X-Remote-Group",
"--requestheader-username-headers=X-Remote-User",
"--runtime-config=",
"--secure-port=6443",
"--service-account-issuer=https://kubernetes.default.svc.cluster.local",
"--service-account-key-file=/etc/kubernetes/pki/sa.pub",
"--service-account-signing-key-file=/etc/kubernetes/pki/sa.key",
"--service-cluster-ip-range=10.96.0.0/12", ## Aquí
"--tls-cert-file=/etc/kubernetes/pki/apiserver.crt",
"--tls-private-key-file=/etc/kubernetes/pki/apiserver.key"
]
10.96.0.0/12 significa que los primeros 12 bits de la dirección IP son fijos (10.96) y los 20 bits restantes son variables. Esto da un rango de direcciones IP de 10.96.0.0 a 10.111.255.255. Entonces está dentro.
El predeterminado es 10.0.0.0/24 lo que nos permitiría crear mucho menos services.
Cuando proporcionamos a nuestro CNI el rango de IP que los pods usarán, vimos que es 10.244.0.0/16. No pueden superponerse. No debe haber un caso en que un pod y un service reciban el mismo IP
¿Y por qué la dirección del pod está entonces en 10.34.0.1?
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
nginx-7854ff8877-wc4dr 1/1 Running 0 47m 10.34.0.1 cka-cluster-worker2 <none> <none>
¿Por qué es como el Weavenet trabaja. ¿Recuerdas los agentes?
Vamos a verificar las reglas creadas cuando ese service fue ejecutado