Меня зовут Дмитрий Демченко. Я главный разработчик (Java) в крупной российской строительной компании. На проектах мы активно используем новые технологии и подходы при разработке приложений, в том числе Spring Cloud и компоненты, входящие в общую экосистему этой технологии.

В данной статье я хочу поделиться с вами своим подходом к конфигурации Spring Boot приложений в облаке, где файлы конфигураций приложений находятся в Git репозитории, и который я применяю в работе и считаю одним из самых простых, удобных и легко поддерживаемых.

Плюсы и минусы данного подхода

Плюсы:

  1. Работает из коробки

  2. Легко настраивается

  3. Версионирование в Git

  4. Нет необходимости поднимать и обслуживать сторонние хранилища секретов

Минусы: по моему мнению очевидных минусов нет.

В рамках данной статьи я не буду затрагивать поднятие всего стека Spring Cloud, а расскажу лишь о примитивном применении Spring Cloud Config.

Выбор версии Java и Spring Boot

Многие проекты сейчас переписываются, а какие-то уже используют Java 17, поэтому и мы будем использовать Java 17 и Spring Boot 3.

Настройка Spring Cloud Config Server

Для начала нам нужно создать новый Spring Boot проект и подключить зависимость spring-cloud-config-server

Пример для Maven:

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-config-server</artifactId>
</dependency>

Пример для Gradle:

implementation 'org.springframework.cloud:spring-cloud-config-server'

Далее нам нужно сконфигурировать сервис таким образом, чтобы он мог получать конфигурации наших Spring Boot приложений из нашего Git репозитория.

application.yml:

server:
  port: 8888

spring:
  application:
    name: config-service
  cloud:
    config:
      server:
        git:
          uri: git@github.com:dmitrii-demchenko/spring-boot-configs.git
          username: username
          password: password
          default-label: main
          search-paths: "{application}"

И добавить аннотацию @EnableConfigServer:

@SpringBootApplication
@EnableConfigServer
public class ConfigServiceApplication {

    public static void main(String[] args) {
        SpringApplication.run(ConfigServiceApplication.class, args);
    }

}

Репозиторий с конфигурациями приложений

Все конфигурации находятся в своих папках, где имя папки является именем приложения.

Представим, что у нас есть три приложения: clients, orders и couriers. Исходя из этого, структура нашего репозитория с конфигурациями будет выглядеть так:

Структура репозитория
Структура репозитория

Здесь можно увидеть 3 папки с конкретными конфигурациями наших приложений, а так же 2 общих файла: application.yml и application-dev.yml

Дело в том, что Spring Cloud Config позволяет объединять конфигурацию таким образом, чтобы мы могли избавиться от "копипаста" и не повторять общие свойства в каждом конфигурационном файле. А постфикс -dev дает нам возможность задавать конфигурации для определенного профиля, с которым запускается приложение.

Посмотрим на содержимое этих файлов:

application.yml:

spring:
  datasource:
    driver-class-name: org.postgresql.Driver
  jpa:
    hibernate:
      ddl-auto: validate
    properties:
      hibernate:
        format_sql: true
    show-sql: false
  flyway:
    enabled: true

application-dev.yml:

spring:
  datasource:
    hikari:
      maximum-pool-size: 10
      minimum-idle: 0

clients/application-dev.yml:

spring:
  datasource:
    url: jdbc:postgresql://localhost:5432/clients
    username: username
    password: password

orders/application-dev.yml:

spring:
  datasource:
    url: jdbc:postgresql://localhost:5432/orders
    username: username
    password: password

couriers/application-dev.yml:

spring:
  datasource:
    url: jdbc:postgresql://localhost:5432/couriers
    username: username
    password: password

Получается, что, например, для приложения clients конфигурация должна выглядеть следующим образом:

spring:
  datasource:
    url: jdbc:postgresql://localhost:5432/clients
    username: username
    password: password
    driver-class-name: org.postgresql.Driver
    hikari:
      maximum-pool-size: 10
      minimum-idle: 0
  jpa:
    hibernate:
      ddl-auto: validate
    properties:
      hibernate:
        format_sql: true
    show-sql: false
  flyway:
    enabled: true

Запустим сервис конфигураций и проверим это.

Получение конфигурации (руками)

Выполнив запрос на эндпоинт http://localhost:8888/clients/dev получаем ответ:

{
  "name": "clients",
  "profiles": [
    "dev"
  ],
  "label": null,
  "version": "2e6f884042d117b7298baa10955e5f1d0bf8f6f2",
  "state": null,
  "propertySources": [
    {
      "name": "https://github.com/dmitrii-demchenko/spring-boot-configs.git/clients/application-dev.yml",
      "source": {
        "spring.datasource.url": "jdbc:postgresql://localhost:5432/clients",
        "spring.datasource.username": "username",
        "spring.datasource.password": "password"
      }
    },
    {
      "name": "https://github.com/dmitrii-demchenko/spring-boot-configs.git/application-dev.yml",
      "source": {
        "spring.datasource.hikari.maximum-pool-size": 10,
        "spring.datasource.hikari.minimum-idle": 0
      }
    },
    {
      "name": "https://github.com/dmitrii-demchenko/spring-boot-configs.git/clients/application.yml",
      "source": {
        "spring.application.name": "clients"
      }
    },
    {
      "name": "https://github.com/dmitrii-demchenko/spring-boot-configs.git/application.yml",
      "source": {
        "spring.datasource.driver-class-name": "org.postgresql.Driver",
        "spring.jpa.hibernate.ddl-auto": "validate",
        "spring.jpa.properties.hibernate.format_sql": true,
        "spring.jpa.show-sql": false,
        "spring.flyway.enabled": true
      }
    }
  ]
}

Так убеждаемся, что приложение clients, запущенное с профилем dev получит необходимую конфигурацию.

Подключение сервиса конфигураций к приложению

Подключение зависимости spring-cloud-starter-config в приложении, которое будет получать конфигурацию от сервиса конфигураций.

Пример для Maven:

<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-config</artifactId>
</dependency>

Пример для Gradle:

implementation 'org.springframework.cloud:spring-cloud-starter-config'

Осталось описать application.yml:

spring:
  application:
    name: clients

---

spring:
  config:
    activate:
      on-profile: dev
    import: optional:configserver:http://localhost:8888
  cloud:
    config:
      fail-fast: true

Пройдемся по основным свойствам.

Во-первых, нужно задать имя приложения таким образом, чтобы оно совпадало с именем папки в репозитории с конфигурациями. Это свойство spring.application.name

Во-вторых, нужно указать приложению откуда получать конфигурации, за это отвечает свойство spring.config.import

Так же, в своем примере я добавил следующие свойства:

  1. spring.config.activte.on-profile: dev для того, чтобы сказать приложению при запуске с каким профилем получать конфигурации. Можно указать множество профилей.

  2. spring.cloud.config.fail-fast: true этот параметр задан для того, чтобы, если приложению не удалось получить конфигурацию, оно не будет запущено. Такой режим рекомендуется использовать в реальных проектах.

Запустим приложение с профилем dev и убедимся, что оно успешно получило конфигурацию от нашего сервиса конфигураций:

Но что делать, если нужно приложение запустить локально и без получения конфигураций от сервиса конфигураций?

Как пример, которым я пользуюсь, это добавить файл application-local.yml и запускать приложение с профилем local:

spring:
  cloud:
    config:
      enabled: false

Резюме

Можно легко и быстро развернуть готовое и централизованное решение, которое будет сообщать приложениям в облаке их конфигурации. Это вполне рабочий вариант, который вы можете применять в своей работе.

Ссылки

Официальная документация Spring Cloud Config

Комментарии (5)


  1. stgunholy
    29.09.2023 15:46

    Пароли открытым текстом в репозитории лежат?


    1. Dmitrii_Demchenko Автор
      29.09.2023 15:46
      +1

      Все зависит от требований безопасности. Хранить пароли можно не только в репозитории. Можно как и в переменных окружения, так и в секретах, если это Kubernetes. От проекта к проекту по разному может быть.


  1. breninsul
    29.09.2023 15:46

    А в чем преимущество по сравнению с переменными окружения?

    Приложения же скорее-всего в контейнере с автодеплоем при обновлении docker-compose в репозитории?


    1. Dmitrii_Demchenko Автор
      29.09.2023 15:46

      В том, что у вас может быть несколько окружений - девелоперское, для тестирования, продакшен и т. д., и тут преимуществом будет централизация конфигов. К тому же конфиги часто разбухают и хранить все свойства в переменных окружения станет не удобно.


      1. breninsul
        29.09.2023 15:46

        с окружением проблем нет - разные ветки, по поводу разбухания - согласен