RE:NODE

Приложения11 мин чтения

Версии .NET: LTS или STS и когда обновляться

Какую версию .NET запускать в продакшене: сроки поддержки LTS и STS, что означает конец поддержки, смена target framework, global.json и roll-forward.

0 прочтений

Запускайте в продакшене LTS-релиз, если нет причин поступить иначе, и планируйте обновление до окончания поддержки, а не после. На октябрь 2026 года это .NET 10, текущий релиз с долгосрочной поддержкой, который поддерживается до ноября 2028 года. .NET 8 (LTS) и .NET 9 (STS) оба теряют поддержку 10 ноября 2026 года - через несколько недель, - так что всё, что ещё работает на любом из них, уже должно переезжать. .NET 11 ожидается в ноябре 2026 года как релиз со стандартным сроком поддержки. Эта статья объясняет модель поддержки, что на самом деле происходит, когда версия выходит из поддержки, как перенести проект между версиями без сюрпризов и что global.json и настройки roll-forward делают с тем, какой SDK и рантайм вы получаете.

Модель релизов: одна мажорная версия в год#

Microsoft выпускает новую мажорную версию .NET каждый ноябрь. Чётные релизы - LTS (long-term support, долгосрочная поддержка), нечётные - STS (standard-term support, стандартная поддержка).

ТипСрок поддержкиПримеры
LTS3 года.NET 6, .NET 8, .NET 10
STS2 года (18 месяцев до .NET 7).NET 7, .NET 9, .NET 11

Раньше окно STS составляло 18 месяцев. Начиная с .NET 9 Microsoft продлила его до 24 месяцев, поэтому .NET 9 теперь заканчивается в тот же день, что и .NET 8, а не на полгода раньше. STS-релиз - не бета: он полностью поддерживается, получает те же ежемесячные патчи и используется в продакшене многими командами, которым нужны новые возможности на год раньше. Цена - обновляться каждый год, а не раз в два-три года.

Текущий жизненный цикл для версий, которые вы, скорее всего, встретите:

ВерсияТипВыпущенаКонец поддержки
.NET 6LTSноябрь 2021ноябрь 2024 (завершена)
.NET 7STSноябрь 2022май 2024 (завершена)
.NET 8LTSноябрь 202310 ноября 2026
.NET 9STSноябрь 202410 ноября 2026
.NET 10LTSноябрь 2025ноябрь 2028
.NET 11STSожидается в ноябре 2026ожидается в ноябре 2028

Патчи выходят в Patch Tuesday, второй вторник каждого месяца. Дата окончания поддержки - последний день патчей; после неё версия не получает вообще никаких исправлений безопасности. Официальная страница политики поддержки .NET от Microsoft - источник, который стоит проверить, прежде чем строить планы вокруг любой даты из этой таблицы.

Что на самом деле означает конец поддержки#

В день окончания поддержки ничего не выключается. Приложение на .NET 8 запустится 11 ноября 2026 года точно так же, как накануне. Что меняется:

  • Больше никаких патчей безопасности. Следующая уязвимость в Kestrel, стеке TLS, System.Text.Json или самом рантайме остаётся открытой в вашей версии навсегда. Для веб-приложения, смотрящего в интернет, в этом весь аргумент.
  • Образы контейнеров перестают обновляться. Теги mcr.microsoft.com/dotnet/aspnet:8.0 по-прежнему можно скачать, но пакеты базовой операционной системы под ними тоже больше не обновляются.
  • Библиотеки идут дальше. В течение следующего года-двух пакеты NuGet отказываются от старых target, и версия библиотеки, которая чинит встреченный вами баг, может не поддерживать ваш фреймворк.
  • Хостинги и инструменты идут дальше. Агенты сборки, установщики SDK и образы хостинга со временем перестают включать старые рантаймы.

Практическое правило: LTS-релиз даёт год пересечения со следующим LTS. Обновляйтесь в течение этого года, когда новая версия получила первые несколько патчей, а используемые вами библиотеки её догнали, а не в последний месяц.

Target framework, SDK и рантайм: три разные версии#

Большая часть путаницы при обновлении происходит из-за того, что это считают одним числом.

  • Target framework - то, под что компилируется проект, задаётся в файле проекта как <TargetFramework>net10.0</TargetFramework>. Он определяет, какие API вы можете вызывать и какой рантайм нужен приложению.
  • SDK - набор инструментов, которым собирается проект: dotnet build, dotnet publish, компиляторы. Версии SDK выглядят как 10.0.100; разряд сотен - это feature band (10.0.1xx, 10.0.2xx), а последние две цифры - патч.
  • Рантайм - то, что выполняет скомпилированное приложение. Приложениям ASP.NET Core нужен общий рантайм ASP.NET Core той же мажорной версии, с версиями вида 10.0.0, 10.0.1 и так далее.

SDK умеет собирать под свою версию и под все более ранние target framework, так что SDK .NET 10 без возражений собирает проект net8.0. Обратное не работает: SDK .NET 8 не может собрать net10.0. Рантайм запускает приложения, нацеленные на его мажорную версию, а с настройками roll-forward - иногда и на более поздние.

bash
$ dotnet --list-sdks8.0.415 [/usr/share/dotnet/sdk]10.0.100 [/usr/share/dotnet/sdk]$ dotnet --list-runtimesMicrosoft.AspNetCore.App 10.0.0 [/usr/share/dotnet/shared/Microsoft.AspNetCore.App]Microsoft.NETCore.App 10.0.0 [/usr/share/dotnet/shared/Microsoft.NETCore.App]

Ошибку, которую видят, когда рантайм и target не совпадают, ни с чем не спутаешь:

code
You must install or update .NET to run this application.Framework: 'Microsoft.AspNetCore.App', version '10.0.0' (x64)The following frameworks were found:  8.0.11 at [/usr/share/dotnet/shared/Microsoft.AspNetCore.App]

На хостинговом сервере приложений доступен тот рантайм, который предоставляет образ сервера, так что выясните, какие SDK и рантайм в нём есть, прежде чем менять target. Self-contained публикация обходит это полностью, упаковывая рантайм прямо в результат сборки, ценой гораздо большего размера деплоя; этот компромисс разобран в статье dotnet publish и параметры рантайма.

Фиксация SDK через global.json#

Без global.json команда dotnet использует самый новый SDK, установленный на машине. На ноутбуке это обычно то, что нужно, а на сервере сборки иногда проблема: обновление SDK может между двумя сборками одного и того же коммита изменить предупреждения анализаторов, поведение по умолчанию или формат генерируемых файлов.

global.json
{  "sdk": {    "version": "10.0.100",    "rollForward": "latestFeature"  }}

version - минимальный SDK. rollForward решает, насколько выше него может уйти dotnet:

rollForwardЧто разрешает
patchТот же feature band, только более новый патч
latestPatchСамый высокий патч в том же feature band (по умолчанию, когда задан version)
feature / latestFeatureБолее новые feature band в пределах того же major.minor
minor / latestMinorБолее новые минорные версии
major / latestMajorЛюбой более новый SDK
disableРовно указанную версию

latestFeature - разумный выбор для большинства репозиториев: сборки остаются на SDK .NET 10 и при этом подхватывают патчи и feature band. disable звучит безопасно, а на деле ломает сборку на каждой машине, где есть патч новее вашего. Если ни один установленный SDK не удовлетворяет файлу, ошибка называет нужную версию - установите этот SDK или ослабьте политику.

global.json влияет только на SDK. Он не меняет target framework и не меняет рантайм, на котором работает приложение.

Roll-forward рантайма#

Framework-dependent приложение с target net10.0 автоматически работает на самом высоком установленном патче рантайма 10.0.x. Так пропатченный рантайм приносит вашему приложению исправления безопасности без пересборки, и именно поэтому не стоит пытаться закрепить конкретный патч рантайма.

Между минорными и мажорными версиями поведение задаётся свойством проекта RollForward (или переменной окружения DOTNET_ROLL_FORWARD):

RollForwardПоведение
Minor (по умолчанию)Перейти на более высокую минорную версию, если запрошенной нет
LatestPatchТолько патчи запрошенного major.minor
MajorПерейти на более высокую мажорную версию, если подходящей нет
LatestMinor / LatestMajorВсегда использовать самую высокую из доступных
DisableТолько точная версия

Major позволяет приложению net8.0 запуститься на машине, где есть только рантайм .NET 10. Обычно это работает, но обновления не заменяет: вы запускаетесь на рантайме, с которым приложение никогда не тестировалось, и все ломающие изменения между двумя версиями применяются молча. Используйте это, чтобы выиграть время в экстренной ситуации, а не как политику.

Обновление проекта шаг за шагом#

Переход с .NET 8 на .NET 10 для большинства веб-приложений - короткая работа, если делать её по порядку:

  1. Установите новый SDK и добавьте или обновите global.json, чтобы репозиторий собирался им.
  2. Смените target framework во всех проектах: net8.0 на net10.0. В решении с множеством проектов Directory.Build.props, задающий TargetFramework один раз, избавляет от правки каждого файла.
  3. Обновите пакеты Microsoft до соответствующей мажорной версии. Пакеты вроде Microsoft.AspNetCore.Authentication.JwtBearer, Microsoft.EntityFrameworkCore.* и Microsoft.Extensions.* версионируются вместе с рантаймом; приложение net10.0 должно использовать их версии 10.x.
  4. Обновите сторонние пакеты, особенно провайдеры баз данных. Npgsql, MySQL-провайдер Pomelo и другие выпускают по версии на каждую мажорную версию EF Core; прежде чем начинать, проверьте, что нужная вам существует.
  5. Собирайте с видимыми предупреждениями и читайте их. Новые анализаторы и пометки obsolete сначала появляются как предупреждения.
  6. Прочитайте список ломающих изменений для каждой версии, которую пропускаете. Microsoft публикует такой список для каждого релиза в документации .NET, с разбивкой по областям. Переход с 8 на 10 означает чтение списков и для 9, и для 10.
  7. Прогоните тесты, затем запустите приложение на копии продакшен-данных и задеплойте на тестовый сервер, прежде чем на продакшен.
bash
$ dotnet list package --outdated$ dotnet list package --vulnerable --include-transitive

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

Некоторые изменения последних версий, на которых люди действительно спотыкаются:

  • .NET 8 сменил порт по умолчанию в образах контейнеров ASP.NET Core от Microsoft с 80 на 8080 и добавил ASPNETCORE_HTTP_PORTS как более простой способ его задать. Приложения, полагавшиеся на порт 80 внутри контейнера, перестали отвечать после обновления базового образа.
  • .NET 9 удалил реализацию `BinaryFormatter` - он выбрасывает исключение во время выполнения. Старый код кэширования или сессий, который сериализовал объекты через него, падает только тогда, когда выполняется этот путь кода.
  • Мажорные версии EF Core следуют за рантаймом. EF Core 10 требует .NET 10, так что новый EF Core без нового рантайма не взять, а миграции, сгенерированные более новым инструментом EF Core, стоит просмотреть перед применением. Как применять их безопасно, разобрано в статье Миграции EF Core в продакшене.

Как выкатить обновление, не ставя на кон продакшен#

Самое безопасное обновление - то, которое можно отменить за минуту. Держите обновление в отдельной ветке и деплойте её на второй, небольшой сервер, прежде чем она окажется рядом с тем, куда ходят ваши пользователи. Тестовый сервер, который при запуске подтягивает ветку с обновлением и направлен на копию продакшен-базы, ловит два сбоя, которые пропускают unit-тесты: зависимость, которая ведёт себя иначе во время выполнения, и миграцию, которая на реальных данных выполняется медленно или блокирует таблицу. Тестовый сервер рядом с продакшеном рассказывает, как держать их раздельно, а деплой приложения ASP.NET Core из GitHub - про сам деплой.

Перед переключением продакшена сделайте бэкап базы данных и убедитесь, что предыдущая сборка в одном коммите от вас. Если новая версия ведёт себя плохо, откат - это revert коммита со сменой target framework и повторный деплой; уже выполненная миграция базы - та часть, которую так просто не откатишь, поэтому изменения схемы должны выходить отдельным деплоем до или после обновления рантайма, а не в том же самом.

Перевод библиотеки net8.0 на multi-targeting - хороший промежуточный шаг, когда от неё зависят несколько приложений: <TargetFrameworks>net8.0;net10.0</TargetFrameworks> (обратите внимание на множественное число) собирает обе версии, и каждое приложение берёт подходящую.

LTS или STS: что запускать?#

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

STS, если вы деплоите непрерывно, регулярно обновляете зависимости и хотите получать новые возможности - особенно улучшения производительности, которые приходят каждый год, - на двенадцать месяцев раньше. С 24 месяцами поддержки STS-релиз больше не тот выбор с коротким запалом, каким был раньше, но он всё равно заканчивается одновременно с предыдущим LTS или раньше, так что вы подписываетесь на обновление каждый год.

Чего запускать не стоит, так это версию без поддержки или preview в продакшене. Preview и release candidate появляются каждый год начиная с февраля; release candidate идут с go-live лицензией от Microsoft, что означает поддержку использования в продакшене, но приложение, зависящее от preview-рантайма на хостинговом сервере, зависит от того, есть ли он на этом сервере.

Версии языка C# следуют за SDK: C# 12 вышел вместе с .NET 8, C# 13 - с .NET 9, C# 14 - с .NET 10. LangVersion по умолчанию соответствует вашему target framework, а задавать его выше, чем поддерживает target, - неподдерживаемый сценарий, даже если код компилируется.

.NET Framework - это другой продукт#

Последнее, что важно для хостинга: .NET Framework 4.x - это не .NET. Это изначальный фреймворк только для Windows, всё ещё поддерживаемый как часть Windows, на котором всё ещё работает огромное количество софта, и на Linux он не работает. Приложение ASP.NET (не ASP.NET Core) на .NET Framework 4.8 вообще нельзя задеплоить в Linux-контейнер. Перенос означает портирование на ASP.NET Core на .NET 10, что для MVC-приложений - настоящий проект миграции, а не смена target: в современном .NET нет System.Web.

Библиотеки, которым нужно обслуживать оба мира, нацеливаются на netstandard2.0, который могут использовать .NET Framework 4.6.1 и новее и любой современный .NET. Новые библиотеки только для современного .NET должны нацеливаться прямо на net8.0 или новее.

FAQ#

Безопасно ли всё ещё использовать .NET 8?

До 10 ноября 2026 года он получает патчи безопасности, так что сегодня безопасно. После этой даты он не получает ничего. Если вы сейчас на .NET 8, обновление до .NET 10 должно быть запланировано, а не обдумываться.

Стоит ли пропустить .NET 11 и дождаться .NET 12?

Если вы работаете на LTS-релизах, да: переходите с 10 на 12, который ожидается в ноябре 2027 года, в течение года, когда поддерживаются оба. Если на STS-релизах, берите 11, когда он выйдет и появится его первый патч.

Нужно ли пересобирать приложение для каждого ежемесячного патча?

Для framework-dependent приложений нет. Приложение автоматически переходит на самый новый установленный патч своего рантайма. Self-contained приложения несут свой рантайм с собой, так что для получения каждого патча им нужны пересборка и повторный деплой.

Может ли приложение на .NET 8 работать на рантайме .NET 10?

Только с RollForward, равным Major или LatestMajor, и тогда без всякого тестирования на ломающие изменения более нового рантайма. Правильное решение - сменить target на net10.0 и протестировать.

Что делает global.json, если его нет?

Ничего, а значит, проект собирает самый новый установленный SDK. Локально это нормально, а на серверах сборки может приводить к расхождениям. Добавьте его с rollForward, равным latestFeature, чтобы сборки оставались на той мажорной версии, которую вы задумали.


Комментарии

Полностью анонимно: без аккаунта, без почты, без cookie. Мы храним имя, которое вы ввели, текст и время - больше ничего. Количество ссылок ограничено, разметка не отображается.

0/2000