Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support multi-control plane - HA clusters #17909

Merged
merged 34 commits into from
Mar 6, 2024
Merged
Show file tree
Hide file tree
Changes from 28 commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
ad7fb39
support kubernetes ha cluster topology in minikube
prezha Jan 7, 2024
6731cde
Merge branch 'kubernetes:master' into ha
prezha Jan 9, 2024
bcdfc03
workaround for kube-vip and k8s v1.29+
prezha Jan 9, 2024
f650994
use path and path_filepath according to os
prezha Jan 10, 2024
5278d88
Merge branch 'master' into ha
prezha Jan 10, 2024
047bad0
Merge branch 'kubernetes:master' into ha
prezha Jan 10, 2024
b1e785c
Merge branch 'kubernetes:master' into ha
prezha Jan 10, 2024
d45ff8f
resolve merge conflict due to rebase-needed
prezha Jan 10, 2024
d002316
fix k8s node labels for "none" driver
prezha Jan 11, 2024
a26ebbf
Merge branch 'kubernetes:master' into ha
prezha Jan 13, 2024
fb7ae16
ensure override with --kubernetes-version is respected
prezha Jan 13, 2024
dad37da
improve specific error message handling in mustload package
prezha Jan 14, 2024
99ea346
allow "cluster does not require reconfiguration" msg to passthrough
prezha Jan 14, 2024
32b2a5f
workaround for "none" driver and IsPrimaryControlPlane()
prezha Jan 15, 2024
6f580e8
fix PingHostFromPods integration test by adding NET_RAW capabilities
prezha Jan 16, 2024
937d6f6
Merge branch 'kubernetes:master' into ha
prezha Jan 18, 2024
a2cf2f9
Merge branch 'kubernetes:master' into ha
prezha Jan 18, 2024
782d55b
Update pkg/minikube/bootstrapper/bsutil/kubeadm.go
prezha Jan 31, 2024
9d5dfaf
Update pkg/addons/addons_storage_classes.go
prezha Jan 31, 2024
2ec3472
Update cmd/minikube/cmd/config/profile_list.go
prezha Jan 31, 2024
3a0ada1
spowelljr: rename HA() to IsHA()
prezha Jan 31, 2024
dda8db0
spowelljr: prevent user from modifying apiserver port
prezha Jan 31, 2024
86e00e6
Merge branch 'kubernetes:master' into ha
prezha Jan 31, 2024
22603d1
use forwarded apiserver port, for drivers that neededs it
prezha Feb 7, 2024
757e657
Merge branch 'kubernetes:master' into ha
prezha Feb 7, 2024
2f040dc
bump kube-vip to v0.7.0
prezha Feb 7, 2024
1c682fa
Merge branch 'kubernetes:master' into ha
prezha Feb 7, 2024
cc4dc8c
Merge branch 'kubernetes:master' into ha
prezha Feb 9, 2024
54c6e69
add "(multi-control plane)" after each reference to "ha"
prezha Feb 25, 2024
f34ef00
bump kube-vip to v0.7.1
prezha Mar 3, 2024
6f4d0f1
Merge branch 'kubernetes:master' into ha
prezha Mar 3, 2024
ca3119b
Revert "Update go from 1.21.6 to"
prezha Mar 3, 2024
46ca799
Reapply "Update go from 1.21.6 to"
prezha Mar 4, 2024
8c4e1a0
workaround for cp bug 63245 and rename TestHA to TestMutliControlPlane
prezha Mar 5, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ _testmain.go
*.exe
*.test
*.prof
*.pprof

/deploy/iso/minikube-iso/board/minikube/x86_64/rootfs-overlay/usr/bin/auto-pause
/deploy/iso/minikube-iso/board/minikube/aarch64/rootfs-overlay/usr/bin/auto-pause
Expand Down
114 changes: 78 additions & 36 deletions cmd/minikube/cmd/config/profile_list.go
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ func printProfilesTable() {
}

if len(validProfiles) == 0 {
exit.Message(reason.UsageNoProfileRunning, "No minikube profile was found. ")
exit.Message(reason.UsageNoProfileRunning, "No minikube profile was found.")
}

updateProfilesStatus(validProfiles)
Expand Down Expand Up @@ -111,45 +111,81 @@ func updateProfilesStatus(profiles []*config.Profile) {
}

func profileStatus(p *config.Profile, api libmachine.API) string {
cp, err := config.PrimaryControlPlane(p.Config)
if err != nil {
exit.Error(reason.GuestCpConfig, "error getting primary control plane", err)
cps := config.ControlPlanes(*p.Config)
if len(cps) == 0 {
exit.Message(reason.GuestCpConfig, "No control-plane nodes found.")
}

host, err := machine.LoadHost(api, config.MachineName(*p.Config, cp))
if err != nil {
klog.Warningf("error loading profiles: %v", err)
return "Unknown"
}
status := "Unknown"
healthyCPs := 0
for _, cp := range cps {
machineName := config.MachineName(*p.Config, cp)

// The machine isn't running, no need to check inside
s, err := host.Driver.GetState()
if err != nil {
klog.Warningf("error getting host state: %v", err)
return "Unknown"
}
if s != state.Running {
return s.String()
}
ms, err := machine.Status(api, machineName)
if err != nil {
klog.Warningf("error loading profile (will continue): machine status for %s: %v", machineName, err)
continue
}
if ms != state.Running.String() {
klog.Warningf("error loading profile (will continue): machine %s is not running: %q", machineName, ms)
status = ms
continue
}

cr, err := machine.CommandRunner(host)
if err != nil {
klog.Warningf("error loading profiles: %v", err)
return "Unknown"
}
host, err := machine.LoadHost(api, machineName)
if err != nil {
klog.Warningf("error loading profile (will continue): load host for %s: %v", machineName, err)
continue
}

hostname, _, port, err := driver.ControlPlaneEndpoint(p.Config, &cp, host.DriverName)
if err != nil {
klog.Warningf("error loading profiles: %v", err)
return "Unknown"
hs, err := host.Driver.GetState()
if err != nil {
klog.Warningf("error loading profile (will continue): host state for %s: %v", machineName, err)
continue
}
if hs != state.Running {
klog.Warningf("error loading profile (will continue): host %s is not running: %q", machineName, hs)
status = hs.String()
continue
}

cr, err := machine.CommandRunner(host)
if err != nil {
klog.Warningf("error loading profile (will continue): command runner for %s: %v", machineName, err)
continue
}

hostname, _, port, err := driver.ControlPlaneEndpoint(p.Config, &cp, host.DriverName)
if err != nil {
klog.Warningf("error loading profile (will continue): control-plane endpoint for %s: %v", machineName, err)
continue
}

as, err := kverify.APIServerStatus(cr, hostname, port)
if err != nil {
klog.Warningf("error loading profile (will continue): apiserver status for %s: %v", machineName, err)
continue
}
status = as.String()
if as != state.Running {
klog.Warningf("error loading profile (will continue): apiserver %s is not running: %q", machineName, hs)
continue
}

healthyCPs++
}

status, err := kverify.APIServerStatus(cr, hostname, port)
if err != nil {
klog.Warningf("error getting apiserver status for %s: %v", p.Name, err)
return "Unknown"
if config.IsHA(*p.Config) {
switch {
case healthyCPs < 2:
return state.Stopped.String()
case healthyCPs == 2:
return "Degraded"
default:
return "HAppy"
}
}
return status.String()
return status
}

func renderProfilesTable(ps [][]string) {
Expand All @@ -166,9 +202,15 @@ func profilesToTableData(profiles []*config.Profile) [][]string {
var data [][]string
currentProfile := ClusterFlagValue()
for _, p := range profiles {
cp, err := config.PrimaryControlPlane(p.Config)
if err != nil {
exit.Error(reason.GuestCpConfig, "error getting primary control plane", err)
cpIP := p.Config.KubernetesConfig.APIServerHAVIP
cpPort := p.Config.APIServerPort
if !config.IsHA(*p.Config) {
cp, err := config.ControlPlane(*p.Config)
if err != nil {
exit.Error(reason.GuestCpConfig, "error getting control-plane node", err)
}
cpIP = cp.IP
cpPort = cp.Port
}

k8sVersion := p.Config.KubernetesConfig.KubernetesVersion
Expand All @@ -179,7 +221,7 @@ func profilesToTableData(profiles []*config.Profile) [][]string {
if p.Name == currentProfile {
c = "*"
}
data = append(data, []string{p.Name, p.Config.Driver, p.Config.KubernetesConfig.ContainerRuntime, cp.IP, strconv.Itoa(cp.Port), k8sVersion, p.Status, strconv.Itoa(len(p.Config.Nodes)), c})
data = append(data, []string{p.Name, p.Config.Driver, p.Config.KubernetesConfig.ContainerRuntime, cpIP, strconv.Itoa(cpPort), k8sVersion, p.Status, strconv.Itoa(len(p.Config.Nodes)), c})
}
return data
}
Expand Down
7 changes: 2 additions & 5 deletions cmd/minikube/cmd/cp.go
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ Example Command : "minikube cp a.txt /home/docker/b.txt" +
runner = remoteCommandRunner(&co, dst.node)
} else if src.node == "" {
// if node name not explicitly specified in both of source and target,
// consider target is controlpanel node for backward compatibility.
// consider target is control-plane node for backward compatibility.
runner = co.CP.Runner
} else {
runner = command.NewExecRunner(false)
Expand All @@ -84,9 +84,6 @@ Example Command : "minikube cp a.txt /home/docker/b.txt" +
},
}

func init() {
}

// setDstFileNameFromSrc sets the src filename as dst filename
// when the dst file name is not provided and ends with a `/`.
// Otherwise this function is a no-op and returns the passed dst.
Expand Down Expand Up @@ -211,7 +208,7 @@ func validateArgs(src, dst *remotePath) {
}

// if node name not explicitly specified in both of source and target,
// consider target node is controlpanel for backward compatibility.
// consider target node is control-plane for backward compatibility.
if src.node == "" && dst.node == "" && !strings.HasPrefix(dst.path, "/") {
exit.Message(reason.Usage, `Target <remote file path> must be an absolute Path. Relative Path is not allowed (example: "minikube:/home/docker/copied.txt")`)
}
Expand Down
3 changes: 1 addition & 2 deletions cmd/minikube/cmd/docker-env.go
Original file line number Diff line number Diff line change
Expand Up @@ -228,8 +228,7 @@ func mustRestartDockerd(name string, runner command.Runner) {
// will need to wait for apisever container to come up, this usually takes 5 seconds
// verifying apisever using kverify would add code complexity for a rare case.
klog.Warningf("waiting to ensure apisever container is up...")
startTime := time.Now()
if err = waitForAPIServerProcess(runner, startTime, time.Second*30); err != nil {
if err = waitForAPIServerProcess(runner, time.Now(), time.Second*30); err != nil {
klog.Warningf("apiserver container isn't up, error: %v", err)
}
}
Expand Down
2 changes: 1 addition & 1 deletion cmd/minikube/cmd/logs.go
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,7 @@ func shouldSilentFail() bool {

api, cc := mustload.Partial(ClusterFlagValue())

cp, err := config.PrimaryControlPlane(cc)
cp, err := config.ControlPlane(*cc)
if err != nil {
return false
}
Expand Down
44 changes: 28 additions & 16 deletions cmd/minikube/cmd/node_add.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ package cmd
import (
"github.com/spf13/cobra"
"github.com/spf13/viper"

"k8s.io/minikube/pkg/minikube/cni"
"k8s.io/minikube/pkg/minikube/config"
"k8s.io/minikube/pkg/minikube/driver"
Expand All @@ -32,8 +33,9 @@ import (
)

var (
cp bool
worker bool
cpNode bool
workerNode bool
deleteNodeOnFailure bool
)

var nodeAddCmd = &cobra.Command{
Expand All @@ -48,20 +50,31 @@ var nodeAddCmd = &cobra.Command{
out.FailureT("none driver does not support multi-node clusters")
}

name := node.Name(len(cc.Nodes) + 1)
if cpNode && !config.IsHA(*cc) {
out.FailureT("Adding a control-plane node to a non-HA cluster is not currently supported. Please first delete the cluster and use 'minikube start --ha' to create new one.")
}

roles := []string{}
if workerNode {
roles = append(roles, "worker")
}
if cpNode {
roles = append(roles, "control-plane")
}

// for now control-plane feature is not supported
if cp {
out.Step(style.Unsupported, "Adding a control-plane node is not yet supported, setting control-plane flag to false")
cp = false
// calculate appropriate new node name with id following the last existing one
lastID, err := node.ID(cc.Nodes[len(cc.Nodes)-1].Name)
if err != nil {
lastID = len(cc.Nodes)
out.ErrLn("determining last node index (will assume %d): %v", lastID, err)
}
name := node.Name(lastID + 1)

out.Step(style.Happy, "Adding node {{.name}} to cluster {{.cluster}}", out.V{"name": name, "cluster": cc.Name})
// TODO: Deal with parameters better. Ideally we should be able to acceot any node-specific minikube start params here.
out.Step(style.Happy, "Adding node {{.name}} to cluster {{.cluster}} as {{.roles}}", out.V{"name": name, "cluster": cc.Name, "roles": roles})
n := config.Node{
Name: name,
Worker: worker,
ControlPlane: cp,
Worker: workerNode,
ControlPlane: cpNode,
KubernetesVersion: cc.KubernetesConfig.KubernetesVersion,
}

Expand All @@ -77,7 +90,7 @@ var nodeAddCmd = &cobra.Command{
}

register.Reg.SetStep(register.InitialSetup)
if err := node.Add(cc, n, false); err != nil {
if err := node.Add(cc, n, deleteNodeOnFailure); err != nil {
_, err := maybeDeleteAndRetry(cmd, *cc, n, nil, err)
if err != nil {
exit.Error(reason.GuestNodeAdd, "failed to add node", err)
Expand All @@ -93,10 +106,9 @@ var nodeAddCmd = &cobra.Command{
}

func init() {
// TODO(https://github.com/kubernetes/minikube/issues/7366): We should figure out which minikube start flags to actually import
nodeAddCmd.Flags().BoolVar(&cp, "control-plane", false, "This flag is currently unsupported.")
nodeAddCmd.Flags().BoolVar(&worker, "worker", true, "If true, the added node will be marked for work. Defaults to true.")
nodeAddCmd.Flags().Bool(deleteOnFailure, false, "If set, delete the current cluster if start fails and try again. Defaults to false.")
nodeAddCmd.Flags().BoolVar(&cpNode, "control-plane", false, "If set, added node will become a control-plane. Defaults to false. Currently only supported for existing HA clusters.")
nodeAddCmd.Flags().BoolVar(&workerNode, "worker", true, "If set, added node will be available as worker. Defaults to true.")
nodeAddCmd.Flags().BoolVar(&deleteNodeOnFailure, "delete-on-failure", false, "If set, delete the current cluster if start fails and try again. Defaults to false.")

nodeCmd.AddCommand(nodeAddCmd)
}
8 changes: 3 additions & 5 deletions cmd/minikube/cmd/node_start.go
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ var nodeStartCmd = &cobra.Command{
}

register.Reg.SetStep(register.InitialSetup)
r, p, m, h, err := node.Provision(cc, n, n.ControlPlane, viper.GetBool(deleteOnFailure))
r, p, m, h, err := node.Provision(cc, n, viper.GetBool(deleteOnFailure))
if err != nil {
exit.Error(reason.GuestNodeProvision, "provisioning host for node", err)
}
Expand All @@ -71,10 +71,8 @@ var nodeStartCmd = &cobra.Command{
ExistingAddons: cc.Addons,
}

_, err = node.Start(s, n.ControlPlane)
if err != nil {
_, err := maybeDeleteAndRetry(cmd, *cc, *n, nil, err)
if err != nil {
if _, err = node.Start(s); err != nil {
if _, err := maybeDeleteAndRetry(cmd, *cc, *n, nil, err); err != nil {
node.ExitIfFatal(err, false)
exit.Error(reason.GuestNodeStart, "failed to start node", err)
}
Expand Down
Loading
Loading