Но меня больше волнует то, как люди подходят к наполнению самого appsettings.json, ведь слоистость IConfiguration хоть и удобно, но может быть источником скрытых ошибок.
Так вот, я видел на практике следующие подходы:
1. в appsettings.json проставляют значения для production
2. в appsettings.json проставляют значения для локальной разработки
3. в appsettings.json проставляют только общие настройки для всех окружений
4. всё вперемешку
а всё остальное специфичное для окружения выносят в файлы appsettings.{env}.json
или же всё специфичное для окружения пробрасывают через переменные окружения
мне лично не нравится ни один из этих вариантов по нескольким причинам:
1. бывает сложно понять какой конфиг будет в итоге после всех мержей со всех источников
2. Как правило все файлы appsettings.{env}.json попадут в докер образ и в зависимости от окружения будет подключаться один из них
3. нарушается принцип конфигурирования из 12 факторов т.к. многое зашивается в докер образ
4. если ошибиться и указать (или вообще не указать) переменную окружения ASPNETCORE_ENVIRONMENT, то подключится либо не всё, либо совсем не то и может уйти прилично времени прежде чем найдется причина ошибок.
5. сложно или невозможно подменить конфиг без передеплоя приложения
Чтобы устранить эти проблемы, я в своих рабочих проектах использую следующий подход:
Удаляем appsettings.json и оставляем только appsettings.debug.json который подключаем только при локальной разработке. Секреты там не храним, используем user-secrets.
Далее создаем папку configs на 2 папки выше, со структурой:
configs
test
appsettings.json
stage
appsettings.json
prod
appsettings.json
src
api
api.csproj
appsettings.debug.json
Для наглядности, показал папку src с исходниками проекта.
Как можно догадаться, в каждом из файлов лежат конфиги под конкретные окружения.
Так как нет общего appsettings.json, то в каждом из файлов лежит почти полный конфиг поэтому проще понимать что будет в итоге на стенде.
Конфиг будет "почти" полный, так как секреты в репозитории не храним, а подгружаем их с помощью Vault Agent Injector через переменные окружения.
По сути, теперь через переменные окружения передаются в основном только секреты.
Эти конфиги я рендерю как json файлы в ConfigMap отдельным CI/CD пайплайном с триггером на изменения в этой папке.
Таким образом я могу поменять конфиг и задеплоить только его, что сильно быстрее, чем пересобирать и деплоить всё приложение.
Если хочется еще быстрее, то это к FeatureFlags :)
И чтобы все это работало, ConfigMap подключаю к Pod как volume и благодаря этому имею возможность внутри приложения отслеживать изменения конфигов в рантайме и менять конфиги без передеплоя самого приложения, конечно же с соблюдением обратной совместимости в конфиге :)
Как бонус: содержимое configs не попадет в докер образы так как они не участвуют в сборке вообще.
А чтобы не терять привычный опыт в IDE, можно в csproj подключить эти файлы как ссылки на внешние файлы и дать соответствующее имя. При этом они будут открываться из папки configs:
appsettings.prod.json
Так вот, я видел на практике следующие подходы:
1. в appsettings.json проставляют значения для production
2. в appsettings.json проставляют значения для локальной разработки
3. в appsettings.json проставляют только общие настройки для всех окружений
4. всё вперемешку
а всё остальное специфичное для окружения выносят в файлы appsettings.{env}.json
или же всё специфичное для окружения пробрасывают через переменные окружения
мне лично не нравится ни один из этих вариантов по нескольким причинам:
1. бывает сложно понять какой конфиг будет в итоге после всех мержей со всех источников
2. Как правило все файлы appsettings.{env}.json попадут в докер образ и в зависимости от окружения будет подключаться один из них
3. нарушается принцип конфигурирования из 12 факторов т.к. многое зашивается в докер образ
4. если ошибиться и указать (или вообще не указать) переменную окружения ASPNETCORE_ENVIRONMENT, то подключится либо не всё, либо совсем не то и может уйти прилично времени прежде чем найдется причина ошибок.
5. сложно или невозможно подменить конфиг без передеплоя приложения
Чтобы устранить эти проблемы, я в своих рабочих проектах использую следующий подход:
Удаляем appsettings.json и оставляем только appsettings.debug.json который подключаем только при локальной разработке. Секреты там не храним, используем user-secrets.
Далее создаем папку configs на 2 папки выше, со структурой:
configs
test
appsettings.json
stage
appsettings.json
prod
appsettings.json
src
api
api.csproj
appsettings.debug.json
Для наглядности, показал папку src с исходниками проекта.
Как можно догадаться, в каждом из файлов лежат конфиги под конкретные окружения.
Так как нет общего appsettings.json, то в каждом из файлов лежит почти полный конфиг поэтому проще понимать что будет в итоге на стенде.
Конфиг будет "почти" полный, так как секреты в репозитории не храним, а подгружаем их с помощью Vault Agent Injector через переменные окружения.
По сути, теперь через переменные окружения передаются в основном только секреты.
Эти конфиги я рендерю как json файлы в ConfigMap отдельным CI/CD пайплайном с триггером на изменения в этой папке.
Таким образом я могу поменять конфиг и задеплоить только его, что сильно быстрее, чем пересобирать и деплоить всё приложение.
Если хочется еще быстрее, то это к FeatureFlags :)
И чтобы все это работало, ConfigMap подключаю к Pod как volume и благодаря этому имею возможность внутри приложения отслеживать изменения конфигов в рантайме и менять конфиги без передеплоя самого приложения, конечно же с соблюдением обратной совместимости в конфиге :)
Как бонус: содержимое configs не попадет в докер образы так как они не участвуют в сборке вообще.
А чтобы не терять привычный опыт в IDE, можно в csproj подключить эти файлы как ссылки на внешние файлы и дать соответствующее имя. При этом они будут открываться из папки configs:
appsettings.prod.json