Skip to content

Commit

Permalink
Add endpoint load-balancing mode
Browse files Browse the repository at this point in the history
This is the heart of the scalability change for services in libnetwork.
The present routing mesh adds load-balancing rules for a network to
every container connected to the network.  This newer approach creates a
load-balancing endpoint per network per node.  For every service on a
network, libnetwork assigns the VIP of the service to the endpoint's
interface as an alias.  This endpoint must have a unique IP address in
order to route return traffic to it.  Traffic destined for a service's
VIP arrives at the load-balancing endpoint on the VIP and from there,
Linux load balances it among backend destinations while SNATing said
traffic to the endpoint's unique IP address.

The net result of this scheme is that each node in a swarm need only
have one set of load balancing state per service instead of one per
container on the node.  This scheme is very similar to how services
currently operate on Windows nodes in libnetwork.  It (as with Windows
nodes) costs the use of extra IP addresses in a network (one per node)
and an extra network hop in the stack, although, always in the stack
local to the container.

In order to prevent existing deployments from suddenly failing if they
failed to allocate sufficient address space to include per-node
load-balancing endpoint IP addresses, this patch preserves the existing
functionality and activates the new functionality on a per-network
basis depending on whether the network has a load-balancing endpoint.
Eventually, moby should always set this option when creating new
networks and should only omit it for networks created as part of a swarm
that are not marked to use endpoint load balancing.

This patch also normalizes the code to treat "load" and "balancer"
as two separate words from the perspectives of variable/function naming.
This means that the 'b' in "balancer" must be capitalized.

Signed-off-by: Chris Telfer <ctelfer@docker.com>
  • Loading branch information
ctelfer committed Jun 28, 2018
1 parent 2166946 commit c47239d
Show file tree
Hide file tree
Showing 8 changed files with 141 additions and 176 deletions.
2 changes: 1 addition & 1 deletion controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -871,7 +871,7 @@ addToStore:
}
}()

if len(network.loadBalancerIP) != 0 {
if network.hasLoadBalancerEndpoint() {
if err = network.createLoadBalancerSandbox(); err != nil {
return nil, err
}
Expand Down
6 changes: 6 additions & 0 deletions endpoint.go
Original file line number Diff line number Diff line change
Expand Up @@ -540,6 +540,12 @@ func (ep *endpoint) sbJoin(sb *sandbox, options ...EndpointOption) (err error) {
}
}()

// Load balancing endpoints should never have a default gateway nor
// should they alter the status of a network's default gateway
if ep.loadBalancer && !sb.ingress {
return nil
}

if sb.needDefaultGW() && sb.getEndpointInGWNetwork() == nil {
return sb.setupDefaultGW()
}
Expand Down
21 changes: 13 additions & 8 deletions network.go
Original file line number Diff line number Diff line change
Expand Up @@ -997,8 +997,8 @@ func (n *network) delete(force bool, rmLBEndpoint bool) error {
}

// Check that the network is empty
var emptyCount uint64 = 0
if len(n.loadBalancerIP) != 0 {
var emptyCount uint64
if n.hasLoadBalancerEndpoint() {
emptyCount = 1
}
if !force && n.getEpCnt().EndpointCnt() > emptyCount {
Expand All @@ -1008,7 +1008,7 @@ func (n *network) delete(force bool, rmLBEndpoint bool) error {
return &ActiveEndpointsError{name: n.name, id: n.id}
}

if len(n.loadBalancerIP) != 0 {
if n.hasLoadBalancerEndpoint() {
// If we got to this point, then the following must hold:
// * force is true OR endpoint count == 1
if err := n.deleteLoadBalancerSandbox(); err != nil {
Expand Down Expand Up @@ -1077,9 +1077,6 @@ func (n *network) delete(force bool, rmLBEndpoint bool) error {
// Cleanup the service discovery for this network
c.cleanupServiceDiscovery(n.ID())

// Cleanup the load balancer
c.cleanupServiceBindings(n.ID())

removeFromStore:
// deleteFromStore performs an atomic delete operation and the
// network.epCnt will help prevent any possible
Expand Down Expand Up @@ -1931,6 +1928,10 @@ func (n *network) hasSpecialDriver() bool {
return n.Type() == "host" || n.Type() == "null"
}

func (n *network) hasLoadBalancerEndpoint() bool {
return len(n.loadBalancerIP) != 0
}

func (n *network) ResolveName(req string, ipType int) ([]net.IP, bool) {
var ipv6Miss bool

Expand Down Expand Up @@ -2111,9 +2112,9 @@ func (c *controller) getConfigNetwork(name string) (*network, error) {
}

func (n *network) lbSandboxName() string {
name := n.name + "-sbox"
name := "lb-" + n.name
if n.ingress {
name = "lb-" + n.name
name = n.name + "-sbox"
}
return name
}
Expand Down Expand Up @@ -2145,6 +2146,10 @@ func (n *network) createLoadBalancerSandbox() (retErr error) {
CreateOptionIpam(n.loadBalancerIP, nil, nil, nil),
CreateOptionLoadBalancer(),
}
if n.hasLoadBalancerEndpoint() && !n.ingress {
// Mark LB endpoints as anonymous so they don't show up in DNS
epOptions = append(epOptions, CreateOptionAnonymous())
}
ep, err := n.createEndpoint(endpointName, epOptions...)
if err != nil {
return err
Expand Down
18 changes: 1 addition & 17 deletions sandbox.go
Original file line number Diff line number Diff line change
Expand Up @@ -740,16 +740,8 @@ func releaseOSSboxResources(osSbox osl.Sandbox, ep *endpoint) {

ep.Lock()
joinInfo := ep.joinInfo
vip := ep.virtualIP
ep.Unlock()

if len(vip) != 0 {
loopName := osSbox.GetLoopbackIfaceName()
if err := osSbox.RemoveAliasIP(loopName, &net.IPNet{IP: vip, Mask: net.CIDRMask(32, 32)}); err != nil {
logrus.Warnf("Remove virtual IP %v failed: %v", vip, err)
}
}

if joinInfo == nil {
return
}
Expand Down Expand Up @@ -862,14 +854,6 @@ func (sb *sandbox) populateNetworkResources(ep *endpoint) error {
}
}

if len(ep.virtualIP) != 0 {
loopName := sb.osSbox.GetLoopbackIfaceName()
err := sb.osSbox.AddAliasIP(loopName, &net.IPNet{IP: ep.virtualIP, Mask: net.CIDRMask(32, 32)})
if err != nil {
return fmt.Errorf("failed to add virtual IP %v: %v", ep.virtualIP, err)
}
}

if joinInfo != nil {
// Set up non-interface routes.
for _, r := range joinInfo.StaticRoutes {
Expand All @@ -895,7 +879,7 @@ func (sb *sandbox) populateNetworkResources(ep *endpoint) error {
// information including gateway and other routes so that
// loadbalancers are populated all the network state is in
// place in the sandbox.
sb.populateLoadbalancers(ep)
sb.populateLoadBalancers(ep)

// Only update the store if we did not come here as part of
// sandbox delete. If we came here as part of delete then do
Expand Down
17 changes: 9 additions & 8 deletions service_common.go
Original file line number Diff line number Diff line change
Expand Up @@ -289,8 +289,7 @@ func (c *controller) addServiceBinding(svcName, svcID, nID, eID, containerName s
logrus.Warnf("addServiceBinding %s possible transient state ok:%t entries:%d set:%t %s", eID, ok, entries, b, setStr)
}

// Add loadbalancer service and backend in all sandboxes in
// the network only if vip is valid.
// Add loadbalancer service and backend to the network
n.(*network).addLBBackend(ip, lb)

// Add the appropriate name resolutions
Expand All @@ -305,11 +304,6 @@ func (c *controller) rmServiceBinding(svcName, svcID, nID, eID, containerName st

var rmService bool

n, err := c.NetworkByID(nID)
if err != nil {
return err
}

skey := serviceKey{
id: svcID,
ports: portConfigs(ingressPorts).String(),
Expand Down Expand Up @@ -367,7 +361,14 @@ func (c *controller) rmServiceBinding(svcName, svcID, nID, eID, containerName st
// Remove loadbalancer service(if needed) and backend in all
// sandboxes in the network only if the vip is valid.
if entries == 0 {
n.(*network).rmLBBackend(ip, lb, rmService, fullRemove)
// The network may well have been deleted before the last
// of the service bindings. That's ok, because removing
// the network sandbox implicitly removes the backend
// service bindings.
n, err := c.NetworkByID(nID)
if err == nil {
n.(*network).rmLBBackend(ip, lb, rmService, fullRemove)
}
}

// Delete the name resolutions
Expand Down
Loading

0 comments on commit c47239d

Please sign in to comment.