Как стать автором
Обновить

От dotnet restore до publish, и что это значит для докера и построения CI/CD-пайплайна

Время на прочтение4 мин
Количество просмотров3.9K

Всем привет! Когда я узнаю, что человек передо мной начинает изучать c# - первым делом я его спрашиваю, как ему язык, на чем раньше программировал и прочее. И в какой то момент разговоры доходят до докера\пайплайнов => многие ребята (которые не пробовали это раньше) начинают нехотя избегать эту тему, считая её чересчур скучной, странной и вообще "это уже какой то девопс". Хотя на деле - зная базово, что значат папки в твоем проекте на компе - можно освоить базовые основы красивой работы докера (+ можно пришить пайплайны, основной мотив у них один). Поэтому сегодня я попытаюсь привлечь ваше внимание к базовым командам dotnet`а.

Все изучать и делать будем сразу в докере. Создаем такой докерфайл:

FROM mcr.microsoft.com/dotnet/sdk:8.0 AS build
COPY . /app

Сдк и готового проекта нам хвтаит. Кстати, а на чем будем "тренироваться"? На простом консольном проекте с 1 нугет пакетом + работает в команде с еще одним проектом (типо для моделек), попробуем воссоздать "реальную" рабочую обстановку.

Выглядит это дело как то так. В первых 2 папках наши проекты.

Наше приложение:

using Extensions;
using Newtonsoft.Json;

namespace ConsoleApp1;

class Program
{
    static void Main(string[] args)
    {
        var model = new Model(){a = 2};
        string json = JsonConvert.SerializeObject(model, Formatting.Indented);
        var deserializedModel = JsonConvert.DeserializeObject<Model>(json);
        Console.WriteLine($"{deserializedModel.a}");
    }
}

Теперь делаем докербилд и запускаем приложение.

Из интересного будет что-то вроде этого. Нужных нугетов, сборок и всего остального у нас нет. Начинаем с самого начала: устанавливаем нужные нугеты. делаем команду в папке Console.App1 для нашего основного приложения - dotnet restore ConsoleApp1.csproj. Получаем скаченный нугет пакет на машине:

Также появились доп файлы к проектам, но нас пока это не интересует.

Далее делаем dotnet build ConsoleApp1.csproj.

Появились нужные длл для нашего проекта! Осталось "опубликовать" наше приложение, поместив в папку, например main, всё что нужно нашему приложению.

Делаем dotnet publish ConsoleApp1.csproj -o /main (флаг -o говорит куда сложить все файлы, делаем это для удобства)

Появилась папка main и нужным нам длл файлик. Перейдем в папку и запустим проект dotnet ConsoleApp1.dll

# dotnet ConsoleApp1.dll
2

Ура! Приложение работает. Думаете как долго и нудно? На самом деле можно использовать только команду dotnet publish ConsoleApp1.csproj -o /main - под капотом будет билд + рестор. Но полную "последовательность" создания нашего основого dll файла нам знать нужно. Теперь научимся красиво писать докерфайлы с новыми знаниями:

FROM mcr.microsoft.com/dotnet/runtime:8.0 AS base
WORKDIR /app

FROM mcr.microsoft.com/dotnet/sdk:8.0 AS build
WORKDIR /src
COPY ConsoleApp1/ConsoleApp1.csproj ConsoleApp1/
RUN dotnet restore "ConsoleApp1/ConsoleApp1.csproj"
COPY . .

WORKDIR /src/ConsoleApp1
RUN dotnet build ConsoleApp1.csproj --no-restore -c Release

FROM build AS publish
RUN dotnet publish ConsoleApp1.csproj --no-build -c Release -o /app/publish

FROM base AS final
WORKDIR /app
COPY --from=publish /app/publish .
ENTRYPOINT ["dotnet", "ConsoleApp1.dll"]

Что же тут происходит? Мы используем несколько этапов сборки aka многоступенчатую сборку, чтобы максимально уменшить итоговый контейнер. Сначала берем ТОЛЬКО рантайм, уже без всего нужного нам сдк. Далее в следующей сборке восстанавливаем зависимости, делаем это ДО копирования всех файлов, что позволит докеру нормально делать кеш слоя с зависимостями, этот закешированный слой будет использоваться, если файл ConsoleApp1.csproj не изменился, что происходит редко. После мы делаем билд с флагом -no--restore, чтобы исключить повторный рестор (это экономит время, позволяя на шаги разбить процесс сборки), далее ставим конфигурацию на релиз и делаем публикацию нашего приложения в папку publish. Возвращаясь к 1 образу, копируем нужную нам итоговую папку publish из предыдущего действия. Запускам наше приложение. Таким образом имеем в контейнере:

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

Теперь перейдем к ci/cd.

Тут сразу переходим к самому простому (и бессмысленному) пайплану:

name: .NET CI/CD Pipeline

on:
  push:
    branches:
      - main
  pull_request:
    branches:
      - main

jobs:
  build:
    name: Build & Test
    runs-on: ubuntu-latest
    
    steps:
      - name: Checkout репозитория
        uses: actions/checkout@v4

      - name: Кэш NuGet пакетов
        uses: actions/cache@v3
        with:
          path: ~/.nuget/packages
          key: nuget-${{ runner.os }}-${{ hashFiles('**/packages.lock.json') }}
          restore-keys: nuget-${{ runner.os }}-

      - name: Установка .NET SDK
        uses: actions/setup-dotnet@v4
        with:
          dotnet-version: '8.0'

      - name: Восстановление зависимостей
        run: dotnet restore ConsoleApp1/ConsoleApp1.csproj

      - name: Сборка проекта
        run: dotnet build ConsoleApp1/ConsoleApp1.csproj --configuration Release --no-restore

      - name: Паблиш проекта
        run: dotnet publish ConsoleApp1/ConsoleApp1.csproj --configuration Release --no-build -o pub

      - name: Сохранение артефактов сборки
        uses: actions/upload-artifact@v4
        with:
          name: ConsoleApp1-artifact
          path: pub

  deploy:
    name: 🚀 Deploy
    needs: build
    runs-on: ubuntu-latest
    if: github.ref == 'refs/heads/main'

    steps:
      - name: 📥 Загрузка артефактов
        uses: actions/download-artifact@v4
        with:
          name: ConsoleApp1-artifact
          path: deploy/

      - name: Просто хочу видеть 2
        run: dotnet /home/runner/work/Rep/Rep/deploy/ConsoleApp1.dll

Получаем:

Более подробно этот файл можно разобрать с гпт. Но моменты, которые вы должны заметить: берем нугет пакеты в кеш (чтобы потом каждый раз не скачивали их), делаем также рестор, билд, паблиш, не забываем флаги. Сохраянем артефакт - просто готовыую папку с нужными файлами, её мы возьмём в другом джобе (логика, как у многоступенчатого докера). И в конце просто запустим приложение (не запутайтесь в папках, послежняя строка - пример, где лежит наш итоговый основной длл). Пайплайном это не назовёшь, но как базово обращаться со сборкой, вы узнали.

Теги:
Хабы:
Всего голосов 11: ↑6 и ↓5+4
Комментарии17

Публикации

Работа

Ближайшие события