В процессе разработки приложений мы часто сталкиваемся с необходимостью опубликовать приложение — чтобы им могли пользоваться другие люди, чтобы оно было постоянно запущенным и не зависело, например, от состояния ПК разработчика.
В этой статье мы рассмотрим способ публикации веб-приложения на виртуальный выделенный сервер с помощью контейнеров Docker.
cloud
Контейнеризация и Docker
Что такое контейнеризация?
Контейнеризация — это метод, применяемый в разработке программ, при котором сервисы вместе со своими зависимостями «упаковываются» в специальные контейнеры. Далее эти контейнеры можно развернуть на сервере или, например, локально — и быть точно уверенным в том, что мы тестируем и разворачиваем одну и ту же версию приложения.
Контейнеры также изолируют приложения друг от друга в общей операционной системе, при этом, в сравнении с виртуальными машинами, они гораздо менее требовательны к ресурсам.
Docker
Docker — это проект с открытым исходным кодом, который позволяет автоматизировать деплой ПО в виде специальных контейнеров. Эти контейнеры могут выполняться в разных средах — как на Linux, так на Windows или Mac. В целом, контейнеры Docker можно сравнить с виртуальными машинами, однако контейнеры используют ядро операционной системы хоста (сервера, где мы разворачиваем приложение) совместно. Виртуальные машины используют собственное ядро ОС, поэтому они требуют больше ресурсов.
Основная цель создания образов — привести среду вместе с зависимостями вашего приложения к единообразию. Это означает, что вы можете отладить образ на одном компьютере, а затем развернуть его на другом компьютере и получить ту же среду.
Основные термины Docker:
- Образы (Image) — некоторый пакет, который содержит все зависимости и сведения, необходимые для создания контейнера.
 
- Докерфайл (Dockerfile) — специальный текстовый файл, который содержит некоторые инструкции для сборки образа Docker.
 
- Контейнер (Container) — экземпляр конкретного образа Docker, созданного на основе Dockerfile. Контейнер отвечает за выполнение приложения и является основной единицей развёртывания — именно контейнеры мы запускаем на сервере или локально.
 
- Docker Registry — некоторое хранилище образов docker. Очень похоже на github, только вместо кода тут образы контейнеров.
 
У Docker есть отличная документация, и для большего погружения рекомендуем с ней ознакомиться.
Пишем приложение
Мы будем разворачивать шаблонный web API на ASP.NET Core. Для этого добавим новую папку и в терминале выполним команду:
dotnet new webapi
Теперь у нас есть шаблонное приложение. Его можно запустить, выполнив команду:
dotnet run
Перейдя на http://localhost:5167/swagger/index.html (порт может отличаться), мы увидим сгенерированную документацию Swagger к нашему API и сможем попробовать выполнить запрос:
Упаковываем приложение в Docker
Итак, приложение работает, теперь его надо добавить в Docker и развернуть на сервере. Для начала добавим файл Dockerfile, чтобы описать, из чего будет состоять наш контейнер:
# укажем, на основе какого образа будем 
# делать наш контейнер. Для сборки приложения используем dotnet-sdk 
# и назовём этот образ builder
FROM mcr.microsoft.com/dotnet/sdk:7.0 AS builder 
# укажем директорию для нашего приложения внутри контейнера
WORKDIR /Application
# Скопируем все файлы из проекта в файловую систему контейнера
COPY . ./
# Запустим restore для загрузки зависимостей
RUN dotnet restore
# Опубликуем собранный dll в папку "output"
RUN dotnet publish -c Release -o output
# Теперь соберём образ, в котором наше приложение 
# будет запущено. Для запуска приложения достаточно
# среды выполнения aspnet, без sdk
FROM mcr.microsoft.com/dotnet/aspnet:7.0
WORKDIR /Application
# Скопируем файлы приложения из предыдущего образа 
COPY --from=builder /Application/output .
# укажем команду, которая будет запускать приложение
ENTRYPOINT ["dotnet", "dotnet-app.dll"]
Запустим команду docker build -t dotnet-app ., чтобы собрать наше приложение:
С помощью параметра -t dotnet-app мы даём название образу, а с помощью параметра . (точка в конце — текущая директория) указываем контекст, в котором docker будет искать Dockerfile и собирать его.
Используя команду docker images, мы можем посмотреть список существующих образов и информацию о них:
Теперь мы можем запустить контейнер с помощью команды:
docker run -p 5000:80 dotnet-app
С помощью параметра -p 5000:80 мы говорим докеру, чтобы порт 80 из контейнера был открыт на 5000 порту нашей ОС. 
По адресу http://localhost:5000/WeatherForecast мы можем использовать наше приложение.
В стандартном шаблоне приложения ASP.NET Swagger доступен, только если установлена переменная среды Development. Эта логика описана в файле program.cs:
// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
    app.UseSwagger();
    app.UseSwaggerUI();
}
По умолчанию контейнер разворачивается в среде Production. Мы можем убрать условие if (app.Environment.IsDevelopment()) или добавить переменную окружения в Dockerfile:
ENV ASPNETCORE_ENVIRONMENT=Development
...
Теперь надо пересобрать приложение:
docker build -t dotnet-app .
И снова его запустить:
docker run -p 5000:80 dotnet-app 
Теперь нам доступен Swagger:
Публикация в docker registry
Итак, у нас есть образ контейнера, и мы хотим его использовать для развёртывания на сервере. Для этого нам необходимо этот образ как-то отправить на сервер. Для этих целей подойдёт docker registry. Можно развернуть свой, а можно использовать https://hub.docker.com.
Если Docker Hub недоступен, можно использовать наш бесплатный прокси, который возобновляет этот доступ.
После прохождения авторизации необходимо нажать на кнопку «Create Repository», дать название нашему репозиторию и сохранить его:
Теперь вернёмся в терминал и выполним команду:
docker login
Чтобы отправить образ контейнера в репозиторий, сначала соберём образ, указав в названии ваш логин:
docker build -t [ваш логин]/dotnet-app .
А затем выполним команду:
docker push [ваш логин]/dotnet-app:latest
Разворачиваем приложение на сервере
Для начала арендуем выделенный сервер. Я использую Timeweb Cloud.
Для приложения вполне хватит минимальной конфигурации. Готовый сервер с Docker можно выбрать из маркетплейса, а если будете ставить Docker сами — есть инструкция.
С помощью SSH можно авторизоваться на вашем сервере. В консоли выполним команду docker login, чтобы авторизоваться в registry, а затем выполним команду:
docker pull [ваш логин]/dotnet-app
И запустим наше приложение:
docker run -p 5000:80 [ваш логин]/dotnet-app -d
Параметр -d используется для запуска контейнера в фоновом режиме.
Теперь мы можем использовать наш API по IP-адресу сервера, где мы его развернули:
Подготовили для вас выгодные тарифы на облачные серверы
Заключение
Мы научились упаковывать приложение ASP.NET Core в Docker, публиковать его в docker registry и запускать на VPS. Для более полного погружения советую вам почитать документацию Microsoft: Образы Docker для ASP.NET Core | Microsoft Learn
До скорых встреч!
