Traefik

a modern proxy for Microservices

 #breakfast-club

by Ole Rößner

$ whoami

  • Ole Rößner
  • married, father
  • neusta GmbH (Bremen)
  • Coding, Coaching and Consulting
  • Symfony Enthusiast
  • Clean Code Evangelist
  • Former DJ

@djbasster

@oroessner@mastodon.social

Basster

basster

o.roessner@neusta.de

Edge Router?

Auto Service Discovery?

Simple Example with

Docker-Compose

# docker-compose.yaml

version: '3'

services:
  traefik:
    # The official Traefik docker image
    image: traefik:2.2
    # Enables the web UI and tells Traefik to listen to docker
    command: --api.insecure=true --providers.docker
    ports:
      # The HTTP port
      - "80:80"
      # The Web UI (enabled by --api.insecure=true)
      - "8080:8080"
    volumes:
      # So that Traefik can listen to the Docker events
      - /var/run/docker.sock:/var/run/docker.sock:ro
# docker-compose.yaml

# [...]
  whoami:
    # A container that exposes an API to show its IP address
    image: containous/whoami
    labels:
      # /etc/hosts -> 127.0.0.1 app.test
      - "traefik.http.routers.whoami.rule=Host(`app.test`)" 
      
# docker-compose.yaml

# [...]
  hello:
    # A container that always answers "Hello World!" on port 8000
    image: djbasster/hello-react-php
    labels:
      - "traefik.http.routers.hello.rule=Path(`/hello`)"
      

Routing Rules

  • Headers(`key`, `value`)
  • HeadersRegexp(`key`, `regexp`)
  • Host(`example.com`, ...)
  • HostRegexp(`example.com`, `{subdomain:[a-z]+}.example.com`, ...)
  • Method(`GET`, ...)
  • Path(`/path`, `/articles/{cat:[a-z]+}/{id:[0-9]+}`, ...)
  • PathPrefix(`/products/`, `/articles/{cat:[a-z]+}/{id:[0-9]+}`)
  • Query(`foo=bar`, `bar=baz`)

&&, || and () possible!

Image Source: Dribbble

Quiz Time

Priority & Service Injection

## Dynamic configuration
http:
  routers:
    Router-1:
      rule: "HostRegexp(`.*\.traefik\.com`)"
      # ...
    Router-2:
      rule: "Host(`foobar.traefik.com`)"
      # ...
Name Rule Priority
Router-1
HostRegexp(`.*\.traefik\.com`)
30
Router-2
Host(`foobar.traefik.com`)
26

What's wrong here?

Priority & Service Injection

## Dynamic configuration
http:
  routers:
    Router-1:
      rule: "HostRegexp(`.*\.traefik\.com`)"
      priority: 1
      # ...
    Router-2:
      rule: "Host(`foobar.traefik.com`)"
      priority: 2
      # ...
priority = 0

Use  the default rules length sorting!

⚠️

Service Injection?

Service Injection?

http:
  routers:
    monolyth:
      rule: "Host(`www.my-old-fat-website.com`)"
      service: website
      # priority: 35 (implicit)
      # ...
    microservice:
      rule: "Host(`www.my-old-fat-website.com`) && Path(`/votes`)"
      service: voting-service
      # priority: 53 (implicit)
      # ...

Maintenance Injection

version: '3'

services:
  website:
    image: my-website
    labels:
      - traefik.http.routers.website.rule=Host(`example.com`)
  foo-service:
    image: foo-service
    labels:
      - traefik.http.routers.foo.rule=Host(`example.com`) && Path(`/foo`)
  maintenance:
    image: my-static-maintenance-site
    labels:
      - traefik.http.routers.maintenance.rule=Host(`example.com`)
      - traefik.http.routers.maintenance.priority=1
      - traefik.http.middlewares.maintenance-stripper.stripprefixregex.regex=/.*
      - traefik.http.routers.maintenance.middlewares=maintenance-stripper@docker
version: '3'

services:
  website:
    image: my-website
    labels:
      - traefik.http.routers.website.rule=Host(`example.com`)
  foo-service:
    image: foo-service
    labels:
      - traefik.http.routers.foo.rule=Host(`example.com`) && Path(`/foo`)
  maintenance:
    image: my-static-maintenance-site
    labels:
      - traefik.http.routers.maintenance.rule=Host(`example.com`)
      - traefik.http.routers.maintenance.priority=9999
      - traefik.http.middlewares.maintenance-stripper.stripprefixregex.regex=/.*
      - traefik.http.routers.maintenance.middlewares=maintenance-stripper@docker
docker-compose up -d maintenance

Services

File Provider VS Docker-Compose

## Dynamic configuration: File Provider
http:
  services:
    my-service:
      loadBalancer:
        servers:
        - url: "http://<private-ip-server-1>:<private-port-server-1>/"
        - url: "http://<private-ip-server-2>:<private-port-server-2>/"
## docker-compose.yaml
version: "3"
services:
  my-service:
    image: my-service-image
    deploy:
      replicas: 6
    labels:
      - "traefik.http.routers.my-service.rule=Host(`example.com`)"
    

Example: Sticky Sessions

## docker-compose.yaml
version: "3"
services:
  my-service:
    image: my-service-image
    deploy:
      replicas: 6
    labels:
      - "traefik.http.routers.my-service.rule=Host(`example.com`)"
      - "traefik.http.routers.my-service.service=my-service-svc"
      - "traefik.http.services.my-service-svc.loadbalancer.sticky.cookie=true"
    

Traefik Docs: Sticky Sessions

Example: Explicit Port

## docker-compose.yaml
version: "3"
services:
  my-service:
    image: my-service-image # exposes 8080 and 9000
    labels:
      - "traefik.http.routers.my-service.rule=Host(`example.com`)"
      - "traefik.http.routers.my-service.service=my-service-svc"
      - "traefik.http.services.my-service-svc.loadbalancer.server.port=8080"
    

What else is a Service for?

Middlewares

Middleware Purpose Area
AddPrefix Add a Path Prefix Path Modifier
BasicAuth Basic auth mechanism Security, Authentication
Buffering Buffers the request/response Request Lifecycle
Chain Combine multiple pieces of middleware Middleware tool
CircuitBreaker Stop calling unhealthy services Request Lifecycle
Compress Compress the response Content Modifier
DigestAuth Adds Digest Authentication Security, Authentication
Errors Define custom error pages Request Lifecycle
ForwardAuth Authentication delegation Security, Authentication
Headers Add / Update headers Security
IPWhiteList Limit the allowed client IPs Security, Request lifecycle
InFlightReq Limit the number of simultaneous connections Security, Request lifecycle
PassTLSClientCert Adding Client Certificates in a Header Security
RateLimit Limit the call frequency Security, Request lifecycle
RedirectScheme Redirect easily the client elsewhere Request lifecycle
RedirectRegex Redirect the client elsewhere Request lifecycle
ReplacePath Change the path of the request Path Modifier
ReplacePathRegex Change the path of the request Path Modifier
Retry Automatically retry the request in case of errors Request lifecycle
StripPrefix Change the path of the request Path Modifier
StripPrefixRegex Change the path of the request Path Modifier

HTTP -> HTTPS Redirect

version: '3'

services:
  traefik:
    image: traefik:2.0
    # ...
    labels:
      - traefik.http.middlewares.http-to-https.redirectscheme.permanent=true
      - traefik.http.middlewares.http-to-https.redirectscheme.port=443
      - traefik.http.middlewares.http-to-https.redirectscheme.scheme=https
  website:
    # ...
    labels:
      - traefik.http.routers.website.entrypoints=web
      - traefik.http.routers.website.middlewares=http-to-https@docker
      - traefik.http.routers.website.rule=Host(`example.com`)
      - traefik.http.routers.website-secured.entrypoints=web-secure
      - traefik.http.routers.website-secured.rule=Host(`example.com`)
version: '3'

services:
  traefik:
    image: traefik:2.1
    command:
      # ...
      - --entrypoints.web.address=:80
      - --entrypoints.web.http.redirections.entryPoint.to=websecure
      - --entrypoints.web.http.redirections.entryPoint.scheme=https
      - --entrypoints.web.http.redirections.entrypoint.permanent=true
      - --entrypoints.websecure.address=:443
  website:
    # ...
    labels:
      - traefik.http.routers.website.entrypoints=websecure # optional!
      - traefik.http.routers.website.rule=Host(`example.com`)

OLD!

NEW!

Example: GZIP Compression

version: '3'

services:
  traefik:
    image: traefik:2.2
    # ...
    labels:
      - traefik.http.middlewares.gzip.compress=true
  whoami:
    image: containous/whoami
    labels:
      - traefik.http.routers.whoami.rule=Host(`app.test`)
      - traefik.http.routers.whoami.middlewares=gzip@docker

Example: Headers

version: '3'

services:
  traefik:
    image: traefik:2.2
    # ...
    labels:
      - traefik.http.middlewares.testHeader.headers.customrequestheaders.X-Script-Name=test
      - traefik.http.middlewares.testHeader.headers.customresponseheaders.X-Custom-Response-Header=value
      - traefik.http.middlewares.testheader.headers.accesscontrolalloworiginlist=https://example.org
  whoami:
    image: containous/whoami
    labels:
      - traefik.http.routers.whoami.rule=Host(`app.test`)
      - traefik.http.routers.whoami.middlewares=testHeader

Example: Rate Limit

version: '3'

services:
  traefik:
    image: traefik:2.2
    # ...
    labels:
      # Here, an average of 100 requests per second is allowed.
      # In addition, a burst of 50 requests is allowed.
      - traefik.http.middlewares.test-ratelimit.ratelimit.average=100
      - traefik.http.middlewares.test-ratelimit.ratelimit.burst=50
  whoami:
    image: containous/whoami
    labels:
      - traefik.http.routers.whoami.rule=Host(`app.test`)
      - traefik.http.routers.whoami.middlewares=test-ratelimit@docker

Let's Encrypt

version: '3'

services:
  traefik:
    image: traefik:2.2
    command:
    - # ...
    - --certificatesresolvers.myresolver.acme.email=your-email@example.com
    - --certificatesresolvers.myresolver.acme.storage=acme.json
    # used during the challenge
    - --certificatesresolvers.myresolver.acme.httpchallenge.entrypoint=web
    # during development
    - --certificatesresolvers.myresolver.acme.caserver=https://acme-staging-v02.api.letsencrypt.org/directory
  blog:
    image: my-blog
    # ...
    labels:
    - traefik.http.routers.blog.rule=Host(`example.com`) && Path(`/blog`)
    - traefik.http.routers.blog.tls=true
    - traefik.http.routers.blog.tls.certresolver=myresolver

Provider: Traefik Docs

Metrics

Metrics Support

Traefik supports 4 metrics backends:

  • Datadog
  • InfluxDB
  • Prometheus
  • StatsD
version: '3'

services:
  traefik:
    image: traefik:2.2
    command:
    - --api.insecure=true
    - --providers.docker
    - --metrics.prometheus=true
    - --entryPoints.metrics.address=:8082
    - --metrics.prometheus.entryPoint=metrics

Example: Prometheus

Tracing

Tracing Support

Traefik supports six tracing backends:

  • Jaeger
  • Zipkin
  • Datadog
  • Instana
  • Haystack
  • Elastic

 

 

B3-Propagation & OpenCensus compatible!

version: '3'

services:
  traefik:
    image: traefik:2.2
    command:
    - --api.insecure=true
    - --providers.docker
    - --tracing.zipkin=true
    - --tracing.zipkin.httpEndpoint=http://localhost:9411/api/v2/spans
    - --tracing.zipkin.sampleRate=0.2

Example: Zipkin

<label class="sr-only">Traefik loves Kubernetes</label>

Ingress Controller

# deployment.yaml
containers:
  - name: traefik
    image: traefik:v2.2
    args:
    - --log.level=DEBUG
    - --api
    - --api.insecure
    - --entrypoints.web.address=:80
    - --providers.kubernetesingress
    ports:
    - name: web
      containerPort: 80
    - name: admin
      containerPort: 8080

Custom Resource Definition (CRD)

apiVersion: traefik.containo.us/v1alpha1
kind: IngressRoute
metadata:
  name: my-route
spec:
  entryPoints:                     
    - foo
  routes:                          
  - kind: Rule
    match: Host(`test.example.com`)
---
apiVersion: traefik.containo.us/v1alpha1
kind: Middleware
metadata:
  name: gzip
spec:
  compress: {}

Questions?

Questions!

Thank You!

Ole Rößner | o.roessner@neusta.de |      @djbasster

Image Source: Dribbble