Dockerizirajte svoj pozadinski sustav: izvan osnovnih spremnika za otporne sustave
Spremnik koji se uspješno pokrene nije nužno i pozadinski servis spreman za obradu prometa. Upravo na toj razlici mnoge Docker postavke podbacuju. Osnovni Dockerfile može zapakirati aplikaciju, ali otporan pozadinski servis također treba jasne granice izvođenja, predvidivu konfiguraciju, graciozno rukovanje ovisnostima i put prema produkcijskim operacijama.
Docker je najvrjedniji kada „radi na mom računalu” pretvori u ponovljiv ugovor sustava. Vaša aplikacija trebala bi sa sobom donijeti svoje zahtjeve za izvođenje, dok pitanja infrastrukture poput tajni, trajnih podataka, provjera stanja i pravila implementacije ostaju izričita, a ne slučajna.
Izgradite sliku aplikacije, a ne razvojnu mapu
Slika pozadinskog servisa trebala bi sadržavati točno ono što joj je potrebno za rad: odgovarajuća PHP proširenja, kôd aplikacije i produkcijske ovisnosti. Ne bi se trebala oslanjati na datoteke montirane s računala razvojnog programera niti na pakete instalirane interaktivno nakon pokretanja.
Za PHP-FPM aplikaciju koja koristi PostgreSQL, višefazna izgradnja zadržava Composer izvan konačne slike za izvođenje, a pritom čuva jednostavan proces izgradnje.
FROM composer:2 AS vendor
WORKDIR /app
COPY composer.json composer.lock ./
RUN composer install --no-dev --prefer-dist --no-interaction --optimize-autoloader
FROM php:8.3-fpm-alpine
RUN docker-php-ext-install pdo_pgsql
WORKDIR /var/www/html
COPY --from=vendor /app/vendor ./vendor
COPY . .
USER www-data
CMD ["php-fpm"]
Ovaj pristup čini instalaciju ovisnosti ponovljivom kada je datoteka zaključavanja predana u repozitorij. Također izbjegava isporuku Composera i njegovih alata za vrijeme izgradnje u slici koja se pokreće. U stvarnoj aplikaciji dodajte datoteku .dockerignore kako lokalne ovisnosti, zapisnici, artefakti testiranja i datoteke okruženja ne bi postali dio konteksta izgradnje.
Promišljeno odredite koje promjene poništavaju sloj izgradnje. Kopiranje datoteka composer.json i composer.lock prije ostatka izvornog kôda omogućuje Dockeru ponovno korištenje sloja ovisnosti kada se promijeni samo kôd aplikacije. To je mala odluka u dizajnu s važnim učinkom na lokalnu iteraciju i brzinu CI-ja.
Odvojite konfiguraciju od slike
Slika bi trebala biti prenosiva između okruženja. Adrese baze podataka, vjerodajnice, krajnje točke reda, razine zapisivanja i postavke značajki pripadaju konfiguraciji tijekom izvođenja, a ne tvrdo kodiranim PHP datotekama ili slojevima ugrađenima u sliku.
Datoteka Composea za razvoj može te ovisnosti učiniti vidljivima i jednostavnima za zajedničko pokretanje.
services:
app:
build: .
environment:
DB_DSN: "pgsql:host=db;port=5432;dbname=app"
DB_USER: "app"
DB_PASSWORD: "development-password"
depends_on:
db:
condition: service_healthy
db:
image: postgres:16-alpine
environment:
POSTGRES_DB: app
POSTGRES_USER: app
POSTGRES_PASSWORD: development-password
volumes:
- postgres_data:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U app -d app"]
interval: 5s
timeout: 3s
retries: 10
volumes:
postgres_data:
Imenovani volumen štiti podatke baze podataka od uobičajene zamjene spremnika. Provjera stanja poboljšava redoslijed pokretanja, ali nije zamjena za otpornost aplikacije. Baza podataka može postati nedostupna nakon što je pozadinski servis već pokrenut, tijekom ponovnog pokretanja, prekida mreže, događaja održavanja ili prebacivanja na pričuvni sustav.
Za produkciju nemojte primjer lozinke smatrati strategijom za tajne. Dostavite tajne putem odobrenog mehanizma platforme za tajne i ograničite tko ih može čitati. Varijable okruženja praktične su, ali njihove karakteristike izloženosti ovise o izvršnom okruženju, dozvolama za inspekciju procesa, zapisnicima i alatima za implementaciju.
Dizajnirajte za ovisnosti koje su privremeno nedostupne
Pozadinske usluge trebale bi razlikovati trajni kvar konfiguracije od prolaznog kvara ovisnosti. Ponovni pokušaj s neispravnim DSN-om neće pomoći. Ponovni pokušaj povezivanja s bazom podataka tijekom pokretanja može biti sasvim razuman, osobito kada se infrastruktura pokreće neovisno o aplikaciji.
Ograničite ponovne pokušaje, dodajte odgodu i jasno zakažite kada se proračun iscrpi. Neograničeni ponovni pokušaji skrivaju neispravne implementacije i mogu spriječiti orkestrator da prepozna neuspjelu uslugu.
<?php
function connectDatabase(): PDO
{
$attempts = 5;
for ($attempt = 1; $attempt <= $attempts; $attempt++) {
try {
return new PDO(
$_ENV['DB_DSN'],
$_ENV['DB_USER'],
$_ENV['DB_PASSWORD'],
[
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_TIMEOUT => 5,
]
);
} catch (PDOException $exception) {
if ($attempt === $attempts) {
throw new RuntimeException(
'Database connection failed after retries.',
0,
$exception
);
}
usleep($attempt * 200000);
}
}
throw new LogicException('Unreachable retry state.');
}
Ovo je namjerno skromno. Ponovne pokušaje treba pažljivo odabrati za svaku operaciju. Ponovni pokušaj idempotentnog povezivanja razlikuje se od ponovnog pokušaja zahtjeva za plaćanje ili upisa u bazu podataka čiji je ishod nepoznat. Za rad na putanji zahtjeva koristite vremenska ograničenja, očuvajte idempotentnost gdje je moguće i izbjegnite pretvaranje spore ovisnosti u gomilu PHP radnika koji čekaju.
Provjere stanja trebale bi predstavljati korisnu spremnost
Provjera procesa odgovara na pitanje: „Radi li PHP-FPM?” Provjera spremnosti odgovara na pitanje: „Može li ova instanca sigurno prihvatiti promet?” Povezane su, ali nisu istovjetne.
Izložite laganu krajnju točku koja provjerava samo ovisnosti potrebne za posluživanje vaše ključne putanje zahtjeva. Izbjegavajte da izvršava skupe upite ili poziva svaku neobaveznu integraciju. Ako aplikacija ne može dosegnuti primarnu bazu podataka, vraćanje odgovora o nezdravom stanju može biti prikladno. Ako je analitička usluga privremeno nedostupna, uklanjanje cijelog API-ja iz rotacije može biti kontraproduktivno.
Postavite krajnju točku stanja iza obrnutog proxyja i konfigurirajte platformu za implementaciju da je koristi. Docker može ponovno pokrenuti neuspjeli proces, ali samo okolna platforma obično može ukloniti nezdravu instancu iz prometa, zamijeniti je ili skalirati. Spremnici su primitiv za pakiranje; otpornost je svojstvo sustava.
Neka opažljivost i gašenje budu dio dizajna
Spremnici su prolazni. Zapisnike pišite na standardni izlaz i standardnu pogrešku kako bi ih izvršno okruženje moglo prikupljati. Uključite identifikatore zahtjeva ondje gdje ih arhitektura aplikacije podržava, bilježite neuspjehe s dovoljno konteksta za njihovu dijagnostiku i nikada ne bilježite vjerodajnice ni neobrađene osjetljive podatke.
Gašenje zaslužuje jednaku pažnju. Kada spremnik primi signal za prekid, aplikacija bi trebala prestati prihvaćati novi posao, dopustiti aktivnim zahtjevima ograničeno razdoblje za dovršetak i čisto zatvoriti veze. Dugotrajni radnici trebaju izričito rukovanje signalima i siguran način nastavka poslova bez dupliciranja popratnih učinaka.
Naposljetku, testirajte operativno ponašanje, a ne samo sretan put. Pokrenite s čistog računala pomoću docker compose up --build. Ponovno pokrenite bazu podataka dok aplikacija radi. Potvrdite da ponovno izgrađena slika ne sadrži lokalne razvojne artefakte. Potvrdite da podaci prežive zamjenu spremnika aplikacije, ali da ne prežive namjerno uklanjanje volumena baze podataka. Ove provjere otkrivaju odgovara li arhitektura dijagramu u vašoj glavi.
Cilj nije učiniti Docker konfiguraciju složenom. Cilj je važne pretpostavke učiniti vidljivima: što usluzi treba, kako otkazuje, kako se oporavlja i kako se nadzire. Kada ti odgovori žive u slici, konfiguraciji i dizajnu implementacije, spremnik prestaje biti praktičan omotač i postaje pouzdana jedinica isporuke pozadinskog servisa.