11import logging
2+ import threading
23
34import requests
45from celery import shared_task
1314K8S_API_TIMEOUT = 30
1415HTTP_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