Микросервисы. Унификация и почему это так важно. Часть 1 — Конфигурация



    Введение


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

    Ни для кого не секрет, что данный подход в разработке продуктов всё больше и больше захватывают рынок. И чем больше мы в них окунаемся, тем больше необходимо не забывать базовые правила работы с ними. Дабы структурировать полученный нами опыт написание микросервисных продуктов решено было написать серию статей по тому как же можно обобщить некоторые аспекты разработки на все сервисы.

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

    Всех заинтересовавшихся прошу под кат.

    Проблема


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

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

    Вы можете сказать что этим должен заниматься разработчик каждого сервиса, но что если все ваши конфиги хранятся в том же Kubernetes, доступ куда нельзя давать всем разработчикам? Бедный DevOps будет вынужден потратить кучу времени просто на изучение сервисов и методов их конфигураций. И данная процедура будет повторяться с обновлением сервисов, особенно если кто-то захочет попробовать что-нибудь новое в настройке сервиса. С таким подходи в команде постоянно будет тратится часть времени на работу с конфигами, а не на разработку новых фич, правку багов и т.п.

    Как раз таки для этого случая и требуется общий метод конфигурации, который не будет привязан к определённому языку или технологии и позволит настраивать все сервисы с минимальными отличиями в общей структуре конфига. Для данной задачи нами была разработана система по настройке «модулей»(сервисов) с использованием yaml файлов, возможностью хранить конфигурации для разных стейджов(dev/prod/local etc) и делить всё это дело на различные блоки относящиеся к тем или иным вещами.

    Спецификация


    Можно много разглагольствовать на тему того где и как это можно использовать, но я предлагаю сразу перейти к спецификации данного метода конфигурации. Как говорится, теория — это хорошо, а практика ещё лучше.

    Требования системы


    Начнём с определения нашей системы и требований для неё
    • Каждый модуль — независимый компонент в контейнере
    • Мы можем передавать в контейнер переменные окружения
    • Мы НЕ можем на лету менять конфигурацию без перезагрузки сервиса (создания нового контейнера).
    • Все fallback действия(вроде переключения на резервную базу данных) осуществляются вне компонента и являются для него прозрачными.


    Требования к методу конфигурации


    Теперь определимся с тем что же мы хотим видеть от нашего метода конфигурации что бы удовлетворить всем требованиям.

    1. Тип конфигурационного файла — YAML указанной структуры. YAML был выбран нами по нескольким причинам:
      • Возможность писать комментарии и удобная структура в отличии от JSON
      • Возможность описания массивов в отличии от ENV
      • Из коробки можно использовать для инклюда из values.yaml в helm (Kubernetes)

    2. Конфигурационные файлы должны мержиться в дерево конфигурации
    3. Конфигурация должна быть stage-specific. На каждый stage — свой полный сет. Тут стоит сделать несколько оговорок для уточнения:
      • Переиспользовать значения переменных из другого стейджа нельзя, кроме стейджа defaults, который забронирован для дефолтных значений.
      • При загрузке конфигурации надо делать рекурсивный мерж слоя конфигурации из указанного стейджа поверх defaults с приоритетом слоя указанного стейджа. Значения (массивы и т.д.) объединяться не должны.
      • При наличии нескольких конфигурационных файлов для одного стейджа, ключи в них должны мержиться и, соответственно, должны быть уникальными относительно друг-друга.

    4. Текущий используемый стейдж должен определяться значением переменной окружения «STAGE». Изменение переменной у работающего экземпляра сервиса не предполагается.
    5. Абсолютный путь до директории с конфигурацией должен определяться значением переменной окружения «CONFIG_PATH». Для удобства, возможен fallback при отсутствии переменной в некий дефолтный путь, который должен быть обозначен в документации к модулю. В этом случае, указанный путь должен быть относительным к корню директории приложения.


    Примеры конфигурации


    Допустим у нас есть сервис которому необходима хранить настройки подключения к Postgres, а так же немного информации о себе

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

    defaults


    # configuration/defaults/service.yaml
    defaults:
      version: 1.0.0
      name: "config-example"
    
    # configuration/defaults/redis.yaml
    defaults:
      redis:
        host: "host"
        db: 0
        port: 6379
        password: "password"
    


    dev


    # configuration/dev/redis.yaml
    dev:
      redis:
        host: "localhost"
        password: "hard_pwd"
    


    Результирующий конфиг


    version: 1.0.0
    name: "config-example"
    redis:
        host: "localhost"
        db: 0
        port: 6379
        password: "hard_pwd"
    


    Выводы



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

    Для тех кому данный метод конфигурации интересен в «голом виде»:
    Наши пакеты для разных языков программирования


    За помощь в написании стать отдельное спасибо Roque, SMGladkovskiy
    Поделиться публикацией

    Комментарии 0

    Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

    Самое читаемое