En este artículo, te muestro, como de una manera bastante sencilla, podemos desplegar Traefik v2.3.5 en Kubernetes.

Desde hace unos días estoy armando un ambiente de Kubernetes, específicamente corriendo k3s y me dispuse a instalar el reverse proxy, por supuesto, todo este tipo de experiencias son material para este blog y porque creo, que alguien seguramente debe estar buscando como hacerlo.

K3s

Si estás montando tu cluster de Kubernetes en K3s, en el momento que lo vas a instalar, tenés que pedirle que se saltee la parte de Traefik, ya que lo vamos a hacer nosotros, de manera manual y con la última versión disponible.

Para Instalar K3s sin Traefik, debemos correr lo siguiente:

$ curl -sfL https://get.k3s.io | sh -s - --disable=traefik

Una vez que K3s está corriendo, que podemos verificarlo de la siguiente manera:

$ kubectl get nodes

El resultado se vería mas o menos así: En mi caso, tengo dos nodos corriendo, pero si solo usas uno, deberías ver el "master" solamente.

NAME               STATUS   ROLES    AGE   VERSION
kube-master-27aa   Ready    master   17h   v1.18.6+k3s1
kube-node-4f41     Ready    worker   17h   v1.18.6+k3s1

Declarando el recurso ingress y otras cosillas...

Vamos a empezar con la instalación, para eso, antes debemos declarar cual va a ser nuestro Ingress y algunas otras definiciones, usando CRD. Yo lo tengo especificado en un archivo llamado crd.yml y el mismo se ve de la siguiente manera:

apiVersion: apiextensions.k8s.io/v1beta1
kind: CustomResourceDefinition
metadata:
  name: ingressroutes.traefik.containo.us

spec:
  group: traefik.containo.us
  version: v1alpha1
  names:
    kind: IngressRoute
    plural: ingressroutes
    singular: ingressroute
  scope: Namespaced

---
apiVersion: apiextensions.k8s.io/v1beta1
kind: CustomResourceDefinition
metadata:
  name: middlewares.traefik.containo.us

spec:
  group: traefik.containo.us
  version: v1alpha1
  names:
    kind: Middleware
    plural: middlewares
    singular: middleware
  scope: Namespaced

---
apiVersion: apiextensions.k8s.io/v1beta1
kind: CustomResourceDefinition
metadata:
  name: ingressroutetcps.traefik.containo.us

spec:
  group: traefik.containo.us
  version: v1alpha1
  names:
    kind: IngressRouteTCP
    plural: ingressroutetcps
    singular: ingressroutetcp
  scope: Namespaced

---
apiVersion: apiextensions.k8s.io/v1beta1
kind: CustomResourceDefinition
metadata:
  name: ingressrouteudps.traefik.containo.us

spec:
  group: traefik.containo.us
  version: v1alpha1
  names:
    kind: IngressRouteUDP
    plural: ingressrouteudps
    singular: ingressrouteudp
  scope: Namespaced

---
apiVersion: apiextensions.k8s.io/v1beta1
kind: CustomResourceDefinition
metadata:
  name: tlsoptions.traefik.containo.us

spec:
  group: traefik.containo.us
  version: v1alpha1
  names:
    kind: TLSOption
    plural: tlsoptions
    singular: tlsoption
  scope: Namespaced

---
apiVersion: apiextensions.k8s.io/v1beta1
kind: CustomResourceDefinition
metadata:
  name: tlsstores.traefik.containo.us

spec:
  group: traefik.containo.us
  version: v1alpha1
  names:
    kind: TLSStore
    plural: tlsstores
    singular: tlsstore
  scope: Namespaced

---
apiVersion: apiextensions.k8s.io/v1beta1
kind: CustomResourceDefinition
metadata:
  name: traefikservices.traefik.containo.us

spec:
  group: traefik.containo.us
  version: v1alpha1
  names:
    kind: TraefikService
    plural: traefikservices
    singular: traefikservice
  scope: Namespaced

---
kind: ClusterRole
apiVersion: rbac.authorization.k8s.io/v1beta1
metadata:
  name: traefik-ingress-controller

rules:
  - apiGroups:
      - ""
    resources:
      - services
      - endpoints
      - secrets
    verbs:
      - get
      - list
      - watch
  - apiGroups:
      - extensions
      - networking.k8s.io
    resources:
      - ingresses
      - ingressclasses
    verbs:
      - get
      - list
      - watch
  - apiGroups:
      - extensions
    resources:
      - ingresses/status
    verbs:
      - update
  - apiGroups:
      - traefik.containo.us
    resources:
      - middlewares
      - ingressroutes
      - traefikservices
      - ingressroutetcps
      - ingressrouteudps
      - tlsoptions
      - tlsstores
    verbs:
      - get
      - list
      - watch

---
kind: ClusterRoleBinding
apiVersion: rbac.authorization.k8s.io/v1beta1
metadata:
  name: traefik-ingress-controller

roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: traefik-ingress-controller
subjects:
  - kind: ServiceAccount
    name: traefik-ingress-controller
    namespace: default

Para aplicar esto, tal cual esta, debemos correr:

$ kubectl apply -f crd.yml

El resultado, se verá mas o menos así:

customresourcedefinition.apiextensions.k8s.io/ingressroutes.traefik.containo.us created
customresourcedefinition.apiextensions.k8s.io/middlewares.traefik.containo.us created
customresourcedefinition.apiextensions.k8s.io/ingressroutetcps.traefik.containo.us created
customresourcedefinition.apiextensions.k8s.io/ingressrouteudps.traefik.containo.us created
customresourcedefinition.apiextensions.k8s.io/tlsoptions.traefik.containo.us created
customresourcedefinition.apiextensions.k8s.io/tlsstores.traefik.containo.us created
customresourcedefinition.apiextensions.k8s.io/traefikservices.traefik.containo.us created
clusterrole.rbac.authorization.k8s.io/traefik-ingress-controller created
clusterrolebinding.rbac.authorization.k8s.io/traefik-ingress-controller created

Desplegando Traefik

Ahora si se viene lo bueno, vamos a desplegar nuestro Traefik en un archivo llamado deployment.yml, el mismo tiene el siguiente contenido:

apiVersion: v1
kind: Service
metadata:
 name: traefik
spec:
 ports:
 - protocol: TCP
   name: web
   port: 80
 - protocol: TCP
   name: websecure
   port: 443
 type: LoadBalancer
 selector:
  app: traefik
---
apiVersion: v1
kind: ServiceAccount
metadata:
 namespace: default
 name: traefik-ingress-controller

---
apiVersion: apps/v1
kind: Deployment
metadata:
  namespace: default
  name: traefik
  labels:
    app: traefik

spec:
  replicas: 1
  selector:
    matchLabels:
      app: traefik
  template:
    metadata:
      labels:
        app: traefik
    spec:
      serviceAccountName: traefik-ingress-controller
      containers:
       - name: traefik
         image: traefik:v2.3.5
         args:
            - --api
            - --log.level=ERROR
            - --accesslog
            - --entrypoints.web.Address=:80
            - --entrypoints.websecure.Address=:443
            - --entrypoints.web.http.redirections.entryPoint.to=:443
            - --entrypoints.web.http.redirections.entryPoint.scheme=https
            - --providers.kubernetescrd
            - --providers.kubernetesingress
            - --certificatesresolvers.myresolver.acme.tlschallenge
            - --certificatesresolvers.myresolver.acme.email=tuemail@tudominio.com
            - --certificatesresolvers.myresolver.acme.storage=acme.json
         ports:
            - name: web
              containerPort: 80
            - name: websecure
              containerPort: 443

Acá básicamente lo que hacemos es definir el servicio, que se va a llamar Traefik y el mismísimo deployment, publicamos puertos, definimos la configuración de Let's Encrypt, la redirección, para que todo lo que sirvamos sea a través de SSL seteamos el numero de replicas y por favor, no se olviden de poner su correo electrónico en esta sección:

certificatesresolvers.myresolver.acme.email=tuemail@tudominio.com

Una vez que estamos listos, vamos a aplicar este deployment:

$ kubectl apply -f deployment.yml

El resultado debería ser algo como esto:

service/traefik created
serviceaccount/traefik-ingress-controller created
deployment.apps/traefik created

Comprobando...

Buenos, vamos a ver que todo este andando como esperamos. Para esto, podemos ejecutar un comando que nos va a permitir ver nuestros pods, en este caso deberiamos ver el de Traefik y uno (o varios, dependiendo de los nodos que tengas en tu cluster).

$ kubectl get pods

El resultado debería ser algo como esto:

NAME                       READY   STATUS    RESTARTS   AGE
svclb-traefik-5f6qf        2/2     Running   0          2m59s
traefik-7b7f4d6f68-mrwb4   1/1     Running   0          2m57s

Todo parece estar bien, hagamos una comprobación más, corramos un pod de hello world para asegurarnos de que Traefik esta funcionando bien.

Este es el que vamos a correr y lo guardaremos con el nombre hello.yml

kind: Deployment
apiVersion: apps/v1
metadata:
   name: hello-world
spec:
   replicas: 1
   selector:
      matchLabels:
         app: hello-world
   template:
      metadata:
         labels:
            app: hello-world
      spec:
         containers:
            - name: hello-world
              image: containous/whoami
---
apiVersion: v1
kind: Service
metadata:
   name: hello-world
   labels:
      app: hello-world
spec:
   ports:
      - port: 80
        name: hello-world
   selector:
      app: hello-world
---
apiVersion: traefik.containo.us/v1alpha1
kind: IngressRoute
metadata:
  name: hello-ingress
spec:
  entryPoints:  
    - websecure
  routes:
  - match: Host(`midominio.com`)
    kind: Rule
    services:
    - name: hello-world
      port: 80
  tls:
      certResolver: myresolver

Ejecutamos con...

$ kubectl apply -f hello.yml

El resultado debería ser algo así:

deployment.apps/hello-world created
service/hello-world created
ingressroute.traefik.containo.us/hello-ingress created

Ahora, si apuntamos nuestro navegador a "midominio.com", veremos que nos redirige a https y nos mostrará algo parecido a esto:

Para ir cerrando

Como ves, no es muy complicado, es bastante rápido y sencillo. Da miedo lo largo y la cantidad de definiciones de los archivos yml, pero es un poco la gracia de Kubernetes, que sea algo completamente modular y que esos "módulos" puedan escalar, cambiar o ser diferentes dependiendo de nuestras necesidades.

Les dejo la receta lista para usar, solo tienen que cambiar su email en la configuración de Traefik y el nombre de dominio en el hello.yml.

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

¿Qué te pareció este artículo? ¿Te sirvió? ¿Tienes alguna duda o comentario? Puedes dejarla aquí abajo.

Buena semana.