Одной из основных проблем при разработке и последующей эксплуатации микросервисов является грамотная и аккуратная настройка их инстансов. В этом, на мой взгляд, может помочь новый фреймворк microconfig.io. Он позволяет довольно элегантно решить некоторые рутинные задачи настройки приложений.
Если у вас много микросервисов, и каждый из них поставляется вместе со своим файлом/файлами настроек, то велика вероятность совершить ошибку в одном из них, которую без должной сноровки и системы логирования может быть очень не просто отловить. Основная задача, которую перед собой ставит фреймворк — свести к минимуму дублирующие параметры настройки инстанса, тем самым уменьшить вероятность добавления ошибки.
Рассмотрим на примере. Допустим, что есть простое приложение с файлом конфигурации yaml. Это может быть любой микросервис на любом языке. Посмотрим, как фреймворк можно применить к этому сервису.
Но прежде, для большего удобства, создадим пустой проект в Idea IDE, предварительно установив в ней плагин microconfig.io:
Настраиваем конфигурацию запуска плагина, можно использовать конфигурацию по умолчанию, как на скриншоте сверху.
Наш сервис называется order, тогда в новом проекте создадим подобную структуру:
В папку с именем сервиса помещаем файл конфигурации — application.yaml. Все микросервисы запускаются в каком-то окружении, так что, кроме создания конфига самого сервиса, необходимо описать саму среду: для этого создадим папку envs и добавим в нее файл с именем нашей рабочей среды. Таким образом, фреймворк создаст конфигурационные файлы для сервисов в среде dev, так как в настройках в плагина установлен именно этот параметр.
Структура файла dev.yaml будет довольно простая:
Фреймворк работает с конфигурациями, которые объединены в группы. Для нашего сервиса выберем имя для группы mainorder. Фреймворк находит каждую такую группу приложений в файле окружений и создает для всех из них конфигурации, которые находит в соответствующих папках.
В самом файле настроек сервиса order укажем пока только один параметр:
Теперь запустим плагин, и он сгенерирует нам нужную конфигурацию нашего сервиса по указанному в свойствах пути:
Можно обойтись и без установки плагина, просто скачав дистрибутив фреймворка и запустив его из командной строки.
Такое решение подойдет для использования на сервере сборки.
Стоит отметить, что фреймворк отлично понимает property синтаксис, то есть обычные property файлы, которые можно использовать вместе в yaml конфигурациями.
Добавим еще один сервис payment и усложним одновременно существующий.
В order:
В payment:
Основная проблема этих конфигураций — наличие большого количества копипаста в настройках сервисов. Посмотрим, как фреймворк поможет от нее избавиться. Начнем с самой очевидной — наличие конфигурации eureka в описании каждого микросервиса. Создадим новый каталог с файлом настроек и добавим в нее новую конфигурацию:
И в каждый из наших проектов теперь добавим строку #include eureka.
Фреймворк автоматически сам найдет конфигурацию eureka и скопирует ее в конфигурационные файлы сервисов, при этом отдельная конфигурация eureka не будет создана, так как мы не укажем ее в файле среды dev.yaml. Сервис order:
Также мы можем вынести настройки базы данных в отдельную конфигурацию, изменив строку импорта на #include eureka, oracle.
Стоит отметить, что каждое изменение при перегенерации конфигурационных файлов фреймворк отслеживает и помещает в специальный файл рядом с основным файлом конфигурации. Запись в его логе выглядит так: “Stored 1 property changes to order/diff-application.yaml”. Это позволяет быстро обнаружить изменения в больших конфигурационных файлах.
Вынос общих частей конфигурации позволяет избавиться от множества ненужного копипаста, но не позволяет гибко создавать конфигурацию для различных сред — эндпоинты наших сервисов единственны и захардкожены, это плохо. Попробуем это убрать.
Хорошим решением будет держать все эндпоинты в какой-то одной конфигурации, на которую могли бы ссылаться остальные. Для этого в фреймворк внедрена поддержка плейсхолдеров. Вот как изменится файл конфигурации eureka:
Теперь посмотрим, как работает этот плейсхолдер. Система находит компонент с именем endpoints и ищет в нем значение eurekaip, после чего подставляет в нашу конфигурацию. Но как быть с различными средами? Для этого создадим файл настроек в endpoints следующего вида application.dev.yaml. Фреймворк самостоятельно, по расширению файла принимает решение к какой среде относится данная конфигурация и подгружает ее:
Содержимое dev файла:
Такую же конфигурацию мы можем создать и для портов наших сервисов:
Все важные настройки находятся в одном месте, тем самым уменьшается вероятность ошибки из-за разбросанных параметров по файлам конфигурации.
Фреймворк предоставляет множество уже готовых плейсхолдеров, например, можно получить название директории, в которой находится файл конфигурации и присвоить его:
Благодаря этому, не нужно дополнительно указывать имя приложения в конфигурации и его можно также вынести в общий модуль, например, в ту же eureka:
Файл конфигурации order сократится до одной строчки:
В случае, если какая-либо настройка из родительской конфигурации нам не нужна мы можем указать ее в нашей конфигурации и именно она будет применена при генерации. То есть если по какой-то причине нам нужно уникальное имя для сервиса order, просто оставим параметр spring.application.name.
Допустим, в сервис необходимо добавить кастомизированные настройки логирования, которые хранятся в отдельном файле, например, logback.xml. Создадим для него отдельную группу настроек:
В базовой конфигурации укажем фреймворку куда размещать нужный нам файл настроек логирования с помощью плейсхолдера @ConfigDir:
В файле logback.xml настраиваем стандартные аппендеры, которые так же в свою очередь могут содержать плейсхолдеры, которые фреймворк изменит во время генерации конфигов, например:
Добавляя в конфигурации сервисов импорт logback, мы автоматически получаем настроенное логирование для каждого сервиса:
Настало время более подробно ознакомится со всеми доступными плейсхолдерами фреймворка:
${this@env} — возвращает имя текущей среды.
${...@name} — возвращает имя компонента.
${...@configDir} — возвращает полный путь к каталогу config компонента.
${...@resultDir} — возвращает полный путь к каталогу назначения компонента (полученные файлы будут помещены в этот каталог).
${this@configRoot} — возвращает полный путь к корневому каталогу хранилища конфигураций.
Также система позволяет получить переменные среды, например путь к java:
${env@JAVA_HOME}
Либо, так как фреймворк написан на JAVA, можем получить системные переменные аналогичные вызову System::getProperty с помощью конструкции такого вида:
${system@os.name}
Стоит упомянуть про поддержку языка расширений Spring EL. В конфигурации применимы подобные выражения:
и можно использовать локальные переменные в конфигурационных файлах с помощью выражения #var:
Таким образом, фреймворк представляет из себя довольно мощный инструмент для тонкой и гибкой настройки конфигураций микросервисов. Фреймворк прекрасно выполняет свою основную задачу — устранение копипаста в настройках, консолидацию настроек и как следствие сведение к минимуму возможных ошибок, при этом позволяя легко комбинировать конфигурации и изменять для различных сред.
Если вас заинтересовал данный фреймворк, то рекомендую посетить его официальную страницу и ознакомится с полной документацией, либо покопаться в исходниках тут.
Если у вас много микросервисов, и каждый из них поставляется вместе со своим файлом/файлами настроек, то велика вероятность совершить ошибку в одном из них, которую без должной сноровки и системы логирования может быть очень не просто отловить. Основная задача, которую перед собой ставит фреймворк — свести к минимуму дублирующие параметры настройки инстанса, тем самым уменьшить вероятность добавления ошибки.
Рассмотрим на примере. Допустим, что есть простое приложение с файлом конфигурации yaml. Это может быть любой микросервис на любом языке. Посмотрим, как фреймворк можно применить к этому сервису.
Но прежде, для большего удобства, создадим пустой проект в Idea IDE, предварительно установив в ней плагин microconfig.io:
Настраиваем конфигурацию запуска плагина, можно использовать конфигурацию по умолчанию, как на скриншоте сверху.
Наш сервис называется order, тогда в новом проекте создадим подобную структуру:
В папку с именем сервиса помещаем файл конфигурации — application.yaml. Все микросервисы запускаются в каком-то окружении, так что, кроме создания конфига самого сервиса, необходимо описать саму среду: для этого создадим папку envs и добавим в нее файл с именем нашей рабочей среды. Таким образом, фреймворк создаст конфигурационные файлы для сервисов в среде dev, так как в настройках в плагина установлен именно этот параметр.
Структура файла dev.yaml будет довольно простая:
mainorder:
components:
- order
Фреймворк работает с конфигурациями, которые объединены в группы. Для нашего сервиса выберем имя для группы mainorder. Фреймворк находит каждую такую группу приложений в файле окружений и создает для всех из них конфигурации, которые находит в соответствующих папках.
В самом файле настроек сервиса order укажем пока только один параметр:
spring.application.name: order
Теперь запустим плагин, и он сгенерирует нам нужную конфигурацию нашего сервиса по указанному в свойствах пути:
Можно обойтись и без установки плагина, просто скачав дистрибутив фреймворка и запустив его из командной строки.
Такое решение подойдет для использования на сервере сборки.
Стоит отметить, что фреймворк отлично понимает property синтаксис, то есть обычные property файлы, которые можно использовать вместе в yaml конфигурациями.
Добавим еще один сервис payment и усложним одновременно существующий.
В order:
eureka:
instance.preferIpAddress: true
client:
serviceUrl:
defaultZone: http://192.89.89.111:6782/eureka/
server.port: 9999
spring.application.name: order
db.url: 192.168.0.100
В payment:
eureka:
instance.preferIpAddress: true
client:
serviceUrl:
defaultZone: http://192.89.89.111:6782/eureka/
server.port: 9998
spring.application.name: payments
db.url: 192.168.0.100
Основная проблема этих конфигураций — наличие большого количества копипаста в настройках сервисов. Посмотрим, как фреймворк поможет от нее избавиться. Начнем с самой очевидной — наличие конфигурации eureka в описании каждого микросервиса. Создадим новый каталог с файлом настроек и добавим в нее новую конфигурацию:
И в каждый из наших проектов теперь добавим строку #include eureka.
Фреймворк автоматически сам найдет конфигурацию eureka и скопирует ее в конфигурационные файлы сервисов, при этом отдельная конфигурация eureka не будет создана, так как мы не укажем ее в файле среды dev.yaml. Сервис order:
#include eureka
server.port: 9999
spring.application.name: order
db.url: 192.168.0.100
Также мы можем вынести настройки базы данных в отдельную конфигурацию, изменив строку импорта на #include eureka, oracle.
Стоит отметить, что каждое изменение при перегенерации конфигурационных файлов фреймворк отслеживает и помещает в специальный файл рядом с основным файлом конфигурации. Запись в его логе выглядит так: “Stored 1 property changes to order/diff-application.yaml”. Это позволяет быстро обнаружить изменения в больших конфигурационных файлах.
Вынос общих частей конфигурации позволяет избавиться от множества ненужного копипаста, но не позволяет гибко создавать конфигурацию для различных сред — эндпоинты наших сервисов единственны и захардкожены, это плохо. Попробуем это убрать.
Хорошим решением будет держать все эндпоинты в какой-то одной конфигурации, на которую могли бы ссылаться остальные. Для этого в фреймворк внедрена поддержка плейсхолдеров. Вот как изменится файл конфигурации eureka:
client:
serviceUrl:
defaultZone: http://${endpoints@eurekaip}:6782/eureka/
Теперь посмотрим, как работает этот плейсхолдер. Система находит компонент с именем endpoints и ищет в нем значение eurekaip, после чего подставляет в нашу конфигурацию. Но как быть с различными средами? Для этого создадим файл настроек в endpoints следующего вида application.dev.yaml. Фреймворк самостоятельно, по расширению файла принимает решение к какой среде относится данная конфигурация и подгружает ее:
Содержимое dev файла:
eurekaip: 192.89.89.111
dbip: 192.168.0.100
Такую же конфигурацию мы можем создать и для портов наших сервисов:
server.port: ${ports@order}.
Все важные настройки находятся в одном месте, тем самым уменьшается вероятность ошибки из-за разбросанных параметров по файлам конфигурации.
Фреймворк предоставляет множество уже готовых плейсхолдеров, например, можно получить название директории, в которой находится файл конфигурации и присвоить его:
#include eureka, oracle
server.port: ${ports@order}
spring.application.name: ${this@name}
Благодаря этому, не нужно дополнительно указывать имя приложения в конфигурации и его можно также вынести в общий модуль, например, в ту же eureka:
client:
serviceUrl:
defaultZone: http://${endpoints@eurekaip}:6782/eureka/
spring.application.name: ${this@name}
Файл конфигурации order сократится до одной строчки:
#include eureka, oracle
server.port: ${ports@order}
В случае, если какая-либо настройка из родительской конфигурации нам не нужна мы можем указать ее в нашей конфигурации и именно она будет применена при генерации. То есть если по какой-то причине нам нужно уникальное имя для сервиса order, просто оставим параметр spring.application.name.
Допустим, в сервис необходимо добавить кастомизированные настройки логирования, которые хранятся в отдельном файле, например, logback.xml. Создадим для него отдельную группу настроек:
В базовой конфигурации укажем фреймворку куда размещать нужный нам файл настроек логирования с помощью плейсхолдера @ConfigDir:
microconfig.template.logback.fromFile: ${logback@configDir}/logback.xml
В файле logback.xml настраиваем стандартные аппендеры, которые так же в свою очередь могут содержать плейсхолдеры, которые фреймворк изменит во время генерации конфигов, например:
<file>logs/${this@name}.log</file>
Добавляя в конфигурации сервисов импорт logback, мы автоматически получаем настроенное логирование для каждого сервиса:
#include eureka, oracle, logback
server.port: ${ports@order}
Настало время более подробно ознакомится со всеми доступными плейсхолдерами фреймворка:
${this@env} — возвращает имя текущей среды.
${...@name} — возвращает имя компонента.
${...@configDir} — возвращает полный путь к каталогу config компонента.
${...@resultDir} — возвращает полный путь к каталогу назначения компонента (полученные файлы будут помещены в этот каталог).
${this@configRoot} — возвращает полный путь к корневому каталогу хранилища конфигураций.
Также система позволяет получить переменные среды, например путь к java:
${env@JAVA_HOME}
Либо, так как фреймворк написан на JAVA, можем получить системные переменные аналогичные вызову System::getProperty с помощью конструкции такого вида:
${system@os.name}
Стоит упомянуть про поддержку языка расширений Spring EL. В конфигурации применимы подобные выражения:
connection.timeoutInMs: #{5 * 60 * 1000}
datasource.maximum-pool-size: #{${this@datasource.minimum-pool-size} + 10}
и можно использовать локальные переменные в конфигурационных файлах с помощью выражения #var:
#var feedRoot: ${system@user.home}/feed
folder:
root: ${this@feedRoot}
success: ${this@feedRoot}/archive
error: ${this@feedRoot}/error
Таким образом, фреймворк представляет из себя довольно мощный инструмент для тонкой и гибкой настройки конфигураций микросервисов. Фреймворк прекрасно выполняет свою основную задачу — устранение копипаста в настройках, консолидацию настроек и как следствие сведение к минимуму возможных ошибок, при этом позволяя легко комбинировать конфигурации и изменять для различных сред.
Если вас заинтересовал данный фреймворк, то рекомендую посетить его официальную страницу и ознакомится с полной документацией, либо покопаться в исходниках тут.