Skip to content

Commit

Permalink
Podman Pod Create --cpus and --cpuset-cpus flags
Browse files Browse the repository at this point in the history
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 <[email protected]>
  • Loading branch information
cdoern committed Jun 23, 2021
1 parent 510509b commit bbd085a
Show file tree
Hide file tree
Showing 15 changed files with 326 additions and 29 deletions.
3 changes: 2 additions & 1 deletion cmd/podman/containers/create.go
Original file line number Diff line number Diff line change
Expand Up @@ -225,7 +225,6 @@ func createInit(c *cobra.Command) error {
}
cliVals.Env = env
}

if c.Flag("cgroups").Changed && cliVals.CGroupsMode == "split" && registry.IsRemote() {
return errors.Errorf("the option --cgroups=%q is not supported in remote mode", cliVals.CGroupsMode)
}
Expand Down Expand Up @@ -316,6 +315,8 @@ func createPodIfNecessary(s *specgen.SpecGenerator, netOpts *entities.NetOptions
Net: netOpts,
CreateCommand: os.Args,
Hostname: s.ContainerBasicConfig.Hostname,
Cpus: cliVals.CPUS,
CpusetCpus: cliVals.CPUSetCPUs,
}
// Unset config values we passed to the pod to prevent them being used twice for the container and pod.
s.ContainerBasicConfig.Hostname = ""
Expand Down
53 changes: 53 additions & 0 deletions cmd/podman/pods/create.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,13 @@ import (
"fmt"
"io/ioutil"
"os"
"runtime"
"sort"
"strconv"
"strings"

"github.com/containers/common/pkg/completion"
"github.com/containers/common/pkg/sysinfo"
"github.com/containers/podman/v3/cmd/podman/common"
"github.com/containers/podman/v3/cmd/podman/parse"
"github.com/containers/podman/v3/cmd/podman/registry"
Expand All @@ -16,6 +20,7 @@ import (
"github.com/containers/podman/v3/pkg/errorhandling"
"github.com/containers/podman/v3/pkg/specgen"
"github.com/containers/podman/v3/pkg/util"
"github.com/docker/docker/pkg/parsers"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
"github.com/spf13/cobra"
Expand Down Expand Up @@ -55,6 +60,14 @@ func init() {

common.DefineNetFlags(createCommand)

cpusetflagName := "cpuset-cpus"
flags.StringVar(&createOptions.CpusetCpus, cpusetflagName, "", "CPUs in which to allow execution")
_ = createCommand.RegisterFlagCompletionFunc(cpusetflagName, completion.AutocompleteDefault)

cpusflagName := "cpus"
flags.Float64Var(&createOptions.Cpus, cpusflagName, 0.000, "set amount of CPUs for the pod")
_ = createCommand.RegisterFlagCompletionFunc(cpusflagName, completion.AutocompleteDefault)

cgroupParentflagName := "cgroup-parent"
flags.StringVar(&createOptions.CGroupParent, cgroupParentflagName, "", "Set parent cgroup for the pod")
_ = createCommand.RegisterFlagCompletionFunc(cgroupParentflagName, completion.AutocompleteDefault)
Expand Down Expand Up @@ -185,6 +198,46 @@ func create(cmd *cobra.Command, args []string) error {
}
}

numCPU := sysinfo.NumCPU()
if numCPU == 0 {
numCPU = runtime.NumCPU()
}
if createOptions.Cpus > float64(numCPU) {
createOptions.Cpus = float64(numCPU)
}
copy := createOptions.CpusetCpus
cpuSet := createOptions.Cpus
if cpuSet == 0 {
cpuSet = float64(sysinfo.NumCPU())
}
ret, err := parsers.ParseUintList(copy)
copy = ""
if err != nil {
errors.Wrapf(err, "could not parse list")
}
var vals []int
for ind, val := range ret {
if val {
vals = append(vals, ind)
}
}
sort.Ints(vals)
for ind, core := range vals {
if core > int(cpuSet) {
if copy == "" {
copy = "0-" + strconv.Itoa(int(cpuSet))
createOptions.CpusetCpus = copy
break
} else {
createOptions.CpusetCpus = copy
break
}
} else if ind != 0 {
copy += "," + strconv.Itoa(core)
} else {
copy = "" + strconv.Itoa(core)
}
}
response, err := registry.ContainerEngine().PodCreate(context.Background(), createOptions)
if err != nil {
return err
Expand Down
16 changes: 16 additions & 0 deletions docs/source/markdown/podman-pod-create.1.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,22 @@ Add a host to the /etc/hosts file shared between all containers in the pod.

Path to cgroups under which the cgroup for the pod will be created. If the path is not absolute, the path is considered to be relative to the cgroups path of the init process. Cgroups will be created if they do not already exist.

#### **--cpus**=*amount*

Set the total number of CPUs delegated to the pod. Default is 0.000 which indicates that there is no limit on computation power.

#### **--cpuset-cpus**=*amount*

Limit the CPUs to support execution. First CPU is numbered 0. Unlike --cpus this is of type string and parsed as a list of numbers

Format is 0-3,0,1

Examples of the List Format:

0-4,9 # bits 0, 1, 2, 3, 4, and 9 set
0-2,7,12-14 # bits 0, 1, 2, 7, 12, 13, and 14 set


#### **--dns**=*ipaddr*

Set custom DNS servers in the /etc/resolv.conf file that will be shared between all containers in the pod. A special option, "none" is allowed which disables creation of /etc/resolv.conf for the pod.
Expand Down
1 change: 0 additions & 1 deletion libpod/container_validate.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
12 changes: 12 additions & 0 deletions libpod/define/pod_inspect.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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.
Expand Down
42 changes: 40 additions & 2 deletions libpod/options.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -559,7 +560,6 @@ func WithMaxLogSize(limit int64) CtrCreateOption {
if ctr.valid {
return define.ErrRuntimeFinalized
}

ctr.config.LogSize = limit

return nil
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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: &quota,
}
} else {
pod.config.InfraContainer.ResourceLimits = &specs.LinuxResources{}
pod.config.InfraContainer.ResourceLimits.CPU = &specs.LinuxCPU{
Period: &period,
Quota: &quota,
}
}
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
}
}
104 changes: 85 additions & 19 deletions libpod/pod.go
Original file line number Diff line number Diff line change
@@ -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"
)

Expand Down Expand Up @@ -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
Expand All @@ -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)
Expand Down Expand Up @@ -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
}

Expand Down
6 changes: 6 additions & 0 deletions libpod/pod_api.go
Original file line number Diff line number Diff line change
Expand Up @@ -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))
Expand Down Expand Up @@ -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
Expand Down
Loading

0 comments on commit bbd085a

Please sign in to comment.