Обновить
7
2

Пользователь

Отправить сообщение

Здравствуйте!

Спасибо за вопрос. Мы выбрали единое решение не просто так, а по практическим причинам — чтобы создавать согласованные, легко настраиваемые и управляемые фоновые сервисы.

Когда у вас есть единый шаблон, вы можете:

  • Шаблонно создавать и быстро менять как настройки очередей, так и сами менеджеры

  • Легко расширять его — например, мы добавили аналог ActionFilter из ASP.NET Core для фоновых менеджеров, что решает типовые задачи: логирование, обработки ошибок, метрики

  • Снизить когнитивную нагрузку на разработчиков — не нужно каждый раз выбирать между Quartz, Confluent.Kafka и другими инструментами

Важно отметить: наш шаблон не заменяет Quartz или Confluent.Kafka. Это адаптерный слой, который:

  • Использует проверенные библиотеки (Cronos для расписаний, Confluent.Kafka для очередей)

  • Приводит их к единому интерфейсу и конфигурации

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

Что это даёт на практике:

  1. Разработчику не нужно думать, как реализовать очередную фоновую задачу с учётом требований по логированию, метрикам и обработке ошибок

  2. Всё работает согласованно: один конфиг, один стиль кода, один подход к тестированию

  3. Если появится новый брокер сообщений — мы встроим его в шаблон один раз, и все разработчики смогут использовать его без переписывания инфраструктурного кода

В итоге мы получаем скорость разработки без потери контроля и качества

Отличный вопрос, который в статье описан не совсем прозрачно. Если отвечать кратко, то нет, не рассматривали.

Основных причин для это 2

  • Мобильность. В силу разных ограничений (платформы, ИБ и т.д.) у нас есть ряд приложений, которые административно нельзя или технически пока не получается вынести в k8s. Такие приложения хоститься на обычных windows или linux машинах. Нам нужно было решение, которое в этом плане будет платформо независимым, и с которым приложение сможет с минимальными затратами быстро качевать с одной платформы на другую

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

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

Спасибо!

Что произойдет с ОФД, если процесс упадет? Потеря данных или храните очередь в каком-то хранилище?

Зависит от реализации ваших фоновых менеджеров. Падение прям приложения или процесса в приложении у нас не частая история, но если происходит, то обычно срабатывает один из кейсов:

  • Если обработка по таймеру или расписанию, то скорее всего при запуске менеджеров обрабатываются очереди основанные таблицах в БД. И в случаее падения приложения просто не сохраняется транзакция. Тогда при рестарте приложение просто также находит эти элементы в этих таблицах и обрабатывает их

  • Если идет обработка очереди в Kafka или Rabbit. То пока элемент очереди поступивший из брокера не будет успешно обработан, то в брокер не поступит сигнал о комите данного сообщения. Тогда при рестарте приложение просто также подхватит этот же элемент из брокера. Правда в этом кейс уже именно реализация PushBackgroundManagerHostedService так устроена. Поэтому в менеджере с транзакциями, подключением и прочим колдовать не надо

Здравствуйте! Да, эту интеграцию делали на .net. Библиотеку не используем, так как не сразу её заметили, а как заметили, то уже не захотели тратить силы на переход. Поэтому описать плюсы и минусы не смогу

Еще хочу обратить ваше внимание на 2 момента при взаимодействии с Диадок

  1. Через API Диадок передачу данных можно выполнять как в JSON, так и в protobuf форматах

  2. В API диадок параметры запросов пронумерованы. Это важно. Эти параметры нужно передавать именно в указанных порядках. Иначе ваш запрос может быть не верно интерпретирован. Это касается обоих форматов передачи данных

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

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

Еще хотел добавить про service filter и action filter. Они вызываются уже после работы middleware. А нам нужна была логика во время его работы. Переделывать реализацию по умлочанию под чистую не охота, т.к. базовая её функциональность нам нужна

Здравствуйте!

Как будто-бы Net Core это не язык программирования

Net Core это не язык прогаммирования

Но нет, там не нужно ничего дорабатывать, нужно просто написать так, как нужно, и все, будь то свой middleware, service filter или action filter

Все в ваших руках. Платформа не закрытая поэтому как и что в ней делать решать вам.

middleware нам не подходит, так как нам нужна поддержка хендлеров из сторонних библиотек. Они созданы для работы с со стандартным middleware авторизации.

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

У нас весь конфиг хранится в vault и перезаписывается при CI/CD

Еще вопрос вспыл. Значит ли это, что при смене секрета нужно перезапустить CI/CD, а не просто приложение? Крайне редко при перезапуске CI/CD что-то идет не так и поэтому это сложно назвать проблемой, но все же интересно

Привет!) Прям весь конфиг? Или именно секции с секретами?

В зависимости от того, куда идëт деплой - берëтся весь конфиг и перезаписывается на тачке.

И еще вопрос. Разве это не означает, что в итоге на конечной тачке секреты хранятся на уровне файлов? А то этого как раз лучше избегать в целях ИБ

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

Ваш подход удобен с точки зрения конфигурирования. Но не удобен с точки зрения чтения и настройки остальных систем. А нам это важно так как системы уже существую, уже не настроены чисто под конфигурацию систем и уже у множества их пользователей появились паттерны поведения с этими системами. Если кто-то далекий от конфигурации приложения (например, бизнес-аналитик, PO и т.д.) захочет прочитать секерт, то ему малось будет сложно ориентироваться в предложанном варианте. Дублировать секреты, не хочется. Можно конечно как-то это регламинтировать и написать инструкции, но как паказывает практика "делай удобно", а не "пиши инструкции" вызывает в итоге меньше проблем

Проблем с чужими секретами нет, так как другие секреты находятся в других секциях, а свалки секретов, где всякий берёт то, что ему нужно, этого избегаем

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

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

Согласен, есть такой недостаток. Но так как свойства в конфигурации являются ссылочными и на них нет никакой завязки в самом приложении, то я не вижу в этом проблемы. Т.е. используя ссылочное свойство Password-VaultSecret, идет ссылка на свойство Password. В коде будет завязка на свойство Password. И вам ничего не мешает использовать другой провайдер определяющий это свойство. Поэтому с этой точки зрения описанный вами минус будет в основном влиять только на наличие ссылочного свойства в конфиге. Можно конечно сделать завязку на ссылочное свойство, но по умолчанию привязка не работает со свойствами в намиеновании которых есть "-", да это как-то странно на такое завязываться

Учитывая это, нужен отдельный mapping: ключ Vault => ключ приложения. Программисту не интересно, откуда в приложении возьмётся секрет, он определяет параметр конфигурации, который нужно заполнить.

Тут соглашусь, это прям хороший плюсь

Вот как-то так вижу. Нисколько не умаляю проделанной вами работы, и плюсы. Но всё равно не покидает ощущение, как будто к микроскопу приделали ручку от молотка, для удобства забивания гвоздей :)

Может быть, кто как видит. Поэтому статья и является лишь рассказом, а не рекомендацией. Спасибо за обратную связь :)

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

При администрировании секретов в HashiCorp Vault необходимо учитывать структуру заданную секциями в свойстве VaultSecretsPath

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

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

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

Также при таком подходе во многие приложения будут попадать секерты, которые попросту ему не нужны. А если делать в Vault пространства для каждого приложения со своим набором секертов, то это просто приведет в свое вермя к большому количеству таких пространств, что в свою очередь будет не удобно использовать и поддерживать

Хороший вопрос! Мы над ним в свое время хорошо подумали. Но, к сожалению, в целях соблюдения политик ИБ, я не могу расскрыть данную информацию

И если вы не используете обновление секретов, то почему не взяли готовое решение в виде External Secrets Operator?

Мы не рассматривали данный функционал. Я почитал о нем. Он бы нам не подошел по следующим причинам:

  • у нас есть приложения которые не работают в Kubernetes

  • у нас и без того большое кол-во приложений. Мы хотим минимизировать администрирование в наших системах. А эта затея приведет к её увеличению. а эффект от этого будет не большой

  • API этого инструмета хоть и простой, но явно сложенее чем то к чему мы пришли

А как вы работаете с обновлением значений секретов если они обновились в Vault?

Этот момент пока что в планах. Сейчас мы с этим не работаем. Пока что по старинке перезагружаем приложение, но хочется от этого уходить

Спасибо за совет, учтем в будушем. Но как уже упомяналось в 1-й части статьи, такая задача была нам в новинку. У нас в принципе не было компетенций в области работы с изображениями. Поэтому что удалось узнать в кратчайшие сроки, на том и базировали фанкционал распознавания подписи

Информация

В рейтинге
1 220-й
Откуда
Москва, Москва и Московская обл., Россия
Зарегистрирован
Активность

Специализация

Бэкенд разработчик, Веб-разработчик
C#
Разработка программного обеспечения
Visual Studio
.NET
ООП
Паттерны проектирования
Entity framework
ASP.NET
WPF
Git