Kubernetes: Nginx Ingress Controller setup

Buenas!! Siguiendo con los posts de Kubernetes, hoy hablaremos de como crear un Ingress Controller y para que sirve.
Un ingress controler, es un servicio que se encarga de gestionar las peticiones que llegan desde fuera y redirigirlas al contenedor adecuado. Para hacer esto, cuenta con varios elementos. Ingress, LoadBalancer Service (puede ser NodePort) y un POD de controller (en este caso utilizaremos Nginx Ingress Controller). Para los controllers, hay diferentes herramientas como; Traefik, Istio, HAproxy…
Una comparación entre los diferentes Ingress Controllers:
https://docs.google.com/spreadsheets/d/16bxRgpO1H_Bn-5xVZ1WrR_I-0A-GOI6egmhvqqLMOmg/edit#gid=1612037324

Como podemos ver en la imagen, el workflow es el siguiente: Se despliega un servicio que es de tipo LoadBalancer(1) en Kubernetes, que crea un Balanceador en el proveedor cloud con los puertos que se le hayan indicado, en este caso el 80. Luego se despliega el Nginx Ingress Controller(2) que será el que se encargue de recepcionar las requests y consultar al Ingress (3) donde están definidas las rutas y la lógica para enviárselas a los servicios(4) y estos a sus contenedores(5) correspondientes.

Let’s Go

Para ver esto un poco mas a fondo, vamos a basarnos en el ejemplo que usamos en el anterior post y vamos a añadirle la parte del Ingress.

LoadBalancer Service

Como hemos dicho, este servicio solo se puede utilizar con proveedores de cloud (AWS, GCP, Azure). No es un servicio obligatorio ya que se puede sustituir por NodePort.
92-ingress-service.yaml

---
kind: Service
apiVersion: v1
metadata:
  name: ingress-nginx
  namespace: production
spec:
  type: LoadBalancer
  selector:
    app.kubernetes.io/name: ingress-nginx
    app.kubernetes.io/part-of: ingress-nginx
  ports:
    - name: http
      port: 80
      targetPort: http
    - name: https
      port: 443
      targetPort: http

Nginx Controller RBAC

Para que el Nginx Controller pueda funcionar correctamente, hay que darle permisos tanto a nivel de namespace como a nivel de cluster. Para esto, están los Roles y los ClusterRoles. A estos se les da una serie de permisos y luego se vinculan a una ServiceAccount que se le añadirá al POD. No vamos a entrar en profundidad en esto por que lo explicaré mejor en el siguiente post.
Más info: https://kubernetes.github.io/ingress-nginx/deploy/rbac/
90-ingress-rbac.yaml

---
apiVersion: v1
kind: ServiceAccount
metadata:
  name: nginx-ingress-serviceaccount
  namespace: production
  labels:
    app.kubernetes.io/name: ingress-nginx
    app.kubernetes.io/part-of: ingress-nginx

---
apiVersion: rbac.authorization.k8s.io/v1beta1
kind: ClusterRole
metadata:
  name: nginx-ingress-clusterrole
  labels:
    app.kubernetes.io/name: ingress-nginx
    app.kubernetes.io/part-of: ingress-nginx
rules:
  - apiGroups:
      - ""
    resources:
      - configmaps
      - endpoints
      - nodes
      - pods
      - secrets
    verbs:
      - list
      - watch
  - apiGroups:
      - ""
    resources:
      - nodes
    verbs:
      - get
  - apiGroups:
      - ""
    resources:
      - services
    verbs:
      - get
      - list
      - watch
  - apiGroups:
      - "extensions"
    resources:
      - ingresses
    verbs:
      - get
      - list
      - watch
  - apiGroups:
      - ""
    resources:
      - events
    verbs:
      - create
      - patch
  - apiGroups:
      - "extensions"
    resources:
      - ingresses/status
    verbs:
      - update

---
apiVersion: rbac.authorization.k8s.io/v1beta1
kind: Role
metadata:
  name: nginx-ingress-role
  namespace: production
  labels:
    app.kubernetes.io/name: ingress-nginx
    app.kubernetes.io/part-of: ingress-nginx
rules:
  - apiGroups:
      - ""
    resources:
      - configmaps
      - pods
      - secrets
      - namespaces
    verbs:
      - get
  - apiGroups:
      - ""
    resources:
      - configmaps
    resourceNames:
      - "ingress-controller-leader-nginx"
    verbs:
      - get
      - update
  - apiGroups:
      - ""
    resources:
      - configmaps
    verbs:
      - create
  - apiGroups:
      - ""
    resources:
      - endpoints
    verbs:
      - get

---
apiVersion: rbac.authorization.k8s.io/v1beta1
kind: RoleBinding
metadata:
  name: nginx-ingress-role-nisa-binding
  namespace: production
  labels:
    app.kubernetes.io/name: ingress-nginx
    app.kubernetes.io/part-of: ingress-nginx
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: Role
  name: nginx-ingress-role
subjects:
  - kind: ServiceAccount
    name: nginx-ingress-serviceaccount
    namespace: production

---
apiVersion: rbac.authorization.k8s.io/v1beta1
kind: ClusterRoleBinding
metadata:
  name: nginx-ingress-clusterrole-production-binding
  labels:
    app.kubernetes.io/name: ingress-nginx
    app.kubernetes.io/part-of: ingress-nginx
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: nginx-ingress-clusterrole
subjects:
  - kind: ServiceAccount
    name: nginx-ingress-serviceaccount
    namespace: production

Nginx Ingress Controller

Este será el que se encargue de gestionar las redirecciones del tráfico. Lo que hace es leer del Ingress y actuar como proxy aplicando la lógica definida.
En este caso, se va a desplegar como Deployment, pero también se podría desplegar como un DaemonSet para que corra uno en cada nodo.
93-ingress-deployment.yaml

---
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
  name: nginx-ingress-controller
  namespace: production
spec:
  replicas: 1
  selector:
    matchLabels:
      app.kubernetes.io/name: ingress-nginx
      app.kubernetes.io/part-of: ingress-nginx
  template:
    metadata:
      labels:
        app.kubernetes.io/name: ingress-nginx
        app.kubernetes.io/part-of: ingress-nginx
    spec:
      serviceAccountName: nginx-ingress-serviceaccount
      containers:
        - name: nginx-ingress-controller
          image: quay.io/kubernetes-ingress-controller/nginx-ingress-controller:0.24.1
          args:
            - /nginx-ingress-controller
            - --configmap=$(POD_NAMESPACE)/nginx-configuration
            - --tcp-services-configmap=$(POD_NAMESPACE)/tcp-services
            - --udp-services-configmap=$(POD_NAMESPACE)/udp-services
            - --publish-service=$(POD_NAMESPACE)/ingress-nginx
            - --annotations-prefix=nginx.ingress.kubernetes.io
            - --force-namespace-isolation
            - --watch-namespace=$(POD_NAMESPACE)
          securityContext:
            capabilities:
              drop:
                - ALL
              add:
                - NET_BIND_SERVICE
            runAsUser: 33
          env:
            - name: POD_NAME
              valueFrom:
                fieldRef:
                  fieldPath: metadata.name
            - name: POD_NAMESPACE
              valueFrom:
                fieldRef:
                  fieldPath: metadata.namespace
          ports:
            - name: http
              containerPort: 80
            - name: https
              containerPort: 443
          livenessProbe:
            failureThreshold: 3
            httpGet:
              path: /healthz
              port: 10254
              scheme: HTTP
            initialDelaySeconds: 10
            periodSeconds: 10
            successThreshold: 1
            timeoutSeconds: 1
          readinessProbe:
            failureThreshold: 3
            httpGet:
              path: /healthz
              port: 10254
              scheme: HTTP
            periodSeconds: 10
            successThreshold: 1
            timeoutSeconds: 1

Ingress

Por último, queda desplegar el Ingress. Aquí se definen las reglas que se quiere aplicar tanto por dominio como por path. Por defecto, el Ingress se acopla a todos los Controllers, se puede gestionar para que no lo haga a todos mediante anotaciones.
https://kubernetes.github.io/ingress-nginx/user-guide/multiple-ingress/
91-ingress.yaml

---
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  name: ingress
  namespace: production
spec:
 rules:
 - host: test.info
   http:
     paths:
     - path: /
       backend:
         serviceName: nginx
         servicePort: 80

Nota: Si se quiere utilizar en Minikube, hay que realizar varios cambios.
https://medium.com/@Oskarr3/setting-up-ingress-on-minikube-6ae825e98f82

Revisión

Para terminar, vamos a comprobar que todo esté funcionando correctamente

kubectl -n production get ingresses
NAME      HOSTS       ADDRESS         PORTS   AGE
ingress   test.info   130.211.49.70   80      91m
kubectl -n production get svc 
NAME            TYPE           CLUSTER-IP      EXTERNAL-IP     PORT(S)                      AGE
ingress-nginx   LoadBalancer   10.19.254.75    130.211.49.70   80:31426/TCP,443:30622/TCP   79m
mysql           ClusterIP      10.19.245.211   <none>          3306/TCP                     109m
nginx           ClusterIP      10.19.254.100   <none>          80/TCP                       109m
phpfpm          ClusterIP      10.19.253.52    <none>          9000/TCP                     109m
kubectl -n production get pods
NAME                                        READY   STATUS    RESTARTS   AGE
mysql-0                                     1/1     Running   0          18m
nginx-55bfcc5f59-tjnsq                      1/1     Running   0          19m
nginx-ingress-controller-6d676dc446-645rt   1/1     Running   2          14m
phpfpm-fd548b6d5-jxwtb                      1/1     Running   0          19m

Y con esto ya tendríamos un Ingress controller despegado y funcionando. Solo quedaría crear un registro DNS apuntando a la IP pública del balanceador.
En los próximos posts, haremos algo parecido pero con Istio, certificados con Let’s Encrypt y más seguridad.

Leave a Reply

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *