When I played around with DNS load balancing in a Docker Compose environment, I initially thought that the Docker DNS server (
127.0.0.11) performed round robin load balancing by shuffling the list of IP addresses when it resolves a host. I assumed that the client (i.e. NGINX in that example) would always select the first IP address from the resolved shuffled IP addresses. This approach means that the client wouldn't need to handle shuffling, caching, and iterating through the host's IP addresses, which achieves a form of "poor man's load balancing".
I shared my assumption with my manager and he offered a different view. He shared Daniel Stenberg's (creator of cURL) article on DNS round robin, which challenged my assumption and led me down a rabbit hole of digging through RFCs and source code. I gleaned the following learnings from Daniel's article:
- Host names can be resolved to IP addresses using the following functions:
getaddressinfo()is newer and supports IPv6, which
getaddressinfo()might sort the list of IP addresses returned by the DNS server. 🤔
- RFC 3484: Default Address Selection for IPv6 was shipped in 2003. Page 9 of the RFC specifies an algorithm for destination address selection (i.e. the resolved IP addresses returned by the DNS server). RFC 3484 would later be superceded by RFC 6724.
getaddressinfo()was written before RFC 3484 was published.
- Notably RFC 3484 Section 6 Rule 9 states that
getaddrinfo()implementations should prefer destination addresses with the longest matching prefix as the source destination. A simplified example would look like this:
- When IPv6 is disabled,
getaddressinfo()returns returns IP addresses in random order, which can be used for poor man's round-robin load balancing.
- When IPv6 is enabled,
getaddressinfo()returns IP addresses in a fixed order (based on RFC 6724 rules?). Round-robin load balancing (where the client selects the first IP address always) no longer works.