Skip to content

Commit ac0a0cb

Browse files
authored
Merge pull request #675 from nextcloud/feat/podman-CDI-support
feat(deploy): add support for GPUs under podman 5.4+
2 parents b826154 + 0a9dffa commit ac0a0cb

File tree

1 file changed

+48
-4
lines changed

1 file changed

+48
-4
lines changed

lib/DeployActions/DockerActions.php

Lines changed: 48 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -162,7 +162,7 @@ public function deployExAppHarp(ExApp $exApp, DaemonConfig $daemonConfig, array
162162
'mount_points' => $mountPoints,
163163
'start_container' => true
164164
];
165-
165+
166166
if (isset($params['container_params']['resourceLimits']) && !empty($params['container_params']['resourceLimits'])) {
167167
$createPayload['resource_limits'] = $params['container_params']['resourceLimits'];
168168
}
@@ -529,12 +529,31 @@ public function createContainer(string $dockerUrl, string $imageId, DaemonConfig
529529
$containerParams['NetworkingConfig'] = $networkingConfig;
530530
}
531531

532+
$isPodman = $this->isPodman($dockerUrl);
532533
if (isset($params['computeDevice'])) {
533534
if ($params['computeDevice']['id'] === 'cuda') {
534-
if (isset($params['deviceRequests'])) {
535-
$containerParams['HostConfig']['DeviceRequests'] = $params['deviceRequests'];
535+
if ($isPodman === true) {
536+
// Podman 5.4+: use Docker-compat DeviceRequests with CDI driver.
537+
$selectors = $params['cdiDevices'] ?? ['nvidia.com/gpu=all'];
538+
$containerParams['HostConfig']['DeviceRequests'] = [[
539+
'Driver' => 'cdi',
540+
'DeviceIDs' => $selectors,
541+
]];
542+
// Remove NVIDIA_* envs to avoid hook/handoff conflicts under CDI
543+
if (!empty($containerParams['Env'])) {
544+
$containerParams['Env'] = array_values(array_filter(
545+
$containerParams['Env'],
546+
fn (string $e) =>
547+
!str_starts_with($e, 'NVIDIA_VISIBLE_DEVICES=') &&
548+
!str_starts_with($e, 'NVIDIA_DRIVER_CAPABILITIES=')
549+
));
550+
}
536551
} else {
537-
$containerParams['HostConfig']['DeviceRequests'] = $this->buildDefaultGPUDeviceRequests();
552+
if (isset($params['deviceRequests'])) {
553+
$containerParams['HostConfig']['DeviceRequests'] = $params['deviceRequests'];
554+
} else {
555+
$containerParams['HostConfig']['DeviceRequests'] = $this->buildDefaultGPUDeviceRequests();
556+
}
538557
}
539558
}
540559
if ($params['computeDevice']['id'] === 'rocm') {
@@ -1351,4 +1370,29 @@ private function isLocalSocket(string $host): bool {
13511370
}
13521371
return $isLocalPath;
13531372
}
1373+
1374+
private function buildCompatUrlRaw(string $dockerUrl, string $route): string {
1375+
return rtrim($dockerUrl, '/') . '/' . ltrim($route, '/');
1376+
}
1377+
1378+
private function isPodman(string $dockerUrl): bool {
1379+
try {
1380+
// _ping is unversioned and returns headers we can key on
1381+
$resp = $this->guzzleClient->head($this->buildCompatUrlRaw($dockerUrl, '_ping'));
1382+
if ($resp->hasHeader('Libpod-Api-Version')) {
1383+
return true;
1384+
}
1385+
$server = $resp->getHeaderLine('Server');
1386+
if ($server && stripos($server, 'Libpod') !== false) {
1387+
return true;
1388+
}
1389+
// fallback: /version body
1390+
$resp = $this->guzzleClient->get($this->buildCompatUrlRaw($dockerUrl, 'version'));
1391+
$body = json_decode((string) $resp->getBody(), true);
1392+
$platformName = $body['Platform']['Name'] ?? '';
1393+
return stripos($platformName, 'podman') !== false;
1394+
} catch (\Throwable $e) {
1395+
return false;
1396+
}
1397+
}
13541398
}

0 commit comments

Comments
 (0)