Skip to content

Commit b818cc5

Browse files
appleapple
authored andcommitted
Created singleton Kubernetes API clients avoid memory leak
1 parent 3c835b4 commit b818cc5

File tree

1 file changed

+51
-11
lines changed

1 file changed

+51
-11
lines changed

agent/tasks.py

Lines changed: 51 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import logging
2+
import threading
23

34
import requests
45
from celery import shared_task
@@ -13,19 +14,59 @@
1314
K8S_API_TIMEOUT = 30
1415
HTTP_REQUEST_TIMEOUT = 30
1516

17+
# Kubernetes client cache (singleton pattern)
18+
_k8s_clients_cache = {}
19+
_k8s_config_lock = threading.Lock()
20+
_k8s_config_loaded = False
1621

17-
def get_pod_details(namespace='drdroid'):
18-
# Import kubernetes here to avoid loading at module import time
19-
from kubernetes import client, config
2022

21-
try:
22-
# Try to load in-cluster config first, then local config
23-
try:
24-
config.load_incluster_config()
25-
except config.ConfigException:
26-
config.load_kube_config()
23+
def _get_k8s_clients():
24+
"""
25+
Get or create singleton Kubernetes API clients.
26+
Reuses the same client instances to prevent connection leaks.
27+
Thread-safe for Celery workers.
28+
"""
29+
global _k8s_config_loaded, _k8s_clients_cache
30+
31+
# Fast path: clients already initialized
32+
if _k8s_clients_cache:
33+
return _k8s_clients_cache['v1'], _k8s_clients_cache['custom']
34+
35+
# Slow path: initialize clients (thread-safe)
36+
with _k8s_config_lock:
37+
# Double-check after acquiring lock
38+
if _k8s_clients_cache:
39+
return _k8s_clients_cache['v1'], _k8s_clients_cache['custom']
40+
41+
from kubernetes import client, config
42+
43+
# Load Kubernetes config only once
44+
if not _k8s_config_loaded:
45+
try:
46+
config.load_incluster_config()
47+
logger.info("Loaded in-cluster Kubernetes config")
48+
except config.ConfigException:
49+
config.load_kube_config()
50+
logger.info("Loaded local Kubernetes config")
51+
_k8s_config_loaded = True
2752

28-
v1 = client.CoreV1Api()
53+
# Create singleton API clients
54+
_k8s_clients_cache['v1'] = client.CoreV1Api()
55+
_k8s_clients_cache['custom'] = client.CustomObjectsApi()
56+
57+
logger.info("Initialized Kubernetes API clients (singleton)")
58+
59+
return _k8s_clients_cache['v1'], _k8s_clients_cache['custom']
60+
61+
62+
def get_pod_details(namespace='drdroid'):
63+
"""
64+
Fetch pod details from Kubernetes API.
65+
Uses cached API clients to prevent connection leaks.
66+
"""
67+
try:
68+
# Get singleton clients (reuses existing instances)
69+
v1, custom_api = _get_k8s_clients()
2970

3071
# Get all pods in the namespace with timeout
3172
pods = v1.list_namespaced_pod(namespace, _request_timeout=K8S_API_TIMEOUT)
@@ -34,7 +75,6 @@ def get_pod_details(namespace='drdroid'):
3475
# Get pod metrics for CPU/memory usage
3576
pod_metrics = {}
3677
try:
37-
custom_api = client.CustomObjectsApi()
3878
metrics = custom_api.list_namespaced_custom_object(
3979
group="metrics.k8s.io",
4080
version="v1beta1",

0 commit comments

Comments
 (0)