diff options
| author | Павел Жуков <33721692+LeaveMyYard@users.noreply.github.com> | 2023-04-13 11:37:32 +0300 |
|---|---|---|
| committer | Павел Жуков <33721692+LeaveMyYard@users.noreply.github.com> | 2023-04-13 11:37:32 +0300 |
| commit | b0222c746d20c0352ab37cfe6527197afaab78a4 (patch) | |
| tree | 8d01dd35e74eb5eef0380cd59a8c2572a2fc10e7 | |
| parent | 86e05a612ed90e76693ac1159120d7c0b3537e41 (diff) | |
Make KRR work from inside the cluster
| -rw-r--r-- | .dockerignore | 17 | ||||
| -rw-r--r-- | Dockerfile | 30 | ||||
| -rw-r--r-- | pyproject.toml | 2 | ||||
| -rw-r--r-- | robusta_krr/core/integrations/kubernetes.py | 24 | ||||
| -rw-r--r-- | robusta_krr/core/integrations/prometheus.py | 2 | ||||
| -rw-r--r-- | robusta_krr/core/models/config.py | 16 | ||||
| -rw-r--r-- | robusta_krr/core/models/objects.py | 2 | ||||
| -rw-r--r-- | robusta_krr/core/runner.py | 2 | ||||
| -rw-r--r-- | robusta_krr/utils/service_discovery.py | 1 |
9 files changed, 79 insertions, 17 deletions
diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..240e615 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,17 @@ +# .dockerignore +__pycache__ +*.pyc +*.pyo +*.pyd + +# Exclude development files +.git +.gitignore +Dockerfile +*.md +*.txt +.vscode + +# Exclude logs and cache +logs/ +cache/ diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..56a182f --- /dev/null +++ b/Dockerfile @@ -0,0 +1,30 @@ +# Use the official Python 3.9 slim image as the base image +FROM python:3.9-slim as builder + +# Set the working directory +WORKDIR /app + +# Install system dependencies required for Poetry +RUN apt-get update && \ + apt-get install --no-install-recommends -y curl && \ + apt-get clean && \ + rm -rf /var/lib/apt/lists/* + +# Install Poetry +RUN curl -sSL https://install.python-poetry.org | python - + +# Add Poetry to the PATH +ENV PATH="/root/.local/bin:${PATH}" + +# Copy the pyproject.toml files +COPY pyproject.toml ./ + +# Install the project dependencies +RUN poetry config virtualenvs.create false \ + && poetry install --no-dev --no-interaction --no-ansi --no-root + +# Copy the rest of the application code +COPY . . + +# Run the application using 'poetry run krr simple' +CMD ["python", "krr.py", "simple"] diff --git a/pyproject.toml b/pyproject.toml index 7e9eba6..44b6490 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -20,7 +20,7 @@ include_trailing_comma = true krr = "robusta_krr.main:run" [tool.poetry.dependencies] -python = ">=3.9" +python = ">=3.9,<3.12" typer = {extras = ["all"], version = "^0.7.0"} pydantic = "1.10.7" kubernetes = "^26.1.0" diff --git a/robusta_krr/core/integrations/kubernetes.py b/robusta_krr/core/integrations/kubernetes.py index 3affe7e..c4a3fc8 100644 --- a/robusta_krr/core/integrations/kubernetes.py +++ b/robusta_krr/core/integrations/kubernetes.py @@ -1,6 +1,6 @@ import asyncio import itertools -from typing import Union +from typing import Optional, Union from kubernetes import client, config # type: ignore from kubernetes.client.models import ( @@ -22,11 +22,11 @@ from robusta_krr.utils.configurable import Configurable class ClusterLoader(Configurable): - def __init__(self, cluster: str, *args, **kwargs): + def __init__(self, cluster: Optional[str], *args, **kwargs): super().__init__(*args, **kwargs) self.cluster = cluster - self.api_client = config.new_client_from_config(context=cluster) + self.api_client = config.new_client_from_config(context=cluster) if cluster is not None else None self.apps = client.AppsV1Api(api_client=self.api_client) self.batch = client.BatchV1Api(api_client=self.api_client) self.core = client.CoreV1Api(api_client=self.api_client) @@ -168,17 +168,17 @@ class ClusterLoader(Configurable): class KubernetesLoader(Configurable): - def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) - config.load_kube_config() - - async def list_clusters(self) -> list[str]: + async def list_clusters(self) -> Optional[list[str]]: """List all clusters. Returns: A list of clusters. """ + if self.config.inside_cluster: + self.debug("Working inside the cluster") + return None + contexts, current_context = await asyncio.to_thread(config.list_kube_config_contexts) self.debug(f"Found {len(contexts)} clusters: {', '.join([context['name'] for context in contexts])}") @@ -196,13 +196,17 @@ class KubernetesLoader(Configurable): return [context["name"] for context in contexts if context["name"] in self.config.clusters] - async def list_scannable_objects(self, clusters: list[str]) -> list[K8sObjectData]: + async def list_scannable_objects(self, clusters: Optional[list[str]]) -> list[K8sObjectData]: """List all scannable objects. Returns: A list of scannable objects. """ - cluster_loaders = [ClusterLoader(cluster=cluster, config=self.config) for cluster in clusters] + if clusters is None: + cluster_loaders = [ClusterLoader(cluster=None, config=self.config)] + else: + cluster_loaders = [ClusterLoader(cluster=cluster, config=self.config) for cluster in clusters] + objects = await asyncio.gather(*[cluster_loader.list_scannable_objects() for cluster_loader in cluster_loaders]) return list(itertools.chain(*objects)) diff --git a/robusta_krr/core/integrations/prometheus.py b/robusta_krr/core/integrations/prometheus.py index a52dde7..39c7f00 100644 --- a/robusta_krr/core/integrations/prometheus.py +++ b/robusta_krr/core/integrations/prometheus.py @@ -67,7 +67,7 @@ class PrometheusLoader(Configurable): self.auth_header = self.config.prometheus_auth_header self.ssl_enabled = self.config.prometheus_ssl_enabled - self.api_client = k8s_config.new_client_from_config(context=cluster) + self.api_client = k8s_config.new_client_from_config(context=cluster) if cluster is not None else None self.prometheus_discovery = PrometheusDiscovery(config=self.config) self.url = self.config.prometheus_url diff --git a/robusta_krr/core/models/config.py b/robusta_krr/core/models/config.py index 153f9d3..96c4e61 100644 --- a/robusta_krr/core/models/config.py +++ b/robusta_krr/core/models/config.py @@ -1,10 +1,19 @@ from typing import Literal, Optional, Union import pydantic as pd +from kubernetes import config +from kubernetes.config.config_exception import ConfigException from robusta_krr.core.abstract.formatters import BaseFormatter from robusta_krr.core.abstract.strategies import AnyStrategy, BaseStrategy +try: + config.load_incluster_config() + IN_CLUSTER = True +except ConfigException: + config.load_kube_config() + IN_CLUSTER = False + class Config(pd.BaseSettings): quiet: bool = pd.Field(False) @@ -13,9 +22,6 @@ class Config(pd.BaseSettings): clusters: Union[list[str], Literal["*"], None] = None namespaces: Union[list[str], Literal["*"]] = pd.Field("*") - # Make this True if you are running KRR inside the cluster - inside_cluster: bool = pd.Field(False) - # Value settings cpu_min_value: int = pd.Field(5, ge=0) # in millicores memory_min_value: int = pd.Field(10, ge=0) # in megabytes @@ -63,3 +69,7 @@ class Config(pd.BaseSettings): def validate_format(cls, v: str) -> str: BaseFormatter.find(v) # NOTE: raises if strategy is not found return v + + @property + def inside_cluster(self) -> bool: + return IN_CLUSTER diff --git a/robusta_krr/core/models/objects.py b/robusta_krr/core/models/objects.py index 74f07ac..8c8acfd 100644 --- a/robusta_krr/core/models/objects.py +++ b/robusta_krr/core/models/objects.py @@ -6,7 +6,7 @@ from robusta_krr.core.models.allocations import ResourceAllocations class K8sObjectData(pd.BaseModel): - cluster: str + cluster: Optional[str] name: str container: str pods: list[str] diff --git a/robusta_krr/core/runner.py b/robusta_krr/core/runner.py index 7aae3bd..51f4f05 100644 --- a/robusta_krr/core/runner.py +++ b/robusta_krr/core/runner.py @@ -121,7 +121,7 @@ class Runner(Configurable): async def _collect_result(self) -> Result: clusters = await self._k8s_loader.list_clusters() - self.debug(f'Using clusters: {", ".join(clusters)}') + self.debug(f'Using clusters: {clusters if clusters is not None else "inner cluster"}') objects = await self._k8s_loader.list_scannable_objects(clusters) resource_recommendations = await self._gather_objects_recommendations(objects) diff --git a/robusta_krr/utils/service_discovery.py b/robusta_krr/utils/service_discovery.py index f341225..a6bc8b9 100644 --- a/robusta_krr/utils/service_discovery.py +++ b/robusta_krr/utils/service_discovery.py @@ -6,6 +6,7 @@ from kubernetes import client from kubernetes.client import V1ServiceList from kubernetes.client.api_client import ApiClient from kubernetes.client.models.v1_service import V1Service +from kubernetes.config.config_exception import ConfigException from robusta_krr.utils.configurable import Configurable |
