Skip to content

Commit 2086bcb

Browse files
authored
Merge pull request #8718 from medyagh/check_docker_deskop
docker/podman: warn if allocated memory is below limit
2 parents c6000e6 + 13357d9 commit 2086bcb

File tree

6 files changed

+114
-21
lines changed

6 files changed

+114
-21
lines changed

.github/workflows/pr.yml

+2-2
Original file line numberDiff line numberDiff line change
@@ -99,11 +99,11 @@ jobs:
9999
echo "--------------------------"
100100
docker version || true
101101
echo "--------------------------"
102-
docker info || true
102+
docker info --format='{{json .}}' || true
103103
echo "--------------------------"
104104
docker system df || true
105105
echo "--------------------------"
106-
docker system info || true
106+
docker system info --format='{{json .}}'|| true
107107
echo "--------------------------"
108108
docker ps || true
109109
echo "--------------------------"

cmd/minikube/cmd/start.go

+57-8
Original file line numberDiff line numberDiff line change
@@ -759,12 +759,13 @@ func memoryLimits(drvName string) (int, int, error) {
759759
containerLimit := 0
760760

761761
if driver.IsKIC(drvName) {
762-
s, err := oci.DaemonInfo(drvName)
762+
s, err := oci.CachedDaemonInfo(drvName)
763763
if err != nil {
764764
return -1, -1, err
765765
}
766766
containerLimit = int(s.TotalMemory / 1024 / 1024)
767767
}
768+
768769
return sysLimit, containerLimit, nil
769770
}
770771

@@ -807,11 +808,13 @@ func suggestMemoryAllocation(sysLimit int, containerLimit int, nodes int) int {
807808
}
808809

809810
// validateMemorySize validates the memory size matches the minimum recommended
810-
func validateMemorySize() {
811-
req, err := util.CalculateSizeInMB(viper.GetString(memory))
811+
func validateMemorySize(req int, drvName string) {
812+
813+
sysLimit, containerLimit, err := memoryLimits(drvName)
812814
if err != nil {
813-
exit.WithCodeT(exit.Config, "Unable to parse memory '{{.memory}}': {{.error}}", out.V{"memory": viper.GetString(memory), "error": err})
815+
glog.Warningf("Unable to query memory limits: %v", err)
814816
}
817+
815818
if req < minUsableMem && !viper.GetBool(force) {
816819
exit.WithCodeT(exit.Config, "Requested memory allocation {{.requested}}MB is less than the usable minimum of {{.minimum}}MB",
817820
out.V{"requested": req, "mininum": minUsableMem})
@@ -820,12 +823,30 @@ func validateMemorySize() {
820823
out.T(out.Notice, "Requested memory allocation ({{.requested}}MB) is less than the recommended minimum {{.recommended}}MB. Kubernetes may crash unexpectedly.",
821824
out.V{"requested": req, "recommended": minRecommendedMem})
822825
}
826+
827+
if driver.IsDockerDesktop(drvName) {
828+
// in Docker Desktop if you allocate 2 GB the docker info shows: Total Memory: 1.945GiB which becomes 1991 when we calculate the MBs
829+
// thats why it is not same number as other drivers which is 2 GB
830+
if containerLimit < 1991 {
831+
out.T(out.Tip, `Increase Docker for Desktop memory to at least 2.5GB or more:
832+
833+
Docker for Desktop > Settings > Resources > Memory
834+
835+
`)
836+
} else if containerLimit < 2997 && sysLimit > 8000 { // for users with more than 8 GB advice 3 GB
837+
out.T(out.Tip, `Your system has {{.system_limit}}MB memory but Docker has only {{.container_limit}}MB. For a better performance increase to at least 3GB.
838+
839+
Docker for Desktop > Settings > Resources > Memory
840+
841+
`, out.V{"container_limit": containerLimit, "system_limit": sysLimit})
842+
}
843+
}
823844
}
824845

825846
// validateCPUCount validates the cpu count matches the minimum recommended
826-
func validateCPUCount(local bool) {
847+
func validateCPUCount(drvName string) {
827848
var cpuCount int
828-
if local {
849+
if driver.BareMetal(drvName) {
829850
// Uses the gopsutil cpu package to count the number of physical cpu cores
830851
ci, err := cpu.Counts(false)
831852
if err != nil {
@@ -839,6 +860,30 @@ func validateCPUCount(local bool) {
839860
if cpuCount < minimumCPUS && !viper.GetBool(force) {
840861
exit.UsageT("Requested cpu count {{.requested_cpus}} is less than the minimum allowed of {{.minimum_cpus}}", out.V{"requested_cpus": cpuCount, "minimum_cpus": minimumCPUS})
841862
}
863+
864+
if driver.IsKIC((drvName)) {
865+
si, err := oci.CachedDaemonInfo(drvName)
866+
if err != nil {
867+
out.T(out.Confused, "Failed to verify '{{.driver_name}} info' will try again ...", out.V{"driver_name": drvName})
868+
si, err = oci.DaemonInfo(drvName)
869+
if err != nil {
870+
exit.UsageT("Ensure your {{.driver_name}} is running and is healthy.", out.V{"driver_name": driver.FullName(drvName)})
871+
}
872+
873+
}
874+
if si.CPUs < 2 {
875+
if drvName == oci.Docker {
876+
out.T(out.Conflict, `Your Docker Desktop has less than 2 CPUs. Increase CPUs for Docker Desktop.
877+
878+
Docker icon > Settings > Resources > CPUs
879+
880+
`)
881+
}
882+
out.T(out.Documentation, "https://docs.docker.com/config/containers/resource_constraints/")
883+
exit.UsageT("Ensure your {{.driver_name}} system has enough CPUs. The minimum allowed is 2 CPUs.", out.V{"driver_name": driver.FullName(viper.GetString("driver"))})
884+
885+
}
886+
}
842887
}
843888

844889
// validateFlags validates the supplied flags against known bad combinations
@@ -855,14 +900,18 @@ func validateFlags(cmd *cobra.Command, drvName string) {
855900
}
856901

857902
if cmd.Flags().Changed(cpus) {
858-
validateCPUCount(driver.BareMetal(drvName))
859903
if !driver.HasResourceLimits(drvName) {
860904
out.WarningT("The '{{.name}}' driver does not respect the --cpus flag", out.V{"name": drvName})
861905
}
862906
}
907+
validateCPUCount(drvName)
863908

864909
if cmd.Flags().Changed(memory) {
865-
validateMemorySize()
910+
req, err := util.CalculateSizeInMB(viper.GetString(memory))
911+
if err != nil {
912+
exit.WithCodeT(exit.Config, "Unable to parse memory '{{.memory}}': {{.error}}", out.V{"memory": viper.GetString(memory), "error": err})
913+
}
914+
validateMemorySize(req, drvName)
866915
if !driver.HasResourceLimits(drvName) {
867916
out.WarningT("The '{{.name}}' driver does not respect the --memory flag", out.V{"name": drvName})
868917
}

cmd/minikube/cmd/start_flags.go

+5
Original file line numberDiff line numberDiff line change
@@ -232,11 +232,16 @@ func generateClusterConfig(cmd *cobra.Command, existing *config.ClusterConfig, k
232232
if err != nil {
233233
exit.WithCodeT(exit.Config, "Generate unable to parse memory '{{.memory}}': {{.error}}", out.V{"memory": viper.GetString(memory), "error": err})
234234
}
235+
if driver.IsKIC(drvName) && mem > containerLimit {
236+
exit.UsageT("{{.driver_name}} has only {{.container_limit}}MB memory but you specified {{.specified_memory}}MB", out.V{"container_limit": containerLimit, "specified_memory": mem, "driver_name": driver.FullName(drvName)})
237+
}
235238

236239
} else {
237240
glog.Infof("Using suggested %dMB memory alloc based on sys=%dMB, container=%dMB", mem, sysLimit, containerLimit)
238241
}
239242

243+
validateMemorySize(mem, drvName)
244+
240245
diskSize, err := pkgutil.CalculateSizeInMB(viper.GetString(humanReadableDiskSize))
241246
if err != nil {
242247
exit.WithCodeT(exit.Config, "Generate unable to parse disk size '{{.diskSize}}': {{.error}}", out.V{"diskSize": viper.GetString(humanReadableDiskSize), "error": err})

pkg/drivers/kic/oci/info.go

+17-9
Original file line numberDiff line numberDiff line change
@@ -32,21 +32,29 @@ type SysInfo struct {
3232
OSType string // container's OsType (windows or linux)
3333
}
3434

35+
var cachedSysInfo *SysInfo
36+
var cachedSysInfoErr *error
37+
38+
// CachedDaemonInfo will run and return a docker/podman info only once per minikube run time. to avoid performance
39+
func CachedDaemonInfo(ociBin string) (SysInfo, error) {
40+
if cachedSysInfo == nil {
41+
si, err := DaemonInfo(ociBin)
42+
cachedSysInfo = &si
43+
cachedSysInfoErr = &err
44+
}
45+
return *cachedSysInfo, *cachedSysInfoErr
46+
}
47+
3548
// DaemonInfo returns common docker/podman daemon system info that minikube cares about
3649
func DaemonInfo(ociBin string) (SysInfo, error) {
37-
var info SysInfo
3850
if ociBin == Podman {
3951
p, err := podmanSystemInfo()
40-
info.CPUs = p.Host.Cpus
41-
info.TotalMemory = p.Host.MemTotal
42-
info.OSType = p.Host.Os
43-
return info, err
52+
cachedSysInfo = &SysInfo{CPUs: p.Host.Cpus, TotalMemory: p.Host.MemTotal, OSType: p.Host.Os}
53+
return *cachedSysInfo, err
4454
}
4555
d, err := dockerSystemInfo()
46-
info.CPUs = d.NCPU
47-
info.TotalMemory = d.MemTotal
48-
info.OSType = d.OSType
49-
return info, err
56+
cachedSysInfo = &SysInfo{CPUs: d.NCPU, TotalMemory: d.MemTotal, OSType: d.OSType}
57+
return *cachedSysInfo, err
5058
}
5159

5260
// dockerSysInfo represents the output of docker system info --format '{{json .}}'

pkg/minikube/driver/driver.go

+30
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ import (
2424
"strings"
2525

2626
"github.com/golang/glog"
27+
"k8s.io/minikube/pkg/drivers/kic/oci"
2728
"k8s.io/minikube/pkg/minikube/config"
2829
"k8s.io/minikube/pkg/minikube/registry"
2930
)
@@ -105,6 +106,22 @@ func IsKIC(name string) bool {
105106
return name == Docker || name == Podman
106107
}
107108

109+
// IsDocker checks if the driver docker
110+
func IsDocker(name string) bool {
111+
return name == Docker
112+
}
113+
114+
// IsKIC checks if the driver is a Docker for Desktop (Docker on windows or MacOs)
115+
// for linux and exotic archs, this will be false
116+
func IsDockerDesktop(name string) bool {
117+
if IsDocker(name) {
118+
if runtime.GOOS == "darwin" || runtime.GOOS == "windows" {
119+
return true
120+
}
121+
}
122+
return false
123+
}
124+
108125
// IsMock checks if the driver is a mock
109126
func IsMock(name string) bool {
110127
return name == Mock
@@ -156,6 +173,19 @@ func NeedsShutdown(name string) bool {
156173
return false
157174
}
158175

176+
// FullName will return the human-known and title formatted name for the driver based on platform
177+
func FullName(name string) string {
178+
switch name {
179+
case oci.Docker:
180+
if IsDockerDesktop(name) {
181+
return "Docker for Desktop"
182+
}
183+
return "Docker Service"
184+
default:
185+
return strings.Title(name)
186+
}
187+
}
188+
159189
// FlagHints are hints for what default options should be used for this driver
160190
type FlagHints struct {
161191
ExtraOptions []string

pkg/minikube/node/advice.go

+3-2
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ import (
2323
"github.com/spf13/viper"
2424
"k8s.io/minikube/pkg/drivers/kic/oci"
2525
"k8s.io/minikube/pkg/minikube/bootstrapper/kubeadm"
26+
"k8s.io/minikube/pkg/minikube/driver"
2627
"k8s.io/minikube/pkg/minikube/exit"
2728
"k8s.io/minikube/pkg/minikube/out"
2829
)
@@ -45,7 +46,7 @@ func MaybeExitWithAdvice(err error) {
4546

4647
if errors.Is(err, oci.ErrCPUCountLimit) {
4748
out.ErrLn("")
48-
out.ErrT(out.Conflict, "{{.name}} doesn't have enough CPUs. ", out.V{"name": viper.GetString("driver")})
49+
out.ErrT(out.Conflict, "{{.name}} doesn't have enough CPUs. ", out.V{"name": driver.FullName(viper.GetString("driver"))})
4950
if runtime.GOOS != "linux" && viper.GetString("driver") == "docker" {
5051
out.T(out.Warning, "Please consider changing your Docker Desktop's resources.")
5152
out.T(out.Documentation, "https://docs.docker.com/config/containers/resource_constraints/")
@@ -54,7 +55,7 @@ func MaybeExitWithAdvice(err error) {
5455
if cpuCount == 2 {
5556
out.T(out.Tip, "Please ensure your system has {{.cpu_counts}} CPU cores.", out.V{"cpu_counts": viper.GetInt(cpus)})
5657
} else {
57-
out.T(out.Tip, "Please ensure your {{.driver_name}} system has access to {{.cpu_counts}} CPU cores or reduce the number of the specified CPUs", out.V{"driver_name": viper.GetString("driver"), "cpu_counts": viper.GetInt(cpus)})
58+
out.T(out.Tip, "Please ensure your {{.driver_name}} system has access to {{.cpu_counts}} CPU cores or reduce the number of the specified CPUs", out.V{"driver_name": driver.FullName(viper.GetString("driver")), "cpu_counts": viper.GetInt(cpus)})
5859
}
5960
}
6061
exit.UsageT("Ensure your {{.driver_name}} system has enough CPUs. The minimum allowed is 2 CPUs.", out.V{"driver_name": viper.GetString("driver")})

0 commit comments

Comments
 (0)