Masterless puppet

Ke správě serverů používáme puppet. Trvalo nám celkem dlouho, než jsme jej dostali do stavu, kde by pravidelně nedocházelo k různým konfliktům a v nejhorších případech i k výpadkům služeb.

Puppet s puppet masterem

Naše první nasazení puppetu bylo celkem běžné. Jeden puppet master, ručně spravované certifikáty, statické prostředí, konfigurace serverů bez testů. Byli jsme rádi, že to funguje a ono to překvapivě fungovalo dobře. To bylo v době kolem roku 2012 v prostředí s Debianem Squeeze.

Postupem času se zvyšoval počet služeb i serverů, které jsou puppetem spravované. Zároveň s tím byla značná část puppet modulů v hektickém vývoji a začaly se ukazovat první nedostatky nasazení.

Škálování puppet masteru

První zádrhel přišel s klesajícím výkonem puppet masteru. Při desítkách serverů už jeden puppet master přestával stačit a bylo potřeba nainstalovat víc puppet masterů. S tím už puppet sám o sobě počítal a tato konfigurace byla podporovaná. Bylo však potřeba všechny puppet mastery držet synchronizované, stejně tak mít i správně nastavenou certifikační autoritu, proti které se servery ověřovaly.

Škálování modulů a vývoje

Zásadnější zádrhel přišel se škálováním puppet modulů, jejíchž aktualizace vycházely často a nikdo pořádně nevěděl kde se co změní po jejich nasazení. V té době navíc nebyly puppet moduly na takové úrovní jako dnes, o ty nejčastější se místo puppetlabs starala komunita a jednotlivé moduly byly různé kvality. Značné problémy zde přinesl i rapidní vývoj modulů stdlib, který bylo potřeba udržovat ve správné verzi, aby byl kompatibilní se všemi ostatními moduly, které jsme používali.

Jedno z řešení, které jsme v té době na internetu vídali bylo v podobě současného použití více modulů s upravenými názvy (mysql, mysql_2, atd.). Ve výsledku to celou konfiguraci ještě více znepřehlednilo a to vedlo k nechtěným chybám.

Testování konfigurace

Testování konfigurace probíhalo pomocí různých prostředí, kdy se nejdříve daná úprava nasadila na separátní environment, pustily se proti ní některé puppet agenty a když bylo vše vyladěno, tak se změna aplikovala do produkčního prostředí puppetu. Jednalo se o konfiguraci, která je vesměs popsaná v tomto článku. Nasazení změn do produkce pak probíhalo se stresem, jestli se někde něco nezapomnělo otestovat.

Nové verze Debianu

A do toho se přidaly nové verze Debian, na které bylo potřeba postupně upgradovat a které bylo potřeba podporovat. Každá nová verze Debianu přinášela velké množství novinek včetně nové verze puppet agenta. V jeden moment jsme tak museli podporovat 3 verze Debianu. Od Debian 6, kterému končila podpora v únoru 2016 až po Debian 8, který byl vydán v dubnu 2015. Zároveň musela být kontrolována podpora zásadních modulů, které se používaly napříč projekty (např. apache).

Podpora více zákazníků

S rostoucím počtem zákazníků vyvstal i požadavek na jejich autonomní provoz. U některých zákazníků vznikly separátní puppet konfigurace s jejich vlastními puppet mastery, jejíchž některé části bylo potřeba sdílet s naším puppetem. Jednalo se například o seznam výchozích balíčků, které chceme mít nainstalované na všech serverech (tcpdump, htop, atd), ale i o konfiguraci firewallů, ssh, atd.

Požadavky na konfigurační management

Problémy, které jsou výše popsané byly dlouhodobě neudržitelné a tak jsme začali řešit co s tím. Sepsali jsme si proto základní body, které jsme konfiguračního managementu vyžadovali.

Popis stavu

Líbila se nám myšlenka popisu stavu, ve kterém má systém být (má být nainstalován balíček htop), nikoliv popis toho, co se má udělat (použit apt k instalaci balíčku htop)

Testovatelnost

Potřebovali jsme, aby konfigurace byla replikovatelně testovatelná v rámci CI/CD. V ještě lepším případě mít možnost celý server si puppetem nainstalovat stejně jako na produkci a pustit proti němu integrační testy a validovat, že je vše v pořádku. Zároveň s tím bylo potřeba zjednodušit vývoj a maximálním možným způsobem eliminovat hlášky typu: Na vývoji mi to funguje, ale na produkci ne.

Škálovatelnost

S častějším využitím cloudových služeb bylo potřeba, aby konfigurační management byl schopný reagovat na velké změny v množství serverů, které obsluhuje. V praxi to znamená nainstalovat během několika minut 200 serverů, které se o pár hodin později zase smažou.

Zároveň bylo potřeba škálovat i kód, protože základna aplikací, které podporujeme se stále rozrůstá a je potřeba jí spravovat.

Bezpečnost

Konfigurace by měla být udělaná takovým způsobem, aby k ní za běžných okolností nebyl možný neoprávněný přístup.

Co vlastně nepotřebujeme

Bylo důležité uvědomit si, že některé věci, které puppet nabízí vlastně nepotřebujeme nebo je dokážeme vyřešit jinak. Jednou z nich je například sdílení zdrojů mezi jednotlivými servery (exported resources). Jednalo se o věc, kterou vyžadovaly pouze clustery. V tomto případě navíc existují mnohem lepší řešení.

Masterless puppet - první pokus

Celkem rychle jsme objevili masterless puppet. Jedná se o nasazení, kdy se katalog vygeneruje a aplikuje na každém serveru lokálně. Zároveň s tím i potřebuje mít lokálně všechny potřebné moduly a data. To nám řešilo celkem dost výše uvedených problémů. Některé nové ale vytvořilo - ty byly naštěstí řešitelné.

Nejdříve jsme si připravili skeleton, který obsahoval základní konfiguraci serveru - tzn. SSH, firewall, základní nástroje (htop, strace, atd). Ze skeletonu jsme následně dělali klony pro jednotlivé systémy, které jsme puppetem potřebovali spravovat a přidávali do jednoho forku apache s php, do dalšího MySQL databázi, atd.

Tento pokus nedopadl nejlépe. U pátého projektu nám bylo jasné, že aplikovat změny provedené ve skeletonu do všech projektů bude nereálné a dlouhodobě neudržitelné.

Dobré bylo, že jsme byli schopni testovat konfigurace jednotlivých serverů na jednorázových instancích.

Špatné bylo, že se nám vývoji instalovaly i věci, které jsme chtěli mít jen na produkci (např. firewall) a naopak na produkci vývojové nástroje.

Věděli jsme ale, že tento způsob má něco do sebe.

Masterless puppet - současný stav

K současnému stavu nám hodně pomohla myšlenka opustit běžně doporučené postupy a podívat se na problematiku trochu jinak.

Co by se stalo, kdybychom oddělili puppet pro prostředí a aplikaci? Znamenalo by to spouštět puppet dvakrát - jednou pro prostředí, po druhé pro aplikaci samotnou.

Puppet prostředí

Puppet prostředí obsahuje konfiguraci, která je důležitá jen pro dané prostředí (vývoj, produkce, ...). Každé z nich má svou vlastní potřeby, svůj vlastní repozitář a žije si nezávislým životem na ostatních. Na vývoji např. nemáme firewall, protože všechny vývojové servery jsou již za firewallem. Naopak na produkci firewall chceme mít všude, protože nevíme který server bude k čemu sloužit.

Puppet pro produkční prostředí instalujeme automaticky na všechny servery. Získáme tím automaticky základní uživatelské účty, restriktivní firewall, na všech serverech máme všechny běžně používané nástroje.

Puppet aplikace/služby

Aplikační puppet vždy obsahuje pouze konfiguraci pro danou aplikaci nebo server. Znamená to, že obsahuje například pouze pro konfiguraci mysql databázi. Nic víc.

Sdílení dat

Jsou ale situace, které vyžadují sdílení dat mezi puppetem prostředí a aplikačním puppetem. Jedná se například o konfiguraci firewallu. Ve výchozím stavu produkční firewall automaticky maže všechna pravidla, která nezná. Kdyby si aplikační puppet přidal své pravidlo (např. povolil port 80), tak mu to při příštím běhu puppet prostředí zase odstraní.

Tyto situace řešíme pomocí hiery v puppetu prostředí, která si sahá pro data nejenom do své struktury, ale dívá se i do aplikační struktury, jestli aplikace nepotřebuje udělat nějaké důležité změny.

Množství takto sdílených dat se snažíme držet pouze na naprostém minimu, protože je potřeba udržovat kompatibilitu napříč všemi aplikacemi, které takto zasahují do puppetu prostředí.

Distribuce puppet konfigurace

Pro distribuci puppet konfigurace využíváme běžné Debian balíčky. Sestavení balíčku a jeho následné nainstalování na server je roky ověřený způsob distribuce software a nemá smysl vymýšlet něco nového. Zároveň s tím nás tento způsob nutí mít balíček připravený už v době sestavování se všemi moduly. Odpadá tím možnost nekonzistence mezi servery, kdy se na některém serveru nepodaří nainstalovat závislosti v rámci r10k nebo librarian-puppet.

Další výhodou je, že když už máme sestavený balíček, tak si jej můžeme rovnou v rámci buildu vyzkoušet nainstalovat na čistém serveru.

Abychom vyřešili problém s několika paralelně podporovanými verzemi distribuce současně, tak má každé prostředí vlastní repozitář a vlastní balíček. Pro Debian Buster tedy můžeme používat jiné používat novější verze modulů než pro Stretch. Zároveň můžeme plánovat větší změny - např. přechod z iptables na nftables, kterou provedeme při změně verze distribuce.

Spouštění puppetu

Na všech serverech spouštíme puppet cronem každých 12 hodin, aby v případě potřeby došlo k aktualizaci samotných balíčků a nasazení nových konfigurací. Zároveň s tím dojde k případným úklidům na serverech (např. když někdo ručně přidá firewall pravidlo).

V clusterech, kde je potřeba spouštět puppet cíleně (např. při deploymentu), používáme různé nástroje dle potřeby a jedná se vždy o součást aplikačního puppetu.

V obou případech se ale vždy volá jeden příkaz, který provede následující kroky:

Aktualizace apt cache

Jedná se o klasický apt-get update

Aktualizace puppet balíčků

Dojde k aktualizaci všech balíčků s prefixem puppet-. Tím se dostáváme k názvosloví. Všechny balíčky s puppet nazýváme puppet-skupina-služba. Balíček pro produkční prostředí je puppet-env-prod, balíček pro NAT64 server má název puppet-infra-nat64.

Spustí se puppet pro prostředí

cd /etc/puppet

PUPPET_DEBUG="--debug" # Používá se jen při manualním spuštění
ENVIRONMENT="environment"
HIERA="hiera.yaml" # Cesta ke konfiguračnímu souboru hiery (pokud je dostupná)
puppet apply $PUPPET_DEBUG --environment $ENVIRONMENT $HIERA $ENVIRONMENT/site.pp

U proměnné $ENVIRONMENT bych ještě chvilku zůstal. Striktně používáme dvě cesty pro puppet konfiguraci:

  • /etc/puppet/environment obsahuje vždy puppet konfiguraci pro dané prostředí

  • /etc/puppet/service obsahuje vždy puppet konfiguraci pro službu

Spustí se puppet pro aplikaci/službu

Úplně stejně jako se spustil puppet prostředí, se spustí i puppet pro danou službu nebo aplikaci. Jen se spustí site.pp z jiného adresáře.

Závěr

Vyzkoušeli jsme i ostatní nástroje jako je chef, ansible a další. Z nějakého důvodu nám ale přirostl nejvíce k srdci puppet. Současná konfigurace nám navíc umožňuje používat puppet (pro prostředí) i chef (pro aplikaci) zároveň. Stačí dodržet jen minimální rozhraní pro sdílení dat. Toho využíváme například při instalaci gitlabu.

Masterless puppet v této konfiguraci používáme aktuálně 3 roky a zatím naplňuje naše potřeby. Je jedno, jestli se jedná o dynamický cluster, server s dockerem nebo manuálně nainstalovaný server. Vždy se můžeme spolehnout, že bude obsahovat základ, který potřebujeme, a zároveň kterýkoliv z vývojářů si bude moc vyzkoušet kompletní instalaci aplikace s minimálním úsilím.

Nic není dokonalé a i masterless puppet má svou sadu problémů, které je potřeba řešit. Konkrétní případy si ale necháme do jednoho z budoucích blog postů.

Jako bonus jsme tímto způsobem získali možnost provozovat aplikaci v nekonečném množství prostředí - od vývojového přes dynamická testovací prostředí, stabilní staging prostředí až k produkci.

Chcete praktickou ukázku?

Napište nám na podpora@serverzone.cz a rádi se o naše know-how podělíme.