Spring BootMiddleTechnical

Что такое Spring Boot Actuator и какие endpoint'ы он открывает по умолчанию?

Spring Boot Actuator добавляет HTTP-эндпоинты для мониторинга: /actuator/health и /actuator/info открыты по умолчанию, остальные (metrics, env, loggers, threaddump, heapdump) нужно явно разрешить через management.endpoints.web.exposure.include.

Что такое Spring Boot Actuator

Spring Boot Actuator — модуль, который встраивает в приложение набор HTTP-эндпоинтов для мониторинга, диагностики и управления без написания дополнительного кода. Actuator работает поверх стандартного Spring MVC или WebFlux и маршрутизирует эндпоинты через отдельный порт или префикс /actuator.

Зависимость добавляется одной строкой:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-actuator</artifactId>
</dependency>

Эндпоинты по умолчанию

После подключения зависимости Actuator регистрирует около 20 эндпоинтов, но наружу (web exposure) по умолчанию открыты только два:

  • /actuator/health — агрегированный статус приложения: UP / DOWN / OUT_OF_SERVICE. Включает sub-indicators (DB, Redis, Disk).
  • /actuator/info — произвольные метаданные из management.info.* или META-INF/build-info.properties.

Остальные эндпоинты включены (enabled=true), но не экспонированы через HTTP. Это разграничение между enabled и exposed — ключевая деталь, которую проверяют на собеседованиях.

Расширение exposure

Конфигурация через application.yml:

management:
  endpoints:
    web:
      exposure:
        include: health,info,metrics,loggers,env,threaddump,heapdump
        # или include: "*" — открыть все (только за фаерволом!)
  endpoint:
    health:
      show-details: always   # показывать sub-indicators без авторизации
  server:
    port: 8081               # вынести Actuator на отдельный порт

Реальный пример: кастомный HealthIndicator

import org.springframework.boot.actuate.health.Health;
import org.springframework.boot.actuate.health.HealthIndicator;
import org.springframework.stereotype.Component;

@Component
public class ExternalApiHealthIndicator implements HealthIndicator {

    private final ExternalApiClient client;

    public ExternalApiHealthIndicator(ExternalApiClient client) {
        this.client = client;
    }

    @Override
    public Health health() {
        try {
            boolean ok = client.ping(); // реальный HTTP-вызов
            return ok
                ? Health.up().withDetail("latency", client.lastLatencyMs()).build()
                : Health.down().withDetail("reason", "ping returned false").build();
        } catch (Exception ex) {
            return Health.down(ex).build();
        }
    }
}

Результат в /actuator/health:

{
  "status": "UP",
  "components": {
    "externalApi": {
      "status": "UP",
      "details": { "latency": 42 }
    },
    "db": { "status": "UP" },
    "diskSpace": { "status": "UP" }
  }
}

Пример: изменение уровня логирования в runtime

# Получить текущий уровень
curl http://localhost:8080/actuator/loggers/com.example.service

# Установить DEBUG без перезапуска
curl -X POST http://localhost:8080/actuator/loggers/com.example.service \
     -H 'Content-Type: application/json' \
     -d '{"configuredLevel": "DEBUG"}'

Защита эндпоинтов через Spring Security

@Bean
public SecurityFilterChain actuatorSecurity(HttpSecurity http) throws Exception {
    http
        .securityMatcher("/actuator/**")
        .authorizeHttpRequests(auth -> auth
            .requestMatchers("/actuator/health", "/actuator/info").permitAll()
            .anyRequest().hasRole("ADMIN")
        )
        .httpBasic(Customizer.withDefaults());
    return http.build();
}

Подводные камни

  • include: "*" в продакшене без фаервола — открывает /actuator/env (переменные окружения с паролями), /actuator/heapdump (дамп кучи), /actuator/shutdown (остановка приложения). Это реальный вектор атаки.
  • show-details: always без авторизации — /actuator/health начинает возвращать внутренние IP-адреса БД, имена хостов Redis и другие данные, полезные атакующему.
  • Путаница enabled vs exposed — эндпоинт может быть enabled=true, но не экспонирован через web. management.endpoint.shutdown.enabled=true сам по себе не открывает /actuator/shutdown снаружи без добавления в exposure.include.
  • Actuator на том же порту, что и приложение — если nginx проксирует 443→8080, а Actuator тоже на 8080, он доступен извне. Выносить на отдельный порт (management.server.port=8081) и не пробрасывать его через nginx — стандартная практика.
  • heapdump и threaddump весят сотни мегабайт — запросы к этим эндпоинтам под нагрузкой могут вызвать GC-паузу или OOM. Их следует ограничивать по IP или отключать в продакшене.
  • Кастомные HealthIndicator синхронные по умолчанию — если внешний сервис тормозит, health-check блокирует поток. Для реактивных приложений нужно реализовывать ReactiveHealthIndicator.
  • Метрики Micrometer не бесплатны — каждый тег (URI, метод, статус) создаёт отдельную временну́ю серию. Высококардинальные теги (user_id, request_id) приводят к memory leak в Prometheus/InfluxDB.
  • /actuator/info пуст без конфигурации — новички ожидают автоматических данных, но без spring-boot-maven-plugin с build-info goal или явных management.info.* свойств эндпоинт возвращает пустой объект.

Common mistakes

  • Путать термин «actuator endpoints» с соседним механизмом Spring Boot.
  • Не называть границу lifecycle, transaction, thread или request для «actuator endpoints».
  • Игнорировать production-эффекты «actuator endpoints»: latency, SQL shape, memory, security или observability.

What the interviewer is testing

  • Попросить объяснить механизм «actuator endpoints» на минимальном примере.
  • Проверить, видит ли кандидат failure mode и диагностику для «actuator endpoints».
  • Уточнить, какие настройки или API меняют «actuator endpoints» в реальном сервисе.

Sources

Related topics