[K8s] All You Need to Know to Integrate Traefik with Cloud Ingress



Traefik is a powerful ingress controller with easy deployment and configuration. However, cloud providers like AWS and GCP also provide ingress implementations of their managed Kubernetes.

In this post, we will take GCP as example and walk through all needed knowledge of integrating Traefik deployment with ingress provided by GKE. This way, we could enjoy the benefits of feature-rich Traefik CRD as well as convenient infrastruture provisions provided by cloud ingress.

All source codes are available at https://github.com/minghsu0107/traefik-ingress-gke.

Traefik Introduction

Traefik is an open-source Edge Router that makes publishing your services a fun and easy experience. It receives requests on behalf of your system and finds out which components are responsible for handling them. There are a lot of useful middlewares available, such as regex matching, rate limiting, authentication, and many more. The best part is that Traefik automatically discover new configurations and apply them in real time. For those who are not familiar with Traefik, reading the Traefik documentation is a good start.

Traefik Configuration

In this section, we will cover common configuration of Traefik that is vendor-neutral and compatible with native Kubernetes.

Traefik CRD

Traefik is natively compliant with Kubernetes using the custom resource definitions. Custom resources are extensions of the Kubernetes API, representing a customization of a particular Kubernetes installation. Custom resources help us to declare Traefik components without resorting to lots of ingress annotations.

To start with, let’s create a namespace traefik:

---
apiVersion: v1
kind: Namespace
metadata:
  name: traefik

Then deploy Traefik CRDs.

---
apiVersion: apiextensions.k8s.io/v1beta1
kind: CustomResourceDefinition
metadata:
  name: ingressroutes.traefik.containo.us
spec:
  group: traefik.containo.us
  names:
    kind: IngressRoute
    plural: ingressroutes
    singular: ingressroute
  scope: Namespaced
  version: v1alpha1
---
apiVersion: apiextensions.k8s.io/v1beta1
kind: CustomResourceDefinition
metadata:
  name: ingressroutetcps.traefik.containo.us
spec:
  group: traefik.containo.us
  names:
    kind: IngressRouteTCP
    plural: ingressroutetcps
    singular: ingressroutetcp
  scope: Namespaced
  version: v1alpha1
---
apiVersion: apiextensions.k8s.io/v1beta1
kind: CustomResourceDefinition
metadata:
  name: ingressrouteudps.traefik.containo.us
spec:
  group: traefik.containo.us
  names:
    kind: IngressRouteUDP
    plural: ingressrouteudps
    singular: ingressrouteudp
  scope: Namespaced
  version: v1alpha1
---
apiVersion: apiextensions.k8s.io/v1beta1
kind: CustomResourceDefinition
metadata:
  name: middlewares.traefik.containo.us
spec:
  group: traefik.containo.us
  names:
    kind: Middleware
    plural: middlewares
    singular: middleware
  scope: Namespaced
  version: v1alpha1
---
apiVersion: apiextensions.k8s.io/v1beta1
kind: CustomResourceDefinition
metadata:
  name: tlsoptions.traefik.containo.us
spec:
  group: traefik.containo.us
  names:
    kind: TLSOption
    plural: tlsoptions
    singular: tlsoption
  scope: Namespaced
  version: v1alpha1
---
apiVersion: apiextensions.k8s.io/v1beta1
kind: CustomResourceDefinition
metadata:
  name: tlsstores.traefik.containo.us
spec:
  group: traefik.containo.us
  names:
    kind: TLSStore
    plural: tlsstores
    singular: tlsstore
  scope: Namespaced
  version: v1alpha1
---
apiVersion: apiextensions.k8s.io/v1beta1
kind: CustomResourceDefinition
metadata:
  name: traefikservices.traefik.containo.us
spec:
  group: traefik.containo.us
  names:
    kind: TraefikService
    plural: traefikservices
    singular: traefikservice
  scope: Namespaced
  version: v1alpha1

Next, create a service account traefik-ingress-controller and bind a cluster role to it so that Traefik could use the service account to access custom resources.

---
apiVersion: v1
kind: ServiceAccount
metadata:
  name: traefik-ingress-controller
  namespace: traefik
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
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
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
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: traefik

Next, deploy Traefik itself using Kubernetes Deployment type.

---
apiVersion: apps/v1
kind: Deployment
metadata:
  annotations:
    prometheus.io/port: http-metrics
    prometheus.io/scrape: "true"
  labels:
    app: traefik
  name: traefik
  namespace: traefik
spec:
  replicas: 1
  selector:
    matchLabels:
      app: traefik
  template:
    metadata:
      labels:
        app: traefik
    spec:
      serviceAccountName: traefik-ingress-controller
      containers:
      - name: traefik
        ports:
        - containerPort: 80
          name: web
        - containerPort: 8080
          name: admin
        - containerPort: 8082
          name: http-metrics
        args:
        - --log.level=INFO
        - --api
        - --api.dashboard
        - --api.insecure
        - --entrypoints.web.address=:80
        - --providers.kubernetescrd
        - --metrics.prometheus.entryPoint=metrics
        - --entryPoints.metrics.address=:8082
        - --accesslog=true
        - --ping
        - --ping.entryPoint=web
        image: traefik:v2.3
        readinessProbe:
          httpGet:
            path: /ping
            port: 80

We deploy Traefik in traefik namespace and expose prometheus endpoint at 0.0.0.0:8082/metrics. We also enable readiness probe on 0.0.0.0/ping. This is neccessary because GKE inteprets readiness probe as service health check parameter.

Finally, deploy a NodePort service to expose Traefik deployment.

---
apiVersion: v1
kind: Service
metadata:
  annotations:
    prometheus.io/port: "8082"
    prometheus.io/scrape: "true"
  labels:
    app: traefik
  name: traefik
  namespace: traefik
spec:
  type: NodePort
  ports:
  - name: web
    port: 80
    protocol: TCP
    targetPort: 80
  - name: http-metrics
    port: 8082
    protocol: TCP
    targetPort: 8082
  selector:
    app: traefik

Deploy to Google Kubernetes Engine (GKE)

Instead of directly enabling external load balancer on Traefik service, we handle external traffic by GKE ingress and proxy it to internal Traefik router by HTTP. This way, we could benefit from L7 load balancer features, such as managed certificates and HTTPS redirection, of our ingress instance without modifying any Traefik configuration. The routing now would be like Ingress -> Traefik -> App.

Note that your GKE cluster needs to be running at least a recent version 1.17-gke for this to work.

First, create a static reserved ip address going by the name of traefik-ip.

gcloud compute addresses create traefik-ip --global

Then, deploy an ingress annotated with managed certificate and HTTPS redirection.

---
apiVersion: networking.gke.io/v1
kind: ManagedCertificate
metadata:
  name: traefik-cert
spec:
  domains:
  - mydomain.com
  - api.mydomain.com
---
apiVersion: networking.gke.io/v1beta1
kind: FrontendConfig
metadata:
  name: traefik-frontend-cfg
  namespace: traefik
spec:
  redirectToHttps:
    enabled: true
    responseCodeName: PERMANENT_REDIRECT
---
kind: Ingress
apiVersion: networking.k8s.io/v1beta1
metadata:
  name: myingress
  namespace: traefik
  annotations:
    kubernetes.io/ingress.global-static-ip-name: "traefik-ip"
    networking.gke.io/v1beta1.FrontendConfig: "traefik-frontend-cfg"
    networking.gke.io/managed-certificates: "traefik-cert"
spec:
  backend:
    serviceName: traefik
    servicePort: 80

Above configuration creates a managed certficate on domains mydomain.com and api.mydomain.com. Google Cloud provisions managed certificates valid for 90 days. About one month before expiry, the process to renew your certificate automatically begins. Our ingress instance will use this certificate to handle HTTPS requests. Also, we have set up automatic HTTPS redirection using native GKE resource FrontendConfig.

Conclusion

In this post, we have covered details on deploying Traefik to GKE while having it integrated with GKE-native ingress. Such combination helps us decouple Traefik components from cloud ingress features like certificate management and HTTPS redirection, increasing the overall maintainability.

Read More

Comments