OpenFaaS con K3S en un servidor ARM64 (1/2)
Tags: kubernetes, k3s, openfaas, spanish
Published: 2022-07-11 23:09:00

En esta ocasión escribo acerca de mi experiencia levantando un servicio propio de funciones utilizando https://www.openfaas.com/

Antes que nada primero veremos los requisitos para esta tarea.

Primero fue levantado en un servidor Ampere A1 con 2 nucleos y 12 de Memoria y 100 GB de almacenamiento, se encuentra alojado en [Oracle Cloud](https://cloud.oracle.com/), cuenta con una IP pública.

Después para los certificados de seguridad y asegurado del sitio mediante SSL se necesita un dominio  con el cual se pueda hacer pruebas y configurar el DNS. La forma más económica de obtener un dominio para mi fue comprarlo en [namecheap](http://namecheap.com/), desde el cual voy a crear un subdominio para acceder al servicio de funciones.

Con estos requisitos podemos iniciar, lo primero que hice fue instalar [K3S](https://k3s.io/), nos permite instalar una versión “Lightweight Kubernetes”, una versión ligera y rápida para desarrollar y poner en producción un cluster de Kubernetes.

Para instalar el cluster  usamos, en cual instalaremos el cluster de pruebas y su acceso a través del usuario actual que tenemos en el servidor, se podría implementar en un cluster con más nodos, en mi caso solo utilizaré uno.

```sh
$ curl -sfL https://get.k3s.io | sh -s - --write-kubeconfig-mode 644
$ kubectl config view --raw > ~/.kube/config
```

Podemos verificar el estado con 

```sh
$ kubectl get nodes -o wide
```
Donde obtendremos una salida similar a esta:


![Get Nodes](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/989ssornnnbj795t5ibn.png)

La idea es siempre hacerlo simple entender que está sucediendo ya que son un conjunto de cosas que funcionan entre sí, para lo cual la solución pensada es similar a algo así:


![Arquitecture](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/nhvyl5m6pnfxpzhtqa5u.png)

Asi que bueno:

> “Divide y vencerás”

Antes de iniciar con la instalación de los diferentes componentes utilizaremos dos gestores de paquetes para Kubernetes

Helm donde podemos instalarlo siguiendo la documentación [Helm Install](https://helm.sh/docs/intro/install/ )

```sh
$ curl -fsSL -o get_helm.sh https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3
$ chmod 700 get_helm.sh
$ ./get_helm.sh
```
Podemos verificar la instalación utilizando 

```sh
$ helm version
```
El segundo gestor de paquetes que utilizaremos es [Arkade](https://github.com/alexellis/arkade)  un gestor de paquetes que nos permite instalar charts con un solo comando de  manera rápida
Podemos instalarla y verificar utilizando los siguientes comandos

```sh
$ curl -sLS https://get.arkade.dev | sudo sh
$ arkade --help
```
Ahora que ya están instalados podemos continuar con los despliegues.

Primero el Ingress, encargarnos del Ingress para lo cual primero nos asignaremos un subdominio a nuestra ip pública,  algo así “openfaas.example.com” con la cual nos aseguraremos que todo funcione incluso la emisión de certificados SSL antes de instalar open faas, en caso de utilizar Namecheap para el dominio puede seguir el siguiente enlace que explica como crear un subdominio y apuntar a la IP que vayamos a utilizar, cuando se realiza el registro puede llevar un tiempo en reflejarse me ha pasado que a veces tarda unos 10 minutos en detectar el subdominio el DNS hasta un 1 dia entero asi a tener paciencia xD [Crear Subdominio en Namecheap](https://www.namecheap.com/support/knowledgebase/article.aspx/9776/2237/how-to-create-a-subdomain-for-my-domain/)

En el ejemplo utilizamos dos subdominios uno de prueba para una aplicación de demostración y otra que nos permita acceder a openfaas.

Como vamos a utilizar Nginx-ingress, necesitamos también asegurar nuestros sitios para lo cual utilizaremos cert-manager. Puede leer más al respecto del [cert-manager](https://cert-manager.io/docs/tutorials/acme/nginx-ingress/ )

## Nginx Ingress

Lo primero vamos a instalar utilizando helm nuestro Nginx Ingress usando sus repositorios

```sh
$ helm repo add ingress-nginx https://kubernetes.github.io/ingress-nginx
$ helm repo update
$ helm install nginxingress ingress-nginx/ingress-nginx
```
Podemos comprobar el estado del servicio usando

```sh
$ kubectl get svc
```
Donde tendremos una salida similar a la siguiente image


![get svc](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/2j3wa5n99arn4rmwunwh.png)

Donde en principio se encontrara <Pending> pendiente hasta que se asocie a la IP pública, en caso de suceder puede realizarlo manualmente editando el servicio usando Kubernetes.

```sh
$ kubectl edit svc nginxingress-ingress-nginx-controller
```
Donde usaremos VIM para editar nuestro servicio añadiendo al nivel del type: LoadBalancer nuestra IP externa

```sh
…
  type: LoadBalancer
  externalIPs:
  - XXX.XXX.XXX.XXX
…
```
Para probar nuestro Ingress y Cert Manager utilizaremos un deploy de demostración llamado [KUARD](https://github.com/kubernetes-up-and-running/kuard). Para lo cual crearemos los dos archivos del deploy y del ingress
```sh
# kuard-ingress.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: kuard
spec:
  selector:
    matchLabels:
      app: kuard
  replicas: 1
  template:
    metadata:
      labels:
        app: kuard
    spec:
      containers:
      - image: gcr.io/kuar-demo/kuard-arm64:1
        imagePullPolicy: Always
        name: kuard
        ports:
        - containerPort: 8080
```

Levantamos el deployment:
```sh
kubectl create -f kuard-deploy.yaml
```
Luego configuramos nuestro ingress para Kuard:

```sh
#kuard-ingress.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: kuard
  annotations:
    kubernetes.io/ingress.class: "nginx"
    # cert-manager.io/issuer: "letsencrypt-prod"

spec:
  tls:
  - hosts:
    - kuard.example.com # Dominio de prueba
    secretName: quickstart-example-tls # Nombre de nuestro Certificado
  rules:
  - host: kuard.example.com # Dominio de prueba
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: kuard
            port:
              number: 80
```
Levantamos el ingress:
```sh
kubectl create -f kuard-ingress.yaml
```
Podemos verificar el estado de nuestro deploy e ingres utilizando
```sh
$ kubectl get ingress,deploy -o wide
# probar el acceso a nuestro dominio de prueba usando su IP
$ curl -kivL -H 'Host: kuard.example.com' 'http://10.0.0.210'
```
Una vez levantado el deploy y el Ingress tenemos que configurar nuestro cert manager que se encargará de gestionar los certificados para nuestro sitio, para lo cual nos ayudará [helm]( https://cert-manager.io/docs/installation/helm/#installing-with-helm)
```sh
$ helm repo add jetstack https://charts.jetstack.io
$ helm update
$  kubectl apply -f https://github.com/cert-manager/cert-manager/releases/download/v1.8.2/cert-manager.crds.yaml 
$ helm install   cert-manager jetstack/cert-manager   --namespace cert-manager   --create-namespace   --version v1.8.2
```

Una vez instalado procedemos a crear nuestros issuers, que se encargaran de generar los certificados en nuestro caso usaremos letsencrypt creando los dos siguientes issuers:
```sh
# lets-staging.yaml
   apiVersion: cert-manager.io/v1
   kind: Issuer
   metadata:
     name: letsencrypt-staging
   spec:
     acme:
       # The ACME server URL
       server: https://acme-staging-v02.api.letsencrypt.org/directory
       # Email address used for ACME registration
       email: me@example.com
       # Name of a secret used to store the ACME account private key
       privateKeySecretRef:
         name: letsencrypt-staging
       # Enable the HTTP-01 challenge provider
       solvers:
       - http01:
           ingress:
             class:  nginx

```
Y el archivo para generar certificados en prod
```sh
# lets-prod.yaml
   apiVersion: cert-manager.io/v1
   kind: Issuer
   metadata:
     name: letsencrypt-prod
   spec:
     acme:
       # The ACME server URL
       server: https://acme-v02.api.letsencrypt.org/directory
       # Email address used for ACME registration
       email: me@example.com
       # Name of a secret used to store the ACME account private key
       privateKeySecretRef:
         name: letsencrypt-prod
       # Enable the HTTP-01 challenge provider
       solvers:
       - http01:
           ingress:
             class: nginx
```
Creamos ambos issuers.
```sh
$ kubectl create -f lets-staging.yaml
$ kubectl create -f lets-prod.yaml
$ kubectl get Issuers
$ kubectl describe issuer letsencrypt-staging
```
Hasta este punto ya tenemos el cert-manager con nuestros Issuers, ahora toca configurar nuestro Ingress que estamos probando, editamos nuestro kuard-ingress.yaml
```sh
#kuard-ingress.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: kuard
  annotations:
    kubernetes.io/ingress.class: "nginx"
    cert-manager.io/issuer: "letsencrypt-staging"

spec:
  tls:
  - hosts:
    - kuard.example.com # Dominio de prueba
    secretName: quickstart-example-tls # Nombre de nuestro Certificado
  rules:
  - host: kuard.example.com # Dominio de prueba
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: kuard
            port:
              number: 80
```
Y  volvemos a levantar nuestro Ingress.
```sh
$ kubectl apply -f kuard-ingress.yaml
```
O sino eliminamos el Ingress y levantamos nuevamente.
```sh
$ kubectl delete -f kuard-ingress.yaml
$ kubectl create -f kuard-ingress.yaml
```
En este punto se creará el punto de ingreso y el certificado.
```sh
$ kubectl get certificate
NAME                     READY   SECRET                   AGE
quickstart-example-tls   True    quickstart-example-tls   16m
```
Podemos ver más detalles del mismo
```sh
$ kubectl describe certificate quickstart-example-tls
$ kubectl describe secret quickstart-example-tls
```
Una vez comprobado que existen los certificados podemos hacer la actualización a producción de nuestro Ingress.

```sh
#kuard-ingress.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: kuard
  annotations:
    kubernetes.io/ingress.class: "nginx"
    cert-manager.io/issuer: "letsencrypt-prod"

spec:
  tls:
  - hosts:
    - kuard.example.com # Dominio de prueba
    secretName: quickstart-example-tls # Nombre de nuestro Certificado
  rules:
  - host: kuard.example.com # Dominio de prueba
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: kuard
            port:
              number: 80
```
Y  volvemos a levantar nuestro Ingress.
```sh
$ kubectl apply -f kuard-ingress.yaml
```
O sino eliminamos el Ingress y levantamos nuevamente.
```sh
$ kubectl delete -f kuard-ingress.yaml
$ kubectl create -f kuard-ingress.yaml
```
También es necesario eliminar el certificado en estado de staging para que se pueda generar el certificado en producción.
```sh
$  kubectl delete secret quickstart-example-tls
```
Una vez eliminado esperamos un poco hasta que se vuelva a generar un certificado esta vez apuntando al issuer de producción.
En este punto accediendo desde el navegador puede comprobar la validez del certificado accediendo a su dominio kuard.example.com. 

En este punto terminamos la parte inicial de nuestro esquema, ya tenemos nuestro Ingress y Nuestro Cert Manager Funcionando correctamente ahora toca instalar OpenFaas para el servicio de Funciones.

> Kuard muestra un poco de información sensible así que se recomienda eliminarlo cuando no se lo esté utilizando, puede eliminar el secret, el ingress y el deploy y eliminar el subdominio de prueba “kuard.example.com” para continuar con el servicio de OpenFaas
```sh
$  kubectl delete -f kuard-ingress.yaml
$  kubectl delete secret quickstart-example-tls
$  kubectl delete -f kuard-deploy.yaml
```

En caso de querer revisar los logs de nuestro ingress nginx podemos utilizar un comando similar a este:
```
kubectl logs -f $(kubectl get po -l "app.kubernetes.io/instance=nginxingress,app.kubernetes.io/component=controller" -o jsonpath="{.items[0].metadata.name}")
```

## OpenFaas
Es un servicio de Funciones Self Hosted, que nos permite levantar e implementar funciones basados en eventos sobre Kubernetes, generando un endpoint, tal como Azure Functions o Amazon Lambda https://docs.openfaas.com/. 

Puede leer más información sobre el proceso de [instalación](https://docs.openfaas.com/reference/ssl/kubernetes-with-cert-manager/)

Para instalar OpenFaas sobre nuestro Kubernetes utilizaremos arkade:
```sh
$ arkade install openfaas
```
Utilizando helm podemos ver todos nuestros charts instalados incluido openfaas
```sh
$ helm list --all-namespaces
```
![Helm List](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/n8mblpgzx5e2nizhsjmv.png)

Una vez instalado openfaas vamos a crear los issuers para su namespace openfaas en el cual estaran almacenados
```sh
# letsencrypt-issuer.yaml
apiVersion: cert-manager.io/v1
kind: Issuer
metadata:
  name: letsencrypt-staging
  namespace: openfaas
spec:
  acme:
    email: me@example.com
    server: https://acme-staging-v02.api.letsencrypt.org/directory
    privateKeySecretRef:
      name: staging-issuer-account-key
    solvers:
    - http01:
        ingress:
          class: nginx
---
apiVersion: cert-manager.io/v1
kind: Issuer
metadata:
  name: letsencrypt-prod
  namespace: openfaas
spec:
  acme:
    email: me@example.com
    server: https://acme-v02.api.letsencrypt.org/directory
    privateKeySecretRef:
      name: prod-issuer-account-key
    solvers:
    - http01:
        ingress:
          class: nginx
```

Una vez creado nuestro archivo de Issuers levantamos el mismo
```sh
$ kubectl create -f letsencrypt-issuer.yaml
```

Ahora procedemos a configurar nuestro openfaas creando un archivo tls.yaml y actualizando con helm nuestro chart de faas
```sh
# tls.yaml
ingress:
  enabled: true
  annotations:
    kubernetes.io/ingress.class: "nginx"
    cert-manager.io/issuer: letsencrypt-staging # utilizaremos staging de pruebas
  tls:
    - hosts:
        - openfaas.example.com
      secretName: openfaas-crt
  hosts:
    - host: openfaas.example.com
      serviceName: gateway
      servicePort: 8080
      path: /
```
Actualizamos nuestro chart
```sh
$ helm upgrade openfaas --namespace openfaas --reuse-values --values tls.yaml openfaas/openfaas
```
Esperamos un poco a que se apliquen los cambios y genere el certificado en staging y podemos comprobarlo con
```sh
$ kubectl describe certificate   -n openfaas   openfaas-crt
```
Al poder comprobar que se encuentre creado podemos cambiar ahora a prod nuestro staging
```sh
# tls.yaml
ingress:
  enabled: true
  annotations:
    kubernetes.io/ingress.class: "nginx"
    cert-manager.io/issuer: letsencrypt-prod # Cambiamos a prod
  tls:
    - hosts:
        - openfaas.example.com
      secretName: openfaas-crt
  hosts:
    - host: openfaas.example.com
      serviceName: gateway
      servicePort: 8080
      path: /
```
Volvemos a actualizar nuestro chart con helm de openfaas
```sh
$ helm upgrade openfaas --namespace openfaas --reuse-values --values tls.yaml openfaas/openfaas
```
Si el certificado no se regenera con el Issuer de producción puede ser necesario eliminarlo.
```sh
$ kubectl -n openfaas delete secret  openfaas-crt
```
Ahora hay q descargar las credenciales auth del admin del open faas para poder acceder desde el cliente o la web
```
$ PASSWORD=$(kubectl get secret -n openfaas basic-auth -o jsonpath="{.data.basic-auth-password}" | base64 --decode; echo)
$ echo $PASSWORD
```
Este password guardalo con mucha seguridad ya que es la clave de acceso a su openfaas.


Una vez que se genera el certificado, ya se encuentra listo para acceder a su openfaas para lo cual puede realizarlo desde el navegador accediendo a su dominio openfaas.example.com


![OpenFaas](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/f9d8cesk8waizig4m5x0.png)

Como se puede observar se encuentra ya con https y está listo para poder empezar a deployar sus funciones, tanto desde la web como desde su cliente.

Para la instalación del [cliente de openfaas](https://docs.openfaas.com/cli/install/)

## Recursos utilizados:
https://docs.oracle.com/iaas/Content/FreeTier/freetier_topic-Always_Free_Resources.htm
https://rancher.com/docs/k3s/latest/en/ 
https://helm.sh/docs/intro/install/ 
https://github.com/alexellis/arkade 
https://cert-manager.io/docs/tutorials/acme/nginx-ingress/
https://docs.openfaas.com/deployment/kubernetes/ 
https://docs.openfaas.com/cli/install/ 

Aqui esta el siguiente POST de una función en Python con OpenFaas 
["Crear una función en Python para desplegar en OpenFaas (2/2)"](
https://dev.to/jevillanueva/crear-una-funcion-en-python-para-desplegar-en-openfaas-22-2k9e)

Ahí les dejo mi página personal [jevillanueva.dev](https://jevillanueva.dev)