Hello James,
The Balance random should do exactly what you want in HAProxy 1.9 :
Check out
https://www.haproxy.com/blog/haproxy-1-9-has-arrived/#random-load-balancing-algorithm
"We’ve added a new random load-balancing algorithm. When used, a random
number will be chosen as the key for the consistent hashing function. In
this mode, server weights are respected. Dynamic weight changes take
effect immediately, as do new server additions."
So to get the desired effect, you could use balance random without
server weights: this should yield an uniform distribution.
However, the socket approach is probably the best approach if you really
want to have the 503-response-on-cluster-failure scenario.
The reason why the socket approach is also probably the best bet in
production environments is that when you are scaling SSL termination
across multiple haproxy processes it is often the case that you will use
unix sockets anyway to distribute the load, and you can cleverly reuse
those sockets to do what you want.
So you would have haproxy in tcp mode binding to ports 80 and 443,
creating multiple sockets which different processes would bind to to
handle TLS (and handle http load balancing). By cleverly adjusting the
weights you could reuse these sockets for A/B testing purposes. Also,
this allows you to test different TLS ciphers as well (each process can
define their own TLS ciphers).
I should also point out that you can test different haproxy versions if
you split the haproxy version used for the level 4 load balancing and
the haproxy version used for level 7 load balancing. However, that might
be overkill in your setup.
Considering this approach with L4 load balancing -> sockets -> L7 load
balancing works quite nicely at scale, I would recommend it, though
there are a few pitfalls associated with this. Heavy testing of the
HAProxy configuration in staging is recommended or chatting with our
enterprise support to avoid nasty surprises.
Please note that you should consider setting send-proxy-v2 and
accept-proxy respectively to preserve source IP information when using
sockets. Also, tcp-request inspect-delay is your friend (multiples of 3s
are recommended to handle tcp re-transmission + 1 e.g. 4s, 5s etc.).
As for the latency/performance impact of sockets, it would be nice to
measure the overhead and see if something can be optimized, but again,
depending on the scale you're working at, it might be negligible
compared to the elephant in the room (TLS CPU overhead, especially with
RSA).
Also, with sockets, you can disable routing traffic to a backend simply
by toggling the state of the socket, which is pretty neat.
The crux of the matter is that use_backend doesn't allow you to simply
split the traffic in half in an obvious way (e.g. by simply specifying a
keyword), instead, you have to define an ACL that would to that. You
could do that by setting a special header or a cookie in your
application code which would allow you to construct an ACL rule to split
the traffic on.
An useful feature would be to modify the use_backend syntax to allow for
easier splitting across multiple backends. It might be possible that Lua
can achieve this. Can't make any promises, but I will investigate
further into this if I have some spare time.
Another approach would be to add a feature that automatically rebalances
all weights if a server is added/removed (so you would just use one big
backend): However, this is also achievable by appropriately wrapping the
HAProxy API. Might be less painful than the alternatives.
Another approach if you are working with multiple haproxy instances
would be to deploy different haproxy configurations to different servers
(one haproxy server exclusively servers A traffic, another one B traffic).
To conclude, there are multiple approaches that might yield good results
with HAProxy, and some good features for better testing could be
developed, but one should consider if this should be done by HAProxy at all.
Your application might be able to make the decision instead in an
environment you are more comfortable with: the programming language used
by your team.
If you do A/B testing in your application, you can use the entire server
fleet
instead of sacrificing 50% of your servers as guinea pigs.
What happens when e.g. the entire B backend starts serving 500 responses?
The load on your A backend will double. Are you sure you have the
capacity for that?
Contrast that to implementing the change via a feature flag: if anything
goes wrong with "B" you change one value in e.g. your database, and the
entire server fleet switches to using "A".
Without knowing more about the environment you are performing A/B
testing (production, development, staging), the scale you are working
at, and which part of the stack you are A/B testing, it is difficult to
come up with a recommendation.
Let me know if you have any questions.
Best regards,
Bruno Henc
On 2/11/19 10:15 PM, James Root wrote:
Hey Aleks,
Thank you for the reply, I should have included my version. I am
currently using HAProxy 1.8, but moving up a version is a possibility.
I understand what your example is doing, but it has the same issue my
original example has I think, that I have to have one unix socket per
cluster. In my case, "cluster" is just a small collection of servers
with the same service, but there could be dozens of these clusters.
In our setup, an inbound request gets routed to the correct backend
based on the host header (in my original example, this backend would
be "haproxy-test"). But then I effectively want to A/B test between
the two clusters that can serve this backend. I could put ever server
in this one backend, with the proper weights, but that isn't exactly
what I am looking for. Ideally, I would like if one cluster goes out
completely that 503s get returned for any requests that would normally
get round robined to that cluster. The only way I could find to
actually enforce weighting between two clusters was to forward the
request through a socket to a new "frontend" (functionally this is the
same as running a proxy instance per cluster). This seems to work, but
I am looking for a way to do it without opening up a large amount of
unix sockets.
On Wed, Feb 6, 2019 at 11:43 AM Aleksandar Lazic <[email protected]
<mailto:[email protected]>> wrote:
Hi James.
Am 06.02.2019 um 16:16 schrieb James Root:
> Hi All,
>
> I am doing some research and have not really found a great way
to configure
> HAProxy to get the desired results. The problem I face is that I
a service
> backed by two separate collections of servers. I would like to
split traffic
> between these two clusters (either using percentages or
weights). Normally, I
> would configure a single backend and calculate my weights to get
the desired
> effect. However, for my use case, the list of servers can be
update dynamically
> through the API. To maintain correct weighting, I would then have to
> re-calculate the weights of every entry to maintain a correct
balance.
>
> An alternative I found was to do the following in my
configuration file:
>
> backend haproxy-test
> balance roundrobin
> server cluster1 [email protected] weight 90
> server cluster2 [email protected] weight 10
>
> listen cluster1
> bind [email protected]
> balance roundrobin
> server s1 127.0.0.1:8081 <http://127.0.0.1:8081>
<http://127.0.0.1:8081>
>
> listen cluster2
> bind [email protected]
> balance roundrobin
> server s1 127.0.0.1:8082 <http://127.0.0.1:8082>
<http://127.0.0.1:8082>
> server s2 127.0.0.1:8083 <http://127.0.0.1:8083>
<http://127.0.0.1:8083>
>
> This works, but is a bit nasty because it has to take another
round trip through
> the kernel. Ideally, there would be a way to accomplish this
without having to
> open unix sockets, but I couldn't find any examples or any leads
in the haproxy
> docs.
>
> I was wondering if anyone on this list had any ideas to
accomplish this without
> using extra unix sockets? Or an entirely different way to get
the same effect?
Well as we don't know which version of HAProxy do you use I will
suggest you a
solution based on 1.9.
I would try to use the set-priority-* feature
https://cbonte.github.io/haproxy-dconv/1.9/configuration.html#4.2-http-request%20set-priority-class
https://cbonte.github.io/haproxy-dconv/1.9/configuration.html#4.2-http-request%20set-priority-offset
https://cbonte.github.io/haproxy-dconv/1.9/configuration.html#7.3.2-prio_class
https://cbonte.github.io/haproxy-dconv/1.9/configuration.html#7.3.2-prio_offset
https://cbonte.github.io/haproxy-dconv/1.9/configuration.html#7.3.3-src
I would try the following, untested but I think you get the idea.
frontend clusters
bind [email protected]
bind [email protected]
balance roundrobin
# I'm not sure if src works with unix sockets like this
# maybe you need to remove the unix@ part.
acl src-cl1 src [email protected]
acl src-cl2 src [email protected]
http-request set-priority-class -10s if src-cl1
http-request set-priority-class +10s if src-cl2
# http-request set-priority-offset 5s if LOGO
# http-request set-priority-offset 5s if LOGO
use_backend cluster1 if priority-class < 5
use_backend cluster2 if priority-class > 5
backend cluster1
server s1 127.0.0.1:8081 <http://127.0.0.1:8081>
backend cluster2
server s1 127.0.0.1:8082 <http://127.0.0.1:8082>
server s2 127.0.0.1:8083 <http://127.0.0.1:8083>
There are a lot of fetching functions so maybe you find a better
solution with
another fetch function as I don't know your application.
https://cbonte.github.io/haproxy-dconv/1.9/configuration.html#7
In case you haven't seen it there is also a management interface
for haproxy.
https://cbonte.github.io/haproxy-dconv/1.9/management.html#9.3
https://www.haproxy.com/blog/dynamic-configuration-haproxy-runtime-api/
> Thanks,
> James Root
Regards
Aleks