Cómo desplegar Traefik v2.3.5 en Kubernetes con soporte SSL
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.
¿Qué te pareció este artículo? ¿Te sirvió? ¿Tienes alguna duda o comentario? Puedes dejarla aquí abajo.
Buena semana.