Crear un Service y un Ingress

En este documento, se muestra cómo crear un objeto Ingress de Kubernetes en un clúster de usuario, híbrido o independiente para GKE en Bare Metal. Un Ingress se asocia con uno o más Services, cada uno de los cuales está asociado con un conjunto de Pods.

Antes de comenzar

Obtén una conexión SSH de tu clúster a la estación de trabajo de administrador.

Crear una implementación

Este es un manifiesto para una implementación:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: hello-deployment
spec:
  selector:
    matchLabels:
      greeting: hello
  replicas: 3
  template:
    metadata:
      labels:
        greeting: hello
    spec:
      containers:
      - name: hello-world
        image: "gcr.io/google-samples/hello-app:2.0"
        env:
        - name: "PORT"
          value: "50000"
      - name: hello-kubernetes
        image: "gcr.io/google-samples/node-hello:1.0"
        env:
        - name: "PORT"
          value: "8080"

A los fines de este ejercicio, estos son los puntos importantes que debes comprender sobre el manifiesto de Deployment:

  • Cada Pod que pertenece al Deployment tiene la etiqueta greeting: hello.

  • Cada Pod tiene dos contenedores.

  • En los campos env, se especifica que los contenedores hello-app escuchan en el puerto TCP 50000, y los contenedores node-hello escuchan en el puerto TCP 8080. En el caso de hello-app, puedes ver el efecto de la variable de entorno PORT si observas el código fuente.

Copia el manifiesto en un archivo llamado hello-deployment.yaml y crea la implementación:

kubectl apply --kubeconfig CLUSTER_KUBECONFIG -f hello-deployment.yaml

Reemplaza CLUSTER_KUBECONFIG por el nombre del archivo kubeconfig del clúster.

Expón tu Deployment con un Service

A fin de proporcionar una forma estable para que los clientes envíen solicitudes a los Pods de tu Deployment, crea un Service.

Este es un manifiesto para un Service que expone tu Deployment a los clientes dentro del clúster:

apiVersion: v1
kind: Service
metadata:
  name: hello-service
spec:
  type: ClusterIP
  selector:
    greeting: hello
  ports:
  - name: world-port
    protocol: TCP
    port: 60000
    targetPort: 50000
  - name: kubernetes-port
    protocol: TCP
    port: 60001
    targetPort: 8080

Copia el manifiesto en un archivo llamado hello-service.yaml y crea el Service:

kubectl apply --kubeconfig CLUSTER_KUBECONFIG -f hello-service.yaml

Reemplaza CLUSTER_KUBECONFIG por el nombre del archivo kubeconfig del clúster.

Observa el Service:

kubectl --kubeconfig CLUSTER_KUBECONFIG get service hello-service --output yaml

El resultado muestra el valor de clusterIP que se le dio al Service. Por ejemplo:

apiVersion: v1
kind: Service
metadata:
  annotations:
    ...
spec:
  clusterIP: 10.96.14.249
  clusterIPs:
  - 10.96.14.249
  ipFamilies:
  - IPv4
  ipFamilyPolicy: SingleStack
  ports:
  - name: world-port
    port: 60000
    protocol: TCP
    targetPort: 50000
  - name: kubernetes-port
    port: 60001
    protocol: TCP
    targetPort: 8080
  selector:
    greeting: hello
  sessionAffinity: None
  type: ClusterIP
status:
  loadBalancer: {}

En el resultado anterior, el campo ports es un array de objetos ServicePort: uno llamado world-port y otro llamado kubernetes-port. Para obtener más información sobre los campos de Service, consulta ServiceSpec en la documentación de Kubernetes.

Estas son las formas en que un cliente puede llamar al Service:

  • Mediante world-port: Un cliente que se ejecuta en uno de los nodos del clúster envía una solicitud a clusterIP en port. En este ejemplo, 10.96.14.249:60000: La solicitud se reenvía a un Pod miembro en targetPort. En este ejemplo, POD_IP_ADDRESS:50000.

  • Mediante kubernetes-port: Un cliente que se ejecuta en uno de los nodos del clúster envía una solicitud a clusterIP en port. En este ejemplo, 10.96.14.249:60001: La solicitud se reenvía a un Pod miembro en targetPort. En este ejemplo, POD_IP_ADDRESS:8080.

Componentes de Ingress

Estos son algunos de los componentes del clúster relacionados con la entrada:

  • El Deployment istio-ingress. Este es el proxy de entrada. El proxy de entrada reenvía el tráfico a los servicios internos según las reglas especificadas en un objeto Ingress.

  • El servicio de istio-ingress. Este Service expone la implementación de istio-ingress.

  • El Deployment istiod. Este es el controlador de entrada. El controlador de Ingress detecta la creación de los objetos Ingress y configura el proxy de entrada según corresponda.

Todos estos componentes en el clúster de Istio se instalan en el espacio de nombres gke-system. Este espacio de nombres no entra en conflicto con una instalación completa de Istio/Anthos Service Mesh.

Crea un Ingress

Este es el manifiesto de un Ingress:

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: my-ingress
spec:
  rules:
  - http:
      paths:
      - path: /greet-the-world
        pathType: Exact
        backend:
          service:
            name: hello-service
            port:
              number: 60000
      - path: /greet-kubernetes
        pathType: Exact
        backend:
          service:
            name: hello-service
            port:
              number: 60001

Copia el manifiesto en un archivo llamado my-ingress.yaml y crea el Ingress:

kubectl apply --kubeconfig CLUSTER_KUBECONFIG -f my-ingress.yaml

Cuando creas un clúster de usuario, especificas un valor para loadbalancer.ingressVIP en el archivo de configuración del clúster. Esta dirección IP se configura en el balanceador de cargas del clúster. Cuando creas un Ingress, se le proporciona este mismo VIP como su dirección IP externa.

Cuando un cliente envía una solicitud a tu VIP de entrada del clúster de usuario, la solicitud se enruta al balanceador de cargas. El balanceador de cargas usa el servicio de istio-ingress para reenviar la solicitud al proxy de entrada, que se ejecuta en el clúster de usuario. El proxy de entrada está configurado para reenviar la solicitud a diferentes backends según la ruta en la URL de la solicitud.

La ruta /greet-the-world

En el manifiesto de Ingress, puedes ver una regla que indica que la ruta /greet-the-world está asociada con serviceName: hello-service y servicePort: 60000. Recuerda que 60000 es el valor de port en la sección world-port del Service hello-service.

- name: world-port
    port: 60000
    protocol: TCP
    targetPort: 50000

El Service de entrada reenvía la solicitud a clusterIP:50000. Luego, la solicitud va a uno de los Pods miembros del servicio hello-service. El contenedor, en ese Pod, que escucha en el puerto 50000 muestra un mensaje Hello World!.

La ruta /greet-kubernetes

En el manifiesto de Ingress, puedes ver una regla que indica que la ruta /greet-kubernetes está asociada con serviceName: hello-service y servicePort: 60001. Recuerda que 60001 es el valor de port en la sección kubernetes-port del Service hello-service.

- name: kubernetes-port
    port: 60001
    protocol: TCP
    targetPort: 8080

El Service de entrada reenvía la solicitud a clusterIP: 8080. Luego, la solicitud va a uno de los Pods miembros del servicio hello-service. El contenedor, en ese Pod, que escucha en el puerto 8080 muestra un mensaje Hello Kubernetes!.

Prueba el Ingress:

Prueba el Ingress con la ruta /greet-the-world:

curl CLUSTER_INGRESS_VIP/greet-the-world

Reemplaza CLUSTER_INGRESS_VIP por la dirección IP externa del Ingress.

El resultado muestra un mensaje Hello, world!:

Hello, world!
Version: 2.0.0
Hostname: ...

Prueba el Ingress con la ruta /greet-kubernetes:

curl CLUSTER_INGRESS_VIP/greet-kubernetes

El resultado muestra un mensaje Hello, Kubernetes!:

Hello Kubernetes!

Inhabilita el Ingress agrupado

La capacidad de entrada incluida con GKE en Bare Metal solo admite la funcionalidad de entrada. Puedes elegir integrarlo en Istio o en Anthos Service Mesh. Estos productos ofrecen los beneficios adicionales de una malla de servicios completamente funcional, como la TLS mutua (mTLS), la capacidad de administrar la autenticación entre servicios y la observabilidad de las cargas de trabajo. Si te integras a Istio o a Anthos Service Mesh, te recomendamos que inhabilites la capacidad de Ingress empaquetada.

Puedes habilitar o inhabilitar el Ingress agrupado con el campo spec.clusterNetwork.bundledIngress en el archivo de configuración del clúster. Este campo solo está disponible para los clústeres de la versión 1.13.0 y posteriores. El campo bundledIngress predeterminado es true y no está presente en el archivo de configuración del clúster generado. Este campo es mutable y se puede cambiar cuando crees o actualices un clúster de la versión 1.13.0 o superior. También puedes especificar este campo cuando actualices un clúster a la versión 1.13.0 o superior.

En el siguiente archivo de configuración de clúster de muestra, se indica cómo configurar tu clúster para inhabilitar la capacidad de Ingress agrupada:

apiVersion: v1
kind: Namespace
metadata:
  name: cluster-hybrid-basic
---
apiVersion: baremetal.cluster.gke.io/v1
kind: Cluster
metadata:
  name: hybrid-basic
  namespace: cluster-hybrid-basic
spec:
  type: hybrid
  profile: default
  anthosBareMetalVersion: 1.13.0
  gkeConnect:
    projectID: project-fleet
  controlPlane:
    nodePoolSpec:
      nodes:
      - address: 10.200.0.2
  clusterNetwork:
    bundledIngress: false
    pods:
      cidrBlocks:
      - 192.168.0.0/16
    services:
      cidrBlocks:
      - 10.96.0.0/20
...

Configura HTTPS para Ingress

Si deseas aceptar solicitudes HTTPS de los clientes, el proxy de entrada debe contar con un certificado para que pueda demostrar su identidad a los clientes. Este proxy también debe tener una clave privada para completar el protocolo de enlace HTTPS.

En el siguiente ejemplo, se usan estas entidades:

  • Proxy de Ingress: participa en el protocolo de enlace HTTPS y, luego, reenvía los paquetes a los pods miembros del servicio hello-service.

  • Dominio para el servicio hello-service: altostrat.com en la organización de ejemplo

Lleva a cabo los pasos siguientes:

  1. Crea un certificado raíz y una clave privada. En este ejemplo, se usa una autoridad de certificación raíz de root.ca.example.com en la organización de ejemplo de CA raíz.

    openssl req -x509 -sha256 -nodes -days 365 -newkey rsa:2048 -subj \
        '/O=Root CA Example Inc./CN=root.ca.example.com' -keyout root-ca.key \
        -out root-ca.crt
    
  2. Crea una solicitud de firma de certificado:

     openssl req -out server.csr -newkey rsa:2048 -nodes -keyout server.key -subj \
         "/CN=altostrat.com/O=Example Org"
    
  3. Crea un certificado de entrega para el proxy de entrada.

    openssl x509 -req -days 365 -CA root-ca.crt -CAkey root-ca.key -set_serial 0 \
        -in server.csr -out server.crt
    

    Ya creaste los siguientes certificados y claves:

    • root-ca.crt: Certificado para la CA raíz
    • root-ca.key: Clave privada para la CA raíz
    • server.crt: Certificado de entrega para el proxy de entrada
    • server.key: Clave privada para el proxy de entrada
  4. Crea un secreto de Kubernetes que contenga la clave y el certificado de entrega.

    kubectl create secret tls example-server-creds --key=server.key --cert=server.crt \
        --namespace gke-system
    

    El secreto que se obtiene como resultado se llama example-server-creds.

Crea una implementación y un servicio

Si creaste una implementación y un servicio en la parte HTTP de esta guía, déjalos en su lugar. Si no lo hiciste, créalos ahora y sigue los pasos descritos para HTTP.

Crea un Ingress

Si creaste un Ingress en la parte HTTP antes, bórralo antes de continuar.

Borra el Ingress:

kubectl --kubeconfig CLUSTER_KUBECONFIG delete ingress my-ingress

Para controlar el tráfico del Service que creaste antes, crea un nuevo Ingress con una sección tls. Esto habilitará HTTPS entre los clientes y el proxy de entrada.

Este es el manifiesto de un Ingress:

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: my-ingress-2
spec:
  tls:
  - hosts:
    - altostrat.com
    secretName: example-server-creds
  rules:
  - host: altostrat.com
    http:
      paths:
      - path: /greet-the-world
        pathType: Exact
        backend:
          service:
            name: hello-service
            port:
              number: 60000
      - path: /greet-kubernetes
        pathType: Exact
        backend:
          service:
            name: hello-service
            port:
              number: 60001

Guarda el manifiesto en un archivo llamado my-ingress-2.yaml y crea el Ingress.

kubectl apply --kubeconfig CLUSTER_KUBECONFIG -f my-ingress-2.yaml

Confirma mediante una prueba.

  • Prueba la ruta /greet-the-world:

    curl -v --resolve altostrat.com:443:CLUSTER_INGRESS_VIP\
        https://altostrat.com/greet-the-world \
        --cacert root-ca.crt
    

    Resultado:

    Hello, world!
    Version: 2.0.0
    Hostname: hello-deployment-5ff7f68854-wqzp7
    
  • Prueba la ruta /greet-kubernetes:

    curl -v --resolve altostrat.com:443:CLUSTER_INGRESS_VIP \
        https://altostrat.com/greet-kubernetes --cacert root-ca.crt
    

    Resultado:

    Hello Kubernetes!
    

Crea un Service LoadBalancer

El tipo LoadBalancer es una extensión del tipo NodePort. Entonces, un servicio de tipo LoadBalancer tiene una dirección IP de clúster y uno o más valores nodePort. De forma predeterminada, Kubernetes asigna puertos de nodo a servicios LoadBalancer. Estas asignaciones pueden agotar con rapidez los puertos de nodo disponibles de los 2,768 asignados a tu clúster. Si quieres guardar los puertos de nodo, inhabilita la asignación de puertos del nodo del balanceador de cargas mediante la configuración del campo allocateLoadBalancerNodePorts en false en la especificación del servicio LoadBalancer. Esta configuración evita que Kubernetes asigne puertos de nodos a los servicios de LoadBalancer. Para obtener más información, consulta Inhabilita la asignación de NodePort del balanceador de cargas en la documentación de Kubernetes.

A continuación, se muestra un manifiesto para crear un Service que no use ningún puerto de nodo:

apiVersion: v1
kind: Service
metadata:
  name: service-does-not-use-nodeports
spec:
  selector:
    app: my-app
  type: LoadBalancer
  ports:
  - port: 8000
  # Set allocateLoadBalancerNodePorts to false
  allocateLoadBalancerNodePorts: false

Realice una limpieza

Borra el Ingress:

kubectl --kubeconfig CLUSTER_KUBECONFIG delete ingress INGRESS_NAME

Reemplaza INGRESS_NAME por el nombre del Ingress, como my-ingress o my-ingress-2.

Borra el Service:

kubectl --kubeconfig CLUSTER_KUBECONFIG delete service hello-service

Borra el Deployment:

kubectl --kubeconfig CLUSTER_KUBECONFIG delete deployment hello-deployment

Borra el Service LoadBalancer:

kubectl --kubeconfig CLUSTER_KUBECONFIG delete service service-does-not-use-nodeports