🤖 Votre propre agent DevOps: Le futur de l'observabilité

🚀 Introduction

Tout le code source et les configurations de cet article sont disponnible sur le dépot Github: [https://github.com/matgou/devops-agents] 💻

Google a récemment publié l’Agent Development Kit (ADK), un framework conçu pour simplifier le développement et le déploiement d’agents basés sur l’IA. Ce framework est remarquablement facile d’utilisation. Ici, dans ce contexte, un agent est une API interactive qui s’appuie sur un LLM, comme Gemini, et qui est en outre enrichie par vos scripts personnalisés pour générer des réponses basées sur leurs sorties.

Dans le monde DevSecOps, nous sommes déjà équipés d’une multitude d’outils et de scripts (CLI, API, et plus encore). Maintenant, imaginez doter une IA de toutes ces capacités.

En tant qu’ingénieurs DevOps, nous nous retrouvons souvent à lire manuellement les logs, à décrire les ressources pour trouver leurs statuts. Et si chacun de nous disposait d’un assistant dédié pour effectuer ces premières étapes de diagnostic à notre place, en pointant directement vers les informations pertinentes ?

Cet article explore comment construire et utiliser un agent Kubernetes basé sur ADK (Python). Cet agent agira comme un outil de diagnostic, appelable via un chat ou une API, pour recueillir rapidement des informations cruciales sur notre cluster et fournir des premières informations de diagnostic.

Testez-le ; personnellement, j’en suis convaincu: c’est le futur de l’observabilité.

🛠️ Démarrage rapide du développement d’un agent ADK

Démarrer avec l’Agent Development Kit (ADK) est simple. Cette section vous guidera à travers la configuration et la création d’un agent (très) basique.

Prerequisites

poetry init

Vous pouvez accepter les valeurs par défaut pour la plupart, mais assurez-vous que le nom du paquet est kubernetes-admin * Puis Ajoutez la dépendance ADK à votre projet :

poetry add google-adk
# Kubernetes-admin

Basic structure

- kubernetes_admin/
    |- agent.py # Containing agent instructions : initial prompt and parameters
    |- __init__.py # A default init file
    |- .env
    |- tools/ # To hosting futurer tools interfaces
        |- __init__.py # A default init file for the tools package
- README.md # existing default README.md file
- pyproject.toml # existing poetry config file
- poetry.lock # existing poetry config file
- venv/ # virtual env directory
mkdir kubernetes_admin && cd kubernetes_admin
# In kubernetes_admin/kubernetes_admin/__init__.py
from . import agent
# In kubernetes_admin/kubernetes_admin/tools/
mkdir tools && touch __init__.py

🧠 Donner des instructions à l’agent

Le fichier agent.py (situé dans kubernetes-admin/kubernetes_admin/agent.py) est crucial dans l’Agent Development Kit (ADK). Il définit le prompt initial et les instructions de base pour l’agent IA. Plus tard, il sera étendu pour inclure les outils spécifiques que l’agent peut utiliser et la logique sur la manière de les utiliser.

Vous pouvez utiliser l’IA pour aider à générer le prompt initial et les instructions, ou les écrire vous-même.

Créez/modifiez kubernetes-admin/kubernetes_admin/agent.py avec le contenu suivant :

# In kubernetes_admin/kubernetes_admin/agent.py
"""Kubernetes_Admin: Agent for interacting with and managing a Kubernetes cluster."""

from google.adk.agents import LlmAgent

MODEL = "gemini-2.5-pro-preview-05-06"

KUBERNETES_ADMIN_INSTRUCTION = """
You are Kubernetes Admin, an AI assistant designed to help users manage and understand their Kubernetes (K8s) clusters.
Your primary goal is to help users by:
- Answering questions about their Kubernetes cluster.
- Retrieving information about resources like pods, nodes, services, deployments, namespaces, etc. using the available tools.
- Explaining Kubernetes concepts relevant to their queries.
- Assisting in understanding resource status and configurations.

Always strive to be clear, concise, and helpful in your responses.

Example interactions:
- User: "How many pods are running in the 'default' namespace?"
- You: (After using a tool to get pod count) "There are X pods currently running in the 'default' namespace."
- User: "What's the status of the pod named 'my-app-pod-123'?"
- You: (After using a tool to get pod status) "The pod 'my-app-pod-123' is currently in a 'Running' state."
"""

kubernetes_admin_agent = LlmAgent(
    name="kubernetes_admin_agent",
    model=MODEL,
    description=(
        "An AI agent that helps users explore and understand their Kubernetes cluster by answering questions and retrieving information about resources."
    ),
    instruction=KUBERNETES_ADMIN_INSTRUCTION,
    tools=[], # Pass the FunctionTool instance
)

root_agent = kubernetes_admin_agent

Tester l’agent

Avant d’exécuter le test, vous devez configurer un environnement cloud:

export GOOGLE_GENAI_USE_VERTEXAI=true
export GOOGLE_CLOUD_PROJECT=<Complete with your GCP project ID>
export GOOGLE_CLOUD_LOCATION=us-central1
export GOOGLE_CLOUD_STORAGE_BUCKET=${GOOGLE_CLOUD_PROJECT}-adk-bucket-$(openssl rand -hex 4) # generate a unique bucket name
gcloud auth application-default login
gcloud storage buckets create gs://$GOOGLE_CLOUD_STORAGE_BUCKET

Vous pouvez maintenant exécuter le test avec la commande suivante (dans le répertoire kubernetes-admin/kubernetes_admin/):

adk run .

Si tout est bien configuré, vous allez voir le prompt suivant et vous allez pouvoir interagir avec (utiliser Ctrl+D pour sortir):

Log setup complete: /tmp/agents_log/agent.20250528_200909.log
To access latest log: tail -F /tmp/agents_log/agent.latest.log
Running agent kubernetes_admin_agent, type exit to exit.
[user]: hello
[kubernetes_admin_agent]: Hello! I'm Kubernetes Admin, your AI assistant for managing and understanding your Kubernetes cluster.

How can I help you today? For example, you can ask me about pods, nodes, services, or any other Kubernetes resources.
[user]: how many pod in my kubernetes clusters 
[kubernetes_admin_agent]: I can help you with that! To tell you how many pods are in your cluster, I need to check all namespaces.

Should I proceed to get the list of all pods across all namespaces in your cluster?

Fournissons des outils à l’agent

Tout d’abord, écrivons une fonction Python pour interagir avec kubectl et l’encapsuler en tant qu’outil ADK.

# In kubernetes_admin/kubernetes_admin/tools/kubectl_tool.py
"""Tool for executing kubectl get commands."""

import subprocess # For actual kubectl calls
from google.adk.tools import FunctionTool

def run_kubectl(verbs: str = "get", resource_type: str = "pods", resource_name: str = "", namespace: str = "default") -> str:
    """
    Runs a kubectl command with the specified verb, resource type, optional resource name, and namespace.
    Exemples:
        To get configuration of a specific pod use verbs: "describe", namespace: "all", resource_type: "pods", resource_name: "<custom pod name>"
        To get logs of a specific pod use verbs: "logs", namespace: "<namespace>", resource_type: "", resource_name: "<custom pod name>"
        To list pods in all namespace use verbs: "get", namespace: "all", resource_type: "pods", resource_name: ""

    Args:
        verbs: The kubectl verb to use (e.g., 'get', 'describe', 'logs', 'top').
        resource_type: The type of Kubernetes resource (e.g., 'pods', 'nodes', 'services', 'deployments').
        resource_name: Optional. The specific name of the resource. If None, lists all resources of the type.
        namespace: Optional. The Kubernetes namespace. Defaults to 'default'.

    Returns:
        A string containing the output of the kubectl command or an error message.
    """
    try:
        command = ["kubectl", verbs]
        if resource_name != "":
            command.append(f'{resource_type}/{resource_name}')
        else:
            command.append(f'{resource_type}')
        if namespace != "all":
            command.extend(["-n", namespace])
        else:
            command.extend(["--all-namespaces"])
        result = subprocess.run(command, capture_output=True, text=True, check=True, timeout=30)
        return result.stdout
    except FileNotFoundError:
        return "Error: kubectl command not found. Please ensure kubectl is installed and in your PATH."
    except subprocess.CalledProcessError as e:
        return f"Error executing kubectl command: {e.stderr}"
    except subprocess.TimeoutExpired:
        return "Error: kubectl command timed out."
    except Exception as e:
         return f"An unexpected error occurred: {str(e)}"

kubectl_tool = FunctionTool(
    func=run_kubectl)
from .tools.kubectl_tool import kubectl_tool # Import the FunctionTool instance
...
...
kubernetes_admin_agent = LlmAgent(
    name="kubernetes_admin_agent",
    model=MODEL,
    description=(
        "An AI agent that helps users explore and understand their Kubernetes cluster by answering questions and retrieving information about resources."
    ),
    instruction=KUBERNETES_ADMIN_INSTRUCTION,
    tools=[kubectl_tool], # Pass the FunctionTool instance
)
KUBERNETES_ADMIN_INSTRUCTION = """
...
...
You have a tool named 'run_kubectl_command' to fetch information from the Kubernetes cluster by executing kubectl commands.
Tool 'run_kubectl_command' arguments:
- 'verbs': (string, required) The kubectl verb to use (e.g., 'get', 'describe', 'logs', 'top').
- 'resource_type': (string, required) The type of Kubernetes resource (e.g., 'pods', 'nodes', 'services', 'deployments', 'namespaces').
- 'resource_name': (string, optional) The specific name of the resource. If not provided, all resources of the given type in the namespace will be listed.
- 'namespace': (string, optional, defaults to 'default') The Kubernetes namespace to query.

When a user asks for information that requires querying the cluster (e.g., "list pods", "get node status", "describe service my-service", "get logs for pod xyz"), you MUST use the 'run_kubectl_command' tool.
Clearly state that you are using the tool, what you are querying for, and then present the information returned by the tool.
Determine the correct 'verbs' argument based on the user's request (e.g., "list" or "how many" implies 'get', "what's the status" implies 'get', "describe" implies 'describe', "show logs" implies 'logs').
"""

Un test en condition réélle

Maintenant, effectuons un test réel pour voir si notre agent utilise kubectl comme prévu. (Assurez-vous d’avoir un fichier kubeconfig valide et d’être connecté à un cluster Kubernetes pour ce test.)

# In kubernetes-admin/kubernetes_admin/ directory
$ adk run .
...
[user]: combien de pods dans mon cluster tous namespaces
[kubernetes_admin_agent]: D'accord, je peux vous aider avec cela. J'utiliserai l'outil `run_kubectl` pour lister tous les pods dans tous les namespaces, puis les compter pour vous.
[kubernetes_admin_agent]: J'ai récupéré la liste de tous les pods dans tous les namespaces. Il y en a beaucoup !
D'après la sortie, il y a 119 pods dans votre cluster, tous namespaces confondus.

Integration dans Kubernetes

Nous allons maintenant déployer notre agent dans un cluster Kubernetes, pour donner la possibilité à d’autres utilisateurs de l’utiliser.

🐳 Configurer uvicorn

Exposer l’interface web en configurant Uvicorn et en ajoutant un fichier main.py.

# In kubernetes-admin/main.py
import os

import uvicorn
from fastapi import FastAPI
from google.adk.cli.fast_api import get_fast_api_app

# Get the directory where main.py is located
AGENT_DIR = os.path.dirname(os.path.abspath(__file__))
# Example session DB URL (e.g., SQLite)
SESSION_DB_URL = "sqlite:///./sessions.db"
# Example allowed origins for CORS
ALLOWED_ORIGINS = ["http://localhost", "http://localhost:8080", "*"]
# Set web=True if you intend to serve a web interface, False otherwise
SERVE_WEB_INTERFACE = True

# Call the function to get the FastAPI app instance
app: FastAPI = get_fast_api_app(
    agents_dir=AGENT_DIR,
    session_db_url=SESSION_DB_URL,
    allow_origins=ALLOWED_ORIGINS,
    web=SERVE_WEB_INTERFACE,
)

if __name__ == "__main__":
    # Use the PORT environment variable provided by Cloud Run, defaulting to 8080
    uvicorn.run(app, host="0.0.0.0", port=int(os.environ.get("PORT", 8080)))

🐳 Construire un conteneur Docker

  1. Voici un Dockerfile pour construire une image de conteneur pour votre agent Python. Ce Dockerfile:

    • Utilise une image de base Python slim.

    • Installe kubectl (puisque l’outil de votre agent l’utilise) et Poetry.

    • Configure un utilisateur non root pour une meilleure sécurité.

    • Installe les dépendances de votre projet en utilisant Poetry.

    • Définit la commande par défaut pour exécuter votre agent ADK en utilisant son serveur HTTP intégré.

# In kubernetes_admin/Dockerfile
FROM python:3.13-slim
WORKDIR /app

RUN pip install poetry
ENV PORT=8080
RUN apt update && apt install -y kubernetes-client/stable prometheus/stable && apt clean
COPY . .

RUN adduser --disabled-password --gecos "" myuser && \
    chown -R myuser:myuser /app

USER myuser
RUN poetry install

ENV PATH="/home/myuser/.local/bin:$PATH"
CMD poetry run uvicorn main:app --host 0.0.0.0 --port $PORT
  1. Créez un dépôt Artifact Registry si vous ne l’avez pas déjà fait.
#    Replace REGION and GOOGLE_CLOUD_PROJECT with your details.
gcloud artifacts repositories create adk-repo --repository-format=docker \
    --location=us-central1 \
    --description="ADK agent repository" \
    --project=${GOOGLE_CLOUD_PROJECT}
  1. Construisez l’image Docker et poussez-la vers Artifact Registry.
#    Replace REGION and GOOGLE_CLOUD_PROJECT with your details.
gcloud builds submit --tag us-central1-docker.pkg.dev/${GOOGLE_CLOUD_PROJECT}/adk-repo/k8s-admin-agent:latest

🛡️ Ajouter l’RBAC

Le manifeste suivant crée un ServiceAccount Kubernetes dans le namespace kubernetes-admin-agent. Il définit également un ClusterRole avec des permissions en lecture seule (get, list, watch) sur tous les groupes d’API et ressources, puis utilise un ClusterRoleBinding pour accorder ces permissions au ServiceAccount créé.

# In kubernetes_admin/rbac.yaml file
apiVersion: v1
kind: ServiceAccount
metadata:
  name: kubernetes-admin-agent
---
apiVersion: v1
kind: Namespace
metadata:
  name: kubernetes-admin-agent
---
apiVersion: v1
kind: ServiceAccount
metadata:
  name: kubernetes-admin-agent
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  name: kubernetes-admin-agent
rules:
- apiGroups:
  - "*"
  resources:
  - "*"
  verbs:
  - get
  - list
  - watch
---
apiVersion: rbac.authorization.k8s.io/v1
# This cluster role binding allows the kubernetes-admin-agent ServiceAccount to read all resources in any namespace.
kind: ClusterRoleBinding
metadata:
  name: kubernetes-admin-agent-global
subjects:
- kind: ServiceAccount
  name: kubernetes-admin-agent
  namespace: kubernetes-admin-agent
roleRef:
  kind: ClusterRole
  name: kubernetes-admin-agent
  apiGroup: rbac.authorization.k8s.io

Apply this configuration :

kubectl apply -f kubernetes_admin/rbac.yaml

Ajouter les google credentials dans un secret

Pour permettre à votre agent s’exécutant dans Kubernetes de s’authentifier auprès des services Google Cloud (spécifiquement Vertex AI pour Gemini), vous devez créer un compte de service GCP, lui accorder les permissions nécessaires, générer une clé pour celui-ci et stocker cette clé en tant que secret Kubernetes.

Assurez-vous que votre variable d’environnement GOOGLE_CLOUD_PROJECT est correctement définie avant d’exécuter ces commandes.

# 1. Create the service account
gcloud iam service-accounts create kubernetes-admin-agent

# 2. Grant the service account the "Vertex AI User" role on your project
gcloud projects add-iam-policy-binding ${GOOGLE_CLOUD_PROJECT} \
    --member="serviceAccount:kubernetes-admin-agent@${GOOGLE_CLOUD_PROJECT}.iam.gserviceaccount.com" \
    --role="roles/aiplatform.user"

# 3. Create and download a key for the service account
gcloud iam service-accounts keys create key.json \
    --iam-account="kubernetes-admin-agent@${GOOGLE_CLOUD_PROJECT}.iam.gserviceaccount.com"

# 4. Create a Kubernetes secret from the downloaded key file
kubectl create secret generic google-cloud-keys -n kubernetes-admin-agent --from-file=key.json=key.json

Deployer le container de notre agent et présenter le service

Créons maintenant le manifeste de déploiement et de service Kubernetes pour votre agent. Ce fichier, kubernetes_admin/deployment.yaml, définira comment votre agent s’exécute dans votre cluster Kubernetes.

Il est très important de ne pas oublier de remplacer l’espace réservé <Complétez avec votre ID de projet GCP> par votre ID de projet Google Cloud réel à deux endroits dans ce fichier : * Sous spec.template.spec.containers[0].image pour le chemin de l’image Docker. * Sous spec.template.spec.containers[0].env pour la variable d’environnement GOOGLE_CLOUD_PROJECT.

# In kubernetes_admin/deployment.yaml
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: kubernetes-admin-agent
spec:
  replicas: 1
  selector:
    matchLabels:
      app: kubernetes-admin-agent
  template:
    metadata:
      labels:
        app: kubernetes-admin-agent
    spec:
      serviceAccountName: kubernetes-admin-agent
      volumes:
      - name: google-cloud-keys
        secret:
          secretName: google-cloud-keys
      containers:
      - name: kubernetes-admin-agent
        imagePullPolicy: Always
        image: us-central1-docker.pkg.dev/<Complete with your GCP project ID>/adk-repo/k8s-admin-agent:latest
        resources:
          limits:
            memory: "1Gi"
            cpu: "500m"
            ephemeral-storage: "128Mi"
          requests:
            memory: "128Mi"
            cpu: "500m"
            ephemeral-storage: "128Mi"
        ports:
        - containerPort: 8080
        volumeMounts:
        - name: google-cloud-keys
          readOnly: true
          mountPath: "/etc/google-cloud-keys"
        env:
          - name: PORT
            value: "8080"
          - name: GOOGLE_CLOUD_PROJECT
            value: <Complete with your GCP project ID>
          - name: GOOGLE_APPLICATION_CREDENTIALS
            value: /etc/google-cloud-keys/key.json
          - name: GOOGLE_CLOUD_LOCATION
            value: us-central1
          - name: GOOGLE_GENAI_USE_VERTEXAI
            value: "true"
          # If using AI Studio, set GOOGLE_GENAI_USE_VERTEXAI to false and set the following:
          # - name: GOOGLE_API_KEY
          #   value: GOOGLE_API_KEY
          # Add any other necessary environment variables your agent might need
---
apiVersion: v1
kind: Service
metadata:
  name: kubernetes-admin-agent
spec:       
  type: LoadBalancer
  ports:
    - port: 80
      targetPort: 8080
  selector:
    app: kubernetes-admin-agent

Appliquer cette configuration :

kubectl apply -n kubernetes-admin-agent -f deployment.yaml

Trouver l’IP externe de l’agent, et aller sur: http://<EXTERNAL_IP_ADDRESS>

kubectl get svc -n kubernetes-admin-agent

⚠️ Il est très important de noter que pour cette démonstration, nous avons exposé l’agent à Internet. Cependant, dans un scénario réel, nous implémenterions une authentification en amont ou l’exposerions uniquement en interne.

🎉 Tests et expérimentations

Nous pouvons effectuer de nombreux tests pour expérimenter avec notre agent, en lui posant des questions de plus en plus complexes sur des concepts tels que “Pourquoi l’ingress ne fonctionne-t-il pas ?” ou “Pourquoi ce pod ne fonctionne-t-il pas ?”. Cependant, pour cet article, j’ai choisi de démontrer avec quelle facilité l’agent peut aider à détecter et comprendre un événement OOMKill.

Je ne détaillerai pas la configuration d’un pod de “stress” utilisé pour générer intentionnellement un OOMKill dans mon cluster, mais observons l’interaction avec notre agent.

Dépanner un OOMKill

L’agent peut être particulièrement utile pour diagnostiquer les problèmes courants de Kubernetes. Par exemple, si vous suspectez qu’un pod est OOMKilled (Out Of Memory Killed), vous pourriez demander :

image image

Conclusion

Dans cet article, nous avons parcouru le processus de construction et de déploiement d’un agent Kubernetes pratique basé sur l’IA en utilisant l’Agent Development Kit de Google.

Nous avons commencé par les bases de l’ADK, créé un agent capable de comprendre le langage naturel, puis l’avons doté d’un outil kubectl pour interagir directement avec notre cluster. Nous avons également couvert les étapes essentielles pour conteneuriser cet agent, sécuriser son accès avec RBAC et le déployer en tant que service évolutif au sein de Kubernetes.

L’agent que nous avons construit sert d’exemple fondamental, un assistant de diagnostic capable de récupérer rapidement des informations et de fournir des premières analyses. Mais ce n’est que la partie émergée de l’iceberg. Imaginez étendre cet agent avec des outils plus sophistiqués : des outils capables non seulement de lire les logs mais aussi de corréler des événements entre différents composants, des outils capables d’analyser les modèles d’utilisation des ressources et de prédire les problèmes potentiels, ou même des outils capables d’exécuter en toute sécurité des tâches opérationnelles pré-approuvées en fonction des conditions observées.

Le futur de l’observabilité pourrait ne pas se limiter à de meilleurs tableaux de bord et à plus de données, mais concerner des assistants intelligents qui nous aident activement à comprendre et à gérer la complexité croissante de nos environnements cloud-natifs. En tirant parti de l’IA et de frameworks comme l’ADK, nous nous dirigeons vers un avenir où nos clusters Kubernetes ne sont pas seulement surveillés, mais véritablement compris et exploités intelligemment, libérant ainsi les devops pour qu’ils se concentrent sur des initiatives stratégiques de plus haut niveau. Le potentiel de transformation de la manière dont nous interagissons avec notre infrastructure et la maintenons est immense, et le voyage ne fait que commencer.