.NET Core i Visual Studio Team Services – sposób na Azure WebJob

.NET Core i Visual Studio Team Services – sposób na Azure WebJob

Mateusz Pobudejski 1 sierpnia 2017

W tym artykule postaram się przybliżyć mechanizm budowania i wdrażania usługi Azure WebJob, bez wsparcia narzędzi wbudowanych w Visual Studio.

Azure WebJobs pozwalają na łatwe uruchamianie skryptów lub programów jako procesów w tle w ramach naszej WebAppki. Mogą one działać w sposób ciągły, na żądanie, albo zgodnie z harmonogramem (cron). Wystarczy wgrać, np. za pośrednictwem portalu odpowiedni plik w jednym ze wspieranych formatów:

  • .cmd, .bat, .exe (windows cmd)
  • .ps1 (powershell)
  • .sh (bash)
  • .php (php)
  • .py (python)
  • .js (node)
  • .jar (java)

Microsoft udostępnia również odpowiednie SDK, które znacznie ułatwia integrację z usługami Azure Storage takimi jak Storage Blobs, Queues, Tables i Service Bus Queues.

Brzmi dobrze.

Niestety na dzień, w którym powstaje ten artykuł, WebJobs SDK nie wspiera .NET Core.

Nic straconego, bo być może w ogóle nie potrzebujemy integracji z Azure Storage? Sprawdźmy zatem jak wygląda wsparcie narzędziowe. Przeglądamy dalszą część dokumentacji:

Tworzenie, wdrażanie i zarządzanie WebJobs jest łatwe dzięki zintegrowanemu wsparciu w Visual Studio. Można je tworzyć z szablonów, publikować i zarządzać nimi (uruchamiać / zatrzymywać / monitorować / debugować).

Świetnie... ale znowu nie dla .NET Core!

O ile sama konieczność targetowania projektu na pełny framework nie jest dla mnie wielkim problemem (w końcu i tak uruchamiamy nasze zadania na systemie Windows z .NET Framework 4.7), to brak wsparcia dla nowego, wygodnego formatu .csproj była już dla mnie wystarczającą motywacją. Postanowiłem spróbować wdrożyć jako WebJob konsolową aplikację napisaną w .NET Core, z wykorzystaniem VSTS jako CI/CD.

Przy tradycyjnym projekcie (klasyczny .csproj), cała konfiguracja WebJoba, łączne z procesem budowania i wdrożenia w VSTS dzieje się prawie automatycznie i nie wymaga od nas wchodzenia w szczegóły tego co dzieje się „pod spodem”.

Jeśli jednak przyjrzymy się, jakie tak naprawdę są wymagania, aby Azure wiedział, że coś jest WebJobem, to okazuje się, że nie jest to wcale takie trudne do odtworzenia ręcznie.

Artefakt wyprodukowany przez proces budowania powinien być plikiem zip, w którym:

  • w katalogu \App_Data\jobs\continuous\<nazwa_joba> (jeśli zadanie uruchamiane w trybie ciągłym)
  • lub \App_Data\jobs\triggered\<nazwa_joba> (jeśli cron)
  • musi znajdować się plik run.cmd (lub inne wspierane rozszerzenie)
  • i opcjonalnie plik schedule.json

Na podstawie ścieżki oraz obecności pliku run.cmd, Azure sam wywnioskuje:

  1. że ma do czynienia z WebJobem;
  2. kiedy i jak go uruchomić.

Tworzymy aplikację

Spróbujmy wdrożyć przykładową aplikację. Zaczynamy od zbudowania prostej aplikacji konsolowej.

Uwaga! Nasz projekt musi znajdować się w podkatalogu. Jeśli plik *.csproj będzie w katalogu głównym naszego repozytorium, VSTS niepoprawnie przekaże ścieżkę komendzie publish (doklei suffix „/s” zamiast nazwy projektu). Jeśli tworzymy solucję w Visual Studio, zamiast linii poleceń – nie mamy się czym martwić.

C:\demo> dotnet new console -n WebJobCore
Getting ready...
Content generation time: 74.7661 ms
The template "Console Application" created successfully.
C:\demo> cd .\WebJobCore\
C:\demo\WebJobCore> dotnet restore
  Restoring packages for C:\demo\WebJobCore\WebJobCore.csproj...
  Generating MSBuild file C:\demo\WebJobCore\obj\WebJobCore.csproj.nuget.g.props.
  Generating MSBuild file C:\demo\WebJobCore\obj\WebJobCore.csproj.nuget.g.targets.
  Writing lock file to disk. Path: C:\demo\WebJobCore\obj\project.assets.json
  Restore completed in 856.44 ms for C:\demo\WebJobCore\WebJobCore.csproj.

  NuGet Config files used:
      C:\Users\MateuszPobudejski\AppData\Roaming\NuGet\NuGet.Config
      C:\Program Files (x86)\NuGet\Config\Microsoft.VisualStudio.Offline.config

  Feeds used:
      https://api.nuget.org/v3/index.json
      C:\Program Files (x86)\Microsoft SDKs\NuGetPackages\
C:\demo\WebJobCore> dotnet run
Hello World!

Działa! :)

Aby aplikacja mogła być odpalona jako WebJob, w katalogu musi znajdować się plik run.cmd. Wynikiem kompilacji jest plik DLL, więc plik run.cmd powinien mieć następującą postać:

@echo off
dotnet WebJobCore.dll

UWAGA! Należy się upewnić, że plik nie zawiera symbolu UTF-8 BOM! Może to powodować trudne do zdiagnozowania błędy przy uruchomieniu (więcej tutaj).

Musimy również zadbać, żeby komenda publish uwzględniła go przy budowaniu paczki. Robimy to, modyfikując plik .csproj w następujący sposób:

<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>netcoreapp1.1</TargetFramework>
  </PropertyGroup>
  <ItemGroup>
    <Content Include="run.cmd" CopyToPublishDirectory="PreserveNewest" />
  </ItemGroup>
</Project>

 Jeżeli w naszym projekcie planujemy używać appsettings.json, należy dodać również:

<Content Include="appsettings.json" CopyToPublishDirectory="PreserveNewest">
  <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>

Szybki test:

C:\demo\WebJobCore> dotnet publish .\WebJobCore.csproj
C:\demo\WebJobCore> cd .\bin\Debug\netcoreapp1.1\publish\
C:\demo\WebJobCore\bin\Debug\netcoreapp1.1\publish> .\run.cmd
Hello World!

Działa!

Teraz git commit & push i przenosimy się do Visual Studio Team Services.

Utworzenie definicji build

 

 Tworzymy nową definicję i wybieramy standardowy szablon dla .NET Core

Poza standardowymi ustawieniami agenta i repozytorium, pierwszym krokiem, który wymaga od nas uwagi jest Publish:

Istotne jest aby ustawić odpowiednią ścieżkę dla plików wynikowych i wyłączyć domyślny mechanizm pakowania.

Następnie dodajemy krok Archive files. Zmieniamy źródło z Build.BinariesDirectory na Build.ArtifactStagingDirectory (definicję zmiennych znajdziemy tutaj) oraz ustawiamy własną nazwę archiwum. Ważne jest także aby odznaczyć opcję prefiksowania ścieżki w archiwum – w innym przypadku paczka nie będzie zgadzała się z wymaganym formatem.

Zapisujemy i uruchamiamy proces budowania. Jeśli wszystko poszło dobrze, produktem powinien być artefakt w postaci:

(w pliku WebJobCore.zip powinien znajdować się spakowany folder App_Data)

Przygotowanie środowiska

Mamy już gotową paczkę, teraz tworzymy w portalu Azure naszą aplikację:

 

Definicja procesu instalacji

Konfigurujemy standardowy proces deploymentu:

1) Wybieramy template:

 

 

2) Dodajemy i konfigurujemy zadanie Azure App Service Deploy:

 

3) I uruchamiamy release:

Jeśli wszystko poszło zgodnie z planem i proces zakończył się sukcesem to wchodzimy do Kudu (przez portal lub bezpośrednio pod adresem https://<AppName>.scm.azurewebsites.net/) a następnie przechodzimy na zakładkę Tools->WebJobs dashboard, gdzie możemy zweryfikować działanie naszej aplikacji.

Nasz Job będzie przez większość czasu znajdował się w stanie Pending restart, ponieważ po wypisaniu tekstu na konsole od razu się wyłącza. Jeżeli ma być uruchomiony cały czas, należy zaimplementować mechanizm blokujący, który obserwuje czy istnieje plik kontrolny określony przez zmienną środowiskową

WEBJOBS_SHUTDOWN_FILE (więcej informacji na temat np. tutaj)

Klikając w nazwę naszego WebJoba, przechodzimy do ekranu szczegółów, w którym można zweryfikować działanie:

 

 

I gotowe!

Podsumowanie

Jak widać, cały proces nie różni się znacznie od wdrożenia zwykłej aplikacji .NET Core jako Azure WebApp. Wystarczy znać strukturę katalogu, w którym powinny znaleźć się pliki i pamiętać o kilku trikach, niezbędnych aby przejść cały proces. A zyskujemy wiedzę o tym jak wdrożyć Azure WebJob, niezależnie od wykorzystywanych frameworków, IDE oraz narzędzi CI/CD.

 

Mateusz Pobudejski, FinAi.com

Warszawa, 1 sierpnia 2017 roku

Location icon Facebook icon Twitter icon Google+ icon LinkedIn icon Technology icon Business icon Marketing icon Phone icon Mail icon User icon Tag icon Bubble icon Arrow right icon Arrow left icon Calendar PR Contact