Skip to content

Commit 8fcad99

Browse files
mskuratowskiMaciej Skuratowski
andauthored
[K8s] Auto-detect cluster architecture for runtime build (closes #1462) (#1465)
Co-authored-by: Maciej Skuratowski <mskuratowski@Maciejs-MacBook-Air.local>
1 parent 8e868b3 commit 8fcad99

3 files changed

Lines changed: 53 additions & 2 deletions

File tree

docs/source/compute_config/kubernetes.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,7 @@ k8s:
7878
|k8s | master_timeout | 600 |no | Master pod timeout in seconds. Default 600 seconds |
7979
|k8s | container_security_context | PSS Baseline (drop ALL caps, no privilege escalation, RuntimeDefault seccomp) | no | Mapping injected as the container `securityContext` on every Lithops pod. Set to `null` to disable. |
8080
|k8s | pod_security_context | | no | Mapping injected as the pod-level `securityContext`. Required for clusters enforcing Pod Security Standards Restricted (e.g. EGI Rancher, GKE Autopilot, OpenShift). Requires a non-root runtime image. |
81+
|k8s | runtime_arch | auto-detected from cluster nodes; falls back to `amd64` if mixed or unknown | no | Architecture passed to `docker build --platform=linux/<arch>`. Set explicitly when targeting a specific architecture on a mixed-arch cluster. Allowed values: `amd64`, `arm64`. |
8182

8283
## Running on Pod Security Standards Restricted clusters
8384

lithops/serverless/backends/k8s/config.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,11 @@
3434
'seccompProfile': {'type': 'RuntimeDefault'},
3535
}
3636

37+
# Architectures supported by `docker build --platform=linux/<arch>`; matches the
38+
# values emitted by `v1.NodeStatus.NodeInfo.architecture`.
39+
SUPPORTED_RUNTIME_ARCHS = {'amd64', 'arm64'}
40+
DEFAULT_RUNTIME_ARCH = 'amd64'
41+
3742
DEFAULT_GROUP = "batch"
3843
DEFAULT_VERSION = "v1"
3944
MASTER_NAME = "lithops-master"
@@ -159,6 +164,12 @@ def load_config(config_data):
159164
if value is not None and not isinstance(value, dict):
160165
raise Exception(f"'{key}' under 'k8s' must be a mapping or null, got {type(value).__name__}")
161166

167+
arch = config_data['k8s'].get('runtime_arch')
168+
if arch is not None and arch not in SUPPORTED_RUNTIME_ARCHS:
169+
raise Exception(
170+
f"'runtime_arch' under 'k8s' must be one of {sorted(SUPPORTED_RUNTIME_ARCHS)} or null, got '{arch}'"
171+
)
172+
162173
if 'runtime' in config_data['k8s']:
163174
runtime = config_data['k8s']['runtime']
164175
registry = config_data['k8s']['docker_server']

lithops/serverless/backends/k8s/k8s.py

Lines changed: 41 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,43 @@ def _apply_security_context(self, job_res):
135135
if container_sc:
136136
pod_spec['containers'][0]['securityContext'] = container_sc
137137

138+
def _detect_cluster_arch(self):
139+
"""Return the dominant node architecture, or None if mixed/unknown."""
140+
try:
141+
nodes = self.core_api.list_node()
142+
except ApiException as e:
143+
logger.warning(f"Could not list cluster nodes for arch detection: {e}")
144+
return None
145+
archs = {
146+
n.status.node_info.architecture for n in nodes.items
147+
if n.status and n.status.node_info
148+
}
149+
if len(archs) == 1:
150+
return archs.pop()
151+
if len(archs) > 1:
152+
logger.warning(
153+
f"Cluster has mixed node architectures {sorted(archs)}; "
154+
"set 'runtime_arch' in the k8s config to pick one explicitly."
155+
)
156+
return None
157+
158+
def _resolve_runtime_arch(self):
159+
"""Resolve the platform arch for `docker build --platform=linux/<arch>`."""
160+
configured = self.k8s_config.get('runtime_arch')
161+
if configured:
162+
return configured
163+
detected = self._detect_cluster_arch()
164+
if detected in config.SUPPORTED_RUNTIME_ARCHS:
165+
logger.debug(f"Auto-detected cluster arch: {detected}")
166+
return detected
167+
if detected is not None:
168+
logger.warning(
169+
f"Auto-detected cluster arch '{detected}' is not supported by Lithops "
170+
f"(expected one of {sorted(config.SUPPORTED_RUNTIME_ARCHS)}); "
171+
f"falling back to '{config.DEFAULT_RUNTIME_ARCH}'."
172+
)
173+
return config.DEFAULT_RUNTIME_ARCH
174+
138175
def build_runtime(self, docker_image_name, dockerfile, extra_args=[]):
139176
"""
140177
Builds a new runtime from a Docker file and pushes it to the registry
@@ -143,11 +180,13 @@ def build_runtime(self, docker_image_name, dockerfile, extra_args=[]):
143180

144181
docker_path = utils.get_docker_path()
145182

183+
arch = self._resolve_runtime_arch()
184+
platform = f'linux/{arch}'
146185
if dockerfile:
147186
assert os.path.isfile(dockerfile), f'Cannot locate "{dockerfile}"'
148-
cmd = f'{docker_path} build --platform=linux/amd64 -t {docker_image_name} -f {dockerfile} . '
187+
cmd = f'{docker_path} build --platform={platform} -t {docker_image_name} -f {dockerfile} . '
149188
else:
150-
cmd = f'{docker_path} build --platform=linux/amd64 -t {docker_image_name} . '
189+
cmd = f'{docker_path} build --platform={platform} -t {docker_image_name} . '
151190
cmd = cmd + ' '.join(extra_args)
152191

153192
try:

0 commit comments

Comments
 (0)