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.
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
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!