Kubernetes становится все более популярным средством развертывания приложений, особенно в архитектурах микросервисов.
С другой стороны, что касается Java-приложений, написанных с использованием Spring Boot, то от архитектуры Netflix практически отказались в пользу решений, которые напрямую используют потенциал Kubernetes, например, для создания сервисов, управления конфигурацией и т.д..
Spring следует этой тенденции, создав специальный проект: “Spring Cloud Kubernetes”.
В этой статье мы рассмотрим, как использовать его для чтения конфигураций непосредственно из ConfigMap. Решение, которое, как уже упоминалось, позволяет нам в полной мере использовать возможности Kubernetes и управлять горячими изменениями данных
Зависимости
Давайте начнем с установки необходимых зависимостей Maven:
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Hoxton.SR8</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-kubernetes-dependencies</artifactId>
<version>1.1.6.RELEASE</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<!-- needed for spring kubernetes config reloads -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<!-- spring cloud kubernetes config server -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-kubernetes-config</artifactId>
</dependency>
</dependencies>
Code language: HTML, XML (xml)
Конфигурация
Инициализация значений осуществляется Spring в bootstrap, поэтому вам необходимо определить конфигурацию для доступа к ConfigMap(ам) в файле bootstrap.yaml.
Давайте рассмотрим общую конфигурацию:
spring:
application:
name: cloud-k8s-app
cloud:
kubernetes:
config: # not needed if spring.application.name is equal to configmap metadata name
name: default-name # ConfigMap metadata name
namespace: default-namespace
sources: # configure this to load from more than one ConfigMap
# Spring Cloud Kubernetes looks up a ConfigMap named c1 in namespace default-namespace
- name: c1
# Spring Cloud Kubernetes looks up a ConfigMap named default-name in whatever namespace n2
- namespace: n2
# Spring Cloud Kubernetes looks up a ConfigMap named c3 in namespace n3
- namespace: n3
name: c3
Code language: PHP (php)
Вся часть spring.cloud.kubernetes не нужна, если значение spring.application.name равно названию метаданных ConfigMap.
Часть spring.cloud.kubernetes.config.sources необходима, если вам нужно подключиться к большему количеству ConfigMaps, в противном случае вам просто нужно поместить значение имени метаданных в spring.cloud.kubernetes.config.name.
Пример
Сейчас мы рассмотрим пример, в котором мы будем считывать значения из двух отдельных ConfigMaps, определение которых мы покажем ниже:
apiVersion: v1
kind: ConfigMap
metadata:
name: k8s-demo-configmap
data:
application.properties: |-
greeting.message=Hello from
---
apiVersion: v1
kind: ConfigMap
metadata:
name: k8s-demo-custom-configmap
data:
custom-conf.properties: |-
custom.name=K8s
Code language: JavaScript (javascript)
далее файл bootstrap.yaml со ссылками на ConfigMaps для чтения значений из них:
spring:
application:
name: k8s-demo
cloud:
kubernetes:
reload:
enabled: true
mode: event
strategy: refresh
config:
namespace: default
sources:
- name: k8s-demo-configmap
- name: k8s-demo-custom-configmap
Code language: JavaScript (javascript)
Ниже приведены два класса, которые отображают через аннотацию @ConfigurationProperties две ConfigMaps:
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;
@Configuration
@ConfigurationProperties(prefix = "greeting")
public class GreetingConfig {
private String message = "Hi from";
}
Code language: JavaScript (javascript)
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;
@Configuration
@ConfigurationProperties(prefix = "custom")
public class CustomConfig {
private String name = "Spring";
}
Code language: JavaScript (javascript)
На этом этапе мы определяем простой контроллер, в котором мы будем использовать два класса, управляющие конфигурациями, для чтения значений, взятых из двух ConfigMaps.
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class K8sDemoController {
@Autowired
private GreetingConfig config;
@Autowired
private CustomConfig customConfig;
@GetMapping(value = "/hello")
public String hello() {
return config.getMessage() + " " + customConfig.getName();
}
}
Code language: CSS (css)
Вызов метода GET /hello отобразит строку “Hello from K8S”.
С конфигурацией, определенной в файле bootstrap.yaml, если мы изменим значение в ConfigMap, оно будет автоматически обновлено Spring и станет доступно во время выполнения.
В дополнение к использованию PropertySources, как это сделано в примере, вы можете получать значения из ConfigMap непосредственно из Spring bean Environment. Spring помещает все значения, считанные из всех ConfigMaps, в свой экземпляр Enviroment, к которому вы можете получить доступ, сделав его @Autowired в вашем bean.
ВНИМАНИЕ: Механизм reload не работает, если вы читаете значения из ConfigMap с помощью аннотации @Value, поскольку они внедряются в экземпляр bean при загрузке.
Reload
Существуют различные режимы и стратегии для обработки перезагрузки значений из ConfigMap.
В основном, Spring предлагает 3 режима обновления (refresh, restart_context и shutdown) и две стратегии (event или polling). Подробности см. в официальной документации.