Back to Traefik 2.0
Gigawatts of Routing Power
Back in 2015, a revolution was under way. We were moving from manual, handcrafted infrastructures, to container-based, industrial, and human-free platforms. In those dark ages of orchestration, edge traffic was remarkably difficult to manage. On one side, we had traditional reverse-proxies that were built for static infrastructures, on the other side, we were building dynamic clusters made to deploy and manage thousands of microservices. The idea of having a simple and automatic edge router, all in one binary, was appealing, but also idealistic. The foundation of Traefik was laid that year, paving the way to building a project with strong values: simplicity of configuration, modern features, and open to the community. A few years later, the project has reached 21k stars on Github, and more than 600 millions downloads. Insane.
Since then, the ecosystem around containers has changed a lot: Docker has become a de-facto standard, and Kubernetes has shown itself to be the clear winner in the battle for the best orchestration platform. Because of this, the CNCF landscape has exploded, with hundreds of components to choose from.
The time has come to work on something new for Traefik.
For several months, the maintainer team has been working on a deep refactoring of the codebase to provide the firm foundations for the next iteration of Traefik, and we are ready to share this vision with you.
Today, we’re announcing Traefik 2.0 alpha, the edge router built with the future in mind.
The new core is here, help us finalize Traefik with the features you want!
We Want Features!
Let’s see what we have in store.
Not a feature per se, but we still wanted to make it easier for newcomers to understand the core concepts at work in Traefik. We wanted every word to have a clear and unique meaning throughout the code and documentation.
Welcome providers, entrypoints, routers, middleware, and services.
- Providers are the cluster technologies you already use (Kubernetes, Docker, Consul, Mesos, Rancher…). Traefik uses your provider’s API to discover the routes to your services.
- Entrypoints, in their most basic forms, are the open ports where requests will land.
- Services represent the software hosted on your infrastructure. Traefik knows how to deal with multiple instances of your programs (currently supporting multiple load balancing capabilities), and use the services configuration to determine how to reach the actual program.
- Routers connect incoming requests to your services. They hold the rules that decide which service handles the request.
- Finally, pieces of middleware are components that may update the request before it is handled by a service. Out of the box, Traefik comes with middleware to manage authentication, rate limiting, circuit breaker, whitelisting, buffering, and so on.
Despite our best efforts, and because so many features have been added to Traefik since its first launch, we needed to polish things up and make sure every configuration option belonged in its place.
Here, it was a menial job that needed to be done, and we’ll spare you the details.
Let Us Be Clear … We Want ACTUAL Features!
So, let’s remove the mystery, since we’ve teased you enough. Here are the features we’ve added!
That’s right! It’s time to say goodbye to Issue #10!
Yes, three years later, after a lot of work (as foretold), and a lot of discussions, Traefik now fully supports the TCP protocol.
Want to see an example? Let’s use the File Provider, and redirect every request on port
27010 to your database service!
[entrypoints] [entrypoints.web] address = ":80" [entrypoints.mongo-port] address = ":27017" [tcp] # YAY! [tcp.routers] [tcp.routers.everything-to-mongo] entrypoints = ["mongo-port"] rule = "HostSNI(`*`)" # Catches every request service = "database"
The best part? Traefik supports routing based on SNIs!
[entrypoints] [entrypoints.web] address = ":80" [entrypoints.mongo-port] address = ":27017" [tcp] # YAY! [tcp.routers] [tcp.routers.to-db-1] entrypoints = ["mongo-port"] rule = "HostSNI(`db-1.domain`)" service = "db-1" [tcp.routers.to-db-1.tls] # The route is for TLS requests only [tcp.routers.to-db-2] entrypoints = ["mongo-port"] rule = "HostSNI(`db-2.domain`)" service = "db-2" [tcp.routers.to-db-2.tls] # The route is for TLS requests only
What’s even better? Traefik can go crazy, and support both HTTP and TCP on the same port.
Yes, multiple protocols on the same entrypoint!
[entrypoints] [entrypoints.the-one] address = ":443" [tcp] [tcp.routers] [tcp.routers.to-db-1] rule = "HostSNI(`db-1.domain`)" service = "db-1" [tcp.routers.to-db-1.tls] # The route is for TLS requests only [http] [http.routers] [http.routers.my-api] rule = "Host(`api.domain`)" service = "my-api"
In its first alpha version, Traefik only enables TCP routes in the File Provider, but stay tuned because it will (very) soon be available for other providers!
Middleware to Update the Requests
Many features used to be magically embedded in Traefik, with room for customization but no option for fine-tuning.
Middlewares fill the gap and give you the possibility to enable/configure any feature you’d like, per router if you wish.
Middlewares are components that act on the request before it is actually forwarded to the services, and can even decide to not forward the request if a required condition isn’t met.
At release, Traefik comes with the following middlewares: AddPrefix (to add a prefix to the request path), BasicAuth & DigestAuth, ForwardAuth (to delegate authentication to a third-party service), Buffering, Chain (to define reusable sets of middlewares), CircuitBreaker (to avoid calling broken services), Compress, Errors (to provide custom error pages), Headers, IpWhitelist, MaxConn (to limit the number of simultaneous connections to a service), PassTLSClientCert, RateLimit (to limit the number of requests to a service within a given period of time), RedirectRegex and RedirectScheme, and ReplacePath (to update the query path before forwarding it to the service).
And more importantly, we’ve re-architectured the code to make it easier to provide additional middlewares in the near future.
Kubernetes & CRD
In the past two years, the community has been discussing (a lot) around “better ingress,” and looking at our previous Kubernetes provider, we agreed that there was room for improvement. With v2, it became obvious that if we wanted to let our Kubernetes fans benefit from every feature (like TCP and middleware) without being swarmed by a clutter of annotations, we had to propose an alternative.
Among the alternatives, CRDs are more and more popular since they solve shortcomings of the ingress specification. Inspired by what Heptio did in Contour with IngressRoute, we extended the specification to implement every Traefik feature.
We firmly believe that a consensus can emerge from those proposals and lead to the next Ingress specification in Kubernetes.
Below is an example of this specification.
apiVersion: traefik.containo.us/v1alpha1 kind: IngressRoute metadata: name: test.crd namespace: default spec: entrypoints: - web - web-secure routes: - match: Host(`traefik.io`) && PathPrefix(`/foo`) kind: Rule services: - name: whoami1 port: 80 strategy: RoundRobin middlewares: - name: stripprefix - name: addprefix namespace: foo - match: Host(`containo.us`) && Method(`POST`) kind: Rule services: - name: whoami2 port: 80 tls: secretName: supersecret
New Expressive Routing Rule Syntax
Traefik now supports an expressive syntax to define the router rules, with
The available matchers being
Since TCP is a whole different world, for now, it only supports a dedicated matcher:
Want to see examples?
rule = (Host(`api.domain`) && PathPrefix(`/v2`)) || Host(`api-v2.domain`) rule = (Method(`DELETE`) || (Method(`POST`) && Query(`action`, `delete`))) && Host('api.domain')
Traefik has always been compatible with multiple providers, which is one of its greater strengths: whatever your infrastructure is, from bare metal to orchestrators and clusters, Traefik just works!
But with v2, we’ve gone a little further and allowed users to declare elements (middlewares, services, routers) in a provider, and to use them from a different one.
Let’s take a look at the following example, that declares an authentication middleware in a configuration file (the File Provider), which is being used from a Docker label (the Docker Provider).
# somewhere in a configuration file for the file provider [http.middlewares.my-users.basicauth] users = ["test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/", "test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0"]
# somewhere in a docker compose file your-container: image: your-docker-image labels: - "traefik.http.routers.my-router.middlewares=file.my-users"
And of course, you could declare a router in a provider that points to services declared by another.
TLS Termination per Route
While adding TCP support to Traefik, we had to re-think almost everything, starting with the entrypoint itself — the very door to your cluster.
Our first idea was to dedicate entrypoints (and as a consequence port numbers) to a type of “protocol.” Unfortunately, in some cases, especially when people used Traefik to route requests for multiple clusters, we deemed it too restrictive. As a consequence, we allowed multiple protocols per entrypoint.
Then, as debates were still going on, we kept looking for ways to better control how users would configure TLS termination or passthrough. The idea of enabling TLS on the Router level won the brainstorming, and then passed the tests of proof of concept (we have quite a fan of proof of concept branches on the team, to the point it has almost become a meme).
Here is an example with three routers listening on the same entrypoint, the first router terminates TLS connections (on HTTPS), the second router terminates TLS connections (on TCP), and the third router passes through, leaving the details of the TLS connection to be handled by the service itself.
[entrypoints] [entrypoints.web-secure] address = ":443" [http] [http.routers.to-service-1] rule = "Host(`domain-1`)" service = "service-1" [http.routers.to-service-1.tls] # terminates the tls connection and sends clear data # to service 1 [tcp] [tcp.routers.to-service-2] rule = "HostSNI(`domain-2`)" service = "service-2" [tcp.routers.to-service-2.tls] # terminates the tls connection and sends clear data # to service 2 [tcp.routers.to-service-3] rule = "HostSNI(`domain-3`)" service = "service-3" [tcp.routers.to-service-3.tls] passthrough = true # sends encrypted data "as is" to service-3
Labels, Key-Value Configuration, Tags, …
A small tweak on the surface, but quite an impact in the code, we’ve re-written the configuration parser. This new system ensures that every option in Traefik has the same path, whether expressed in TOML
[something.that.is.here], a label attached to a container
something.that.is.there, a key (as in key-value store)
something/that/is/somewhere, or anything that might be available in the future.
We’ve updated the documentation structure to help new users quickly understand how to use and configure Traefik. At the same time, we came up with an outline that mirrored the configuration structure, making it easier for expert users to learn more about specific configuration details.
The Road to the Final Version
Yes, more (much more) is on the way, but we were so eager to share the work that we couldn’t wait a minute longer than we already did.
New WebUI, remaining providers, metrics, UDP, YAML, TLS stores & options, canary, more documentation — everything is possible.
Apart from re-enabling other providers (alpha comes with file, Docker, and k8s only), and enabling TCP for all of them (currently file only), the roadmap is full of features.
First, the most visible one, a contributor is actively working on a revamped WebUI, and from the mockups we saw, it looks very promising. We expect this new UI to help you navigate seamlessly through hundreds of routes.
Second, we’re working on improvements for TLS configuration, with stores and options, which will deserve its own explanation.
Third, now that we have tried our hand on a new protocol, well, we might as well add others (like UDP).
If you want to discuss or even help us implementing these features, please join us on the community forum or on Github, the maintainer team is always open to your contributions.
But the options don’t stop there — Traefik is a community-driven project, and we never know what is going to happen! We can’t wait to see what you’ll do with this new architecture.
To be continued…
The community is at the center of our attention.
We’ve scheduled an online meet-up where the maintainer team will exclusively talk about Traefik v2.0! If you were looking for an opportunity to ask questions or see a live demo, here it is.
Probably the most important, we need your feedback. Tell us what you think, voice your opinion! The alpha stage is the perfect timing to include disruptive changes. Go crazy with pull requests, tests, and ideas.
And finally, of course, our goal is to release the final as soon as possible!
We hope you enjoy Traefik as much as we enjoy building it with you!
Grab the latest binaries for Linux, Windows, and Mac on Github or get the official Docker image!
docker pull traefik:v2.0.0-alpha1 (or 2.0.0-alpha1, v2.0, 2.0, faisselle) docker pull traefik:v2.0.0-alpha1-alpine docker pull traefik:v2.0.0-alpha1-nanoserver
The documentation can be found here and the huge changelog is here.
Thank you Gérald Croës for your major contribution on this blog post and congrats to all the team members that have been working so hard on this alpha release 👏