Repository navigation
feat: tell the user when the Podman Machine runs out of memory and offer more #2249
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
Merged
Merged
Changes from 1 commit
Commits
Show all changes
5 commits
Select commit
Hold shift + click to select a range
2364860
feat: tell the user when the Podman Machine runs out of memory and of…
geodro 27b55f4
fix: guard the memory restart and say when it fails
geodro 1a75bc8
Merge branch 'main' into feat/vm-memory-pressure
geodro bedb266
Merge branch 'main' into feat/vm-memory-pressure
geodro 7301790
fix: say when the VM memory check could not run, and bound its machin…
geodro File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or 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 hidden or 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 hidden or 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 hidden or 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 hidden or 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,76 @@ | ||
| //go:build darwin | ||
|
|
||
| package cli | ||
|
|
||
| import ( | ||
| "fmt" | ||
| "os" | ||
| "os/exec" | ||
| "path/filepath" | ||
| "strconv" | ||
| "strings" | ||
|
|
||
| "github.com/geodro/lerd/internal/podman" | ||
| ) | ||
|
|
||
| // MachineMemoryStatus reports the processes the VM's kernel killed for lack of | ||
| // memory since it booted, read from its serial console log on the host, so no | ||
| // call reaches the VM. The memory sizes are only looked up when there were kills. | ||
| func MachineMemoryStatus() MachineMemory { | ||
| name := selectedMachineName() | ||
| if name == "" { | ||
| return MachineMemory{} | ||
| } | ||
| b, err := os.ReadFile(machineConsoleLog(name)) | ||
| if err != nil { | ||
| return MachineMemory{} | ||
|
greptile-apps[bot] marked this conversation as resolved.
Outdated
|
||
| } | ||
| st := MachineMemory{Kills: podman.ParseOOMKills(string(b))} | ||
| if len(st.Kills) == 0 { | ||
| return st | ||
| } | ||
| out, err := machineQuery("machine", "inspect", "--format", "{{.Resources.Memory}}", name) | ||
| if err != nil { | ||
| return st | ||
|
greptile-apps[bot] marked this conversation as resolved.
Outdated
|
||
| } | ||
| st.CurrentMiB, _ = strconv.ParseInt(strings.TrimSpace(string(out)), 10, 64) | ||
| st.SuggestedMiB = raisedVMMemoryMiB(st.CurrentMiB, hostMemoryGiB()) | ||
| return st | ||
| } | ||
|
|
||
| // machineConsoleLog is where podman keeps the VM's serial console, in the | ||
| // per-user temp dir. launchd jobs do not always carry TMPDIR, hence getconf. | ||
| func machineConsoleLog(name string) string { | ||
| dir := os.Getenv("TMPDIR") | ||
| if dir == "" { | ||
| out, _ := exec.Command("getconf", "DARWIN_USER_TEMP_DIR").Output() | ||
| dir = strings.TrimSpace(string(out)) | ||
| } | ||
| return filepath.Join(dir, "podman", name+".log") | ||
| } | ||
|
|
||
| // ResizeMachineMemory restarts the VM with mib of memory, then runs the normal | ||
| // start so every container comes back with its port forwards. | ||
| func ResizeMachineMemory(mib int64, emit func(StartEvent), skip ...string) error { | ||
| name := selectedMachineName() | ||
| if name == "" { | ||
| return fmt.Errorf("no Podman Machine found; run `lerd start` to create one") | ||
| } | ||
| if ceiling := int64(hostMemoryGiB()) * 1024 / 2; mib < 2048 || mib > ceiling { | ||
| return fmt.Errorf("%d MB is outside 2048 to %d MB, half of this Mac's memory", mib, ceiling) | ||
| } | ||
| release, err := podman.LockLifecycle() | ||
| if err != nil { | ||
| return fmt.Errorf("lifecycle lock: %w", err) | ||
| } | ||
| if emit != nil { | ||
| emit(StartEvent{Phase: "step", Step: "machine"}) | ||
| } | ||
| _ = runMachineStreaming(machineStopTimeout, "machine", "stop", name) | ||
| err = runMachineStreaming(machineQueryTimeout, "machine", "set", "--memory", strconv.FormatInt(mib, 10), name) | ||
| release() | ||
| if err != nil { | ||
| return fmt.Errorf("podman machine set --memory: %w", err) | ||
| } | ||
| return RunStart(emit, skip...) | ||
|
greptile-apps[bot] marked this conversation as resolved.
Outdated
|
||
| } | ||
This file contains hidden or 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,13 @@ | ||
| //go:build !darwin | ||
|
|
||
| package cli | ||
|
|
||
| import "fmt" | ||
|
|
||
| // MachineMemoryStatus is empty off macOS: there is no Podman Machine VM whose | ||
| // memory lerd sizes. | ||
| func MachineMemoryStatus() MachineMemory { return MachineMemory{} } | ||
|
|
||
| func ResizeMachineMemory(int64, func(StartEvent), ...string) error { | ||
| return fmt.Errorf("resizing the Podman Machine is only supported on macOS") | ||
| } |
This file contains hidden or 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 hidden or 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 hidden or 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 hidden or 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,35 @@ | ||
| package podman | ||
|
|
||
| import ( | ||
| "regexp" | ||
| "strconv" | ||
| "strings" | ||
| ) | ||
|
|
||
| var ( | ||
| kernelStamp = regexp.MustCompile(`\[\s*(\d+)\.\d+\]`) | ||
| oomKill = regexp.MustCompile(`Out of memory: Killed process \d+ \(([^)]+)\)`) | ||
| ) | ||
|
|
||
| // ParseOOMKills returns the processes the VM's kernel killed for lack of memory | ||
| // since its last boot, read from the machine's serial console log. The log can | ||
| // span boots, so a kernel uptime stamp going backwards starts the count over. | ||
| func ParseOOMKills(log string) []string { | ||
| var kills []string | ||
| last := int64(-1) | ||
| for _, line := range strings.Split(log, "\n") { | ||
| m := kernelStamp.FindStringSubmatch(line) | ||
| if m == nil { | ||
| continue | ||
| } | ||
| stamp, _ := strconv.ParseInt(m[1], 10, 64) | ||
| if stamp < last { | ||
| kills = nil | ||
| } | ||
| last = stamp | ||
| if k := oomKill.FindStringSubmatch(line); k != nil { | ||
| kills = append(kills, k[1]) | ||
| } | ||
| } | ||
| return kills | ||
| } |
This file contains hidden or 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,32 @@ | ||
| package podman | ||
|
|
||
| import ( | ||
| "reflect" | ||
| "testing" | ||
| ) | ||
|
|
||
| // Trimmed from a real applehv console log: the kernel's uptime stamp starting | ||
| // over marks a reboot, and only the kills after it are this boot's. | ||
| const oomConsoleLog = `[ 0.000000] Booting Linux on physical CPU 0x0000000000 [0x610f0000] | ||
| [708961.947962] Out of memory: Killed process 1318 (dbus-broker-lau) total-vm:9292kB, anon-rss:276kB | ||
| [709000.465360] Out of memory: Killed process 2357719 (mysqld) total-vm:2178448kB, anon-rss:435684kB | ||
| [ 0.000000] Booting Linux on physical CPU 0x0000000000 [0x610f0000] | ||
| [ 2.114023] systemd[1]: Detected virtualization apple. | ||
| [root@localhost ~]# [ 239.106785] Out of memory: Killed process 4157 (mysqld) total-vm:2112916kB, anon-rss:456404kB | ||
| [ 919.398937] Out of memory: Killed process 1144 (systemd) total-vm:24620kB, anon-rss:6588kB | ||
| [ 948.967036] Out of memory: Killed process 3952 (php-fpm) total-vm:268840kB, anon-rss:64160kB | ||
| ` | ||
|
|
||
| func TestParseOOMKills_countsOnlyTheCurrentBoot(t *testing.T) { | ||
| got := ParseOOMKills(oomConsoleLog) | ||
| want := []string{"mysqld", "systemd", "php-fpm"} | ||
| if !reflect.DeepEqual(got, want) { | ||
| t.Errorf("ParseOOMKills = %v, want %v", got, want) | ||
| } | ||
| } | ||
|
|
||
| func TestParseOOMKills_noneOnAHealthyBoot(t *testing.T) { | ||
| if got := ParseOOMKills("[ 0.000000] Booting Linux\n[ 2.1] systemd[1]: ok\n"); len(got) != 0 { | ||
| t.Errorf("ParseOOMKills = %v, want none", got) | ||
| } | ||
| } |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.