Spring Boot: привязка server.address в Kubernetes Helm
Исправьте привязку server.address в Spring Boot при развертывании в Kubernetes Helm. Узнайте, почему имя контейнера не резолвится и как настроить Helm-шаблоны.
Я разворачиваю приложение Spring Boot в Kubernetes с помощью Helm и сталкиваюсь с проблемой привязки свойства server.address для конфигурации server.address.
Структура приложения
Мое приложение состоит из двух компонентов:
FileProcessorIo
Оба компонента используют один и тот же файл развертывания Helm и один и тот же Kubernetes Service.
Конфигурация Helm Deployment
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 я задаю следующие переменные окружения:
FILEPROCESSOR_ADDRESS: "ingest-{{ .Values.global.branch }}"
IO_ADDRESS: "ingest-{{ .Values.global.branch }}"
Свойства Spring Boot
В файлах свойств для обоих компонентов я использую переменные Spring:
# Для 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:
- Имена контейнеров против сетевых адресов: имена контейнеров, такие как «fileprocessor», не разрешаются как сетевые хосты в продакшн‑кластерах Kubernetes.
- Разрешение переменных окружения: переменные
FILEPROCESSOR_ADDRESSиIO_ADDRESSустанавливаются в «fileprocessor» и «io» соответственно, а не в имена сервисов Kubernetes. - Расширение шаблона Helm: шаблон Helm
ingest-{{ .Values.global.branch }}не расширяется корректно в продакшн‑окружении, либо переменные окружения не задаются правильно. - Связывание свойств Spring Boot: свойство
server.addressожидает сетевой адрес, а не символическое имя контейнера.
Подходы к решению
1. Использовать 0.0.0.0 для привязки контейнера
Для большинства контейнеризованных приложений следует привязываться ко всем интерфейсам, задав 0.0.0.0:
# Для FileProcessor и Io
server.address=0.0.0.0
Это позволит приложению принимать соединения на всех доступных сетевых интерфейсах внутри контейнера.
2. Использовать имена сервисов Kubernetes
Если необходимо привязаться к конкретному сервису, используйте имя сервиса Kubernetes, которое разрешается внутри кластера:
# Для обоих компонентов
server.address=${K8S_SERVICE_NAME:-ingest-default}
3. Перезапись через переменные окружения
Как описано в руководстве Tutorial Works, задайте переменные окружения напрямую в контейнерах:
envFrom:
- configMapRef:
name: tryphon-config-{{ .Values.global.branch }}
- secretRef:
name: app-secrets
Рекомендованная реализация
Опция 1: Использовать 0.0.0.0 (рекомендовано для большинства случаев)
Измените свойства Spring Boot:
# Для FileProcessor и Io
server.address=0.0.0.0
server.port=8080
Это стандартный подход для контейнеризованных приложений и будет работать стабильно во всех окружениях Kubernetes.
Опция 2: Использовать корректное разрешение имени сервиса
Если действительно нужно привязаться к конкретному сервису:
- Обновите ConfigMap, чтобы использовать правильное имя сервиса:
data:
application.properties: |
server.address=${K8S_SERVICE_NAME:-ingest-${BRANCH:-default}}
- Задайте переменные окружения в деплойменте:
env:
- name: K8S_SERVICE_NAME
value: "ingest-{{ .Values.global.branch }}"
- name: BRANCH
value: "{{ .Values.global.branch }}"
Опция 3: Конфигурация для отдельных контейнеров
Если два компонента действительно требуют разных настроек привязки:
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:
server.address=${SERVER_ADDRESS:0.0.0.0}
Дополнительные настройки
Расслабленное связывание Spring Boot
Помните, что в документации Red Hat указано:
Kubernetes не позволяет определять имена свойств в CamelCase (требуется использовать только строчные буквы). Чтобы обойти это ограничение, используйте дефисную форму queue-username, которую Spring Boot сопоставит с queueUsername.
Хотя server.address не в CamelCase, этот принцип применим для обеспечения согласованности имен свойств.
Отладка шаблона Helm
Чтобы убедиться, что шаблон Helm расширяется корректно:
- Проверьте отрендеренные шаблоны:
helm template . --debug > rendered.yaml
- Убедитесь, что переменные окружения заданы правильно в отрендеренном деплойменте:
containers:
- name: fileprocessor
env:
- name: FILEPROCESSOR_ADDRESS
value: "ingest-production" # или любое другое имя ветки
Сетевые настройки в продакшн
Подумайте о требованиях к сети в продакшн:
- Общаются ли компоненты внутри одного пода?
- Общаются ли они через сервисы Kubernetes между подами?
- Доступны ли внешние клиенты к этим сервисам?
Ответ определит, подходит ли 0.0.0.0 или нужен конкретный сервис.
Заключение
Проблема привязки server.address возникает потому, что имена контейнеров, такие как «fileprocessor», не разрешаются как сетевые адреса в продакшн‑окружениях Kubernetes. Ключевые выводы:
- Используйте 0.0.0.0 для большинства контейнеризованных приложений Spring Boot, чтобы привязаться ко всем интерфейсам.
- Проверьте расширение шаблона Helm, чтобы убедиться, что переменные окружения заданы корректно.
- Рассмотрите паттерны обнаружения сервисов с помощью Spring Cloud Kubernetes, если требуется межсервисное взаимодействие.
- Тестируйте в продакшн‑подобных окружениях на ранних этапах разработки, чтобы выявить подобные проблемы.
Самое надёжное решение — задать server.address=0.0.0.0 в свойствах Spring Boot и полагаться на сервисы и сетевую маршрутизацию Kubernetes для управления трафиком между компонентами.