Izvan Dockerfileova: projektiranje za evoluciju spremnika spremnih za produkciju
Dockerfile može učiniti aplikaciju pokretljivom. To je korisno, ali nije isto što i pripremiti aplikaciju za produkciju.
Razlika postaje vidljiva kada se servis mora opetovano implementirati, promatrati pod opterećenjem, sigurno nadograđivati i biti razumljiv ljudima koji nisu napisali njegovu prvu verziju. Kontejnerizacija mijenja pakiranje; ne uklanja arhitektonske odluke vezane uz konfiguraciju, podatke, procese, umrežavanje, sigurnost ili kvarove.
Najtrajnija strategija za kontejnere počinje tretiranjem slike kao jednog dijela šireg operativnog ugovora. Aplikacija bi se trebala ponašati predvidljivo, bilo da se izvodi na prijenosnom računalu, u kontinuiranoj integraciji ili iza produkcijskog balansera opterećenja.
Gradite slike za ponovljivost, a ne za praktičnost
Produkcijska slika trebala bi sadržavati ono što je potrebno za pokretanje servisa i malo toga više. Razvojni alati, metapodaci sustava za kontrolu izvornog koda, lokalne vjerodajnice i predmemorije upravitelja paketa čine izgradnje sporijima, većima i težima za razumijevanje.
Za PHP aplikaciju višefazna izgradnja često je razumna početna osnova. Jedna faza instalira ovisnosti i kompajlira sve potrebne resurse; završna faza sadrži PHP izvođenje, kôd aplikacije i samo biblioteke za izvođenje koje su joj stvarno potrebne.
FROM composer:2 AS dependencies
WORKDIR /app
COPY composer.json composer.lock ./
RUN composer install --no-dev --prefer-dist --no-interaction --no-scripts
FROM php:8.3-fpm
WORKDIR /var/www/app
COPY --from=dependencies /app/vendor ./vendor
COPY . .
CMD ["php-fpm"]
Ovaj je primjer namjerno nepotpun: stvarna slika mora instalirati i PHP ekstenzije te biblioteke operacijskog sustava koje aplikacija zahtijeva. Važno je načelo da je instalacija ovisnosti odvojena od izvođenja. Kada se composer.lock ne mijenja, Docker može ponovno upotrijebiti sloj ovisnosti, čineći izgradnje predvidljivijima i bržima.
Važno je i fiksiranje verzija. Oznaka slike poput php:8.3-fpm praktična je, ali se s vremenom može razriješiti u različite temeljne slike. Timovi bi trebali odlučiti gdje im je potrebna reproducibilnost i tu odluku zabilježiti u procesu izgradnje i izdavanja. Cilj nije zauvijek zamrznuti svaku ovisnost; cilj je promjene učiniti namjernima i podložnima pregledu.
Konfiguracija pripada izvan slike
Slika bi trebala biti prenosiva između okruženja. Ako sadrži naziv hosta baze podataka, API ključ, URL specifičan za implementaciju ili prekidač značajke, svako okruženje treba vlastitu varijantu slike. To brzo pretvara izdanja u zagonetku konfiguracije.
Umjesto toga, ubrizgajte konfiguraciju specifičnu za okruženje tijekom izvođenja. Varijable okruženja dobro funkcioniraju za male, eksplicitne vrijednosti kao što su APP_ENV, DATABASE_URL i postavke zapisivanja. Tajne zahtijevaju dodatnu pažnju: izbjegavajte njihovo ugrađivanje u sliku, predavanje u repozitorij ili izlaganje u zapisima i dijagnostičkom izlazu.
Učitavanje konfiguracije trebalo bi jasno ne uspjeti kada obavezne vrijednosti nedostaju ili su neispravnog formata. Kontejner koji se uspješno pokrene, a zatim zakaže tek nakon što počne posluživati promet, stvara mnogo teži incident od onoga koji odmah odbije nevaljanu konfiguraciju.
Rano provjerite konfiguraciju
Neka provjera pri pokretanju bude dio granice aplikacije. Provjerite postoje li obavezne postavke, raščlanite nizove za povezivanje i provjerite da nekompatibilne opcije nisu uključene zajedno. Nemojte se nužno povezivati sa svakom vanjskom ovisnošću tijekom pokretanja; to privremeni prekid rada nizvodne usluge može pretvoriti u nepotreban neuspjeh implementacije. Provjerite ono što se može provjeriti lokalno, a zatim udaljene kvarove obradite uobičajenim putanjama ponovnog pokušaja i pogrešaka.
Odvojite web-zahtjeve od pozadinskog rada
Kontejner bi obično trebao imati jednu primarnu odgovornost. Za PHP servise to često znači da jedan kontejner pokreće PHP-FPM, drugi pokreće radnika reda, a obrnuti proxy ili platforma usmjerava HTTP promet. Mogu dijeliti istu sliku aplikacije, ali trebali bi koristiti različite naredbe i pravila skaliranja.
To odvajanje čini operativno ponašanje vidljivim. Web-procesi mogu se skalirati s volumenom zahtjeva. Radnici se mogu skalirati s dubinom reda. Planirani zadatak može se izvoditi kao namjenski posao, umjesto kao beskonačna petlja skrivena unutar web-kontejnera.
- Održavajte obradu HTTP zahtjeva responzivnom i ograničenom vremenskim ograničenjima.
- Neka radnici potvrđuju rad tek nakon uspješne obrade.
- Dizajnirajte poslove tako da budu idempotentni, jer su ponovni pokušaji i dvostruka isporuka uobičajena stvarnost.
- Osigurajte da graciozno gašenje zaustavlja novi rad prije izlaska procesa.
Graciozno gašenje posebno je važno tijekom postupnih implementacija. Kada proces primi signal za prekid, trebao bi prestati prihvaćati nove zahtjeve ili poslove, dovršiti sigurni rad u tijeku unutar ograničenog razdoblja i izaći. Ako takvo ponašanje nedostaje, implementacije mogu uzrokovati izbježive neuspjele zahtjeve ili djelomično obrađene poruke.
Učinite promjene baze podataka implementabilnima
Kontejneri su prolazni; baza podataka nije. Tretirajte migracije sheme kao pitanje izdanja, a ne kao nešto što svaki web-kontejner pokušava tijekom pokretanja. Više replika koje se pokrenu odjednom mogu se natjecati za dobivanje zaključavanja, primjenu iste promjene ili rad nad shemom koja se još mijenja.
Sigurniji je obrazac izvršiti migracije jednom kroz kontrolirani korak izdanja ili namjenski posao. Još je važnije dizajnirati migracije za kompatibilnost tijekom postupne implementacije. Dodajte stupac koji dopušta null prije nego što kôd ovisi o njemu. Implementirajte kôd koji može čitati i stare i nove prikaze. Po potrebi zasebno dopunite podatke. Uklonite stare stupce ili ponašanje tek nakon što se nijedna pokrenuta verzija više ne oslanja na njih.
Ovaj pristup proširivanja i sužavanja manje je dramatičan od velike migracije, ali štiti razdoblje u kojem stare i nove verzije aplikacije koegzistiraju.
Promatranje je dio ugovora kontejnera
Zdrav proces nije automatski zdrav servis. Kontejnerima su potrebni korisni signali za operatere i sustave za implementaciju.
- Zapisujte zapise aplikacije na standardni izlaz ili standardni izlaz za pogreške u strukturiranom obliku koji se može pretraživati.
- Izložite laganu krajnju točku za provjeru zdravlja koja razlikuje dostupnost procesa od spremnosti ovisnosti kada platforma podržava oba koncepta.
- Priložite identifikatore zahtjeva ili korelacije kako bi se jedan neuspjeli API poziv mogao pratiti kroz servise.
- Mjerite latenciju, stope pogrešaka, starost reda i pritisak na resurse umjesto da se oslanjate na ponovno pokretanje kontejnera kao dijagnozu.
Provjere zdravlja trebale bi biti male i pouzdane. Provjera živosti obično odgovara na pitanje je li proces zaglavljen. Provjera spremnosti odgovara na pitanje treba li primati promet. Izbjegavajte da bilo koja krajnja točka obavlja skup rad ili ovisi o svakoj neobaveznoj integraciji. U protivnom nadzor može pojačati već narušenu situaciju opetovanim uklanjanjem zdravog kapaciteta.
Optimizirajte za promjene, a ne samo za prvu implementaciju
Najjača Docker postava ona je koja buduće promjene čini sigurnijima. Neka Dockerfile bude čitljiv. Dokumentirajte pretpostavke o izvođenju blizu kôda. Testirajte izgrađenu sliku u kontinuiranoj integraciji, a ne samo stablo izvornog koda. Pokrenite provjere ovisnosti, konfiguracije i pokretanja nad istim artefaktom koji će biti implementiran.
Spremnost za produkciju nije kontrolni popis dovršen nakon dodavanja CMD. To je arhitektonska navika: gradite nepromjenjive artefakte, sigurno ubrizgavajte konfiguraciju, odvajajte radna opterećenja, pažljivo razvijajte podatke i učinite kvar vidljivim. Dockerfile započinje razgovor. Prava inženjerska vrijednost dolazi od dizajniranja onoga što se događa nakon pokretanja kontejnera.