Časovaná bomba - FDD deployment ASP.NET Core do Azure App Service
Článek se vztahuje k verzi produktu ASP.NET Core 2.1
Tento článek byl napsán v roce 2018. Vývojářské technologie se neustále inovují a článek již nemusí popisovat aktuální stav technologie, ideální řešení a můj současný pohled na dané téma.
Pokud vyvíjíte ASP.NET Core aplikace verze 2.1, používáte ASP.NET Core runtime, publikujete aplikace v režimu FDD (framework-dependent) a hostujete v Azure App Service, gratuluji. Máte namíchaný celkem výbušný koktejl. Stačí jen trochu zatřást.
V prvním odstavci jsem na vás vychrlil plno pojmů, které možná jako vývojáři vůbec neřešíte. Nejprve si tedy přiblížíme výše uvedené pojmy a poté se vrhneme na konkrétní případovku, která za tímto článkem stojí. Pokud se chystáte na pražské DotNET Talks (v.1) (vstup zdarma, posledních asi 15 míst), budu tam přesně tuto problematiku vysvětlovat naživo.
Verze ASP.NET Core
ASP.NET Core dnes existuje v několika verzích. První verze, se kterými se už moc nesetkáte jsou ASP.NET Core 1.0 a 1.1, které běží nad .NET Core runtimem (nebo nad .NET Framework runtime, ale tuto možnost dále zcela ignorujme).
Za účelem konsolidace NuGet balíčků Microsoft přišel s metabalíčkem Microsoft.AspNetCore.All, který v podstatě sjednocuje celý ASP.NET Core framework + přidává nějaké 3rd party libraries. Nemusíte tedy udržovat 50 NuGetů, ale staráte se pouze o jeden Microsoft.AspNetCore.All.
Ve verzi ASP.NET Core 2.1. vydal Microsoft vedle metabalíčku Microsoft.AspNetCore.All ještě Microsoft.AspNetCore.App. Premisa byla jednak odříznout 3rd party libraries, které dělaly neplechu a dále stabilizovat celý framework. Proto verze Microsoft.AspNetCore.App 2.1.0 obsahuje závislosti na striktních verzích balíčků. Od verze 2.1.x už konstelace balíčků není tak striktní, ale jsou definovány rozumné rozsahy verzí (které jsou údajně otestované).
Od verze (ASP).NET Core 3.0 už se nebude 3.x verze metabalíčku Microsoft.AspNetCore.All vydávat (respektive bude se patchovat jen 2.1.x a 2.2.x). Už teď tedy doporučuji začít konsolidovat projekty a upřednostnit verzi Microsoft.AspNetCore.App.
Důležité je, že Microsoft. AspNetCore.All a App jsou nyní vydávány nejen jako metabalíčky ale i jako shared runtimes. Čili když píšete nyní ASP.NET Core aplikaci, můžete ji psát "nad" ASP.NET Core Runtime (nebo taky nemusíte). A dokonce si můžete vybrat mezi All / App.
Ve vašem csproj stačí mít něco takového:
<Project Sdk="Microsoft.NET.Sdk.Web"> <PropertyGroup> <TargetFramework>netcoreapp2.1</TargetFramework> </PropertyGroup> <ItemGroup> <PackageReference Include="Microsoft.AspNetCore.App" Version="2.1.2" /> </ItemGroup> </Project>
Tím vlastně říkáte, že využíváte .NET Core 2.1 a ASP.NET Core Runtime 2.1.2. Máte k dispozici dokonce automatické patchování, takže když na cílové mašině bude ASP.NET Core 2.1.3+, tak se rovnou aplikace natargetuje na tuto verzi. Zní to super, ale má to jednu logickou podmínku.
SCD vs FDD deployment
Existuje ještě tzv. Self-Contained Deployment a Framework-Dependent Deployment. Pokud vypublikujete aplikaci v režimu SCD, v podstatě se vyrobí mega velký publikační balíček pro určitou platformu, obsahující absolutně všechny potřebné dll. V případě publikace pro windows bude součástí i EXE file, který můžete spustit (tím rozjedete kestrel a aplikace bude dostupná na nějaké URL + portu, by default localhost:5000). V případě linuxu nebo jiné platformy to bude zase jiný balast v závislosti na cílové platformě.
Druhá možnost je, že uděláte tzv. FDD. V tomto případě vytváříte multiplatformní balíček, který obsahuje jen nezbytně nutné DLLka. Tento balíček je minimalistický a předpokládá, že tam kam ho nasadíte bude přítomen .NET Core / ASP.NET Core runtime (dané nebo vyšší verze). Výhoda je ta, že tento balíček lze nasadit na linuxovou mašinu stejně jako na windows. Podmínkou je jen přítomnost onoho runtimu. Právě v tomto režimu funguje patchování.
Abychom to tedy shrnuli. Pokud jedete na novém ASP.NET Core Runtime 2.1 a publikujete v režimu FDD na cílový server, můžete tam hostovat třeba 20 aplikací a pod nimi patchovat ASP.NET Core shared runtime, aniž byste museli do aplikací sahat.
Azure App Service
Pokud máte vlastní server, na kterém ASP.NET Core aplikace hostujete, máte všechno pod kontrolou (jako správný líný vývojář nainstalujete novou verzi jednou za uherský rok, takže nic moc nemáte šanci rozsekat).
Pokud nasazujete do Azure App Service, stará se vám o prostředí přímo Microsoft. Takže tak nějak očekáváte, že Microsoft bude hrnout na App Service všechny patche a vy zase budete hrnout přes CI nové verze aplikace a všechno bude "works like a charm", jak by řekli amíci.
Důvod, proč tento článek píšu je má třetí negativní zkušenost s tím, že po nasazení aplikace (cca 3 týdny zpět) prostě jednoho rána vstanu z postele a aplikace nejede. Prostě je vychcíplá na chybě 502.5. Mimochodem chyba 502.5 se stane časem neprosto legendární, protože je absolutně nic neříkající a může mít desítky příčin, které plánuji v blízké době shrnout do článku a minimálně hodinové přednášky. Pro ty z vás, co ještě neměli tu čest si vás dovolím s tou bestií seznámit:
Abych ale došel k nějakému závěru, můj csproj při nasazení vypadal nějak takto:
<ItemGroup> <PackageReference Include="Microsoft.AspNetCore.App" /> <PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="2.1.2" </ItemGroup>
Po třech týdnech od nasazení Microsoft vydal novou verzi Microsoft.EntityFrameworkCore.Sqlite (2.1.3), která referencuje Microsoft.EntityFrameworkCore 2.1.3. Takže se automaticky na serveru začala hledat tato verze balíčku (což jsem zjsitil z logů po přihlášení do kudu na app service). Jenomže tato verze balíčku tam z nějakého důvodu není.
Microsoft.NETCore.App 2.1.1 [D:\Program Files (x86)\dotnet\shared\Microsoft.NETCore.App] Microsoft.NETCore.App 2.1.4 [D:\Program Files (x86)\dotnet\shared\Microsoft.NETCore.App]
Jsem od přírody zvědavý, takže jsem začal prolézat další App Services a zjistil jsem, že toto se týká App Service v regionu West Europe. V případě North Europe, kde hostuje jeden můj zákazník je nadílka runtimes bohatší:
Microsoft.NETCore.App 2.1.1 [D:\Program Files (x86)\dotnet\shared\Microsoft.NETCore.App] Microsoft.NETCore.App 2.1.3 [D:\Program Files (x86)\dotnet\shared\Microsoft.NETCore.App] Microsoft.NETCore.App 2.1.4 [D:\Program Files (x86)\dotnet\shared\Microsoft.NETCore.App]
Takže až budete deployovat aplikaci do více regionů a budete se divit, proč to v jednom jede a ve druhém máte 502.5, vzpomeňte si na to, jak je skvělé, když se vám o prostředí stará někdo jiný, komu důvěřujete.
Known issues
Výše popsaný problém není jen Holec-specific. Řeší ho mnoho vývojářů v různých podobách. Když například nareferencujete metabalíček All / App a poté připojíte referenci na NuGet balíček z tohoto runtime jiné specifické verze, bude se vám dít to samé. Jenomže na to přijdete celkem rychle při nasazení. Problém je, když vám prostě aplikace týdny běží a pak jednoho dne spadne, protože někdo vydal nový NuGet balíček.
Řešení
I když je FDD velmi lákavý režim publikace aplikací, shledal jsem ho fatálně rizikovým a všechny aplikace budu odteď publikovat v režimu self-contained (což bych teoreticky považoval za horší variantu). Někdy ale praxe teorii prostě předčí. Pokud chcete aplikace publikovat jako self-contained (třeba přes CI), stačí v csproj uvést tzv. runtime identifier, pro který je publikační balíček určen. Například:
<PropertyGroup> <TargetFramework>netcoreapp2.1</TargetFramework> <RuntimeIdentifiers>win10-x64</RuntimeIdentifiers> </PropertyGroup>
Pro publikaci z CMD nebo obecně při použití dotnet toolu lze použít ekvivalentně:
dotnet publish -c release -r win10-x64
Závěr
Asi k tomu není co dodat. Většina vývojářů, se kterými se bavím nemá sebemenší tušení, že existují nějaké ASP.NET Core runtimes a v ASP.NET Core projektech většinou vidím smečky balíčků nebo metabalíček All. Chápu, že pro většinu vývojářů je těžké usledovat tyto drobnosti, když s každou verzí se toho celkem dost změní. O to víc bych očekával, že za tou netransparencí bude alespoň chytristika, která zabrání podobným situacím.
Pokud píšete aplikace v ASP.NET Core, sledujte novinky a snažte se vždy ve chvílích času rozkrývat co a jak funguje. Jednoho dne se vám to může hodit. Pokud tomu chcete jít trochu naproti, přijďte na DotNET Talks (v.1) nebo na mé školení, které chystám v říjnu.
Update 29.09.2018
Pro tentokrát je po problému: