-
Notifications
You must be signed in to change notification settings - Fork 323
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Set leave_on_terminate=true for servers and hardcode maxUnavailable=1
When leave_on_terminate=false (default), rolling the statefulset is disruptive because the new servers come up with the same node IDs but different IP addresses. They can't join the server cluster until the old server's node ID is marked as failed by serf. During this time, they continually start leader elections because they don't know there's a leader. When they eventually join the cluster, their election term is higher, and so they trigger a leadership swap. The leadership swap happens at the same time as the next node to be rolled is being stopped, and so the cluster can end up without a leader. With leave_on_terminate=true, the stopping server cleanly leaves the cluster, so the new server can join smoothly, even though it has the same node ID as the old server. This increases the speed of the rollout and in my testing eliminates the period without a leader. The downside of this change is that when a server leaves gracefully, it also reduces the number of raft peers. The number of peers is used to calculate the quorum size, so this can unexpectedly change the fault tolerance of the cluster. When running with an odd number of servers, 1 server leaving the cluster does not affect quorum size. E.g. 5 servers => quorum 3, 4 servers => quorum still 3. During a rollout, Kubernetes only stops 1 server at a time, so the quorum won't change. During a voluntary disruption event, e.g. a node being drained, Kubernetes uses the pod disruption budget to determine how many pods in a statefulset can be made unavailable at a time. That's why this change hardcodes this number to 1 now. Also set autopilot min_quorum to min quorum and disable autopilot upgrade migration since that's for blue/green deploys.
- Loading branch information
Showing
16 changed files
with
259 additions
and
26 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,3 @@ | ||
```releast-note:feature | ||
```release-note:feature | ||
api-gateway: (Consul Enterprise) Add JWT authentication and authorization for API Gateway and HTTPRoutes. | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
```release-note:breaking-change | ||
server: set `leave_on_terminate` to `true` and set the server pod disruption budget `maxUnavailable` to `1`. | ||
|
||
This change makes server rollouts faster and more reliable. However, there is now a potential for reduced reliability if users accidentally | ||
scale the statefulset down. Now servers will leave the raft pool when they are stopped gracefully which reduces the fault | ||
tolerance. For example, with 5 servers, you can tolerate a loss of 2 servers' data as raft guarantees data is replicated to | ||
a majority of nodes (3). However, if you accidentally scale the statefulset down to 3, then the raft quorum will now be 2, and | ||
if you lose 2 servers, you may lose data. Before this change, the quorum would have remained at 3. | ||
|
||
During a regular rollout, the number of servers will be reduced by 1 at a time, which doesn't affect quorum when running | ||
an odd number of servers, e.g. quorum for 5 servers is 3, and quorum for 4 servers is also 3. That's why the pod disruption | ||
budget is being set to 1 now. | ||
|
||
If a server is stopped ungracefully, e.g. due to a node loss, it will not leave the raft pool, and so fault tolerance won't be affected. | ||
|
||
For the vast majority of users, this change will be beneficial, however if you wish to remain with the old settings you | ||
can set: | ||
|
||
server: | ||
extraConfig: | | ||
{"leave_on_terminate": false} | ||
disruptionBudget: | ||
maxUnavailable: <previous setting> | ||
|
||
``` | ||
|
||
```release-note:breaking-change | ||
server: set `autopilot.min_quorum` to the correct quorum value to ensure autopilot doesn't prune servers needed for quorum. Also set `autopilot. disable_upgrade_migration` to `true` as that setting is meant for blue/green deploys, not rolling deploys. | ||
|
||
This setting makes sense for most use-cases, however if you had a specific reason to use the old settings you can use the following config to keep them: | ||
|
||
server: | ||
extraConfig: | | ||
{"autopilot": {"min_quorum": 0, "disable_upgrade_migration": false}} | ||
|
||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
// Copyright (c) HashiCorp, Inc. | ||
// SPDX-License-Identifier: MPL-2.0 | ||
|
||
package server | ||
|
||
import ( | ||
"os" | ||
"testing" | ||
|
||
testsuite "github.com/hashicorp/consul-k8s/acceptance/framework/suite" | ||
) | ||
|
||
var suite testsuite.Suite | ||
|
||
func TestMain(m *testing.M) { | ||
suite = testsuite.NewSuite(m) | ||
os.Exit(suite.Run()) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,84 @@ | ||
// Copyright (c) HashiCorp, Inc. | ||
// SPDX-License-Identifier: MPL-2.0 | ||
|
||
package server | ||
|
||
import ( | ||
"encoding/json" | ||
"fmt" | ||
"testing" | ||
"time" | ||
|
||
"github.com/hashicorp/consul-k8s/acceptance/framework/consul" | ||
"github.com/hashicorp/consul-k8s/acceptance/framework/helpers" | ||
"github.com/hashicorp/consul-k8s/acceptance/framework/k8s" | ||
"github.com/hashicorp/consul-k8s/acceptance/framework/logger" | ||
"github.com/hashicorp/go-multierror" | ||
"github.com/stretchr/testify/require" | ||
) | ||
|
||
// Test that when servers are restarted, they don't lose leadership. | ||
func TestServerRestart(t *testing.T) { | ||
ctx := suite.Environment().DefaultContext(t) | ||
replicas := 3 | ||
releaseName := helpers.RandomName() | ||
helmValues := map[string]string{ | ||
"global.enabled": "false", | ||
"connectInject.enabled": "false", | ||
"server.enabled": "true", | ||
"server.replicas": fmt.Sprintf("%d", replicas), | ||
} | ||
consulCluster := consul.NewHelmCluster(t, helmValues, suite.Environment().DefaultContext(t), suite.Config(), releaseName) | ||
consulCluster.Create(t) | ||
|
||
// Start a separate goroutine to check if at any point more than one server is without | ||
// a leader. We expect the server that is restarting to be without a leader because it hasn't | ||
// yet joined the cluster but the other servers should have a leader. | ||
expReadyPods := replicas - 1 | ||
var unmarshallErrs error | ||
timesWithoutLeader := 0 | ||
done := make(chan bool) | ||
defer close(done) | ||
go func() { | ||
for { | ||
select { | ||
case <-done: | ||
return | ||
default: | ||
out, err := k8s.RunKubectlAndGetOutputE(t, ctx.KubectlOptions(t), "get", fmt.Sprintf("statefulset/%s-consul-server", releaseName), | ||
"-o", "jsonpath={.status}") | ||
if err != nil { | ||
// Not failing the test on this error to reduce flakiness. | ||
logger.Logf(t, "kubectl err: %s: %s", err, out) | ||
break | ||
} | ||
type statefulsetOut struct { | ||
ReadyReplicas *int `json:"readyReplicas,omitempty"` | ||
} | ||
var jsonOut statefulsetOut | ||
if err = json.Unmarshal([]byte(out), &jsonOut); err != nil { | ||
unmarshallErrs = multierror.Append(err) | ||
} else if jsonOut.ReadyReplicas == nil || *jsonOut.ReadyReplicas < expReadyPods { | ||
// note: for some k8s api reason when readyReplicas is 0 it's not included in the json output so | ||
// that's why we're checking if it's nil. | ||
timesWithoutLeader++ | ||
} | ||
time.Sleep(1 * time.Second) | ||
} | ||
} | ||
}() | ||
|
||
// Restart servers | ||
out, err := k8s.RunKubectlAndGetOutputE(t, ctx.KubectlOptions(t), "rollout", "restart", fmt.Sprintf("statefulset/%s-consul-server", releaseName)) | ||
require.NoError(t, err, out) | ||
|
||
// Wait for restart to finish. | ||
start := time.Now() | ||
out, err = k8s.RunKubectlAndGetOutputE(t, ctx.KubectlOptions(t), "rollout", "status", "--timeout", "5m", "--watch", fmt.Sprintf("statefulset/%s-consul-server", releaseName)) | ||
require.NoError(t, err, out, "rollout status command errored, this likely means the rollout didn't complete in time") | ||
|
||
// Check results | ||
require.NoError(t, unmarshallErrs, "there were some json unmarshall errors, this is likely a bug") | ||
logger.Logf(t, "restart took %s, there were %d instances where than one server had no leader", time.Since(start), timesWithoutLeader) | ||
require.Equal(t, 0, timesWithoutLeader, "there were %d instances where more than one server had no leader", timesWithoutLeader) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.