Skip to content

Commit

Permalink
add --sort-endpoints-by command-line option
Browse files Browse the repository at this point in the history
Add a few more options on how to sort endpoints of a backend. Currently
`--sort-backends` switches between the same k8s endpoints order and
naming order - which only makes sense if `backend-server-naming` was
changed.

`--sort-endpoints-by` also brings back the old behavior (v0.7 and older)
of missing `--sort-backends`: `random` option will shuffle endpoints of
all backends whenever a haproxy reload is fired.
  • Loading branch information
jcmoraisjr committed Oct 17, 2020
1 parent b9474ac commit 103318c
Show file tree
Hide file tree
Showing 7 changed files with 76 additions and 13 deletions.
18 changes: 17 additions & 1 deletion docs/content/en/docs/configuration/command-line.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ The following command-line options are supported:
| [`--rate-limit-update`](#rate-limit-update) | uploads per second (float) | `0.5` | |
| [`--reload-strategy`](#reload-strategy) | [native\|reusesocket] | `reusesocket` | |
| [`--sort-backends`](#sort-backends) | [true\|false] | `false` | |
| [`--sort-endpoints-by`](#sort-endpoints-by) | [endpoint\|ip\|name\|random] | `endpoint` | v0.11 |
| [`--stats-collect-processing-period`](#stats) | time | `500ms` | v0.10 |
| [`--tcp-services-configmap`](#tcp-services-configmap) | namespace/configmapname | no tcp svc | |
| [`--verify-hostname`](#verify-hostname) | [true\|false] | `true` | |
Expand Down Expand Up @@ -235,7 +236,8 @@ describes how it works.
## --sort-backends

Defines if backend's endpoints should be sorted by name. Since v0.8 the endpoints will stay in the
same order found in the Kubernetes' endpoint objects if `--sort-backends` is missing.
same order found in the Kubernetes' endpoint objects if `--sort-backends` is missing. This option
has less precedence than `--sort-endpoints-by` if both are declared.

In v0.7 and older version, if `--sort-backends` is missing, HAProxy Ingress randomly shuffle endpoints
on each reload in order to avoid requesting always the same backends just after haproxy reloads.
Expand All @@ -246,6 +248,20 @@ option, because the default value builds the server name using a numeric sequenc
See also:

* [backend-server-naming]({{% relref "keys#backend-server-naming" %}}) configuration key
* [sort-endpoints-by]({{% relref "#sort-endpoints-by" %}}) command-line option

---

## --sort-endpoints-by

Since v0.11

Defines in which order the endpoints of a backend should be sorted.

* `endpoint`: this is the default value, uses the same order declared in the Kubernetes' Endpoint objects. `ep` is an alias to `endpoint`
* `ip`: sort endpoints by the IP and port of the destination server
* `name`: sort the endpoints by the name given to the server, see also [backend-server-naming]({{% relref "keys#backend-server-naming" %}})
* `random`: randomly shuffle the endpoints every time haproxy needs to be reloaded, this option avoids to always send requests to the same endpoints depending on the balancing algorithm

---

Expand Down
2 changes: 1 addition & 1 deletion pkg/common/ingress/controller/controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ type Configuration struct {
UpdateStatusOnShutdown bool

BackendShards int
SortBackends bool
SortEndpointsBy string
IgnoreIngressWithoutClass bool
}

Expand Down
21 changes: 19 additions & 2 deletions pkg/common/ingress/controller/launch.go
Original file line number Diff line number Diff line change
Expand Up @@ -169,7 +169,12 @@ func NewIngressController(backend ingress.Controller) *GenericController {
`Defines how much files should be used to configure the haproxy backends`)

sortBackends = flags.Bool("sort-backends", false,
`Defines if backend's endpoints should be sorted by name. It uses the same k8s endpoint order if missing`)
`Defines if backend's endpoints should be sorted by name. This option has less precedence than
--sort-endpoints-by if both are declared.`)

sortEndpointsBy = flags.String("sort-endpoints-by", "",
`Defines how to sort backend's endpoints. Allowed values are: 'endpoint' - same k8s endpoint order (default);
'name' - server/endpoint name; 'ip' - server/endpoint IP and port; 'random' - shuffle endpoints on every haproxy reload`)

useNodeInternalIP = flags.Bool("report-node-internal-ip-address", false,
`Defines if the nodes IP address to be returned in the ingress status should be the internal instead of the external IP address`)
Expand Down Expand Up @@ -296,6 +301,18 @@ func NewIngressController(backend ingress.Controller) *GenericController {
glog.Fatal("Cannot use --allow-cross-namespace if --force-namespace-isolation is true")
}

sortEndpoints := strings.ToLower(*sortEndpointsBy)
if sortEndpoints == "" {
if *sortBackends {
sortEndpoints = "name"
} else {
sortEndpoints = "endpoint"
}
}
if !stringInSlice(sortEndpoints, []string{"ep", "endpoint", "ip", "name", "random"}) {
glog.Fatalf("Unsupported --sort-endpoint-by option: %s", sortEndpoints)
}

config := &Configuration{
UpdateStatus: *updateStatus,
ElectionID: *electionID,
Expand Down Expand Up @@ -332,7 +349,7 @@ func NewIngressController(backend ingress.Controller) *GenericController {
DisablePodList: *disablePodList,
UpdateStatusOnShutdown: *updateStatusOnShutdown,
BackendShards: *backendShards,
SortBackends: *sortBackends,
SortEndpointsBy: sortEndpoints,
UseNodeInternalIP: *useNodeInternalIP,
IgnoreIngressWithoutClass: *ignoreIngressWithoutClass,
}
Expand Down
2 changes: 1 addition & 1 deletion pkg/controller/controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,7 @@ func (hc *HAProxyController) configController() {
Metrics: hc.metrics,
ReloadStrategy: *hc.reloadStrategy,
MaxOldConfigFiles: *hc.maxOldConfigFiles,
SortBackends: hc.cfg.SortBackends,
SortEndpointsBy: hc.cfg.SortEndpointsBy,
ValidateConfig: *hc.validateConfig,
}
hc.instance = haproxy.CreateInstance(hc.logger, instanceOptions)
Expand Down
10 changes: 7 additions & 3 deletions pkg/haproxy/instance.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ type InstanceOptions struct {
MaxOldConfigFiles int
Metrics types.Metrics
ReloadStrategy string
SortBackends bool
SortEndpointsBy string
ValidateConfig bool
// TODO Fake is used to skip real haproxy calls. Use a mock instead.
fake bool
Expand Down Expand Up @@ -258,8 +258,12 @@ func (i *instance) haproxyUpdate(timer *utils.Timer) {
}
updater := i.newDynUpdater()
updated := updater.update()
if i.options.SortBackends {
i.config.Backends().SortChangedEndpoints()
if i.options.SortEndpointsBy != "random" {
i.config.Backends().SortChangedEndpoints(i.options.SortEndpointsBy)
} else if !updated {
// Only shuffle if need to reload
i.config.Backends().ShuffleAllEndpoints()
timer.Tick("shuffle_endpoints")
}
if !updated || updater.cmdCnt > 0 {
// only need to rewrtite config files if:
Expand Down
25 changes: 22 additions & 3 deletions pkg/haproxy/types/backend.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,11 @@ package types

import (
"fmt"
"math/rand"
"reflect"
"sort"
"strings"
"time"
)

// BackendID ...
Expand Down Expand Up @@ -92,9 +94,26 @@ func (b *Backend) addEndpoint(ip string, port int, targetRef string) *Endpoint {
return endpoint
}

func (b *Backend) sortEndpoints() {
sort.SliceStable(b.Endpoints, func(i, j int) bool {
return b.Endpoints[i].Name < b.Endpoints[j].Name
func (b *Backend) sortEndpoints(sortBy string) {
ep := b.Endpoints
switch sortBy {
// ignoring "ep"/"endpoint" (use the k8s order) and "random" (shuffleEndpoints implements)
case "name":
sort.Slice(ep, func(i, j int) bool {
return ep[i].Name < ep[j].Name
})
case "ip":
sort.Slice(ep, func(i, j int) bool {
return ep[i].IP < ep[j].IP
})
}
}

func (b *Backend) shuffleEndpoints() {
ep := b.Endpoints
rand.Seed(time.Now().UnixNano())
rand.Shuffle(len(ep), func(i, j int) {
ep[i], ep[j] = ep[j], ep[i]
})
}

Expand Down
11 changes: 9 additions & 2 deletions pkg/haproxy/types/backends.go
Original file line number Diff line number Diff line change
Expand Up @@ -146,9 +146,16 @@ func (b *Backends) ChangedShards() []int {
}

// SortChangedEndpoints ...
func (b *Backends) SortChangedEndpoints() {
func (b *Backends) SortChangedEndpoints(sortBy string) {
for _, backend := range b.itemsAdd {
backend.sortEndpoints()
backend.sortEndpoints(sortBy)
}
}

// ShuffleAllEndpoints ...
func (b *Backends) ShuffleAllEndpoints() {
for _, backend := range b.items {
backend.shuffleEndpoints()
}
}

Expand Down

0 comments on commit 103318c

Please sign in to comment.