-
Notifications
You must be signed in to change notification settings - Fork 7
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
v1: re-implement Node failure recovery
Due to the usage of local NVME disks, redpanda deployments are particular sensitive to Node failure. Whenever a Node crashes, the resultant redpanda Pod will be stuck in a Pending state due to the NodeAffinity of it's PV. This commit implements a "PVCUnbinder" reconciler that watches for such cases and attempts automatic remediation by "unbinding" PVs. See the implementation for details on the strategy. This implementation is similar to, yet much more paranoid than, the `RedpandaNodePVCReconciler`. Ideally the two implementation should merge together before long. Fixes #166
- Loading branch information
Showing
7 changed files
with
818 additions
and
24 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
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,116 @@ | ||
package pvcunbinder | ||
|
||
import ( | ||
"fmt" | ||
"os/exec" | ||
"strings" | ||
"sync" | ||
"time" | ||
|
||
"github.com/redpanda-data/helm-charts/pkg/kube" | ||
"k8s.io/client-go/tools/clientcmd" | ||
) | ||
|
||
type K3DCluster struct { | ||
Name string | ||
|
||
mu sync.Mutex | ||
restConfig *kube.RESTConfig | ||
agentCounter int32 | ||
} | ||
|
||
func (c *K3DCluster) RESTConfig() *kube.RESTConfig { | ||
return c.restConfig | ||
} | ||
|
||
func (c *K3DCluster) DeleteNode(name string) error { | ||
c.mu.Lock() | ||
defer c.mu.Unlock() | ||
if out, err := exec.Command( | ||
"k3d", | ||
"node", | ||
"delete", | ||
name, | ||
).CombinedOutput(); err != nil { | ||
return fmt.Errorf("%w: %s", err, out) | ||
} | ||
return nil | ||
} | ||
|
||
func (c *K3DCluster) CreateNode() error { | ||
c.mu.Lock() | ||
defer c.mu.Unlock() | ||
|
||
c.agentCounter += 1 | ||
|
||
if out, err := exec.Command( | ||
"k3d", | ||
"node", | ||
"create", | ||
fmt.Sprintf("k3d-%s-agent-%d", c.Name, c.agentCounter), | ||
fmt.Sprintf("--cluster=%s", c.Name), | ||
"--wait", | ||
"--role=agent", | ||
).CombinedOutput(); err != nil { | ||
return fmt.Errorf("%w: %s", err, out) | ||
} | ||
return nil | ||
} | ||
|
||
func (c *K3DCluster) Cleanup() error { | ||
c.mu.Lock() | ||
defer c.mu.Unlock() | ||
|
||
_, err := exec.Command( | ||
"k3d", | ||
"cluster", | ||
"delete", | ||
c.Name, | ||
).CombinedOutput() | ||
return err | ||
} | ||
|
||
func NewK3DCluster(name string) (*K3DCluster, error) { | ||
name = strings.ToLower(name) | ||
|
||
out, err := exec.Command( | ||
"k3d", | ||
"cluster", | ||
"create", | ||
name, | ||
fmt.Sprintf("--agents=%d", 3), | ||
fmt.Sprintf("--timeout=%s", 30*time.Second), | ||
// See also https://github.com/k3d-io/k3d/blob/main/docs/faq/faq.md#passing-additional-argumentsflags-to-k3s-and-on-to-eg-the-kube-apiserver | ||
// As the formatting is QUITE finicky. | ||
// Halve the node-monitor-grace-period to speed up tests that rely on dead node detection. | ||
`--k3s-arg`, `--kube-controller-manager-arg=node-monitor-grace-period=10s@server:*`, | ||
// Dramatically decrease (5m -> 10s) the default tolerations to ensure | ||
// Pod eviction happens in a timely fashion. | ||
`--k3s-arg`, `--kube-apiserver-arg=default-not-ready-toleration-seconds=10@server:*`, | ||
`--k3s-arg`, `--kube-apiserver-arg=default-unreachable-toleration-seconds=10@server:*`, | ||
).CombinedOutput() | ||
if err != nil { | ||
return nil, fmt.Errorf("%w: %s", err, out) | ||
} | ||
|
||
kubeconfigYAML, err := exec.Command("k3d", "kubeconfig", "get", name).CombinedOutput() | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
kubeconfig, err := clientcmd.Load(kubeconfigYAML) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
cfg, err := kube.ConfigToRest(*kubeconfig) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
return &K3DCluster{ | ||
Name: name, | ||
restConfig: cfg, | ||
agentCounter: 3, | ||
}, nil | ||
} |
Oops, something went wrong.