Получение статуса подов по сервису с помощью Fabric8

Получение статуса подов по сервису с помощью Fabric8

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

Многие современные серверные системы работают в Kubernetes, и запросы к Pod для проверки состояния сервисов выполняются очень часто. До сих пор операторы (или ответственные разработчики) решали эту задачу, подключаясь к серверу и напрямую вводя команды kubectl, однако с ростом масштаба эксплуатации такой подход выявляет ряд ограничений.

В этой статье рассматривается процесс реализации функции, использующей Fabric8 Kubernetes Client в приложении Spring Boot для прямого вызова Kubernetes API из слоя сервисов и получения состояния Pod. Описаны все этапы: от проверки информации о подключении к кластеру до реализации кода и настройки разрешений, необходимых для развёртывания.

1. Предпосылки разработки

1.1 Ограничения поиска на основе shell-команд

Ранее, если возникало подозрение, что состояние Pod ненормальное, оператор подключался к серверу по SSH и напрямую вводил команды kubectl. Изначально этого было достаточно, но по мере роста сервиса накопились следующие проблемы.

  • Участники, не относящиеся к разработчикам и специалистам по эксплуатации, например PM и сотрудники службы поддержки клиентов, также хотели немедленно проверять состояние, однако предоставление им доступа к кластеру создавало угрозу безопасности.

  • Раздача kubeconfig всем заинтересованным лицам увеличивала количество точек управления и усложняла отзыв разрешений и отслеживание аудита.

  • Результаты выполнения shell-команд предоставлялись только в виде текста, поэтому их было трудно интегрировать с информационными панелями и системами оповещений, а также накапливать в качестве исторических записей.

В конечном итоге эти проблемы привели к требованию «перенести проверку состояния Pod на уровень сервиса (API)».

1.2 Другие предпосылки разработки

  • Автоматизация реагирования на инциденты: приложение должно напрямую обнаруживать изменения состояния, чтобы программно настраивать последующие действия, такие как перезапуск и отправка оповещений.

  • Интеграция с информационной панелью: требуется серверная часть, предоставляющая эти сведения через REST API для визуализации состояния Pod, узлов и меток.

  • Управление несколькими пространствами имён: требовалась возможность просматривать несколько пространств имён в интегрированном виде на одном экране.

2. Выбор библиотеки: почему Fabric8

В экосистеме Java ведущими клиентами Kubernetes являются официальный client-java и Fabric8 kubernetes-client. В этом проекте был выбран Fabric8 по следующим причинам.

Пункт

Описание

Стиль API

Его Fluent API в форме .pods().inNamespace().withLabel() отличается высокой читаемостью.

Совместимость со Spring

Поскольку Spring Cloud Kubernetes основан на Fabric8, интеграция со Spring Boot выполняется естественным образом.

Автоматическое определение конфигурации

Config.autoConfigure() автоматически определяет, следует ли использовать конфигурацию внутри кластера или локальный kubeconfig.

3. Предварительные условия: проверка информации о подключении к кластеру

Прежде чем писать код, необходимо точно определить адрес API-сервера, способ аутентификации, сертификат CA и разрешения (RBAC) соответствующей учётной записи.

3.1 Проверка адреса API-сервера

Если kubectl настроен, адрес можно легко проверить следующей командой.

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

Если отображается 127.0.0.1, требуется соблюдать осторожность. Это адрес самого узла плоскости управления, поэтому использовать его как есть можно только в том случае, если приложение работает на том же сервере. Если приложение развёрнуто как Pod внутри кластера, используется конфигурация внутри кластера, поэтому знать этот адрес не требуется (KUBERNETES_SERVICE_HOST внедряется автоматически). Если подключение выполняется удалённо с внешнего сервера, необходимо проверить фактический IP-адрес узла (kubectl get nodes -o wide, hostname -I). Для облачного управляемого кластера конечную точку можно узнать с помощью таких команд, как aws eks describe-cluster и gcloud container clusters describe.

3.2 Быстрая проверка ответа сервера

После проверки адреса убедитесь, что сервер отвечает, вызвав конечную точку проверки работоспособности с помощью curl. Параметр -k пропускает проверку TLS для самоподписанного CA и должен использоваться только для тестирования подключения.

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

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

3.3 Проверка способа аутентификации · сертификата CA

Способ аутентификации можно определить по полям в разделе users команды kubectl config view.

Поле kubeconfig

Способ аутентификации

client-certificate / client-key

mTLS (сертификат клиента)

token

Статический токен или токен ServiceAccount

Блок exec присутствует

Аутентификация с интеграцией с облачным CLI (например, aws eks get-token)

При развёртывании в виде Pod внутри кластера обычно используется подход с конфигурацией внутри кластера: токен ServiceAccount автоматически монтируется независимо от локального kubeconfig. Поскольку сертификат CA включён в kubeconfig в формате base64, извлеките его следующим образом.

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

3.4 Предварительная проверка RBAC

Заранее проверьте, действительно ли соответствующая учётная запись имеет разрешение на получение Pod. Обе команды должны вернуть yes; если хотя бы одна возвращает no, попросите администратора создать Role/RoleBinding, описанные в разделе 4.5.

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

4. Реализация в Spring Boot

4.1 Добавление зависимостей

// 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>

※ Рекомендуется ещё раз проверить версию в Maven Central на момент развёртывания.

4.2 Регистрация компонента KubernetesClient

Config.autoConfigure(null) автоматически обнаруживает конфигурацию внутри кластера и локальный kubeconfig в заданной последовательности, позволяя использовать один и тот же код в разработке и production без отдельного ветвления.

@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

Три наиболее часто запрашиваемые функции — получение полного списка, фильтрация по метке и получение отдельного элемента.

@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 Контроллер

Предоставляя информацию из сервисного слоя через REST API, операционные панели и внутренние инструменты могут напрямую обращаться к ней для проверки статуса Pod.

@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 для Deployment

При развертывании в виде Pod внутри кластера предоставьте ServiceAccount этого Pod только минимально необходимые разрешения (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

Укажите serviceAccountName: my-backend-sa в Deployment. Если необходимо выполнять запросы к нескольким пространствам имён, область действия можно расширить с помощью ClusterRole/ClusterRoleBinding.

4.6 Обработка исключений

Вызовы Kubernetes API выбрасывают KubernetesClientException в таких случаях, как ошибка аутентификации (401) или недостаток разрешений (403). Обрабатывайте эти исключения через общий обработчик исключений, чтобы возвращать понятные коды состояния.

@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. Заключение

Эта работа заложила основу для замены процесса, при котором операторам каждый раз приходится подключаться к серверу и вводить команды kubectl, вызовами REST API на уровне сервисов. Участники без разрешений на доступ к кластеру теперь могут безопасно проверять необходимую им информацию; кроме того, заложена основа для интеграции с панелями мониторинга и системами оповещений.

Хотя эта реализация начиналась с единственной функции получения Pod, применение той же структуры позволяет естественным образом расширить её для получения статуса других ресурсов, таких как Deployments, Services и Nodes. Впоследствии эти накопленные API можно подключить к системам оповещений или внутренним операционным панелям, создав основу для более быстрого обнаружения признаков сбоев и реагирования на них.

Jsia

Site footer