Jedna aplikace nasazená pro více projektů různých firem - jak na modifikace?

76 views
Skip to first unread message

stanisl...@gmail.com

unread,
Sep 11, 2020, 6:25:08 PM9/11/20
to django-cs
Zdravím,

potřeboval bych poradit či navést na best-practice v Django, jak jeden projekt nasadit pro více firem, ale tak, abych v čase mohl dělat klientské modifikace. Aktuálně jsem vymyslel aplikaci a používají ji 2 moji klienti. Django aplikace běží na nGinx a abych mohl obě aplikace aktualizovat současně, na serveru jsem nalinkoval společné zdrojové kódy (virtuál linkuje společné adresáře/aplikace). Vše běží, protože každá aplikace má svou konfiguraci a svou databázi. Pokud aplikaci vylepším, nahraju ji na 1 místo, restartuji server či oba virtuály a oba klienti frčí na aktualizacích.

Nicméně: jak nejlépe postupovat, pokud např. 1. firma bude chtít nějakou změnu v šabloně, 2. firma například upravit či přidat celou funkčnost. Rád bych udržel zdrojový kód jednotný s nějakými "odbočkami" pro konkrétního klienta, ale nemohu najít jak nejlépe a nejsystémověji se tomu postavit, aby projekt byl dlouhodobě spravovatelný, zvlášť pokud by přibylo klientů.

Věřím, že existuje nějaké systémové řešení, budu rád i za navedení či odkaz na nějaký tutorial. Předem díky!

Vladimír Macek

unread,
Sep 11, 2020, 8:11:38 PM9/11/20
to djan...@googlegroups.com

Ahoj,

a) jedna z cest na začátku vývoje byla, že bys měl projekt koncipovaný jako multi-tenant, tzn. databáze a globální objekty (glob. objekty, cache, fajly, ...) by s izolací klientů počítaly. Pak bys to měl na jednu stranu jednoduché s údržbou. Bylo by ale neustále nutné hlídat tu izolaci a udržovat rozdíly ve funkčnostech.

b1) Když by funkčních rozdílů mezi doménami bylo míň, můžeš vyvíjet JEDEN projekt pro všechny, provozovat pro každého klienta extra instanci, ale z JEDNOHO branche. Funkční rozdíly mezi doménami by se rešily výhybkami v kódu.

Zdrojáky na serveru můžou být jen jednou, dokonce i virtualenv jen jeden, jen různé settings, databáze a file storage.

b2) Větší funkční oddělený celek, by se dal uzavřít do apky, kterou by v INSTALLED_APPS měli jen příslušní klienti. Pozor, stále se z ní dá importovat.

c) Silnější varianta je udržovat takovou per-instance apku jako separátně instalovanou jen do virtualenvu příslušného klienta. Pak by importovatelná nebyla, ale pro klienty bys již musel udržovat oddělené virtualenvy.

Zde bych jit doporučil nástroj na automatický deployment, například Ansible. Nedávno jsem ho zavedl do svého toolsetu a je opravdu fajn. Deployneš novou verzi všem najednou, klidně s customizacemi.

d) Může tě zaujmout ještě další varianta: Společnou část projektu bys udržoval v hlavním git branchi, třeba 'prod' (jako produkční). A pak bys pro klienta požadujícího změnu vytvořil branch 'prod-klient1', ve které by oproti 'prod' byly změny, které tento klient chtěl.

Vývoj a bugfixy společných částí bys pushoval do 'prod' a hned mergnul do všech 'prod-*' branchů. Abys nezapomněl, můžeš si nastavit různé git hooky -- jen varovací nebo i blokující, že třeba nepushneš 'prod-*', pokud jsi do něj zapomněl mergnout 'prod'.

Chce to jenom větší sebekázeň, ale git je na souběžný vývoj přímo dělaný. Výhody jsou, že git ti vždycky krásně zobrazí diffy mezi společnou verzí a klienty i mezi klienty navzájem. U žádného klienta není kód navíc. Nespolečné commity si navždy ponesou své messages, kde svému budoucímu já podrobně vysvětlíš, proč existují. To se u výhybek ne vždy daří. Viděl bych i omezený dosah chyb v 'prod-*' brenčích. Další výhoda mě napada, že když při mergi do 'prod-*' dojde někde k průniku změn (nechci říkat přímo kolizi), přiměje tě to k úvaze, jestli dělení funkčností koncipuješ dobře.

Ansible ti opět na jediný příkaz deployne do virtualenvů všech klientů, každého z příslušného branche gitu.

V každém případě si veď dokumentaci kdy, kdo, chtěl jakou změnu, proč ji chtěl, jak jsi s ním o úpravě diskutoval. Tvým zájmem je mít rozdíly v kódu mezi klienty co nejmenší.

Měj se, ať to jde,

Vláďa
tel. 608 978 164

--
--
E-mailová skupina djan...@googlegroups.com
Správa: http://groups.google.cz/group/django-cs
---
Tuto zprávu jste obdrželi, protože jste přihlášeni k odběru skupiny „django-cs“ ve Skupinách Google.
Chcete-li zrušit odběr skupiny a přestat dostávat e‑maily ze skupiny, zašlete e-mail na adresu django-cs+...@googlegroups.com.
Chcete-li tuto diskusi zobrazit na webu, navštivte https://groups.google.com/d/msgid/django-cs/f508f99c-c9d9-46c4-9ce3-102506cd4634n%40googlegroups.com.

Jakub Vysoky

unread,
Sep 12, 2020, 3:39:25 AM9/12/20
to djan...@googlegroups.com
Abychom ti dokazali spravne poradit, museli bychom vedet, jak velike jsou rozdily v tech jednotlivych "instalacich" pro ruzne klienty.

Za me je urcite "spravne" mit:

* jednu spolecnou knihovnu v niz je vetsina funkcionality (vetsina django apps)
* virtualenv per klient
* v kazdem virtualenvu pro kazdeho klienta jejich vlastni django projekt (cili django settings, INSTALLED_APPS)
* nejspis i v tom vlastnim projektu jednu django appku, kterou mohu customizovat sablony, pridavat nejakou funkcionalitu, ktera je unikatni pro daneho klienta

Vsechno bych instaloval pomoci pipu - i kdyz to bude z nekolika vlastnich repositaru - to neni zas takovy problem. A urcite mel minimalne na upgrade nejaky skript, at to volam vzdycky stejne. Ansible je urcite super, ale muze byt pro tebe prekazka na nauceni se.

Stejne tak mozna v tvem pripade by moje navrhovane oddeleni bylo zbytecne silne. Ale ja bych to videla do budoucna nejspolehlivejsi.

Drzim palce!




--

Jan Walter

unread,
Sep 12, 2020, 5:15:42 AM9/12/20
to djan...@googlegroups.com
Z vlastních zkušeností bych doporučil:
- Mít vše v 1 repositáři - tzv. monorepo. Balíčky, knihovny, verzování, kompatibilita, je to hrozně administrativy navíc, základem monorepa jsou dobrý testy i na ty klientsky specifický funkčnosti.
- Neodlišovat klientské verze větvemi, je to akorát další příležitost se v tom utopit. Udržoval bych jednotnou hlavní vývojovou linii (dev, stage, prod, nebo jak je libo/potřeba) a tam aktuální zdrojáky všeho a pro všechny.

Klíčem bude vždy dobře předem zvážit, která funkcionalita je specifická a která obecná. Ale monorepo přecejen dává větší flexibilitu věci přesouvat. Společné funkce bych držel za každou cenu stejné (zejm. na úrovni modelů). I specifické funkce lze často zavádět na základě obecných mechanismů (jednotné rozhraní).

Další otázkou je, jak se verze budou provozovat - 1 instance pro všechny (zde jsi již udělal rozhodnutí s různými db, ale úvaha o společné db je určitě validní), nebo na druhé straně spektra on-site u/pro konkrétního klienta. Potřeby se můžou v čase významně měnit. Ale i tady poskytuje monorepo větší flexibilitu z hlediska experimentů a změn dle potřeb.

Hodně štěstí,
John

Stanislav Vasko

unread,
Sep 12, 2020, 6:25:29 AM9/12/20
to djan...@googlegroups.com
Ahoj,

to tak vlastně mám. To přetěžování přes appky per klient zvážím, ale asi až při větších změnách, jinak by mohly (do určitého času) stačit výhybky. Ale jdu do něčeho takového poprvé, ale tuším, že od určitého počtu klientů a změn budou problémy.

Co vidím jako klíčový problém a kdy oddělit je změna v DB. Jak ale oddělí DB, už to skoro můžu rozsejat celé :/

Díky moc, jdu to poválet v hlavě, Standa 

On 12 Sep 2020, at 09:39, Jakub Vysoky <jakub....@gmail.com> wrote:



Vladimír Macek

unread,
Sep 13, 2020, 9:19:16 AM9/13/20
to djan...@googlegroups.com
Ahoj Stando, rozumím. Pamatuju si sám sebe v minulosti. :-)

Nebuď ostražitý ke gitu. It's like UNIX - user friendly, just selective
about who its friends are. :-)

Programátor se gitu nevyhne. Čím dřív se ho pořádně naučíš, tím líp. Ano,
řádkový klient je z hlediska UX blbě navržený, místy neintuitivní. Ale celý
je to stabilní a pomáhá to. Jen věnuj víc energie a času jeho používání.
Bohatě se ti učení vrátí.

Tím neprosazuju svojí variantu d) níž. Byla by funkční, ale je neobvyklá a
jen pro někoho. Uvedl jsem ji jako možnost.

Hlavní pro tebe je udržovat minimum rozdílů mezi verzemi, protože "bolesti"
tvojí hlavy, kterou ti jejich TRVALÁ údržba způsobí, ti žádný klient nebude
chtít zaplatit.

Nad každou výhybkou dobře přemýšlej, než jí zavedeš. Budeš s ní žít ty, ne
klient. Velmi pomáhá mít zavedenou diskusi s klienty a tak, abyste spolu
jednali jako partneři. Dokázat umravnit nebo i odmítnout klienta, který
odmítá pochopit, že něco mít nebude nebo to bude mít draho, i když jemu to
připadá easy a je to přece technicky zvládnutelné.

V.

On 12. 09. 20 12:20, Stanislav Vasko wrote:
> Díky za podrobnou odpověď. Spoléhat na GIT se bojím, už jsem si s ním
> užil. Asi nejlépe mi zatím vychází ty výhybky (zatím). Dám si do settings
> jméno klienta a podle toho si udělám změny. Líbí se mi, že to budu mít
> pohromadě, ale dlouhodobě a při více klientech/změnách to bude asi dost
> hokej.
>
> Moduly ad klient budou cesta, jakmile do toho někdo slápne více, zbytek
> poběží na "base". Dokumentace je základ, to je základ, píšu si i hromadu
> poznámek do kódu, jinak bych se zbláznil.
>
> Díky, Standa
>>> <mailto:django-cs+...@googlegroups.com>.
>>> <https://groups.google.com/d/msgid/django-cs/f508f99c-c9d9-46c4-9ce3-102506cd4634n%40googlegroups.com?utm_medium=email&utm_source=footer>.

Petr Messner

unread,
Sep 17, 2020, 7:58:49 AM9/17/20
to djan...@googlegroups.com
Ahoj,

pár nápadů:

- je fajn si rozmyslet, jestli chceš provozovat pro každého klienta samostatnou instanci (kontejner apod.), nebo to mít vše v jedné. Se samostatnými instancemi je víc režie, ale zase je např. omezený blast radius, pokud se něco u jedné z nich podělá. Samostatné instance bych asi řešil jen pokud jich bude třeba do deseti. Na druhou stranu my třeba víc instancí toho samého používáme pro škálování (sharding) a pro speciální deploymenty pro "VIP klienty", ale všechny instance mají stejný kód, jen jinou db.

- chce to trochu software engineering :) Víc git větví bych nedělal, do toho se můžeš zamotat, a nebo se v tom pak už nevyzná někdo, s kým bys na tom případně spolupracoval. Spíš by tě mohly zajímat feature flagy, design patterny pro pluginy, věci trochu víc zapouzdřit, než v Djangu bývá běžné... Oddělovat věci do knihoven nebo modulů... Možná se nejdřív zamyslet, jak bys to dělal, kdyby to byl normální softwarový projekt (který začíná main funkcí a vše řeší explicitně), a pak přijít na to, jak to udělat v Djangu :)

- možná by stálo zato to rozdělit do komponent. Např. backend bude stejný, frontend se může lišit u každého klienta. Btw. toto teď řeší Shoptet Premium, vyšlo o nich pár podcastů, mají zajímavý přístup (ale neříkám, že bych to dělal přesně takto).

- asi by ses neměl bránit zlepšování se v devops praktikách :) Když děláš takovéhle věci, tak to chce mít infrastrukturu, o kterou se můžeš opřít.

- je otázka, jak naložit s databází (v podstatě pro ni platí ty samé úvahy výše, jako pro celou aplikaci), to by chtělo vědět, co tam bude za data a jestli/jak se mohou mezi klienty strukturně lišit. Tady mám třeba zkušenosti s tím, že jsme na několika místech udělali overengineered generalizovaný systém, který by měl teoreticky zvládnout jakéhokoliv klienta (resp. jeho data) nějakým univerzálním způsobem, ale pak to stejně shořelo na něčem, s čím nikdo vůbec nepočítal :) a takový systém pak tvoří zbytečná omezení nebo dokonce hacky (což je paradoxně to, čemu měl ten systém zabránit). Takže nakonec máme spíš kód, který na první pohled vypadá duplikovaný (což triggeruje programátorské ADHD) ale zase je triviálně přehledné, co to dělá a jak se logika liší mezi jednotlivými klienty. Mimochodem mě se tady líbí nosql přístup, ve kterém se můžeš vyhnout migracím a může být jednodušší pracovat s mnoha "malými" databázemi/kolekcemi, včetně operations aspektů; ale nosql a django možná není dobrá kombinace.

Jako tohle jsou přesně věci, které když se pokazí, tak rozpočet, kvalita kódu a použitelnost projektu jdou přes palubu :) Ale jestli to programuješ sám, tak snad nebudeš moc trpět sunken cost syndromem.

Petr

so 12. 9. 2020 v 0:25 odesílatel stanisl...@gmail.com <stanisl...@gmail.com> napsal:
--
--
E-mailová skupina djan...@googlegroups.com
Správa: http://groups.google.cz/group/django-cs
---
Tuto zprávu jste obdrželi, protože jste přihlášeni k odběru skupiny „django-cs“ ve Skupinách Google.
Chcete-li zrušit odběr skupiny a přestat dostávat e‑maily ze skupiny, zašlete e-mail na adresu django-cs+...@googlegroups.com.
Chcete-li tuto diskusi zobrazit na webu, navštivte https://groups.google.com/d/msgid/django-cs/f508f99c-c9d9-46c4-9ce3-102506cd4634n%40googlegroups.com.

MirekZv

unread,
Oct 6, 2020, 5:50:34 AM10/6/20
to django-cs
Zaujalo mě, že se tady vůbec neprobírá možnost použít Sites framework (ze základní instalace, contrib.sites).
Pak bys udržoval jeden kód, který by pouze v settings.py dělal: from konfigurace_tehle_firmy import SITE_ID, a kód by sis větvil: if SITE_ID=n:...
Resp. pokud v konfiguráku SITE_ID nemáš, tj. máš společný konfigurák pro více zákazníků, tak Sites framework určí SITE_ID podle domény (url).
Neboli, dávalo by Ti to možnost pro větší zákazníky mít extra instalaci a extra databázi.

Jinak s multitenant jsem si něco zkoušel, konkrétně s django-tenant-schemas, které mají každou kopii databáze jako jedno Postgres schéma a podle domény (např. podle domény 3.řádu: zakaznik.sluzba.cz) vyberou schéma, se kterým pracují.
Funguje to, zdá se dobře.
Seznam zákazníků si uděláš do své aplikace např. customer, do tabulky např. customers.Customer. Je otázka, jestli by to šlo směřovat přímo na djangové Sites, to asi ne (asi nebudou sedět pole, která má Sites a pole, která chce django-tenant-schemas), ale neměl by být problém nějak druhotně určit to SITE_ID, abys mohl větvit kód podle zákazníka, pokud má mít něco extra.

No a nebo to zkombinovat nějak jakkoli jinak: třeba nastavené SITE_ID by znamenalo produkční instalaci, přičemž ta by buď jela přímo (velký zákazník, vše pro něj extra), nebo s django-tenant-schemas + tabulkou Customers (malý zákazník, sdílí Postgres cluster).
Kombinace SITE_ID a customers.id by pak dávala unikátní klíč, který určuje zákazníka. Ten třeba pomocí nějaké tabulky centrální evidence zákazníků změnit na string, a větvit úplně obyčejně: if request.zakaznik='TESCO':...

Napsal jsem schválně request.zakaznik, protože takovou věc je asi vhodné přiřadit v middlewaru (=vždy).


Dne čtvrtek 17. září 2020 v 13:58:49 UTC+2 uživatel Messa napsal:

stanisl...@gmail.com

unread,
Oct 17, 2020, 12:24:36 PM10/17/20
to django-cs
Ahoj, moc díky. 

GIT jsem si prošel jako videa na YT a používám, je to velice jednoduchý nástroj. Mám nějaká GUI klikátka, díky čemuž se v něm lze hezky vyznat a rychle se vracet v čase. V podstatě s GIT (protože dělám sám) mám jediný problém: větve. Ale to je utrpení! Jakmile potřebuji rozdělat práci ve dvou větvích, tedy budu logicky jednoho dne spojovat, je to cesta do pekla. Kdykoliv mi GIT udělá merge, je to něco jako hodit nový kód do mixéru a něco vypadne. Někdy ignoruje kód z jedné větve, někdy z druhé, migrace už ani neřeším (dneska si už ani netroufnu mít změny 1 modulu ve dvou větvích). Testováním a skládáním modelů z obou větví někdy strávím hodně času a je třeba projekt komplet přetestovat, udělat nový commit a pak zase chvíli se zdviženým čelem pokračovat. Už jsem i raději kód napsal znovu a díky stroji času v PyCharm kopíroval části kódu do nového commitu. Občas jsem potřeboval určitý stav větve přenést do jiné a, pak rebase, výsledkem byla už úplně jiná aplikace, ale nezjistil jsem, co má dělat :)

Zkusil jsem každého klienta vést jako vlastní větev, vypadalo to krásně, než jsem potřeboval vzájemně prohazovat funkčnosti. Myslím, že i cestování časem má lidstvo lépe zdokumentované, než co já dostal za výsledek :). Ale to jsou ty větve, nesmí se holt mixovat. Aktuálně tedy jedu projekty odděleně a pokud si vzpomenu, že něco mám hotové u jiného klienta, copy&paste je sice trapné, ale mám to pod kontrolou a funkční. Celková časová náročnost je vyšší, ale čas se vrací, že nemusím "uklízet" a testovat po GITu a jeho merge. Základní/společné moduly pak jedou společně, tam není co zkazit, snad jen, občas bych si nějaký kód potřeboval "odložit", neboť zastaral a už ho asi nepoužiji, ale chci mít možnost o něm vědět a případně ho rychle nalézt, na to jsem v GITu nenašel jiné řešení než vlastní větev s "odpadem",  takže to držím jako odkomentovaný kód.

Každopádně díky za názory, bylo to velice poučné. Trochu mě mrzí, že Django toto neřeší nějak systematičtěji. Snad časem GIT bude mít funkce tímto směrem a o to systematičtěji se o to půjde postarat. Standa

stanisl...@gmail.com

unread,
Oct 17, 2020, 12:35:39 PM10/17/20
to django-cs
To podmínkování jsme používali i poslední firmě, jen toho pak časem bylo nějak moc. Pokud budu muset do základního/společného modulu, asi to bude právě takto "trapně", stylem IF ()..., ale zatím jsem se tomu ubránil. Nicméně už nyní má každý klient v settings.py USER_ID = "jmeno_klienta", takže to asi jednoho dne nastane. 

Spoléhat se na něco, že to vždy vybere správnou DB nechci, takže v settings (ad klient) je konfigurace databáze pro daného klienta. Navíc jsem zůstal u SQLite (jedná se zatím o tisíce řádek denně), neboť miluji, když celý projekt je v souborech.

Měj se, Standa

Petr Messner

unread,
Oct 17, 2020, 12:38:27 PM10/17/20
to djan...@googlegroups.com
Ahoj,

git zvládá větve a merge v pohodě, stejně nebo lépe než ostatní verzovací systémy. Asi děláš něco špatně :) Nebo ten kód při každé změně natolik překopeš, že to prostě mergnout nejde?

Nauč se s gitem pracovat v příkazové řádce. Podle mých zkušeností lidi, kteří používají GUI, se to prostě pořádně nenaučí a dělají chyby.

Pro snadný přenos jednotlivých změn mezi větvemi slouží git cherry-pick (pokud samozřejmě není vhodné dělat merge).

Petr Messner

17. 10. 2020 v 18:24, stanisl...@gmail.com <stanisl...@gmail.com>:

Ahoj, moc díky. 
Chcete-li zrušit odběr skupiny a přestat dostávat e‑maily ze skupiny, zašlete e-mail na adresu django-cs+...@googlegroups.com.
Chcete-li tuto diskusi zobrazit na webu, navštivte https://groups.google.com/d/msgid/django-cs/94928c7b-29e6-4a51-b881-9f42c572fcb1n%40googlegroups.com.
Reply all
Reply to author
Forward
0 new messages