С помощью какого типа ресурсов мы можем управлять конфигурацией нашего приложения
Файл конфигурации позволяет хранить параметры приложения в формате XML для одного и более вычислительного устройства.
Для того, чтобы ощутить всю мощь работы с конфигурационными файлами, создадим проект-консольное приложение, а затем добавим файл конфигурации.
В обозревателе решений правый клик мыши на проект -> Добавить (Add . ) -> Создать элемент (Create new)
Примечание: в старых версиях Visual Studio файл конфигурации добавляется через добавить -> создать новый -> выбираем "xml файл" в списке с элементами.
После добавления откроем файл конфига и лицезреем следующее:
Добавим раздел appSettings в наш файл, и затем элементы "ключ-значение" :
Здесь мы объявляем три переменные неявного типа, которым в то же время присваиваем значение элемента appSettings по ключу.
Очевидным образом, мы увидим следующий вывод на консоли:
Так как ConfigurationManager.AppSettings является NameValueCollection , косвенно наследуемым от IEnumerable , то нижеприведенный код имеет место быть:
Здесь мы получаем элементы appSettings в виде коллекции, затем берем у каждого элемента коллекции ключ и подставляем в метод Get, который впоследствии возвращает нам необходимое значение. То есть, мы получим тот же самый результат, но зато без явной подстановки ключей.
Итак, брать конфигурационные данные из стандартной секции appSettings мы научились. Едем дальше.
Для того, чтобы добавить свою собственную конфигурационную секцию, мы освободим поле боя, оставив консольное приложение и конфиг в первоначальном виде.
Теперь предоставим сведения о конфигурации пары имя/значение в разделе конфигурации:
Важно (!) секция configSections должна всегда быть в шапке файла конфигурации, тк она отвечает за инициализацию остальных секций. В ином случае приложение будет выдавать ошибки выполнения.
Что и как здесь происходит? Мы получаем секцию по имени в виде System.Object, приводим к NameValueCollection, и получаем значение элемента по ключу. Здесь все довольно просто.
Перейдем к самой сложной части данной статьи: создание структуры данных через App.Config (не рекомендуется для новичков)
Итак, для того что бы загрузить свою структуру данных из файла App.Config нам потребуются следующие классы:
ConfigurationSection — Этот объект вернет нам пользовательскую секцию.
ConfigurationElementCollection — Это собственно коллекция элементов, которые мы определим в пользовательской секции.
ConfigurationElement — Это сам элемент, описывающий какую-то определенную вами сущность.
Очистим поле боя и приведем секцию configSections к следующему виду:
Далее создадим нашу собственную секцию, которая будет реализовывать нашу собственную модель данных:
Замечание : если кому-то не нравится добавление узлов командой add в данном примере, то всегда можно сделать свой собственный префикс, используя следующий код:
при определении коллекции элементов в структуре данных. Тогда в конфигурационном файле можно будет писать так:
Закончим модификации в файле конфигурации и перейдем к организации взаимодействия нашего приложения с ним.
Первым делом создадим класс-наследник от ConfigurationSection, что позволит нам взаимодействовать с нашей секцией в файле конфигурации через ConfigurationManager во время исполнения программы.
Атрибут ConfigurationProperty("Folders") требуется для сопоставления свойства FolderItems с корневым узлом нашей структуры данных.
Класс FoldersCollection является наследником ConfigurationElementCollection, который обеспечивает взаимодействие с коллекцией наших элементов, описанных в Аpp.config . Определяется класс так:
Последним нам нужно создать ConfigurationElement , класс который свяжет нас с конечными данными, определенными в конфигурационном файле.
Атрибут ConfigurationProperty("folderType") требуется для того, что бы проассоциировать имя xml-атрибута в файле конфигурации. Остальные параметры атрибута такие как DefaultValue="", IsKey=true, IsRequired=true определяют только различные опции применимые к свойствам.
Обычно клиенты разворачивают на отдельных серверах, но тут пришла задача, прощупать возможность запуска в openshift и пособирать граблей.
Для начала надо поговорить про наше приложение. Проект с богатой историей. Используется в больших организациях и вероятно каждый из вас косвенно пересекался. Приложение поддерживает множество баз данных, интеграций итд итп.
Пререквизиты
Приложение должно работать в совершенно разных окружениях. Как результат наша документация по установке, весьма, обширная. Но если посмотреть свысока, то ничего сложного:
- Применить схему БД.
- Настроить сервер приложений.
- Установить лицензию.
- Настроить приложение и интеграции с внешними системами.
Но мир жесток, у нас был ряд ограничений:
- Приложение можно собирать только на специально Jenkins, который занимается подписанием. И только там.
- Нет доступа из клиентского Openshift в окружение для разработки.
- По ряду идеологических причин Не было возможности переиспользовать существующие Docker образы для разработки.
- У нас есть ansible playbooks для установки и настройки приложения на серверах.
Ansible-container demo
Ansible container это открытое программное обеспечение, которое преследует цель автоматизации сборки контейнеров, деплоя и управления процессом. Как можно догадаться из названия. для сборки контейнеров используется Ansible. У нас уже были написано Ansible роли для инсталляции и развертывания приложения поверх серверов, так что мы решили не изобретать велосипед и переиспользовать их. Не то что бы это идеальный инструмент, но быстрое переиспользование существующих ролей оказалось решающим фактором для демо.
По большому счету, что бы сделать сделать демо мы взяли существующие роли, настраивающие всё и вся, и сделали "монолитный контейнер". Что собрать контейнер не было особых проблем, т.к. у Openshift есть замечательные рекомендации, но отдельно отмечу:
- Роли было необходимо было доработать, т.к. мы используем systemd.
- По умолчанию, в целях безопасности в openshift запрещено использовать некоторые syscall. Как следствие будут нюансы с chroot, sudo. Привет CVE-2019-5736.
- Аналогично из соображений безопасности контейнер запускается из под пользователя со случайным ID, это так же настраиваемое поведение.
Основная идея в этом пункте, что мы сделали демо ооочень быстро.
Multiple containers demo
Демо контейнер выполнил свою роль и мы распилили его на отдельные составляющие:
- Наше приложение.
- База данных.
- Внешние сервисы итд.
Первое с чем столкнулись, как инициализировать базу данных? Понятно что используем миграции, но когда и как их применять? Тут стоит дать ссылку на замечательную статью описывающие устройство POD: PODs life. По большому счету есть несколько подходом:
- Использовать init-container
- Использовать системы оркестрации, которые определят порядок развертывания сервисов и накатят миграции когда надо.
Мы решили пойти по пути Init-container. Т.е. в POD нашего приложение, до старта нашего приложения, стартует контейнер, который катит миграции. Но как сконфигурировать само приложение и внешние интеграции?
Initialize the application
Как я уже упоминал, наше приложение может и должно работать совершенно в разных окружениях, с разными БД и интеграциями. Опять же вопрос, как это все настраивать?
- Использовать системы оркестрации, которые определят порядок развертывания сервисов и применять конфигурацию после старта приложения.
- Передавать через переменные окружения контейнеру как настроиться.
- Использовать start hook.
- Сделать отдельный контейнер, который содержит конфигурацию и применит ее к приложению. Грубо аналог миграция для БД.
Мы выбрали последний подход, т.к. он позволяет делать конфигурацию воспроизводимой и самодостаточной. Только зачем-то изначально сделали этот контейнер в отдельном replication controller с фактором 1.
Ок, почитаем документацию снова.
A pod (as in a pod of whales or pea pod) is a group of one or more containers (such as Docker containers), with shared storage/network, and a specification for how to run the containers.
POD это группа контейнеров. В итоге наш под состоял из 3 контейнеров
- Init container для инициализации a PostgreSQL.
- Контейнер с приложение.
- Контейнер с конфигурацией приложения.
У нас получилась схема как выглядит приложение развернутое. Теперь настало время обсудить инструментарий в природе существует много уже готового, я рассмотрю некоторые из списка ниже и сделаю субъективные выводы.
Openshift templates
Плюсы:
Минусы:
- Еще один шаблонизатор.
- Длинные и ужасные YAML файлы.
- Если у вас есть зависимости между сервисами и их очередностью старта, то будет сложно.
Scripts and template
Плюсы:
- Можете использовать отличные инструменты и всю мощь ООП.
Минусы:
- Костыли, которые поддерживать. И не только вам.
Terraform k8s provider
Плюсы:
- Вы не беспокоитесь об очередности создания элементов инфраструктуры.
- Можно переиспользовать код инфраструктуры как модули.
- Можно добавить логику инициализации приложения.
Минусы:
- Нет поддержки Openshift, только k8s.
- Иногда устаревшая дока и модули.
- Еще одна тула в вашей команде.
Ansible-container
Плюсы:
Минусы:
- Огромные образы, т.к. идут одним слоем.
- Выглядит покинутым и не поддерживаемым. Был заменен Ansible bender.
Ansible k8s module
Плюсы:
- Один плэйбук для описания все инфраструктуры проекта внутри Openshift.
- Переиспользование кода в виде ролей.
- Можно добавить логику инициализации приложения.
Минусы:
- Нет поддержки прокси.
- Вы заботитесь об удаление. Если объект больше не нужен надо описать его удаление.
- Вы сами описываете очередность создания элементов инфраструктуры.
Ansible Playbook Bundle
Утилита Ansible Playbook Bundle (APB) предлагает подход: а давайте запакуем ansible роли для развертывания приложения внутри k8s/openshift в контейнер и будем запускать внутри k8s/openshift.
Плюсы:
- Всё свое ношу с собой.
- Тестируемо и воспроизводимо.
- Интеграция с Service catalogue(дружелюбный веб интерфейс для запуска приложений).
Минусы:
- Вам нужны привилегии уровня администратора.
- Документация иногда оставляет желать лучшего.
Result
Не хочется быть последний инстанцией, но поделюсь своими умозаключениями:
Я создаю новое приложение, основанное на потоке изображения, которое соответствует изображению докеров в приватном реестре докеров Windows OpenShift. Команда:
Вопрос 1: Эта команда автоматически создает конфигурацию развертывания (dc), на которую можно ссылаться как dc/my-image ? Эта конфигурация развертывания связана с my-project ?
Вопрос 2: Что такое команда oc для создания конфигурации развертывания? В руководстве разработчика OpenShift есть раздел " Создание конфигурации развертывания", но, на удивление, он не говорит, как создать DC или дать какие-либо примеры. Он просто показывает структуру JSON и говорит, что DC можно управлять с помощью команды oc .
спросил(а) 2017-02-20T17:02:00+03:00 4 года, 9 месяцев назадДа, ваша команда создаст материал в указанном проекте. Вы можете проверить, какие объекты создаются с помощью команды oc get . т.е. чтобы проверить, какие у вас есть DC, вы должны выполнить oc get dc или oc get deploymentconfigs .
Другие полезные команды oc describe - похоже на get но больше информации. oc status -v - см. более широкую информацию о проекте, включая предупреждения и ошибки.
Вы создаете DC и любые другие типы ресурсов, используя команду oc create . например, вы копируете пример DC с URL-адреса, на который вы ссылаетесь, и помещаете его в файл. Наконец, вы oc create -f mydc.yaml . Поддерживаются как YAML, так и JSON.
Как вы видите, некоторые команды могут создавать DC самостоятельно, без предоставления им YAML или JSON. Вы можете позже изменить существующие ресурсы с помощью oc edit service/my-app . Существует команда oc patch подходящая для сценариев.
Вы можете увидеть существующий ресурс YAML, делающий oc get dc/myds -o yaml . То же самое с любым другим ресурсом. Имейте в виду, что в настоящее время вы используете желаемый проект или используете параметр -n как вы делаете в своем примере.
Не так сложно, как только вы поймете некоторые основы и научитесь использовать команду oc describe и oc logs для отладки проблем с вашими изображениями/контейнерами. например, oc describe pod/my-app-1-asdfg , oc logs my-app-1-asdfg , oc logs -f dc/my-app .
Много работая с командами разработки, только что перешедшими на OpenShift, мы стремимся дать им рекомендации и лучшие практики для успешного создания и внедрения приложений на этой платформе. По итогам этой работы мы отобрали 14 ключевых, на наш взгляд, практик, которые можно разбить на две категории: надежность приложений и безопасность приложений. Эти категории пересекаются, поскольку чем выше надежность, тем лучше безопасность, и наоборот, а сам список лучших практик выглядит следующим образом.
Надежность приложений
В этом разделе собраны 9 передовых практик, помогающих повысить доступность и аптайм приложений, а также сделать их лучше в плане пользовательского опыта.
1. Не храните конфигурацию приложения внутри контейнера
Если контейнерный образ содержит конфигурацию для какой-то конкретной среды (Dev, QA, Prod), его не получится без изменений переносить между средами. Это плохо с точки зрения надежности процесса выпуска версий, поскольку в продакшн пойдет уже не тот образ, который тестировался на предыдущих этапах. Поэтому не держите внутри контейнера конфигурацию приложения для конкретной среды, а храните ее отдельно, например, с помощью ConfigMaps и Secrets.
2. Задавайте ресурсные требования и лимиты в определениях pod’а
Без должной настройки требований к ресурсам приложения могут создавать непосильную нагрузку на память и процессор. И наоборот, имея явно прописанные требования приложения к ЦП и памяти, кластер может эффективно выполнять диспетчеризацию так, чтобы предоставлять приложению запрашиваемые ресурсы.
3. Прописывайте зонды активности (liveness) и готовности (readiness) в определениях pod’а
С помощью этих зондов кластер может обеспечивать базовую устойчивость: перезапускать приложение, если не проходит liveness, или больше не маршрутизировать на него трафик, когда не отвечает readiness. Подробнее см. раздел Мониторинг работоспособности приложений в документации OpenShift Platform.
4. Используйте PodDisruptionBudget для защиты приложений
Иногда pod’ы приходится убирать с узла кластера, например, при обслуживании хоста, или когда autoscaler даунсайзит кластер, отключая лишние узлы. Чтобы приложение при этом оставалось доступным, надо настроить объекты PodDistruptionBudget.
5. Правильное завершение работы pod’ов
При завершении работы pod должен доводить до конца все текущие запросы и корректно закрывать все открытые подключения, чтобы перезагрузка pod'а, например, при обновлении приложения, проходила незаметно для конечных пользователей.
6. Один контейнер – один процесс
Старайтесь, чтобы каждый процесс работал в своем собственном контейнере. Это повышает изоляцию процессов и предотвращает проблемы с маршрутизацией сигналов и появление зомби-процессов, которые иначе придется периодически вычищать. Подробнее см. раздел Избегайте нескольких процессов в документации OpenShift Platform.
7. Используйте системы мониторинга приложений и оповещения
Мониторинг приложений средствами Prometheus и Grafana помогает поддерживать нормальную работу приложений в продакшн-режиме согласно требованиям бизнеса.
8. Пусть приложения пишут свои логи в stdout/stderr
Тогда OpenShift сможет собирать и пересылать их в централизованную систему обработки (ELK, Splunk). Логи – это бесценный ресурс при анализе работы приложения в продакшн-режиме. Кроме того, оповещения, генерируемые на базе содержимого логов, помогают проконтролировать, что приложение работает как задумано.
9. Изучите целесообразность применения Circuit breakers, Timeouts, Retries, Rate Limiting
Эти механизмы повышают устойчивость приложения к отказам, защищая от перегрузок (Rate Limiting, Circuit Breakers) и помогая справляться с сетевыми проблемами (Timeouts, Retries). Рассмотрите возможность применения решения OpenShift Service Mesh, которое позволяет реализовать эти функции, не трогая код приложений.
Безопасность приложений
Ниже приводятся 5 передовых и, на наш взгляд, абсолютно необходимых практик усиления безопасности приложений, которые вам обязательно надо рассмотреть к применению.
10. Используйте только доверенные контейнерные образы
Применяйте вендор-образы везде, где это возможно, поскольку они гарантировано тестируются, дорабатываются на безопасность и обеспечиваются поддержкой. Что касается community-образов, используйте наработки только тех сообществ, которым доверяете. И помните, что в публичных реестрах, вроде Docker Hub, имеются образы неизвестного происхождения – не используйте их ни в коем случае!
11. Используйте последние версии базовых контейнерных образов
Потому что только в них есть все доступные на текущий момент исправления безопасности. Настройте свой конвейер непрерывной интеграции так, чтобы при каждой сборке образов приложений он всегда подтягивал последние версии базовых образов, а также пересобирал образы приложений при выходе новых версий базовых образов.
12. build-образы отдельно, runtime-образы отдельно
Build-образ содержит зависимости, которые нужны при сборке приложения, но не нужны при запуске. Поэтому создавайте отдельные runtime-образы с минимумом зависимостей, чтобы уменьшить поверхность атак, а заодно и размер образа.
13. Restricted security context constraint (SCC) – везде, где только возможно
Модифицируйте образы, чтобы они могли работать в рамках restricted SCC (подробнее см. раздел Поддержка произвольных идентификаторов пользователей в документации). Риски взлома приложений включают в себя и ситуации, когда злоумышленник получает контроль над самим приложением, и restricted SCC позволяет обезопасить узел кластера на этот случай.
14. TLS для защиты коммуникаций между компонентами приложения
Компоненты приложения могут пересылать друг другу конфиденциальные данные. Если сеть, в которой работает OpenShift, не может считаться безопасной, то трафик между компонентами приложения можно защитить с помощью TLS-шифрования, а уже упомянутый OpenShift Service Mesh позволяет сделать это, не трогая код приложений.
Заключение
Итак, теперь у вас есть список из 14 лучших практик для построения более надежных и безопасных приложений OpenShift. Это хорошее начало для выработки собственного кодекса разработки для членов вашей команды. Дополнительные сведения и рекомендации можно найти в разделе Создание образов документации OpenShift.
Разрабатывая приложения, можно столкнуться с ситуацией, когда некоторые значения не следует сохранять в листинге исходного кода программы, а эти значения зависят от той среды, где приложение исполняется. В случае сохранения этих настроек в исходном коде, приложение пришлось бы перекомпилировать всякий раз, когда оно переносится в другую среду, например, на другой компьютер .
Поэтому очень часто такие значения выносят за рамки исходного кода программы и сохраняют их во внешнем файле, который впоследствии можно изменить – файл конфигурации.
Исторически сложилось, что конфигурирование приложения - одна из наиболее классических задач, с которой сталкиваются разработчики приложений. Эта задача не специфична только для веб-приложений и решается в различных платформах по -разному.
В более ранних инструментах разработки приложений не содержалось встроенных средств для работы с настройками приложения, поэтому разработчикам каждый раз приходилось этот механизм разрабатывать заново. Формат настроек не был стандартизирован и каждый разработчик хранил в том формате, в котором считал нужным (например, в текстовом файле или в бинарном файле с собственной структурой). Подобный подход создавал ряд неудобств при разработке и использовании приложений. При разработке приложений приходилось постоянно возвращаться к вопросу о том, как хранить настройки приложения. При использовании приложения никогда не было стандартизированного формата конфигурационного файла, который можно использовать.
Платформа . NET Framework решает этот вопрос – в рамках платформы существует стандартный способ хранения конфигурации. В качестве формата для хранения настроек используется формат XML . Использование XML в качестве формата файла конфигурации обладает рядом преимуществ:
Все конфигурационные файлы в рамках платформы . NET имеют расширение ".config". Например, для настройки веб-приложений используются файлы " web .config", а для настольных – " app .config".
Существует целая иерархия файлов конфигурации. Схема хранения файлов настроек показана ниже.
Система конфигурации . NET Framework разделена на две части – глобальные настройки и настройки приложения. Настройки, заданные в глобальных файлах конфигурации доступны для каждого приложения, т.е. глобальные настройки наследуются в каждом приложении. На уровне приложения определяется главный файл конфигурации (он расположен в корневой папке приложения), в котором задаются основные настройки приложения. Для каждой подпапки в рамках проекта может быть определен собственный файл конфигурации. В этом случае для этой подпапки существует собственная конфигурация , которая наследуется из основного файла конфигурации и уточняется файлом конфигурации в данной папке. Такая иерархия позволяет определить глобальные настройки и постепенно, где это необходимо, уточнять их.
Глобальные файлы настроек расположены в папке "C:\Windows\Microsoft.NET\Framework\*версия+\CONFIG". Как видно из приведенной выше схемы в этой папке содержаться два файла – machine.config и web .config. Оба этих файла содержат конфигурационные настройки и имеют идентичный формат. Разница этих файлов заключается в том, что параметры, определенные в файле "machine.config" нельзя переопределить на уровне приложения, а параметры, определенные в файле " web .config" можно переопределять на любом уровне иерархии. Например, давайте рассмотрим параметр , который отвечает за конкурентный доступ к веб-сервисам на основе платформы WCF. Если этот параметр определить в глобальном файле " web .config", то он будет доступен для каждого приложения. При этом нет необходимости определять его для каждого приложения – он уже определен. Однако, если требуется изменить этот параметр для конкретного приложения, то это можно легко сделать переопределив этот параметр в файле " web .config" для приложения. Однако, если переместить определение этого параметра из глобального файла " web .config" в глобальный файл "machine.config", то переопределить параметр на уровне приложения уже будет нельзя.
Возможность такого "жесткого" задания конфигурационных параметров позволяет явным образом задавать политики сервера, в рамках которых исполняются множество приложений. При этом каждое приложение не сможет установить настройки сервера так, как ему это необходимо.
Как уже было сказано ранее, конфигурационные файлы построены на базе формата XML . Пример конфигурационного файла приведен ниже.
Как видно, файл конфигурации содержит ряд настроек в иерархическом виде. Каждый узел в файле настроек определяет отдельный аспект функционирования веб-приложения – настройки кеширования, настройки компиляции и отладки, строки соединения с СУБД , параметры безопасности и т.д. Для каждого узла определен собственный класс -обработчик этих настроек. Узел не может быть добавлен в конфигурационный файл , если с ним не ассоциирован никакой класс -обработчик. В противном случае при запуске приложения будет сгенерирована ошибка.
Система конфигурации . NET Framework предполагает использование файлов конфигурации и для хранения собственных параметров конфигурации приложения, а не только общих настроек. Для того, чтобы добавить свою секцию конфигурации в файл настроек, для начала следует определить эту секцию специальным образом. Для этого существует стандартная секция " configSections ". Определить секцию конфигурации можно следующим образом.
В приведенном примере мы определяем секцию с именем " MySection ". При этом должен быть создан класс " MyConfigSection ", который является обработчиком параметров этой секции. После в конфигурационном файле можно определить и саму конфигурационную секцию и задать нужные параметры.
Количество параметров, уровень вложенности и весь внешний вид секции конфигурации определяет класс -обработчик для этой конфигурационной секции. Этот класс является наследником базового класса " ConfigurationSection ", который содержит базовые механизмы для работы с данными конфигурационных файлов. В наиболее простой ситуации можно определить обработку свойств секции, в данном случае свойства " MyParam1 ". Для этого необходимо создать в классе-обработчике новое публичное свойство и разметить его соответствующим атрибутом " ConfigurationProperty ".
Такое определение позволит среде исполнения . NET Framework корректно интерпретировать конфигурационную секцию.
Следует отметить, что все конфигурационные секции, доступные по умолчанию в . NET Framework объявлены подобным образом (используя секцию " configSections "). Эти определения сделаны в глобальном файле " machine.config ".
Наконец, для получения доступа к существующим конфигурационным секциям используется объект WebConfigurationManager . Для этого следует воспользоваться статическим методом " OpenWebConfiguration " для получения объекта Configuration , а затем методом " GetSection " для получения доступа к конкретной секции. После этого, эту секцию можно привести к классу-обработчику этой секции и, используя публичные свойства последнего, работать с настройками конфигурации.
Таким образом, подсистема конфигурации . NET Framework является мощным механизмом, который позволяет, как переопределять стандартные настройки приложения, так и определять собственные параметры конфигурирования.
Читайте также: