pondělí 9. dubna 2018

DevOps a Release management

Release management je proces, který je dnes přímo spojen s věcmi jako je Continuous Integration, Continuous Delivery (ve zkratce CI/CD), Git, Docker, apod. Díky tomu se z Release managementu stává obor, který vyžaduje vcelku hluboké znalosti v IT oboru. V dnešním článku se pokusím rozebrat, jakým způsobem k Release managementu přistupujeme my a jak do tohoto procesu zakomponovat ostatní moderní technologie jako je Docker, apod.

Verzování


Základním předpokladem je určení, jakým způsobem verzovat. První variantou je, že si sami vytvoříme verzování podle svého vlastního uvážení. To ovšem nedělejte. Vymýšlení "kola" je to nejhorší, co můžete udělat. Chápu, že každý z nás chce být průkopníkem ve své oblasti, ale je velice malá pravděpodobnost, že se Vám to podaří. Nicméně, pokud máte ambice, že změníte IT svět a nastavíte novou specifikaci pro verzování, směle do toho.

Druhou a mnohem lepší variantou je, že zvolíte již existující návrh. Tím je specifikace, která se označuje jako Sémantické verzování, zkráceně SemVer. Tuto specifikaci vytvořil Tom Preston-Werner, což je spoluzakladatel GitHubu. Samotná specifikace je vcelku jednoduchá a její osvojení je otázka několika hodin. Výhodou tohoto řešení je zejména fakt, že pokud dodržíte tuto specifikaci, je mnohem větší pravděpodobnost, že konzumenti Vašeho API, knihovny, či celého systému porozumí Vašemu verzování. Nebudete muset nikomu vysvětlovat, že když jste zvýšili MAJOR verzi, že tím automaticky říkáte, že Váš systém je zpětně nekompatibilní, apod.

Vedle toho je SemVer vhodné i pro vývoj, tedy v době, kdy sice v produkci ještě nejste, ale potřebujete si v projektu udělat trochu pořádek a jednoduše zajistit, aby se do vyšších prostředí nedostalo něco, co tam nepatří.

Následující popis počítá se znalostí SemVer.

Verzování a Git


Samotný Git je sice označován jako distribuovaný systém správy verzí, ale to ještě neznamená, že použitím Gitu mám vyřešen Release management. Naopak. Git slouží pouze jako prostředek k tomu, jak Release management uskutečnit.

Pokud dáte do spojení Release management a Git, s největší pravděpodobností skončíte u něčeho, co se jmenuje Gitflow.

Gitflow

Jedná se o specifikaci, která určuje, jakým způsobem řídit Váš projekt od prvního řádku kódu až po produkční verzi. Vedle toho také definuje, jak řešit věci jako je release nové verze, opravy na stávajících verzích, apod.

Gitflow vychází z předpokladu, že díky Gitu máte možnost jednoduše vyrábět větve a ty mezi sebou zase spojovat a štítkovat (tagovat). Díky tomu si můžete ušetřit několik bezesných nocí, kdy budete přemýšlet nad tím, jak se zachovat, když například máte v produkci verzi 1.0.3 a vy už pracujete na verzi 2.0.0, ale zároveň potřebujete opravit něco ve verzi 1.0.3 a danou opravu udělat jak pro produkci, tak i pro stávající vývoj.

Gitflow je robusní řešení, které pokrývá všechny známé problémy při dodávce. Vedle toho je to návrh, který předpokládá, že v produkci máte několik verzí. Gitflow je vcelku rošířená specifikace, se kterou se lze často setkat. Otázkou jen je, jestli skutečně Gitflow potřebujeme.

Důvodů, proč neskočit po tomto řešení je hned několik. Prvním důvodem je to, že toto řešení je příliš komplexní a často se dostáváte do situace, kdy kvůli jedné malé změně musíte "mergovat první ligu". Druhou nevýhodou je to, že pokud vydáváte nové verze agilněji, tedy v rychlých iteracích, tak je Gitflow něco, co skutečně nechcete. Další nevýhodou je to, že je velká pravděpodobnost, že se v tom někteří vývojáři budou ztrácet a samotný Release management tak bude výsadou jen těch, kteří perfektně znají Gitflow. Tady platí pravidlo, že méně je někdy více.

Když jsem řešil samotný Release management, tak po prostudování několika specifikací a po ohlédnutí se zpět, na projekty, na kterých jsem pracoval, tak se mi v hlavě točila pořád a jedna a ta samá otázka. Skutečně Gitflow potřebuji? Co mi to nakonec přinese? Je to pro projekt, na kterém pracuji, vhodná specifikace? Nakonec jsem dospěl k tomu, že nikoli.

Github release

Další variantou, jak vyrábět release je pomocí Githubu. Pokud své projekty máte v Githubu, ať už jako privátní repozitáře či veřejné, máte možnost v určitém momentu udělat release. Onen release nemusí být jen produkční, ale také pre-release, což znamená, že se jedná o určitý milník ve vývoji, kterým si označíte Váš poslední commit. Tento pre-release může být vhodný třeba pro nasazení na vyšší prostředí, než je Vaše vývojové. Třeba pro demo, testy apod.

Samotný Github release nedělá nic jiného, než to, že přidá tag na daný commit. Buď podle větve a nebo na jasně definovaný commit. Je vcelku na Vás, kde daný tag umístíte, jde spíše o to, že ho umístíte.

Další věcí je i to, že při vytvoření releasu Vám Github nabízí možnost napsat Release notes. Tedy k dané verzi sepsat poznámku, ve které jasně specifikujete, co nová verze obsahuje, jaké bugy opravuje, apod. Přiznám se, že Release notes je věc, která je nesmírně důležitá a zároveň nejvíce ignorovaná mezi vývojáři. Je sice hezké, že jste vytvořili novou verzi, ale bez popisu dané verze je to spíše jen tvorba verze pro Vás, nikoli pro ostatní. V případě, že píšete knihovnu, kterou má konzumovat někdo další, tak absence Release notes je kontraproduktivní i pro Vás. Vždy, když použiji nějakou knihovnu, tak se koukám, zda vývojáři píší ony poznámky. Pokud ne, knihovnu ignoruji a jdu jinam. Veřte, že nejsem jediný.

Když jsem si dal na misku vah plusy a mínusy mezi Gitflow vs Github releases, tak jsem nakonec dospěl k závěru, že raději zvolím Github. Jednak pro jeho jednoduchost, ale také pro to, že je plně postačující. Jsem presvědčen, že pro drtivou většinu projektů to tak je. Když jsem přemýšlel, kde by mi Github releases nestačil, nebyl jsem schopný žádný takový příklad najít. Pokud Vás nějaký napadne, budu rád, když se o tento případ podělíte. Samozřejmě v případě absence Githubu můžeme říci, že release je řízen tagováním, což je ve výsledku to samé, akorát často přijdete o ony hezky zobrazené Release notes.

Abyste měli představu, jak takové releases v Githubu vypadají, podívejte se třeba na knihovnu React.

Verzování a Docker


Pokud dnes mluvíme o dodávce softwaru, který je server side charakteru, tak bychom druhým dechem měli říct, že ho dodáváme pomocí Docker image. V případě, že Docker nepoužíváte, ale chtěli byste, věřte, že si tím neskutečně ušetříte problémy při integracích a dodávce směrem k provozu.

Díky tomu, že Docker image je tagována, tak se tím přímo nabízí, aby byla tagována tak, jak jsou tagovány jednotlivé verze. Více se o verzování Docker image zmíním dále, v ukázkovém projektu.

Verzování a NPM


V případě, že píšete aplikace v javascriptu, je vcelku jedno, zda serverové či klientské, tak jistě používáte hromadu knihoven, na kterých jste závislí. Vedle toho se ovšem často dostanete do fáze, kdy byste si i Vy sami chtěli vyrobit vlastní knihovnu, která Vám umožní nasdílet si části kódu mezi jednotlivými systémy.

K tomuto účelu slouží právě NPM repozitář. A světě div se, ale i NPM podporuje verzování. Dokonce používá specifikaci SemVer. Tudíž je vcelku jasné, že i zde použijete stejný přístup. Více opět v ukázce.

Verzování a CircleCI


Pokud mluvíme o Release managementu a dodávce softwaru, tak bychom si měli najít nástroj, který nám pomůže s tím, abychom automatizovali to, co automatizovat lze. Tedy o nástroji, který se označuje jako CI/CD.

Nejznámějším a asi možná i nejpoužívanejším je Jenkins. Hromada lidí má s Jenkinsem zkušenosti a jistě Vám řeknou, že je skvělý. Po pravdě, osobně Jenkins nesnáším. Nesnáším je možná silné slovo, ale nespatřuji v něm příliš velkou výhodu oproti ostatním. Asi tím nejzásadnějším je fakt, že Jenkins lze použít pouze jako On-premises, tedy musíte si ho "někam" nainstalovat.

Moje osobní zkušenost s Jenkinsem je taková, že jsem zažil instance, které byly příšerně pomalé a vytvořit v něm "job", chtělo notnou dávku trpělivosti, která mi scházela. I když s Jenkinsem pracovat umím, vůbec mi nebude vadit, když ho již nepotkám.

V době, když jsem hledal smysluplnou alternativu, která bude navíc fungovat jako služba, tudíž nepotřebujete člověka, co se Vám musí starat o Váš vyšperkovaný Jenkins, nalezl jsem několik variant. Osobně jsem si nejvíce zamiloval CircleCI a to z několika důvodů.

První věcí je tedy to, že funguje jako služba. Další výhodou je to, že má vcelku jednoduché použití. Tedy nemusíte vystudovat 5 vysokých škol, abyste byli schopni napsat jednoduché joby. Dále je to fakt, že každý job, který spouštíte je spuštěn ve vlastním Docker kontejneru a tím se od sebe jednotlivé joby izolujete a můžete je spouštět paralelně. Dané joby pak nadefinujete do workflow a určíte závislosti mezi nimi. Typickým příkladem je, že pokud nedoběhne build projektu, nespustí se deploy, apod.

CircleCI neumožňuje vyrábet Joby bez zdroje. Tedy každa akce je vázána na repozitář v Gitu. Tohle pro někoho může být blokující, ale já to vnímám jako výhodu. Jakým lepším způsobem si verzovat joby, než přímo Gitem? A věřte, jde to i bez toho, jen je třeba občas se nad danou věcí jinak zamyslet. Tím, že CircleCI podporuje i scheduler, tedy opakované spouštění jobů v určitý čas, lze říci, že je to plnohodnotná alternativa.

Poslední věcí, kterou bych chtěl zmínit je to, že CircleCI je spojen s BitBucketem nebo Githubem. Nepotřebujete tedy speciálně pro CD/CI definovat oprávnění a něco složitě nastavovat, ale ono oprávnění je přebíráno právě z těchto systémů pro Git. Pokud jste ve Vaší privátní organizaci na Githubu administrátorem, budete administrátorem i zde, apod.

CircleCI je pro mě osvobozující v tom, že dělá jen to, co dělat má. Tam jeho role začíná i končí.

Ukázkový projekt


Nyní se přesuňme k druhé části, která ma za cíl ukázat, jakým způsobem náš proces úspěšně nastartovat a zároveň provozovat i v produkci.

Nejprve si nadefinujme projekt, který se skládá ze tří částí:

1. Frontend v Next.js
2. Backend v Node.js
3. Knihovna X

Frontend pro svojí činnost potřebuje bežící Node.js a zároveň používá knihovnu z NPM, tedy naší Knihovnu X. Backend také běží nad Node.js a připojuje se k databázi. Poskytuje tedy vrstvu mezi databází a frontendem. Zároveň i backend používá naší Knihovnu X z NPM jako závislost.

Náš projekt bude dodáván formou Docker image a poběží v Azure Cloudu v Kubernetes.

První setup


Prvním krokem je inicizalizace našich systému do Gitu. Budeme mít tři Git repozitáře (Frontend, Backend, KnihovnaX). Inicializaci můžeme udělat v master větvi, ale hned bychom měli vyrobit větev s názvem dev (potažmo devel či develop).

Druhým krokem je definování verze v package.json. Pokud začínáme, označme verzi jako: 0.1.0-alpha.1. Tím, že nám SemVer říká, že do produkce nemůže jít nic, co začíná 0, tak alespoň víme, že v produkci ještě nejsme. Pokud již systém existuje a to i v produkci, tak záleží jak moc jsme verzování ignorovali. Pokud úplně, nastavil bych verzi jako: 2.0.0-alpha.1. Rovnou říkáme, děláme verzi 2 a verze 1 bylo vše předchozí :)

Vývoj


Pokud na daném projektu či repozitáři pracujeme sami, klidně můžeme vše commitovat do naší dev větve. Jsme tam sami, takže proč ne. V případě, že nás pracuje na projektu více, je lepší si vytvořit vlastní větev. Například tak, že si vytvořím novou větev z větve dev a do názvu dám: feature/xxx. Pod názvem xxx by se mělo vyskytovat to, co skutečně děláme. Ve chvíli, kdy svou činnost máme hotovou, zamergujeme naší větev feature/xxx do větve dev. A tak stále dokola. Každopádně nezapomínejte po sobě dané větve mazat.

Vedle toho můžeme rovnou použít CircleCI a říci, že pokud někdo uloží něco do větve dev, spustí se následující scénář (frontend, backend):

1. Build projektu (npm install, tsc, tslint, apod)
2. Test projektu (unit testy, apod)
3. Tvorba Docker image (s tagy: "commit hash" a "dev")
4. Deploy (deploy projektu do Kubernetes)

Krok 1 a 2 je vcelku jasný. Krok 3 říká, že vytvořím Docker image, kde commit hash je unitátní a tudíž bude možné kdykoli nasadit verzi Docker image podle commitu. Tag dev v Docker repozitory se bude neustále přepisovat. Ale zase nám umožní, vytvořit prostředí, které bude z posledních commitů z dev větví.

Krok 4 je deploy, což znamená, že pokud je již systém běžící, tak stačí pouze aktualizovat Docker image tag v prostředí dev. Nejlépe podle commit hash.

Co se týče knihovny, tak workflow by bylo jednodušší:

1. Build projektu
2. Test projektu

První pre-release


Nyní se přesuňme k tomu, že v rámci Githubu vytvoříme svůj první release. V tomhle případě začněme rovnou naší knihovnou, protože tu budete muset releasovat jistě jako první.

Poté, co v Githubu provedete release, tak se do Gitu zapíše tag. Při releasování v Githubu, zapíšete verzi takto: v0.1.0-alpha.1. CircleCI umí zareagovat i pouze na push tag a spustí následující workflow:

1. Build projektu
2. Test projektu
3. Publish do NPM (tag: alpha, verze: 0.1.0-alpha a 0.1.0-alpha.1)

V NPM repozitáři budete verzi 0.1.0-alpha přepisovat každým vydáním nove alpha verze.

Nyní si v naší knihovně (v dev větvi) otevřeme soubor package.json a verzi přepíšeme na 0.1.0-alpha.2. Následuje další vývoj, až do doby, kdy se rozhodneme, že by stálo za to, opět udělat nový pre-release.

Co se týče frontendu a backendu, tak budeme postupovat úplně stejně. Rozdílné bude pouze naše workflow:

1. Build projektu
2. Test projektu
3. Tvorba Docker image (s tagy: "0.1.0-alpha" a "0.1.0-alpha.1")

A opět v dev větvi zvýšíme verzi na: 0.1.0-alpha.2.

Tímto způsobem jsme vytvořili první pre-release.

Je dobré ještě zmínit, že samotné verzování je hodně na Vás. Nicméně byste měli z alpha verze přecházet do beta verze a poté do Release candidate (rc). Tímto způsobem byste měli postupně dojít až do produkce. Použití těchto identifikátorů je následující:

1. Alpha říká, že funkcionalita je nekompletní a rozpracovaná
2. Beta říká, že funkcionalita je již téměř kompletní, ale může obsahovat chyby
3. Release Candidate říká, že funkcionalita je vyvinuta a opravují se jen chyby

Další variantou je, že s jednotným identifikátorem dojdete až před produkci. Určitě ale nemodifikujte samotnou verzi. Je nežádoucí, aby v rámci vývoje došlo k tomu, že z verze 0.1.0 udělám verzi 0.2.0 apod. To patří do produkce.

Pokud jste pečlivě prostudovali SemVer, tak jste jistě narazili i na to to, že v rámci pre-release identifikátoru můžete být více konkrétní. Klasickým příkladem je to, že alpha.x je málo vypovídající. Nic nebrání tomu, aby se verze označila třeba takto: 0.1.0.-alpha.1+api.3. Je jen na Vás, co vše chcete v pre-release identifikátoru evidovat. Samozřejmě bych to s výčtem informací nepřeháněl :)

Pre-release je stav, kdy vývoj říká, že splnil nějaký milník. Něco, kdy stojí za to, aby provedl pre-release a tím nabídnul své dílo vyššímu prostředí (demo, testy, apod). Vedle toho není cílem, aby všechny projekty měly stejné číslo v identifikátoru alpha. Vývoj jen řekne, že pro sestavení prostředí z posledního milníku je třeba:

Frontend: 0.1.0-alpha.7
Backend: 0.1.0-alpha.12
Knihovna: 0.1.0-alpha.4

Popřípadě můžete říct, že použití verze 0.1.0-alpha, vždy použije poslední existující release v alpha fázi.

Určitě doporučuji, aby pre-release vznikal tak často, jak to jen lze. V rámci konce sprintu (jednou za 14) je maximum. Klidně může vznikat častěji a to je žádoucí. Přeci jen, chceme říkat, že jsme "agilní" :)

První produkční release


Pokud již nastal čas, management už je naštvaný, že jsme po termínu, přejdeme k oficiálnímu releasu. Ten uděláme tak, že v našich package.json souborech nastavíme verzi na: 1.0.0 a tento commit zamergujeme do master větve. Poté přes Github uděláme release.

V NPM nám vznikne knihovna s číslem verze 1.0.0. Pro Docker image zvolíme tagy: 1, 1.0, 1.0.0 a latest. Důvod, proč bychom měli opět vyrábet více tagů je ten, že poté si můžeme zvolit, jakým způsobem budete sestavovat produkci či jiné prostředí. Pokud zvolíme verzi 1, tak říkáme, aktualizuj se vždy, když někdo vytvoří produkční release 1.x.x. Pokud 1.0, tak totéž platí pro 1.0.x, apod. Latest je tag, kterým se definuje poslední existující stabilní verze a navíc je pro Docker výchozí.

V rámci Docker tagů můžete použít i kódové označení. Jedním z příkladů je třeba Docker image pro Node.js.

Nyní se vrátíme k naší dev větvi a verzi nastavíme na 1.1.0-alpha.1.

Opravy chyb na produkci


Možná jste si všimli, že jsme v dev větvi použili verzi 1.1.0-alpha.1 a ne 1.0.1-alpha.1. Důvod je ten, že pokud se na produkci vyskytne závažná chyba, která musí být opravena dříve, než bude nový release, tak její oprava bude mít verzi: 1.0.1.

Oprava chyby se provede tak, že se vytvoří nová větev z tagu 1.0.0. Název branche by měl být hotfix/xxx, kde pod xxx se může skrývat identifikace chyby. Poté, co je chyba opravena a otestována, je zamergována zpět do master větve a vydána jako verze: 1.0.1. Současně s tím bychom daný hotfix měli zamergovat do naší dev větve a následně branch hotfix/xxx smazat.

Následný vývoj


Jak už jsme si řekli, tak následný vývoj bude mít verzi 1.1.0-alpha.1. Nicméně může se stát, že nový release bude znamenat nekompatibilitu s předchozí verzí. V tom případě bychom verzi nastavili jako: 2.0.0-alpha.1.

V této chvíli se můžete vrátit ke kapitole Vývoj, protože přesně na tom místě se nyní nacházíme...

Závěr


Jsem si vědom toho, že existuje tisíce patternů, jak řídit samotnou dodávku softwaru. Každopádně cílem bylo Vám ukázat jednu z cest, kterou když se vydáte, tak jistě neselžete.

Stejně tak lze mnohem více věcí automatizovat, viz třeba ruční přepisování package.json souborů. Ale vždy je zatím skryto určité nebezpečí. Tím nebezpečím je fakt, že automatizace je dobrá cesta, ale do určitých limitů. Mohlo by se Vám totiž jednoduše stát, že touha po dokonalé automatizaci končí šílenými bash/python/perl skripty, které jsou často jen náhradou ručního releasu. Za mě, ona automatizace končí přesně ve chvíli, kdy se dostanu do míst, kde musím řešit příliš mnoho výjimek a pravidel.

A co vy? Jak řešíte Release management?


Žádné komentáře:

Okomentovat

Když programátor založí a řídí firmu

Jako malý jsem chtěl být popelářem. Ani ne tak proto, že bych měl nějaký zvláštní vztah k odpadkům, ale hrozně se mi líbilo, jak...