Kubernetes,  QNAP

Kubernetes. k0s – Añadir un Ingress con certificados LetsEncrypt

El siguiente paso lógico para mejorar la aplicación, y una vez que tenemos el LoadBalancer funcionando, sería crear un recurso INGRESS. Con Ingress se consigue publicar la aplicación web al exterior proporcionando balanceo de carga y terminación SSL. Esta última característica es muy importante en motionEye ya que la aplicación no permite por diseño el protocolo seguro. También voy a describir como gestionar con kubernetes el mantenimiento de los certificados de LetsEncrypt con CERT-MANAGER.

Instalación de INGRESS

Existen diferentes implementaciones de Ingress, yo voy a utilizar nginx que además es mantenida por Kubernetes. Un esquema básico del funcionamiento de Ingress se detalla en la siguiente imagen.

Imagen obtenida de la documentación de k0s.

La primera acción es desplegar el Ingress Controller. Además comprobamos que se han desplegado los pods.

$ kubectl apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/controller-v1.1.3/deploy/static/provider/baremetal/deploy.yaml

$ kubectl get pods -n ingress-nginx
NAME                                        READY   STATUS      RESTARTS       AGE
ingress-nginx-admission-create-rdpjk        0/1     Completed   0              15d
ingress-nginx-admission-patch-z29m7         0/1     Completed   2              15d
ingress-nginx-controller-7fc8d55869-zwmvj   1/1     Running     4 (2d2h ago)   15d

$ kubectl -n ingress-nginx get ingressclasses
NAME    CONTROLLER             PARAMETERS   AGE
nginx   k8s.io/ingress-nginx   <none>       15d

$

Es importante indicarle al Ingress Controller que utilice un LoadBalancer en lugar del NodePort. Para conseguirlo es necesario editar el service en el apartado spec.type cambiando “NodePort” por “LoadBalancer”. Una vez cambiado comprobar que el TYPE aparece como LoadBalancer.

$ kubectl edit service ingress-nginx-controller -n ingress-nginx

$ kubectl get services -n ingress-nginx
NAME                                 TYPE           CLUSTER-IP      EXTERNAL-IP     PORT(S)                      AGE
ingress-nginx-controller             LoadBalancer   10.106.95.250   192.168.2.211   80:31780/TCP,443:30997/TCP   15d
ingress-nginx-controller-admission   ClusterIP      10.98.178.209   <none>          443/TCP                      15d
$ 

Instalación de CERT-MANAGER

Con la instalación de Ingress se obtiene un certificado valido a nivel de cifrado. Este certificado, obviamente no es válido para cualquier navegador por lo que es necesario dotar al cluster de una gestión de certificados. Esta utilidad la proporciona cert-manager y la instalación está descrita en Installation.

kubectl apply -f https://github.com/cert-manager/cert-manager/releases/download/v1.9.1/cert-manager.yaml

Una vez instalado es necesario configurar un “cluster issuer”. En mi caso elijo letsencrypt. A continuación detallo el yaml que he creado. El server indicado es el de staging que proporciona letsencrypt para las pruebas. Si utilizas el de producción puedes provocar bloqueos de la cuenta. El de PRO es –> https://acme-v02.api.letsencrypt.org/directory.

$ kubectl apply -f letsencrypt-staging.yaml 
clusterissuer.cert-manager.io/letsencrypt-staging configured

$ cat letsencrypt-staging.yaml 
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
  name: letsencrypt-staging
spec:
  acme:
    email: tucorreo@correo.com
    server: https://acme-staging-v02.api.letsencrypt.org/directory
    privateKeySecretRef:
      # Secret resource that will be used to store the account's private key.
      name: letsencrypt-issuer-account-key
    # Add a single challenge solver, HTTP01 using nginx
    solvers:
    - http01:
        ingress:
          class: nginx

$

Aplicación motionEye definitiva (Al menos por el momento)

El detalle anterior describe la creación de Deploys necesarios en el Cluster y ahora os dejo los yaml necesarios para la aplicación.

$ cat motioneye-deployment-con-ingress-acme-production.yaml 

apiVersion: v1
kind: Namespace
metadata:
  name: motioneye

---
apiVersion: v1
kind: Service
metadata:
  name: motioneye
  namespace: motioneye
  labels:
    app: motioneye
spec:
  ports:
    - port: 8765
      targetPort: 8765
  selector:
    app: motioneye

---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  annotations:
    # Añado el cluster-issuer definido para letsencrypt
    cert-manager.io/cluster-issuer: letsencrypt-production
  name: motioneye
  namespace: motioneye
spec:
  rules:
    # Aquí tienes que poner el FQDN de tu aplicación
    - host: motion.tudominio.com
      http:
        paths:
          - path: /
            pathType: Prefix
            backend:
              service:
                name: motioneye
                port:
                  number: 8765
  tls:
    - hosts:
      - motion.tudominio.com
      # En este certificate cert-manager guarda el certificado.
      # kubectl get certificate -n motioneye
      secretName: camaras-cert

---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: motioneye
spec:
  replicas: 1
  selector:
    matchLabels:
      app: motioneye
  template:
    metadata:
      labels:
        app: motioneye
    spec:
      volumes:
      - name: nfs-motioneye-etc
        nfs:
          server: servidor-nfs
          path: /DPVs/motionEye/etc
          readOnly: no
      - name: nfs-motioneye-var-lib
        nfs:
          server: servidor-nfs
          path: /DPVs/motionEye/var-lib
          readOnly: no
      - name: localtime
        hostPath:
          path: "/etc/localtime" 
      containers:
      - name: motioneye-container
        image: ccrisan/motioneye:master-amd64
        ports:
        - containerPort: 8765
          protocol: TCP
          name: "server"
        volumeMounts:
        - mountPath: "/etc/motioneye"
          name: nfs-motioneye-etc
        - mountPath: "/var/lib/motioneye"
          name: nfs-motioneye-var-lib
        - mountPath: "/etc/localtime"
          name: localtime
        env:
        - name: hostname
          value: "CAMARAS"

Sólo te queda el consiguiente apply del fichero anterior y, ya estaría! (0‿~).