Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions docs/source/compute_config/kubernetes.md
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@ k8s:
|k8s | master_timeout | 600 |no | Master pod timeout in seconds. Default 600 seconds |
|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. |
|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. |
|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`. |

## Running on Pod Security Standards Restricted clusters

Expand Down
11 changes: 11 additions & 0 deletions lithops/serverless/backends/k8s/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,11 @@
'seccompProfile': {'type': 'RuntimeDefault'},
}

# Architectures supported by `docker build --platform=linux/<arch>`; matches the
# values emitted by `v1.NodeStatus.NodeInfo.architecture`.
SUPPORTED_RUNTIME_ARCHS = {'amd64', 'arm64'}
DEFAULT_RUNTIME_ARCH = 'amd64'

DEFAULT_GROUP = "batch"
DEFAULT_VERSION = "v1"
MASTER_NAME = "lithops-master"
Expand Down Expand Up @@ -159,6 +164,12 @@ def load_config(config_data):
if value is not None and not isinstance(value, dict):
raise Exception(f"'{key}' under 'k8s' must be a mapping or null, got {type(value).__name__}")

arch = config_data['k8s'].get('runtime_arch')
if arch is not None and arch not in SUPPORTED_RUNTIME_ARCHS:
raise Exception(
f"'runtime_arch' under 'k8s' must be one of {sorted(SUPPORTED_RUNTIME_ARCHS)} or null, got '{arch}'"
)

if 'runtime' in config_data['k8s']:
runtime = config_data['k8s']['runtime']
registry = config_data['k8s']['docker_server']
Expand Down
43 changes: 41 additions & 2 deletions lithops/serverless/backends/k8s/k8s.py
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,43 @@ def _apply_security_context(self, job_res):
if container_sc:
pod_spec['containers'][0]['securityContext'] = container_sc

def _detect_cluster_arch(self):
"""Return the dominant node architecture, or None if mixed/unknown."""
try:
nodes = self.core_api.list_node()
except ApiException as e:
logger.warning(f"Could not list cluster nodes for arch detection: {e}")
return None
archs = {
n.status.node_info.architecture for n in nodes.items
if n.status and n.status.node_info
}
if len(archs) == 1:
return archs.pop()
if len(archs) > 1:
logger.warning(
f"Cluster has mixed node architectures {sorted(archs)}; "
"set 'runtime_arch' in the k8s config to pick one explicitly."
)
return None

def _resolve_runtime_arch(self):
"""Resolve the platform arch for `docker build --platform=linux/<arch>`."""
configured = self.k8s_config.get('runtime_arch')
if configured:
return configured
detected = self._detect_cluster_arch()
if detected in config.SUPPORTED_RUNTIME_ARCHS:
logger.debug(f"Auto-detected cluster arch: {detected}")
return detected
if detected is not None:
logger.warning(
f"Auto-detected cluster arch '{detected}' is not supported by Lithops "
f"(expected one of {sorted(config.SUPPORTED_RUNTIME_ARCHS)}); "
f"falling back to '{config.DEFAULT_RUNTIME_ARCH}'."
)
return config.DEFAULT_RUNTIME_ARCH

def build_runtime(self, docker_image_name, dockerfile, extra_args=[]):
"""
Builds a new runtime from a Docker file and pushes it to the registry
Expand All @@ -143,11 +180,13 @@ def build_runtime(self, docker_image_name, dockerfile, extra_args=[]):

docker_path = utils.get_docker_path()

arch = self._resolve_runtime_arch()
platform = f'linux/{arch}'
if dockerfile:
assert os.path.isfile(dockerfile), f'Cannot locate "{dockerfile}"'
cmd = f'{docker_path} build --platform=linux/amd64 -t {docker_image_name} -f {dockerfile} . '
cmd = f'{docker_path} build --platform={platform} -t {docker_image_name} -f {dockerfile} . '
else:
cmd = f'{docker_path} build --platform=linux/amd64 -t {docker_image_name} . '
cmd = f'{docker_path} build --platform={platform} -t {docker_image_name} . '
cmd = cmd + ' '.join(extra_args)

try:
Expand Down
Loading