mirror of
https://github.com/containers/podman.git
synced 2025-07-04 10:10:32 +08:00
Merge pull request #25010 from robertgzr/play-cdi
Add kube play support for CDI resource allocation
This commit is contained in:
@ -39,6 +39,7 @@ import (
|
||||
spec "github.com/opencontainers/runtime-spec/specs-go"
|
||||
"github.com/sirupsen/logrus"
|
||||
"sigs.k8s.io/yaml"
|
||||
cdiparser "tags.cncf.io/container-device-interface/pkg/parser"
|
||||
)
|
||||
|
||||
func ToPodOpt(ctx context.Context, podName string, p entities.PodCreateOptions, publishAllPorts bool, podYAML *v1.PodTemplateSpec) (entities.PodCreateOptions, error) {
|
||||
@ -276,36 +277,14 @@ func ToSpecGen(ctx context.Context, opts *CtrSpecGenOptions) (*specgen.SpecGener
|
||||
// but apply to the containers with the prefixed name
|
||||
s.SeccompProfilePath = opts.SeccompPaths.FindForContainer(opts.Container.Name)
|
||||
|
||||
s.ResourceLimits = &spec.LinuxResources{}
|
||||
milliCPU := opts.Container.Resources.Limits.Cpu().MilliValue()
|
||||
if milliCPU > 0 {
|
||||
period, quota := util.CoresToPeriodAndQuota(float64(milliCPU) / 1000)
|
||||
s.ResourceLimits.CPU = &spec.LinuxCPU{
|
||||
Quota: "a,
|
||||
Period: &period,
|
||||
}
|
||||
}
|
||||
|
||||
limit, err := quantityToInt64(opts.Container.Resources.Limits.Memory())
|
||||
err = setupContainerResources(s, opts.Container)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to set memory limit: %w", err)
|
||||
return nil, fmt.Errorf("failed to configure container resources: %w", err)
|
||||
}
|
||||
|
||||
memoryRes, err := quantityToInt64(opts.Container.Resources.Requests.Memory())
|
||||
err = setupContainerDevices(s, opts.Container)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to set memory reservation: %w", err)
|
||||
}
|
||||
|
||||
if limit > 0 || memoryRes > 0 {
|
||||
s.ResourceLimits.Memory = &spec.LinuxMemory{}
|
||||
}
|
||||
|
||||
if limit > 0 {
|
||||
s.ResourceLimits.Memory.Limit = &limit
|
||||
}
|
||||
|
||||
if memoryRes > 0 {
|
||||
s.ResourceLimits.Memory.Reservation = &memoryRes
|
||||
return nil, fmt.Errorf("failed to configure container devices: %w", err)
|
||||
}
|
||||
|
||||
ulimitVal, ok := opts.Annotations[define.UlimitAnnotation]
|
||||
@ -840,6 +819,85 @@ func makeHealthCheck(inCmd string, interval int32, retries int32, timeout int32,
|
||||
return &hc, nil
|
||||
}
|
||||
|
||||
func setupContainerResources(s *specgen.SpecGenerator, containerYAML v1.Container) error {
|
||||
s.ResourceLimits = &spec.LinuxResources{}
|
||||
milliCPU := containerYAML.Resources.Limits.Cpu().MilliValue()
|
||||
if milliCPU > 0 {
|
||||
period, quota := util.CoresToPeriodAndQuota(float64(milliCPU) / 1000)
|
||||
s.ResourceLimits.CPU = &spec.LinuxCPU{
|
||||
Quota: "a,
|
||||
Period: &period,
|
||||
}
|
||||
}
|
||||
|
||||
limit, err := quantityToInt64(containerYAML.Resources.Limits.Memory())
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to set memory limit: %w", err)
|
||||
}
|
||||
|
||||
memoryRes, err := quantityToInt64(containerYAML.Resources.Requests.Memory())
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to set memory reservation: %w", err)
|
||||
}
|
||||
|
||||
if limit > 0 || memoryRes > 0 {
|
||||
s.ResourceLimits.Memory = &spec.LinuxMemory{}
|
||||
}
|
||||
|
||||
if limit > 0 {
|
||||
s.ResourceLimits.Memory.Limit = &limit
|
||||
}
|
||||
|
||||
if memoryRes > 0 {
|
||||
s.ResourceLimits.Memory.Reservation = &memoryRes
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
const PodmanDeviceResourcePrefix = "io.podman/device"
|
||||
|
||||
func setupContainerDevices(s *specgen.SpecGenerator, containerYAML v1.Container) error {
|
||||
s.Devices = make([]spec.LinuxDevice, 0)
|
||||
// avoid duplicates
|
||||
devices := make(map[string]bool, 0)
|
||||
|
||||
parse := func(device string) error {
|
||||
vendor, class, name := cdiparser.ParseDevice(device)
|
||||
if vendor == "" {
|
||||
return nil
|
||||
}
|
||||
|
||||
if err := cdiparser.ValidateDeviceName(name); err != nil {
|
||||
// handle internal "fake" CDI
|
||||
if vendor == "podman.io" && class == "device" {
|
||||
device = name
|
||||
} else {
|
||||
return fmt.Errorf("not a qualified name %v: %w", device, err)
|
||||
}
|
||||
}
|
||||
|
||||
if _, ok := devices[device]; !ok {
|
||||
devices[device] = true
|
||||
s.Devices = append(s.Devices, spec.LinuxDevice{Path: device})
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
for key := range containerYAML.Resources.Requests {
|
||||
if err := parse(key.String()); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
for key := range containerYAML.Resources.Limits {
|
||||
if err := parse(key.String()); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func setupSecurityContext(s *specgen.SpecGenerator, securityContext *v1.SecurityContext, podSecurityContext *v1.PodSecurityContext) {
|
||||
if securityContext == nil {
|
||||
securityContext = &v1.SecurityContext{}
|
||||
|
@ -16,6 +16,7 @@ import (
|
||||
"github.com/containers/podman/v5/pkg/k8s.io/apimachinery/pkg/util/intstr"
|
||||
"github.com/containers/podman/v5/pkg/specgen"
|
||||
"github.com/docker/docker/pkg/meminfo"
|
||||
spec "github.com/opencontainers/runtime-spec/specs-go"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"sigs.k8s.io/yaml"
|
||||
)
|
||||
@ -1400,3 +1401,67 @@ func TestTCPLivenessProbe(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestDeviceResource(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
specGenerator specgen.SpecGenerator
|
||||
container v1.Container
|
||||
succeed bool
|
||||
devices []spec.LinuxDevice
|
||||
}{
|
||||
{
|
||||
"ParseQualifiedCDI",
|
||||
specgen.SpecGenerator{},
|
||||
v1.Container{
|
||||
Resources: v1.ResourceRequirements{
|
||||
Limits: v1.ResourceList{
|
||||
"nvidia.com/gpu=0": resource.MustParse("1"),
|
||||
},
|
||||
},
|
||||
},
|
||||
true,
|
||||
[]spec.LinuxDevice{
|
||||
{Path: "nvidia.com/gpu=0"},
|
||||
},
|
||||
},
|
||||
{
|
||||
"ParsePodmanDeviceResource",
|
||||
specgen.SpecGenerator{},
|
||||
v1.Container{
|
||||
Resources: v1.ResourceRequirements{
|
||||
Requests: v1.ResourceList{
|
||||
"podman.io/device=/dev/kmsg": resource.MustParse("1"),
|
||||
},
|
||||
},
|
||||
},
|
||||
true,
|
||||
[]spec.LinuxDevice{
|
||||
{Path: "/dev/kmsg"},
|
||||
},
|
||||
},
|
||||
{
|
||||
"InvalidCDI",
|
||||
specgen.SpecGenerator{},
|
||||
v1.Container{
|
||||
Resources: v1.ResourceRequirements{
|
||||
Limits: v1.ResourceList{
|
||||
"foobar.net/class=///": resource.MustParse("1"),
|
||||
},
|
||||
},
|
||||
},
|
||||
false,
|
||||
[]spec.LinuxDevice{},
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
err := setupContainerDevices(&test.specGenerator, test.container)
|
||||
assert.Equal(t, err == nil, test.succeed)
|
||||
if err == nil {
|
||||
assert.Equal(t, test.specGenerator.Devices, test.devices)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user