From bbd085ad1e3cf9c5b543c907ad7014ccf8a5cb34 Mon Sep 17 00:00:00 2001 From: cdoern Date: Thu, 17 Jun 2021 17:36:35 -0400 Subject: Podman Pod Create --cpus and --cpuset-cpus flags Added logic and handling for two new Podman pod create Flags. --cpus specifies the total number of cores on which the pod can execute, this is a combination of the period and quota for the CPU. --cpuset-cpus is a string value which determines of these available cores, how many we will truly execute on. Signed-off-by: cdoern --- libpod/container_validate.go | 1 - libpod/define/pod_inspect.go | 12 +++++ libpod/options.go | 42 ++++++++++++++- libpod/pod.go | 104 +++++++++++++++++++++++++++++++------- libpod/pod_api.go | 6 +++ libpod/runtime_pod_infra_linux.go | 8 +-- 6 files changed, 147 insertions(+), 26 deletions(-) (limited to 'libpod') diff --git a/libpod/container_validate.go b/libpod/container_validate.go index aae96ae85..6ff46f1b1 100644 --- a/libpod/container_validate.go +++ b/libpod/container_validate.go @@ -131,6 +131,5 @@ func (c *Container) validate() error { if c.config.User == "" && (c.config.Spec.Process.User.UID != 0 || c.config.Spec.Process.User.GID != 0) { return errors.Wrapf(define.ErrInvalidArg, "please set User explicitly via WithUser() instead of in OCI spec directly") } - return nil } diff --git a/libpod/define/pod_inspect.go b/libpod/define/pod_inspect.go index 2fa91166f..67f075b3c 100644 --- a/libpod/define/pod_inspect.go +++ b/libpod/define/pod_inspect.go @@ -51,6 +51,12 @@ type InspectPodData struct { // Containers gives a brief summary of all containers in the pod and // their current status. Containers []InspectPodContainerInfo `json:"Containers,omitempty"` + // CPUPeriod contains the CPU period of the pod + CPUPeriod uint64 `json:"cpu_period,omitempty"` + // CPUQuota contains the CPU quota of the pod + CPUQuota int64 `json:"cpu_quota,omitempty"` + // CPUSetCPUs contains linux specific CPU data for the pod + CPUSetCPUs string `json:"cpuset_cpus,omitempty"` } // InspectPodInfraConfig contains the configuration of the pod's infra @@ -91,6 +97,12 @@ type InspectPodInfraConfig struct { Networks []string // NetworkOptions are additional options for each network NetworkOptions map[string][]string + // CPUPeriod contains the CPU period of the pod + CPUPeriod uint64 `json:"cpu_period,omitempty"` + // CPUQuota contains the CPU quota of the pod + CPUQuota int64 `json:"cpu_quota,omitempty"` + // CPUSetCPUs contains linux specific CPU data for the container + CPUSetCPUs string `json:"cpuset_cpus,omitempty"` } // InspectPodContainerInfo contains information on a container in a pod. diff --git a/libpod/options.go b/libpod/options.go index f2468e41b..b12153512 100644 --- a/libpod/options.go +++ b/libpod/options.go @@ -20,6 +20,7 @@ import ( "github.com/containers/storage" "github.com/containers/storage/pkg/idtools" "github.com/cri-o/ocicni/pkg/ocicni" + "github.com/opencontainers/runtime-spec/specs-go" "github.com/opencontainers/runtime-tools/generate" "github.com/pkg/errors" "github.com/sirupsen/logrus" @@ -559,7 +560,6 @@ func WithMaxLogSize(limit int64) CtrCreateOption { if ctr.valid { return define.ErrRuntimeFinalized } - ctr.config.LogSize = limit return nil @@ -867,7 +867,6 @@ func WithMountNSFrom(nsCtr *Container) CtrCreateOption { if err := checkDependencyContainer(nsCtr, ctr); err != nil { return err } - ctr.config.MountNsCtr = nsCtr.ID() return nil @@ -2359,3 +2358,42 @@ func WithVolatile() CtrCreateOption { return nil } } + +// WithPodCPUPAQ takes the given cpu period and quota and inserts them in the proper place. +func WithPodCPUPAQ(period uint64, quota int64) PodCreateOption { + return func(pod *Pod) error { + if pod.valid { + return define.ErrPodFinalized + } + if pod.CPUPeriod() != 0 && pod.CPUQuota() != 0 { + pod.config.InfraContainer.ResourceLimits.CPU = &specs.LinuxCPU{ + Period: &period, + Quota: "a, + } + } else { + pod.config.InfraContainer.ResourceLimits = &specs.LinuxResources{} + pod.config.InfraContainer.ResourceLimits.CPU = &specs.LinuxCPU{ + Period: &period, + Quota: "a, + } + } + return nil + } +} + +// WithPodCPUSetCPUS computes and sets the Cpus linux resource string which determines the amount of cores, from those available, we are allowed to execute on +func WithPodCPUSetCPUs(inp string) PodCreateOption { + return func(pod *Pod) error { + if pod.valid { + return define.ErrPodFinalized + } + if pod.ResourceLim().CPU.Period != nil { + pod.config.InfraContainer.ResourceLimits.CPU.Cpus = inp + } else { + pod.config.InfraContainer.ResourceLimits = &specs.LinuxResources{} + pod.config.InfraContainer.ResourceLimits.CPU = &specs.LinuxCPU{} + pod.config.InfraContainer.ResourceLimits.CPU.Cpus = inp + } + return nil + } +} diff --git a/libpod/pod.go b/libpod/pod.go index dce2a0c1c..d7a9b15d9 100644 --- a/libpod/pod.go +++ b/libpod/pod.go @@ -1,12 +1,14 @@ package libpod import ( + "context" "net" "time" "github.com/containers/podman/v3/libpod/define" "github.com/containers/podman/v3/libpod/lock" "github.com/cri-o/ocicni/pkg/ocicni" + "github.com/opencontainers/runtime-spec/specs-go" "github.com/pkg/errors" ) @@ -91,25 +93,26 @@ type podState struct { // Generally speaking, aside from those two exceptions, these options will set // the equivalent field in the container's configuration. type InfraContainerConfig struct { - ConmonPidFile string `json:"conmonPidFile"` - HasInfraContainer bool `json:"makeInfraContainer"` - NoNetwork bool `json:"noNetwork,omitempty"` - HostNetwork bool `json:"infraHostNetwork,omitempty"` - PortBindings []ocicni.PortMapping `json:"infraPortBindings"` - StaticIP net.IP `json:"staticIP,omitempty"` - StaticMAC net.HardwareAddr `json:"staticMAC,omitempty"` - UseImageResolvConf bool `json:"useImageResolvConf,omitempty"` - DNSServer []string `json:"dnsServer,omitempty"` - DNSSearch []string `json:"dnsSearch,omitempty"` - DNSOption []string `json:"dnsOption,omitempty"` - UseImageHosts bool `json:"useImageHosts,omitempty"` - HostAdd []string `json:"hostsAdd,omitempty"` - Networks []string `json:"networks,omitempty"` - ExitCommand []string `json:"exitCommand,omitempty"` - InfraImage string `json:"infraImage,omitempty"` - InfraCommand []string `json:"infraCommand,omitempty"` - Slirp4netns bool `json:"slirp4netns,omitempty"` - NetworkOptions map[string][]string `json:"network_options,omitempty"` + ConmonPidFile string `json:"conmonPidFile"` + HasInfraContainer bool `json:"makeInfraContainer"` + NoNetwork bool `json:"noNetwork,omitempty"` + HostNetwork bool `json:"infraHostNetwork,omitempty"` + PortBindings []ocicni.PortMapping `json:"infraPortBindings"` + StaticIP net.IP `json:"staticIP,omitempty"` + StaticMAC net.HardwareAddr `json:"staticMAC,omitempty"` + UseImageResolvConf bool `json:"useImageResolvConf,omitempty"` + DNSServer []string `json:"dnsServer,omitempty"` + DNSSearch []string `json:"dnsSearch,omitempty"` + DNSOption []string `json:"dnsOption,omitempty"` + UseImageHosts bool `json:"useImageHosts,omitempty"` + HostAdd []string `json:"hostsAdd,omitempty"` + Networks []string `json:"networks,omitempty"` + ExitCommand []string `json:"exitCommand,omitempty"` + InfraImage string `json:"infraImage,omitempty"` + InfraCommand []string `json:"infraCommand,omitempty"` + Slirp4netns bool `json:"slirp4netns,omitempty"` + NetworkOptions map[string][]string `json:"network_options,omitempty"` + ResourceLimits *specs.LinuxResources `json:"resource_limits,omitempty"` } // ID retrieves the pod's ID @@ -128,6 +131,45 @@ func (p *Pod) Namespace() string { return p.config.Namespace } +// ResourceLim returns the cpuset resource limits for the pod +func (p *Pod) ResourceLim() *specs.LinuxResources { + resCopy := &specs.LinuxResources{} + if err := JSONDeepCopy(p.config.InfraContainer.ResourceLimits, resCopy); err != nil { + return nil + } + if resCopy != nil && resCopy.CPU != nil { + return resCopy + } + empty := &specs.LinuxResources{ + CPU: &specs.LinuxCPU{}, + } + return empty +} + +// CPUPeriod returns the pod CPU period +func (p *Pod) CPUPeriod() uint64 { + resCopy := &specs.LinuxResources{} + if err := JSONDeepCopy(p.config.InfraContainer.ResourceLimits, resCopy); err != nil { + return 0 + } + if resCopy != nil && resCopy.CPU != nil && resCopy.CPU.Period != nil { + return *resCopy.CPU.Period + } + return 0 +} + +// CPUQuota returns the pod CPU quota +func (p *Pod) CPUQuota() int64 { + resCopy := &specs.LinuxResources{} + if err := JSONDeepCopy(p.config.InfraContainer.ResourceLimits, resCopy); err != nil { + return 0 + } + if resCopy != nil && resCopy.CPU != nil && resCopy.CPU.Quota != nil { + return *resCopy.CPU.Quota + } + return 0 +} + // Labels returns the pod's labels func (p *Pod) Labels() map[string]string { labels := make(map[string]string) @@ -208,7 +250,31 @@ func (p *Pod) CgroupPath() (string, error) { if err := p.updatePod(); err != nil { return "", err } + if p.state.CgroupPath != "" { + return p.state.CgroupPath, nil + } + if !p.HasInfraContainer() { + return "", errors.Wrap(define.ErrNoSuchCtr, "pod has no infra container") + } + + id := p.state.InfraContainerID + if id != "" { + ctr, err := p.runtime.state.Container(id) + if err != nil { + return "", errors.Wrapf(err, "could not get infra") + } + if ctr != nil { + ctr.Start(context.Background(), false) + cgroupPath, err := ctr.CGroupPath() + if err != nil { + return "", errors.Wrapf(err, "could not get container cgroup") + } + p.state.CgroupPath = cgroupPath + p.save() + return cgroupPath, nil + } + } return p.state.CgroupPath, nil } diff --git a/libpod/pod_api.go b/libpod/pod_api.go index 14fe8276c..d8f5d15f8 100644 --- a/libpod/pod_api.go +++ b/libpod/pod_api.go @@ -538,6 +538,9 @@ func (p *Pod) Inspect() (*define.InspectPodData, error) { infraConfig.StaticMAC = p.config.InfraContainer.StaticMAC.String() infraConfig.NoManageResolvConf = p.config.InfraContainer.UseImageResolvConf infraConfig.NoManageHosts = p.config.InfraContainer.UseImageHosts + infraConfig.CPUPeriod = p.CPUPeriod() + infraConfig.CPUQuota = p.CPUQuota() + infraConfig.CPUSetCPUs = p.ResourceLim().CPU.Cpus if len(p.config.InfraContainer.DNSServer) > 0 { infraConfig.DNSServer = make([]string, 0, len(p.config.InfraContainer.DNSServer)) @@ -581,6 +584,9 @@ func (p *Pod) Inspect() (*define.InspectPodData, error) { SharedNamespaces: sharesNS, NumContainers: uint(len(containers)), Containers: ctrs, + CPUSetCPUs: p.ResourceLim().CPU.Cpus, + CPUPeriod: p.CPUPeriod(), + CPUQuota: p.CPUQuota(), } return &inspectData, nil diff --git a/libpod/runtime_pod_infra_linux.go b/libpod/runtime_pod_infra_linux.go index c20153c8d..6b002f65a 100644 --- a/libpod/runtime_pod_infra_linux.go +++ b/libpod/runtime_pod_infra_linux.go @@ -146,7 +146,6 @@ func (r *Runtime) makeInfraContainer(ctx context.Context, p *Pod, imgName, rawIm options = append(options, WithExitCommand(p.config.InfraContainer.ExitCommand)) } } - g.SetRootReadonly(true) g.SetProcessArgs(infraCtrCommand) @@ -173,7 +172,6 @@ func (r *Runtime) makeInfraContainer(ctx context.Context, p *Pod, imgName, rawIm // Ignore mqueue sysctls if not sharing IPC if !p.config.UsePodIPC && strings.HasPrefix(sysctlKey, "fs.mqueue.") { logrus.Infof("Sysctl %s=%s ignored in containers.conf, since IPC Namespace for pod is unused", sysctlKey, sysctlVal) - continue } @@ -188,7 +186,6 @@ func (r *Runtime) makeInfraContainer(ctx context.Context, p *Pod, imgName, rawIm logrus.Infof("Sysctl %s=%s ignored in containers.conf, since UTS Namespace for pod is unused", sysctlKey, sysctlVal) continue } - g.AddLinuxSysctl(sysctlKey, sysctlVal) } @@ -200,7 +197,11 @@ func (r *Runtime) makeInfraContainer(ctx context.Context, p *Pod, imgName, rawIm if len(p.config.InfraContainer.ConmonPidFile) > 0 { options = append(options, WithConmonPidFile(p.config.InfraContainer.ConmonPidFile)) } + newRes := new(spec.LinuxResources) + newRes.CPU = new(spec.LinuxCPU) + newRes.CPU = p.ResourceLim().CPU + g.Config.Linux.Resources.CPU = newRes.CPU return r.newContainer(ctx, g.Config, options...) } @@ -211,7 +212,6 @@ func (r *Runtime) createInfraContainer(ctx context.Context, p *Pod) (*Container, if !r.valid { return nil, define.ErrRuntimeStopped } - imageName := p.config.InfraContainer.InfraImage if imageName == "" { imageName = r.config.Engine.InfraImage -- cgit v1.2.3-54-g00ecf