Опирается на правила: R-OBS-CFG-1R-OBS-CFG-4 и R-OBS-CFG-X1R-OBS-CFG-X3 из Observability Style Guide → раздел 5. Конфигурация.

Важно знать

  • Отдельный management port (management.server.port: 8081) для изоляции actuator от business traffic и сетевой защиты.
  • Explicit exposure список — health,info,metrics,prometheus. Не '*'.
  • Percentiles-histogram + SLO buckets для http.server.requests100ms,500ms,1s,5s.
  • Стандартные tags через management.metrics.tags.service/env/version глобально, не в каждой метрике.
  • Logback-spring.xml с двумя springProfile: dev/test (text pattern) и prod/staging (LogstashEncoder с MDC).
  • Запрет публичных /actuator/env, /heapdump, /threaddump — содержат секреты и memory dumps.
  • Запрет одного port для business + actuator в проде — невозможно сетевая изоляция.

Конфигурация observability собирается из четырёх частей: application.yml (Actuator, Micrometer, OTel), logback-spring.xml (формат логов), build.gradle.kts (зависимости + git-commit-id-plugin), K8s манифесты (probes на management port). Эта статья — про первые два.

Отдельный management port

R-OBS-CFG-1: business и actuator на разных портах.

server:
  port: 8080

management:
  server:
    port: 8081

Что это даёт:

  • Network policy в K8s разрешает Prometheus scraper подключаться на 8081, не на 8080. Ingress публикует только 8080.
  • Actuator-нагрузка (scraping каждые 15s, healthchecks каждые 5s) не блокирует thread pool business-tomcat'а.
  • Тесты на staging можно делать на 8081 (heapdump, threaddump) без риска зацепить production-traffic.

Если один port — невозможно «закрыть actuator извне», NetworkPolicy не разделит endpoints по path.

Explicit exposure

R-OBS-CFG-2: явный список endpoints, не wildcard.

management:
  endpoints:
    web:
      exposure:
        include: health,info,metrics,prometheus

Дефолт Spring — только health + info. Production — добавляем metrics (для дебага через JSON) и prometheus (для scraper-а).

Не добавляем без явной причины: env, beans, mappings, loggers, heapdump, threaddump, configprops — это либо secrets exposure, либо attack surface (heap dump может содержать в памяти credentials, токены).

Percentiles и SLO buckets

R-OBS-CFG-3: точные квантили для критичных метрик.

management:
  metrics:
    distribution:
      percentiles-histogram:
        http.server.requests: true
      slo:
        http.server.requests: 100ms,500ms,1s,5s
    tags:
      service: ${spring.application.name}
      env: ${ENV:dev}
      version: ${BUILD_VERSION:unknown}
  • percentiles-histogram: true — Micrometer публикует full histogram (~64 bucket-ов) для http.server.requests_seconds_bucket, что даёт точный histogram_quantile() в Prometheus.
  • slo: 100ms,500ms,1s,5s — добавляет explicit SLO buckets. В Prometheus http_server_requests_seconds_bucket{le="0.5"} даёт «сколько запросов уложились в 500ms» без интерполяции.
  • tags.service/env/version — глобальные, см. Metrics.

BUILD_VERSION инжектится из CI как env var.

Logback-spring.xml с двумя профилями

R-OBS-CFG-4: текстовый pattern для dev, JSON для prod.

<configuration>
    <springProfile name="dev,test">
        <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
            <encoder>
                <pattern>%d{HH:mm:ss.SSS} %-5level [%thread] %X{traceId:-} %logger{30} - %msg%n</pattern>
            </encoder>
        </appender>
    </springProfile>
    <springProfile name="prod,staging">
        <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
            <encoder class="net.logstash.logback.encoder.LogstashEncoder">
                <includeMdcKeyName>traceId</includeMdcKeyName>
                <includeMdcKeyName>spanId</includeMdcKeyName>
                <includeMdcKeyName>requestId</includeMdcKeyName>
                <includeMdcKeyName>userId</includeMdcKeyName>
            </encoder>
        </appender>
    </springProfile>
    <root level="INFO">
        <appender-ref ref="STDOUT"/>
    </root>
</configuration>

springProfile — Logback-расширение Spring Boot, активирует appender только на матчинг профиля.

%X{traceId:-} в dev pattern — кладёт traceId из MDC, дефолт пустая строка. Для глаз достаточно: видишь короткий trace-id в логе, по нему ищешь в Tempo/Jaeger.

В prod — LogstashEncoder пишет JSON с явно перечисленными MDC-полями, остальные ключи MDC попадают в mdc блок автоматически.

Что запрещено

Exposing /actuator/env, /heapdump, /threaddump

R-OBS-CFG-X1: эти endpoints — security risk.

  • /actuator/env показывает все configs включая возможные secrets в plain (если они попали туда мимо Vault — например, через ${ENV:default}).
  • /actuator/heapdump — full heap dump. В нём в памяти могут быть JWT-токены, credentials, PII.
  • /actuator/threaddump — стек всех threads, может раскрыть внутреннюю структуру + class names attack surface.

Если очень нужно (debug в проде) — Spring Security + roles: ADMIN + audit log на каждый вызов. Дефолт — не expose.

Один port для business + actuator в проде

R-OBS-CFG-X2: тогда /actuator/health доступен извне через тот же Ingress, что /api/orders. NetworkPolicy не разделит по path. Уязвимости в актуаторе становятся доступны attacker'у.

exposure.include: '*' в проде

R-OBS-CFG-X3: открывает всё, включая env, beans, mappings, loggers, configprops. Каждое из них — отдельный leak.

# ОПАСНО
management.endpoints.web.exposure.include: '*'

# ХОРОШО
management.endpoints.web.exposure.include: health,info,metrics,prometheus

В Spring Boot 3+ есть management.endpoints.web.exposure.exclude — можно использовать чтобы убрать конкретные после '*', но default-deny с explicit-allow надёжнее.

Что запрещено — таблица

АнтипаттернПравилоЧто взамен
/actuator/env exposed публичноR-OBS-CFG-X1не expose / ADMIN-only
/actuator/heapdump exposedR-OBS-CFG-X1не expose
Один port для business + actuator в продеR-OBS-CFG-X2management.server.port: 8081
exposure.include: '*'R-OBS-CFG-X3explicit list
percentiles-histogram: false для критичныхR-OBS-CFG-3true + SLO buckets
Один Logback-pattern для dev и prodR-OBS-CFG-4springProfile dev/test vs prod/staging
MDC keys не в encoder include listR-OBS-CFG-4<includeMdcKeyName>traceId</includeMdcKeyName>

Куда дальше