Моя шпаргалка :)

Мануалы и настройки => Настройки *nix (почта, web, система etc) => Тема начата: George от Сен. 17, 2022, 09:52

Название: How to Map Domain Names to Backend Server Pools with HAProxy
Отправлено: George от Сен. 17, 2022, 09:52
our HAProxy load balancer may only ever need to relay traffic for a single domain name, but HAProxy can handle two, ten, or even ten million routing rules without breaking a sweat. This article shows several ways of handling multi-domain configurations, including an introduction to using HAProxy maps.There's a range of techniques explained here, whether you are deploying a simple, fairly-static multi-domain web server, or leveraging HAProxy's more advanced features to tame the configuration and management of a dynamic API Gateway, or anything in between.

Access Control List Mapping
If your HAProxy is already serving multiple domains, you're probably familiar with using Access Control Lists (ACLs) in your frontend declaration. This works well, but once you find yourself writing ACLs for many domains, it can become difficult to maintain.

Here's how you might write ACLs for two domains, example.com and example.net:

frontend default
  bind :80

  # ACL for "example.com" and "www.example.com"
  acl ACL_example.com hdr(host) -i example.com www.example.com
  use_backend be_example.com if ACL_example.com

  # ACL for "example.net"
  acl ACL_example.net hdr(host) -i example.net
  use_backend be_example.net if ACL_example.net
view raw
Let's take the second ACL as an example. It breaks down as follows:

acl   ACL definition begins with acl.
ACL_example.net   The name of the ACL. The name is arbitrary, but it's good form to make them understandable.
hdr(host) -i example.net www.example.net   This says that this rule will match any request that has an HTTP Host header of
example.net or www.example.net.

Once the ACL is set, define what to do with it:

use_backend be_example_net if ACL_example.net
view raw
This tells HAProxy to send any matching requests to a backend named be_example_net.
This is a good approach for a small number of domains, if they are fairly static, but what happens when you need it to handle tens of thousands of domains mapped to multiple backends,and you need to change them dynamically?

Direct Mapping
One strategy is to simply create a backend with the same name as your incoming domain names and use this use_backend directive in your frontend:
use_backend be_example_net if ACL_example.net
view raw
frontend fe_main
  bind :80
  # If Host header is api.example.com then use
  # api.example.com backend
  use_backend %[req.hdr(Host),lower]
view raw
Above, %[req.hdr(host)] is replaced with the incoming host header, and forced to lowercase with lower. Therefore, if a request comes in for api.example.com, it will be sent to this backend:

backend api.example.com
  balance            roundrobin
  server api1    127.0.0.1:8080 check
  server api2    127.0.0.1:8081 check
view raw
Please note that the incoming host header variable includes any port explicitly specified, so incoming requests for example.com:6666 would be sent to a backend named backend example.com:6666, which may or may not exist. To strip the port, use:
use_backend %[req.hdr(host),lower,word(1,:)]

HAProxy Maps
If you need something more flexible and dynamic than ACLs or Direct Mapping, take a look at HAProxy maps. A map is an in-memory key/value data structure of a type known as an Elastic Binary Tree that is loaded at startup from a text file that you specify in your HAProxy configuration file. These maps are highly-optimized search tree structures that allow for incredibly fast lookups of the data stored within. The format of the text file used to create the map is quite simple: two columns, separated with one or more spaces or tabs. Comment lines begin with a hash (#) and must be on their own line.

#domainname           backendname
example.com           be_default
example.net           be_default
api.example.com       be_api
api1.example.net      be_api1
api2.example.com      be_api2
# [...]
api10000.example.com  be_api
view raw
To have HAProxy load this file, save it as /etc/haproxy/maps/hosts.map and then add the following line to your frontend config:

frontend default
   bind :80
   use_backend %[req.hdr(host),lower,map_dom(/etc/haproxy/maps/hosts.map,be_default)]
view raw
When using an ACL for this task, there was a line that looked like this:

use_backend be_example.com if ACL_example.com
view raw
But when using a map, the use_backend line gets a little more complicated, so let's break it down. The directive use_backend is the same, but the second part within the square brackets is as follows:

req.hdr(host) is the Host header that contains the domain part of the URL. This is the key that we look up in the map.
Using lower forces the Host header value to lowercase, turning "EXAMPLE.COM" into "example.com" to simplify matches.
map_dom(/path/to/map,be_default)

The map_dom function takes two arguments, the first being the location of the map and the second being the default backend to use if an incoming Host header isn't in the map file.

In addition to mapping domain names to backends, you can also use a similar technique to map URL paths to backends. This simplifies some of the complexities of designing and maintaining the type of routing explained in Using HAProxy as an API Gateway, Part 1 [Introduction].
To map paths to backends using a map, create a use_backend line that uses the path fetch method to get the URL path and the map_beg converter to find that path in the map file.

frontend www
  bind :80
  use_backend %[path,map_beg(/etc/haproxy/maps/routes.map,be_default)]
view raw
Your route map, located at /etc/haproxy/maps/routes.map in the line above, might look like this:

/api    be_api
/login  be_auth
view raw
Routes that aren't listed in the map are sent to the default backend, as well as requests for routes in the map that point to nonexistent or unavailable backends.

Modifying Maps
You have several options for modifying your maps, depending upon your use case.

Manual Update
The simplest method is to edit the map file in a text editor and do a hitless reload of HAProxy. This is a direct way of making a change, but it's a manual process. Using hitless reloads means that you won't lose any connections while it is reloading.

HAProxy Runtime API
You can directly interact with a running instance of HAProxy by using HAProxy's Runtime API. To do this, first ensure that a socket has been defined in the global section of your configuration file:
stats socket /var/run/haproxy.sock
  user haproxy group haproxy mode 660 level admin expose-fd listeners
view raw
Test your socket using socat by piping echo "help" at it:
$ echo "help" | sudo socat stdio /var/run/haproxy.sock | grep map
view raw
This will produce the following output, which shows you several map functions:
add map       : add map entry
clear map <id> : clear the content of this map
del map       : delete map entry
get map       : report the keys and values matching a sample for a map
set map       : modify map entry
show map [id] : report available maps or dump a map's contents
view raw
For example, to add a mapping for a domain and backend pairing, use:

$ echo "add map /etc/haproxy/maps/hosts.map api.example.com be_api" | socat stdio /var/run/haproxy.sock
view raw
Deleting a map entry with the Runtime API is just as straightforward:

$ echo "del map /etc/hapee-2.1/hosts.map api.example.com" | socat  stdio /var/run/haproxy.sock
view raw
Note: Changes to the running instance made using this API are not written to disk and will be lost if you do a restart or reload of HAProxy.

HAProxy Data Plane API
Maps can also be managed using a RESTful interface, using the HAProxy Data Plane API. Unlike the Runtime API, with the Data Plane API, changes are written to disk.
Automatic Dynamic Updates via URL
HAProxy Enterprise can set maps dynamically via URL, checking for changes at an interval you specify:

dynamic-update
  update id /etc/hapee-2.1/maps/domains.map url http://10.0.0.1/domains.map delay 300s
view raw
This is ideal if you run a cluster of HAProxy Enterprise instances, since their map files can all be kept in sync with the file you host at the configured URL.

Conclusion