Versión imprimible multipagina. Haga click aquí para imprimir.
Servicios, balanceo de carga y redes
- 1: Service
- 2: Ingress
- 3: Controladores Ingress
- 4: Políticas de red (Network Policies)
- 5: EndpointSlices
1 - Service
Un Service, servicio en castellano, es el objeto de la API de Kubernetes que describe cómo se accede a las aplicaciones, tal como un conjunto de Pods, y que puede describir puertos y balanceadores de carga.Con Kubernetes no necesitas modificar tu aplicación para que utilice un mecanismo de descubrimiento de servicios desconocido. Kubernetes le otorga a sus Pods su propia dirección IP y un nombre DNS para un conjunto de Pods, y puede balancear la carga entre ellos.
Motivación
Los Pods de Kubernetes son creados y destruidos para coincidir con el estado de tu clúster. Los Pods son recursos no permanentes. Si utilizas un Deployment para correr tu aplicación, puede crear y destruir los Pods dinámicamente.
Cada Pod obtiene su propia dirección IP, sin embargo, en un Deployment, el conjunto de Pods corriendo en un momento dado puede ser diferente al conjunto de Pods corriendo esa aplicación un momento después.
Esto conlleva un problema: si un conjunto de Pods (llamémoslos "backends") provee funcionalidad a otros Pods (llamémoslos "frontends") dentro de tu clúster, ¿de qué manera los frontends encuentran y tienen seguimiento de cuál dirección IP conectarse, para que el frontend pueda usar la parte del backend de la carga de trabajo?
Entran los Services.
Recursos Service
En Kubernetes, un Service es una abstracción que define un conjunto lógico de Pods y una política por la cual acceder a ellos (algunas veces este patrón es llamado micro-servicio). El conjunto de Pods a los que apunta un Servicio se determina usualmente por un Selector. Para aprender más sobre otras maneras de definir Endpoints para un Service, mira Services sin selectores.
Por ejemplo, considera un backend sin estado para procesar imágenes que está corriendo con 3 réplicas. Estas réplicas son fungibles; a los frontends no les importa cuál backend usar. Mientras que los Pods actuales que componen el backend pueden cambiar, los clientes del frontend no deberían estar al tanto de ello, ni deberían llevar un seguimiento del conjunto de backends en sí mismos.
La abstracción del Service habilita este desacoplamiento.
Descubrimiento de servicios nativos en la nube
Si eres capaz de usar la API de Kubernetes para descubrir servicios en tu aplicación, puedes hacer una búsqueda en el servidor API para los Endpoints, que se actualizan cuando cambian el conjunto de Pods en el servicio.
Para aplicaciones no nativas, Kubernetes ofrece una manera de colocar un puerto de red o un balanceador de carga entre tu aplicación y los Pods del backend.
Definiendo un Service
Un Service en Kubernetes es un objeto REST, similar a un Pod. Como todos los objetos REST, puedes hacer un Post
a una definición de un Service al servidor API para crear una nueva instancia.
EL nombre de un objeto Service debe ser un nombre RFC 1035 válido.
Por ejemplo, supongamos que tienes un conjunto de Pods en el que cada uno escucha el puerto TCP 9376 y contiene la etiqueta app.kubernetes.io/name=MyApp
:
apiVersion: v1
kind: Service
metadata:
name: mi-servicio
spec:
selector:
app.kubernetes.io/name: MyApp
ports:
- protocol: TCP
port: 80
targetPort: 9376
Esta especificación crea un nuevo objeto Service llamado "mi-servicio", que apunta via TCP al puerto 9376 de cualquier Pod con la etiqueta app.kubernetes.io/name=MyApp
.
Kubernetes asigna una dirección IP a este Service (Algunas veces llamado "Cluster IP"), la cual es usada por los proxies de los Services (mira IPs Virtuales y proxies de servicios abajo).
El controlador para el selector del Service escanea continuamente a los Pods que coincidan con este selector, y luego hace un Post de cualquier actualización a un objeto Endpoint llamado también "mi-servicio".
Nota:
Un Service puede mapear cualquierport
de entrada a un targetPort
. Por defecto y conveniencia, el targetPort
se establece con el mismo valor que el campo port
.Las definiciones de puerto en los Pods tienen nombres, y puedes hacer referencia a estos nombres en el atributo targetPort
del Service. Esto funciona incluso si existe una mezcla
de Pods en el Service usando un único nombre configurado, con el mismo protocolo de red disponible via diferentes números de puerto.
Esto ofrece mucha flexibilidad para desplegar y evolucionar tus Services. Por ejemplo, puedes cambiar los números de puertos que los Pods exponen en la siguiente versión de tu software backend, sin romper los clientes.
El protocolo por defecto para los Services is TCP; también puedes usar cualquier otro protocolo soportado.
Como muchos Services necesitan exponer más de un puerto, Kubernetes soporta múltiples definiciones de puertos en un único objeto Service. Cada definición de un puerto puede tener el mismo protocolo, o uno diferente
Services sin selectores
Los Services comúnmente abstraen el acceso a los Pods de Kubernetes, pero también pueden abstraer otros tipos de backends.
Por ejemplo:
- Quieres tener un clúster de base de datos externo en producción, pero en el entorno de pruebas quieres usar tus propias bases de datos.
- Quieres apuntar tu Service a un Service en un Namespace o en un clúster diferente.
- Estás migrando tu carga de trabajo a Kubernetes. Mientras evalúas la aproximación, corres solo una porción de tus backends en Kubernetes.
En cualquiera de estos escenarios puedes definir un Service sin un selector de Pod.
Por ejemplo:
apiVersion: v1
kind: Service
metadata:
name: mi-servicio
spec:
ports:
- protocol: TCP
port: 80
targetPort: 9376
Debido a que este Service no tiene un selector, el objeto Endpoints no se crea de forma automática. Puedes mapear manualmente el Service a la dirección de red y puerto donde está corriendo, añadiendo el objeto Endpoints manualmente:
apiVersion: v1
kind: Endpoints
metadata:
name: mi-servicio
subsets:
- addresses:
- ip: 192.0.2.42
ports:
- port: 9376
El nombre del objeto Endpoints debe ser un nombre de subdominio DNS válido.
Nota:
Las direcciones IPs no deben ser: loopback (127.0.0.0/8 para IPv4, ::1/128 para IPv6), o link-local (169.254.0.0/16 and 224.0.0.0/24 para IPv4, fe80::/64 para IPv6).
Las direcciones IP del Endpoint no pueden ser IPs de clúster de otros Services de Kubernetes, debido a que el kube-proxy no soporta IPs virtuales como destino.
Acceder a un Service sin un selector funciona de la misma manera que si tuviese un selector.
En el ejemplo de abajo, el tráfico se dirige al único Endpoint definido en el YAML:
192.0.2.42:9376
(TCP).
Nota:
El servidor de API de Kubernetes no permite hacer proxy a Endpoints que no están mapeados a Pods. Acciones comokubectl port-forward service/<service-name> forwardedPort:servicePort
donde el servicio no tiene un selector fallará debido a esta restricción.
Esto previene que el servidor API de Kubernetes sea utilizado como proxy a endpoints a los que quien llama no tenga acceso autorizado.Un Service ExternalName es un caso especial de Service que no tiene selectores y usa nombres DNS en su lugar. Para más información, mira la sección ExternalName en este documento.
Endpoints de sobrecapacidad
Si un recurso Endpoint tiene más de 1000 endpoints entonces un clúster de Kubernetes v1.22 (o posterior)
anota los Endpoints con endpoints.kubernetes.io/over-capacity: truncated
.
Esta anotación indica que el objeto Endpoints afectado está por encima de la capacidad y que
el controlador de endpoints ha truncado el número de endpoints a 1000.
EndpointSlices
Kubernetes v1.21 [stable]
Los EndpointSlices son un recurso de la API que pueden proveer una alternativa más escalable a los Endpoints. Aunque conceptualmente es muy similar a los Endpoints, los EndpointSlices permiten distribuir los endpoints de red a través de múltiples recursos. Por defecto, un EndpointSlice se considera "full" una vez que alcanza 100 endpoints, punto en el cual un EndpointSlice se creará para almacenar cualquier endpoint adicional.
Los EndpointSlices proveen atributos adicionales y funcionalidad que se describe en detalle en EndpointSlices.
Protocolo de aplicación
Kubernetes v1.20 [stable]
El campo appProtocol
provee una manera de especificar un protocolo de aplicación para cada puerto de un Service.
El valor de este campo es reflejado por el Endpoint correspondiente y los objetos EndpointSlices.
Este campo sigue una sintaxis estándar de etiquetas de Kubernetes. Los valores deberían ser nombres de servicio IANA estándar
o nombres de dominio con prefijos tales como mycompany.com/my-custom-protocol
.
IPS Virtuales proxies de servicio
Cada nodo en un clúster de Kubernetes ejecuta un kube-proxy
. El kube-proxy
es el responsable de implementar una forma de IP virtual para los
Services
de un tipo distinto al de ExternalName
.
Por qué no usar DNS round-robin?
Una pregunta que surge algunas veces es por qué Kubernetes depende de proxies para redirigir el tráfico de entrada a los backends. ¿Qué hay de otros enfoques? Por ejemplo, ¿sería posible configurar registros DNS que tengan múltiples valores A (o AAA para IPv6), y depender en la resolución de nombres round-robin?
Existen algunas razones para usar proxies en los Services:
- Hay una larga historia de implementaciones DNS que no respetan los registros TTLs, y cachean los resultados de la búsqueda de nombres luego de que deberían haber expirado.
- Algunas aplicaciones realizan la búsqueda de DNS solo una vez y almacenan en caché los resultados indefinidamente.
- Incluso si las aplicaciones y las librerías hicieran una resolución apropiada, los TTLs bajos o nulos en los registros DNS podrían imponer una carga alta en los DNS que luego se volvería difícil de manejar.
Más adelante en esta página puedes leer acerca del trabajo de varias implementaciones de kube-proxy. En general, deberías notar que, cuando ejecutas kube-proxy
, los niveles de reglas del kernel podrían modificarse (por ejemplo, podrían crearse reglas iptables), que luego no son limpiados, en algunos casos hasta que reinicias. Por tanto, ejecutar kube-proxy es algo que
solo debería hacer un administrador que entienda las consecuencias de tener un servicio de bajo nivel privilegiado de proxy de red en un computador. Aunque el ejecutable de kube-proxy
soporta una función de cleanup
, esta función no es una característica oficial y solo está disponible para usarse como está.
Configuración
Ten en cuenta que el kube-proxy inicia en diferentes modos, los cuales están determinados por su configuración.
- La configuración del kube-proxy se hace via un ConfigMap, y el ConfigMap para el kube-proxy remplaza efectivamente el comportamiento de casi todas las banderas para el kube-proxy.
- La configuración del ConfigMap no soporta la recarga en vivo de la configuración.
- Los parámetros del ConfigMap para el kube-proxy no se pueden validar y verificar en el arranque. Por ejemplo, si tu sistema operativo no permite ejecutar comandos iptables, el kube-proxy del kernel estándar no funcionará. De igual forma, si tienes un sistema operativo que no soporta
netsh
, no se ejecutará en modo userspace en Windows.
Modo proxy userspace
En este modo, el kube-proxy observa la adición y eliminación de objetos Endpoint Service del plano de control de Kubernetes.
Para cada Service se abre un puerto (elegido al azar) en el nodo local. Cualquier conexión a este "puerto proxy" es dirigido a uno de los Pods backend del Servicio (como se reporta via Endpoints). El kube-proxy toma el valor sessionAffinity
del Service en cuenta cuando decide cuál Pod del backend utilizar.
Finalmente, el proxy del userspace instala reglas de iptables que capturan el tráfico al clusterIP
(que es virtual) del servicio y el port
. Las reglas redirigen el tráfico al puerto proxy que redirige al Pod del backend.
Por defecto, el kube-proxy en modo userspace elige un backend con un algoritmo round-robin.
Modo proxy iptables
En este modo, el kube-proxy observa la adición y eliminación de objetos Endpoint Service del panel de control de Kubernetes.
Para Service, instala reglas iptables, las cuales capturan el tráfico al clusterIP
y el port
del Service, y redirige este tráfico a uno de los conjuntos del backend.
Para cada objeto Endpoint, instala reglas de iptables que seleccionan un Pod del backend.
Por defecto, el kube-proxy en modo iptables elige un backend al azar.
Usar iptables para manejar tráfico tiene una sobrecarga más baja del sistema, porque el tráfico es manejado por el netfilter de Linux sin la necesidad de cambiar entre userspace y el espacio del kernel. Esta aproximación puede llegar a ser más confiable.
Si el kube-proxy está corriendo en modo iptables y el primer Pod seleccionado no responde, la conexión falla. Esto es diferente del modo userspace: en ese escenario, el kube-proxy detectaría que la conexión al primer Pod ha fallado e intentaría automáticamente con otro Pod del backend.
Puedes usar readiness probes para verificar que los Pods del backend están funcionando correctamente, para que kube-proxy en modo iptables solo vea los backends que han sido comprobados como sanos. Hacer esto significa que evitas enviar tráfico via kube-proxy a un Pod que se sabe que ha fallado.
Modo Proxy IPVS
Kubernetes v1.11 [stable]
En el modo ipvs
, el kube-proxy observa los Services de Kubernetes y los Endpoints, llama la interfaz netlink
para crear reglas IPVS respectivamente y sincroniza las reglas IPVS con los Services de Kubernetes y los Endpoints periódicamente. Este ciclo de control asegura que los estados del IPVS coincidan con el estado deseado.
Cuando accede a un Service, IPVS dirige el tráfico a uno de estos Pods del backend.
El modo proxy IPVS está basado en la función de enlace netfilter que es similar al modo iptables, pero usa una tabla hash como estructura de datos subyacente y opera en el espacio del kernel.
Esto significa que el kube-proxy en modo IPVS redirige el tráfico como menor latencia que el kube-proxy en modo iptables, con mejor desempeño cuando sincroniza las reglas proxy. Comparado con otros modos proxy, el modo IPVS también soporta un rendimiento más alto de tráfico de red.
IPVS provee más opciones para balancear el tráfico a los Pods del backend; estas son:
rr
: round-robinlc
: menor conexión (el número más pequeño de conexiones abiertas)dh
: hash de destinosh
: hash de origensed
: retraso esperado más cortonq
: nunca hacer cola
Nota:
Para correr kube-proxy en modo IPVS, deber estar disponible IPVS en el nodo antes de iniciar el kube-proxy.
Cuando kube-proxy inicia en modo IPVS, este verifica si los módulos kernel IPVS están disponibles. Si no se detectan los módulos del kernel IPVS, kube-proxy vuelve al modo proxy iptables.
En estos modelos de proxy, el tráfico enlazado para la IP:Port del Service es redirigido al backend apropiado sin que el cliente sepa nada de Kubernetes, Services o Pods.
Si quieres asegurarte que las conexiones desde un cliente en particular se pasen al mismo Pod cada vez, puedes seleccionar la afinidad de sesión basada en la dirección IP del cliente al establecer service.spec.sessionAffinity
a "ClientIP" (por defecto es "None").
Puedes establecer también el número máximo de tiempo al establecer service.spec.sessionAffinityConfig.clientIP.timeoutSeconds
apropiadamente. (El valor por defecto es 10800, que resulta ser unas 3 horas).
Services multi puerto
Para algunos servicios, necesitas exponer más de un puerto. Kubernetes te permite configurar múltiples definiciones puertos en un objeto Service. Cuando usas múltiples puertos para un Service, debes nombrar todos tus puertos para que no sean ambiguos. Por ejemplo:
apiVersion: v1
kind: Service
metadata:
name: mi-servicio
spec:
selector:
app: MiApp
ports:
- name: http
protocol: TCP
port: 80
targetPort: 9376
- name: https
protocol: TCP
port: 443
targetPort: 9377
Nota:
Como con los nombres de Kubernetes en general, los nombres para los puertos deben contener alfanuméricos en minúsculas y -
. Los nombres de puertos deben comenzar y terminar con un carácter alfanumérico.
Por ejemplo, los nombres 123-abc
and web
son válidos, pero 123_abc
y -web
no lo son.
Eligiendo tu propia dirección IP
Puedes especificar tu propia dirección IP para el clúster como parte de la petición de creación de un Service
. Para hacer esto, establece el campo .spec.clusterIP
. Por ejemplo, si ya tienes una entrada DNS existente que quieres reutilizar, o sistemas legacy que están configurados para direcciones IP específicas que son difíciles de reconfigurar.
La dirección IP que elijas debe ser una dirección IPV4 o IPV6 válida dentro del rango CIDR service-cluster-ip-range
que está configurado para el servidor API.
Si intentas crear un Service con una dirección clusterIP inválida, el servidor API devolverá un código de estado 422 para indicar que hay un problema.
Políticas de tráfico
Política de tráfico externa
Puedes establecer el campo spec.externalTrafficPolicy
para controlar cómo se enruta el tráfico de fuentes externas. Los valores válidos son Cluster
y Local
. Establece el campo a Cluster
para enrutar tráfico externo a todos los endpoints listos y Local
para enrutar solamente a los endpoints locales del nodo. Si la política de tráfico es Local
y no hay endpoints de nodos locales, kube-proxy no redirige ningún tráfico al Service relevante.
Nota:
Kubernetes v1.22 [alpha]
Si habilitas el feature gate ProxyTerminatingEndpoints
para el kube-proxy, el kube-proxy revisa si el nodo tiene endpoints locales y si todos los endpoints locales están marcados como terminando. Si hay endpoints locales y todos están terminando, el kube-proxy ignora todo tráfico externo de Local
. En cambio, mientras que los endpoints locales del nodo permanecen todos como terminando, el kube-proxy reenvía el tráfico para ese Service para endpoints sanos en otro lugar, como si la política de tráfico externo fuese Cluster
.
Este comportamiento de reenvío para endpoints que están terminando existe para permitir que los balanceadores de carga externos terminen las conexiones que están respaldadas por Services NodePort
gradualmente, incluso cuando la verificación del estado del puerto del nodo comienza a fallar. De lo contrario, el tráfico puede perderse entre el tiempo en que un nodo está todavía en el grupo de nodos de un balanceador de carga y el tráfico se cae durante el periodo de terminación de un Pod.
Política de tráfico interna
Kubernetes v1.22 [beta]
spec.internalTrafficPolicy
para controlar como se enruta el tráfico desde las fuentes internas. Los valores válidos son Cluster
y Local
. Establece el campo a Cluster
para enrutar el tráfico interno a todos los endpoints listos y Local
para enrutar solo los endpoints locales del nodo. Si la política de tráfico es Local
y no hay endpoints locales de nodo, el tráfico es terminado por el kube-proxy.
Descubriendo servicios
Kubernetes soporta 2 modos primarios para encontrar un Service - variables de entorno y DNS
Variables de entorno
Cuando un Pod está corriendo en un Node, kubelet añade un conjunto de variables de entorno para cada Service activo. Soporta tanto variables Docker links
compatible como variables más sencillas {SVCNAME}_SERVICE_HOST
and {SVCNAME}_SERVICE_PORT
, donde el nombre del Service está en mayúsculas y los guiones medios se convierten en guiones bajos.
Por ejemplo, el Service redis-master
que expone el puerto TCP 6739 y se le ha asignado una dirección IP de clúster 10.0.0.11, produce las siguientes variables de entorno:
REDIS_MASTER_SERVICE_HOST=10.0.0.11
REDIS_MASTER_SERVICE_PORT=6379
REDIS_MASTER_PORT=tcp://10.0.0.11:6379
REDIS_MASTER_PORT_6379_TCP=tcp://10.0.0.11:6379
REDIS_MASTER_PORT_6379_TCP_PROTO=tcp
REDIS_MASTER_PORT_6379_TCP_PORT=6379
REDIS_MASTER_PORT_6379_TCP_ADDR=10.0.0.11
Nota:
Cuando tienes un Pod que necesita acceso a un Service, y estás usando el método de variable de entorno para publicar el puerto y la dirección clúster IP al Pod cliente, debes crear el Service antes de que los Pods del cliente lleguen a existir. De lo contrario, esos Pods cliente no tendrán las variables de entorno pobladas.
Si solo usas DNS para descubrir la clúster IP para un Service, no tienes que preocuparte acerca de este tema de ordenación.
DNS
Puedes (y casi siempre deberías) configurar un servicio DNS para tu clúster de Kubernetes usando un add-on.
Un servidor DNS consciente del clúster, como CoreDNS, observa la API de Kubernetes por nuevos Services y crea un conjunto de registros DNS para cada uno. Si DNS ha sido habilitado a través de tu clúster entonces todos los Pods automáticamente serán capaces de resolver los Services por su nombre DNS.
Por ejemplo, si tienes un Service llamado mi-servicio
en un namespace mi-ns
, el plano de control y el Service DNS crean un registro DNS para mi-servicio.mi-ns
conjuntamente. Los Pods en el namespace mi-ns
deberían ser capaces de encontrar el Service haciendo una búsqueda de nombre por mi-servicio
(mi-servicio.mi-ns
también funcionaría)
Los Pods en otros namespaces deben calificar el nombre como my-service.my-ns
. Estos nombres resolverán la clúster IP asignada para el Service.
Kubernetes también soporta registros DNS SRV (Service) para los puertos nombrados. Si el Service mi-servicio.mi-ns
tiene un puerto llamado http
con el protocolo fijado a TCP
, puedes hacer una consulta DNS SRV a _http._tcp.mi-servicio.mi-ns
para descubrir el número de puerto para http
así como la dirección IP.
El servidor DNS de Kubernetes es la única manera de acceder a los Services ExternalName
. Puedes encontrar más información sobre la resolución ExternalName
en Pods y Services DNS.
Servicios Headless
Algunas veces no necesitas balancear cargas y una IP única. En este caso, puedes crear lo que llamamos Services "headless", especificando "None"
para el clúster IP (.spec.clusterIP
).
Puedes usar un Service headless para hacer una interfaz con otros mecanismos de descubrimiento de servicios, sin estar atado a la implementación de Kubernetes.
Para los Services
headless, no se asigna una clúster IP, kube-proxy no maneja estos Services, y no hay balanceo de cargas o redirección por la plataforma para ellos. Cómo se configura el DNS automáticamente depende de si el Service tiene selectores definidos:
Con selectores
Para los Services headless que definen selectores, el controlador de endpoints crea registros Endpoints
en la API, y modifica la configuración DNS para devolver registros A (direcciones IP) que apuntan directamente a los Pods
que respaldan el Service
.
Sin selectores
Para Services headless que no definen selectores, el controlador de endpoints no crea registros Endpoints
. Sin embargo, el sistema DNS busca y configura:
- Registros CNAME para Services del tipo
ExternalName
. - Registros A para cualquier
Endpoints
que comparten un nombre con el Service, para todos los otros tipos.
Publicar Services (ServiceTypes)
En algunas partes de tu aplicación (por ejemplo, frontends) puede que necesites exponer un Service a una dirección IP externa, que está fuera de tu clúster local
Los ServiceTypes
de Kubernetes permiten especificar qué tipo de Service quieres. El valor por defecto es ClusterIP
Los valores Type
y sus comportamientos son:
-
ClusterIP
: Expone el Service en una dirección IP interna del clúster. Al escoger este valor el Service solo es alcanzable desde el clúster. Este es elServiceType
por defecto. -
NodePort
: Expone el Service en cada IP del nodo en un puerto estático (elNodePort
). Automáticamente se crea un ServiceClusterIP
, al cual enruta elNodePort
del Service. Podrás alcanzar el ServiceNodePort
desde fuera del clúster, haciendo una petición a<NodeIP>:<NodePort>
. -
LoadBalancer
: Expone el Service externamente usando el balanceador de carga del proveedor de la nube. Son creados automáticamente ServicesNodePort
yClusterIP
, a los cuales el apuntará el balanceador externo. -
ExternalName
: Mapea el Service al contenido del campoexternalName
(ej.foo.bar.example.com
), al devolver un registroCNAME
con su valor. No se configura ningún tipo de proxy.Nota:
- Necesitas la versión 1.7 de
kube-dns
o CoreDNS versión 0.0.8 o más para usar el tipoExternalName
.
- Necesitas la versión 1.7 de
También puedes usar un Ingress para exponer tu Service. Ingress no es un tipo de Service, pero actúa como el punto de entrada de tu clúster. Te permite consolidar tus reglas de enrutamiento en un único recurso, ya que puede exponer múltiples servicios bajo la misma dirección IP.
Tipo NodePort
Si estableces el campo type
a NodePort
, el plano de control de Kubernetes asigna un puerto desde un rango especificado por la bandera --service-node-port-range
(por defecto: 30000-32767).
Cada nodo es un proxy de ese puerto (el mismo número de puerto en cada nodo) hacia tu Service. Tu Service reporta al puerto asignado en el campo .spec.ports[*].nodePort
Si quieres especificar una(s) IP(s) particular(es) para hacer proxy del puerto, puedes establecer la bandera --nodeport-addresses
para el kube-proxy o el campo equivalente nodePortAddresses
del fichero de configuración de kube-proxy para ese bloque particular de IP(s).
Esta bandera recibe un listado de bloques de IP separados por coma (ej. 10.0.0.0/8
, 192.0.2.0/25
) para especificar rangos de direcciones IP que el kube-proxy debería considerar como local para este nodo.
Por ejemplo, si arrancas el kube-proxy con la bandera --nodeport-addresses=127.0.0.0/8
, el kube-proxy solo selecciona la interfaz loopback para los Services NodePort. El valor por defecto es --nodeport-addresses
es una lista vacía. Esto significa que el kube-proxy considera todas las interfaces de red disponibles para el NodePort. (Esto es compatible también con versiones más antiguas de Kubernetes).
Si quieres un número de puerto específico, puedes especificar un valor en el campo nodePort
. El plano de control te asignará ese puerto o reportará que la transacción API ha fallado.
Esto significa que necesitas prestar atención a posibles colisiones de puerto por tu cuenta.
También tienes que usar un número de puerto válido, uno que esté dentro del rango configurado para uso del NodePort.
Usar un NodePort te da libertad para configurar tu propia solución de balanceo de cargas, para configurar entornos que no soportan Kubernetes del todo, o para exponer uno o más IPs del nodo directamente.
Ten en cuenta que este Service es visible como <NodeIP>:spec.ports[*].nodePort
y .spec.clusterIP:spec.ports[*].port
.
Si la bandera --nodeport-addresses
está configurada para el kube-proxy o para el campo equivalente en el fichero de configuración, <NodeIP>
sería IP filtrada del nodo. Si
Por ejemplo:
apiVersion: v1
kind: Service
metadata:
name: mi-servicio
spec:
type: NodePort
selector:
app: MiApp
ports:
# Por defecto y por comodidad, el `TargetPort` tiene el mismo valor que el campo `port.
- port: 80
targetPort: 80
# Campo opcional
# Por defecto y por comodidad, el plano de control de Kubernetes asignará el puerto desde un rango (por defecto: 30000-32767)
nodePort: 30007
Tipo LoadBalancer
En proveedores de la nube que soportan balanceadores de carga externos, establecer el campo type
a LoadBalancer
aprovisiona un balanceador de carga para tu Service. La creación del balanceador de carga ocurre de forma asíncrona, y la información sobre el balanceador de carga provisto se publica en el campo .status.loadBalancer
del Service.
Por ejemplo:
apiVersion: v1
kind: Service
metadata:
name: mi-servicio
spec:
selector:
app: MiApp
ports:
- protocol: TCP
port: 80
targetPort: 9376
clusterIP: 10.0.171.239
type: LoadBalancer
status:
loadBalancer:
ingress:
- ip: 192.0.2.127
El tráfico desde el balanceador de carga externo es dirigido a los Pods del backend. El proveedor de la nube decide cómo balancear la carga.
Algunos proveedores de la nube te permiten especificar la IP loadBalancerIP
. En esos caso, el balanceador de carga es creado con la loadBalancerIP
especificada por el usuario. Si el campo loadBalancerIP
no se especifica, el balanceador de carga se configura con una dirección IP efímera. Si especificas una loadBalancerIP
pero tu proveedor de la nube no soporta esta característica, se ignora el campo loadBalancerIP
que has configurado.
Nota:
En Azure, si quieres usar un tipo loadBalancerIP
público definido por el usuario, primero necesitas crear una dirección IP estática y pública. Esta dirección IP pública debería estar en el mismo grupo de recursos que los otros recursos del clúster creados automáticamente.
Por ejemplo, MC_myResourceGroup_myAKSCluster_eastus
.
Especifica la dirección IP asignada como loadBalancerIP. Asegúrate que tienes actualizado el securityGroupName en tu fichero de configuración del proveedor de la nube. Para información sobre cómo resolver problemas de permisos de CreatingLoadBalancerFailed
, mira Usar una IP estática con el balanceador de carga de Azure Kubernetes Service (AKS) o CreatingLoadBalancerFailed en un clúster AKS con configuración de red avanzada.
Balanceadores de carga con tipos de protocolo mixtos
Kubernetes v1.20 [alpha]
Si la feature gate MixedProtocolLBService
está habilitada para el kube-apiserver se permiten usar diferentes protocolos cuando hay más de un puerto definido.
Nota:
El conjunto de protocolos que pueden ser usados para Services de tipo LoadBalancer es definido por el proveedor de la nube.Deshabilitar la asignación NodePort del balanceador de carga
Kubernetes v1.20 [alpha]
A partir de v1.20, puedes deshabilitar opcionalmente la asignación del puerto del nodo para un Service de tipo LoadBalancer estableciendo el campo spec.allocateLoadBalancerNodePorts
a false
. Esto debería ser usado solo para implementaciones de balanceadores de carga que enrutan el tráfico directamente a los Pods al contrario de usar puertos del nodo. Por defecto, spec.allocateLoadBalancerNodePorts
es true
y los Services de tipo LoadBalancer continuarán asignando puertos. Si spec.allocateLoadBalancerNodePorts
es false
en un Service existente con puertos asignado, esos puertos del nodo no serán desasignados automáticamente.
Debes quitar explícitamente la entrada nodePorts
en cada puerto del Service para desasignar esos puertos del nodo.
Debes habilitar la feature gate ServiceLBNodePortControl
para usar este campo.
Especificar la clase de implementación del balanceador de carga
Kubernetes v1.22 [beta]
spec.loadBalancerClass
te permite usar una implementación del balanceador de carga distinta que la que viene por defecto para el proveedor de la nube. Esta característica está disponible desde v1.21, debes habilitar la feature gate ServiceLoadBalancerClass
para usar este campo en v1.21, y la feature gate está habilitada por defecto desde v1.22 en adelante.
Por defecto, spec.loadBalancerClass
es nil
y un tipo de Service LoadBalancer
usa la implementación por defecto del proveedor de la nube si el clúster está configurado con un proveedor de nube usando la bandera de componente --cloud-provider
.
Si spec.loadBalancerClass
está especificado, se asume que una implementación de un balanceador de carga que coincida con la clase especificada está observando los Services. Cualquier implementación por defecto del balanceador de carga (por ejemplo, la que es provista por el proveedor de la nube) ignorará los Services que tienen este campo establecido. spec.loadBalancerClass
se puede establecer en cualquier Service de tipo LoadBalancer
únicamente. Una vez hecho, no se puede cambiar.
El valor de spec.loadBalancerClass
debe ser un identificador de etiqueta, con un prefijo opcional como "internal-vip
" o "example.com/internal-vip
". Los nombres sin prefijo están reservados para usuarios finales.
Balanceador de carga interno
En un entorno mixto algunas veces es necesario enrutar el tráfico desde Services dentro del mismo bloque (virtual) de direcciones de red.
En un entorno de split-horizon DNS necesitarías dos Services para ser capaz de enrutar tanto el tráfico externo como el interno a tus Endpoints.
Para establecer un balanceador de carga interno, agrega una de las siguientes anotaciones a tu Service dependiendo del proveedor de Service en la nube que estás usando.
Selecciona una de las pestañas.
[...]
metadata:
name: my-service
annotations:
networking.gke.io/load-balancer-type: "Internal"
[...]
[...]
metadata:
name: my-service
annotations:
service.beta.kubernetes.io/aws-load-balancer-internal: "true"
[...]
[...]
metadata:
name: my-service
annotations:
service.beta.kubernetes.io/azure-load-balancer-internal: "true"
[...]
[...]
metadata:
name: my-service
annotations:
service.kubernetes.io/ibm-load-balancer-cloud-provider-ip-type: "private"
[...]
[...]
metadata:
name: my-service
annotations:
service.beta.kubernetes.io/openstack-internal-load-balancer: "true"
[...]
[...]
metadata:
name: my-service
annotations:
service.beta.kubernetes.io/cce-load-balancer-internal-vpc: "true"
[...]
[...]
metadata:
annotations:
service.kubernetes.io/qcloud-loadbalancer-internal-subnetid: subnet-xxxxx
[...]
[...]
metadata:
annotations:
service.beta.kubernetes.io/alibaba-cloud-loadbalancer-address-type: "intranet"
[...]
Soporte para TLS en AWS
Para soporte parcial de TLS/SSL en clústeres corriendo en AWS, puedes agregar tres anotaciones al servicio LoadBalancer
:
metadata:
name: mi-servicio
annotations:
service.beta.kubernetes.io/aws-load-balancer-ssl-cert: arn:aws:acm:us-east-1:123456789012:certificate/12345678-1234-1234-1234-123456789012
El primero especifica el ARN del certificado a usar. Este puede ser un certificado de un emisor de un tercero que fue subido en IAM o uno creado dentro del Administrador de Certificados de AWS.
metadata:
name: mi-servicio
annotations:
service.beta.kubernetes.io/aws-load-balancer-backend-protocol: (https|http|ssl|tcp)
La segunda anotación especifica cuál protocolo habla el Pod. Para HTTPS y SSL, el ELB espera que el Pod se autentique a sí mismo sobre una conexión encriptada, usando un certificado.
HTTP y HTTPS seleccionan un proxy de capa 7: el ELB termina la conexión con el usuario, interpreta los encabezados, e inyecta el encabezado X-Forwared-For
con la dirección IP del usuario (los Pods solo ven la dirección IP del ELB del otro lado de su conexión) cuando reenvía las peticiones.
TCP y SSL seleccionan un proxy de capa 4: el ELB reenvía el tráfico sin modificar los encabezados.
En un entorno mixto donde algunos puertos están asegurados y otros se dejan sin encriptar, puedes usar una de las siguientes anotaciones:
metadata:
name: mi-servicio
annotations:
service.beta.kubernetes.io/aws-load-balancer-backend-protocol: http
service.beta.kubernetes.io/aws-load-balancer-ssl-ports: "443,8443"
En el ejemplo de arriba, si el Service contenía tres puertos, 80
, 443
y 8443
entonces 443
y 8443
usarían el certificado SSL, pero 80
sería HTTP proxy.
A partir de Kubernetes v1.9 en adelante puedes usar políticas predefinidas de AWS SSL con listeners HTTPS o SSL para tus Services. Para ver cuáles políticas están disponibles para usar, puedes usar la herramienta de línea de comandos aws
:
aws elb describe-load-balancer-policies --query 'PolicyDescriptions[].PolicyName'
Puedes especificar cualquiera de estas políticas usando la anotación "service.beta.kubernetes.io/aws-load-balancer-ssl-negotiation-policy
", por ejemplo:
metadata:
name: mi-servicio
annotations:
service.beta.kubernetes.io/aws-load-balancer-ssl-negotiation-policy: "ELBSecurityPolicy-TLS-1-2-2017-01"
Soporte de Protocolo PROXY en AWS
Para habilitar el soporte para el protocolo PROXY en clústeres corriendo en AWS, puedes usar la siguiente anotación para el servicio:
metadata:
name: mi-servicio
annotations:
service.beta.kubernetes.io/aws-load-balancer-proxy-protocol: "*"
A partir de la versión 1.3.0, el uso de esta anotación aplica para todos los puertos proxy del ELB y no puede ser configurado de otra manera.
Acceso a los logs ELB en AWS
Existen algunas anotaciones para administrar el acceso a los logs para Services ELB en AWS.
La anotación service.beta.kubernetes.io/aws-load-balancer-access-log-enabled
controla si el acceso a los logs están habilitados.
La anotación service.beta.kubernetes.io/aws-load-balancer-access-log-emit-interval
controla el intervalo en minutos para publicar los logs de acceso. Puedes especificar un intervalo de 5 0 60 minutos.
La anotación service.beta.kubernetes.io/aws-load-balancer-access-log-s3-bucket-name
controla el nombre del bucket de Amazon S3 donde se almacenan los logs del balanceador de carga.
La anotación service.beta.kubernetes.io/aws-load-balancer-access-log-s3-bucket-prefix
especifica la jerarquía lógica que has creado para tu bucket de Amazon S3.
metadata:
name: mi-servicio
annotations:
service.beta.kubernetes.io/aws-load-balancer-access-log-enabled: "true"
# Especifica si está habilitado el acceso a los logs
service.beta.kubernetes.io/aws-load-balancer-access-log-emit-interval: "60"
# EL intervalo para publicar los logs de acceso. Puedes especificar un intervalo de 5 o 60 (minutos)
service.beta.kubernetes.io/aws-load-balancer-access-log-s3-bucket-name: "my-bucket"
# El nombre del bucket S· de Amazon donde se almacenan los logs de acceso
service.beta.kubernetes.io/aws-load-balancer-access-log-s3-bucket-prefix: "my-bucket-prefix/prod"
# La jerarquía lógica que has creado para tu bucket S3 de Amazon, por ejemplo `my-bucket-prefix/prod`
Drenaje de conexión en AWS
Drenaje de conexión para ELBs clásicos se puede administrar con la anotación
service.beta.kubernetes.io/aws-load-balancer-connection-draining-enabled
fijada al valor "true"
.
La anotación service.beta.kubernetes.io/aws-load-balancer-connection-draining-timeout
puede
ser usada también para establecer el tiempo máximo, en segundos, para mantener las conexiones existentes antes de dar de baja las instancias.
metadata:
name: mi-servicio
annotations:
service.beta.kubernetes.io/aws-load-balancer-connection-draining-enabled: "true"
service.beta.kubernetes.io/aws-load-balancer-connection-draining-timeout: "60"
Otras anotaciones ELB
Existen otras anotaciones para administrar Classic Elastic Load Balancers que se describen abajo.
metadata:
name: mi-servicio
annotations:
service.beta.kubernetes.io/aws-load-balancer-connection-idle-timeout: "60"
# El tiempo, en segundos, que se permite a una conexión estar en reposo (no se han enviado datos sobre la conexión) antes que sea cerrada por el balanceador de carga
service.beta.kubernetes.io/aws-load-balancer-cross-zone-load-balancing-enabled: "true"
# Especifica si el balanceo de cargas entre zonas está habilitado para el balanceador de carga
service.beta.kubernetes.io/aws-load-balancer-additional-resource-tags: "environment=prod,owner=devops"
# Un listado separado por comas de valores de clave-valor que será guardados como etiquetas adicionales en el ELB.
service.beta.kubernetes.io/aws-load-balancer-healthcheck-healthy-threshold: ""
# El número de comprobaciones de estado exitosas sucesivas requeridas para considerar sano para el tráfico a un backend.
# Por defecto es 2, debe ser entre 2 y 10
service.beta.kubernetes.io/aws-load-balancer-healthcheck-unhealthy-threshold: "3"
# El número de comprobaciones de estado fallidas requeridas para considerar a un backend no apto para el tráfico.
# Por defecto es 6, debe ser entre 2 y 10
service.beta.kubernetes.io/aws-load-balancer-healthcheck-interval: "20"
# EL intervalo aproximado, en segundos, entre comprobaciones de estados de una instancia individual.
# Por defecto es 10, debe ser entre 5 y 300.
service.beta.kubernetes.io/aws-load-balancer-healthcheck-timeout: "5"
# La cantidad de tiempo, en segundos, durante el cual no recibir respuesta significa una comprobación de estado fallida.
# Este valor debe ser menor que el valor de service.beta.kubernetes.io/aws-load-balancer-healthcheck-interval
# Por defecto es 5, debe estar entre 2 y 60
service.beta.kubernetes.io/aws-load-balancer-security-groups: "sg-53fae93f"
# Un listado de grupos de seguridad existentes para configurar en el ELB creado. A diferencia de la anotación
# service.beta.kubernetes.io/aws-load-balancer-extra-security-groups, esta reemplaza todos los grupos de seguridad previamente asignados al ELB y también sobreescribe la creación de un grupo de seguridad creado únicamente para este ELB.
# El primer ID grupo de seguridad en esta lista se utiliza para permitir tráfico de entrada a los nodos workers objetivo (tráfico de servicio y comprobaciones de estados).
# Si se configuran múltiples ELBs con el mismo grupo de seguridad, solo una única línea de permisos será añadida a los grupos de seguridad del nodo worker, lo que significa que si eliminas cualquiera de esos ELBs removerá la línea de permisos y bloqueará el acceso para todos los ELBs que comparten el mismo ID de seguridad de grupo.
# Esto puede ocasionar cortes entre servicios si no se usa apropiadamente
service.beta.kubernetes.io/aws-load-balancer-extra-security-groups: "sg-53fae93f,sg-42efd82e"
# Un listado adicional de grupos de seguridad para añadir al ELB creado, esto deja un grupo de seguridad creado únicamente, asegurando que cada ELB tiene un ID de grupo de seguridad único que coincide con la línea de permiso para permitir tráfico a los nodos worker objetivo (tráfico de servicio y comprobaciones de estados)
# Grupos de seguridad definidos se pueden compartir entre servicios.
service.beta.kubernetes.io/aws-load-balancer-target-node-labels: "ingress-gw,gw-name=public-api"
# Un listado separado por comas de clave-valor que se utilizan para seleccionar los nodos objetivos para el balanceador de carga
Soporte para Balanceador de Carga de Red (NLB) en AWS
Kubernetes v1.15 [beta]
service.beta.kubernetes.io/aws-load-balancer-type
con el valor fijado a nlb
.
metadata:
name: mi-servicio
annotations:
service.beta.kubernetes.io/aws-load-balancer-type: "nlb"
Nota:
NLB solo funciona con ciertas clases de instancias; mira la documentación AWS sobre balanceo de cargas elástico para un listado de tipos de instancia soportados.A diferencia de los balanceadores de cargas, el balanceador de carga de red (NLB) reenvía la dirección IP del cliente a través del nodo. Si el campo .spec.externalTrafficPolicy
está fijado a clúster
, la dirección IP del cliente no es propagada a los Pods finales.
Al fijar .spec.externalTrafficPolicy
en Local
, la dirección IP del cliente se propaga a los Pods finales,
pero esto puede resultar a una distribución de tráfico desigual. Los nodos sin ningún Pod para un Service particular de tipo LoadBalancer fallarán en la comprobación de estado del grupo objetivo del NLB en el puerto .spec.healthCheckNodePort
y no recibirán ningún tráfico.
Para conseguir trafico equilibrado, usa un DaemonSet o especifica pod anti-affinity para no localizar en el mismo nodo.
También puedes usar Services NLB con la anotación del balanceador de carga interno
Para permitir que el tráfico del cliente alcance las instancias detrás del NLB, los grupos de seguridad del Nodo se modifican con las siguientes reglas de IP:
Regla | Protocolo | Puerto(s) | Rango de IP(s) | Descripción del Rango de IP |
---|---|---|---|---|
Health Check | TCP | NodePort(s) (.spec.healthCheckNodePort para .spec.externalTrafficPolicy = Local ) |
Subnet CIDR | kubernetes.io/rule/nlb/health=<loadBalancerName> |
Tráfico del Cliente | TCP | NodePort(s) | .spec.loadBalancerSourceRanges (por defecto en 0.0.0.0/0 ) |
kubernetes.io/rule/nlb/client=<loadBalancerName> |
MTU Discovery | ICMP | 3,4 | .spec.loadBalancerSourceRanges (por defecto en 0.0.0.0/0 ) |
kubernetes.io/rule/nlb/mtu=<loadBalancerName> |
Para limitar cuáles IPs del cliente pueden acceder al balanceador de carga de red, especifica loadBalancerSourceRanges
.
spec:
loadBalancerSourceRanges:
- "143.231.0.0/16"
Nota:
Si no se establece.spec.loadBalancerSourceRanges
, Kubernetes permite el tráfico
desde 0.0.0.0/0
a los Grupos de Seguridad del Nodo. Si los nodos tienen direcciones IP públicas, ten en cuenta que el tráfico que no viene del NLB
también puede alcanzar todas las instancias en esos grupos de seguridad modificados.Otras anotaciones CLS en Tencent Kubernetes Engine (TKE)
Hay otras anotaciones para administrar balanceadores de carga en la nube en TKE como se muestra abajo.
metadata:
name: mi-servicio
annotations:
# Enlaza Loadbalancers con nodos específicos
service.kubernetes.io/qcloud-loadbalancer-backends-label: key in (value1, value2)
# Identificador de un balanceador de carga existente
service.kubernetes.io/tke-existed-lbid:lb-6swtxxxx
#Parámetros personalizados para el balanceador de cargas (LB), no soporta la modificación del tipo de LB todavía
service.kubernetes.io/service.extensiveParameters: ""
# Parámetros personalizados para el listener LB
service.kubernetes.io/service.listenerParameters: ""
# Especifica el tipo de balanceador de carga;
# valores válidos: clásico (Balanceador de Carga clásico) o aplicación (Balanceador de Carga de aplicación de la nube)
service.kubernetes.io/loadbalance-type: xxxxx
# Especifica método de pago el ancho de banda de la red pública;
# valores válidos: TRAFFIC_POSTPAID_BY_HOUR(bill-by-traffic) y BANDWIDTH_POSTPAID_BY_HOUR (bill-by-bandwidth).
service.kubernetes.io/qcloud-loadbalancer-internet-charge-type: xxxxxx
# Especifica el valor del ancho de banda (rango valor: [1,2000] Mbps).
service.kubernetes.io/qcloud-loadbalancer-internet-max-bandwidth-out: "10"
# Cuando se fija esta anotación, los balanceadores de carga solo registrarán nodos con Pods corriendo en él, de lo contrario todos los nodos serán registrados.
service.kubernetes.io/local-svc-only-bind-node-with-pod: true
Tipo ExternalName
Los Services de tipo ExternalName mapean un Service a un nombre DNS, no a un selector típico como mi-servicio
o cassandra
. Estos Services se especifican con el parámetro spec.externalName
.
Esta definición de Service, por ejemplo, mapea el Service mi-Servicio
en el namespace prod
a my.database.example.com
:
apiVersion: v1
kind: Service
metadata:
name: mi-servicio
namespace: prod
spec:
type: ExternalName
externalName: my.database.example.com
Nota:
ExternalName acepta una cadena de texto IPv4, pero como un nombre DNS compuesto de dígitos, no como una dirección IP. ExternalNames que se parecen a direcciones IPv4 no se resuelven por el CoreDNS o ingress-nginx, ya que ExternalName se usa para especificar un nombre DNS canónico. Al fijar una dirección IP, considera usar headless Services.Cuando busca el host mi-servicio.prod.svc.cluster.local
, el Service DNS del clúster devuelve un registro CNAME
con el valor my.database.example.com
. Acceder a mi-servicio
funciona de la misma manera que otros Services, pero con la diferencia crucial de que la redirección ocurre a nivel del DNS en lugar reenviarlo o redirigirlo. Si posteriormente decides mover tu base de datos al clúster, puedes iniciar sus Pods, agregar selectores apropiados o endpoints, y cambiar el type
del Service.
Advertencia:
Podrías tener problemas al usar ExternalName para algunos protocolos comunes, incluyendo HTTP y HTTPS, si usas ExternalName entonces el nombre del host usado por los clientes dentro de tu clúster es diferente del nombre al que hace referencia el ExternalName.
Para protocolos que usan el nombre del host esta diferencia puede llevar a errores o respuestas inesperadas. Las peticiones HTTP tendrán un encabezado Host:
que el servidor de origen no reconocerá; los servidores TLS no serán capaces de proveer un certificado que coincida con el nombre del host al que el cliente está conectado.
IPs Externas
Si existen IPs externas que enrutan hacia uno o más nodos del clúster, los Services de Kubernetes pueden ser expuestos en esas externalIPs
. El tráfico que ingresa al clúster con la IP externa (como IP de destino), en el puerto del Service, será enrutado a uno de estos endpoints del Service. Las externalIPs
no son administradas por Kubernetes y son responsabilidad del administrador del clúster.
En la especificación del Service, las externalIPs
se pueden especificar junto con cualquiera de los ServiceTypes
.
En el ejemplo de abajo, "mi-servicio
" puede ser accedido por clientes en "198.51.100.32:80
" (externalIP:port
)
apiVersion: v1
kind: Service
metadata:
name: mi-servicio
spec:
selector:
app.kubernetes.io/name: MyApp
ports:
- name: http
protocol: TCP
port: 80
targetPort: 9376
externalIPs:
- 80.11.12.10
Limitaciones
Usar el proxy del userspace for VIPs funciona en pequeña y mediana escala, pero no escalará a clústeres muy grandes con miles de Services. El tópico original design proposal for portals tiene más detalles sobre esto.
Usar el proxy del userspace oculta la dirección IP de origen de un paquete que accede al Service. Esto hace que algún tipo de filtrado (firewalling) sea imposible. El modo proxy iptables no oculta IPs de origen en el clúster, pero aún tiene impacto en clientes que vienen desde un balanceador de carga o un node-port.
El campo Type
está diseñado como una funcionalidad anidada - cada nivel se agrega al anterior. Esto no es estrictamente requerido en todos los proveedores de la nube (ej. Google Compute Engine no necesita asignar un NodePort
para que funcione el LoadBalancer
, pero AWS si) pero la API actual lo requiere.
Implementación de IP Virtual
La información previa sería suficiente para muchas personas que quieren usar Services. Sin embargo, ocurren muchas cosas detrás de bastidores que valdría la pena entender.
Evitar colisiones
Una de las principales filosofías de Kubernetes es que no debe estar expuesto a situaciones que podrían hacer que sus acciones fracasen por su propia culpa. Para el diseño del recurso de Service, esto significa no obligarlo a elegir su propio número de puerto si esa elección puede colisionar con la de otra persona. Eso es un fracaso de aislamiento.
Para permitirte elegir un número de puerto en tus Services, debemos asegurarnos que dos Services no puedan colisionar. Kubernetes lo hace asignando a cada Service su propia dirección IP.
Para asegurarse que cada Service recibe una IP única, un asignador interno actualiza atómicamente el mapa global de asignaciones en etcd antes de crear cada Service. El objeto mapa debe existir en el registro para que los Services obtengan asignaciones de dirección IP, de lo contrario las creaciones fallarán con un mensaje indicando que la dirección IP no pudo ser asignada.
En el plano de control, un controlador de trasfondo es responsable de crear el mapa (requerido para soportar la migración desde versiones más antiguas de Kubernetes que usaban bloqueo en memoria). Kubernetes también utiliza controladores para revisar asignaciones inválidas (ej. debido a la intervención de un administrador) y para limpiar las direcciones IP que ya no son usadas por ningún Service.
Direcciones IP del Service
A diferencia de direcciones IP del Pod, que enrutan a un destino fijo, las IPs del Service no son respondidas por ningún host. En lugar de ello, El kube-proxy usa iptables (lógica de procesamiento de paquetes en Linux) para definir direcciones IP virtuales que se redirigen de forma transparente cuando se necesita. Cuando el cliente se conecta con la VIP, su tráfico es transportado automáticamente al endpoint apropiado. Las variables de entorno y DNS para los Services son pobladas en términos de la dirección IP virtual del Service (y el puerto).
Kube-proxy soporta tres modos — userspace, iptables e IPVS — los cuales operan ligeramente diferente cada uno.
Userspace
Por ejemplo, considera la aplicación de procesamiento de imágenes descrita arriba. Cuando el Service del backend es creado, el nodo maestro de Kubernetes asigna una dirección IP virtual, por ejemplo 10.0.0.1. Asumiendo que el puerto del Service es 1234, el Service es observado por todas las instancias del kube-proxy en el clúster. Cuando un proxy mira un nuevo Service, abre un puerto al azar, establece una redirección iptables desde la dirección IP virtual a este nuevo puerto, y comienza a aceptar conexiones a este.
Cuando un cliente se conecta a la dirección IP virtual del Service, la regla de iptables entra en acción, y redirige los paquetes al propio puerto del proxy. El "proxy del Service" elige un backend, y comienza a redirigir el tráfico desde el cliente al backend.
Esto quiere decir que los dueños del Service pueden elegir cualquier puerto que quieran sin riesgo de colisión. Los clientes pueden conectarse a una IP y un puerto, sin estar conscientes de a cuáles Pods están accediendo.
iptables
Nuevamente, considera la aplicación de procesamiento de imágenes descrita arriba. Cuando se crea el Service Backend, el plano de control de Kubernetes asigna una dirección IP virtual, por ejemplo 10.0.0.1. Asumiendo que el puerto del servicio es 1234, el Service es observado por todas las instancias del kube-proxy en el clúster. Cuando un proxy mira un nuevo Service, instala una serie de reglas de iptables que redirigen desde la dirección IP virtual a las reglas del Service. Las reglas del Service enlazan a las reglas del Endpoint que redirigen el tráfico (usando NAT de destino) a los backends.
Cuando un cliente se conecta a la dirección IP virtual del Service la regla de iptables son aplicadas. A diferencia del modo proxy userspace, el kube-proxy no tiene que estar corriendo para que funcione la dirección IP virtual, y los nodos observan el tráfico que viene desde la dirección IP del cliente sin alteraciones.
El mismo flujo básico se ejecuta cuando el tráfico viene a través de un node-port o de un balanceador de carga, aunque en estos casos la IP del cliente es alterada.
IPVS
Las operaciones iptables ralentizan dramáticamente en un clúster a gran escala, ej. 10.000 Services. IPVS está diseñado para balancear cargas y está basado en tablas hash dentro del kernel. De esta manera puedes alcanzar consistencia en el desempeño en un número grande de Services de un kube-proxy basado en IPVS. Mientras tanto, el kube-proxy basado en IPVS tiene algoritmos de balanceo de cargas más sofisticados (least conns, locality, weighted, persistence).
Objeto API
El Service es un recurso de alto nivel en la API REST de Kubernetes. Puedes encontrar más detalles sobre el objeto API en: Objeto API Service API.
Protocolos soportados
TCP
Puedes usar TPC para cualquier tipo de Service, y es el protocolo de red por defecto.
UDP
Puedes usar UDP para la mayoría de los Services. Para Services type=LoadBalancer, el soporte UDP depende del proveedor de la nube que ofrece esta facilidad.
SCTP
Kubernetes v1.20 [stable]
Advertencias
Soporte para asociaciones SCTP multihomed
Advertencia:
El soporte para asociaciones SCTP multihomed requiere que el plugin CNI pueda soportar la asignación de múltiples interfaces y direcciones IP a un Pod.
NAT para asociaciones SCTP multihomed requiere una lógica especial en los módulos del kernel correspondientes.
Windows
Nota:
SCTP no está soportado en nodos basados en Windows.Userspace kube-proxy
Advertencia:
El kube-proxy no soporta la administración de asociaciones SCTP cuando está en el modo userspace.HTTP
Si tu proveedor de la nube lo soporta, puedes usar un Service en modo LoadBalancer para configurar un proxy invertido HTTP/HTTPS, redirigido a los Endpoints del Service.
Nota:
También puedes usar Ingress en lugar de un Service para exponer Services HTTP/HTTPS.Protocolo PROXY
Si tu proveedor de la nube lo soporta, puedes usar un Service en modo LoadBalancer para configurar un balanceador de carga fuera de Kubernetes mismo, que redirigirá las conexiones prefijadas con protocolo PROXY.
El balanceador de carga enviará una serie inicial de octetos describiendo la conexión entrante, similar a este ejemplo
PROXY TCP4 192.0.2.202 10.0.42.7 12345 7\r\n
Seguido de la data del cliente.
Siguientes pasos
- Leer sobre Conectar aplicaciones con Services
- Leer sobre Ingress
- Leer sobre EndpointSlices
2 - Ingress
Kubernetes v1.19 [stable]
Un objeto de la API que administra el acceso externo a los servicios en un clúster, típicamente HTTP.
Un Ingress podría proveer balanceo de cargas, terminación SSL y hosting virtual basado en nombres.
Nota:
El recurso Ingress está congelado. Las nuevas características se añaden al API del GatewayTerminología
Para mayor claridad, esta guía define los siguientes términos:
- Nodo: Una máquina worker en Kubernetes, parte de un clúster.
- Clúster: Un conjunto de Nodos que ejecutan aplicaciones en contenedores, administrados por Kubernetes. Para este ejemplo, y para los despliegues más comunes de Kubernetes, los nodos en el clúster no son parte del internet público.
- Enrutador Edge: un enrutador que refuerza la política de seguridad del cortafuegos para tu clúster. Esto podría ser una puerta de entrada administrada por un proveedor de la nube o una pieza física de hardware.
- Red del clúster: un conjunto de enlaces, lógicos o físicos, que facilitan la comunicación dentro de un clúster de acuerdo con el modelo de redes de Kubernetes.
- Service: Un Service que identifica un conjunto de Pods que utilizan selectores de label. A menos que se indique de otra manera, los Services se asumen que tienen IPs virtuales que solo se pueden enrutar dentro de la red del clúster.
¿Qué es un Ingress?
Un Ingress expone rutas HTTP y HTTPS desde el exterior del clúster a los servicios dentro del clúster. El control del tráfico es controlado por las reglas definidas en el recurso Ingress.
Aquí tienes un ejemplo simple de un Ingress que envía todo su tráfico a un Service:
Un Ingress se puede configurar para otorgar URLs a los Services que son accesibles desde el exterior, para hacer balance de cargas del tráfico, finalizar SSL/TLS y ofrecer alojamiento virtual basado en nombres.
Un controlador de Ingress es responsable de complementar el Ingress, comúnmente con un balanceador de cargas, aunque también puedes configurar tu enrutador edge con frontends adicionales para ayudar a manejar el tráfico.
Un Ingress no expone puertos o protocolos arbitrariamente. Exponer servicios distintos de HTTP o HTTPS al internet usa un servicio de tipo Service.Type=NodePort o Service.Type=LoadBalancer.
Prerrequisitos
Debes tener un controlador de Ingress para satisfacer a un Ingress. Crear únicamente un recurso Ingress no tiene ningún efecto.
Puede que necesites desplegar un controlador Ingress controller tal como el ingress-nginx. Puedes elegir de un número de controladores de Ingress.
Idealmente, todos los controladores de Ingress deberían encajar con la especificación de referencia. En realidad, los distintos controladores de Ingress operan ligeramente diferente.
Nota:
Asegúrate de revisar la documentación del controlador de Ingress para entender las precauciones de usarlo.El recurso Ingress
Un ejemplo mínimo de un recurso Ingress:
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: minimal-ingress
annotations:
nginx.ingress.kubernetes.io/rewrite-target: /
spec:
ingressClassName: nginx-example
rules:
- http:
paths:
- path: /testpath
pathType: Prefix
backend:
service:
name: test
port:
number: 80
Un Ingress necesita los campos apiVersion
, kind
, metadata
y spec
.
El nombre del objeto Ingress debe ser un
nombre de subdominio DNS
válido.
Para información general sobre cómo trabajar con archivos de configuración,
mira desplegando aplicaciones,
configurando contenedores,
administrando recursos.
Los Ingress usan anotaciones frecuentemente para configurar algunas opciones dependiendo del controlador de Ingress, un ejemplo de ello es la anotación rewrite-target. Distintos controladores de Ingress soportan anotaciones diferentes. Revisa la documentación para tu elección del controlador de Ingress para saber qué anotaciones son soportadas.
La especificación Ingress tiene toda la información que necesitas para configurar un balanceador de cargas o un servidor proxy. Mucho más importante, contiene un listado de reglas que emparejan contra todas las peticiones entrantes. El recurso Ingress solo soporta reglas para dirigir el tráfico HTTP(S).
Si se omite la ingressClassName
, se define
una clase Ingress por defecto.
Existen algunos controladores de Ingress,
que trabajan sin la definición de una IngressClass
por defecto.
Por ejemplo, el controlador Ingress-NGINX se puede configurar con
una opción
--watch-ingress-without-class
.
Sin embargo,
se recomienda
especificar el IngressClass
por defecto como se
muestra abajo.
Reglas del Ingress
Cada regla HTTP contiene la siguiente información:
- Un host opcional. En este ejemplo, no se define un host así que la regla se aplica a todo el tráfico de entrada HTTP a través de la dirección IP especificada. Cuando se proporciona un host (por ejemplo, foo.bar.com), las reglas se aplican a ese host.
- Un listado de rutas (por ejemplo,
/testpath
), cada una de las cuales tiene un backend asociado con unservice.name
y unservice.port.name
o unservice.port.number
. Tanto el host como la ruta deben coincidir con el contenido de una petición de entrada antes que el balanceador de cargas dirija el tráfico al Service referenciado. - Un backend es una combinación de un Service y un puerto como se describe en la documentación del Service o un recurso personalizado backend a través de un CRD. Las peticiones HTTP (y HTTPS) al Ingress que coinciden con el host y la ruta de la regla se envían al backend del listado.
Un defaultBackend
se configura frecuentemente en un controlador de Ingress
para dar servicio a cualquier petición que no coincide con una ruta en la
especificación.
DefaultBackend
Un Ingress sin reglas envía todo el tráfico a un único backend
y .spec.defaultBackend
está en el backend que debería manejar las peticiones
en ese caso.
El defaultBackend
es una opción de configuración convencional
del controlador Ingress
y no se especifica en tus recursos del Ingress.
Si no se especifican reglas .spec.rules
, se debe
especificar .spec.defaultBackend
.
Si no se establece un defaultBackend
, las peticiones que no coincidan con
ninguna de las reglas las decidirá el controlador ingress (consulta la
documentación de tu controlador de ingress para saber cómo maneja este caso).
Si ninguno de los hosts o rutas coincide con la petición HTTP en los objetos Ingress, el tráfico será enrutado a tu backend predeterminado.
Resource backends
Un Resource
backend es una referencia de objeto
(ObjectRef en inglés)
a otro recurso de Kubernetes dentro del mismo espacio de nombres que el objeto
Ingress.
Este Resource
es mutuamente exclusivo con el Service,
y la validación fallará si ambos se especifican.
Un uso común para un Resource
backend es para ingresar datos a un backend de almacenamiento de datos con activos estáticos.
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: ingress-resource-backend
spec:
defaultBackend:
resource:
apiGroup: k8s.example.com
kind: StorageBucket
name: static-assets
rules:
- http:
paths:
- path: /icons
pathType: ImplementationSpecific
backend:
resource:
apiGroup: k8s.example.com
kind: StorageBucket
name: icon-assets
Luego de crear el Ingress mencionado arriba, puedes verlo con el siguiente comando:
kubectl describe ingress ingress-resource-backend
Name: ingress-resource-backend
Namespace: default
Address:
Default backend: APIGroup: k8s.example.com, Kind: StorageBucket, Name: static-assets
Rules:
Host Path Backends
---- ---- --------
*
/icons APIGroup: k8s.example.com, Kind: StorageBucket, Name: icon-assets
Annotations: <none>
Events: <none>
Tipos de ruta
Se requiere que cada ruta de un Ingress tenga un tipo de ruta correspondiente.
Las Rutas que no incluyen un pathType
explícito fallarán la validación.
Hay 3 tipos de rutas soportadas:
-
ImplementationSpecific
: Con este tipo de ruta, la coincidencia depende de la IngressClass. Las implementaciones pueden tratar esto como unpathType
separado o tratarlas de forma idéntica a los tipos de rutaPrefix
oExact
. -
Exact
: Coincide la ruta de la URL exactamente con sensibilidad a mayúsculas y minúsculas. -
Prefix
: Coincide basado en el prefijo de una ruta URL dividida por/
. La coincidencia es sensible a mayúsculas y minúsculas, y hecha en un elemento de la ruta por elemento. Un elemento de la ruta refiere a la lista de etiquetas en la ruta dividida por el separador/
. Una petición es una coincidencia para la ruta p si cada p es un elemento prefijo de p de la ruta requerida.
Nota:
Si el último elemento de una ruta es una subcadena de caracteres del último elemento de la solicitud de ruta, no es una coincidencia (por ejemplo:/foo/bar
coincide con /foo/bar/baz
, pero no coincide con /foo/barbaz
).Ejemplos
Tipo | Ruta(s) | Ruta de la(s) peticion(es) | ¿Coincide? |
---|---|---|---|
Prefijo | / |
(todas las rutas) | Sí |
Exacto | /foo |
/foo |
Si |
Exacto | /foo |
/bar |
No |
Exacto | /foo |
/foo/ |
No |
Exacto | /foo/ |
/foo |
No |
Prefijo | /foo |
/foo , /foo/ |
Si |
Prefijo | /foo/ |
/foo , /foo/ |
Si |
Prefijo | /aaa/bb |
/aaa/bbb |
No |
Prefijo | /aaa/bbb |
/aaa/bbb |
Si |
Prefijo | /aaa/bbb/ |
/aaa/bbb |
Si, ignora la barra diagonal |
Prefijo | /aaa/bbb |
/aaa/bbb/ |
Si, coincide con barra diagonal |
Prefijo | /aaa/bbb |
/aaa/bbb/ccc |
Si, coincide con la subruta |
Prefijo | /aaa/bbb |
/aaa/bbbxyz |
No, no coincide con el prefijo de cadena |
Prefijo | / , /aaa |
/aaa/ccc |
Si, coincide con el prefijo /aaa |
Prefijo | / , /aaa , /aaa/bbb |
/aaa/bbb |
Si, coincide con el prefijo /aaa/bbb |
Prefijo | / , /aaa , /aaa/bbb |
/ccc |
Si, coincide con el prefijo/ |
Prefijo | /aaa |
/ccc |
No, usa el backend predeterminado |
Mezclado | /foo (Prefijo), /foo (Exacto) |
/foo |
Si, prefiere la coincidencia exacta |
Múltiples coincidencias
En algunos casos, muchas rutas dentro de un Ingress coincidirán con una petición. En esos casos, la precedencia se le dará al primero con la ruta más larga que coincide. Si dos rutas todavía coinciden por igual, se le dará precedencia a las rutas con una coincidencia de ruta exacta sobre las rutas que contienen prefijos.
Comodines Hostname
Los hosts pueden ser coincidencias exactas
(por ejemplo “foo.bar.com
”) o un comodín (por ejemplo “*.foo.com
”).
Las coincidencias precisas requieren que el encabezado host
coincida con el
campo host
.
Las coincidencias de comodín requieren que el encabezado host
sea igual al
sufijo de la regla del comodín.
Host | Encabezado Host | ¿Coincidencia? |
---|---|---|
*.foo.com |
bar.foo.com |
Coincide basado en el sufijo común |
*.foo.com |
baz.bar.foo.com |
No coincide, el comodín solo cubre una etiqueta DNS |
*.foo.com |
foo.com |
No coincide, el comodín solo cubre una etiqueta DNS |
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: ingress-wildcard-host
spec:
rules:
- host: "foo.bar.com"
http:
paths:
- pathType: Prefix
path: "/bar"
backend:
service:
name: service1
port:
number: 80
- host: "*.foo.com"
http:
paths:
- pathType: Prefix
path: "/foo"
backend:
service:
name: service2
port:
number: 80
La Clase Ingress
Los Ingress pueden ser implementados por distintos controladores, comúnmente con una configuración distinta. Cada Ingress debería especificar una clase, una referencia a un recurso IngressClass que contiene información adicional incluyendo el nombre del controlador que debería implementar la clase.
apiVersion: networking.k8s.io/v1
kind: IngressClass
metadata:
name: external-lb
spec:
controller: example.com/ingress-controller
parameters:
apiGroup: k8s.example.com
kind: IngressParameters
name: external-lb
El campo .spec.parameters
de un IngressClass te permite hacer referencia a
otro recurso que proporciona la configuración relacionada con esa IngressClass.
El tipo específico de parámetros a usar depende del controlador de Ingress que
especificas en el campo spec.controller
de la IngressClass.
Alcance de IngressClass
Dependiendo de tu controlador de ingress, podrías ser capaz de usar parámetros que se establecen en todo el clúster, o solamente para un namespace.
El alcance por defecto de los parámetros IngressClass es para todo el clúster.
Si estableces el campo spec.parameters
y no estableces el
campo spec.parameters.scope
,
entonces el IngressClass se refiere al recurso cuyo alcance es todo el clúster.
El atributo kind
(en combinación con el apiGroup
)
de los parámetros se refiere a la API con alcance a todo el clúster
(posiblemente un recurso personalizado), y el name
de los parámetros
identifica al recurso del clúster específico para esa API.
Por ejemplo:
---
apiVersion: networking.k8s.io/v1
kind: IngressClass
metadata:
name: external-lb-1
spec:
controller: example.com/ingress-controller
parameters:
# Los parámetros para este IngressClass se especifican en un
# ClusterIngressParameter (API group k8s.example.net) llamado
# "external-config-1". Esta definición le indica a Kubernetes
# de buscar por un parámetro de recurso con alcance a todo el clúster.
scope: Cluster
apiGroup: k8s.example.net
kind: ClusterIngressParameter
name: external-config-1
Kubernetes v1.23 [stable]
Si estableces el campo spec.parameters
y el spec.parameters.scope
al Namespace
,
entonces el IngressClass se refiere al recurso cuyo alcance es el namespace.
También debes establecer el campo namespace
dentro de spec.parameters
con el
Namespace que contiene los parámetros que quieres usar.
El atributo kind
(en combinación con apiGroup
)
de los parámetros se refiere a la API restringida por un Namespace (por ejemplo:
ConfigMap), y el name
de los parámetros identifica al recurso específico en el
namespace que has especificado en namespace
.
Los parámetros con alcance al Namespace ayudan al operador del clúster a delegar el control sobre la configuración (por ejemplo, ajustes del balanceador de cargas, definición de una API gateway) que se usa para una carga de trabajo. Si utilizas un parámetro con alcance al Namespace entonces:
- El equipo operador del clúster necesita aprobar los cambios de un equipo distinto cada vez que se aplica un nuevo cambio a la configuración.
- O el equipo operador del clúster debe definir específicamente estos controles de acceso, tales como asociaciones de roles RBAC y mapeos, que permitan a la aplicación hacer cambios al recurso de parámetros con alcance al clúster.
La API de la IngressClass por sí misma siempre tiene alcance al clúster.
Aquí hay un ejemplo de una IngressClass que hace referencia a parámetros que están restringidos por un Namespace:
---
apiVersion: networking.k8s.io/v1
kind: IngressClass
metadata:
name: external-lb-2
spec:
controller: example.com/ingress-controller
parameters:
# Los parámetros para esta IngressClass se especifican en un
# IngressParameter (API group k8s.example.com) llamado "external-config",
# que está en el namespace "external-configuration".
scope: Namespace
apiGroup: k8s.example.com
kind: IngressParameter
namespace: external-configuration
name: external-config
Anotación deprecada
Antes que el recurso IngressClass y el campo ingressClassName
se añadieran a
Kubernetes
1.18,
las clases Ingress se especificaban con una
anotación kubernetes.io/ingress.class
en el Ingress.
Esta anotación nunca se definió formalmente,
pero era ampliamente soportada por los controladores de Ingress.
El nuevo campo ingressClassName
en los recursos Ingress es un reemplazo para
esa anotación, pero no es un equivalente directo.
Mientras que la anotación se utilizaba generalmente para hacer referencia al
nombre del controlador de Ingress que debería implementar el Ingress, el campo
es una referencia a un recurso IngressClass que contiene configuración adicional
del Ingress, incluyendo el nombre del controlador Ingress.
IngressClass por defecto
Puedes marcar un ingressClass en particular por defecto para tu clúster.
Establecer la anotación ingressclass.kubernetes.io/is-default-class
a true
en un recurso IngressClass
asegurará que los nuevos Ingress sin un campo ingressClassName
especificado
sean asignados a esta IngressClass por defecto.
Precaución:
Si tienes más de una IngressClass marcada por defecto en tu clúster, el controlador de admisión impide crear objetos que no tienen uningressClassName
especificado.
Puedes resolver esto asegurándote que como máximo 1 IngressClass está marcado
como el predeterminado en tu clúster.Existen algunos controladores de ingress,
que funcionan sin una definición de una ingressClass
.
Por ejemplo, el controlador Ingress-NGINX se puede configurar con
una opción
--watch-ingress-without-class
.
Sin embargo,
se recomienda
especificar el IngressClass
predeterminado:
apiVersion: networking.k8s.io/v1
kind: IngressClass
metadata:
labels:
app.kubernetes.io/component: controller
name: nginx-example
annotations:
ingressclass.kubernetes.io/is-default-class: "true"
spec:
controller: k8s.io/ingress-nginx
Tipos de Ingress
Ingress respaldado por un único servicio
Hay conceptos existentes de Kubernetes que te permiten exponer un Service único (mirar alternativas). También puedes hacerlo con un Ingress especificando un backend predeterminado sin reglas.
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: test-ingress
spec:
defaultBackend:
service:
name: test
port:
number: 80
Si lo creas usando kubectl apply -f
podrías mirar el estado del Ingress que
has creado:
kubectl get ingress test-ingress
NAME CLASS HOSTS ADDRESS PORTS AGE
test-ingress external-lb * 203.0.113.123 80 59s
Donde 203.0.113.123
es la IP asignada por el controlador Ingress para
satisfacer este Ingress.
Nota:
Los controladores de Ingress y los balanceadores de carga pueden tardar un minuto o dos en asignar una dirección IP. Hasta entonces, se podrá ver la dirección marcada como<pending>
.Abanico Simple
Una configuración de abanico enruta el tráfico de una única dirección IP a más de un Service, basado en la URI HTTP solicitada. Un Ingress te permite tener el número de balanceadores de carga al mínimo. Por ejemplo, una configuración como:
Requeriría un Ingress como este:
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: simple-fanout-example
spec:
rules:
- host: foo.bar.com
http:
paths:
- path: /foo
pathType: Prefix
backend:
service:
name: service1
port:
number: 4200
- path: /bar
pathType: Prefix
backend:
service:
name: service2
port:
number: 8080
Cuando creas el Ingress con kubectl apply -f
:
kubectl describe ingress simple-fanout-example
Name: simple-fanout-example
Namespace: default
Address: 178.91.123.132
Default backend: default-http-backend:80 (10.8.2.3:8080)
Rules:
Host Path Backends
---- ---- --------
foo.bar.com
/foo service1:4200 (10.8.0.90:4200)
/bar service2:8080 (10.8.0.91:8080)
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal ADD 22s loadbalancer-controller default/test
El controlador de Ingress aprovisiona un balanceador de cargas específico para
la implementación que satisface al Ingress,
mientras los Services (service1
, service2
) existan.
Cuando sea así, podrás ver la dirección del balanceador de cargas en el campo de dirección.
Nota:
Dependiendo del controlador de Ingress que uses, puede que necesites crear un Service default-http-backend.Hosting virtual basado en nombre
Los hostings virtuales basados en el nombre soportan enrutado de tráfico HTTP a nombres hosts múltiples con la misma dirección IP.
El siguiente Ingress le dice al balanceador de cargas de respaldo de enrutar las peticiones basadas en el encabezado del Host .
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: name-virtual-host-ingress
spec:
rules:
- host: foo.bar.com
http:
paths:
- pathType: Prefix
path: "/"
backend:
service:
name: service1
port:
number: 80
- host: bar.foo.com
http:
paths:
- pathType: Prefix
path: "/"
backend:
service:
name: service2
port:
number: 80
Si creas un recurso Ingress sin ningún host definido en las reglas, luego cualquier tráfico web a la dirección IP de tu controlador Ingress puede coincidir sin requerir un host virtual basado en el nombre.
Por ejemplo, el siguiente Ingress enruta el tráfico solicitado
para first.bar.com
a service1
, second.bar.com
a service2
,
y cualquier tráfico cuyo encabezado de petición del host no coincida
con first.bar.com
y second.bar.com
a service3
.
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: name-virtual-host-ingress-no-third-host
spec:
rules:
- host: first.bar.com
http:
paths:
- pathType: Prefix
path: "/"
backend:
service:
name: service1
port:
number: 80
- host: second.bar.com
http:
paths:
- pathType: Prefix
path: "/"
backend:
service:
name: service2
port:
number: 80
- http:
paths:
- pathType: Prefix
path: "/"
backend:
service:
name: service3
port:
number: 80
TLS
Puedes segurizar un Ingress especificando un Secret que contiene una clave privada TLS y un certificado.
El recurso Ingress solo soporta un puerto TLS,
el 443,
y asume la terminación TLS en el punto del ingress
(El tráfico al Service y sus Pods es en texto plano).
Si la sección de configuración TLS especifica hosts diferentes,
se multiplexan en el mismo puerto de acuerdo con el hostname especificado a
través de la extensión TLS SNI (teniendo el cuenta que el controlador de Ingress
soporte SNI).
El secreto TLS debe contener claves llamadas tls.crt
y tls.key
que contiene
el certificado y llave privad para usar TLS.
Por ejemplo:
apiVersion: v1
kind: Secret
metadata:
name: testsecret-tls
namespace: default
data:
tls.crt: base64 encoded cert
tls.key: base64 encoded key
type: kubernetes.io/tls
Al hacer referencia a este secreto en un Ingress le indica al controlador
Ingress de segurizar el canal desde el cliente al balanceador de cargas usando
TLS.
Necesitas asegurarte que el secreto TLS que has creado viene de un certificado que
contiene un nombre común (CN), también conocido como Nombre de dominio
calificado (FQDN en inglés)
para https-example.foo.com
.
Nota:
Ten en cuenta que TLS no funcionará en la regla predeterminada porque los certificados estarían emitidos para todos los sub-dominios posibles. Por lo tanto, loshosts
en la sección tls
tienen que coincidir
explícitamente con el host
en la sección rules
.apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: tls-example-ingress
spec:
tls:
- hosts:
- https-example.foo.com
secretName: testsecret-tls
rules:
- host: https-example.foo.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: service1
port:
number: 80
Nota:
Hay una diferencia entre las características TLS soportadas por varios controladores Ingress. Mira la documentación en nginx, GCE, o cualquier otro controlador Ingress específico de plataforma para entender cómo funciona el TLS en tu entorno.Balanceo de cargas
Un controlador de Ingress está configurado por defecto con algunos ajustes de política de balanceo de cargas que aplica a todos los Ingress, como los algoritmos de balanceo de cargas, esquema de pesos del backend y otros. Otros conceptos más avanzados de balanceo de cargas (ej., sesiones persistentes, pesos dinámicos) no están expuestos todavía a través del Ingress. En su lugar, obtienes estas características a través del balanceador de cargas usado por un Service.
Vale la pena apuntar que aunque las revisiones de salud no se exponen directamente a través del Ingress, existen conceptos paralelos en Kubernetes tales como readiness probes que permiten lograr el mismo resultado final. Revisa la documentación específica del controlador para conocer cómo manejar estas revisiones de salud (por ejemplo: nginx, o GCE).
Actualizando un Ingress
Para actualizar un Ingress existente a un nuevo Host, puedes actualizarlo editando el recurso:
kubectl describe ingress test
Name: test
Namespace: default
Address: 178.91.123.132
Default backend: default-http-backend:80 (10.8.2.3:8080)
Rules:
Host Path Backends
---- ---- --------
foo.bar.com
/foo service1:80 (10.8.0.90:80)
Annotations:
nginx.ingress.kubernetes.io/rewrite-target: /
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal ADD 35s loadbalancer-controller default/test
kubectl edit ingress test
Esto muestra un editor con la configuración existente en formato YAML. Modifícalo para incluir el nuevo Host:
spec:
rules:
- host: foo.bar.com
http:
paths:
- backend:
service:
name: service1
port:
number: 80
path: /foo
pathType: Prefix
- host: bar.baz.com
http:
paths:
- backend:
service:
name: service2
port:
number: 80
path: /foo
pathType: Prefix
#..
Luego de guardar tus cambios, kubectl actualiza el recurso en el servidor API, que le indica al controlador Ingress de reconfigurar el balanceador de cargas.
Verifica esto:
kubectl describe ingress test
Name: test
Namespace: default
Address: 178.91.123.132
Default backend: default-http-backend:80 (10.8.2.3:8080)
Rules:
Host Path Backends
---- ---- --------
foo.bar.com
/foo service1:80 (10.8.0.90:80)
bar.baz.com
/foo service2:80 (10.8.0.91:80)
Annotations:
nginx.ingress.kubernetes.io/rewrite-target: /
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal ADD 45s loadbalancer-controller default/test
Puedes lograr el mismo resultado invocando kubectl replace -f
en un fichero
YAML de Ingress.
Fallos a través de zonas de disponibilidad
Las técnicas para distribuir el tráfico entre dominios de falla difieren entre los proveedores de la nube. Revisa la documentación del Ingress controller relevante para detalles.
Alternativas
Puedes exponer un Service de muchas maneras que no involucran directamente el recurso Ingress:
- Usa un Service.Type=LoadBalancer
- Usa un Service.Type=NodePort
Siguientes pasos
- Aprende sobre la API del Ingress
- Aprende sobre Ingress controllers
- Configurar un Ingress en Minikube con el controlador NGINX
3 - Controladores Ingress
Para que el recurso Ingress funcione, el clúster necesita tener un controlador Ingress corriendo.
Mientras otro tipo de controladores que corren como parte del binario de kube-controller-manager
, los controladores Ingress no son automaticamente iniciados dentro del clúster. Usa esta página para elegir la mejor implementación de controlador Ingress que funcione mejor para tu clúster.
Kubernetes es un proyecto que soporta y mantiene los controladores Ingress de AWS, GCE y nginx.
Controladores adicionales
- AKS Application Gateway Ingress Controller es un controlador Ingress que controla la configuración de Azure Application Gateway.
- Alibaba Cloud MSE Ingress es un controlador Ingress que controla la configuración de Alibaba Cloud Native Gateway, el cual es una versión comercial de Higress.
- Apache APISIX ingress controller es un Apache APISIX-basado en un controlador Ingress.
- Avi Kubernetes Operator provee un balanceador de cargas L4-L7 usando VMware NSX Advanced Load Balancer.
- BFE Ingress Controller es un controlador Ingress basado en BFE.
- Cilium Ingress Controller es un controlador Ingress potenciado por Cilium.
- El Citrix ingress controller funciona con Citrix Application Delivery Controller.
- Contour es un controlador Ingress basado en Envoy.
- Emissary-Ingress es un API Gateway Envoy-basado en el controlador Ingress.
- EnRoute es un API Gateway basado en Envoy que puede correr como un controlador Ingress.
- Easegress IngressController es una API Gateway basada en Easegress que puede correr como un controlador Ingress.
- F5 BIG-IP Container Ingress Services for Kubernetes te permite usar un Ingress para configurar los servidores virtuales de F5 BIG-IP.
- FortiADC Ingress Controller soporta el recurso de Kubernetes Ingress y te permite manejar los objectos FortiADC desde Kubernetes.
- Gloo es un controlador Ingress de código abierto basado en Envoy, el cual ofrece la funcionalidad de API gateway.
- HAProxy Ingress es un controlador Ingress para HAProxy.
- Higress es una API Gateway basada en Envoy que puede funcionar como un controlador Ingress.
- El HAProxy Ingress Controller for Kubernetes es también un controlador Ingress para HAProxy.
- Istio Ingress es un controlador Ingress basado en Istio.
- El Kong Ingress Controller for Kubernetes es un controlador Ingress que controla Kong Gateway.
- Kusk Gateway es un controlador Ingress OpenAPI-driven basado en Envoy.
- El NGINX Ingress Controller for Kubernetes trabaja con el servidor web (como un proxy) NGINX.
- El ngrok Kubernetes Ingress Controller es un controlador de código abierto para añadir acceso público seguro a sus servicios K8s utilizando la plataforma ngrok.
- El OCI Native Ingress Controller es un controlador Ingress para Oracle Cloud Infrastructure el cual te permite manejar el balanceador de cargas OCI.
- El Pomerium Ingress Controller esta basado en Pomerium, que ofrece una política de acceso sensible al contexto.
- Skipper es un enrutador HTTP y proxy inverso para la composición de servicios, incluyendo casos de uso como Kubernetes Ingress, diseñado como una biblioteca para construir su proxy personalizado.
- El Traefik Kubernetes Ingress provider es un controlador Ingress para el Traefik proxy.
- El Tyk Operator amplía Ingress con recursos personalizados para aportar capacidades de gestión de API a Ingress. Tyk Operator funciona con el plano de control de código abierto Tyk Gateway y Tyk Cloud.
- Voyager es un controlador Ingress para HAProxy.
- Wallarm Ingress Controller es un controlador Ingress que proporciona capacidades de seguridad WAAP (WAF) y API.
Uso de varios controladores Ingress
Puedes desplegar cualquier número de controladores Ingress utilizando clase ingress
dentro de un clúster. Ten en cuenta el .metadata.name
de tu recurso de clase Ingress. Cuando creas un Ingress, necesitarás ese nombre para especificar el campo ingressClassName
de su objeto Ingress (consulta referencia IngressSpec v1). ingressClassName
sustituye el antiguo método de anotación.
Si no especificas una IngressClass para un Ingress, y tu clúster tiene exactamente una IngressClass marcada como predeterminada, Kubernetes aplica la IngressClass predeterminada del clúster al Ingress.
Se marca una IngressClass como predeterminada estableciendo la anotación ingressclass.kubernetes.io/is-default-class
en esa IngressClass, con el valor de cadena "true"
.
Lo ideal sería que todos los controladores Ingress cumplieran esta especificación, pero los distintos controladores Ingress funcionan de forma ligeramente diferente.
Nota:
Asegúrate de revisar la documentación de tu controlador Ingress para entender las advertencias de tu elección.Siguientes pasos
- Más información Ingress.
- Configurar Ingress en Minikube con el controlador NGINX.
4 - Políticas de red (Network Policies)
Si quieres controlar el tráfico de red a nivel de dirección IP o puerto (capa OSI 3 o 4), puedes considerar el uso de Kubernetes NetworkPolicies para las aplicaciones que corren en tu clúster. Las NetworkPolicies son una estructura enfocada en las aplicaciones que permite establecer cómo un Pod puede comunicarse con otras "entidades" (utilizamos la palabra "entidad" para evitar sobrecargar términos más comunes como "Endpoint" o "Service", que tienen connotaciones específicas de Kubernetes) a través de la red. Las NetworkPolicies se aplican a uno o ambos extremos de la conexión a un Pod, sin afectar a otras conexiones.
Las entidades con las que un Pod puede comunicarse son una combinación de estos 3 tipos:
- Otros Pods permitidos (excepción: un Pod no puede bloquear el acceso a sí mismo)
- Namespaces permitidos
- Bloqueos de IP (excepción: el tráfico hacia y desde el nodo donde se ejecuta un Pod siempre está permitido, independientemente de la dirección IP del Pod o del nodo)
Cuando se define una NetworkPolicy basada en Pods o Namespaces, se utiliza un Selector para especificar qué tráfico se permite desde y hacia los Pod(s) que coinciden con el selector.
Por otro lado, cuando se crean NetworkPolicies basadas en IP, se definen políticas basadas en bloques de IP (rangos CIDR).
Prerrequisitos
Las políticas de red son implementadas por el plugin de red. Para usar políticas de red, debes estar utilizando una solución de red que soporte NetworkPolicy. Crear un recurso NetworkPolicy sin un controlador que lo habilite no tendrá efecto alguno.
Dos Tipos de Aislamiento de Pod
Hay dos tipos de aislamiento para un Pod: el aislamiento para la salida y el aislamiento para la entrada. Estos se refieren a las conexiones que pueden establecerse. El término "Aislamiento" en el contexto de este documento no es absoluto, sino que significa "se aplican algunas restricciones". La alternativa, "no aislado para $dirección", significa que no se aplican restricciones en la dirección descrita. Los dos tipos de aislamiento (o no) se declaran independientemente, y ambos son relevantes para una conexión de un Pod a otro.
Por defecto, un Pod no está aislado para la salida; todas las conexiones salientes están permitidas. Un Pod está aislado para la salida si hay alguna NetworkPolicy con "Egress" en su policyTypes
que seleccione el Pod; decimos que tal política se aplica al Pod para la salida. Cuando un Pod está aislado para la salida, las únicas conexiones permitidas desde el Pod son las permitidas por la lista egress
de las NetworkPolicy que se apliquen al Pod para la salida. Los valores de esas listas egress
se combinan de forma aditiva.
Por defecto, un Pod no está aislado para la entrada; todas las conexiones entrantes están permitidas. Un Pod está aislado para la entrada si hay alguna NetworkPolicy con "Ingress" en su policyTypes
que seleccione el Pod; decimos que tal política se aplica al Pod para la entrada. Cuando un Pod está aislado para la entrada, las únicas conexiones permitidas en el Pod son las del nodo del Pod y las permitidas por la lista ingress
de alguna NetworkPolicy que se apliquen al Pod para la entrada. Los valores de esas listas de direcciones se combinan de forma aditiva.
Las políticas de red no entran en conflicto; son aditivas. Si alguna política(s) se aplica a un Pod para una dirección determinada, las conexiones permitidas en esa dirección desde ese Pod son la unión de lo que permiten las políticas aplicables. Por lo tanto, el orden de evaluación no afecta al resultado de la política.
Para que se permita una conexión desde un Pod de origen a un Pod de destino, tanto la política de salida del Pod de origen como la de entrada del Pod de destino deben permitir la conexión. Si cualquiera de los dos lados no permite la conexión, ésta no se producirá.
El Recurso NetworkPolicy
Ver la referencia NetworkPolicy para una definición completa del recurso.
Un ejemplo de NetworkPolicy podría ser este:
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: test-network-policy
namespace: default
spec:
podSelector:
matchLabels:
role: db
policyTypes:
- Ingress
- Egress
ingress:
- from:
- ipBlock:
cidr: 172.17.0.0/16
except:
- 172.17.1.0/24
- namespaceSelector:
matchLabels:
project: myproject
- podSelector:
matchLabels:
role: frontend
ports:
- protocol: TCP
port: 6379
egress:
- to:
- ipBlock:
cidr: 10.0.0.0/24
ports:
- protocol: TCP
port: 5978
Nota:
Enviar esto al API Server de tu clúster no tendrá ningún efecto a menos que tu solución de red soporte políticas de red.Campos Obligatorios: Como con todas las otras configuraciones de Kubernetes, una NetworkPolicy
necesita los campos apiVersion
, kind
, y metadata
. Para obtener información general
sobre cómo funcionan esos archivos de configuración, puedes consultar
Configurar un Pod para usar un ConfigMap,
y Gestión de Objetos.
spec: NetworkPolicy spec contiene toda la información necesaria para definir una política de red dado un Namespace.
podSelector: Cada NetworkPolicy incluye un podSelector
el cual selecciona el grupo de Pods en los cuales aplica la política. La política de ejemplo selecciona Pods con la etiqueta "role=db". Un podSelector
vacío selecciona todos los Pods en un Namespace.
policyTypes: Cada NetworkPolicy incluye una lista de policyTypes
la cual puede incluir Ingress
, Egress
, o ambas. Los campos policyTypes
indican si la política aplica o no al tráfico de entrada hacia el Pod seleccionado, el tráfico de salida desde el Pod seleccionado, o ambos. Si no se especifican policyTypes
en una NetworkPolicy, el valor Ingress
se aplicará siempre por defecto y Egress
se aplicará si la NetworkPolicy contiene alguna regla de salida.
ingress: Cada NetworkPolicy puede incluir una lista de reglas ingress
permitidas. Cada regla permite el tráfico relacionado con los valores de las secciones from
y ports
. La política de ejemplo contiene una única regla, la cual se relaciona con el tráfico sobre un solo puerto, desde uno de los tres orígenes definidos, el primero especificado por el valor ipBlock
, el segundo especificado por el valor namespaceSelector
y el tercero especificado por el podSelector
.
egress: Cada NetworkPolicy puede incluir una lista de reglas de egress
permitidas. Cada regla permite el tráfico relacionado con los valores de las secciones to
y ports
. La política de ejemplo contiene una única regla, la cual se relaciona con el tráfico en un único puerto para cualquier destino en el rango de IPs 10.0.0.0/24
.
Por lo tanto, la NetworkPolicy de ejemplo:
- Aísla los Pods "role=db" en el Namespace "default" para ambos tipos de tráfico ingress y egress (si aún no están aislados).
- (Reglas Ingress) permite la conexión hacia todos los Pods en el Namespace "default" con la etiqueta "role=db" en el puerto TCP 6379 desde los siguientes orígenes:
- cualquier Pod en el Namespace "default" con la etiqueta "role=frontend"
- cualquier Pod en un Namespace con la etiqueta "project=myproject"
- La dirección IP en los rangos 172.17.0.0–172.17.0.255 y 172.17.2.0–172.17.255.255 (por ejemplo, todo el rango de IPs de 172.17.0.0/16 con excepción del 172.17.1.0/24)
- (Reglas de Egress) permite la conexión desde cualquier Pod en el Namespace "default" con la etiqueta "role=db" hacia CIDR 10.0.0.0/24 en el puerto TCP 5978
Ver el artículo de Declarar Network Policy para más ejemplos.
Comportamiento de los selectores to
y from
Existen cuatro tipos de selectores que pueden ser especificados en una sección ingress
from
o en una sección egress
to
:
podSelector: Este selector selecciona Pods específicos en el mismo Namespace que la NetworkPolicy para permitir el tráfico como origen de entrada o destino de salida.
namespaceSelector: Este selector selecciona Namespaces específicos para permitir el tráfico como origen de entrada o destino de salida.
namespaceSelector y podSelector: Una única entrada to
/from
que especifica tanto namespaceSelector
como podSelector
selecciona Pods específicos dentro de Namespaces específicos. Es importante revisar que se utiliza la sintaxis de YAML correcta. A continuación se muestra un ejemplo de esta política:
...
ingress:
- from:
- namespaceSelector:
matchLabels:
user: alice
podSelector:
matchLabels:
role: client
...
contiene un elemento from
permitiendo conexiones desde los Pods con el label role=client
en Namespaces con el label user=alice
. Por el contrario, esta política:
...
ingress:
- from:
- namespaceSelector:
matchLabels:
user: alice
- podSelector:
matchLabels:
role: client
...
contiene dos elementos en el array from
, y permite conexiones desde Pods en los Namespaces con el label role=client
, o desde cualquier Pod en cualquier Namespace con el label user=alice
.
En caso de duda, utilice kubectl describe
para ver cómo Kubernetes ha interpretado la política.
ipBlock: Este selector selecciona rangos CIDR de IP específicos para permitirlas como origen de entrada o destino de salida. Estas IPs deben ser externas al clúster, ya que las IPs de Pod son efímeras e impredecibles.
Los mecanismos de entrada y salida del clúster a menudo requieren reescribir la IP de origen o destino
de los paquetes. En los casos en los que esto ocurre, no está definido si esto ocurre antes o
después del procesamiento de NetworkPolicy, y el comportamiento puede ser diferente para diferentes
combinaciones de plugin de red, proveedor de nube, implementación de Service
, etc.
En el caso de la entrada, esto significa que en algunos casos se pueden filtrar paquetes
entrantes basándose en la IP de origen real, mientras que en otros casos, la "IP de origen" sobre la que actúa la NetworkPolicy
puede ser la IP de un LoadBalancer
o la IP del Nodo donde está el Pod involucrado, etc.
Para la salida, esto significa que las conexiones de los Pods a las IPs de Service
que se reescriben a
IPs externas al clúster pueden o no estar sujetas a políticas basadas en ipBlock
.
Políticas por defecto
Por defecto, si no existen políticas en un Namespace, se permite todo el tráfico de entrada y salida hacia y desde los Pods de ese Namespace. Los siguientes ejemplos muestran cómo cambiar el comportamiento por defecto en ese Namespace.
Denegar todo el tráfico de entrada por defecto
Puedes crear una política que "por defecto" aisle a un Namespace del tráfico de entrada con la creación de una política que seleccione todos los Pods del Namespace pero no permite ningún tráfico de entrada en esos Pods.
---
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: default-deny-ingress
spec:
podSelector: {}
policyTypes:
- Ingress
Esto asegura que incluso los Pods que no están seleccionados por ninguna otra NetworkPolicy también serán aislados del tráfico de entrada. Esta política no afecta el aislamiento en el tráfico de salida desde cualquier Pod.
Permitir todo el tráfico de entrada
Si quieres permitir todo el tráfico de entrada a todos los Pods en un Namespace, puedes crear una política que explícitamente permita eso.
---
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-all-ingress
spec:
podSelector: {}
ingress:
- {}
policyTypes:
- Ingress
Con esta política en curso, ninguna política(s) adicional puede hacer que se niegue cualquier conexión entrante a esos Pods. Esta política no tiene efecto sobre el aislamiento del tráfico de salida de cualquier Pod.
Denegar por defecto todo el tráfico de salida
Puedes crear una política que "por defecto" aisle el tráfico de salida para un Namespace, creando una NetworkPolicy que seleccione todos los Pods pero que no permita ningún tráfico de salida desde esos Pods.
---
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: default-deny-egress
spec:
podSelector: {}
policyTypes:
- Egress
Esto asegura que incluso los Pods que no son seleccionados por ninguna otra NetworkPolicy no tengan permitido el tráfico de salida. Esta política no cambia el comportamiento de aislamiento para el tráfico de entrada de ningún Pod.
Permitir todo el tráfico de salida
Si quieres permitir todas las conexiones desde todos los Pods de un Namespace, puedes crear una política que permita explícitamente todas las conexiones salientes de los Pods de ese Namespace.
---
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-all-egress
spec:
podSelector: {}
egress:
- {}
policyTypes:
- Egress
Con esta política en vigor, ninguna política(s) adicional puede hacer que se niegue cualquier conexión de salida desde esos Pods. Esta política no tiene efecto sobre el aislamiento para el tráfico de entrada a cualquier Pod.
Denegar por defecto todo el tráfico de entrada y de salida
Puedes crear una política que "por defecto" en un Namespace impida todo el tráfico de entrada y de salida creando la siguiente NetworkPolicy en ese Namespace.
---
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: default-deny-all
spec:
podSelector: {}
policyTypes:
- Ingress
- Egress
Esto asegura que incluso los Pods que no son seleccionados por ninguna otra NetworkPolicy no tendrán permitido el tráfico de entrada o salida.
Soporte a SCTP
Kubernetes v1.20 [stable]
Como característica estable, está activada por defecto. Para deshabilitar SCTP a nivel de clúster, usted (o el administrador de su clúster) tiene que deshabilitar la feature gate SCTPSupport
para el API Server con el flag --feature-gates=SCTPSupport=false,...
.
Cuando esta feature gate está habilitada, puede establecer el campo protocol
de una NetworkPolicy como SCTP
.
Nota:
Debes utilizar un plugin de CNI que soporte el protocolo SCTP NetworkPolicies.Apuntar a un rango de puertos
Kubernetes v1.22 [beta]
Cuando se escribe una NetworkPolicy, se puede apuntar a un rango de puertos en lugar de un solo puerto.
Esto se puede lograr con el uso del campo endPort
, como en el siguiente ejemplo:
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: multi-port-egress
namespace: default
spec:
podSelector:
matchLabels:
role: db
policyTypes:
- Egress
egress:
- to:
- ipBlock:
cidr: 10.0.0.0/24
ports:
- protocol: TCP
port: 32000
endPort: 32768
La regla anterior permite que cualquier Pod con la etiqueta role=db
en el Namespace default
se comunique
con cualquier IP dentro del rango 10.0.0.0/24
sobre el protocolo TCP, siempre que el puerto
esté entre el rango 32000 y 32768.
Se aplican las siguientes restricciones al utilizar este campo:
- Como característica en estado beta, está activada por defecto. Para desactivar el campo
endPort
a nivel de clúster, usted (o su administrador de clúster) debe desactivar la feature gateNetworkPolicyEndPort
en el API Server con el flag--feature-gates=NetworkPolicyEndPort=false,...
. - El campo
endPort
debe ser igual o mayor que el campoport
. - Solo se puede definir
endPort
si también se defineport
. - Ambos puertos deben ser numéricos.
Nota:
Su clúster debe utilizar un plugin de CNI que soporte el campoendPort
en las especificaciones de NetworkPolicy.
Si su plugin de red
no soporta el campo endPort
y usted especifica una NetworkPolicy que use este campo,
la política se aplicará solo para el campo port
.Cómo apuntar a un Namespace usando su nombre
Kubernetes 1.22 [stable]
El plano de control de Kubernetes establece una etiqueta inmutable kubernetes.io/metadata.name
en todos los
Namespaces, siempre que se haya habilitado la feature gate NamespaceDefaultLabelName
.
El valor de la etiqueta es el nombre del Namespace.
Aunque NetworkPolicy no puede apuntar a un Namespace por su nombre con algún campo de objeto, puede utilizar la etiqueta estandarizada para apuntar a un Namespace específico.
Qué no puedes hacer con políticas de red (al menos, aún no)
Actualmente, en Kubernetes 1.32, la siguiente funcionalidad no existe en la API de NetworkPolicy, pero es posible que se puedan implementar soluciones mediante componentes del sistema operativo (como SELinux, OpenVSwitch, IPTables, etc.) o tecnologías de capa 7 (controladores Ingress, implementaciones de Service Mesh) o controladores de admisión. En caso de que seas nuevo en la seguridad de la red en Kubernetes, vale la pena señalar que las siguientes historias de usuario no pueden (todavía) ser implementadas usando la API NetworkPolicy.
- Forzar que el tráfico interno del clúster pase por una puerta de enlace común (esto se puede implementar con una malla de servicios u otro proxy).
- Cualquier cosa relacionada con TLS (se puede implementar con una malla de servicios o un controlador Ingress para esto).
- Políticas específicas de los nodos (se puede utilizar la notación CIDR para esto, pero no se puede apuntar a los nodos por sus identidades Kubernetes específicamente).
- Apuntar Services por nombre (sin embargo, se pueden orientar los Pods o los Namespaces por sus labels, lo que suele ser una solución viable).
- Creación o gestión de "solicitudes de políticas" que son atendidas por un tercero.
- Políticas que por defecto son aplicadas a todos los Namespaces o Pods (hay algunas distribuciones y proyectos de Kubernetes de terceros que pueden hacer esto).
- Consulta avanzada de políticas y herramientas de accesibilidad.
- La capacidad de registrar los eventos de seguridad de la red (por ejemplo, las conexiones bloqueadas o aceptadas).
- La capacidad de negar explícitamente las políticas (actualmente el modelo para NetworkPolicies es negar por defecto, con solo la capacidad de añadir reglas de permitir).
- La capacidad de impedir el tráfico entrante de Loopback o de Host (actualmente los Pods no pueden bloquear el acceso al host local, ni tienen la capacidad de bloquear el acceso desde su nodo residente).
Siguientes pasos
- Leer el artículo sobre Cómo Declarar Políticas de Red para ver más ejemplos.
- Ver más recetas de escenarios comunes habilitados por los recursos de las NetworkPolicy.
5 - EndpointSlices
Kubernetes v1.21 [stable]
La API de EndpointSlice de Kubernetes proporciona una forma de rastrear los endpoints de red dentro de un clúster Kubernetes. EndpointSlices ofrece una alternativa más escalable y extensible a Endpoints.
EndpointSlice API
En Kubernetes, un EndpointSlice contiene referencias a un conjunto de endpoints de red. El plano de control crea automáticamente EndpointSlices para cualquier Servicio de Kubernetes que tenga especificado un selector. Estos EndpointSlices incluyen referencias a todos los Pods que coinciden con el selector de Servicio. Los EndpointSlices agrupan los endpoints de la red mediante combinaciones únicas de protocolo, número de puerto y nombre de Servicio.
El nombre de un objeto EndpointSlice debe ser un nombre de subdominio DNS válido.
A modo de ejemplo, a continuación se muestra un objeto EndpointSlice de ejemplo, propiedad del Servicio example
de Kubernetes.
apiVersion: discovery.k8s.io/v1
kind: EndpointSlice
metadata:
name: example-abc
labels:
kubernetes.io/service-name: example
addressType: IPv4
ports:
- name: http
protocol: TCP
port: 80
endpoints:
- addresses:
- "10.1.2.3"
conditions:
ready: true
hostname: pod-1
nodeName: node-1
zone: us-west2-a
Por defecto, el plano de control crea y gestiona EndpointSlices para que no tengan más de 100 endpoints cada una. Puedes configurar esto con la bandera de funcionalidad
--max-endpoints-per-slice
kube-controller-manager
hasta un máximo de 1000.
EndpointSlices puede actuar como la fuente de verdad kube-proxy sobre cómo enrutar el tráfico interno.
Tipos de dirección
EndpointSlices admite tres tipos de direcciones:
- IPv4
- IPv6
- FQDN (Fully Qualified Domain Name)
Cada objeto EndpointSlice
representa un tipo de dirección IP específico. Si tienes un servicio disponible a través de IPv4 e IPv6, habrá al menos dos objetos EndpointSlice
(uno para IPv4 y otro para IPv6).
Condiciones
La API EndpointSlice almacena condiciones sobre los endpoints que pueden ser útiles para los consumidores.
Las tres condiciones son ready
, serving
y terminating
.
Ready
ready
es una condición que corresponde a la condición Ready
de un Pod. Un Pod en ejecución con la condición Ready
establecida a True
debería tener esta condición EndpointSlice también establecida a true
. Por razones de compatibilidad, ready
NUNCA es true
cuando un Pod está terminando. Los consumidores deben referirse a la condición serving
para inspeccionar la disponibilidad de los Pods que están terminando. La única excepción a esta regla son los servicios con spec.publishNotReadyAddresses
a true
. Los endpoints de estos servicios siempre tendrán la condición ready
a true
.
Serving
Kubernetes v1.26 [stable]
La condición serving
es casi idéntica a la condición ready
. La diferencia es que los consumidores de la API EndpointSlice deben comprobar la condición serving
si se preocupan por la disponibilidad del pod mientras el pod también está terminando.
Nota:
Aunqueserving
es casi idéntico a ready
, se añadió para evitar romper el significado existente de ready
. Podría ser inesperado para los clientes existentes si ready
pudiera ser true
para los endpoints de terminación, ya que históricamente los endpoints de terminación nunca se incluyeron en la API Endpoints o EndpointSlice para empezar. Por esta razón, ready
es siempre false
para los Endpoints que terminan, y se ha añadido una nueva condición serving
en la v1.20 para que los clientes puedan realizar un seguimiento de la disponibilidad de los pods que terminan independientemente de la semántica existente para ready
.Terminating
Kubernetes v1.22 [beta]
Terminating
es una condición que indica si un endpoint está terminando. En el caso de los pods, se trata de cualquier pod que tenga establecida una marca de tiempo de borrado.
Información sobre topología
Cada endpoint dentro de un EndpointSlice puede contener información topológica relevante. La información de topología incluye la ubicación del endpoint e información sobre el Nodo y la zona correspondientes. Estos están disponibles en los siguientes campos por endpoint en EndpointSlices:
nodeName
- El nombre del Nodo en el que se encuentra este endpoint.zone
- La zona en la que se encuentra este endpoint.
Nota:
En la API v1, el endpoint topology
se eliminó en favor de los campos dedicados nodeName
y zone
.
La configuración de campos de topología arbitrarios en el campo endpoint
de un recurso EndpointSlice
ha quedado obsoleta y no se admite en la API v1. En su lugar, la API v1 permite establecer campos individuales nodeName
y zone
. Estos campos se traducen automáticamente entre versiones de la API. Por ejemplo, el valor de la clave "topology.kubernetes.io/zone" en el campo topology
de la API v1beta1 es accesible como campo zone
en la API v1.
Administración
En la mayoría de los casos, el plano de control (concretamente, el endpoint slice controller) crea y gestiona objetos EndpointSlice. Existe una variedad de otros casos de uso para EndpointSlices, como implementaciones de servicios Mesh, que podrían dar lugar a que otras entidades o controladores gestionen conjuntos adicionales de EndpointSlices.
Para garantizar que varias entidades puedan gestionar EndpointSlices sin interferir unas con otras, Kubernetes define el parámetro
label
endpointslice.kubernetes.io/managed-by
, que indica la entidad que gestiona un EndpointSlice.
El controlador de endpoint slice establece endpointslice-controller.k8s.io
como valor para esta etiqueta en todos los EndpointSlices que gestiona. Otras entidades que gestionen EndpointSlices también deben establecer un valor único para esta etiqueta.
Propiedad
En la mayoría de los casos de uso, los EndpointSlices son propiedad del Servicio para el que el objeto EndpointSlices rastree los endpoints. Esta propiedad se indica mediante una referencia de propietario en cada EndpointSlice, así como una etiqueta kubernetes.io/service-name
que permite búsquedas sencillas de todos los EndpointSlices que pertenecen a un Servicio.
Replicación de EndpointSlice
En algunos casos, las aplicaciones crean recursos Endpoints personalizados. Para garantizar que estas aplicaciones no tengan que escribir simultáneamente en recursos Endpoints y EndpointSlice, el plano de control del clúster refleja la mayoría de los recursos Endpoints en los EndpointSlices correspondientes.
El plano de control refleja los recursos de los Endpoints a menos que:
- El recurso Endpoints tenga una etiqueta
endpointslice.kubernetes.io/skip-mirror
con el valor entrue
. - El recurso Endpoints tenga una anotación
control-plane.alpha.kubernetes.io/leader
. - El recurso Service correspondiente no exista.
- El recurso Service correspondiente tiene un selector no nulo.
Los recursos Endpoints individuales pueden traducirse en múltiples EndpointSlices. Esto ocurrirá si un recurso Endpoints tiene múltiples subconjuntos o incluye endpoints con múltiples familias IP (IPv4 e IPv6). Se reflejará un máximo de 1000 direcciones por subconjunto en EndpointSlices.
Distribución de EndpointSlices
Cada EndpointSlice tiene un conjunto de puertos que se aplica a todos los endpoints dentro del recurso. Cuando se utilizan puertos con nombre para un Servicio, los Pods pueden terminar con diferentes números de puerto de destino para el mismo puerto con nombre, requiriendo diferentes EndpointSlices. Esto es similar a la lógica detrás de cómo se agrupan los subconjuntos con Endpoints.
El plano de control intenta llenar los EndpointSlices tanto como sea posible, pero no los reequilibra activamente. La lógica es bastante sencilla:
- Iterar a través de los EndpointSlices existentes, eliminar los endpoints que ya no se deseen y actualizar los endpoints coincidentes que hayan cambiado.
- Recorrer los EndpointSlices que han sido modificados en el primer paso y rellenarlos con los nuevos endpoints necesarios.
- Si aún quedan nuevos endpoints por añadir, intente encajarlos en un slice que no se haya modificado previamente y/o cree otros nuevos.
Es importante destacar que el tercer paso prioriza limitar las actualizaciones de EndpointSlice sobre una distribución perfectamente completa de EndpointSlices. Por ejemplo, si hay 10 nuevos endpoints que añadir y 2 EndpointSlices con espacio para 5 endpoints más cada uno, este enfoque creará un nuevo EndpointSlice en lugar de llenar los 2 EndpointSlices existentes. En otras palabras, es preferible una única creación de EndpointSlice que múltiples actualizaciones de EndpointSlice.
Con kube-proxy ejecutándose en cada Nodo y vigilando los EndpointSlices, cada cambio en un EndpointSlice se vuelve relativamente caro ya que será transmitido a cada Nodo del clúster. Este enfoque pretende limitar el número de cambios que necesitan ser enviados a cada Nodo, incluso si puede resultar con múltiples EndpointSlices que no están llenos.
En la práctica, esta distribución menos que ideal debería ser poco frecuente. La mayoría de los cambios procesados por el controlador EndpointSlice serán lo suficientemente pequeños como para caber en un EndpointSlice existente, y si no, es probable que pronto sea necesario un nuevo EndpointSlice de todos modos. Las actualizaciones continuas de los Deployments también proporcionan un reempaquetado natural de los EndpointSlices con todos los Pods y sus correspondientes endpoints siendo reemplazados.
Endpoints duplicados
Debido a la naturaleza de los cambios de EndpointSlice, los endpoints pueden estar representados en más de un EndpointSlice al mismo tiempo. Esto ocurre de forma natural, ya que los cambios en diferentes objetos EndpointSlice pueden llegar a la vigilancia / caché del cliente de Kubernetes en diferentes momentos.
Nota:
Los clientes de la API EndpointSlice deben iterar a través de todos los EndpointSlices existentes asociados a un Servicio y construir una lista completa de endpoints de red únicos. Es importante mencionar que los endpoints pueden estar duplicados en diferentes EndpointSlices.
Puedes encontrar una implementación de referencia sobre cómo realizar esta agregación y deduplicación de endpoints como parte del código EndpointSliceCache
dentro de kube-proxy
.
Comparación con endpoints
La API Endpoints original proporcionaba una forma simple y directa de rastrear los endpoints de red en Kubernetes. A medida que los clústeres de Kubernetes y los Services crecían para manejar más tráfico y enviar más tráfico a más Pods backend, las limitaciones de la API original se hicieron más visibles. Más notablemente, estos incluyen desafíos con la ampliación a un mayor número de endpoints de red.
Dado que todos los endpoints de red para un Servicio se almacenaban en un único objeto Endpoint, esos objetos Endpoints podían llegar a ser bastante grandes. Para los Services que permanecían estables (el mismo conjunto de endpoints durante un largo período de tiempo), el impacto era menos notable; incluso entonces, algunos casos de uso de Kubernetes no estaban bien servidos.
Cuando un Service tenía muchos Endpoints de backend y la carga de trabajo se escalaba con frecuencia o se introducían nuevos cambios con frecuencia, cada actualización del objeto Endpoint para ese Service suponía mucho tráfico entre los componentes del clúster de Kubernetes (dentro del plano de control y también entre los nodos y el servidor de API). Este tráfico adicional también tenía un coste en términos de uso de la CPU.
Con EndpointSlices, la adición o eliminación de un único Pod desencadena el mismo número de actualizaciones a los clientes que están pendientes de los cambios, pero el tamaño de esos mensajes de actualización es mucho menor a gran escala.
EndpointSlices también ha permitido innovar en torno a nuevas funciones, como las redes de doble pila y el enrutamiento con conocimiento de la topología.
Siguientes pasos
- Sigue las instrucciones del tutorial Conexión de aplicaciones con servicios
- Lee la Referencia API para la API EndpointSlice
- Lee la Referencia API para la API Endpoints