HAProxy 2.0 & Beyond

HAProxy Technologies is excited to announce the release of HAProxy 2.0, bringing features critical for cloud-native and containerized environments, while retaining its industry-leading performance and reliability.

HAProxy 2.0 adds a powerful set of core features as well as completely new functionality that further improves its seamless support for integration into modern architectures. This includes Layer 7 retries, Prometheus metrics, traffic shadowing, polyglot extensibility, and gRPC support. In conjunction with this release, we are also introducing the HAProxy Kubernetes Ingress Controller and the powerful HAProxy Data Plane API which provides a modern REST API for configuring and managing HAProxy. Read the release announcement here.

When HAProxy 1.8 was released in November 2017, it introduced features including Hitless ReloadsDNS Service DiscoveryDynamic Scaling with the Runtime API, and HTTP/2 at the edge. These advancements moved HAProxy along the path of supporting a variety of architectures at any scale and in any environment, while also allowing it to maintain its position as the world’s fastest software load balancer.

Since then, many important changes have happened within the core project itself, such as changing the release cadence from an annual to a biannual release cycle. The project has opened up issue submissions on its HAProxy GitHub account. This has allowed our community to continue to flourish and we’re excited to be a part of such a strong corps of contributors.

The HAProxy community provides code submissions covering new functionality and bug fixes, quality assurance testing, continuous integration environments, bug reports, and much more. Everyone has done their part to make this release possible! If you’d like to join this amazing community, you can find it on SlackDiscourse, and the HAProxy mailing list.

This release improves upon capabilities that fit the unique conditions of cloud and container environments. HAProxy 2.0 is an LTS release.

In addition, the inaugural community conference, HAProxyConf, will take place in Amsterdam, Netherlands on November 12-13, 2019. With many interesting talk suggestions already received, we are looking at an amazing conference and we hope to see you there!

We’ve put together a complete HAProxy 2.0 configuration, which allows you to follow along and get started with the latest features right away. You will find the latest Docker images here. We’ll also be hosting webinars to cover the HAProxy 2.0 release, the Data Plane API, and the Kubernetes Ingress Controller. Sign up here.

In this post, we’ll give you an overview of the following updates included in this release.

Cloud-Native Threading & Logging

Tuning HAProxy for optimal performance is now even easier. Since version 1.8, you’ve been able to set the nbthread directive to instruct HAProxy to operate across multiple threads, which allows you to make better use of multi-core processor machines. HAProxy now automatically configures this for you. It will, out of the box, set the number of worker threads to match the machine’s number of available CPU cores. That means that HAProxy can scale to accommodate any environment with less manual configuration.

You can still configure this yourself with the nbthread directive, but this makes the task simpler. It also removes the burden of tuning this in cloud environments where machine instance sizes may be heterogeneous. On systems where HAProxy cannot retrieve the CPU affinity information, it will default to a single thread.

This also simplifies the bind line as it no longer requires you to specify a process setting. Connections will be distributed to threads with the fewest active connections. Also, two new build parameters have been added: MAX_THREADS and MAX_PROCS, which avoid needlessly allocating huge structs. This can be very helpful on embedded devices that do not need to support MAX_THREADS=64.

Logging is now easier to adapt to containerized environments. You can log directly to stdout and stderr, or to a file descriptor. Use the following syntax:

log stdout local0
log fd@1 local0
log stdout format raw local0

HTTP Representation (HTX)

The Native HTTP Representation (HTX) was introduced with HAProxy 1.9 and it laid the foundation that will allow HAProxy to continue to provide best-in-class performance while accelerating cutting-edge feature delivery for modern environments. Many of the latest features, such as end-to-end HTTP/2, gRPC, and Layer 7 retries, are powered by HTX.

HTX creates an internal, native representation of the HTTP protocol(s). It creates strongly typed, well-delineated header fields and allows for gaps and out-of-order fields. Modifying headers now consists simply of marking the old one as deleted and appending the new one to the end. This provides easy manipulation of any representation of the HTTP protocol, allows HAProxy to maintain consistent semantics from end-to-end, and provides higher performance when translating HTTP/2 to HTTP/1.1 or vice versa.

With HTX in place, any future HTTP protocols will be easier to integrate. It has matured since its introduction and starting in 2.0 it will be enabled by default.

End-to-End HTTP/2

With HTX now being on by default, HAProxy officially supports end-to-end HTTP/2. Here’s an example of how to configure it with TLS offloading. The bind and server lines include the alpn parameter, which specifies a list of protocols that can be used in the preferred order:

frontend fe_main
mode http
bind *:80
bind *:443 ssl crt /etc/haproxy/certs/www.example.com.pem alpn h2,http/1.1
http-request redirect scheme https unless { ssl_fc }
default_backend be_main
backend be_main
mode http
server server1 192.168.1.13:443 ssl verify none alpn h2

You can also use HTTP/2 without TLS. Remove any ssl and verify parameters from the server and/or bind lines. Then swap alpn h2 for proto h2 and HAProxy will use only the given protocol.

gRPC

HAProxy 2.0 delivers full support for the open-source RPC framework, gRPC. It allows for bidirectional streaming of data, detection of gRPC messages, and logging gRPC traffic. The gRPC protocol is a modern, high-performance RPC framework that can run in any environment. Using Protocol Buffers, it’s able to serialize messages into a binary format that’s compact and potentially more efficient than JSON.

To begin using gRPC in HAProxy, you just need to set up a standard end-to-end HTTP/2 configuration. Here, we’re using the alpn parameter to enable HTTP/2 over TLS:

frontend fe_main
bind :443 ssl crt /path/to/cert.pem alpn h2
default_backend be_servers
backend be_main
default-server ssl verify none alpn h2 check maxconn 50
server grpc1 10.1.0.11:3000

Standard ACLs apply and allow for path-based matching, as shown:

frontend fe_main
bind :3001 ssl crt /path/to/cert.pem alpn h2
acl is_otherservice_path path /AnotherService/SomeFunction
use_backend be_otherservers if is_otherservice_path
default_backend be_main

Additionally, two new converters, protobuf and ungrpc, have been introduced that let you extract the raw Protocol Buffers messages.

Layer 7 Retries

Reducing downtime often involves having smart contingency mechanisms in place. HAProxy has, since its inception, supported retrying a failed TCP connection by including the option redispatch directive. With HAProxy 2.0, it can also retry from another server at Layer 7 for failed HTTP requests. The new configuration directive, retry-on, can be used in a defaultslisten, or backend section. The number of attempts at retrying can be specified using the retries directive. It is important that you know how your application behaves with Layer 7 retries enabled. Caution must be exercised when retrying requests such as POST requests. In our examples, we have disabled POST requests from being retried using http-request disable-l7-retry if METH_POST

It supports a variety of error types to allow for granular control. Otherwise, you can specify all-retryable-errors, which will retry the request for any error that is considered to be retriable. The full list of retry-on options is below:

Option

What it means

none

Never retry.

conn-failure

Retry when the connection or the TLS handshake failed. This is the default.

empty-response

Retry when the server connection was closed after part of the request was sent and nothing was received from the server. This type of failure may be caused by the request timeout on the server side, poor network conditions, or a server crash or restart while processing the request.

junk-response

Retry when the server returned something not looking like a complete HTTP response. This includes partial response headers as well as non-HTTP contents. It is usually a bad idea to retry on such events, which may be caused by a configuration issue such as having the wrong server port or by the request being rejected because it is potentially harmful to the server (a buffer overflow attack, for example).

response-timeout

The server timeout struck while waiting for the server to respond. This may be caused by poor network conditions, the reuse of an idle connection that has expired, or the request being extremely expensive to process. It is generally a bad idea to retry on such events on servers dealing with heavy database processing (full scans, etc.) as it may amplify denial-of-service attacks.

0rtt-rejected

Retry requests that were sent over TLS Early Data (0-RTT) were rejected by the server. These requests are generally considered to be safe to retry.

<status>

Retry on any HTTP status code among 404 (Not Found), 408 (Request Timeout), 425 (Too Early), 500 (Server Error), 501 (Not Implemented), 502 (Bad Gateway), 503 (Service Unavailable), 504 (Gateway Timeout).

all-retryable-errors

Retry for any error that is considered retriable. This is the same as if you specified conn-failureempty-responsejunk-responseresponse-timeout0rtt-rejected500502503, and 504.

HAProxy 2.0 also introduces a new http-request action called disable-l7-retry that allows you to disable any attempt to retry the request if it fails for any reason other than a connection failure. This can be useful, for example, to make sure that POST requests aren’t retried.

Here’s an example configuration that activates Layer 7 retries:

frontend fe_main
bind :443 tfo ssl crt /etc/haproxy/certs/www.example.com.pem alpn h2,http/1.1
default_backend be_main
backend be_main
default-server ssl verify none alpn h2 check maxconn 50
retry-on all-retryable-errors
http-request disable-l7-retry if METH_POST
server server1 192.168.1.13:443
server server2 192.168.1.14:443

Data Plane API

dataplane api

In today’s cloud-native landscape, ephemeral services are born and die quickly, deployments happen continuously, and configuration needs to be refreshed constantly. The new Data Plane API provides a modern REST API for configuring HAProxy on the fly. You can now dynamically add and remove frontends, backends, and servers. You can create ACL rules, insert HTTP routing directives, set IP and port bindings, and much more. The API updates the configuration file as needed, reloading the HAProxy process when necessary.

HAProxy has proven itself to be dynamic and extensible with its built-in Lua support and its Stream Processing Offload Engine. The new Data Plan API takes that one step forward by providing true dynamic configuration management. The API daemon runs as a sidecar process, which HAProxy can manage using the program directive in the new Process Manager. The HAProxy Data Plane API supports transactions, which allow multiple changes to be applied simultaneously. This gives you the ultimate confidence that updates are atomic.

Process Manager

Several of the exciting innovations happening involve components that run as sidecar processes alongside HAProxy, such as the Data Plane API and any Stream Processing Offload Agents (SPOAs). Clearly, there’s a benefit to having central orchestration to control the lifecycle of these processes.

This release introduces support for the new Process Manager. It allows you to specify external binaries that HAProxy will start and manage directly under its master/worker mode. After enabling master/worker mode by either including the -Ws flag on the command line or adding a master-worker directive to the global section of the HAProxy configuration, you can tell HAProxy to start external programs by using the following syntax:

program <name>
command </path/to/executable> [args]

For example, to have HAProxy handle the start up of the Data Plane API, you would add it as a command in the program section, like this:

program dataplane-api
command /usr/sbin/dataplaneapi --host 0.0.0.0 --port 5555 --haproxy-bin /usr/sbin/haproxy --config-file /etc/haproxy/haproxy.cfg --reload-cmd "systemctl reload haproxy" --reload-delay 5 --userlist api

You can view a list of running commands by issuing a show proc command to the Runtime API:

$ echo "show proc" | socat /var/run/haproxy.master.sock -
#<PID> <type> <relative PID> <reloads> <uptime>
6393 master 0 0 10d 03h02m01s
# workers
6396 worker 1 0 10d 03h02m01s
# programs
6394 dataplane-api - 0 10d 03h02m01s
6395 spoa-mirror - 0 10d 03h02m01s

Polyglot Extensibility

The Stream Processing Offload Engine (SPOE) and Stream Processing Offload Protocol (SPOP) were introduced in HAProxy 1.7. The goal was to create the extension points necessary to build upon HAProxy using any programming language. The initial examples were all C based. Over time, the community saw a need to show how SPOE can be extended in any language and a variety of libraries and examples were contributed. This opens the door to as many developers as possible.

In collaboration with our community, we’re excited to announce that libraries and examples are available in the following languages and platforms:

Traffic Shadowing

Traffic shadowing, or mirroring, allows you to mirror requests from one environment to another. This is helpful in instances where you would like to send a percentage of production traffic to a testing or staging environment to vet a release before it’s fully deployed. The new Traffic Shadowing daemon is written as a Stream Processing Offload Agent (SPOA) and takes advantage of HAProxy’s SPOE, which allows you to extend HAProxy using any programming language.

The Traffic Shadowing SPOA can be launched and managed using the Process Manager, as shown:

program spoa-mirror
command /usr/sbin/spoa-mirror -r0 -u"http://staging.local/"
frontend fe_main
bind :80
filter spoe engine traffic-mirror config mirror.cfg
default_backend be_main
backend be_main
server server1 192.168.1.13:80
backend spoe-traffic-mirror
mode tcp
balance roundrobin
timeout connect 5s
timeout server 1m
server spoa1 127.0.0.1:12345

Above, we specified config mirror.cfg on the filter spoe line. Here is an example of how mirror.cfg would look:

[traffic-mirror]
spoe-agent spoe-traffic-mirror
log global
messages mirror
option set-on-error err
option set-process-time ptime
option set-total-time ttime
option var-prefix spoe
timeout hello 500ms
timeout idle 10s
timeout processing 100ms
use-backend spoe-traffic-mirror
spoe-message mirror
args arg_method=method arg_path=url arg_ver=req.ver arg_hdrs=req.hdrs_bin arg_body=req.body
# 10% chance to mirror traffic
event on-frontend-http-request if { rand(100) le 10 }

Kubernetes Ingress Controller

kubernetes ingress controller

Since February 2017, an HAProxy Ingress Controller for Kubernetes has been provided by community contributor, Joao Morais. HAProxy Technologies contributed features, such as adding DNS service discovery and watched the evolution of the project. There’s a need, however, for a community-driven project that’s developed jointly by HAProxy Technologies.

The new HAProxy Kubernetes Ingress Controller provides a high-performance ingress for your Kubernetes-hosted applications. It supports TLS offloading, Layer 7 routing, rate limiting, whitelisting, and the best-in-class performance that HAProxy is renowned for. Ingresses can be configured through either ConfigMap resources or annotations and there’s support for defining secrets for storing TLS certificates.

Prometheus Exporter

HAProxy now has native support for exposing metrics to Prometheus. Prometheus is an open-source systems monitoring and alerting toolkit that was originally built at SoundCloud. Its adoption has been widespread and it inspires an active community.

To begin using the Prometheus exporter you must first compile HAProxy with the component by using the EXTRA_OBJS variable. An example make command would be:

$ make TARGET=linux2628 EXTRA_OBJS="contrib/prometheus-exporter/service-prometheus.o"

Activate the exporter within your HAProxy configuration by adding an http-request use-service directive, like so:

frontend stats
bind *:8404
# Enable Prometheus Exporter
http-request use-service prometheus-exporter if { path /metrics }
stats enable
stats uri /stats
stats refresh 10s

Read more about the Prometheus integration on the blog post: HAProxy Exposes a Prometheus Metrics Endpoint.

Peers & Stick Tables Improvements

HAProxy allows the propagation of stick table data to other HAProxy nodes using the Peers Protocol. HAProxy 2.0 introduces several improvements to the Peers Protocol including:

  • Heartbeat

  • Stick tables in peers sections

  • SSL support

  • Runtime API command: show peers

  • New stick table counters

  • New stick table data type, server_name

A node now sends a heartbeat message to its peers after a three-second period of inactivity. If there isn’t any activity within a five-second period, the peer is considered dead, the connection is closed, and reconnection is attempted.

The peers section has been expanded to allow using the binddefault-bindserver, and default-server configuration directives. It also now supports having stick tables directly within itself. This means that you no longer need to use dummy backends, as previously recommended when dealing with many different stick tables.

In the following example, we define a stick table directly inside a peers section and encrypt traffic between nodes using SSL:

peers mypeers
bind :10001 ssl crt mycerts/pem
default-server ssl verify none
server haproxy2 192.168.1.24:10000
server haproxy1 #local peer
table src_tracking type string size 10m store http_req_rate(10s),http_req_cnt

Within a frontend you would then specify the following:

frontend fe_main
http-request track-sc0 src table mypeers/src_tracking

Using the Runtime API, you can now get information about the various peers connections using show peers.

$ echo "show peers" | socat /var/run/haproxy.sock -
0xc04aa0: [13/May/2019:02:26:04] id=mypeers state=0 flags=0x3 resync_timeout=<PAST> task_calls=30
0xc068a0: id=haproxy2(remote) addr=192.168.1.24:10000 status=CONN reconnect=3s confirm=0 flags=0x0
0xc06780: id=haproxy1(local) addr=192.168.1.14:10001 status=NONE reconnect=<NEVER> confirm=0 flags=0x0

The stick table counters gpc1 and gpc1_rate are additional, general-purpose counters that can be incremented using configuration logic. A new stick table data type, server_name, was added. It functions the same as server_id except that the server’s name is exchanged over the wire in addition to its ID. To learn how to take advantage of stick tables, check out our blog post: Introduction to HAProxy Stick Tables.

Power of Two Random Choices Algorithm

In the 1.9 release, a new load-balancing algorithm was added called random. It chooses a random number as the key for the consistent hashing function. Random load balancing can be useful with large farms or when servers are frequently added or removed, as it may avoid the hammering effect that could result from roundrobin or leastconn in this situation. It also respects server weights and dynamic weight changes and server additions take effect immediately.

The hash-balance-factor directive can be used to further improve the fairness of the load balancing, especially in situations where servers show highly variable response times.

When setting balance to random, the argument <draws> indicates that HAProxy should draw that many random servers and then select the one that is least loaded. Drawing two servers even has a name: the Power of Two Random Choices algorithm.

Specify Power of Two load balancing within your backend as follows:

backend be_main
balance random(2)
default-server ssl verify none alpn h2 check maxconn 50
server server1 192.168.1.13:443 tfo
server server2 192.168.1.14:443 tfo
server server3 192.168.1.15:443 tfo
server server4 192.168.1.16:443 tfo
server server5 192.168.1.17:443 tfo

You can read more about this in our blog post Test Driving “Power of Two Random Choices” Load Balancing.

Log Distribution & Sampling

When dealing with a high volume of logs, sampling can be extremely beneficial, giving you a random insight into the traffic. Typically, this sampling would need to be performed by a syslog server such as rsyslog. With HAProxy 2.0, you can now do sampling directly within HAProxy by using the log directive’s sample parameter. Multiple log and sample directives can be specified simultaneously.

To get started, configure logging as follows:

log stderr local0
log 127.0.0.1:10001 sample 1:10 local0
log 127.0.0.2:10002 sample 2-3,8-11:11 local0

The first log line configures all local0 logs to be sent to stderr. The second log line configures logging to 127.0.0.1:10001 at a sampled rate. One out of 10 requests would be logged to this source. Sending 100 requests while incrementing the URL parameter i results in the following log entries:

May 13 11:37:40 localhost haproxy[16129]: 192.168.1.3:40624 [13/May/2019:11:37:40.518] fe_main be_main/server2 0/0/0/0/0 200 191 - - ---- 1/1/0/0/0 0/0 "GET /?i=10 HTTP/1.1"
May 13 11:37:40 localhost haproxy[16129]: 192.168.1.3:40644 [13/May/2019:11:37:40.611] fe_main be_main/server2 0/0/0/1/1 200 191 - - ---- 1/1/0/0/0 0/0 "GET /?i=20 HTTP/1.1"
May 13 11:37:40 localhost haproxy[16129]: 192.168.1.3:40664 [13/May/2019:11:37:40.724] fe_main be_main/server2 0/0/0/0/0 200 191 - - ---- 1/1/0/0/0 0/0 "GET /?i=30 HTTP/1.1"
May 13 11:37:40 localhost haproxy[16129]: 192.168.1.3:40684 [13/May/2019:11:37:40.831] fe_main be_main/server2 0/0/0/0/0 200 191 - - ---- 1/1/0/0/0 0/0 "GET /?i=40 HTTP/1.1"
May 13 11:37:40 localhost haproxy[16129]: 192.168.1.3:40704 [13/May/2019:11:37:40.959] fe_main be_main/server2 0/0/0/1/1 200 191 - - ---- 1/1/0/0/0 0/0 "GET /?i=50 HTTP/1.1"

The third log line configures logging to 127.0.0.2 on port 10002 at a sampled rate. For every 11 requests, it will log requests 2, 3, and 8-11. Sending 100 requests while incrementing the URL parameter i results in the following log entries:

May 13 15:13:06 localhost haproxy[27579]: 192.168.1.3:41516 [13/May/2019:15:13:06.134] fe_main be_main/server1 0/0/0/0/0 200 2077 - - ---- 1/1/0/0/0 0/0 "GET /?id=2 HTTP/1.1"
May 13 15:13:06 localhost haproxy[27579]: 192.168.1.3:41518 [13/May/2019:15:13:06.145] fe_main be_main/server2 0/0/0/1/1 200 191 - - ---- 1/1/0/0/0 0/0 "GET /?id=3 HTTP/1.1"
May 13 15:13:06 localhost haproxy[27579]: 192.168.1.3:41528 [13/May/2019:15:13:06.201] fe_main be_main/server1 0/0/0/1/1 200 2077 - - ---- 1/1/0/0/0 0/0 "GET /?id=8 HTTP/1.1"
May 13 15:13:06 localhost haproxy[27579]: 192.168.1.3:41530 [13/May/2019:15:13:06.212] fe_main be_main/server2 0/0/0/2/2 200 191 - - ---- 1/1/0/0/0 0/0 "GET /?id=9 HTTP/1.1"
May 13 15:13:06 localhost haproxy[27579]: 192.168.1.3:41532 [13/May/2019:15:13:06.222] fe_main be_main/server1 0/0/0/1/1 200 2077 - - ---- 1/1/0/0/0 0/0 "GET /?id=10 HTTP/1.1"
May 13 15:13:06 localhost haproxy[27579]: 192.168.1.3:41534 [13/May/2019:15:13:06.232] fe_main be_main/server2 0/0/0/2/2 200 192 - - ---- 1/1/0/0/0 0/0 "GET /?id=11 HTTP/1.1"

Built-in Automatic Profiling

HAProxy now features the profiling.tasks directive, which can be specified in the global section. It takes the parameters autoon, or off. It defaults to auto.

When set to auto, the profiling automatically switches on when the process starts to suffer from an average latency of 1000 microseconds or higher, as reported in the avg_loop_us activity field and automatically turns off when the latency returns below 990 microseconds. This value is an average over the last 1024 loops. So, it does not vary quickly and tends to smooth out short spikes. It may also spontaneously trigger from time to time on overloaded systems, containers, or virtual machines, or when the system swaps—which must absolutely never happen on a load balancer.

To view the activity you can use the show activity Runtime API command, as shown:

$ echo "show activity" | socat /var/run/haproxy.sock -
thread_id: 1 (1..4)
date_now: 1557729853.190497
loops: 4306 1405 4235 1601
wake_cache: 1756 134 1724 215
wake_tasks: 638 113 618 191
wake_signal: 0 0 0 0
poll_exp: 2394 247 2342 406
poll_drop: 588 49 578 91
poll_dead: 0 0 0 0
poll_skip: 0 0 0 0
fd_skip: 0 0 0 0
fd_lock: 4 1 1 3
fd_del: 0 0 0 0
conn_dead: 0 0 0 0
stream: 108 116 85 199
empty_rq: 2224 84 2201 113
long_rq: 0 0 0 0
ctxsw: 1470 400 1376 686
tasksw: 1400 342 1333 586
cpust_ms_tot: 0 0 0 0
cpust_ms_1s: 0 0 0 0
cpust_ms_15s: 0 0 0 0
avg_loop_us: 36 17 44 24
accepted: 4 13 9 8
accq_pushed: 10 8 8 8
accq_full: 0 0 0 0
accq_ring: 0 0 0 0

To view the status of profiling, use the show profiling Runtime API command:

$ echo "show profiling" |socat /var/run/haproxy.sock -
Per-task CPU profiling : auto # set profiling tasks {on|auto|off}

Profiling exposes the following fetches, which can be captured in the HAProxy log:

Fetch method

Description

date_us

The microseconds part of the date.

cpu_calls

The number of calls to the task processing the stream or current request since it was allocated. It is reset for each new request on the same connection.

cpunsavg

The average number of nanoseconds spent in each call to the task processing the stream or current request.

cpunstot

The total number of nanoseconds spent in each call to the task processing the stream or current request.

latnsavg

The average number of nanoseconds spent between the moment the task handling the stream is woken up and the moment it is effectively called.

latnstot

The total number of nanoseconds between the moment the task handling the stream is woken up and the moment it is effectively called.

To use these in the logs, you would either extend the default HTTP log-format, like so:

log-format "%ci:%cp [%tr] %ft %b/%s %TR/%Tw/%Tc/%Tr/%Ta %ST %B %CC %CS %tsc %ac/%fc/%bc/%sc/%rc %sq/%bq %hr %hs {cpu_calls:%[cpu_calls]|cpu_ns_tot:%[cpu_ns_tot]| cpu_ns_avg:%[cpu_ns_avg]|lat_ns_tot:%[lat_ns_tot]|lat_ns_avg:%[lat_ns_avg]} %{+Q}r"

Or, extend the default TCP log-format:

log-format "%ci:%cp [%t] %ft %b/%s %Tw/%Tc/%Tt %B %ts %ac/%fc/%bc/%sc/%rc %sq/%bq {cpu_calls:%[cpu_calls]|cpu_ns_tot:%[cpu_ns_tot]| cpu_ns_avg:%[cpu_ns_avg]|lat_ns_tot:%[lat_ns_tot]|lat_ns_avg:%[lat_ns_avg]}"

Enhanced TCP Fast Open

HAProxy now has end-to-end support for TCP Fast Open (TFO), enabling clients to send a request and receive a response during the TCP three-way handshake. The benefit of this is that you save one round-trip after the first connection.

HAProxy has supported TFO on the frontend since version 1.5. Version 2.0 enhances this by adding TFO for connections to backend servers on systems that support it. This requires Linux kernel 4.11 or newer. Add the tfo parameter to a server line.

frontend fe_main
bind :443 tfo ssl crt /etc/haproxy/certs/www.example.com.pem alpn h2,http/1.1
default_backend be_main
backend be_main
default-server ssl verify none alpn h2 check maxconn 20
retry-on all-retryable-errors
http-request disable-l7-retry if METH_POST
server server1 192.168.1.13:443 tfo
server server2 192.168.1.14:443 tfo

Be sure to enable retries with the retry-on directive or the request won’t be retried on failure.

New Request Actions

As a part of this release, several new http-request and tcp-request actions were introduced. Here is a breakdown of these new actions with their descriptions.

Action

Description

http-request do-resolve(<var>,<resolvers>,[ipv4,ipv6]) <expr>

Performs DNS resolution of the output of <expr> and stores the result in the variable <var>. It uses the DNS resolvers section pointed to by <resolvers>. It is possible to choose a resolution preference by using the optional arguments ipv4 or ipv6.

http-request disable-l7-retry

Disables any attempt to retry the request if it fails for any reason other than a connection failure. This can be useful, for example, to make sure POST requests aren’t retried upon failure.

tcp-request content do-resolve(<var>,<resolvers>,[ipv4,ipv6]) <expr>

Performs DNS resolution of the output of <expr> and stores the result in the variable <var>. It uses the DNS resolvers section pointed to by <resolvers>. It is possible to choose a resolution preference by using the optional arguments ipv4 or ipv6.

tcp-request content set-dst <expr>

Used to set the destination IP address to the value of the specified expression.

tcp-request content set-dst-port <expr>

Used to set the destination port to the value of the specified expression.

http-request replace-uri <match-regex> <replace-fmt>

This matches the regular expression in the URI part of the request according to <match-regex> and replaces it with the <replace-fmt> argument.

The http-request do-resolve and tcp-request do-resolve warrant further explanation. They allow you to resolve a DNS hostname and store the result in an HAProxy variable. Consider the following example:

frontend fe_main
bind :80
http-request do-resolve(txn.dstip,mydns) hdr(Host),lower
http-request capture var(txn.dstip) len 40
# return 503 when the variable is not set,
# which mean DNS resolution error
use_backend be_503 unless { var(txn.dstip) -m found }
default_backend be_main
backend be_503
# dummy backend used to return 503.
# You can use the 'errorfile' directive to send a nice
# 503 error page to end users.
errorfile 503 /etc/haproxy/errorfiles/503sorry.http
backend be_main
# rule to prevent HAProxy from reconnecting to services
# on the local network (forged DNS name used to scan the network)
http-request deny if { var(txn.dstip) -m ip 127.0.0.0/8 10.0.0.0/8 }
http-request set-dst var(txn.dstip)
server clear 0.0.0.0:80

Here, we’re using http-request do-resolve to perform a DNS query on the hostname found in the Host request header. The nameserver(s) referenced in the mydns resolvers section (not shown) will return the IP address associated with that hostname and HAProxy will then store it in the variable txn.dstip. The http-request set-dst line in the be_main backend updates the server address with this variable.

This is beneficial in split horizon DNS environments, wherein the DNS server will return different results, such as publicly routable or internal-only addresses, depending on the client’s, which is the load balancer’s, source IP address. So, you could have Dev and Prod load balancers that receive different DNS records when they call do-resolve. This is much more dynamic than the at-runtime DNS resolution available in HAProxy (i.e. using the resolvers parameter on the server line), which is typically set to hold onto a DNS result for a period of time. As such, it’s also suitable for other scenarios involving highly dynamic environments, such as where upstream servers are ephemeral.

New Converters

Converters allow you to transform data within HAProxy and are usually followed after a fetch. The following converters have been added to HAProxy 2.0:

Converter

Description

aesgcmdec

Decrypts the raw byte input using the AES128-GCM, AES192-GCM, or AES256-GCM algorithm.

protobuf

Extracts the raw field of an input binary sample representation of a Protocol Buffers message.

ungrpc

Extracts the raw field of an input binary sample representation of a gRPC message.

New Fetches

Fetches in HAProxy provide a source of information from either an internal state or from layers 4, 5, 6, and 7. New fetches that you can expect to see in this release include:

Fetch

Description

sslfcclient_random

Returns the client random of the front connection when the incoming connection was made over an SSL/TLS transport layer. It is useful to decrypt traffic sent using ephemeral ciphers. This requires OpenSSL >= 1.1.0, or BoringSSL.+

sslfcserver_random

Returns the server random of the front connection when the incoming connection was made over an SSL/TLS transport layer. It is useful to decrypt traffic sent using ephemeral ciphers. This requires OpenSSL >= 1.1.0, or BoringSSL.

slbcclient_random

Returns the client random of the back connection when the incoming connection was made over an SSL/TLS transport layer. It is useful to decrypt traffic sent using ephemeral ciphers. This requires OpenSSL >= 1.1.0, or BoringSSL.

sslbcserver_random

Returns the server random of the back connection when the incoming connection was made over an SSL/TLS transport layer. It is useful to decrypt traffic sent using ephemeral ciphers. This requires OpenSSL >= 1.1.0, or BoringSSL.

Miscellaneous Improvements

The following miscellaneous improvements have been made:

  • SSL/TLS Ticket Keys

    • TLS session tickets help to speed up session resumption for clients that support them. HAProxy 2.0 adds support for AES256-bit ticket keys specified in both a file or through the Runtime API.

  • Core Dump – Ease of Use

    • A new global directive set-dumpable has been added, which aids in enabling core dumps. It’s been known to be a pain to get a core dump when enabling the user and group settings, which disables the dumpable flag on Linux, when using a chroot and/or when HAProxy is started by a service management tool that requires complex operations to just raise the core dump limit. This directive makes it much easier to retrieve a core file.

  • SOCKS4 Support

    • Introduces 2 new server keywords: socks4 and check-via-socks4 which can be used for communicating with servers within a backend over SOCKS4 and adds similar functionality for health checking over SOCKS4.

LTS Support for 1.9 Features

HAProxy 2.0 bring LTS support for the aforementioned features, as well as the following features that were introduced or improved upon during the 1.9 release:

  • Small Object Cache with an increased caching size of up to 2GB, set with the max-object-size directive. The total-max-size setting determines the total size of the cache and can be increased up to 4095MB.

  • New fetches that report either an internal state or from layer 4, 5, 6, and 7.

  • New converters that allow you to transform data within HAProxy.

  • HTTP 103 (Early Hints), which asks the browser to preload resources.

  • Server Queue Priority Control, which lets you prioritize some queued connections over others.

  • Connection pooling to backend servers.

  • The resolvers section supports using resolv.conf by specifying parse-resolv-conf.

  • The busy-polling directive allows the reduction of request processing latency by 30-100 microseconds on machines using frequency scaling or supporting deep idle states.

  • Lua

    • The Server class gained the ability to change a server’s maxconn value.

    • The TXN class gained the ability to adjust a connection’s priority within the server queue.

    • There is a new StickTable class that allows access to the content of a stick-table by key and allows the dumping of content.

  • Regression testing of the HAProxy code using varnishtest.

HAProxy 2.1 Preview

HAProxy 2.1 will build on the foundation that has been laid in HAProxy 1.9 and 2.0. Some of the exciting features planned are:

  • FastCGI

  • Dynamic SSL Certificate Updates

  • Prometheus exporter improvements

Conclusion

HAProxy remains at the forefront of performance and innovation because of the commitment of the open-source community and the staff at HAProxy Technologies. We’re excited to bring you this news of the 2.0 release! In addition to the features included in this version, it paves the way for many exciting updates, which, with our new release cadence, you’ll see more frequently.

It immediately brings support for end-to-end HTTP/2, gRPC, Layer 7 Retries, traffic shadowing, connection pooling on the server side, a Process Manager, the Power of Two Random Choices Algorithm, and a Prometheus Exporter. Of course, one of the most powerful additions is the new Data Plane API, which allows you to dynamically configure HAProxy using RESTful HTTP calls.

Our enterprise customers have been able to benefit from many of these features for the last few months, as a majority of them have already been backported directly into the HAProxy Enterprise 1.9r1 release. Our philosophy is to provide value to the open-source community first and then rapidly integrate features into Enterprise, which has a focus on stability. You can compare versions on the Community vs Enterprise page.

Keep apprised of the latest news by subscribing to this blog! You can also follow us on Twitter and join us on Slack. Want to learn more about HAProxy Enterprise? Contact us or sign up for a free trial today!

Subscribe to our blog. Get the latest release updates, tutorials, and deep-dives from HAProxy experts.