Развертывание .NET-приложений: публикация, запуск и варианты деплоя | AdminWiki

Развертывание .NET-приложений: публикация, запуск и варианты деплоя

06 сентября 2026 21 мин. чтения
Содержание статьи

Краткий ответ: как выбрать способ развертывания .NET-приложения

Развертывание ASP.NET Core начинается с публикации проекта командой dotnet publish. Команда собирает приложение в отдельную папку с исполняемыми файлами, библиотеками и ресурсами. Готовый артефакт переносят на сервер, передают конфигурацию окружения и запускают через IIS, systemd или Docker.

Для Windows Server с IIS обычно выбирают IIS и ASP.NET Core Hosting Bundle. Для Linux, где нужен нативный запуск процесса, подходят Kestrel и systemd, а Nginx можно поставить перед приложением как reverse proxy. Docker удобен, когда важны одинаковые окружения сборки и выполнения, изоляция зависимостей и единый способ доставки на разные серверы.

Перед деплоем проверьте Target Framework проекта, доступный .NET runtime, архитектуру процесса, режим публикации и параметры окружения. Framework-dependent-публикация требует установленного runtime на сервере. Self-contained-публикация включает runtime в артефакт и снижает зависимость от настроек целевой машины.

Когда выбрать IIS, systemd или Docker

ВариантПлатформаЗапускRuntimeЛоги и управлениеТиповой сценарий
IISWindows ServerASP.NET Core Module и пул приложенийHosting Bundle или self-contained-пакетЖурналы IIS, Event Viewer, stdout при диагностикеКорпоративные сайты и API в Windows-инфраструктуре
systemdLinuxKestrel как системный сервисRuntime на сервере или self-containedsystemctl и journalctlНативный запуск API и фоновых сервисов
DockerLinux, Windows Server, облачная инфраструктураКонтейнерный процессФиксируется в runtime-образеdocker logs, healthcheck, средства оркестратораПовторяемые сборки, CI/CD, микросервисы

Если требуется сравнить CI/CD, Docker, Kubernetes и скриптовые сценарии по повторяемости и сложности сопровождения, используйте обзор систем развертывания приложений. Для тестового или рабочего Linux-сервера можно использовать облачный VDS, например инфраструктуру Timeweb Cloud, а способ запуска выбрать отдельно.

Что должно получиться после успешного деплоя

  • Приложение запускается без исключений и использует ожидаемое окружение, например Production.
  • Процесс слушает нужный адрес и порт, например 127.0.0.1:5000 за reverse proxy или 0.0.0.0:8080 внутри контейнера.
  • После перезагрузки сервера сервис стартует автоматически.
  • Логи доступны администратору и содержат сведения о старте, ошибках и версии сборки.
  • Проверочный HTTP-запрос возвращает ожидаемый статус, а health endpoint подтверждает готовность обязательных зависимостей.
  • Предыдущая версия артефакта сохранена, поэтому неудачный релиз можно быстро откатить.

Публикация .NET-приложения: от проекта до готового артефакта

Исходный код проекта и папка публикации решают разные задачи. На сервер обычно передают результат dotnet publish, а не весь репозиторий с тестами, локальными настройками и инструментами разработки. Такой подход уменьшает размер поставки и исключает случайное попадание секретов в production.

Проверка проекта перед публикацией

Сначала определите целевую платформу проекта. Откройте файл .csproj и найдите параметр TargetFramework или TargetFrameworks. Примеры значений: net8.0, net9.0, net10.0. Конкретная версия должна совпадать с планом поддержки и окружением, где приложение будет работать.

dotnet --info
dotnet --list-sdks
dotnet --list-runtimes

Проверьте проект в конфигурации Release и отдельно протестируйте production-конфигурацию. Локальный запуск с переменной Development может скрыть отсутствие обязательного параметра, использовать другой файл настроек или включить подробные сообщения об ошибках.

  • Убедитесь, что все project reference и NuGet-зависимости восстанавливаются без ошибок.
  • Проверьте статические файлы, миграции базы данных, шаблоны, сертификаты и другие ресурсы, которые приложение читает при запуске.
  • Зафиксируйте архитектуру: x64, x86 или arm64.
  • Определите каталоги, куда процесс должен писать логи, временные файлы, загруженные документы или кэш.
  • Уберите секреты из appsettings.json, исходного кода и скриптов публикации.

Полезный минимальный тест перед отправкой артефакта на сервер:

dotnet build -c Release
dotnet test -c Release
dotnet publish -c Release -o ./publish

Для решения с несколькими проектами запускайте публикацию именно для веб-проекта. Публикация solution-файла может дать несколько результатов и усложнить выбор каталога, который нужно передать на сервер.

Как опубликовать ASP.NET Core приложение в разных режимах

Framework-dependent, или зависимая от framework, публикация содержит приложение и его зависимости, но использует .NET runtime на целевой машине. Это компактный вариант с централизованным обновлением runtime. Ошибка запуска появится, если нужная версия runtime отсутствует или не совпадает с Target Framework.

dotnet publish ./MyApp.csproj -c Release -o ./publish/fdd

Self-contained-публикация включает runtime для выбранной операционной системы и архитектуры. Она занимает больше места, зато приложение меньше зависит от состояния сервера.

dotnet publish ./MyApp.csproj -c Release -r linux-x64 --self-contained true -o ./publish/linux-x64
dotnet publish ./MyApp.csproj -c Release -r win-x64 --self-contained true -o ./publish/win-x64
РежимЧто нужно на сервереПреимуществаОграничения
Framework-dependentСовместимый .NET runtimeМеньше артефакт, проще централизованно обновлять runtimeЗапуск зависит от версии и архитектуры окружения
Self-containedОС и системные библиотеки, подходящие приложениюПредсказуемая версия runtime внутри поставкиБольше размер, runtime нужно обновлять в каждом артефакте
Single-fileЗависит от выбранного режима публикацииУдобнее переносить один основной исполняемый файлНе отменяет проверку нативных зависимостей и особенностей распаковки

Single-file подключают как дополнительную настройку:

dotnet publish ./MyApp.csproj -c Release -r linux-x64 --self-contained true -p:PublishSingleFile=true -o ./publish/linux-single

Single-file не заменяет выбор между framework-dependent и self-contained. Сначала определите требования к runtime и платформе, затем добавляйте упаковку в один файл, если она упрощает доставку.

Какие файлы передавать на сервер

В папке публикации должны находиться исполняемый файл приложения, библиотеки .NET и сторонних пакетов, файлы конфигурации, статические ресурсы и прочие зависимости, которые добавил SDK. Для framework-dependent ASP.NET Core приложения это часто DLL-файл и команда запуска через dotnet. Для self-contained-публикации создается исполняемый файл под выбранную платформу.

Перед копированием проверьте артефакт локальным запуском:

cd ./publish/fdd
dotnet MyApp.dll

Для self-contained Linux-пакета сначала задайте право исполнения, если оно потерялось при переносе:

chmod +x ./MyApp
./MyApp
  • Передавайте весь каталог публикации, сохраняя структуру подпапок.
  • Не копируйте на сервер bin и obj вместо папки, созданной командой dotnet publish.
  • Не заменяйте production-конфигурацию локальным файлом без проверки значений.
  • Храните данные приложения, журналы и загружаемые файлы в отдельных каталогах.

Как сделать публикацию повторяемой

Повторяемая публикация использует одинаковые версии SDK, одинаковый файл проекта и фиксированный набор параметров. Профиль можно хранить в Properties/PublishProfiles или описать в pipeline.

dotnet publish ./MyApp.csproj -c Release -r linux-x64 --self-contained false -p:Version=2026.09.06 -o ./artifacts/MyApp-2026.09.06

Для каждого релиза создавайте отдельную папку с номером версии или идентификатором коммита. Не перезаписывайте единственный каталог на сервере до завершения проверки. Такой порядок упрощает сравнение файлов и откат.

  • Фиксируйте версию SDK через global.json, если команда использует общий toolchain.
  • Сохраняйте параметры публикации в одном профиле.
  • Добавляйте номер версии в имя артефакта.
  • Проверяйте результат автоматическим smoke-тестом.
  • Передавайте в production тот же тип артефакта, который прошел тестовый запуск.

Связь коммита, сборки, контейнера и деплоя подробно разобрана в руководстве по DevOps-процессу от коммита до продакшена.

Версии .NET: как избежать несовместимости runtime после деплоя

Ошибка несовместимого runtime возникает, когда приложение собрано под Target Framework, которого нет в окружении выполнения, либо когда сервер использует другую архитектуру. Версия SDK, которой выполнена публикация, не равна версии runtime, которая запускает приложение.

SDK, runtime и Target Framework: в чем разница

  • .NET SDK содержит компилятор, команды dotnet build и dotnet publish, шаблоны и инструменты разработки.
  • .NET runtime нужен для выполнения собранного приложения.
  • ASP.NET Core runtime содержит компоненты для веб-приложений и API.
  • Target Framework в проекте указывает, под какую платформу приложение собирается, например net8.0.
  • Hosting Bundle для Windows добавляет компоненты, которые позволяют IIS запускать ASP.NET Core через ASP.NET Core Module.

Проверка соответствия выглядит так: Target Framework проекта - доступный runtime - архитектура процесса - системная платформа. Если один элемент не совпадает, приложение может завершиться еще до обработки первого запроса.

Что проверить на Windows и Linux перед запуском

На Windows проверьте установленные компоненты через список приложений, PowerShell и журналы установки. Для IIS отдельно убедитесь, что установлен подходящий ASP.NET Core Hosting Bundle. После установки Hosting Bundle перезапустите IIS, чтобы модуль загрузился в рабочие процессы.

dotnet --list-runtimes
dotnet --info

На Linux используйте те же команды, но дополнительно проверьте системные библиотеки, права исполняемого файла и архитектуру:

uname -m
cat /etc/os-release
dotnet --list-runtimes
ldd ./MyApp

Команда ldd помогает найти отсутствующие нативные библиотеки для self-contained-приложения или пакета, который использует системные зависимости. В Docker проверяйте соответствие версий SDK image и ASP.NET runtime image Target Framework проекта.

Framework-dependent или self-contained при несовпадении окружений

Если сервером управляет отдельная команда и runtime обновляют централизованно, framework-dependent-публикация уменьшает размер артефактов и упрощает контроль общих исправлений. Перед каждым релизом фиксируйте список доступных runtime и проверяйте совместимость на тестовом сервере.

Self-contained подходит для изолированного сервера, ограниченного окружения или ситуации, когда нельзя быстро менять установленный runtime. Пакет все равно зависит от ОС, архитектуры и системных библиотек. Публикация под linux-x64 не предназначена для запуска на win-x64 без отдельной сборки.

Переход на self-contained не освобождает от обновления безопасности. Runtime внутри каждого нового артефакта нужно регулярно пересобирать с актуальным SDK и проверенными зависимостями.

Как управлять версиями в production

  • Храните Target Framework в файле проекта и не меняйте его вручную на сервере.
  • Фиксируйте SDK для pipeline через global.json или образ сборки.
  • Записывайте в карточку релиза версию приложения, SDK, runtime, режим публикации и архитектуру.
  • В Docker используйте конкретный тег образа, согласованный с Target Framework. Не полагайтесь на случайное обновление плавающего тега.
  • Тестируйте переход на новую основную версию .NET отдельно от обычного релиза приложения.
  • Сохраняйте предыдущий артефакт и инструкцию возврата.

Конфигурация окружения: параметры, переменные и секреты

Один и тот же артефакт удобно использовать в тестовой и production-среде, если настройки передаются при запуске. Пересборка из-за смены адреса базы данных, порта или уровня логирования создает лишний риск.

Выбор окружения через ASPNETCORE_ENVIRONMENT и DOTNET_ENVIRONMENT

Переменные ASPNETCORE_ENVIRONMENT и DOTNET_ENVIRONMENT задают имя окружения. Чаще всего используют Development, Staging и Production. От выбранного имени зависит загрузка файла appsettings.{Environment}.json и поведение middleware, логирования и страниц ошибок.

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

ASPNETCORE_ENVIRONMENT=Production
DOTNET_ENVIRONMENT=Production

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

appsettings и переменные окружения

Базовые значения обычно хранят в appsettings.json, а параметры production-среды, которые не содержат секретов, можно разместить в appsettings.Production.json. Переменные окружения позволяют переопределить значения без изменения файла публикации.

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

ConnectionStrings__Main=Host=db;Database=app
Logging__LogLevel__Default=Information
ASPNETCORE_URLS=http://127.0.0.1:5000

При чтении конфигурации приложение обычно объединяет JSON-файлы, переменные окружения и аргументы командной строки. Более поздний источник переопределяет ранее загруженное значение. Фактический порядок зависит от host-модели и пользовательских providers, поэтому проверяйте конфигурацию в коде старта и логах.

Как передавать секреты

Пароли баз данных, токены, ключи подписи и сертификаты не должны попадать в git, Docker-образ или открытые логи. Для небольшой установки подойдут переменные окружения с ограниченными правами доступа. В production лучше использовать защищенное хранилище секретов, к которому сервис обращается через отдельную учетную запись.

  • Храните файл переменных окружения вне каталога публикации.
  • Ограничьте права чтения учетной записью процесса.
  • Не выводите значение секретной переменной при диагностике.
  • Маскируйте токены и строки подключения в CI/CD-логах.
  • Меняйте скомпрометированные секреты отдельно от релиза приложения.

Порты, URL и файловые пути

Kestrel должен слушать адрес, доступный выбранной схеме. За Nginx или IIS приложение обычно привязывают к loopback-адресу, например http://127.0.0.1:5000. В контейнере процесс должен слушать интерфейс контейнера, поэтому используют http://+:8080 или переменную ASPNETCORE_HTTP_PORTS=8080.

Относительные пути зависят от рабочей директории процесса. Для systemd задайте WorkingDirectory, для Docker используйте WORKDIR, а в IIS проверьте физический путь сайта. Каталоги с данными должны иметь права записи, каталог с исполняемыми файлами можно сделать доступным только для чтения после выкладки.

Деплой .NET-приложения на Windows через IIS

IIS принимает внешние HTTP-запросы, применяет bindings и передает их ASP.NET Core Module. Пул приложений контролирует процесс и его перезапуск. Само приложение выполняется через Kestrel внутри выбранной hosting model.

Что требуется установить на Windows Server

  • Роль IIS и компоненты, необходимые для приема HTTP-запросов.
  • Подходящий ASP.NET Core Hosting Bundle.
  • Runtime и Hosting Bundle той же основной версии, под которую опубликовано приложение.
  • Совместимую архитектуру процесса, обычно x64 для x64-сервера и x64-публикации.
  • Учетную запись или группу, которой IIS сможет читать каталог приложения.

После установки проверьте список runtime и перезапустите службы IIS. Если сайт создан раньше установки Hosting Bundle, старый рабочий процесс может не увидеть новый модуль до перезапуска.

Размещение опубликованного приложения в IIS

  1. Создайте отдельный каталог для конкретного приложения, например C:\Sites\MyApp.
  2. Скопируйте в него содержимое папки публикации, сохранив структуру файлов.
  3. Создайте сайт IIS и укажите физический путь к каталогу.
  4. Настройте binding: протокол, IP-адрес, порт и имя хоста.
  5. Создайте отдельный пул приложений с параметром .NET CLR Version: No Managed Code.
  6. Выберите подходящую учетную запись пула и выдайте ей права чтения и исполнения.
  7. Проверьте наличие сгенерированного web.config.

Для framework-dependent-публикации в web.config обычно указывается запуск DLL через dotnet. Для self-contained-публикации указывается исполняемый файл приложения. Не копируйте в production случайный web.config из другого проекта: имя DLL, hosting model и путь к stdout-логу должны соответствовать текущему артефакту.

Права на каталог с приложением выдавайте узко. Учетной записи пула нужен доступ на чтение и исполнение. Запись разрешайте только каталогам, где приложение действительно создает файлы, например каталогу загрузок или временных данных.

Практические вопросы установки и автозапуска Windows-приложений разобраны в руководстве по развертыванию Windows-приложения через MSI и EXE. Для ASP.NET Core дополнительно учитывайте Hosting Bundle, ASP.NET Core Module и настройки пула IIS.

Конфигурация окружения и перезапуск приложения

Переменные окружения для IIS можно задать в web.config, через настройки сайта или на уровне конфигурации сервера. Пример фрагмента с переменной окружения:

<aspNetCore processPath="dotnet" arguments=".\MyApp.dll" hostingModel="inprocess">
  <environmentVariables>
    <environmentVariable name="ASPNETCORE_ENVIRONMENT" value="Production" />
    <environmentVariable name="ASPNETCORE_URLS" value="http://127.0.0.1:5000" />
  </environmentVariables>
</aspNetCore>

При изменении web.config IIS обычно перезапускает приложение. При изменении файлов конфигурации или переменных на уровне IIS перезапустите сайт либо пул приложений вручную и проверьте, что новый процесс загрузил значения.

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

Диагностика ошибок запуска в IIS

СимптомЧастая причинаЧто проверить
HTTP 500.30Процесс ASP.NET Core не стартовалEvent Viewer, stdout-лог на короткое время, runtime, переменные окружения, строку запуска
HTTP 500.31Не найден подходящий runtimeHosting Bundle, список runtime, Target Framework и архитектуру
HTTP 500.19Ошибка конфигурации IIS или web.configСинтаксис файла, установленные модули и права IIS
403 или ошибка доступа к файлуНеверные права каталогаУчетную запись пула, ACL и доступ к каждому родительскому каталогу

Временно включайте stdoutLogEnabled только для диагностики и заранее создайте каталог для логов с правами записи. После получения ошибки отключите stdout-логирование: этот журнал быстро растет и может содержать чувствительные сведения.

Системные события Windows помогают отличить ошибку приложения от сбоя ASP.NET Core Module. Журналы IIS показывают HTTP-статус, время и запрос, но не всегда содержат исключение из кода приложения.

Запуск ASP.NET Core приложения на Linux через systemd

На Linux systemd запускает Kestrel как системный сервис, следит за процессом и поднимает его после перезагрузки. Приложение может использовать установленный runtime или self-contained-файл.

Подготовка пользователя и каталогов приложения

Не запускайте веб-приложение от root без отдельной причины. Создайте системного пользователя с минимальными правами:

sudo useradd --system --home /opt/myapp --shell /usr/sbin/nologin myapp
sudo mkdir -p /opt/myapp/releases /opt/myapp/current /var/lib/myapp
sudo chown -R myapp:myapp /opt/myapp /var/lib/myapp

Храните релизы в отдельных каталогах, например /opt/myapp/releases/2026.09.06, а на активную версию указывайте через каталог current или симлинк. Файлы публикации можно оставить доступными для чтения пользователю myapp, а запись разрешить только каталогам данных.

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

Параметры systemd unit-файла

Создайте unit-файл, например /etc/systemd/system/myapp.service:

[Unit]
Description=MyApp ASP.NET Core service
After=network-online.target
Wants=network-online.target

[Service]
WorkingDirectory=/opt/myapp/current
ExecStart=/usr/bin/dotnet /opt/myapp/current/MyApp.dll
User=myapp
Group=myapp
Environment=ASPNETCORE_ENVIRONMENT=Production
Environment=ASPNETCORE_URLS=http://127.0.0.1:5000
EnvironmentFile=-/etc/myapp/myapp.env
Restart=on-failure
RestartSec=5

[Install]
WantedBy=multi-user.target

Для self-contained-публикации замените команду запуска на путь к исполняемому файлу:

ExecStart=/opt/myapp/current/MyApp

Файл с дополнительными переменными можно хранить в /etc/myapp/myapp.env. Ограничьте его права:

sudo chown root:myapp /etc/myapp/myapp.env
sudo chmod 640 /etc/myapp/myapp.env

Не помещайте секреты прямо в unit-файл, если доступ к конфигурации systemd получают лишние учетные записи. Значение WorkingDirectory должно указывать на каталог, где приложение ожидает относительные пути.

Автозапуск, перезапуск и обновление версии

sudo systemctl daemon-reload
sudo systemctl enable --now myapp.service
sudo systemctl status myapp.service --no-pager

При обновлении сначала подготовьте новый релиз, проверьте его локальный запуск, затем переключите current на новый каталог и перезапустите сервис.

sudo ln -sfn /opt/myapp/releases/2026.09.06 /opt/myapp/current
sudo systemctl restart myapp.service
sudo systemctl is-active myapp.service

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

Когда нужен Nginx перед Kestrel

Kestrel может принимать HTTP-запросы напрямую, но reverse proxy нужен для доменных имен, TLS, ограничения доступа, маршрутизации и централизованной обработки заголовков. В типовой схеме Nginx слушает внешний порт, а Kestrel работает на loopback-адресе, например 127.0.0.1:5000.

  • Nginx принимает соединение клиента и завершает TLS.
  • Nginx передает запрос Kestrel через локальный порт.
  • systemd запускает и перезапускает процесс Kestrel.
  • Приложение отвечает за маршруты, авторизацию, бизнес-логику и health endpoint.

При такой схеме проверьте передачу Host, X-Forwarded-For и X-Forwarded-Proto, а в приложении настройте доверие к reverse proxy. Практические примеры связки Nginx, Docker и Linux-сервисов собраны в руководстве по DevOps и Linux-администрированию.

Диагностика запуска через journalctl

sudo systemctl status myapp.service --no-pager
sudo journalctl -u myapp.service -b -n 100 --no-pager
sudo journalctl -u myapp.service -f

Если сервис перезапускается по кругу, найдите первую ошибку, а не последнюю запись о повторном запуске. Проверьте рабочую директорию, путь в ExecStart, права пользователя, наличие runtime, переменные окружения и доступность внешних сервисов.

Для ручного сравнения запуска от имени сервиса используйте:

sudo -u myapp /usr/bin/dotnet /opt/myapp/current/MyApp.dll

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

Контейнерный деплой .NET-приложения через Docker

Docker помещает приложение и runtime в образ. Серверу нужен Docker Engine или совместимая контейнерная среда, а версия SDK и ASP.NET runtime фиксируется в Dockerfile. Это уменьшает различия между сборочным агентом, тестовым стендом и production-хостом.

Как выбрать базовые образы SDK и ASP.NET runtime

Для проекта с Target Framework net8.0 используйте SDK image и ASP.NET runtime image ветки 8.0. Для net9.0 выбирайте соответствующую ветку. Смешивание основных версий приводит к ошибкам восстановления, публикации или запуска.

  • SDK image нужен на этапе восстановления, сборки и публикации.
  • ASP.NET runtime image нужен для запуска веб-приложения.
  • Архитектура образа должна соответствовать хосту или поддерживаться его эмуляцией.
  • Теги образов нужно контролировать и обновлять через плановый процесс.
  • После обновления базового образа выполняйте полную сборку и smoke-тест.

Multi-stage build и размер итогового образа

Multi-stage Dockerfile отделяет инструменты сборки от production-среды. В итоговый образ копируется папка публикации, а SDK остается в промежуточном слое.

FROM mcr.microsoft.com/dotnet/sdk:8.0 AS build
WORKDIR /src
COPY ["MyApp.csproj", "."]
RUN dotnet restore "MyApp.csproj"
COPY . .
RUN dotnet publish "MyApp.csproj" -c Release -o /app/publish --no-restore

FROM mcr.microsoft.com/dotnet/aspnet:8.0 AS final
WORKDIR /app
COPY --from=build /app/publish .
ENV ASPNETCORE_URLS=http://+:8080
EXPOSE 8080
ENTRYPOINT ["dotnet", "MyApp.dll"]

Соберите образ с версией, а не с единственным тегом latest:

docker build -t myapp:2026.09.06 .
docker run -d --name myapp-20260906 -p 8080:8080 -e ASPNETCORE_ENVIRONMENT=Production myapp:2026.09.06

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

Переменные окружения, порты и volumes

Образ хранит код и зависимости. Конфигурация окружения передается при запуске:

docker run -d \
  --name myapp \
  -p 8080:8080 \
  -e ASPNETCORE_ENVIRONMENT=Production \
  -e ConnectionStrings__Main='Host=db;Database=app' \
  myapp:2026.09.06

Порт в EXPOSE документирует порт контейнера, но сам по себе не публикует его наружу. Для доступа с хоста нужен параметр -p 8080:8080. Если контейнер находится за reverse proxy, внешний порт может отличаться от внутреннего.

Volumes используйте для данных, которые должны переживать пересоздание контейнера: загруженных файлов, локальной базы, сертификатов и кэша с требуемым сроком хранения. Файлы приложения и временные данные без необходимости не превращайте в постоянное хранилище.

Проверка контейнера после запуска

docker ps
docker inspect myapp
docker logs --tail 100 myapp
docker exec myapp printenv | grep ASPNETCORE

Проверьте HTTP-порт с хоста и отдельно убедитесь, что приложение отвечает внутри контейнера. Healthcheck должен обращаться к endpoint, который проверяет готовность сервиса. Если в runtime-образе нет curl или wget, добавьте подходящий инструмент в образ либо выполняйте проверку внешним мониторингом.

HEALTHCHECK --interval=30s --timeout=5s CMD curl --fail http://localhost:8080/health || exit 1

Такой пример работает только при наличии curl внутри образа. Health endpoint не должен раскрывать секреты и внутренние сведения. Он может возвращать простой статус готовности и проверять обязательные зависимости, если это предусмотрено архитектурой приложения.

Сохраняйте образы с тегами релизов и идентификаторами коммитов. Удалять предыдущий образ можно после завершения smoke-теста и подтверждения отката.

Типовые ошибки после публикации и запуска: алгоритм диагностики

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

Приложение не стартует из-за отсутствующего runtime

Проверьте Target Framework в .csproj и список runtime на сервере:

dotnet --list-runtimes
dotnet --info

Для framework-dependent-публикации нужен совместимый runtime и правильная архитектура. В IIS дополнительно проверьте Hosting Bundle. В Docker сравните версии SDK image, runtime image и проекта. Если runtime нельзя установить или обновить, соберите self-contained-пакет под нужную ОС и архитектуру.

Сообщение о невозможности найти framework часто содержит имя требуемой версии. Сверяйте его с фактически установленными компонентами, а не с версией SDK на компьютере разработчика.

Приложение завершается сразу после запуска

Запустите приложение вручную в том же каталоге и от имени той же учетной записи, которую использует сервис:

cd /opt/myapp/current
dotnet MyApp.dll

Частые причины:

  • Отсутствует обязательная переменная окружения.
  • Строка подключения указывает на недоступный сервер.
  • Рабочая директория не содержит нужный JSON-файл или статический ресурс.
  • Процесс не может прочитать сертификат или записать временный файл.
  • Изменился формат конфигурации, а код старта не обработал новое значение.
  • Порт занят другим процессом.

Сначала прочитайте исключение в stdout, journalctl, Event Viewer или docker logs. Исправление конфигурации без просмотра первой ошибки часто приводит к цепочке новых симптомов.

Порт занят или приложение недоступно по сети

Успешный запуск процесса не гарантирует доступность сервиса. Проверьте адрес прослушивания и конфликт портов.

ss -ltnp | grep 5000
sudo lsof -i :5000

В Windows можно проверить порт командой:

Get-NetTCPConnection -LocalPort 5000
  • Для IIS проверьте bindings сайта и конфликт портов с другим сайтом.
  • Для systemd проверьте ASPNETCORE_URLS, firewall и адрес Kestrel.
  • Для Docker проверьте связку внутреннего и внешнего портов в -p.
  • Для Nginx проверьте upstream, локальный порт и передачу запроса.
  • Проверьте доступ с самого сервера через loopback, затем с отдельного клиента.

Если локальный запрос проходит, а внешний нет, ищите проблему в firewall, reverse proxy, bindings или сетевой политике. Если не проходит даже локальный запрос, проверяйте само приложение и адрес прослушивания.

Ошибки прав доступа и файловой системы

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

namei -l /opt/myapp/current/MyApp.dll
ls -la /opt/myapp/current
ls -la /var/lib/myapp

Пользователь systemd должен читать файлы публикации и писать только в предусмотренные каталоги. В Windows проверьте ACL для учетной записи пула IIS. Для Docker проверьте владельца подключенного volume внутри контейнера, UID процесса и режим монтирования.

Разделяйте read-only-файлы приложения и каталоги с данными. Такой порядок уменьшает последствия ошибки в коде и помогает понять, какие права действительно нужны сервису.

Минимальный чек-лист сбора диагностики

  • Имя приложения и версия артефакта.
  • Target Framework и режим публикации.
  • ОС, архитектура и версия runtime.
  • Команда запуска, unit-файл, настройки IIS или тег Docker-образа.
  • Имя окружения без раскрытия секретов.
  • Адрес и порт прослушивания.
  • Код завершения процесса или HTTP-статус.
  • Фрагмент релевантного журнала с временем события.
  • Результат проверки прав доступа и доступности внешних зависимостей.

Секреты, токены и полные строки подключения удаляйте из диагностического набора. Для передачи задачи коллеге достаточно имени переменной и признака, что она задана.

Проверка результата и безопасное обновление приложения

Деплой считается завершенным после проверки HTTP-сценария, логов, версии и поведения сервиса после перезапуска. Статус процесса без запроса к приложению не подтверждает работоспособность API или сайта.

Smoke-тест после развертывания

Минимальный smoke-тест должен включать проверку health endpoint, основного маршрута и обязательной зависимости, например базы данных.

curl -i http://127.0.0.1:5000/health
curl -i http://127.0.0.1:5000/api/status

Для IIS обращайтесь к имени хоста и порту binding. Для Docker проверяйте опубликованный порт:

curl -i http://127.0.0.1:8080/health

Ожидайте понятный HTTP-статус, корректный формат ответа и отсутствие ошибок в журнале. Health endpoint должен возвращать ошибку, если сервис не готов принимать рабочие запросы, но не должен раскрывать внутреннюю конфигурацию.

Логи, мониторинг и контроль версии

  • Добавьте в стартовый лог номер версии и окружение без секретных значений.
  • Сохраняйте корреляционный идентификатор запроса, если приложение работает за reverse proxy.
  • Контролируйте статус systemd-сервиса, пула IIS или контейнера.
  • Настройте оповещение по росту HTTP 5xx, остановке процесса и неуспешному healthcheck.
  • Фиксируйте изменения конфигурации рядом с релизом.

Логи должны отвечать на три вопроса: какая версия запущена, с какими параметрами она стартовала и почему конкретный запрос завершился ошибкой. Секретные значения в эти записи не включайте.

Откат неудачного деплоя

Храните предыдущую папку публикации или Docker-образ до завершения проверки нового релиза. Для systemd переключите симлинк на старый каталог, выполните перезапуск и повторите smoke-тест. Для IIS верните прежний каталог в качестве физического пути или восстановите предыдущий набор файлов. Для Docker запустите контейнер из предыдущего тега после остановки проблемной версии.

sudo ln -sfn /opt/myapp/releases/2026.09.05 /opt/myapp/current
sudo systemctl restart myapp.service
sudo systemctl status myapp.service --no-pager

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

Итоговая таблица выбора варианта деплоя

КритерийIISsystemd и KestrelDocker
ОСWindows ServerLinuxЛюбая поддерживаемая контейнерная среда
RuntimeHosting Bundle или self-containedRuntime на сервере или self-containedФиксирован в runtime image
ИзоляцияУровень IIS и пулаУровень пользователя и системных правУровень контейнера, сети и volumes
АвтозапускПул приложений IISsystemctl enableПолитика Docker или оркестратора
ЛогированиеIIS, Event Viewer, stdoutjournald и journalctldocker logs и мониторинг контейнеров
ОбновлениеЗамена каталога или физического путиНовый релиз и переключение симлинкаНовый версионированный образ
СложностьНизкая при готовой Windows-инфраструктуреСредняя, требуется настройка Linux-сервисаСредняя или высокая при registry и оркестрации
Типовой выборКорпоративные Windows-серверыНативные Linux-сервисы и APIПовторяемые сборки, CI/CD и микросервисы

Для классического Windows-окружения выбирайте IIS, для Linux-сервера с прямым контролем процесса, systemd. Docker подходит, когда нужно зафиксировать runtime и зависимости внутри поставки. В каждом варианте разделяйте четыре операции: публикацию, выкладку, запуск и проверку. Версию артефакта и runtime записывайте в журнал релиза, а предыдущую версию сохраняйте до завершения smoke-теста.

Если команда только выстраивает рабочий DevOps-процесс, начните с единого профиля публикации, версионирования артефактов и автоматической проверки HTTP-ответа. Затем добавляйте CI/CD, контейнеризацию и автоматический откат по мере роста требований к скорости и повторяемости.

Поделиться:
Сохранить гайд? В закладки браузера