Izvan kontejnera: projektiranje sustava za prilagodljivost stvarnom svijetu
Spremnici su izvrsni za prenosivost softvera. Međutim, nisu zamjena za arhitekturu.
Docker slika može objediniti PHP runtime, proširenja, aplikacijski kod i web poslužitelj u ponovljivu cjelinu. Time se rješava vrijedan operativni problem: isti artefakt može se premjestiti s prijenosnog računala u testno okruženje i u produkciju. No prilagodljivost u stvarnom svijetu ovisi o odlukama izvan granice spremnika — o tome kako aplikacija upravlja konfiguracijom, podacima, ovisnostima, kvarovima, skaliranjem i promjenama.
Važno pitanje nije „Možemo li ovo spremiti u spremnik?” Nego: „Što se događa kada se promijene pretpostavke oko ovog spremnika?”
Neka spremnik bude zamjenjiv
Dobro osmišljen spremnik trebao bi biti zamjenjiv. Ako aplikacija za ispravan rad treba određenu putanju na hostu, ručno uređenu datoteku unutar slike ili dugotrajnu sesiju ljuske, implementacija postaje krhka. Spremnik se možda pokreće, ali sustavom je teško upravljati.
Za PHP aplikaciju to znači odvajanje nepromjenjivog aplikacijskog koda od aspekata specifičnih za okruženje. Aplikaciju izgradite jednom, a zatim konfiguraciju pri izvođenju pružite putem varijabli okruženja, montiranih tajni ili mehanizma konfiguracije koji podržava platforma.
FROM php:8.3-fpm-alpine
WORKDIR /var/www/app
COPY composer.json composer.lock ./
RUN composer install --no-dev --prefer-dist --no-interaction --optimize-autoloader
COPY . .
CMD ["php-fpm"]
Ovaj je primjer namjerno jednostavan, ali njegov je smjer važan. Ovisnosti se instaliraju tijekom izgradnje, izvorni kod kopira se u sliku, a naredba izvođenja je izričita. Vjerodajnice baze podataka, krajnje točke reda i odredišta zapisivanja ne pripadaju slici. Slika koja sadrži produkcijske vjerodajnice nije prenosiva; ona je sigurnosni incident koji čeka pogrešan put distribucije.
Konfiguracija je sučelje
Varijable okruženja često se smatraju praktičnom zbirkom postavki. U praksi, one su sučelje između aplikacije i njezina radnog okruženja. Kao i svako sučelje, trebaju nazive, zadane vrijednosti, provjeru valjanosti i jasno vlasništvo.
Neuspjeh prijavite rano kada nedostaje potrebna konfiguracija. PHP proces koji se uspješno pokrene, ali se ne može povezati s bazom podataka sve do prvog zahtjeva korisnika, stvara odgođen i zbunjujući kvar.
$databaseUrl = getenv('DATABASE_URL');
if ($databaseUrl === false || $databaseUrl === '') {
throw new RuntimeException('DATABASE_URL must be configured.');
}
Ne bi svaka vrijednost trebala biti varijabla okruženja. Veliku strukturiranu konfiguraciju, certifikate i rotirajuće tajne možda je bolje pružiti putem datoteka ili namjenskog mehanizma za tajne. Načelo je stabilno: aplikacija treba primati konfiguraciju putem namjernih, dokumentiranih ulaza, a ne skrivenog stanja stroja.
Podaci nadživljavaju implementacije
Aplikacijski spremnici su prolazni. Baze podataka nisu. Tretiranje obaju kao jednakih jedinica implementacije jedan je od najbržih načina za stvaranje bolnih scenarija oporavka.
Promjene sheme baze podataka zaslužuju vlastitu disciplinu implementacije. Siguran slijed migracije često zahtijeva povratnu kompatibilnost: implementirajte kod koji može raditi i sa starom i s novom shemom, pokrenite migraciju, provjerite je, a zatim u kasnijem izdanju uklonite zastarjelo ponašanje. To je manje dramatično od koordiniranog „velikog prebacivanja” i znatno otpornije.
Na primjer, dodavanje stupca koji dopušta null obično je jednostavnije uvesti nego preimenovanje aktivno korištenog stupca. Aditivna promjena omogućuje starom kodu da zanemari novo polje dok ga novi kod počinje zapisivati. Kada sve pokrenute verzije aplikacije razumiju novi oblik, staro se polje može namjerno povući.
- Izradite sigurnosnu kopiju podataka i provjerite je li obnova praktična, a ne samo konfigurirana.
- Učinite migracije idempotentnima ondje gdje alat za migracije i baza podataka podržavaju taj pristup.
- Izmjerite dugotrajne migracije na realističnim količinama podataka.
- Izbjegavajte povezivanje destruktivne promjene sheme s istom implementacijom koja uvodi novi kod.
Oblikujte API-je za promjene, a ne samo za današnjeg klijenta
API je još jedna granica na kojoj jednostavnost na razini spremnika može sakriti složenost na razini sustava. JSON odgovor može izgledati uredno u lokalnom testu, a ipak ga je teško razvijati kada se na njega oslanjaju mobilni klijenti, integracije, predmemorije i pozadinski radnici.
Dajte prednost aditivnim promjenama API-ja. Dodavanje neobaveznog polja odgovora općenito je sigurnije od promjene značenja ili vrste postojećeg polja. Jasno navedite pogreške provjere valjanosti, ponašanje paginacije, neuspjele autentifikacije i semantiku ponovnih pokušaja. Klijent se ne može pouzdano integrirati s krajnjom točkom čije je ponašanje pri pogreškama slučajno.
Idempotentnost je osobito korisna za operacije koje se mogu ponoviti. Mrežna prekoračenja vremena ne otkrivaju je li poslužitelj dovršio zahtjev. Za radnje kao što su izrada plaćanja, naručivanje ili stavljanje poslovne operacije u red, idempotencijski ključ može poslužitelju omogućiti da prepozna ponovni pokušaj i vrati izvorni rezultat umjesto da radnju izvrši dvaput.
Planirajte uobičajene kvarove
Većina produkcijskih incidenata nije neobična. Baza podataka privremeno postane nedostupna. Nizvodna HTTP usluga uspori. Red sadrži nevaljanu poruku. Implementacija započne dok druga implementacija još uvijek postupno uklanja promet.
Prilagodljivi sustavi čine te kvarove vidljivima i ograničenima. Postavite vremenska ograničenja za mrežne pozive. Upotrebljavajte ponovne pokušaje samo kada je operaciju sigurno ponoviti te dodajte odgodu kako se preopterećena ovisnost ne bi dodatno opteretila. Skupi rad stavite u redove kada korisnicima ne treba trenutačan rezultat. Pošaljite dovoljno strukturiranih zapisa i metrika da biste mogli odgovoriti na pitanja što je otkazalo, gdje i za koga.
U PHP-u izbjegavajte da zahtjev neograničeno čeka ovisnost. Vremensko ograničenje nije pesimizam; ono je izjava da aplikacija mora sačuvati kapacitet za zahtjeve koje još može poslužiti.
Namjerno upotrebljavajte postupnu degradaciju
Ne zaslužuje svaka ovisnost srušiti svaku značajku. Stranica proizvoda može se i dalje prikazati ako preporuke nisu dostupne. Administrativno izvješće može prikazati privremeno stanje „podaci nisu dostupni” ako analitička usluga kasni. Ispravna zamjena ovisi o poslovnoj posljedici, ali odlučiti o njoj unaprijed daleko je bolje nego otkriti je tijekom prekida rada.
Performanse počinju opažljivošću
Optimizacija performansi bez dokaza često stvara složenost bez značajnog poboljšanja. Prije dodavanja predmemorije, promjene indeksa baze podataka ili povećanja broja radnika, utvrdite ograničavajući resurs. Čeka li zahtjev SQL, udaljeni I/O, CPU, memoriju, sporove oko zaključavanja ili serijalizaciju?
Indeksi baze podataka mogu ciljani upit učiniti brzim, a zapise skupljima. Predmemorije mogu smanjiti opterećenje i uvesti zastarjelost, pravila invalidacije i operativne ovisnosti. To su vrijedni kompromisi kada rješavaju izmjerene probleme. Obveze su kada se dodaju kao arhitektonski ukras.
Praktična početna osnova jest bilježiti trajanje zahtjeva, stopu pogrešaka, vrijeme izvršavanja upita baze podataka, dubinu reda i zasićenost resursa. Ti signali povezuju sporo korisničko iskustvo s tehničkim uzrokom učinkovitije od zbirke izoliranih zapisa spremnika.
Prilagodljivost je svojstvo sustava
Docker ostaje moćan alat jer jedan sloj isporuke čini predvidljivim. Snažno inženjerstvo proizlazi iz prepoznavanja slojeva koje ne rješava.
Izrađujte nepromjenjive artefakte. Izdvojite i provjerite konfiguraciju. Razvijajte sheme i API-je aditivno. Ograničite kvarove vremenskim ograničenjima i namjernim ponovnim pokušajima. Mjerite prije optimizacije. Te navike stvaraju sustave koji mogu prihvatiti nove zahtjeve bez pretvaranja svake implementacije u događaj s velikim ulozima.
Spremnik je paket. Prilagodljivost je dizajn koji omogućuje paketu da preživi dodir sa stvarnošću.