Другое

Spring Boot: привязка server.address в Kubernetes Helm

Исправьте привязку server.address в Spring Boot при развертывании в Kubernetes Helm. Узнайте, почему имя контейнера не резолвится и как настроить Helm-шаблоны.

Я разворачиваю приложение Spring Boot в Kubernetes с помощью Helm и сталкиваюсь с проблемой привязки свойства server.address для конфигурации server.address.

Структура приложения

Мое приложение состоит из двух компонентов:

  • FileProcessor
  • Io

Оба компонента используют один и тот же файл развертывания Helm и один и тот же Kubernetes Service.

Конфигурация Helm Deployment

yaml
spec:
  replicas: {{ .Values.ingest.replicas }}
  selector:
    matchLabels:
      app: ingest-{{ .Values.global.branch }}
  template:
    metadata:
      labels:
        app: ingest-{{ .Values.global.branch }}
    spec:
      containers:
        - name: fileprocessor
          image: "{{ .Values.ingest.fileprocessorRepository }}:{{ tpl .Values.ingest.tag $ }}"
          ports:
            - containerPort: {{ .Values.ingest.fileprocessorPort }}
          envFrom:
            - configMapRef:
                name: tryphon-config-{{ .Values.global.branch }}
        - name: io
          image: "{{ .Values.ingest.ioRepository }}:{{ tpl .Values.ingest.tag $ }}"
          ports:
            - containerPort: {{ .Values.ingest.ioPort }}
          envFrom:
            - configMapRef:
                name: tryphon-config-{{ .Values.global.branch }}
---
apiVersion: v1
kind: Service
metadata:
  name: ingest-{{ .Values.global.branch }}
  namespace: {{ default .Release.Namespace .Values.global.namespace }}
spec:
  selector:
    app: ingest-{{ .Values.global.branch }}
  ports:
    - name: fileprocessor
      port: {{ .Values.ingest.fileprocessorPort }}
      targetPort: {{ .Values.ingest.fileprocessorPort }}
    - name: io
      port: {{ .Values.ingest.ioPort }}
      targetPort: {{ .Values.ingest.ioPort }}

Значения в values.yaml

В файле values.yaml я задаю следующие переменные окружения:

yaml
FILEPROCESSOR_ADDRESS: "ingest-{{ .Values.global.branch }}"
IO_ADDRESS: "ingest-{{ .Values.global.branch }}"

Свойства Spring Boot

В файлах свойств для обоих компонентов я использую переменные Spring:

properties
# Для FileProcessor
server.address=${FILEPROCESSOR_ADDRESS}

# Для Io
server.address=${IO_ADDRESS}

Описание проблемы

Эта конфигурация работает безупречно в моем локальном окружении Minikube. Однако при развертывании в продакшн‑кластер Kubernetes компонент FileProcessor падает с ошибкой:

Failed to bind properties under 'server.address' to java.net.InetAddress:
    Property: server.address
    Value: "fileprocessor"
    Origin: URL [file:/config/fileProcessor.properties] - 6:16
    Reason: failed to convert java.lang.String to java.net.InetAddress (caused by java.net.UnknownHostException: fileprocessor: Name or service not known)

Вопрос

Почему свойство server.address в продакшн‑окружении устанавливается в значение "fileprocessor" вместо имени сервиса "ingest-{{ .Values.global.branch }}", и как можно решить проблему привязки этого свойства?

Дополнительный контекст

  • Развертывание работает корректно в Minikube
  • Проблема проявляется только в продакшн‑кластер
  • Оба компонента используют одинаковую конфигурацию развертывания
  • Имя сервиса корректно задано в values.yaml

Проблема возникает из‑за того, что Spring Boot интерпретирует свойство server.address как сетевой хост, который нужно разрешить, а «fileprocessor» — это имя контейнера, а не разрешаемый сетевой адрес. В продакшн‑кластерах Kubernetes имена контейнеров автоматически не разрешаются как сетевые хосты, в отличие от окружения minikube.


Содержание


Понимание проблемы

Когда вы задаёте server.address=${FILEPROCESSOR_ADDRESS} в свойствах Spring Boot, Spring Boot ожидает, что это будет валидное сетевое имя хоста или IP‑адрес, который можно разрешить для привязки HTTP‑сервера. Как объясняется в ответе Stack Overflow о адресации контейнеров:

В контексте контейнера адрес почти всегда нужно задавать как специальный адрес «все интерфейсы» 0.0.0.0.

Сообщение об ошибке явно указывает на проблему:

Reason: failed to convert java.lang.String to java.net.InetAddress (caused by java.net.UnknownHostException: fileprocessor: Name or service not known)

Это означает, что «fileprocessor» (имя контейнера) не может быть разрешено как сетевой адрес в продакшн‑окружении Kubernetes, хотя в minikube это работает из‑за иной политики DNS‑разрешения.


Анализ причины

Проблема коренится в недопонимании того, как работает сетевое взаимодействие в Kubernetes:

  1. Имена контейнеров против сетевых адресов: имена контейнеров, такие как «fileprocessor», не разрешаются как сетевые хосты в продакшн‑кластерах Kubernetes.
  2. Разрешение переменных окружения: переменные FILEPROCESSOR_ADDRESS и IO_ADDRESS устанавливаются в «fileprocessor» и «io» соответственно, а не в имена сервисов Kubernetes.
  3. Расширение шаблона Helm: шаблон Helm ingest-{{ .Values.global.branch }} не расширяется корректно в продакшн‑окружении, либо переменные окружения не задаются правильно.
  4. Связывание свойств Spring Boot: свойство server.address ожидает сетевой адрес, а не символическое имя контейнера.

Подходы к решению

1. Использовать 0.0.0.0 для привязки контейнера

Для большинства контейнеризованных приложений следует привязываться ко всем интерфейсам, задав 0.0.0.0:

properties
# Для FileProcessor и Io
server.address=0.0.0.0

Это позволит приложению принимать соединения на всех доступных сетевых интерфейсах внутри контейнера.

2. Использовать имена сервисов Kubernetes

Если необходимо привязаться к конкретному сервису, используйте имя сервиса Kubernetes, которое разрешается внутри кластера:

properties
# Для обоих компонентов
server.address=${K8S_SERVICE_NAME:-ingest-default}

3. Перезапись через переменные окружения

Как описано в руководстве Tutorial Works, задайте переменные окружения напрямую в контейнерах:

yaml
envFrom:
  - configMapRef:
      name: tryphon-config-{{ .Values.global.branch }}
  - secretRef:
      name: app-secrets

Рекомендованная реализация

Опция 1: Использовать 0.0.0.0 (рекомендовано для большинства случаев)

Измените свойства Spring Boot:

properties
# Для FileProcessor и Io
server.address=0.0.0.0
server.port=8080

Это стандартный подход для контейнеризованных приложений и будет работать стабильно во всех окружениях Kubernetes.

Опция 2: Использовать корректное разрешение имени сервиса

Если действительно нужно привязаться к конкретному сервису:

  1. Обновите ConfigMap, чтобы использовать правильное имя сервиса:
yaml
data:
  application.properties: |
    server.address=${K8S_SERVICE_NAME:-ingest-${BRANCH:-default}}
  1. Задайте переменные окружения в деплойменте:
yaml
env:
  - name: K8S_SERVICE_NAME
    value: "ingest-{{ .Values.global.branch }}"
  - name: BRANCH
    value: "{{ .Values.global.branch }}"

Опция 3: Конфигурация для отдельных контейнеров

Если два компонента действительно требуют разных настроек привязки:

yaml
spec:
  containers:
    - name: fileprocessor
      env:
        - name: SERVER_ADDRESS
          value: "0.0.0.0"
    - name: io
      env:
        - name: SERVER_ADDRESS
          value: "0.0.0.0"

Тогда в свойствах Spring Boot:

properties
server.address=${SERVER_ADDRESS:0.0.0.0}

Дополнительные настройки

Расслабленное связывание Spring Boot

Помните, что в документации Red Hat указано:

Kubernetes не позволяет определять имена свойств в CamelCase (требуется использовать только строчные буквы). Чтобы обойти это ограничение, используйте дефисную форму queue-username, которую Spring Boot сопоставит с queueUsername.

Хотя server.address не в CamelCase, этот принцип применим для обеспечения согласованности имен свойств.

Отладка шаблона Helm

Чтобы убедиться, что шаблон Helm расширяется корректно:

  1. Проверьте отрендеренные шаблоны:
bash
helm template . --debug > rendered.yaml
  1. Убедитесь, что переменные окружения заданы правильно в отрендеренном деплойменте:
yaml
containers:
  - name: fileprocessor
    env:
      - name: FILEPROCESSOR_ADDRESS
        value: "ingest-production"  # или любое другое имя ветки

Сетевые настройки в продакшн

Подумайте о требованиях к сети в продакшн:

  • Общаются ли компоненты внутри одного пода?
  • Общаются ли они через сервисы Kubernetes между подами?
  • Доступны ли внешние клиенты к этим сервисам?

Ответ определит, подходит ли 0.0.0.0 или нужен конкретный сервис.


Заключение

Проблема привязки server.address возникает потому, что имена контейнеров, такие как «fileprocessor», не разрешаются как сетевые адреса в продакшн‑окружениях Kubernetes. Ключевые выводы:

  1. Используйте 0.0.0.0 для большинства контейнеризованных приложений Spring Boot, чтобы привязаться ко всем интерфейсам.
  2. Проверьте расширение шаблона Helm, чтобы убедиться, что переменные окружения заданы корректно.
  3. Рассмотрите паттерны обнаружения сервисов с помощью Spring Cloud Kubernetes, если требуется межсервисное взаимодействие.
  4. Тестируйте в продакшн‑подобных окружениях на ранних этапах разработки, чтобы выявить подобные проблемы.

Самое надёжное решение — задать server.address=0.0.0.0 в свойствах Spring Boot и полагаться на сервисы и сетевую маршрутизацию Kubernetes для управления трафиком между компонентами.

Источники

  1. Stack Overflow – Failed to bind properties under ‘server.address’ with helm
  2. Tutorial Works – How to Override Any Spring Boot Property in Kubernetes
  3. Red Hat Documentation – Spring Boot Property Binding in Kubernetes
  4. Push Build Test Deploy – Spring Boot application.properties in Kubernetes
Авторы
Проверено модерацией
Модерация