Hey folks,
I though I share this small project of mine with you. It’s a relatively simple quick load balancer implemented in user-space. The performance is as you would expect from a user-space balancer, however the rich feature set can be very appealing.
The fun stuff is that it builds with an embedded JIT compiler for the LUA scripting language and many of the decision making points can be totally rewritten and set up to match your needs.
Ever had the need to balance UDP streams? It’s more difficult than you think, but it’s possible with SSLB. It can be customised to balance any protocol and it has connection modules for TCP, UDP, Unix Domain Sockets (UDS) and it can perform Layer 7 inspections.
You can run multiple instances on a single host to expose different services. It’s quite funky and very resource friendly with a tiny memory footprint. It can be used as an inspecting proxy too.
I’ll go ahead and include the README file in this article to help you get an idea what it is about.
It is completely free and licensed under GPLv2 licence.
Szabi
So the README:
SSLB v2.0 – Simple Server Load Balancer
History
I used to have a project where there were specific requirements around balancing load between servers. Obviously, there’re a number of existing load balancers out there and most of them are capable of doing many things, but at the time I had difficulty to find one which met my requirements.
If a load balancer was meant to be used purely on TCP level it usually lacks capabilities to make decisions based on say Layer7 information. If one is designed to work with Layer7 information, then usually it’s designed around balancing HTTP traffic and were very limited when it came to raw TCP streams. As for HTTP traffic, there are a number of brilliant proxies or proxy modules which support some kind of load balancing, but when it comes to say ‘sticky sessions’ the built-in logic is not sufficient, often very simple implementation not truly tracking session IDs but rely on some specific format of them. (route id)
And what if you want to balance non-HTTP traffic but make balancing decisions based on Layer7 information? Then you’re out of luck.
Given all this I went on and wrote one in C back in 2011. I did not publish the source code back then as I thought the project was only ever going to be useful for me. I however had to do something very similar now in 2016 and ended up looking at my pet-balancer again. I took the source code and ported/rewrote parts of it and the project is now in C++.
This opened up some doors, but frankly it’s more like stylistic difference, I am still yet to be convinced about C++ over C, but Hey!, time flies and recently I was only working in higher level OOP languages, so I thought it’s going to be C++.
During the design phase I have re-evaluated how I had thought about my balancer and figured why hardcode certain things, why not make them completely configurable? That’s not a big deal, everyone gets to this conclusion in about 10s, however I was no longer thinking about modules (like SSLBv1 already had). I went a step further. What if I implemented something more, but still optional, as a module.
I decided to integrate the excellent LUA engine (either interpreter or JIT compiler) into the project and externalise certain logic. This was a big step forward, surely. Have a look at the sample scripts and you’ll understand why.
Features
* Support for raw TCP flows
* Support for various balancing algorithms
– Round-Robin
– Least-Connections
– Weighted
* Support for Layer7 information evaluation (eg: HTTP Cookie based sticky sessions)
* Support for Layer7 stream manipulation
* Scriptable balancing decisions
* Scriptable Layer7 processing
* SSL off-loading
* SSL on-loading
* UDP balancing (this is a bit tricky of course and there are limitations, but can be extremely useful)
* Protocol transformation support (say backend TCP, front-end UDP or the other way round)
* Full support for IPv6
* IPv6 -> IPv4 transformation and vice versa
* Unix domain sockets support
– allows exposing the service as UDS
– allows balancing to UDS services
* Support for various sets of backends at the same time (some can be for example TCP, others SSL,
others UDP or Unix Domain sockets, etc. )
Build Requirements
You’ll need cmake >= 3.0, make and a c++ compiler. I wrote the project on OS X using the clang++ compiler, but tested it on g++-4.9 as well on Debian Linux. Probably compiles fine on any *nix OSes as long as the compiler supports C++11.
If you want the LUA support you’ll need either Lua or LuaJIT libraries and header files. If you need SSL support you’ll need OpenSSL libraries and headers or any derivative.
In a list form:
* clang++ or g++
* cmake (>=3.0)
* make
* POSIX threading library
* OpenSSL (optional)
* Lua or LuaJIT (optional)
Building
mkdir build
cd build
cmake ..
make
How things work?
SSLB can be run either as a daemon or an application. Either way it will use a threadpool based architecture. SSLBv1 had a pre-fork model usable as a `worker`, but I did not bother with that this time, given the threading is more efficient.
The application has a simple config file which is divided into sections. The sections are between square brackets `[` and `]`.
Example:
[Test]
TestVariable = testvalue
A sample config file is included in the `test` directory along with sample LUA scripts.
SSLB logs via syslog by default if available, however you can disable this in the config file and use the built-in logger to write to a log file in the file-system.
And then the fun part. LUA. If you have `Lua` or `LuaJit` headers/libs on your system (libluajit-5.1-dev on Debian) then you can build in the excellent `LUA` language. If you do, then you can externalize certain things if you want to.
`LuaJit` are always preferred (due to the performance benefits), but be aware which interpreter you’re using. `LuaJit` is currently supports up to LUA 5.1, whereas if you use the vanilla LUA library that is already at version 5.3. Obviously, you’re scripts must be written with the used version in mind.
Of course there’s no need to use LUA even if you compiled support in for it, but if you do, you’ll have full control over:
* balancing decisions, upon connection establishment
* Layer7 information access (essentially you can inspect the data passing through)
Well, this is more powerful than you think. Just look at the sample scripts. As far as I know this is the only balancer capable of SIP NAT fixups, for example. Don’t quote me on this though. I am aware of some stateful firewall which capable of just that.
How LUA is so useful?
Balancing scripts:
* you can implement your own balancing logic
– for example provide sticky session by looking up session data from a file or DB or calling a REST endpoint
– fine-tune the balancing logic to something what regular balancers don’t support
* you can record balancing decision and or client information
– you can log all of your client connections and or statistics in a file
Layer7 scripts:
* Provide balancing decisions based on traffic (say an HTTP cookie, see sample script)
* Balance non-HTTP traffic with insight (SIP, RSTP, etc.)
* Log certain parts of the passing traffic
* Manipulate passing traffic (say Rewrite SIP request, perhaps do NAT fixup before/after balancing)
* Duplicate flows (like port spanning in switches), send requests to more servers
* Split request based on traffic (say you can send traffic to nodes based on a field in the passing JSON traffic)
Data persistence
Lua scripts can store data in a memory store using the `*_user_data()` functions. However this is not persisted across restarts for example. You can do this using the `Shutdown` and `Startup` script entry points respectively. These are invoked by the SSLB core and it’s possible for you to save all the data in the memory store and upon the next startup load the data back. A simple data dump is shown in `shutdown.lua`. `Startup` is invoked before SSLB starts to accept connections and `Shutdown` is called after all connections have been terminated.
The user data in-memory store is backed by a regular C++ map, therefore can be considered pretty fast for scripting purposes. The store is entirely managed by the user (hence the name) and it’s only lost on shutdown.
Closing thoughts
SSLB is a very flexible yet tiny networking tool. You can build any kind of proxy out of it. If you don’t want to use it as a balancer, just define one backend and done, SSLB became a proxy where you can script what happens inside without writing all the boilerplate networking code.