mirror of https://github.com/containers/podman.git
feat: Add support for configuring swap in Podman machine
Add `--swap` argument to `podman machine init` command. Passing an int64 value to this flag will trigger the Podman machine ignition file to be generated with a zram-generator.conf file containing the --swap value as the zram-size argument. This file is read by the zram-generator systemd service on boot resulting in a zram swap device being created. Fixes: https://github.com/containers/podman/issues/15980 Signed-off-by: Lewis Roy <lewis@redhat.com>
This commit is contained in:
parent
ce0bac24e5
commit
7b1055a5fb
|
@ -83,6 +83,14 @@ func init() {
|
||||||
)
|
)
|
||||||
_ = initCmd.RegisterFlagCompletionFunc(memoryFlagName, completion.AutocompleteNone)
|
_ = initCmd.RegisterFlagCompletionFunc(memoryFlagName, completion.AutocompleteNone)
|
||||||
|
|
||||||
|
swapFlagName := "swap"
|
||||||
|
flags.Uint64VarP(
|
||||||
|
&initOpts.Swap,
|
||||||
|
swapFlagName, "s", 0,
|
||||||
|
"Swap in MiB",
|
||||||
|
)
|
||||||
|
_ = initCmd.RegisterFlagCompletionFunc(swapFlagName, completion.AutocompleteNone)
|
||||||
|
|
||||||
flags.BoolVar(
|
flags.BoolVar(
|
||||||
&now,
|
&now,
|
||||||
"now", false,
|
"now", false,
|
||||||
|
|
|
@ -119,6 +119,7 @@ func outputTemplate(cmd *cobra.Command, responses []*entities.ListReporter) erro
|
||||||
"CPUs": "CPUS",
|
"CPUs": "CPUS",
|
||||||
"Memory": "MEMORY",
|
"Memory": "MEMORY",
|
||||||
"DiskSize": "DISK SIZE",
|
"DiskSize": "DISK SIZE",
|
||||||
|
"Swap": "SWAP",
|
||||||
})
|
})
|
||||||
|
|
||||||
rpt := report.New(os.Stdout, cmd.Name())
|
rpt := report.New(os.Stdout, cmd.Name())
|
||||||
|
@ -182,6 +183,7 @@ func toMachineFormat(vms []*machine.ListResponse, defaultCon *config.Connection)
|
||||||
response.VMType = vm.VMType
|
response.VMType = vm.VMType
|
||||||
response.CPUs = vm.CPUs
|
response.CPUs = vm.CPUs
|
||||||
response.Memory = strUint(uint64(vm.Memory.ToBytes()))
|
response.Memory = strUint(uint64(vm.Memory.ToBytes()))
|
||||||
|
response.Swap = strUint(uint64(vm.Swap.ToBytes()))
|
||||||
response.DiskSize = strUint(uint64(vm.DiskSize.ToBytes()))
|
response.DiskSize = strUint(uint64(vm.DiskSize.ToBytes()))
|
||||||
response.Port = vm.Port
|
response.Port = vm.Port
|
||||||
response.RemoteUsername = vm.RemoteUsername
|
response.RemoteUsername = vm.RemoteUsername
|
||||||
|
@ -225,6 +227,7 @@ func toHumanFormat(vms []*machine.ListResponse, defaultCon *config.Connection) [
|
||||||
response.VMType = vm.VMType
|
response.VMType = vm.VMType
|
||||||
response.CPUs = vm.CPUs
|
response.CPUs = vm.CPUs
|
||||||
response.Memory = units.BytesSize(float64(vm.Memory.ToBytes()))
|
response.Memory = units.BytesSize(float64(vm.Memory.ToBytes()))
|
||||||
|
response.Swap = units.BytesSize(float64(vm.Swap.ToBytes()))
|
||||||
response.DiskSize = units.BytesSize(float64(vm.DiskSize.ToBytes()))
|
response.DiskSize = units.BytesSize(float64(vm.DiskSize.ToBytes()))
|
||||||
|
|
||||||
humanResponses = append(humanResponses, response)
|
humanResponses = append(humanResponses, response)
|
||||||
|
|
|
@ -104,6 +104,12 @@ if there is no existing remote connection configurations.
|
||||||
|
|
||||||
API forwarding, if available, follows this setting.
|
API forwarding, if available, follows this setting.
|
||||||
|
|
||||||
|
#### **--swap**, **-s**=*number*
|
||||||
|
|
||||||
|
Swap (in MiB). Note: 1024MiB = 1GiB.
|
||||||
|
|
||||||
|
Renders a `zram-generator.conf` file with zram-size set to the value passed to --swap
|
||||||
|
|
||||||
#### **--timezone**
|
#### **--timezone**
|
||||||
|
|
||||||
Set the timezone for the machine and containers. Valid values are `local` or
|
Set the timezone for the machine and containers. Valid values are `local` or
|
||||||
|
|
|
@ -50,6 +50,7 @@ Valid placeholders for the Go template are listed below:
|
||||||
| .RemoteUsername | VM Username for rootless Podman |
|
| .RemoteUsername | VM Username for rootless Podman |
|
||||||
| .Running | Is machine running |
|
| .Running | Is machine running |
|
||||||
| .Stream | Stream name |
|
| .Stream | Stream name |
|
||||||
|
| .Swap | Allocated swap for machine |
|
||||||
| .UserModeNetworking | Whether machine uses user-mode networking |
|
| .UserModeNetworking | Whether machine uses user-mode networking |
|
||||||
| .VMType | VM type |
|
| .VMType | VM type |
|
||||||
|
|
||||||
|
|
|
@ -13,6 +13,7 @@ type ListReporter struct {
|
||||||
VMType string
|
VMType string
|
||||||
CPUs uint64
|
CPUs uint64
|
||||||
Memory string
|
Memory string
|
||||||
|
Swap string
|
||||||
DiskSize string
|
DiskSize string
|
||||||
Port int
|
Port int
|
||||||
RemoteUsername string
|
RemoteUsername string
|
||||||
|
|
|
@ -22,9 +22,7 @@ import (
|
||||||
|
|
||||||
const apiUpTimeout = 20 * time.Second
|
const apiUpTimeout = 20 * time.Second
|
||||||
|
|
||||||
var (
|
var ForwarderBinaryName = "gvproxy"
|
||||||
ForwarderBinaryName = "gvproxy"
|
|
||||||
)
|
|
||||||
|
|
||||||
type Download struct {
|
type Download struct {
|
||||||
Arch string
|
Arch string
|
||||||
|
@ -55,6 +53,7 @@ type ListResponse struct {
|
||||||
VMType string
|
VMType string
|
||||||
CPUs uint64
|
CPUs uint64
|
||||||
Memory strongunits.MiB
|
Memory strongunits.MiB
|
||||||
|
Swap strongunits.MiB
|
||||||
DiskSize strongunits.GiB
|
DiskSize strongunits.GiB
|
||||||
Port int
|
Port int
|
||||||
RemoteUsername string
|
RemoteUsername string
|
||||||
|
|
|
@ -11,6 +11,7 @@ type InitOptions struct {
|
||||||
Volumes []string
|
Volumes []string
|
||||||
IsDefault bool
|
IsDefault bool
|
||||||
Memory uint64
|
Memory uint64
|
||||||
|
Swap uint64
|
||||||
Name string
|
Name string
|
||||||
TimeZone string
|
TimeZone string
|
||||||
URI url.URL
|
URI url.URL
|
||||||
|
|
|
@ -28,6 +28,7 @@ type initMachine struct {
|
||||||
playbook string
|
playbook string
|
||||||
cpus *uint
|
cpus *uint
|
||||||
diskSize *uint
|
diskSize *uint
|
||||||
|
swap *uint
|
||||||
ignitionPath string
|
ignitionPath string
|
||||||
username string
|
username string
|
||||||
image string
|
image string
|
||||||
|
@ -81,6 +82,9 @@ func (i *initMachine) buildCmd(m *machineTestBuilder) []string {
|
||||||
if i.userModeNetworking {
|
if i.userModeNetworking {
|
||||||
cmd = append(cmd, "--user-mode-networking")
|
cmd = append(cmd, "--user-mode-networking")
|
||||||
}
|
}
|
||||||
|
if i.swap != nil {
|
||||||
|
cmd = append(cmd, "--swap", strconv.Itoa(int(*i.swap)))
|
||||||
|
}
|
||||||
name := m.name
|
name := m.name
|
||||||
cmd = append(cmd, name)
|
cmd = append(cmd, name)
|
||||||
|
|
||||||
|
@ -112,11 +116,17 @@ func (i *initMachine) withCPUs(num uint) *initMachine {
|
||||||
i.cpus = &num
|
i.cpus = &num
|
||||||
return i
|
return i
|
||||||
}
|
}
|
||||||
|
|
||||||
func (i *initMachine) withDiskSize(size uint) *initMachine {
|
func (i *initMachine) withDiskSize(size uint) *initMachine {
|
||||||
i.diskSize = &size
|
i.diskSize = &size
|
||||||
return i
|
return i
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (i *initMachine) withSwap(size uint) *initMachine {
|
||||||
|
i.swap = &size
|
||||||
|
return i
|
||||||
|
}
|
||||||
|
|
||||||
func (i *initMachine) withIgnitionPath(path string) *initMachine {
|
func (i *initMachine) withIgnitionPath(path string) *initMachine {
|
||||||
i.ignitionPath = path
|
i.ignitionPath = path
|
||||||
return i
|
return i
|
||||||
|
|
|
@ -238,7 +238,6 @@ var _ = Describe("podman machine init", func() {
|
||||||
Expect(testMachine.Resources.Memory).To(BeEquivalentTo(uint64(2048)))
|
Expect(testMachine.Resources.Memory).To(BeEquivalentTo(uint64(2048)))
|
||||||
}
|
}
|
||||||
Expect(testMachine.SSHConfig.RemoteUsername).To(Equal(remoteUsername))
|
Expect(testMachine.SSHConfig.RemoteUsername).To(Equal(remoteUsername))
|
||||||
|
|
||||||
})
|
})
|
||||||
|
|
||||||
It("machine init with cpus, disk size, memory, timezone", func() {
|
It("machine init with cpus, disk size, memory, timezone", func() {
|
||||||
|
@ -282,6 +281,23 @@ var _ = Describe("podman machine init", func() {
|
||||||
Expect(timezoneSession.outputToString()).To(ContainSubstring("HST"))
|
Expect(timezoneSession.outputToString()).To(ContainSubstring("HST"))
|
||||||
})
|
})
|
||||||
|
|
||||||
|
It("machine init with swap", func() {
|
||||||
|
skipIfWSL("Configuring swap is not supported on WSL")
|
||||||
|
name := randomString()
|
||||||
|
i := new(initMachine)
|
||||||
|
session, err := mb.setName(name).setCmd(i.withImage(mb.imagePath).withSwap(2048).withNow()).run()
|
||||||
|
Expect(err).ToNot(HaveOccurred())
|
||||||
|
Expect(session).To(Exit(0))
|
||||||
|
|
||||||
|
ssh := &sshMachine{}
|
||||||
|
sshSession, err := mb.setName(name).setCmd(ssh.withSSHCommand([]string{"zramctl -bo DISKSIZE"})).run()
|
||||||
|
Expect(err).ToNot(HaveOccurred())
|
||||||
|
Expect(sshSession).To(Exit(0))
|
||||||
|
|
||||||
|
// 2147483648 bytes = 2048MiB
|
||||||
|
Expect(sshSession.outputToString()).To(ContainSubstring("2147483648"))
|
||||||
|
})
|
||||||
|
|
||||||
It("machine init with volume", func() {
|
It("machine init with volume", func() {
|
||||||
if testProvider.VMType() == define.HyperVVirt {
|
if testProvider.VMType() == define.HyperVVirt {
|
||||||
Skip("volumes are not supported on hyperv yet")
|
Skip("volumes are not supported on hyperv yet")
|
||||||
|
@ -373,7 +389,6 @@ var _ = Describe("podman machine init", func() {
|
||||||
output := strings.TrimSpace(sshSession2.outputToString())
|
output := strings.TrimSpace(sshSession2.outputToString())
|
||||||
Expect(output).To(HavePrefix("/run/user"))
|
Expect(output).To(HavePrefix("/run/user"))
|
||||||
Expect(output).To(HaveSuffix("/podman/podman.sock"))
|
Expect(output).To(HaveSuffix("/podman/podman.sock"))
|
||||||
|
|
||||||
})
|
})
|
||||||
|
|
||||||
It("machine init rootful with docker.sock check", func() {
|
It("machine init rootful with docker.sock check", func() {
|
||||||
|
|
|
@ -67,6 +67,7 @@ type DynamicIgnition struct {
|
||||||
Rootful bool
|
Rootful bool
|
||||||
NetRecover bool
|
NetRecover bool
|
||||||
Rosetta bool
|
Rosetta bool
|
||||||
|
Swap uint64
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ign *DynamicIgnition) Write() error {
|
func (ign *DynamicIgnition) Write() error {
|
||||||
|
@ -136,7 +137,7 @@ func (ign *DynamicIgnition) GenerateIgnitionConfig() error {
|
||||||
|
|
||||||
ignStorage := Storage{
|
ignStorage := Storage{
|
||||||
Directories: getDirs(ign.Name),
|
Directories: getDirs(ign.Name),
|
||||||
Files: getFiles(ign.Name, ign.UID, ign.Rootful, ign.VMType, ign.NetRecover),
|
Files: getFiles(ign.Name, ign.UID, ign.Rootful, ign.VMType, ign.NetRecover, ign.Swap),
|
||||||
Links: getLinks(ign.Name),
|
Links: getLinks(ign.Name),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -293,7 +294,7 @@ func getDirs(usrName string) []Directory {
|
||||||
return dirs
|
return dirs
|
||||||
}
|
}
|
||||||
|
|
||||||
func getFiles(usrName string, uid int, rootful bool, vmtype define.VMType, _ bool) []File {
|
func getFiles(usrName string, uid int, rootful bool, vmtype define.VMType, _ bool, swap uint64) []File {
|
||||||
files := make([]File, 0)
|
files := make([]File, 0)
|
||||||
|
|
||||||
lingerExample := parser.NewUnitFile()
|
lingerExample := parser.NewUnitFile()
|
||||||
|
@ -407,6 +408,21 @@ pids_limit=0
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
|
if swap > 0 {
|
||||||
|
files = append(files, File{
|
||||||
|
Node: Node{
|
||||||
|
Path: "/etc/systemd/zram-generator.conf",
|
||||||
|
},
|
||||||
|
FileEmbedded1: FileEmbedded1{
|
||||||
|
Append: nil,
|
||||||
|
Contents: Resource{
|
||||||
|
Source: EncodeDataURLPtr(fmt.Sprintf("[zram0]\nzram-size=%d\n", swap)),
|
||||||
|
},
|
||||||
|
Mode: IntToPtr(0644),
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
// get certs for current user
|
// get certs for current user
|
||||||
userHome, err := os.UserHomeDir()
|
userHome, err := os.UserHomeDir()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
@ -30,9 +30,7 @@ import (
|
||||||
// List is done at the host level to allow for a *possible* future where
|
// List is done at the host level to allow for a *possible* future where
|
||||||
// more than one provider is used
|
// more than one provider is used
|
||||||
func List(vmstubbers []vmconfigs.VMProvider, _ machine.ListOptions) ([]*machine.ListResponse, error) {
|
func List(vmstubbers []vmconfigs.VMProvider, _ machine.ListOptions) ([]*machine.ListResponse, error) {
|
||||||
var (
|
var lrs []*machine.ListResponse
|
||||||
lrs []*machine.ListResponse
|
|
||||||
)
|
|
||||||
|
|
||||||
for _, s := range vmstubbers {
|
for _, s := range vmstubbers {
|
||||||
dirs, err := env.GetMachineDirs(s.VMType())
|
dirs, err := env.GetMachineDirs(s.VMType())
|
||||||
|
@ -49,15 +47,15 @@ func List(vmstubbers []vmconfigs.VMProvider, _ machine.ListOptions) ([]*machine.
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
lr := machine.ListResponse{
|
lr := machine.ListResponse{
|
||||||
Name: name,
|
Name: name,
|
||||||
CreatedAt: mc.Created,
|
CreatedAt: mc.Created,
|
||||||
LastUp: mc.LastUp,
|
LastUp: mc.LastUp,
|
||||||
Running: state == machineDefine.Running,
|
Running: state == machineDefine.Running,
|
||||||
Starting: mc.Starting,
|
Starting: mc.Starting,
|
||||||
//Stream: "", // No longer applicable
|
|
||||||
VMType: s.VMType().String(),
|
VMType: s.VMType().String(),
|
||||||
CPUs: mc.Resources.CPUs,
|
CPUs: mc.Resources.CPUs,
|
||||||
Memory: mc.Resources.Memory,
|
Memory: mc.Resources.Memory,
|
||||||
|
Swap: mc.Swap,
|
||||||
DiskSize: mc.Resources.DiskSize,
|
DiskSize: mc.Resources.DiskSize,
|
||||||
Port: mc.SSH.Port,
|
Port: mc.SSH.Port,
|
||||||
RemoteUsername: mc.SSH.RemoteUsername,
|
RemoteUsername: mc.SSH.RemoteUsername,
|
||||||
|
@ -204,13 +202,13 @@ func Init(opts machineDefine.InitOptions, mp vmconfigs.VMProvider) error {
|
||||||
VMType: mp.VMType(),
|
VMType: mp.VMType(),
|
||||||
WritePath: ignitionFile.GetPath(),
|
WritePath: ignitionFile.GetPath(),
|
||||||
Rootful: opts.Rootful,
|
Rootful: opts.Rootful,
|
||||||
|
Swap: opts.Swap,
|
||||||
})
|
})
|
||||||
|
|
||||||
// If the user provides an ignition file, we need to
|
// If the user provides an ignition file, we need to
|
||||||
// copy it into the conf dir
|
// copy it into the conf dir
|
||||||
if len(opts.IgnitionPath) > 0 {
|
if len(opts.IgnitionPath) > 0 {
|
||||||
err = ignBuilder.BuildWithIgnitionFile(opts.IgnitionPath)
|
err = ignBuilder.BuildWithIgnitionFile(opts.IgnitionPath)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,6 +27,8 @@ type MachineConfig struct {
|
||||||
SSH SSHConfig
|
SSH SSHConfig
|
||||||
Version uint
|
Version uint
|
||||||
|
|
||||||
|
Swap strongunits.MiB
|
||||||
|
|
||||||
// Image stuff
|
// Image stuff
|
||||||
imageDescription machineImage //nolint:unused
|
imageDescription machineImage //nolint:unused
|
||||||
|
|
||||||
|
|
|
@ -81,6 +81,10 @@ func NewMachineConfig(opts define.InitOptions, dirs *define.MachineDirs, sshIden
|
||||||
}
|
}
|
||||||
mc.Resources = mrc
|
mc.Resources = mrc
|
||||||
|
|
||||||
|
if opts.Swap > 0 {
|
||||||
|
mc.Swap = strongunits.MiB(opts.Swap)
|
||||||
|
}
|
||||||
|
|
||||||
sshPort, err := ports.AllocateMachinePort()
|
sshPort, err := ports.AllocateMachinePort()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
|
Loading…
Reference in New Issue