1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325
|
# $OpenLDAP$
# Copyright 2021-2024 The OpenLDAP Foundation, All Rights Reserved.
# COPYING RESTRICTIONS APPLY, see COPYRIGHT.
H1: Load Balancing with lloadd
As covered in the {{SECT:Replication}} chapter, replication is a fundamental
requirement for delivering a resilient enterprise deployment. As such
there's a need for an LDAPv3 capable load balancer to spread the load between the
various directory instances.
{{lloadd}}(8) provides the capability to distribute LDAP v3 requests between a
set of running {{slapd}} instances. It can run as a standalone daemon
{{lloadd}}, or as an embedded module running inside of {{slapd}}.
H2: Overview
{{lloadd}}(8) was designed to handle LDAP loads.
It is protocol-aware and can balance LDAP loads on a per-operation basis rather
than on a per-connection basis.
{{lloadd}}(8) distributes the load across a set of slapd instances. The client
connects to the load balancer instance which forwards the request to one
of the servers and returns the response back to the client.
H2: When to use the OpenLDAP load balancer
In general, the OpenLDAP load balancer spreads the load across configured backend servers. It does not perform
so-called intelligent routing. It does not understand semantics behind the operations being performed by the clients.
More considerations:
- Servers are indistinguishable with respect to data contents. The exact same copy of data resides on every server.
- The sequence of operations isn't important. For example, read after update isn't required by the client.
- If your client can handle both connection pooling and load distribution then it's preferable to lloadd.
- Clients with different requirements (e.g. a coherent session vs. simple but high traffic clients) are directed to separate lloadd configurations.
H2: Directing operations to backends
H3: Default behaviour
In the simplest configuration several backends would be configured within a single roundrobin tier:
> feature proxyauthz
>
> bindconf bindmethod=simple
> binddn="cn=Manager,dc=example,dc=com"
> credentials=secret
>
> tier roundrobin
> backend-server uri=ldap://server1.example.com
> numconns=5 bindconns=5
> max-pending-ops=10 conn-max-pending=3
> retry=5000
> backend-server uri=ldap://server2.example.com
> numconns=5 bindconns=5
> max-pending-ops=10 conn-max-pending=3
> retry=5000
After startup {{lloadd}} will open 10 connections to each
{{B:ldap://server1.example.com}} and {{B:ldap://server2.example.com}},
5 for regular requests, where it will bind as {{B:cn=Manager,dc=example,dc=com}},
and 5 dedicated to serving client Bind requests. If connection set up fails, it
will wait 5000ms (5 seconds) before making another attempt to that server.
When a new Bind request comes from a client, it will be allocated to one of the
available {{bind connections}}, each of which can only carry one request at a
time. For other requests that need to be passed on to the backends, backends are
considered in order:
* if the number of pending/in-flight for that backend is at or above 10, it is
skipped
* the first appropriate upstream connection is chosen:
** an idle {{bind connection}} for Bind requests
** a {{regular connection}} with less than 3 pending operations for other
types of requests
* if no such connection is available, the next backend in order is checked
* if we go through the whole list without choosing an upstream connection,
we return a failure to the client, either an {{B:LDAP_UNAVAILABLE}} if no
connections of the appropriate type have been established at all or
{{B:LDAP_BUSY}} otherwise
When a connection is chosen, the operation is forwarded and response(s)
returned to the client. Should that connection go away before the final
response is received, the client is notified with a {{B:LDAP_OTHER}} failure
code.
So long as {{feature proxyauthz}} is configured, every operation forwarded over
a {{regular connection}} has the {{B:PROXYAUTHZ}} control ({{REF:RFC4370}})
prepended indicating the client's bound identity, unless that identity matches
the {{binddn}} configured in {{bindconf}}.
If another tier is configured:
> tier roundrobin
> backend-server uri=ldap://fallback.example.com
> numconns=5 bindconns=5
> max-pending-ops=10 conn-max-pending=3
> retry=5000
Backends in this tier will only be considered when {{lloadd}} would have
returned {{B:LDAP_UNAVAILABLE}} in the above case.
H3: Alternate selection strategies
For various reasons, the {{roundrobin}} tier is appropriate in the majority of
use cases as it is both very scalable in terms of its implementation and how
its self-limiting interacts with backends when multiple {{lloadd}} instances
are being used at the same time.
Two alternative selection strategies have been implemented:
- {{tier weighted}} applies predefined weights to how often a backend is
considered first
- {{tier bestof}} measures the time to first response from each backend, when a
new operation needs to be forwarded, two backends are selected at random and
the backend with better response time is considered first. If connections on
neither backend can be used, selection falls back to the regular strategy
used by the roundrobin backend
The {{weighted}} tier might be appropriate when servers have differing load
capacity. Due to its reinforced self-limiting feedback, the {{bestof}} tier
might be appropriate in large scale environments where each backend's
capacity/latency fluctuates widely and rapidly.
H3: Coherence
H4: Write coherence
In default configurations, every operation submitted by the client is either
processed internally (e.g. StartTLS, Abandon, Unbind, ...) or is forwarded to a
connection of lloadd's choosing, independent of any other other operation
submitted by the same client.
There are certain traffic patterns where such such freedom is undesirable and
some kind of coherency is required. This applies to write traffic, controls
like Paged Results or many extended operations.
Client's operations can be pinned to the same backend as the last write
operation:
> write_coherence 5
In this case, client's requests will be passed over to the same backend (not
necessarily over the same upstream connection) from the moment a write request
is passed on till at least 5 seconds have elapsed since last write operation
has finished.
> write_coherence -1
Here, there is no timeout and the moment a write request is passed on to a
backend, the client's operations will forever be passed on to this backend.
In both cases above, this limitation is lifted the moment a Bind request is
received from the client connection.
H4: Extended operations/controls
Many controls and Extended operations establish shared state on the session.
While {{lloadd}} implements some of these (StartTLS being one example), it
supports the administrator in defining how to deal with those it does not
implement special handling for.
> restrict_exop 1.1 reject
> # TXN Exop
> restrict_exop 1.3.6.1.1.21.1 connection
> # Password Modify Exop
> restrict_exop 1.3.6.1.4.1.4203.1.11.1 write
>
> # Paged Results Control
> restrict_control 1.2.840.113556.1.4.319 connection
> # Syncrepl
> restrict_control 1.3.6.1.4.1.4203.1.9.1 reject
The above configuration uses the special invalid OID of {{1.1}} to instruct
{{lloadd}} to reject any Extended operation it does not recognize, except for
Password Modify operation which is treated according to {{write_coherence}}
above and the LDAP transactions, where it forwards all subsequent requests over
to the same upstream connection. Similarly, once a Paged results control is
seen on an operation, subsequent request will stick to the same upstream
connection while LDAP Syncrepl requests will be rejected outright.
With both {{restrict_exop}} and {{restrict_control}}, any such limitation is
lifted when a new Bind request comes in as any client state is assumed to be
reset.
When configuring these to anything else than {{reject}}, keep in mind that many
extensions have not been designed or implemented with a multiplexing proxy like
{{lloadd}} in mind and might open considerable operational and/or security
concerns when allowed.
H2: Runtime configurations
It deploys in one of two ways:
^ Standalone daemon: {{ lloadd }}
+ Loaded into the slapd daemon as a module: {{ lloadd.la }}
It is recommended to run with the balancer module embedded in slapd because dynamic configuration (cn=config) and the monitor backend are then available.
{{B: Sample load balancer scenario:}}
!import "load-balancer-scenario.png"; align="center"; title="Load Balancer Scenario"
FT[align="Center"] Figure: Load balancer sample scenario
^ The LDAP client submits an LDAP operation to
the load balancer daemon.
+ The load balancer forwards the request to one of the backend instances in its pool of servers.
+ The backend slapd server processes the request and returns the response to
the load balancer instance.
+ The load balancer returns the response to the client. The client's unaware that it's connecting to a load balancer instead of slapd.
H2: Build Notes
To build the load balancer from source, follow the instructions in the
{{SECT: A Quick-Start Guide}} substituting the following commands:
^ To configure as standalone daemon:
..{{EX:./configure --enable-balancer=yes}}
+ To configure as embedded module to slapd:
..{{EX:./configure --enable-modules --enable-balancer=mod}}
H2: Sample Runtime
^ To run embedded as {{ lloadd }} module:
..{{EX: slapd [-h URLs] [-f lloadd-config-file] [-u user] [-g group]}}
- the startup is the same as starting the {{ slapd }} daemon.
- URLs is for slapd management. The load balancer's listener URLs set in the configuration file or node. (more later)
+ To run as standalone daemon:
..{{EX: lloadd [-h URLs] [-f lloadd-config-file] [-u user] [-g group]}}
- Other than a different daemon name, running standalone has the same options as starting {{ slapd }}.
- -h URLs specify the lloadd's interface directly, there is no management interface.
For a complete list of options, checkout the man page {{ lloadd.8 }}
H2: Configuring load balancer
H3: Common configuration options
Many of the same configuration options as slapd. For complete list, check
the {{lloadd}}(5) man page.
.{{S: }}
{{B:Edit the slapd.conf or cn=config configuration file}}.
To configure your working {{lloadd}}(8) you need to make the following changes to your configuration file:
^ include {{ core.schema }} (embedded only)
+ {{ TLSShareSlapdCTX { on | off } }}
+ Other common TLS slapd options
+ Setup argsfile/pidfile
+ Setup moduleload path (embedded mode only)
+ {{ moduleload lloadd.la }}
+ loglevel, threads, ACL's
+ {{ backend lload }} begin lloadd specific backend configurations
+ {{ listen ldap://:PORT }} Specify listen port for load balancer
+ {{ feature proxyauthz }} Use the proxy authZ control to forward client's identity
+ {{ io-threads INT }} specify the number of threads to use for the connection manager. The default is 1 and this is typically adequate for up to 16 CPU cores
H3: Sample backend config
Sample setup config for load balancer running in front of four slapd instances.
>backend lload
>
># The Load Balancer manages its own sockets, so they have to be separate
># from the ones slapd manages (as specified with the -h "URLS" option at
># startup).
>listen ldap://:1389
>
># Enable authorization tracking
>feature proxyauthz
>
># Specify the number of threads to use for the connection manager. The default is 1 and this is typically adequate for up to 16 CPU cores.
># The value should be set to a power of 2:
>io-threads 2
>
># If TLS is configured above, use the same context for the Load Balancer
># If using cn=config, this can be set to false and different settings
># can be used for the Load Balancer
>TLSShareSlapdCTX true
>
># Authentication and other options (timeouts) shared between backends.
>bindconf bindmethod=simple
> binddn=dc=example,dc=com credentials=secret
> network-timeout=5
> tls_cacert="/usr/local/etc/openldap/ca.crt"
> tls_cert="/usr/local/etc/openldap/host.crt"
> tls_key="/usr/local/etc/openldap/host.pem"
>
>
># List the backends we should relay operations to, they all have to be
># practically indistinguishable. Only TLS settings can be specified on
># a per-backend basis.
>
>tier roundrobin
>backend-server uri=ldap://ldaphost01 starttls=critical retry=5000
> max-pending-ops=50 conn-max-pending=10
> numconns=10 bindconns=5
>backend-server uri=ldap://ldaphost02 starttls=critical retry=5000
> max-pending-ops=50 conn-max-pending=10
> numconns=10 bindconns=5
>backend-server uri=ldap://ldaphost03 starttls=critical retry=5000
> max-pending-ops=50 conn-max-pending=10
> numconns=10 bindconns=5
>backend-server uri=ldap://ldaphost04 starttls=critical retry=5000
> max-pending-ops=50 conn-max-pending=10
> numconns=10 bindconns=5
>
>#######################################################################
># Monitor database
>#######################################################################
>database monitor
|