kr8s Documentation

repository·main·Indexed 21 days ago

https://github.com/kr8s-org/kr8s

A simple, extensible Python client library for Kubernetes designed to replicate the kubectl experience. It provides both synchronous and asynchronous APIs (supporting asyncio and trio) for managing Kubernetes resources, including support for port forwarding, executing commands in pods, and scaling deployments. The library follows standard kubectl credential lookup orders and includes kubectl-ng, a reimplementation of the kubectl CLI.

Tokens
26.1K
Snippets
120
Records
131
Agent score
73%

What's inside kr8s

  1. Compare kr8s with other Kubernetes Python clients

    main

    When choosing a Kubernetes client library for Python, consider the following trade-offs between kr8s and its alternatives:

    NameSyncAsyncioPrimary Characteristic
    kr8sDesigned to replicate the kubectl experience; readable and beginner-friendly.
    kubernetesOfficial client; auto-generated, resulting in verbose code and complex API mapping.
    kubernetes_asyncioAsync version of the official client; highly verbose due to async context managers.
    pykube-ngLightweight and Pythonic; uses an ORM-like (SQLAlchemy-inspired) object-driven API.
    lightkubeStrong emphasis on type safety and strict schema validation; feels like a TypeScript-style client.

    Use kr8s if you want a library that prioritizes readability and a kubectl-like workflow. Use lightkube if you require strict type safety and schema validation. Use the official kubernetes or kubernetes_asyncio libraries if you require 100% coverage of the Kubernetes API surface.

  2. Understand kr8s Object API and resource representation

    main

    Responses from the kr8s Client API are returned as objects from the kr8s.objects (sync) or kr8s.asyncio.objects (async) modules. These objects represent Kubernetes resources (e.g., Pod, Service, Deployment).

    • Sync usage: kr8s.get() returns a list of sync objects.
    • Async usage: kr8s.asyncio.get() returns an async iterator of async objects.

    Attributes and methods work identically in both sync and async versions, though async methods must be awaited.

    import kr8s
    
    # Sync
    pods = list(kr8s.get("pods", namespace=kr8s.ALL))
    pod = pods[0]
    
    # Async
    import kr8s.asyncio
    pods = [po async for po in kr8s.asyncio.get("pods", namespace=kr8s.ALL)]
    pod = pods[0]
  3. How client caching works in kr8s

    main

    The factory functions kr8s.api() and kr8s.asyncio.api() implement a caching mechanism.

    • Singleton Behavior: If you call the factory with the same arguments (e.g., the same kubeconfig), it returns a pointer to the existing cached instance.
    • Multiple Clients: If you provide different arguments, a new kr8s.Api instance is created and cached separately.
    • Default Behavior: Calling kr8s.api() with no arguments returns the first client from the cache if one exists. This allows you to initialize a client with custom auth once and have all subsequent kr8s calls (and resource objects like Pod) automatically use that same instance.

    Warning: To bypass the cache (e.g., if KUBECONFIG changes and you need a fresh instance), you must instantiate kr8s.Api(bypass_factory=True) directly. If you do this, you must manually pass the client reference to all objects, as caching will no longer work.

    import kr8s
    
    # Caching in action
    api = kr8s.api(kubeconfig="/foo/bar")
    api2 = kr8s.api(kubeconfig="/foo/bar") # api2 is a pointer to api
    
    api3 = kr8s.api(kubeconfig="/fizz/buzz") # api3 is a new instance
    
    # Bypassing cache (Not recommended)
    api_bypass = kr8s.Api(bypass_factory=True)
    from kr8s.objects import Pod
    pod = Pod.get("some-pod", api=api_bypass) # Must pass reference manually
  4. Convert kopf resource body to a kr8s object

    main

    When writing a kopf handler, the body argument contains the raw dictionary representation of the Kubernetes resource. To use kr8s features, you must wrap this body in a kr8s object class.

    Synchronous approach: Use kr8s.objects.Pod.

    Asynchronous approach: Use kr8s.asyncio.objects.Pod and await the instantiation.

    # Sync
    from kr8s.objects import Pod
    pod = Pod(body)
    
    # Async
    from kr8s.asyncio.objects import Pod
    pod = await Pod(body)
  5. Deploy a kr8s operator to Kubernetes

    main

    Deploying an operator requires two main steps:

    1. Configure RBAC: Create a ServiceAccount, a ClusterRole with necessary permissions (e.g., get, watch, list, update, patch on deployments), and a ClusterRoleBinding.
    2. Create a Deployment: Deploy your container image and ensure the serviceAccountName in the pod spec matches the ServiceAccount created in step 1.

    RBAC Example (rbac.yaml):

    apiVersion: v1
    kind: ServiceAccount
    metadata:
      name: labelling-operator
    ---
    apiVersion: rbac.authorization.k8s.io/v1
    kind: ClusterRole
    metadata:
      name: labelling-operator
    rules:
    - apiGroups:
      - apps
      resources:
      - deployments
      verbs:
      - get
      - watch
      - list
      - update
      - patch
    ---
    apiVersion: rbac.authorization.k8s.io/v1
    kind: ClusterRoleBinding
    metadata:
      name: labelling-operator
    roleRef:
      apiGroup: rbac.authorization.k8s.io
      kind: ClusterRole
      name: labelling-operator
    subjects:
    - kind: ServiceAccount
      name: labelling-operator
      namespace: default

    Deployment Example (deployment.yaml):

    apiVersion: apps/v1
    kind: Deployment
    metadata:
      name: labelling-operator
    spec:
      replicas: 1
      selector:
        matchLabels:
          app: labelling-operator
      template:
        metadata:
          labels:
            app: labelling-operator
        spec:
          serviceAccountName: labelling-operator
          containers:
          - name: operator
            image: foo/labelling-operator:latest
  6. Build a Kubernetes operator with kopf and kr8s

    main

    You can build event-driven Kubernetes controllers by combining kopf with kr8s. kopf handles the event lifecycle (creation, updates, deletions), while kr8s provides high-level Pythonic interaction with Kubernetes resources like Pods.

    Core Workflow

    1. Define Event Handlers: Use @kopf.on.create("resource_type") and @kopf.on.resume("resource_type") decorators to trigger logic.
    2. Instantiate kr8s Objects: Convert the body provided by kopf into a kr8s object (e.g., Pod(body) for synchronous or await Pod(body) for asynchronous).
    3. Perform Actions: Use kr8s methods like .exec(), .label(), or .exists() to interact with the cluster.
    4. Run the Controller: Use python -m kopf run <your_file>.py to start the operator locally.
    python -m kopf run controller.py
  7. Use the kr8s API Client

    main

    To interact with Kubernetes, kr8s uses an API Client. In most cases, you do not need to manage this object manually; calling high-level kr8s functions will automatically generate or retrieve a client from a cache.

    Implicit Client Usage

    You can call functions directly from the kr8s (sync) or kr8s.asyncio (async) modules without instantiating a client.

    Explicit Client Usage

    If you need to be explicit, you can construct an API client object using kr8s.api() or await kr8s.asyncio.api() and then call methods on that instance. You can also pass a specific client instance to resource objects (like Pod) during instantiation.

    import kr8s
    
    # Implicit (No client needed)
    version = kr8s.version()
    
    # Explicit (Sync)
    api = kr8s.api()
    version = api.version()
    
    # Explicit (Async)
    import kr8s.asyncio
    api = await kr8s.asyncio.api()
    version = await api.version()
  8. Package a kr8s operator in Docker

    main

    To containerize your operator, use a Python base image and ensure kr8s is installed via pip. Copy your controller script into the image and set it as the CMD.

    # Dockerfile
    FROM python:3.11
    
    WORKDIR /usr/local/src
    
    RUN pip install kr8s
    
    COPY controller.py /usr/local/src/
    
    CMD ["python3", "/usr/local/src/controller.py"]

    Build and push the image:

    $ docker build -t foo/labelling-operator:latest .
    $ docker push foo/labelling-operator:latest
  9. Run a port forward in the background

    main

    To run a port forward without blocking your main execution flow, use the .start() and .stop() methods.

    • In Sync code: .start() spawns the port forward in a background thread.
    • In Async code: await .start() spawns the port forward as a background task. Warning: Your main code must be async and non-blocking because the port forward and your code share the same event loop.
    from kr8s.objects import Pod
    
    pod = Pod.get("my-pod")
    pf = pod.portforward(remote_port=1234, local_port=5678)
    
    # Starts the port forward in a background thread
    pf.start()
    
    # ... do other work ...
    
    pf.stop()
  10. Use the asynchronous API via kr8s.asyncio

    main

    If you want to use kr8s natively with asyncio or trio, use the kr8s.asyncio submodule. This provides direct access to asynchronous coroutines and async iterators. Many submodules have async equivalents, such as:

    • kr8s.asyncio.objects (equivalent to kr8s.objects)
    • kr8s.asyncio.portforward (equivalent to kr8s.portforward)

    When using the async API, remember to use await for coroutines and async for for asynchronous iterators.

    import kr8s.asyncio
    
    pods = [pod async for pod in kr8s.asyncio.get("pods")]
  11. Open a port forward with a Pod or Service

    main

    Use Pod.portforward() (or Service.portforward()) to create a tunnel to an application running in a Pod or Service.

    Synchronous Context

    Use a context manager (with) to automatically manage the lifecycle of the port forward. The context manager yields the local_port assigned to the tunnel.

    Asynchronous Context

    Use an async context manager (async with) with httpx.AsyncClient() or similar to communicate with the forwarded port.

    Configuration Options

    • remote_port: The port inside the Pod/Service.
    • local_port: The port on your local machine (optional).
    • address: A list of local IP addresses to bind to (e.g., ["0.0.0.0"] or ["127.0.0.1"]).
    import requests
    from kr8s.objects import Pod
    
    pod = Pod.get("my-pod")
    
    # Basic usage
    with pod.portforward(remote_port=1234) as local_port:
        resp = requests.get(f"http://localhost:{local_port}")
    
    # Binding to specific addresses
    with pod.portforward(remote_port=5000, local_port=8888, address=["0.0.0.0"]):
        resp = requests.get(f"http://0.0.0.0:{local_port}")