Querying Pod Status by Service with Fabric8

Querying Pod Status by Service with Fabric8

-Spring Boot 4.1.0 · Java 25 · Fabric8 Kubernetes Client 7.1.0-

Many modern backend systems operate on Kubernetes, and Pod lookups for checking service status occur very frequently. Until now, operators (or developers in charge) have handled this by connecting to a server and entering kubectl commands directly, but as the operational scale grows, this approach reveals several limitations.

This article introduces the process of implementing a feature that uses Fabric8 Kubernetes Client in a Spring Boot application to call the Kubernetes API directly from the service layer and retrieve Pod status. It covers everything from checking cluster connection information to implementing the code and configuring the permissions required for deployment.

1. Development Background

1.1 Limitations of Shell-Based Lookup

Previously, whenever a Pod’s status was suspected to be abnormal, an operator would SSH into the server and enter kubectl commands directly. This approach was sufficient initially, but as the service grew, the following issues accumulated.

  • Members other than developers and operations staff, such as PMs and customer support representatives, also wanted to check the status immediately, but granting them cluster access was a security burden.

  • Distributing kubeconfig to everyone involved increased the number of management points and made permission revocation and audit tracking more difficult.

  • Shell command results were provided only as text, making it difficult to integrate them with dashboards and alerting systems or accumulate them as historical records.

These issues ultimately led to the requirement to "move Pod status lookups up to the service (API) layer."

1.2 Other Development Background

  • Incident response automation: The application must detect status changes directly to configure follow-up actions such as restarts and alerts in code.

  • Dashboard integration: A backend that provides this information through a REST API is required to visualize Pod status, nodes, and labels.

  • Multi-namespace management: There was a need to view multiple namespaces in an integrated manner on a single screen.

2. Library Selection: Why Fabric8

The leading Kubernetes clients in the Java ecosystem are the official client-java and Fabric8 kubernetes-client. This project selected Fabric8 for the following reasons.

Item

Description

API style

Its Fluent API in the form of .pods().inNamespace().withLabel() provides high readability.

Spring compatibility

Because Spring Cloud Kubernetes is based on Fabric8, integration with Spring Boot is natural.

Automatic configuration detection

Config.autoConfigure() automatically determines whether to use in-cluster configuration or a local kubeconfig.

3. Prerequisites: Checking Cluster Connection Information

Before writing the code, you must accurately identify the API server address, authentication method, CA certificate, and the permissions (RBAC) of the relevant account.

3.1 Checking the API Server Address

If kubectl is configured, you can check it easily with the following command.

$ kubectl cluster-info
Kubernetes control plane is running at https://127.0.0.1:6443

If it is displayed as 127.0.0.1, caution is required. This refers to the control plane node itself, so it can be used as-is only when the application runs on the same server. If you deploy it as a Pod inside the cluster, you use in-cluster configuration, so you do not need to know the address (KUBERNETES_SERVICE_HOST is injected automatically). If you connect remotely from an external server, you must check the node’s actual IP (kubectl get nodes -o wide, hostname -I). For a cloud-managed cluster, you can look up the endpoint with commands such as aws eks describe-cluster and gcloud container clusters describe.

3.2 Quickly Checking Whether the Server Responds

After checking the address, verify whether it responds by calling the health check endpoint with curl. -k skips TLS verification for a self-signed CA and should be used only for connection testing.

$ curl -k https://<주소>:6443/healthz
ok

$ curl -k https://<주소>:6443/api   # 토큰 없이 호출 시 401이 와도 연결은 정상

3.3 Checking the Authentication Method · CA Certificate

You can identify the authentication method from the fields under the users section of kubectl config view.

kubeconfig field

Authentication method

client-certificate / client-key

mTLS (client certificate)

token

Static token or ServiceAccount token

exec block present

Cloud CLI-integrated authentication (e.g., aws eks get-token)

When deploying as a Pod inside the cluster, it is common to use the in-cluster approach, in which a ServiceAccount token is mounted automatically, independently of the local kubeconfig. Because the CA certificate is included in kubeconfig in base64 format, extract it as follows.

$ kubectl config view --raw -o jsonpath='{.clusters[0].cluster.certificate-authority-data}' \
  | base64 -d > ca.crt

3.4 Preliminary RBAC Check

Check in advance whether the relevant account actually has permission to retrieve Pods. Both must return yes; if either returns no, ask an administrator to create the Role/RoleBinding described in Section 4.5.

$ kubectl auth can-i list pods --namespace=<대상-네임스페이스>
$ kubectl auth can-i get pods --namespace=<대상-네임스페이스>

4. Spring Boot Implementation

4.1 Adding Dependencies

// build.gradle.kts
implementation("io.fabric8:kubernetes-client:7.1.0")
 
<!-- pom.xml -->
<dependency>
    <groupId>io.fabric8</groupId>
    <artifactId>kubernetes-client</artifactId>
    <version>7.1.0</version>
</dependency>

※ It is recommended to check the version again in Maven Central at the time of deployment.

4.2 Registering the KubernetesClient Bean

Config.autoConfigure(null) automatically detects in-cluster configuration and the local kubeconfig in sequence, allowing the same code to be used in development and production without separate branching.

@Configuration
public class KubernetesConfig {
 
    @Bean
    public KubernetesClient kubernetesClient() {
        Config config = Config.autoConfigure(null); // in-cluster → kubeconfig 순 탐지
        return new KubernetesClientBuilder().withConfig(config).build();
    }
}

4.3 Pod Lookup Service

The three most frequently requested functions are retrieving the full list, filtering by label, and retrieving a single item.

@Service
public class PodQueryService {
 
    private final KubernetesClient kubernetesClient;
 
    public PodQueryService(KubernetesClient kubernetesClient) {
        this.kubernetesClient = kubernetesClient;
    }
 
    // 특정 네임스페이스 Pod 목록 조회
    public List<PodSummary> getPods(String namespace) {
        PodList podList = kubernetesClient.pods()
                .inNamespace(namespace)
                .list();
 
        return podList.getItems().stream()
                .map(this::toSummary)
                .toList();
    }
 
    // 라벨 셀렉터로 필터링 조회
    public List<PodSummary> getPodsByLabel(String namespace, String key, String value) {
        PodList podList = kubernetesClient.pods()
                .inNamespace(namespace)
                .withLabel(key, value)
                .list();
 
        return podList.getItems().stream()
                .map(this::toSummary)
                .toList();
    }
 
    // 단건 조회
    public PodSummary getPod(String namespace, String podName) {
        Pod pod = kubernetesClient.pods()
                .inNamespace(namespace)
                .withName(podName)
                .get();
 
        if (pod == null) {
            throw new NoSuchElementException("Pod not found: " + podName);
        }
        return toSummary(pod);
    }
 
    private PodSummary toSummary(Pod pod) {
        return new PodSummary(
                pod.getMetadata().getName(),
                pod.getMetadata().getNamespace(),
                pod.getStatus().getPhase(),
                pod.getSpec().getNodeName(),
                pod.getMetadata().getLabels(),
                pod.getMetadata().getCreationTimestamp()
        );
    }
}

public record PodSummary(
        String name,
        String namespace,
        String phase,
        String nodeName,
        Map<String, String> labels,
        String createdAt
) {}

4.4 Controller

By exposing the information in the service layer through a REST API, operations dashboards and internal tools can call it directly to check Pod status.

@RestController
@RequestMapping("/api/pods")
public class PodController {
 
    private final PodQueryService podQueryService;
 
    public PodController(PodQueryService podQueryService) {
        this.podQueryService = podQueryService;
    }
 
    @GetMapping
    public List<PodSummary> list(
            @RequestParam String namespace,
            @RequestParam(required = false) String labelKey,
            @RequestParam(required = false) String labelValue
    ) {
        if (labelKey != null && labelValue != null) {
            return podQueryService.getPodsByLabel(namespace, labelKey, labelValue);
        }
        return podQueryService.getPods(namespace);
    }
 
    @GetMapping("/{name}")
    public PodSummary get(@RequestParam String namespace, @PathVariable String name) {
        return podQueryService.getPod(namespace, name);
    }
}

4.5 RBAC Configuration for Deployment

When deploying as a Pod inside the cluster, grant the Pod's ServiceAccount only the minimum permissions (get/list/watch).

apiVersion: v1
kind: ServiceAccount
metadata:
  name: my-backend-sa
  namespace: default
---
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  name: pod-reader
  namespace: default
rules:
- apiGroups: [""]
  resources: ["pods"]
  verbs: ["get", "list", "watch"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  name: my-backend-pod-reader-binding
  namespace: default
subjects:
- kind: ServiceAccount
  name: my-backend-sa
  namespace: default
roleRef:
  kind: Role
  name: pod-reader
  apiGroup: rbac.authorization.k8s.io

Specify serviceAccountName: my-backend-sa in the Deployment. If you need to query multiple namespaces, you can expand the scope using ClusterRole/ClusterRoleBinding.

4.6 Exception Handling

Kubernetes API calls throw KubernetesClientException in cases such as authentication failure (401) or insufficient permissions (403). Map these exceptions through a common exception handler to return clear status codes.

@RestControllerAdvice
public class KubernetesExceptionHandler {
 
    @ExceptionHandler(KubernetesClientException.class)
    public ResponseEntity<String> handleK8sException(KubernetesClientException e) {
        // 403이면 RBAC 권한 부족, 401이면 인증 실패로 해석
        int code = e.getCode();
        return ResponseEntity.status(code).body("K8s API 호출 실패: " + e.getMessage());
    }
}

5. Conclusion

This work established the foundation for replacing the process of having operators connect to a server and enter kubectl commands each time with service-level REST API calls. Members without cluster access permissions can now safely check the information they need, and a foundation has also been established for integration with dashboards and alerting systems.

Although this implementation started with the single function of retrieving Pods, applying the same structure makes it possible to naturally extend it to retrieving the status of other resources, such as Deployments, Services, and Nodes. These accumulated APIs can later be connected to alerting systems or internal operations dashboards, serving as a foundation for detecting and responding to signs of failures more quickly.

Jsia

Site footer