Cómo desplegar Portainer Business 2.0 detrás de Traefik, con soporte SSL en Kubernetes.

Cómo desplegar Portainer Business 2.0 detrás de Traefik, con soporte SSL en Kubernetes.

En este artículo te muestro como desplegar Portainer Business 2.0 en Kubernetes.

Hace unos meses Portainer lanzo la versión Community Edition 2.0 y como gran novedad incluía el soporte para Kubernetes, convirtiendose así en la única herramienta de management de contenedores que cubría Docker Standalone, Swarm y Kubernetes.

Hace unos días, lanzaron una versión paga, llamada Business, la misma esta enfocada a negocios y empresas que necesitan funcionalidades empresariales tales como gestión de la identidad (por OAuth, LDAP, Active Directory), Manejo de registries, manejo de cuotas para el caso de Kubernetes, pero por sobre todo, soporte técnico que viene en dos opciones, 24x7 o 9x5.

No quiero que este post sea un aviso publicitario, así que si quieren saber más los invito a  la siguiente web:

Explore Portainer Business Container Management Functionality | Portainer
Explore Portainer Business Container Management Functionality | Portainer

¿Qué necesitamos?

Para arrancar, necesitaremos una licencia de Portainer Business, asumiendo que ya la tienes, lo que necesitamos es descargar el YAML que correremos en nuestro nodo de Kubernetes, que dependiendo de nuestra preferencia puede exponer Portainer por NodePort o a través de un Load Balancer.

Si bien la opción de Load Balancer es tentadora y use como base ese archivo, no me da mucha gracia exponer el puerto 9000 y 8000 directamente, sino que prefiero que sea Traefik el que gestione todo.

$ wget https://raw.githubusercontent.com/xe-nvdk/easy-kubernetes-cookbook/main/portainer-business-v2.0/portainer-lb-ee.yml

Este archivo, ya esta modificado con todo lo que necesitamos, tanto desde el lado del volumen, el IngressRoute y la configuración SSL para que Traefik le de un certificado valido.

Empecemos

Para arrancar, vamos a crear el directorio donde persistiremos los datos de Portainer:

$ mkdir -p /mnt/data/portainer

Luego, editaremos el YML, dejándolo de esta manera, particularmente le hice unos cambios a la definición del namespace, volumen y agregue los IngressRoute para el Frontend y Edge, recuerden que vamos a desplegarlo detrás de Traefik quien no solo hará de Reverse Proxy sino que también nos manejará los certificados SSL de Let's Encrypt.

# Source: portainer/templates/serviceaccount.yaml
apiVersion: v1
kind: ServiceAccount
metadata:
  name: portainer-sa-clusteradmin
  namespace: default
  labels:
    app.kubernetes.io/name: portainer
    app.kubernetes.io/instance: portainer
    app.kubernetes.io/version: "2.0.0"
---
# Source: portainer/templates/rbac.yaml
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  name: portainer
  labels:
    app.kubernetes.io/name: portainer
    app.kubernetes.io/instance: portainer
    app.kubernetes.io/version: "2.0.0"
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: cluster-admin
subjects:
- kind: ServiceAccount
  namespace: default
  name: portainer-sa-clusteradmin
---
# Source: portainer/templates/service.yaml
apiVersion: v1
kind: Service
metadata:
  name: portainer
  namespace: default
  labels:
    io.portainer.kubernetes.application.stack: portainer
    app.kubernetes.io/name: portainer
    app.kubernetes.io/instance: portainer
    app.kubernetes.io/version: "2.0.0"
spec:
  ports:
    - port: 9000
      targetPort: 9000
      protocol: TCP
      name: http
    - port: 8000
      targetPort: 8000
      protocol: TCP
      name: edge
  selector:
    app.kubernetes.io/name: portainer
    app.kubernetes.io/instance: portainer
---
# Source: portainer/templates/deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: portainer
  namespace: default
  labels:
    io.portainer.kubernetes.application.stack: portainer
    app.kubernetes.io/name: portainer
    app.kubernetes.io/instance: portainer
    app.kubernetes.io/version: "2.0.0"
spec:
  replicas: 1
  strategy:
    type: "Recreate"
  selector:
    matchLabels:
      app.kubernetes.io/name: portainer
      app.kubernetes.io/instance: portainer
  template:
    metadata:
      labels:
        app.kubernetes.io/name: portainer
        app.kubernetes.io/instance: portainer
    spec:
      serviceAccountName: portainer-sa-clusteradmin
      volumes:
         - name: "portainer-data"
           hostPath:
             path: /mnt/data/portainer
             type: Directory    
      containers:
        - name: portainer
          image: "portainer/portainer-ee:2.0.0"
          imagePullPolicy: IfNotPresent          
          volumeMounts:
            - name: portainer-data
              mountPath: /data
          ports:
            - name: http
              containerPort: 9000
              protocol: TCP
            - name: tcp-edge
              containerPort: 8000
              protocol: TCP              
          livenessProbe:
            httpGet:
              path: /
              port: 9000
          readinessProbe:
            httpGet:
              path: /
              port: 9000
          resources:
            {}

---
apiVersion: traefik.containo.us/v1alpha1
kind: IngressRoute
metadata:
  name: portainer-front-tls
spec:
  entryPoints:  
    - websecure
  routes:
  - match: Host(`portainer.yourdomain.com`)
    kind: Rule
    services:
    - name: portainer
      port: 9000
  tls:
      certResolver: myresolver

---
apiVersion: traefik.containo.us/v1alpha1
kind: IngressRoute
metadata:
  name: portainer-edge-tls
spec:
  entryPoints:  
    - websecure
  routes:
  - match: Host(`edge.portainer.yourdomain.com`)
    kind: Rule
    services:
    - name: portainer
      port: 8000
  tls:
      certResolver: myresolver

Despleguemos...

Una vez que tenemos el YAML modificado a nuestro parecer, ejecutaremos:

$ kubectl apply -f portainer-lb-ee.yml

El resultado deberá parecerse a esto:

serviceaccount/portainer-sa-clusteradmin created
clusterrolebinding.rbac.authorization.k8s.io/portainer created
service/portainer created
deployment.apps/portainer created
ingressroute.traefik.containo.us/portainer-front-tls created
ingressroute.traefik.containo.us/portainer-edge-tls created

Esperamos un minuto a que descargue la imagen y Kubernetes haga toda su magia. Podemos comprobar si ya nuestro Pod está corriendo...

$ kubectl get pods

La terminal nos debería devolver algo como esto:

NAME                         READY   STATUS    RESTARTS   AGE
svclb-traefik-v5jlk          2/2     Running   0          15h
traefik-595897585d-267q6     1/1     Running   0          11m
portainer-5446667648-shqv5   1/1     Running   3          17m
Los restarts en Portainer, fueron pruebas mias :D

Todo parece estar bien, procedamos a terminar de configurar Portainer. Para eso, abrimos el navegador y vamos a la URL que especificamos en nuestro YML.

Configurando Portainer

La primer imágen es conocidos por todos, es donde definimos usuario y contraseña. Algo que me gusta hacer es cambiar el nombre de usuario del administrador, en vez de que diga se llame "admin", que tenga mi nombre u otro random.

Si no te molesta, deja tildada la opción de estadisticas, es solo para ver como usas Portainer, no recolecta información reconocible de usuarios.

Lo siguiente es especificar la licencia, la misma se compra en el sitio de Portainer y se licencia por cantidad de nodos, lo interesante es que el precio baja mientras más nodos tengamos.

Lo siguiente para hacer es seleccionar nuestro Endpoint, como en nuestro caso lo estamos corriendo en Kubernetes, esa será nuestra elección:

Hacemos un clic en "Connect" y veremos una pantalla de configuración, donde podremos definir si queremos que nuestros usuarios use el namespace "default" o no, si habilitaremos cuotas, si permitiremos que hagan overcommit, si vas a permitir a usuarios usen balanceadores externos, si quieres definir un controlador de Ingress y otras opciones como el uso de Storage:

Una vez que tenemos todo definido, hacemos un click en "Save Configuration" y veremos a Portainer listo para comenzar a trabajar:

Para ir cerrando

Como ves, no es para nada complicado y Portainer es una excelente opción si vos o tus usuarios no son muy amigos de archivos YML, pero por sobre todo, queres tener cierto control sobre lo que despliegan tus usuarios, en donde y como.

El archivo YML, lo dejo en este Github, en donde estoy armando un Cookbook para tener todo bien ordenado y listo para cuando yo u otras personas que lo encuentren lo necesiten:

xe-nvdk/easy-kubernetes-cookbook
This is my cookbook to run things on Kubernetes... Take a look, take wherever you need, and contribute it if you want it. - xe-nvdk/easy-kubernetes-cookbook

Si todo fue bien, comparte este artículo, si algo salió mal, deja un comentario aquí abajo.