diff --git a/cmd/minikube/cmd/start.go b/cmd/minikube/cmd/start.go index f92447103caa..4dcf2c1b00c9 100644 --- a/cmd/minikube/cmd/start.go +++ b/cmd/minikube/cmd/start.go @@ -1149,6 +1149,11 @@ func validateRequestedMemorySize(req int, drvName string) { exitIfNotForced(reason.RsrcInsufficientSysMemory, "System only has {{.size}}MiB available, less than the required {{.req}}MiB for Kubernetes", out.V{"size": sysLimit, "req": minUsableMem}) } + // if --memory=no-limit, ignore remaining checks + if req == 0 && driver.IsKIC(drvName) { + return + } + if req < minUsableMem { exitIfNotForced(reason.RsrcInsufficientReqMemory, "Requested memory allocation {{.requested}}MiB is less than the usable minimum of {{.minimum_memory}}MB", out.V{"requested": req, "minimum_memory": minUsableMem}) } @@ -1208,6 +1213,21 @@ func validateCPUCount(drvName string) { availableCPUs = ci } + if availableCPUs < 2 { + if drvName == oci.Docker && runtime.GOOS == "darwin" { + exitIfNotForced(reason.RsrcInsufficientDarwinDockerCores, "Docker Desktop has less than 2 CPUs configured, but Kubernetes requires at least 2 to be available") + } else if drvName == oci.Docker && runtime.GOOS == "windows" { + exitIfNotForced(reason.RsrcInsufficientWindowsDockerCores, "Docker Desktop has less than 2 CPUs configured, but Kubernetes requires at least 2 to be available") + } else { + exitIfNotForced(reason.RsrcInsufficientCores, "{{.driver_name}} has less than 2 CPUs available, but Kubernetes requires at least 2 to be available", out.V{"driver_name": driver.FullName(viper.GetString("driver"))}) + } + } + + // if --cpus=no-limit, ignore remaining checks + if cpuCount == 0 && driver.IsKIC(drvName) { + return + } + if cpuCount < minimumCPUS { exitIfNotForced(reason.RsrcInsufficientCores, "Requested cpu count {{.requested_cpus}} is less than the minimum allowed of {{.minimum_cpus}}", out.V{"requested_cpus": cpuCount, "minimum_cpus": minimumCPUS}) } @@ -1226,19 +1246,6 @@ func validateCPUCount(drvName string) { exitIfNotForced(reason.RsrcInsufficientCores, "Requested cpu count {{.requested_cpus}} is greater than the available cpus of {{.avail_cpus}}", out.V{"requested_cpus": cpuCount, "avail_cpus": availableCPUs}) } - - // looks good - if availableCPUs >= 2 { - return - } - - if drvName == oci.Docker && runtime.GOOS == "darwin" { - exitIfNotForced(reason.RsrcInsufficientDarwinDockerCores, "Docker Desktop has less than 2 CPUs configured, but Kubernetes requires at least 2 to be available") - } else if drvName == oci.Docker && runtime.GOOS == "windows" { - exitIfNotForced(reason.RsrcInsufficientWindowsDockerCores, "Docker Desktop has less than 2 CPUs configured, but Kubernetes requires at least 2 to be available") - } else { - exitIfNotForced(reason.RsrcInsufficientCores, "{{.driver_name}} has less than 2 CPUs available, but Kubernetes requires at least 2 to be available", out.V{"driver_name": driver.FullName(viper.GetString("driver"))}) - } } // validateFlags validates the supplied flags against known bad combinations @@ -1505,13 +1512,18 @@ func validateChangedMemoryFlags(drvName string) { var req int var err error memString := viper.GetString(memory) - if memString == constants.MaxResources { + if memString == constants.NoLimit && driver.IsKIC(drvName) { + req = 0 + } else if memString == constants.MaxResources { sysLimit, containerLimit, err := memoryLimits(drvName) if err != nil { klog.Warningf("Unable to query memory limits: %+v", err) } req = noLimitMemory(sysLimit, containerLimit, drvName) } else { + if memString == constants.NoLimit { + exit.Message(reason.Usage, "The '{{.name}}' driver does not support --memory=no-limit", out.V{"name": drvName}) + } req, err = util.CalculateSizeInMB(memString) if err != nil { exitIfNotForced(reason.Usage, "Unable to parse memory '{{.memory}}': {{.error}}", out.V{"memory": memString, "error": err}) diff --git a/cmd/minikube/cmd/start_flags.go b/cmd/minikube/cmd/start_flags.go index b1cba66a2a88..a167ae28acd3 100644 --- a/cmd/minikube/cmd/start_flags.go +++ b/cmd/minikube/cmd/start_flags.go @@ -160,8 +160,8 @@ func initMinikubeFlags() { startCmd.Flags().Bool(interactive, true, "Allow user prompts for more information") startCmd.Flags().Bool(dryRun, false, "dry-run mode. Validates configuration, but does not mutate system state") - startCmd.Flags().String(cpus, "2", fmt.Sprintf("Number of CPUs allocated to Kubernetes. Use %q to use the maximum number of CPUs.", constants.MaxResources)) - startCmd.Flags().String(memory, "", fmt.Sprintf("Amount of RAM to allocate to Kubernetes (format: [], where unit = b, k, m or g). Use %q to use the maximum amount of memory.", constants.MaxResources)) + startCmd.Flags().String(cpus, "2", fmt.Sprintf("Number of CPUs allocated to Kubernetes. Use %q to use the maximum number of CPUs. Use %q to not specify a limit (Docker/Podman only)", constants.MaxResources, constants.NoLimit)) + startCmd.Flags().String(memory, "", fmt.Sprintf("Amount of RAM to allocate to Kubernetes (format: [], where unit = b, k, m or g). Use %q to use the maximum amount of memory. Use %q to not specify a limit (Docker/Podman only)", constants.MaxResources, constants.NoLimit)) startCmd.Flags().String(humanReadableDiskSize, defaultDiskSize, "Disk size allocated to the minikube VM (format: [], where unit = b, k, m or g).") startCmd.Flags().Bool(downloadOnly, false, "If true, only download and cache files for later use - don't install or start anything.") startCmd.Flags().Bool(cacheImages, true, "If true, cache docker images for the current bootstrapper and load them into the machine. Always false with --driver=none.") @@ -337,6 +337,12 @@ func generateClusterConfig(cmd *cobra.Command, existing *config.ClusterConfig, k } func getCPUCount(drvName string) int { + if viper.GetString(cpus) == constants.NoLimit { + if driver.IsKIC(drvName) { + return 0 + } + exit.Message(reason.Usage, "The '{{.name}}' driver does not support --cpus=no-limit", out.V{"name": drvName}) + } if viper.GetString(cpus) != constants.MaxResources { return viper.GetInt(cpus) } @@ -370,7 +376,9 @@ func getMemorySize(cmd *cobra.Command, drvName string) int { if cmd.Flags().Changed(memory) || viper.IsSet(memory) { memString := viper.GetString(memory) var err error - if memString == constants.MaxResources { + if memString == constants.NoLimit && driver.IsKIC(drvName) { + mem = 0 + } else if memString == constants.MaxResources { mem = noLimitMemory(sysLimit, containerLimit, drvName) } else { mem, err = pkgutil.CalculateSizeInMB(memString) diff --git a/pkg/drivers/kic/kic.go b/pkg/drivers/kic/kic.go index 0b2f3c7ae204..f4dc27c56ddd 100644 --- a/pkg/drivers/kic/kic.go +++ b/pkg/drivers/kic/kic.go @@ -83,13 +83,16 @@ func (d *Driver) Create() error { ClusterLabel: oci.ProfileLabelKey + "=" + d.MachineName, NodeLabel: oci.NodeLabelKey + "=" + d.NodeConfig.MachineName, CPUs: strconv.Itoa(d.NodeConfig.CPU), - Memory: strconv.Itoa(d.NodeConfig.Memory) + "mb", + Memory: strconv.Itoa(d.NodeConfig.Memory), Envs: d.NodeConfig.Envs, ExtraArgs: append([]string{"--expose", fmt.Sprintf("%d", d.NodeConfig.APIServerPort)}, d.NodeConfig.ExtraArgs...), OCIBinary: d.NodeConfig.OCIBinary, APIServerPort: d.NodeConfig.APIServerPort, GPUs: d.NodeConfig.GPUs, } + if params.Memory != "0" { + params.Memory += "mb" + } networkName := d.NodeConfig.Network if networkName == "" { diff --git a/pkg/drivers/kic/oci/oci.go b/pkg/drivers/kic/oci/oci.go index 986f4a72a245..07dabd37edf3 100644 --- a/pkg/drivers/kic/oci/oci.go +++ b/pkg/drivers/kic/oci/oci.go @@ -147,7 +147,7 @@ func checkRunning(p CreateParams) func() error { } // CreateContainerNode creates a new container node -func CreateContainerNode(p CreateParams) error { +func CreateContainerNode(p CreateParams) error { //nolint to suppress cyclomatic complexity // on windows os, if docker desktop is using Windows Containers. Exit early with error if p.OCIBinary == Docker && runtime.GOOS == "windows" { info, err := DaemonInfo(p.OCIBinary) @@ -203,11 +203,11 @@ func CreateContainerNode(p CreateParams) error { // podman mounts var/lib with no-exec by default https://github.com/containers/libpod/issues/5103 runArgs = append(runArgs, "--volume", fmt.Sprintf("%s:/var:exec", p.Name)) - if memcgSwap { + if memcgSwap && p.Memory != NoLimit { runArgs = append(runArgs, fmt.Sprintf("--memory-swap=%s", p.Memory)) } - if memcg { + if memcg && p.Memory != NoLimit { runArgs = append(runArgs, fmt.Sprintf("--memory=%s", p.Memory)) } @@ -218,10 +218,10 @@ func CreateContainerNode(p CreateParams) error { // ignore apparmore github actions docker: https://github.com/kubernetes/minikube/issues/7624 runArgs = append(runArgs, "--security-opt", "apparmor=unconfined") - if memcg { + if memcg && p.Memory != NoLimit { runArgs = append(runArgs, fmt.Sprintf("--memory=%s", p.Memory)) } - if memcgSwap { + if memcgSwap && p.Memory != NoLimit { // Disable swap by setting the value to match runArgs = append(runArgs, fmt.Sprintf("--memory-swap=%s", p.Memory)) } @@ -244,7 +244,7 @@ func CreateContainerNode(p CreateParams) error { } } - if cpuCfsPeriod && cpuCfsQuota { + if cpuCfsPeriod && cpuCfsQuota && p.CPUs != NoLimit { runArgs = append(runArgs, fmt.Sprintf("--cpus=%s", p.CPUs)) } diff --git a/pkg/drivers/kic/oci/types.go b/pkg/drivers/kic/oci/types.go index 1009df9c70c8..3cb6ec483f53 100644 --- a/pkg/drivers/kic/oci/types.go +++ b/pkg/drivers/kic/oci/types.go @@ -39,6 +39,8 @@ const ( nodeRoleLabelKey = "role.minikube.sigs.k8s.io" // CreatedByLabelKey is applied to any container/volume that is created by minikube created_by.minikube.sigs.k8s.io=true CreatedByLabelKey = "created_by.minikube.sigs.k8s.io" + // NoLimit is the value that specifies that no resource limit should be set + NoLimit = "0" ) // CreateParams are parameters needed to create a container diff --git a/pkg/minikube/constants/constants.go b/pkg/minikube/constants/constants.go index 71dd196a9f32..03e45cbfe9d5 100644 --- a/pkg/minikube/constants/constants.go +++ b/pkg/minikube/constants/constants.go @@ -138,6 +138,8 @@ const ( TimeFormat = time.RFC822 // MaxResources is the value that can be passed into the memory and cpus flags to specify to use maximum resources MaxResources = "max" + // NoLimit is the value that can be passed into the memory and cpus flags to specify to not set the resource limit on the container (Docker & Podman only) + NoLimit = "no-limit" // DefaultCertExpiration is the amount of time in the future a certificate will expire in by default, which is 3 years DefaultCertExpiration = time.Hour * 24 * 365 * 3 diff --git a/pkg/minikube/machine/start.go b/pkg/minikube/machine/start.go index 9f1c4692f462..f1876b7f181a 100644 --- a/pkg/minikube/machine/start.go +++ b/pkg/minikube/machine/start.go @@ -397,7 +397,7 @@ func showHostInfo(h *host.Host, cfg config.ClusterConfig) { } if driver.IsKIC(cfg.Driver) { // TODO:medyagh add free disk space on docker machine register.Reg.SetStep(register.CreatingContainer) - out.Step(style.StartingVM, "Creating {{.driver_name}} {{.machine_type}} (CPUs={{.number_of_cpus}}, Memory={{.memory_size}}MB) ...", out.V{"driver_name": cfg.Driver, "number_of_cpus": cfg.CPUs, "memory_size": cfg.Memory, "machine_type": machineType}) + out.Step(style.StartingVM, "Creating {{.driver_name}} {{.machine_type}} (CPUs={{if not .number_of_cpus}}no-limit{{else}}{{.number_of_cpus}}{{end}}, Memory={{if not .memory_size}}no-limit{{else}}{{.memory_size}}MB{{end}}) ...", out.V{"driver_name": cfg.Driver, "number_of_cpus": cfg.CPUs, "memory_size": cfg.Memory, "machine_type": machineType}) return } register.Reg.SetStep(register.CreatingVM)