summaryrefslogtreecommitdiff
path: root/libpod
diff options
context:
space:
mode:
Diffstat (limited to 'libpod')
-rw-r--r--libpod/container_validate.go1
-rw-r--r--libpod/define/containerstate.go10
-rw-r--r--libpod/define/pod_inspect.go12
-rw-r--r--libpod/networking_linux.go38
-rw-r--r--libpod/options.go42
-rw-r--r--libpod/pod.go104
-rw-r--r--libpod/pod_api.go6
-rw-r--r--libpod/runtime.go2
-rw-r--r--libpod/runtime_pod_infra_linux.go8
-rw-r--r--libpod/stats.go10
10 files changed, 198 insertions, 35 deletions
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/containerstate.go b/libpod/define/containerstate.go
index 5d2bc9099..fc272beaa 100644
--- a/libpod/define/containerstate.go
+++ b/libpod/define/containerstate.go
@@ -1,6 +1,10 @@
package define
-import "github.com/pkg/errors"
+import (
+ "time"
+
+ "github.com/pkg/errors"
+)
// ContainerStatus represents the current state of a container
type ContainerStatus int
@@ -120,12 +124,14 @@ func (s ContainerExecStatus) String() string {
// ContainerStats contains the statistics information for a running container
type ContainerStats struct {
+ AvgCPU float64
ContainerID string
Name string
PerCPU []uint64
CPU float64
CPUNano uint64
CPUSystemNano uint64
+ DataPoints int64
SystemNano uint64
MemUsage uint64
MemLimit uint64
@@ -135,4 +141,6 @@ type ContainerStats struct {
BlockInput uint64
BlockOutput uint64
PIDs uint64
+ UpTime time.Duration
+ Duration uint64
}
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/networking_linux.go b/libpod/networking_linux.go
index 5446841f6..1b775a4f3 100644
--- a/libpod/networking_linux.go
+++ b/libpod/networking_linux.go
@@ -46,6 +46,9 @@ const (
// rootlessCNINSName is the file name for the rootless network namespace bind mount
rootlessCNINSName = "rootless-cni-ns"
+
+ // persistentCNIDir is the directory where the CNI files are stored
+ persistentCNIDir = "/var/lib/cni"
)
// Get an OCICNI network config
@@ -150,14 +153,31 @@ func (r *RootlessCNI) Do(toRun func() error) error {
}
}
- // cni plugins need access to /var and /run
- runDir := filepath.Join(r.dir, "run")
- varDir := filepath.Join(r.dir, "var")
+ // cni plugins need access to /var/lib/cni and /run
+ varDir := ""
+ varTarget := persistentCNIDir
+ // we can only mount to a target dir which exists, check /var/lib/cni recursively
+ // while we could always use /var there are cases where a user might store the cni
+ // configs under /var/custom and this would break
+ for {
+ if _, err := os.Stat(varTarget); err == nil {
+ varDir = filepath.Join(r.dir, strings.TrimPrefix(varTarget, "/"))
+ break
+ }
+ varTarget = filepath.Base(varTarget)
+ if varTarget == "/" {
+ break
+ }
+ }
+ if varDir == "" {
+ return errors.New("failed to stat /var directory")
+ }
// make sure to mount var first
- err = unix.Mount(varDir, "/var", "none", unix.MS_BIND, "")
+ err = unix.Mount(varDir, varTarget, "none", unix.MS_BIND, "")
if err != nil {
- return errors.Wrap(err, "failed to mount /var for rootless cni")
+ return errors.Wrapf(err, "failed to mount %s for rootless cni", varTarget)
}
+ runDir := filepath.Join(r.dir, "run")
// recursive mount to keep the netns mount
err = unix.Mount(runDir, "/run", "none", unix.MS_BIND|unix.MS_REC, "")
if err != nil {
@@ -385,7 +405,7 @@ func (r *Runtime) GetRootlessCNINetNs(new bool) (*RootlessCNI, error) {
// create cni directories to store files
// they will be bind mounted to the correct location in a extra mount ns
- err = os.MkdirAll(filepath.Join(cniDir, "var"), 0700)
+ err = os.MkdirAll(filepath.Join(cniDir, strings.TrimPrefix(persistentCNIDir, "/")), 0700)
if err != nil {
return nil, errors.Wrap(err, "could not create rootless-cni var directory")
}
@@ -888,6 +908,10 @@ func (c *Container) getContainerNetworkInfo() (*define.InspectNetworkSettings, e
if err != nil {
return nil, err
}
+ // see https://github.com/containers/podman/issues/10090
+ // the container has to be locked for syncContainer()
+ netNsCtr.lock.Lock()
+ defer netNsCtr.lock.Unlock()
// Have to sync to ensure that state is populated
if err := netNsCtr.syncContainer(); err != nil {
return nil, err
@@ -1043,7 +1067,7 @@ func resultToBasicNetworkConfig(result *cnitypes.Result) (define.InspectBasicNet
// after itself on an unclean reboot. Return what we're pretty sure is the path
// to CNI's internal files (it's not really exposed to us).
func getCNINetworksDir() (string, error) {
- return "/var/lib/cni/networks", nil
+ return filepath.Join(persistentCNIDir, "networks"), nil
}
type logrusDebugWriter struct {
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: &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
+ }
+}
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.go b/libpod/runtime.go
index 84649bf3e..f53789e89 100644
--- a/libpod/runtime.go
+++ b/libpod/runtime.go
@@ -468,7 +468,7 @@ func makeRuntime(ctx context.Context, runtime *Runtime) (retErr error) {
}
// Set up the CNI net plugin
- netPlugin, err := ocicni.InitCNI(runtime.config.Network.DefaultNetwork, runtime.config.Network.NetworkConfigDir, runtime.config.Network.CNIPluginDirs...)
+ netPlugin, err := ocicni.InitCNINoInotify(runtime.config.Network.DefaultNetwork, runtime.config.Network.NetworkConfigDir, "", runtime.config.Network.CNIPluginDirs...)
if err != nil {
return errors.Wrapf(err, "error configuring CNI network plugin")
}
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
diff --git a/libpod/stats.go b/libpod/stats.go
index f4732b4fc..6f0360ef1 100644
--- a/libpod/stats.go
+++ b/libpod/stats.go
@@ -56,7 +56,11 @@ func (c *Container) GetContainerStats(previousStats *define.ContainerStats) (*de
previousCPU := previousStats.CPUNano
now := uint64(time.Now().UnixNano())
+ stats.Duration = cgroupStats.CPU.Usage.Total
+ stats.UpTime = time.Duration(stats.Duration)
stats.CPU = calculateCPUPercent(cgroupStats, previousCPU, now, previousStats.SystemNano)
+ stats.AvgCPU = calculateAvgCPU(stats.CPU, previousStats.AvgCPU, previousStats.DataPoints)
+ stats.DataPoints = previousStats.DataPoints + 1
stats.MemUsage = cgroupStats.Memory.Usage.Usage
stats.MemLimit = getMemLimit(cgroupStats.Memory.Usage.Limit)
stats.MemPerc = (float64(stats.MemUsage) / float64(stats.MemLimit)) * 100
@@ -127,3 +131,9 @@ func calculateBlockIO(stats *cgroups.Metrics) (read uint64, write uint64) {
}
return
}
+
+// calculateAvgCPU calculates the avg CPU percentage given the previous average and the number of data points.
+func calculateAvgCPU(statsCPU float64, prevAvg float64, prevData int64) float64 {
+ avgPer := ((prevAvg * float64(prevData)) + statsCPU) / (float64(prevData) + 1)
+ return avgPer
+}