En Mi Local Funciona

Technical thoughts, stories and ideas

Traefik como Ingress Controller en Kubernetes

Publicado por Alejandro Font el

KubernetestraefikDevOps

A lo largo de este post voy a contar cómo crear diferentes accesos por nombre sobre nuestros despliegues en Kubernetes o cómo gestionar de una forma sencilla y rápida los accesos desde el exterior de nuestros despliegues sobre Kubernetes.

Para ello haremos uso de Traefik. Una solución sencilla y fácil de instalar y que no solo está orientada a Kubernetes, sino que podemos hacer uso de la misma desde una solución basada simplemente en Docker o incluso Spring.

alt

En un post anterior de este mismo blog, mi compañero José María ya explicó cómo hacer esto con nginx.

En el caso de K8s lo importante es que en realidad ya tenemos la base del concepto (Ingress), en la propia arquitectura de Kubernetes que nos permite exponer de forma desacoplada nuestros servicios ofreciendo URL´s accesibles externamente por nombre (HTPP/HTPPS) y balanceo de tráfico.

Para ello, lo que necesitamos es justo un Ingress Controller, de modo que podamos usar un proxy inverso. En la documentación oficial tenemos la lista de todos los que podemos usar.

Para la instalación podemos seguir los pasos de la propia guía oficial de traefik via Custom Resource Definition.

Con los siguientes pasos instalaremos la ultima versión disponible 2.0.2, de ahí que luego tengamos un dashboard tan chulo! Pero si quieres hacer la instalación en el tiempo que te tomas un café usa el siguiente atajo.

Primero, la creación de IngressRoute como recurso:

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: 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: 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: tlsoptions.traefik.containo.us

spec:  
  group: traefik.containo.us
  version: v1alpha1
  names:
    kind: TLSOption
    plural: tlsoptions
    singular: tlsoption
  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
    resources:
      - ingresses
    verbs:
      - get
      - list
      - watch
  - apiGroups:
      - extensions
    resources:
      - ingresses/status
    verbs:
      - update
  - apiGroups:
      - traefik.containo.us
    resources:
      - middlewares
    verbs:
      - get
      - list
      - watch
  - apiGroups:
      - traefik.containo.us
    resources:
      - ingressroutes
    verbs:
      - get
      - list
      - watch
  - apiGroups:
      - traefik.containo.us
    resources:
      - ingressroutetcps
    verbs:
      - get
      - list
      - watch
  - apiGroups:
      - traefik.containo.us
    resources:
      - tlsoptions
    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

Seguidamente los services. En este caso tenemos Traefik como servicio y una imagen whoami, que es la que usaremos como ejemplo para las pruebas. No es mas que un servidor Go para pruebas que nos devuelve información sobre la petición realizada.

Aquí definimos tres puertos:

  • 8080 para la consola web de Traefik (admin)
  • 8000 para las peticiones no seguras (web)
  • 4443 para las peticiones seguras (websecure)
apiVersion: v1  
kind: Service  
metadata:  
  name: traefik

spec:  
  ports:
    - protocol: TCP
      name: web
      port: 8000
    - protocol: TCP
      name: admin
      port: 8080
    - protocol: TCP
      name: websecure
      port: 4443
  selector:
    app: traefik

---
apiVersion: v1  
kind: Service  
metadata:  
  name: whoami

spec:  
  ports:
    - protocol: TCP
      name: web
      port: 80
  selector:
    app: whoami

Seguimos con los deployments:

apiVersion: v1  
kind: ServiceAccount  
metadata:  
  namespace: default
  name: traefik-ingress-controller

---
kind: Deployment  
apiVersion: apps/v1  
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.0
          args:
            - --api.insecure
            - --accesslog
            - --entrypoints.web.Address=:8000
            - --entrypoints.websecure.Address=:4443
            - --providers.kubernetescrd
            - --certificatesresolvers.default.acme.tlschallenge
            - --certificatesresolvers.default.acme.email=foo@you.com
            - --certificatesresolvers.default.acme.storage=acme.json
            # Please note that this is the staging Let's Encrypt server.
            # Once you get things working, you should remove that whole line altogether.
            - --certificatesresolvers.default.acme.caserver=https://acme-staging-v02.api.letsencrypt.org/directory
          ports:
            - name: web
              containerPort: 8000
            - name: websecure
              containerPort: 4443
            - name: admin
              containerPort: 8080

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

spec:  
  replicas: 2
  selector:
    matchLabels:
      app: whoami
  template:
    metadata:
      labels:
        app: whoami
    spec:
      containers:
        - name: whoami
          image: containous/whoami
          ports:
            - name: web
              containerPort: 80

Realizamos un redireccionamiento de puertos:

kubectl port-forward --address 0.0.0.0 service/traefik 8000:8000 8080:8080 443:4443 -n default

Y finalmente, realizamos todos los mapeos que queramos, IngressRoute, de nuestros pods.
En este caso:

apiVersion: traefik.containo.us/v1alpha1  
kind: IngressRoute  
metadata:  
  name: simpleingressroute
  namespace: default
spec:  
  entryPoints:
    - web
  routes:
  - match: Host(`ejemplo.com`) && PathPrefix(`/notls`)
    kind: Rule
    services:
    - name: whoami
      port: 80

---
apiVersion: traefik.containo.us/v1alpha1  
kind: IngressRoute  
metadata:  
  name: ingressroutetls
  namespace: default
spec:  
  entryPoints:
    - websecure
  routes:
  - match: Host(`ejemplo.com`) && PathPrefix(`/tls`)
    kind: Rule
    services:
    - name: whoami
      port: 80
  tls:
    certResolver: default


---

apiVersion: traefik.containo.us/v1alpha1  
kind: IngressRoute  
metadata:  
  name: ingressroutenodesample
  namespace: default
spec:  
  entryPoints:
    - web
  routes:
  - match: Host(`node.ejemplo.com`)
    kind: Rule
    services:
    - name: hello-node
      port: 8090

Aquí podemos ver la consola de traefik, con los tres servicios:

Si todo lo anterior te ha parecido demasiado complejo, podemos hacer la instalación vía Helm y como comentamos al principio para aquellos que simplemente hagan uso de Docker con añadir unos labels en el fichero compose ya podemos ir mapeando los contenedores.

Instalación Vía Helm

Aquí debemos tener en cuenta que la última versión disponible vía chart es la 1.7.14

helm install --name lolo --values values.yaml stable/traefik  
values.yaml:
dashboard:  
  enabled: true
  domain: traefik-ui.dashboard
rbac:  
  enabled: true
kubernetes:  
  namespaces:
   - default
   - kube-system    

¡¡Y con esto ya estaríamos!!.

Un ejemplo sería el siguiente:

kind: Deployment  
apiVersion: extensions/v1beta1  
metadata:  
  name: stilton
  labels:
    app: cheese
    cheese: stilton
spec:  
  replicas: 2
  selector:
    matchLabels:
      app: cheese
      task: stilton
  template:
    metadata:
      labels:
        app: cheese
        task: stilton
        version: v0.0.1
    spec:
      containers:
      - name: cheese
        image: errm/cheese:stilton
        ports:
        - containerPort: 80
apiVersion: v1  
kind: Service  
metadata:  
  name: stilton
spec:  
  ports:
  - name: http
    targetPort: 80
    port: 80
  selector:
    app: cheese
    task: stilton
apiVersion: extensions/v1beta1  
kind: Ingress  
metadata:  
  name: cheese
  annotations:
    kubernetes.io/ingress.class: traefik
spec:  
  rules:
  - host: stilton.ejemplo
    http:
      paths:
      - path: /
        backend:
          serviceName: stilton
          servicePort: http

alt

Nota: Para este ejemplo se ha hecho uso de la versión de Kubernetes 1.14 que viene incluida en Docker Desktop for Mac.

Conclusiones

Ya hemos visto que crear accesos por nombre sobre nuestros despliegues en Kubernetes es muy sencillo gracias al concepto de Ingress que nos proporciona el propio Kubernetes. En esta ocasión lo hemos realizando utilizando Traefik como Ingress Controller aunque, si consultáis la web del propio producto, veréis que con Traefik podemos hacer muchísimas mas cosas.

En el siguiente post hablaremos de como hacer despliegues tipo Canary con Traefik.

Si te ha gustado, ¡síguenos en Twitter para estar al día de nuevas entregas!

Alejandro Font
Autor

Alejandro Font

Oracle ACE y Líder Técnico de la Comunidad Oracle Fusion Middleware en atSistemas. Actualmente con foco en Arquitecturas Ágiles y Contenedores