Merge pull request #11686 from cdoern/podDeviceOptions

Pod Device-Read-BPS support
This commit is contained in:
OpenShift Merge Robot 2021-10-01 10:53:14 -04:00 committed by GitHub
commit 81aabc8054
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
11 changed files with 139 additions and 67 deletions

View file

@ -164,14 +164,6 @@ func DefineCreateFlags(cmd *cobra.Command, cf *entities.ContainerCreateOptions,
)
_ = cmd.RegisterFlagCompletionFunc(deviceCgroupRuleFlagName, completion.AutocompleteNone)
deviceReadBpsFlagName := "device-read-bps"
createFlags.StringSliceVar(
&cf.DeviceReadBPs,
deviceReadBpsFlagName, []string{},
"Limit read rate (bytes per second) from a device (e.g. --device-read-bps=/dev/sda:1mb)",
)
_ = cmd.RegisterFlagCompletionFunc(deviceReadBpsFlagName, completion.AutocompleteDefault)
deviceReadIopsFlagName := "device-read-iops"
createFlags.StringSliceVar(
&cf.DeviceReadIOPs,
@ -869,6 +861,7 @@ func DefineCreateFlags(cmd *cobra.Command, cf *entities.ContainerCreateOptions,
volumeDesciption,
)
_ = cmd.RegisterFlagCompletionFunc(volumeFlagName, AutocompleteVolumeFlag)
deviceFlagName := "device"
createFlags.StringSliceVar(
&cf.Devices,
@ -876,4 +869,12 @@ func DefineCreateFlags(cmd *cobra.Command, cf *entities.ContainerCreateOptions,
"Add a host device to the container",
)
_ = cmd.RegisterFlagCompletionFunc(deviceFlagName, completion.AutocompleteDefault)
deviceReadBpsFlagName := "device-read-bps"
createFlags.StringSliceVar(
&cf.DeviceReadBPs,
deviceReadBpsFlagName, []string{},
"Limit read rate (bytes per second) from a device (e.g. --device-read-bps=/dev/sda:1mb)",
)
_ = cmd.RegisterFlagCompletionFunc(deviceReadBpsFlagName, completion.AutocompleteDefault)
}

View file

@ -101,6 +101,7 @@ func create(cmd *cobra.Command, args []string) error {
podIDFD *os.File
imageName string
rawImageName string
podName string
)
labelFile = infraOptions.LabelFile
labels = infraOptions.Label
@ -158,10 +159,12 @@ func create(cmd *cobra.Command, args []string) error {
return err
}
}
podName = createOptions.Name
err = common.ContainerToPodOptions(&infraOptions, &createOptions)
if err != nil {
return err
}
createOptions.Name = podName
}
if cmd.Flag("pod-id-file").Changed {
@ -264,6 +267,17 @@ func create(cmd *cobra.Command, args []string) error {
podSpec.ImageVolumes = podSpec.InfraContainerSpec.ImageVolumes
podSpec.OverlayVolumes = podSpec.InfraContainerSpec.OverlayVolumes
podSpec.Mounts = podSpec.InfraContainerSpec.Mounts
// Marshall and Unmarshal the spec in order to map similar entities
wrapped, err := json.Marshal(podSpec.InfraContainerSpec)
if err != nil {
return err
}
err = json.Unmarshal(wrapped, podSpec)
if err != nil {
return err
}
podSpec.Name = podName
}
PodSpec := entities.PodSpec{PodSpecGen: *podSpec}
response, err := registry.ContainerEngine().PodCreate(context.Background(), PodSpec)

View file

@ -41,7 +41,7 @@ Examples of the List Format:
#### **--device**=_host-device_[**:**_container-device_][**:**_permissions_]
Add a host device to the pod. Optional *permissions* parameter
can be used to specify device permissions It is a combination of
can be used to specify device permissions. It is a combination of
**r** for read, **w** for write, and **m** for **mknod**(2).
Example: **--device=/dev/sdc:/dev/xvdc:rwm**.
@ -55,6 +55,10 @@ Podman may load kernel modules required for using the specified
device. The devices that Podman will load modules for when necessary are:
/dev/fuse.
#### **--device-read-bps**=*path*
Limit read rate (bytes per second) from a device (e.g. --device-read-bps=/dev/sda:1mb)
#### **--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.

View file

@ -531,49 +531,25 @@ func (c *Container) generateInspectContainerHostConfig(ctrSpec *spec.Spec, named
hostConfig.BlkioWeightDevice = append(hostConfig.BlkioWeightDevice, weightDev)
}
handleThrottleDevice := func(devs []spec.LinuxThrottleDevice) ([]define.InspectBlkioThrottleDevice, error) {
out := []define.InspectBlkioThrottleDevice{}
for _, dev := range devs {
key := fmt.Sprintf("%d:%d", dev.Major, dev.Minor)
if deviceNodes == nil {
nodes, err := util.FindDeviceNodes()
if err != nil {
return nil, err
}
deviceNodes = nodes
}
path, ok := deviceNodes[key]
if !ok {
logrus.Infof("Could not locate throttle device %s in system devices", key)
continue
}
throttleDev := define.InspectBlkioThrottleDevice{}
throttleDev.Path = path
throttleDev.Rate = dev.Rate
out = append(out, throttleDev)
}
return out, nil
}
readBps, err := handleThrottleDevice(ctrSpec.Linux.Resources.BlockIO.ThrottleReadBpsDevice)
readBps, err := blkioDeviceThrottle(deviceNodes, ctrSpec.Linux.Resources.BlockIO.ThrottleReadBpsDevice)
if err != nil {
return nil, err
}
hostConfig.BlkioDeviceReadBps = readBps
writeBps, err := handleThrottleDevice(ctrSpec.Linux.Resources.BlockIO.ThrottleWriteBpsDevice)
writeBps, err := blkioDeviceThrottle(deviceNodes, ctrSpec.Linux.Resources.BlockIO.ThrottleWriteBpsDevice)
if err != nil {
return nil, err
}
hostConfig.BlkioDeviceWriteBps = writeBps
readIops, err := handleThrottleDevice(ctrSpec.Linux.Resources.BlockIO.ThrottleReadIOPSDevice)
readIops, err := blkioDeviceThrottle(deviceNodes, ctrSpec.Linux.Resources.BlockIO.ThrottleReadIOPSDevice)
if err != nil {
return nil, err
}
hostConfig.BlkioDeviceReadIOps = readIops
writeIops, err := handleThrottleDevice(ctrSpec.Linux.Resources.BlockIO.ThrottleWriteIOPSDevice)
writeIops, err := blkioDeviceThrottle(deviceNodes, ctrSpec.Linux.Resources.BlockIO.ThrottleWriteIOPSDevice)
if err != nil {
return nil, err
}
@ -894,3 +870,27 @@ func (c *Container) GetDevices(priv bool, ctrSpec spec.Spec, deviceNodes map[str
}
return devices, nil
}
func blkioDeviceThrottle(deviceNodes map[string]string, devs []spec.LinuxThrottleDevice) ([]define.InspectBlkioThrottleDevice, error) {
out := []define.InspectBlkioThrottleDevice{}
for _, dev := range devs {
key := fmt.Sprintf("%d:%d", dev.Major, dev.Minor)
if deviceNodes == nil {
nodes, err := util.FindDeviceNodes()
if err != nil {
return nil, err
}
deviceNodes = nodes
}
path, ok := deviceNodes[key]
if !ok {
logrus.Infof("Could not locate throttle device %s in system devices", key)
continue
}
throttleDev := define.InspectBlkioThrottleDevice{}
throttleDev.Path = path
throttleDev.Rate = dev.Rate
out = append(out, throttleDev)
}
return out, nil
}

View file

@ -61,6 +61,8 @@ type InspectPodData struct {
Mounts []InspectMount `json:"mounts,omitempty"`
// Devices contains the specified host devices
Devices []InspectDevice `json:"devices,omitempty"`
// BlkioDeviceReadBps contains the Read/Access limit for the pod's devices
BlkioDeviceReadBps []InspectBlkioThrottleDevice `json:"device_read_bps,omitempty"`
}
// InspectPodInfraConfig contains the configuration of the pod's infra

View file

@ -584,6 +584,7 @@ func (p *Pod) Inspect() (*define.InspectPodData, error) {
var infraConfig *define.InspectPodInfraConfig
var inspectMounts []define.InspectMount
var devices []define.InspectDevice
var deviceLimits []define.InspectBlkioThrottleDevice
if p.state.InfraContainerID != "" {
infra, err := p.runtime.GetContainer(p.state.InfraContainerID)
if err != nil {
@ -604,12 +605,18 @@ func (p *Pod) Inspect() (*define.InspectPodData, error) {
if err != nil {
return nil, err
}
var nodes map[string]string
devices, err = infra.GetDevices(false, *infra.config.Spec, nodes)
if err != nil {
return nil, err
}
spec := infra.config.Spec
if spec.Linux != nil && spec.Linux.Resources != nil && spec.Linux.Resources.BlockIO != nil {
deviceLimits, err = blkioDeviceThrottle(nodes, spec.Linux.Resources.BlockIO.ThrottleReadBpsDevice)
if err != nil {
return nil, err
}
}
if len(infra.config.ContainerNetworkConfig.DNSServer) > 0 {
infraConfig.DNSServer = make([]string, 0, len(infra.config.ContainerNetworkConfig.DNSServer))
@ -638,28 +645,29 @@ func (p *Pod) Inspect() (*define.InspectPodData, error) {
}
inspectData := define.InspectPodData{
ID: p.ID(),
Name: p.Name(),
Namespace: p.Namespace(),
Created: p.CreatedTime(),
CreateCommand: p.config.CreateCommand,
State: podState,
Hostname: p.config.Hostname,
Labels: p.Labels(),
CreateCgroup: p.config.UsePodCgroup,
CgroupParent: p.CgroupParent(),
CgroupPath: p.state.CgroupPath,
CreateInfra: infraConfig != nil,
InfraContainerID: p.state.InfraContainerID,
InfraConfig: infraConfig,
SharedNamespaces: sharesNS,
NumContainers: uint(len(containers)),
Containers: ctrs,
CPUSetCPUs: p.ResourceLim().CPU.Cpus,
CPUPeriod: p.CPUPeriod(),
CPUQuota: p.CPUQuota(),
Mounts: inspectMounts,
Devices: devices,
ID: p.ID(),
Name: p.Name(),
Namespace: p.Namespace(),
Created: p.CreatedTime(),
CreateCommand: p.config.CreateCommand,
State: podState,
Hostname: p.config.Hostname,
Labels: p.Labels(),
CreateCgroup: p.config.UsePodCgroup,
CgroupParent: p.CgroupParent(),
CgroupPath: p.state.CgroupPath,
CreateInfra: infraConfig != nil,
InfraContainerID: p.state.InfraContainerID,
InfraConfig: infraConfig,
SharedNamespaces: sharesNS,
NumContainers: uint(len(containers)),
Containers: ctrs,
CPUSetCPUs: p.ResourceLim().CPU.Cpus,
CPUPeriod: p.CPUPeriod(),
CPUQuota: p.CPUQuota(),
Mounts: inspectMounts,
Devices: devices,
BlkioDeviceReadBps: deviceLimits,
}
return &inspectData, nil

View file

@ -119,6 +119,7 @@ type PodCreateOptions struct {
CGroupParent string `json:"cgroup_parent,omitempty"`
CreateCommand []string `json:"create_command,omitempty"`
Devices []string `json:"devices,omitempty"`
DeviceReadBPs []string `json:"device_read_bps,omitempty"`
Hostname string `json:"hostname,omitempty"`
Infra bool `json:"infra,omitempty"`
InfraImage string `json:"infra_image,omitempty"`
@ -167,7 +168,7 @@ type ContainerCreateOptions struct {
CPUSetMems string
Devices []string `json:"devices,omitempty"`
DeviceCGroupRule []string
DeviceReadBPs []string
DeviceReadBPs []string `json:"device_read_bps,omitempty"`
DeviceReadIOPs []string
DeviceWriteBPs []string
DeviceWriteIOPs []string
@ -200,7 +201,7 @@ type ContainerCreateOptions struct {
MemoryReservation string
MemorySwap string
MemorySwappiness int64
Name string `json:"container_name,omitempty"`
Name string `json:"container_name"`
NoHealthCheck bool
OOMKillDisable bool
OOMScoreAdj int

View file

@ -191,9 +191,6 @@ func CompleteSpec(ctx context.Context, r *libpod.Runtime, s *specgen.SpecGenerat
if len(s.User) == 0 && inspectData != nil {
s.User = inspectData.Config.User
}
if err := finishThrottleDevices(s); err != nil {
return nil, err
}
// Unless already set via the CLI, check if we need to disable process
// labels or set the defaults.
if len(s.SelinuxOpts) == 0 {
@ -251,10 +248,10 @@ func CompleteSpec(ctx context.Context, r *libpod.Runtime, s *specgen.SpecGenerat
return warnings, nil
}
// finishThrottleDevices takes the temporary representation of the throttle
// FinishThrottleDevices takes the temporary representation of the throttle
// devices in the specgen and looks up the major and major minors. it then
// sets the throttle devices proper in the specgen
func finishThrottleDevices(s *specgen.SpecGenerator) error {
func FinishThrottleDevices(s *specgen.SpecGenerator) error {
if bps := s.ThrottleReadBpsDevice; len(bps) > 0 {
for k, v := range bps {
statT := unix.Stat_t{}
@ -263,6 +260,9 @@ func finishThrottleDevices(s *specgen.SpecGenerator) error {
}
v.Major = (int64(unix.Major(uint64(statT.Rdev))))
v.Minor = (int64(unix.Minor(uint64(statT.Rdev))))
if s.ResourceLimits.BlockIO == nil {
s.ResourceLimits.BlockIO = new(spec.LinuxBlockIO)
}
s.ResourceLimits.BlockIO.ThrottleReadBpsDevice = append(s.ResourceLimits.BlockIO.ThrottleReadBpsDevice, v)
}
}

View file

@ -2,6 +2,7 @@ package generate
import (
"context"
"fmt"
"os"
"path/filepath"
"strings"
@ -52,6 +53,24 @@ func MakeContainer(ctx context.Context, rt *libpod.Runtime, s *specgen.SpecGener
if infraConfig != nil && len(infraConfig.Spec.Linux.Devices) > 0 {
s.DevicesFrom = append(s.DevicesFrom, infraConfig.ID)
}
if infraConfig != nil && infraConfig.Spec.Linux.Resources != nil && infraConfig.Spec.Linux.Resources.BlockIO != nil && len(infraConfig.Spec.Linux.Resources.BlockIO.ThrottleReadBpsDevice) > 0 {
tempDev := make(map[string]spec.LinuxThrottleDevice)
for _, val := range infraConfig.Spec.Linux.Resources.BlockIO.ThrottleReadBpsDevice {
nodes, err := util.FindDeviceNodes()
if err != nil {
return nil, nil, nil, err
}
key := fmt.Sprintf("%d:%d", val.Major, val.Minor)
tempDev[nodes[key]] = spec.LinuxThrottleDevice{Rate: uint64(val.Rate)}
}
for i, dev := range s.ThrottleReadBpsDevice {
tempDev[i] = dev
}
s.ThrottleReadBpsDevice = tempDev
}
if err := FinishThrottleDevices(s); err != nil {
return nil, nil, nil, err
}
// Set defaults for unset namespaces
if s.PidNS.IsDefault() {
defaultNS, err := GetDefaultNamespaceMode("pid", rtc, pod)

View file

@ -201,6 +201,8 @@ type PodResourceConfig struct {
CPUPeriod uint64 `json:"cpu_period,omitempty"`
// CPU quota of the cpuset, determined by --cpus
CPUQuota int64 `json:"cpu_quota,omitempty"`
// ThrottleReadBpsDevice contains the rate at which the devices in the pod can be read from/accessed
ThrottleReadBpsDevice map[string]spec.LinuxThrottleDevice `json:"throttleReadBpsDevice,omitempty"`
}
// NewPodSpecGenerator creates a new pod spec

View file

@ -903,4 +903,25 @@ ENTRYPOINT ["sleep","99999"]
})
It("podman pod create --device-read-bps", func() {
SkipIfRootless("Cannot create devices in /dev in rootless mode")
SkipIfRootlessCgroupsV1("Setting device-read-bps not supported on cgroupv1 for rootless users")
podName := "testPod"
session := podmanTest.Podman([]string{"pod", "create", "--device-read-bps", "/dev/zero:1mb", "--name", podName})
session.WaitWithDefaultTimeout()
Expect(session).Should(Exit(0))
if CGROUPSV2 {
session = podmanTest.Podman([]string{"run", "--rm", "--pod", podName, ALPINE, "sh", "-c", "cat /sys/fs/cgroup/$(sed -e 's|0::||' < /proc/self/cgroup)/io.max"})
} else {
session = podmanTest.Podman([]string{"run", "--rm", "--pod", podName, ALPINE, "cat", "/sys/fs/cgroup/blkio/blkio.throttle.read_bps_device"})
}
session.WaitWithDefaultTimeout()
Expect(session).Should(Exit(0))
if !CGROUPSV2 {
Expect(session.OutputToString()).To(ContainSubstring("1048576"))
}
})
})