CI/CD z .NET Core v Linux do Kubernetes v Azure s Visual Studio Team Services

Zkusil jsem se pustit do něčeho, v čem jsem se zatím příliš nepohyboval. Visual Studio, .NET, Visual Studio Team Services, ale chtěl jsem to vidět v kombinaci s tím, čemu se naopak věnuji delší dobu - Linux, Docker kontejnery a Kubernetes. Jde to vůbec dohromady? Perfektně! Pojďme na to mrknout.

.NET Core v Linux kontejneru

Myslím, že pročištění celého .NET a jeho otevření byl opravdu skvělý tah, který udělal .NET sexy i v dnešní době. Je open source, funguje v Linuxu a díky své architektuře, self-contained designu a podstatně menší velikosti jde perfektně dohromady s kontejnery. Navíc verze .NET Core 2.0 už je funkčně natolik bohatá, že mi pravověrní dot-netáří tvrdí, že už jim nebrání v rozletu - naopak. Nikdy mě nenapadlo, že se za určitých okolností bude dát vzít .NET aplikace a překlopit do stavu, kdy běží v Linux kontejneru.

Použijeme Visual Studio rovnou s kontejnerizací

Začal jsem tím, že jsem vytvořil nový projekt ve Visual Studio 2017.

Na další stránce si vyberu projektovou šablonu (třeba zjednodušené Razor pages), ale to hlavní je vidět dole. Zapínám podporu pro Docker v Linuxu na svém Windows počítači (musím mít nainstalovaný Docker for Windows).

Tato volba způsobila, že se mi vygeneroval Dockerfile, tedy hlavní předpis pro sestavení kontejnerového image (nicméně tomu musí předcházet build proces).

To určitě využijeme. Nicméně pojďme se nejprve podívat, jestli Visual Studio umí s kontejnery nějak pracovat i lokálně. Umí. V adresáři nacházím Docker compose soubor, který evidentně popisuje build proces aplikace.

Naivně tedy spouštím tlačítko "přehrávání" a moc nevěřím, že se bude dít něco rozumného.

Ale ono se děje. Visual Studio využívá compose souboru a provádí build, následně montuje konejner přes můj Docker for Windows a během chvilky mi v browseru vyskakuje aplikace. Skutečně docker příkazem zjišťuji, že opravdu běžím v lokálním Linux kontejneru.

PS C:\Users\tokubica> docker ps
CONTAINER ID        IMAGE                 COMMAND               CREATED             STATUS              PORTS                   NAMES
ce0434969b2e        webapplication1:dev   "tail -f /dev/null"   42 seconds ago      Up 41 seconds       0.0.0.0:32768->80/tcp   dockercompose12046321161007613461_webapplication1_1

Na straně u vývojáře jsem spokojen a jdeme do Visual Studio Team Services.

Visual Studio Team Services - moje CI/CD do Kubernetes

Nejprve jsem si ve VSTS založil projekt a použil ho jako Git z pohledu version control. To samozřejmě není nutné (můžete využít třeba GitHub a VSTS použít jen na CI/CD ... případně se dá klidně CI dělat v Jenkins a VSTS použít jen na CD apod.). To si ukazovat nebudeme - přejděme rovnou na Continuous Integration.

Continuous Integration: definice Build

Připravil jsem si Build definici, která bude třeba reagovat na commit do nějakého branche. Procesní stránku věci teď nechme stranou, půjde mi spíše o dotažení kódu do Kubernetes, než nastavení governance a celého procesu.

První velmi příjemná vlastnost VSTS je podpora dynamicky přidělených agentů a to včetně Linuxových. Celé VSTS je cloudová služba a agenti, které potřebuji na různé spouštění testování, buildování či montování Docker obrazů, si budu ad-hoc půjčovat z cloudu. Tohle přináší fantastickou produktivitu v porovnání s fixními zdroji, které jsou obvykle omezující v on-premises.

V prvním kroku se ujistím, že do agenta nahrávám nejnovější prostředí - v mém případě .NET Core 2.0.

Následně provedu build aplikace.

Na tomto místě by měl být nějaký Unit test a podobné záležitosti, které v rámci tohoto pokusu s dovolení přeskočím (a ne že pak někde řeknete, že jsem vám takový špatný nápad poradil :) ).

Další krok mi chvilku trval odladit. Nemohl jsem se stále trefit do správných adresářů tak, aby následně fungovala kontejnerizace buildu. Potřeboval jsem přepnout na verzi 2 tohoto tasku a specifikovat správnou cestu v --output přepínači. Následně zakázat (to bylo důležité) přidání názvu aplikace do adresáře a také vypnout publikování věcí, které nepotřebuji.

V dalším kroku potřebuji smontovat Docker image. Na to musím mít "počítač s Linuxem a Dockerem" a tím je samozřejmě můj Linux Agent vypůjčený automaticky z cloudu, takže žádný problém. Potřebuji specifikovat kam bude můj kontejner patřit (v tomto kroku přesněji jaký bude jeho plný název) - v mém případě Azure Container Registry.

Ještě dole v tomto kroku potvrdím, že chci za název kontejneru přidat tag s číslem buildu a současně druhou kopii image vytvořit s tagem latest.

Tento krok lokálně (v mém přiděleném agentovi) vytvořil image tak, že použil Dockerfile (ten nám vygenerovalo Visual Studio dříve), který mu řekl jak dovnitř nakopírovat vybuildovanou aplikaci. Jenže teď nám image sedí sice se správným jménem, ale jen v lokálním repozitáři v agentovi. Potřebujeme publikovat Docker image do mého Azure Container Registry.

Mám tedy připraven build, který jsem zabalil do Docker image a ten mám ve svém repozitáři připravený k releasnutí. Ale ještě jedna věc mi chybí. Release budu provádět do Kubernetes, takže potřebuji ještě připravit příslušný deployment předpis. Vytvořil jsem si jeho šablonu, která má objekt Kubernetes Service (externí reprezentaci s public IP adresou) a Deployment (3 balancované instance mé aplikace). Potřebuji tuto šablonu modifikovat tak, aby ukazovala na právě vytvořený build - tedy změnit v ní tag image, který se z registru použije.

Tady přišlo asi jediné místo, které mi přišlo zatím nedostatečně pokryté - nebo jsem alespoň nenašel vhodné řešení. Představoval bych si nějaký templatovací jazyk, pro mě ideální Jinja2, ale to jsem nějak nenašel a z marketplace si nevybral. Nakonec jsem tedy prozatím vytvořil templatovací "jazyk" sám a použil sed pro dosazení hodnoty. Není to žádná krása, ale alespoň jde o další důkaz, že VSTS agent je skutečně Linuxový :)

Šablona deploymentu vypadá takhle a je součástí version control.

kind: Service
apiVersion: v1
metadata:
  name: myvsts-service-ext
spec:
  selector:
    app: myvsts
  type: LoadBalancer
  ports:
  - protocol: TCP
    port: 80
    targetPort: 80
---
apiVersion: apps/v1beta1
kind: Deployment
metadata:
  name: myvsts
spec:
  replicas: 3
  template:
    metadata:
      labels:
        app: myvsts
    spec:
      containers:
      - name: myvsts
        image: tomdocker.azurecr.io/webapplication1:{tag}
        ports:
        - containerPort: 80
      nodeSelector:
        beta.kubernetes.io/os: linux

Takhle pak vypadá můj "sofistikovaný" systém dosazení čísla buildu.

Výborně. Teď potřebujeme někam uložit artefakty tak, aby bylo možné je nasadit. Samotný Docker image je v mém repozitáři, takže jde o upravený deployment.yaml pro Kubernetes, který nakopíruji do adresáře s artefakty.

Hotovo, teď už stačí artefakt s nějakým názvem publikovat.

Perfektní. Právě jsme si automatizovali připravení všeho potřebného pro přechod z kódu do něčeho, co již je možné přímo poslat do Kubernetes.

Continuous Delivery/Deployment: definice Release

Jednotlivé buildy mám ve stavu "release candidate", tedy je to otestované a připravené pro případné nasazení (Continuous Delivery) a to můžeme vyvolat přímo z VSTS. Ostatně můžeme i tyto procesy automatizovat, přidat nějaké další pojistky, nutnost dalšího schválení odpovědnou osobou a tak podobně a automaticky i šoupnout kód rovnou do produkce, pokud chceme (Countinuous Deployment). Pojďme si vytvořit definici Release.

V mém případě je to strašlivě jednoduché. Na vstupu je artefakt s deployment.yaml souborem a jednoduchý krok pro deployment.

V mém jediném kroku, který musím nastavit, je nejprve vytvoření spojení do Kubernetes (zadáte cestu ke clusteru a kopii Kubernetes konfiguračního souboru - toho stejného, co používáte s kubectl a asi máte v ~/.kube/config

Protože používáme image v zabezpečeném repozitáři, musíme dát v Kubernetes jeho credentials. Perfektní je, že VSTS to udělá za nás a příslušný secret založí.

Poznámka - pokud máte Azure Container Registry společně s AKS (Managed Kubernetes) v jednom místě pod jedním účtem, bude Kubernetes automaticky přihlášený, což je můž případ. Jinak právě musíme v deployment.yaml použít volbu imagePullSecrets: acr (kde acr je Secret name z předchozí konfigurace).

Zbývá poslední krok (tady jsem se znovu trochu trápil s nalezením správné cesty k souboru, ale povedlo se - poslední adresář je pojmenování vašeho artefaktu z Build kroku) - zadání Kubernetes příkazu (apply způsobí rolling upgrade v Kubernetes).

To je všechno - opravdu to funguje!

 

VSTS vám může posloužit pro řízení projektu, trackování bugů, jako version control, continuous integration systém i continuous delivery a mnoho dalšího a to pro libovolný jazyk i prostředí. Nicméně některou z těchto součástek můžete bez problémů nahradit jinou a použít VSTS třeba jen pro CD nebo pro CI/CD ... zkrátka to je na vás.

Pro mne je zásadní zjištění, že celý ekosystém počínaje Visual Studio a .NET Core šablonou s podporou Dockeru v Linux přes VSTS Linux agenty až po deployment do Kubernetes v Azure je skutečně výborně připraven pro moderní svět kontejnerů!



Kubernetes praticky: role Service Mesh Kubernetes
Kubernetes praticky: DAPR jako přenositelná aplikační platforma pro cloud-native aplikace - state store a pub/sub Kubernetes
Vytvářejte aplikace nezávislé na platformě s DAPR (Distributed Application Runtime) Kubernetes
Open Application Model: moderní popis aplikace bez naivity Kubernetes
Kubernetes praticky: serverless s KEDA, Osiris a Azure Functions Kubernetes