diff --git a/cmd/kubeadm/app/constants/constants.go b/cmd/kubeadm/app/constants/constants.go index 974bc57128d..6cf6d1cf7ee 100644 --- a/cmd/kubeadm/app/constants/constants.go +++ b/cmd/kubeadm/app/constants/constants.go @@ -350,6 +350,10 @@ const ( // ControlPlaneNumCPU is the number of CPUs required on control-plane ControlPlaneNumCPU = 2 + // ControlPlaneMem is the number of megabytes of memory required on the control-plane + // Below that amount of RAM running a stable control plane would be difficult. + ControlPlaneMem = 1700 + // KubeadmCertsSecret specifies in what Secret in the kube-system namespace the certificates should be stored KubeadmCertsSecret = "kubeadm-certs" diff --git a/cmd/kubeadm/app/preflight/checks.go b/cmd/kubeadm/app/preflight/checks.go index f01a40ab2cc..cd656189792 100644 --- a/cmd/kubeadm/app/preflight/checks.go +++ b/cmd/kubeadm/app/preflight/checks.go @@ -869,6 +869,16 @@ func (ncc NumCPUCheck) Check() (warnings, errorList []error) { return warnings, errorList } +// MemCheck checks if the number of megabytes of memory is not less than required +type MemCheck struct { + Mem uint64 +} + +// Name returns the label for memory +func (MemCheck) Name() string { + return "Mem" +} + // RunInitNodeChecks executes all individual, applicable to control-plane node checks. // The boolean flag 'isSecondaryControlPlane' controls whether we are running checks in a --join-control-plane scenario. // The boolean flag 'downloadCerts' controls whether we should skip checks on certificates because we are downloading them. @@ -884,6 +894,9 @@ func RunInitNodeChecks(execer utilsexec.Interface, cfg *kubeadmapi.InitConfigura manifestsDir := filepath.Join(kubeadmconstants.KubernetesDir, kubeadmconstants.ManifestsSubDirName) checks := []Checker{ NumCPUCheck{NumCPU: kubeadmconstants.ControlPlaneNumCPU}, + // Linux only + // TODO: support other OS, if control-plane is supported on it. + MemCheck{Mem: kubeadmconstants.ControlPlaneMem}, KubernetesVersionCheck{KubernetesVersion: cfg.KubernetesVersion, KubeadmVersion: kubeadmversion.Get().GitVersion}, FirewalldCheck{ports: []int{int(cfg.LocalAPIEndpoint.BindPort), kubeadmconstants.KubeletPort}}, PortOpenCheck{port: int(cfg.LocalAPIEndpoint.BindPort)}, diff --git a/cmd/kubeadm/app/preflight/checks_darwin.go b/cmd/kubeadm/app/preflight/checks_darwin.go index d4cb02628a4..b6508b5ec44 100644 --- a/cmd/kubeadm/app/preflight/checks_darwin.go +++ b/cmd/kubeadm/app/preflight/checks_darwin.go @@ -25,3 +25,9 @@ package preflight func (idsc IsDockerSystemdCheck) Check() (warnings, errorList []error) { return nil, nil } + +// Check number of memory required by kubeadm +// No-op for Darwin (MacOS). +func (mc MemCheck) Check() (warnings, errorList []error) { + return nil, nil +} diff --git a/cmd/kubeadm/app/preflight/checks_linux.go b/cmd/kubeadm/app/preflight/checks_linux.go index c42363bbbd2..c89d6890136 100644 --- a/cmd/kubeadm/app/preflight/checks_linux.go +++ b/cmd/kubeadm/app/preflight/checks_linux.go @@ -19,6 +19,8 @@ limitations under the License. package preflight import ( + "syscall" + "github.com/pkg/errors" kubeadmutil "k8s.io/kubernetes/cmd/kubeadm/app/util" "k8s.io/utils/exec" @@ -40,3 +42,19 @@ func (idsc IsDockerSystemdCheck) Check() (warnings, errorList []error) { } return nil, nil } + +// Check number of memory required by kubeadm +func (mc MemCheck) Check() (warnings, errorList []error) { + info := syscall.Sysinfo_t{} + err := syscall.Sysinfo(&info) + if err != nil { + errorList = append(errorList, errors.Wrapf(err, "failed to get system info")) + } + + // Totalram returns bytes; convert to MB + actual := uint64(info.Totalram) / 1024 / 1024 + if actual < mc.Mem { + errorList = append(errorList, errors.Errorf("the system RAM (%d MB) is less than the minimum %d MB", actual, mc.Mem)) + } + return warnings, errorList +} diff --git a/cmd/kubeadm/app/preflight/checks_test.go b/cmd/kubeadm/app/preflight/checks_test.go index b77546bcff8..52a8bb127b4 100644 --- a/cmd/kubeadm/app/preflight/checks_test.go +++ b/cmd/kubeadm/app/preflight/checks_test.go @@ -20,6 +20,7 @@ import ( "bytes" "fmt" "io/ioutil" + "runtime" "strings" "testing" @@ -856,3 +857,29 @@ func TestNumCPUCheck(t *testing.T) { }) } } + +func TestMemCheck(t *testing.T) { + // skip this test, if OS in not Linux, since it will ONLY pass on Linux. + if runtime.GOOS != "linux" { + t.Skip("unsupported OS for memory check test ") + } + + var tests = []struct { + minimum uint64 + expectedErrors int + }{ + {0, 0}, + {9999999999999999, 1}, + } + + for _, rt := range tests { + t.Run(fmt.Sprintf("MemoryCheck{%d}", rt.minimum), func(t *testing.T) { + warnings, errors := MemCheck{Mem: rt.minimum}.Check() + if len(warnings) > 0 { + t.Errorf("expected 0 warnings but got %d: %q", len(warnings), warnings) + } else if len(errors) != rt.expectedErrors { + t.Errorf("expected %d error(s) but got %d: %q", rt.expectedErrors, len(errors), errors) + } + }) + } +} diff --git a/cmd/kubeadm/app/preflight/checks_windows.go b/cmd/kubeadm/app/preflight/checks_windows.go index 6f28081991a..57e5e3b8e05 100644 --- a/cmd/kubeadm/app/preflight/checks_windows.go +++ b/cmd/kubeadm/app/preflight/checks_windows.go @@ -54,3 +54,9 @@ func (ipuc IsPrivilegedUserCheck) Check() (warnings, errorList []error) { func (idsc IsDockerSystemdCheck) Check() (warnings, errorList []error) { return nil, nil } + +// Check number of memory required by kubeadm +// No-op for Windows. +func (mc MemCheck) Check() (warnings, errorList []error) { + return nil, nil +}