Add label to expose gpu to container

In the absence of an upstream implementation of the DeviceRequest API introduced
as part of Docker API v1.40 we roll our own using a feature label.

As per my comment in the code, we fall back to the default behavior of
docker cli's `--gpu` and request single device with the `gpu` capabilty.
The only implementation at the moment is the NVIDIA driver; here:
https://github.com/balena-os/balena-engine/blob/master/daemon/nvidia_linux.go

Background on the composefile implementation:
https://github.com/compose-spec/compose-spec/issues/74
https://github.com/docker/compose/issues/6691

Change-type: patch
Connects-to: https://github.com/balena-os/balena-jetson/pull/75
Signed-off-by: Robert Günzler <robertg@balena.io>
This commit is contained in:
Robert Günzler 2020-06-05 16:53:50 +02:00
parent 92e71a95fa
commit ae646a07ec
4 changed files with 49 additions and 0 deletions

View File

@ -370,6 +370,7 @@ export class Service {
command: [],
cgroupParent: '',
devices,
deviceRequests: [],
dnsOpt: [],
entrypoint: '',
extraHosts: [],
@ -520,6 +521,7 @@ export class Service {
capAdd: container.HostConfig.CapAdd || [],
capDrop: container.HostConfig.CapDrop || [],
devices: container.HostConfig.Devices || [],
deviceRequests: container.HostConfig.DeviceRequests || [],
networks,
memLimit: container.HostConfig.Memory || 0,
memReservation: container.HostConfig.MemoryReservation || 0,
@ -640,6 +642,7 @@ export class Service {
Binds: binds,
CgroupParent: this.config.cgroupParent,
Devices: this.config.devices,
DeviceRequests: this.config.deviceRequests,
Dns: this.config.dns,
DnsOptions: this.config.dnsOpt,
DnsSearch: this.config.dnsSearch,

View File

@ -100,6 +100,7 @@ export interface ServiceConfig {
command: string[];
cgroupParent: string;
devices: DockerDevice[];
deviceRequests: Dockerode.DeviceRequest[];
dns: string | string[];
dnsOpt: string[];
dnsSearch: string | string[];

View File

@ -373,6 +373,15 @@ export function addFeaturesFromLabels(
'io.balena.features.sysfs': () => service.config.volumes.push('/sys:/sys'),
'io.balena.features.procfs': () =>
service.config.volumes.push('/proc:/proc'),
'io.balena.features.gpu': () =>
// TODO once the compose-spec has an implementation we
// should probably follow that, for now we copy the
// bahavior of docker cli
// https://github.com/balena-os/balena-engine-cli/blob/19.03-balena/opts/gpus.go#L81-L89
service.config.deviceRequests.push({
Count: 1,
Capabilities: [['gpu']],
} as Dockerode.DeviceRequest),
};
_.each(features, (fn, label) => {

View File

@ -464,6 +464,42 @@ describe('compose/service', () => {
});
});
describe('io.balena.features.gpu: Docker <-> Compose config', () => {
const gpuDeviceRequest = {
Count: 1,
Capabilities: [['gpu']],
};
it('should succeed from compose object', () => {
const s = Service.fromComposeObject(
{
appId: 123,
serviceId: 123,
serviceName: 'test',
labels: {
'io.balena.features.gpu': '1',
},
},
{ appName: 'test' } as any,
);
expect(s.config)
.to.have.property('deviceRequests')
.that.deep.equals([gpuDeviceRequest]);
});
it('should succeed from docker container', () => {
const dockerCfg = _.cloneDeep(
require('./data/docker-states/simple/inspect.json'),
);
dockerCfg.HostConfig.DeviceRequests = [gpuDeviceRequest];
const s = Service.fromDockerContainer(dockerCfg);
expect(s.config)
.to.have.property('deviceRequests')
.that.deep.equals([gpuDeviceRequest]);
});
});
describe('Docker <-> Compose config', () => {
const omitConfigForComparison = (config: ServiceConfig) =>
_.omit(config, ['running', 'networks']);