Краткий ответ: как выбрать способ развертывания .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 | Логи и управление | Типовой сценарий |
|---|---|---|---|---|---|
| IIS | Windows Server | ASP.NET Core Module и пул приложений | Hosting Bundle или self-contained-пакет | Журналы IIS, Event Viewer, stdout при диагностике | Корпоративные сайты и API в Windows-инфраструктуре |
| systemd | Linux | Kestrel как системный сервис | Runtime на сервере или self-contained | systemctl и journalctl | Нативный запуск API и фоновых сервисов |
| Docker | Linux, 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
- Создайте отдельный каталог для конкретного приложения, например
C:\Sites\MyApp. - Скопируйте в него содержимое папки публикации, сохранив структуру файлов.
- Создайте сайт IIS и укажите физический путь к каталогу.
- Настройте binding: протокол, IP-адрес, порт и имя хоста.
- Создайте отдельный пул приложений с параметром
.NET CLR Version: No Managed Code. - Выберите подходящую учетную запись пула и выдайте ей права чтения и исполнения.
- Проверьте наличие сгенерированного
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 | Не найден подходящий runtime | Hosting 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, конфигурацию и доступность внешних зависимостей. Если причина связана с миграцией базы данных, возврат файлов приложения сам по себе может не восстановить прежнее состояние. План обновления должен учитывать обратную совместимость схемы данных.
Итоговая таблица выбора варианта деплоя
| Критерий | IIS | systemd и Kestrel | Docker |
|---|---|---|---|
| ОС | Windows Server | Linux | Любая поддерживаемая контейнерная среда |
| Runtime | Hosting Bundle или self-contained | Runtime на сервере или self-contained | Фиксирован в runtime image |
| Изоляция | Уровень IIS и пула | Уровень пользователя и системных прав | Уровень контейнера, сети и volumes |
| Автозапуск | Пул приложений IIS | systemctl enable | Политика Docker или оркестратора |
| Логирование | IIS, Event Viewer, stdout | journald и journalctl | docker logs и мониторинг контейнеров |
| Обновление | Замена каталога или физического пути | Новый релиз и переключение симлинка | Новый версионированный образ |
| Сложность | Низкая при готовой Windows-инфраструктуре | Средняя, требуется настройка Linux-сервиса | Средняя или высокая при registry и оркестрации |
| Типовой выбор | Корпоративные Windows-серверы | Нативные Linux-сервисы и API | Повторяемые сборки, CI/CD и микросервисы |
Для классического Windows-окружения выбирайте IIS, для Linux-сервера с прямым контролем процесса, systemd. Docker подходит, когда нужно зафиксировать runtime и зависимости внутри поставки. В каждом варианте разделяйте четыре операции: публикацию, выкладку, запуск и проверку. Версию артефакта и runtime записывайте в журнал релиза, а предыдущую версию сохраняйте до завершения smoke-теста.
Если команда только выстраивает рабочий DevOps-процесс, начните с единого профиля публикации, версионирования артефактов и автоматической проверки HTTP-ответа. Затем добавляйте CI/CD, контейнеризацию и автоматический откат по мере роста требований к скорости и повторяемости.