Skip to content

Commit cb05334

Browse files
committed
Use a more flexible port to pool mapping
Previously loadbalancer pools were named as a combination of the service port protocol and outward bound port: - `service.spec.ports[].protocol` - `service.spec.ports[].port` Since a name mis-match causes a pool to be recreated - an operation that takes around 30-45 seconds - a change in the port number caused the pool and its members to be recreated. Since Kubernetes has the option to set a unique name for each service port, we can offer an approach that allows to change the port, without pool recreation in some cases: - If a service has only one port with no name and the port changes. - If a service uses names but keeps the names stable when changing ports. In such cases, only the listener has to be re-created, which can be done in 10-15 seconds. There's still a slight downtime that has to be expected, but it is much shorter. When the name changes, the pool still has to be recreated, which will be a bit unexpected, but for deployments that would like to add/remove/change a port with minimal downtime, there's now a way.
1 parent 6832791 commit cb05334

File tree

3 files changed

+21
-8
lines changed

3 files changed

+21
-8
lines changed

examples/nginx-hello.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ spec:
5151
- port: 80
5252
protocol: TCP
5353
targetPort: 80
54-
name: primary
54+
name: http
5555
selector:
5656
app: hello
5757
type: LoadBalancer

pkg/cloudscale_ccm/reconcile.go

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -146,7 +146,7 @@ func desiredLbState(
146146
}
147147

148148
pool := cloudscale.LoadBalancerPool{
149-
Name: poolName(port.Protocol, port.Port),
149+
Name: poolName(port.Protocol, port.Name),
150150
Algorithm: algorithm,
151151
Protocol: protocol,
152152
}
@@ -954,10 +954,20 @@ func (l *lbState) poolsByName() map[string]*cloudscale.LoadBalancerPool {
954954
// poolName produces the name of the pool for the given service port (the port
955955
// that is bound on the load balancer and reachable from outside of it).
956956
//
957+
// We use the name of the port (may be empty, but is enforced to be unqiue
958+
// for each service).
959+
//
957960
// Warning: This named is used to compare desired pools to actual pools.
958961
// Any change to it causes pools to be rebuilt, which must be avoided!
959-
func poolName(protocol v1.Protocol, port int32) string {
960-
return strings.ToLower(fmt.Sprintf("%s/%d", protocol, port))
962+
func poolName(protocol v1.Protocol, name string) string {
963+
p := strings.ToLower(string(protocol))
964+
965+
// By default, the port has no name (required with more than 1 port)
966+
if name == "" {
967+
return p
968+
}
969+
970+
return fmt.Sprintf("%s/%s", p, name)
961971
}
962972

963973
// poolMemberName produces the name of the pool member for the given node

pkg/cloudscale_ccm/reconcile_test.go

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,9 @@ import (
1313
)
1414

1515
func TestPoolName(t *testing.T) {
16-
assert.Equal(t, "tcp/80", poolName(v1.ProtocolTCP, 80))
17-
assert.Equal(t, "udp/443", poolName(v1.ProtocolUDP, 443))
16+
assert.Equal(t, "tcp", poolName(v1.ProtocolTCP, ""))
17+
assert.Equal(t, "udp/foo", poolName(v1.ProtocolUDP, "foo"))
18+
assert.Equal(t, "udp/FOO", poolName(v1.ProtocolUDP, "FOO"))
1819
}
1920

2021
func TestPoolMemberName(t *testing.T) {
@@ -126,11 +127,13 @@ func TestDesiredService(t *testing.T) {
126127
Protocol: "TCP",
127128
Port: 80,
128129
NodePort: 8080,
130+
Name: "http",
129131
},
130132
{
131133
Protocol: "TCP",
132134
Port: 443,
133135
NodePort: 8443,
136+
Name: "https",
134137
},
135138
}
136139

@@ -143,10 +146,10 @@ func TestDesiredService(t *testing.T) {
143146

144147
// Have one pool per service port
145148
assert.Len(t, desired.pools, 2)
146-
assert.Equal(t, desired.pools[0].Name, "tcp/80")
149+
assert.Equal(t, desired.pools[0].Name, "tcp/http")
147150
assert.Equal(t, desired.pools[0].Protocol, "tcp")
148151
assert.Equal(t, desired.pools[0].Algorithm, "round_robin")
149-
assert.Equal(t, desired.pools[1].Name, "tcp/443")
152+
assert.Equal(t, desired.pools[1].Name, "tcp/https")
150153
assert.Equal(t, desired.pools[0].Protocol, "tcp")
151154
assert.Equal(t, desired.pools[0].Algorithm, "round_robin")
152155

0 commit comments

Comments
 (0)