CRI: Add devices implementation and moves GPU to devices

This commit is contained in:
Pengfei Ni 2016-10-31 16:05:02 +08:00
parent c53fee7725
commit e0f89a322b
5 changed files with 89 additions and 10 deletions

View File

@ -162,13 +162,23 @@ func (ds *dockerService) CreateContainer(podSandboxID string, config *runtimeApi
CPUShares: rOpts.GetCpuShares(), CPUShares: rOpts.GetCpuShares(),
CPUQuota: rOpts.GetCpuQuota(), CPUQuota: rOpts.GetCpuQuota(),
CPUPeriod: rOpts.GetCpuPeriod(), CPUPeriod: rOpts.GetCpuPeriod(),
// TODO: Need to set devices.
} }
hc.OomScoreAdj = int(rOpts.GetOomScoreAdj()) hc.OomScoreAdj = int(rOpts.GetOomScoreAdj())
} }
// Note: ShmSize is handled in kube_docker_client.go // Note: ShmSize is handled in kube_docker_client.go
} }
// Set devices for container.
devices := make([]dockercontainer.DeviceMapping, len(config.Devices))
for i, device := range config.Devices {
devices[i] = dockercontainer.DeviceMapping{
PathOnHost: device.GetHostPath(),
PathInContainer: device.GetContainerPath(),
CgroupPermissions: device.GetPermissions(),
}
}
hc.Resources.Devices = devices
var err error var err error
hc.SecurityOpt, err = getContainerSecurityOpts(config.Metadata.GetName(), sandboxConfig, ds.seccompProfileRoot) hc.SecurityOpt, err = getContainerSecurityOpts(config.Metadata.GetName(), sandboxConfig, ds.seccompProfileRoot)
if err != nil { if err != nil {

View File

@ -619,7 +619,6 @@ func (dm *DockerManager) runContainer(
memoryLimit := container.Resources.Limits.Memory().Value() memoryLimit := container.Resources.Limits.Memory().Value()
cpuRequest := container.Resources.Requests.Cpu() cpuRequest := container.Resources.Requests.Cpu()
cpuLimit := container.Resources.Limits.Cpu() cpuLimit := container.Resources.Limits.Cpu()
nvidiaGPULimit := container.Resources.Limits.NvidiaGPU()
var cpuShares int64 var cpuShares int64
// If request is not specified, but limit is, we want request to default to limit. // If request is not specified, but limit is, we want request to default to limit.
// API server does this for new containers, but we repeat this logic in Kubelet // API server does this for new containers, but we repeat this logic in Kubelet
@ -631,17 +630,18 @@ func (dm *DockerManager) runContainer(
// of CPU shares. // of CPU shares.
cpuShares = milliCPUToShares(cpuRequest.MilliValue()) cpuShares = milliCPUToShares(cpuRequest.MilliValue())
} }
var devices []dockercontainer.DeviceMapping
if nvidiaGPULimit.Value() != 0 { // Set devices for container.
// Experimental. For now, we hardcode /dev/nvidia0 no matter what the user asks for devices := make([]dockercontainer.DeviceMapping, len(opts.Devices))
// (we only support one device per node). for i, device := range opts.Devices {
devices = []dockercontainer.DeviceMapping{ devices[i] = dockercontainer.DeviceMapping{
{PathOnHost: "/dev/nvidia0", PathInContainer: "/dev/nvidia0", CgroupPermissions: "mrw"}, PathOnHost: device.PathOnHost,
{PathOnHost: "/dev/nvidiactl", PathInContainer: "/dev/nvidiactl", CgroupPermissions: "mrw"}, PathInContainer: device.PathInContainer,
{PathOnHost: "/dev/nvidia-uvm", PathInContainer: "/dev/nvidia-uvm", CgroupPermissions: "mrw"}, CgroupPermissions: device.Permissions,
} }
} }
binds := makeMountBindings(opts.Mounts) binds := makeMountBindings(opts.Mounts)
// The reason we create and mount the log file in here (not in kubelet) is because // The reason we create and mount the log file in here (not in kubelet) is because
// the file's location depends on the ID of the container, and we need to create and // the file's location depends on the ID of the container, and we need to create and
// mount the file before actually starting the container. // mount the file before actually starting the container.

View File

@ -74,6 +74,23 @@ func (kl *Kubelet) getActivePods() []*api.Pod {
return activePods return activePods
} }
// makeDevices determines the devices for the given container.
// Experimental. For now, we hardcode /dev/nvidia0 no matter what the user asks for
// (we only support one device per node).
// TODO: add support for more than 1 GPU after #28216.
func makeDevices(container *api.Container) []kubecontainer.DeviceInfo {
nvidiaGPULimit := container.Resources.Limits.NvidiaGPU()
if nvidiaGPULimit.Value() != 0 {
return []kubecontainer.DeviceInfo{
{PathOnHost: "/dev/nvidia0", PathInContainer: "/dev/nvidia0", Permissions: "mrw"},
{PathOnHost: "/dev/nvidiactl", PathInContainer: "/dev/nvidiactl", Permissions: "mrw"},
{PathOnHost: "/dev/nvidia-uvm", PathInContainer: "/dev/nvidia-uvm", Permissions: "mrw"},
}
}
return nil
}
// makeMounts determines the mount points for the given container. // makeMounts determines the mount points for the given container.
func makeMounts(pod *api.Pod, podDir string, container *api.Container, hostName, hostDomain, podIP string, podVolumes kubecontainer.VolumeMap) ([]kubecontainer.Mount, error) { func makeMounts(pod *api.Pod, podDir string, container *api.Container, hostName, hostDomain, podIP string, podVolumes kubecontainer.VolumeMap) ([]kubecontainer.Mount, error) {
// Kubernetes only mounts on /etc/hosts if : // Kubernetes only mounts on /etc/hosts if :
@ -252,6 +269,7 @@ func (kl *Kubelet) GenerateRunContainerOptions(pod *api.Pod, container *api.Cont
volumes := kl.volumeManager.GetMountedVolumesForPod(podName) volumes := kl.volumeManager.GetMountedVolumesForPod(podName)
opts.PortMappings = makePortMappings(container) opts.PortMappings = makePortMappings(container)
opts.Devices = makeDevices(container)
opts.Mounts, err = makeMounts(pod, kl.getPodDir(pod.UID), container, hostname, hostDomainName, podIP, volumes) opts.Mounts, err = makeMounts(pod, kl.getPodDir(pod.UID), container, hostname, hostDomainName, podIP, volumes)
if err != nil { if err != nil {

View File

@ -28,6 +28,7 @@ import (
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
"k8s.io/kubernetes/pkg/api" "k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/api/resource"
"k8s.io/kubernetes/pkg/apimachinery/registered" "k8s.io/kubernetes/pkg/apimachinery/registered"
kubecontainer "k8s.io/kubernetes/pkg/kubelet/container" kubecontainer "k8s.io/kubernetes/pkg/kubelet/container"
containertest "k8s.io/kubernetes/pkg/kubelet/container/testing" containertest "k8s.io/kubernetes/pkg/kubelet/container/testing"
@ -1262,3 +1263,36 @@ func TestGetHostPortConflicts(t *testing.T) {
pods = append(pods, expected) pods = append(pods, expected)
assert.True(t, hasHostPortConflicts(pods), "Should have port conflicts") assert.True(t, hasHostPortConflicts(pods), "Should have port conflicts")
} }
func TestMakeDevices(t *testing.T) {
testCases := []struct {
container *api.Container
devices []kubecontainer.DeviceInfo
test string
}{
{
test: "no device",
container: &api.Container{},
devices: nil,
},
{
test: "gpu",
container: &api.Container{
Resources: api.ResourceRequirements{
Limits: map[api.ResourceName]resource.Quantity{
api.ResourceNvidiaGPU: resource.MustParse("1000"),
},
},
},
devices: []kubecontainer.DeviceInfo{
{PathOnHost: "/dev/nvidia0", PathInContainer: "/dev/nvidia0", Permissions: "mrw"},
{PathOnHost: "/dev/nvidiactl", PathInContainer: "/dev/nvidiactl", Permissions: "mrw"},
{PathOnHost: "/dev/nvidia-uvm", PathInContainer: "/dev/nvidia-uvm", Permissions: "mrw"},
},
},
}
for _, test := range testCases {
assert.Equal(t, test.devices, makeDevices(test.container), "[test %q]", test.test)
}
}

View File

@ -151,6 +151,7 @@ func (m *kubeGenericRuntimeManager) generateContainerConfig(container *api.Conta
Labels: newContainerLabels(container, pod), Labels: newContainerLabels(container, pod),
Annotations: newContainerAnnotations(container, pod, restartCount), Annotations: newContainerAnnotations(container, pod, restartCount),
Mounts: m.makeMounts(opts, container, podHasSELinuxLabel), Mounts: m.makeMounts(opts, container, podHasSELinuxLabel),
Devices: makeDevices(opts),
LogPath: &containerLogsPath, LogPath: &containerLogsPath,
Stdin: &container.Stdin, Stdin: &container.Stdin,
StdinOnce: &container.StdinOnce, StdinOnce: &container.StdinOnce,
@ -251,6 +252,22 @@ func (m *kubeGenericRuntimeManager) generateLinuxContainerConfig(container *api.
return linuxConfig return linuxConfig
} }
// makeDevices generates container devices for kubelet runtime api.
func makeDevices(opts *kubecontainer.RunContainerOptions) []*runtimeApi.Device {
devices := make([]*runtimeApi.Device, len(opts.Devices))
for idx := range opts.Devices {
device := opts.Devices[idx]
devices[idx] = &runtimeApi.Device{
HostPath: &device.PathOnHost,
ContainerPath: &device.PathInContainer,
Permissions: &device.Permissions,
}
}
return devices
}
// makeMounts generates container volume mounts for kubelet runtime api. // makeMounts generates container volume mounts for kubelet runtime api.
func (m *kubeGenericRuntimeManager) makeMounts(opts *kubecontainer.RunContainerOptions, container *api.Container, podHasSELinuxLabel bool) []*runtimeApi.Mount { func (m *kubeGenericRuntimeManager) makeMounts(opts *kubecontainer.RunContainerOptions, container *api.Container, podHasSELinuxLabel bool) []*runtimeApi.Mount {
volumeMounts := []*runtimeApi.Mount{} volumeMounts := []*runtimeApi.Mount{}