Using Traefik as a simple Http Reverse Proxy / LoadBalancer
May 11, 2017
Which Lightweigth Http Reverse Proxy ?
I was looking for a “simple” http reverse proxy.
Apache HttpD, NGinx, or any other reverse proxy would do the job.
Traefik has many advantages:
very easy to install: simply copy a single executable file (statically linked executable, NO installation required)
ligthweigth proxy
run on windows/linux (2 different go executables)
relatively easy to configure
has rich flexible concepts : entrypoints(:8080) - frontends(logical domain/routes) - backend(logical servers group or api) - servers(physical server)
auto reload configuration file if changing
evolutive to use dynamic discovery routing, for clustering with Docker,Mesos,Kubernetes,Consul,Zookeeper,...
Performing a local test: port 8080 - proxy to ports 8081 or 8082
One of the goals is to obtain “High Availability”, by load-balancing to at least 2 server instances for the same logical service.
node1 : running on port 8081 (port may be visible to localhost only, using bind ::1)
node2 : running on port 8082
reverse-proxy: running on port 8080 ... the public port url "http(s)://host:8080" for client users
The load-balancer will re-write every HTTP request to one of the node, and re-write the HTTP response back from the node to the client.
Benefits:
To redeploy my server without interruption of service,
I want to kill node1, redeploy it, restart node1, then kill node2, redeploy it, start node2.
Preparing spring-boot web project to 2 instance nodes on port 8081 and 8082
I have created a hello-world web server, using springboot project https://start.spring.io/
(choose component “web” + click “download”, then “mvn package”, import in your IDE, add @RestController, start main!)
My Rest application contains 2 resource:
a static html page for testing, and a Rest-Json endpoint.
Using springboot jar application, the static file is src/main/resources/static/app/index.html
I have also added a redirect html page from url “/index.html” to “/app/index.html”</BR>
The ultimate goal is to route requests using url PrefixPath
The Rest-Json endpoint is
To run it (using eclipse or equivalent shell command): it is a simple main app
By default, it run on port 8080… and I can test it using shell curl command:
You can check springboot log file to see
To run 2 instances on 2 different network ports, you need either to change jvm arguments
or to change application.yaml
… but then I need anyway to have 2 different projects, or back to 2 different jvm argsuments
Preparing Traefik configuration
Download the 40Mo executable file from the website (or use Docker)
Edit the traefik.toml configuration file:
Cf below for healthcheck section explanation.
Run it
You can test … it run OK!
You URL is now accessible from 8080, and will redirect internally to 8081 and 8082
Traefik does not have a lot of log using loglevel=INFO… Only 6 lines at startup:
For example, if you don’t write the empty section “[file]” in the traefik.toml, you have exactly the same logs at startup, so no warn message… but nothing works!
By changing to loglevel=DEBUG, you got more informations about the “backend servers routes”:
Activating access.log file
Add this option
In the access.log, you can see http requests received and processed successfully (both for html : 304 = OK not-modified, and for Rest/Json : 200 OK)
You can have a “start-traefik.sh” shell script, basically to avoid remember optional debug arguments
HealthCheck ... for detecting up/down nodes
Traefik does not mark nodes up/down based on tcp connection error!!
Traefik does not auto-retry on node2 if node1 failed to answer… Damned!
By using basic round-robin, and NO health-check, you obtain alternatively 50% request OK, and 50% ERROR!!
For the client app, you have 50% “Bad Gateway” http responses.
Maybe if your client is smart enough, it will retry few times, and hopefully (if it is alone),
the round-robin will finish to choose the right node runnnig, so the client will have its answer and stop it’s retries loop.
In the log (with loglevel=INFO), you see only WARN for 50% failing requests.. no messages for “marked down” node.
In the access.log, you see the 50% OK (http code 200) and 50% ERROR (http code 503):
The healthcheck section is necessary for Traefik to detect when your service is unavailable.
So I have added a dummy endpoint “/app/health” that does nothing, and it is here pinged every 3 seconds by Traefik.
There is no log message for “server up”… but you have plenty of repeated messages “HealthCheck still failing” when one server is down.
Doing a ping with a frequency of 3 seconds is not CPU intensive… it is only a local socket open, and no complex action performed for “http GET /app/health”.
It is not very realistic eithre to have a frequency of ~10ms …
During the 3seconds, the server can be estimated has “up” by Traefik, but be really “down”…
Therefore, I can have an interruption of service during 3 seconds! This is not 100% High Availability
Conclusion
Static routing in Traefik is only a very limited use-case, compared to dynamic service-discovery that Traefik is able to perform using Mesos/Kubernetes/Consul/.. !!
It is a pity that Traefik does not automatically mark node down when TCP connection failed,
and silently retry to other nodes when such a connection fail!
However, Traefik is really easy to install and use at start, and very powerfull to continue with.